如何解决Haskell:尽管在文件中有更多输入,但在第一个表达式后仍会解析文件
以下是我正在编写解析器的语言的示例程序。
n := 1
Do (1)-> -- The 1 in brackets is a placeholder for a Boolean or relational expression.
n := 1 + 1
Od
程序看起来像这样时,parseFile
函数在分配的第一行之后结束,但是当删除分配时,它将按预期进行解析。以下是在GHCI
中的调用方式,首先显示第一行,然后删除:
λ > parseFile "example.hnry"
Assign "n" (HInteger 1)
λ > parseFile "example.hnry"
Do (HInteger 1) (Assign "n" (AExpr (HInteger 1) Add (HInteger 1)))
预期输出将类似于以下内容:
λ > parseFile "example.hnry"
Assign "n" (HInteger 1) Do (HInteger 1) (Assign "n" (AExpr (HInteger 1) Add (HInteger 1)))
我首先假设这与赋值解析器有关,但是在循环的主体中,存在一个可以按预期进行解析的赋值,因此我可以排除这种情况。我认为问题出在parseFile
函数本身之内。以下是parseFile
函数以及构成我用来解析程序的parseExpression
函数的其他函数。
我认为错误在parseFile
之内,因为它只解析一次表达式,并且不会“循环”以获取自己想要的更好的单词来检查是否还有更多的输入要离开解析。我认为那是错误,但我不太确定。
parseFile :: String -> IO HVal
parseFile file =
do program <- readFile file
case parse parseExpression "" program of
Left err -> fail "Parse Error"
Right parsed -> return $ parsed
parseExpression :: Parser HVal
parseExpression = parseAExpr <|> parseDo <|> parseAssign
parseDo :: Parser HVal
parseDo = do
_ <- string "Do "
_ <- char '('
x <- parseHVal -- Will be changed to a Boolean expression
_ <- string ")->"
spaces
y <- parseExpression
spaces
_ <- string "Od"
return $ Do x y
parseAExpr :: Parser HVal
parseAExpr = do
x <- parseInteger
spaces
op <- parseOp
spaces
y <- parseInteger <|> do
_ <- char '('
z <- parseAExpr
_ <- char ')'
return $ z
return $ AExpr x op y
parseAssign :: Parser HVal
parseAssign = do
var <- oneOf ['a'..'z'] <|> oneOf ['A'..'Z']
spaces
_ <- string ":="
spaces
val <- parseHVal <|> do
_ <- char '('
z <- parseAExpr
_ <- char ')'
return $ z
return $ Assign [var] val
解决方法
如您所述,您的parseFile
函数使用parseExpression
解析器解析单个表达式(尽管“ statement”会是一个更好的名称)。您可能想为“程序”或表达式/语句序列引入一个新的解析器:
parseProgram :: Parser [HVal]
parseProgram = spaces *> many (parseExpression <* spaces)
,然后在parseFile
中,将parseExpression
替换为parseProgram
:
parseFile :: String -> IO [HVal]
parseFile file =
do program <- readFile file
case parse parseProgram "" program of
Left err -> fail "Parse Error"
Right parsed -> return $ parsed
请注意,我必须将此处的类型从HVal
更改为[HVal]
,以反映一个事实,即程序是一系列HVal
类型的表达式序列,被表示为能够将多个HVal
组合在一起的某种数据类型,而列表[HVal]
是这样做的一种方式。
如果要使程序成为HVal
而不是[HVal]
,则需要在HVal
类型中引入一个能够表示程序的新构造函数。一种方法是使用构造函数直接表示语句块:
data HVal = ... | Block [HVal]
另一种方法是添加一个表示两个语句序列的构造函数:
data HVal = ... | Seq HVal HVal
这两种方法都在真实解析器中使用。 (请注意,您通常会选择一个;而不会同时使用两者。)例如,要表示三个赋值语句的序列,block方法将直接将其作为列表:
Block [Assign "a" (HInteger 1),Assign "b" (HInteger 2),Assign "c" (HInteger 3)]
而两个语句序列方法将构建一种嵌套树:
Seq (Assign "a" (HInteger 1)) (Seq (Assign "b" (HInteger 2)
(Assign "c" (HInteger 3))
这两个备选方案的适当解析器可能都是:
HVal
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。