我在这里得到了这段代码,它是一个用Haskell构造的命令式编程语言编写的程序,所以问题是“如何为这种语言实现词法分析器和解析器”,该程序被定义为一系列语句有6种类型:“:=”,“goto”,“write”,“stop”,“if goto”和“int”
我有点迷失在这里,我读过关于词法分析器和解析器,但没有找到任何示例如何实现它们,如果你能给我一段代码我会很感激,所以我可以试着去做我自己,或者至少是有用信息的链接
答案 0 :(得分:22)
我不会为你写完整件事,但我会让你从每一点开始。我们要经历的三个阶段是:
我们可以在2到3之间建立一个单独的lexing阶段,但Parsec乐意做两个级别。 Lexing是将输入分成标记的地方 - 有意义的输入位 - 相当于人类语言中的单词,也称为lexemes。跳过一个单独的lexing阶段意味着我们需要对空白等更加明确。
首先,您需要定义语法。最好用纸和笔完成,但我会让你开始:
program ::= line {[newline line]}
line ::= num dot whitespace statement
statement ::= declaration | write | ifstatement | goto | assignment | stop
declaration = "Int" whitespace varname equals int
varname = letter {[alphanum]}
-- more things here, including the more interesting ifstatement:
ifstatement = "if" whitespace expression whitespace equals expression whitespace statement
-- lower level stuff:
dot = "."
int = digit {[digit]}
digit = "0" | "1" | ... | "9"
whitespace = " " okspace | "\t" okspace
okspace = "" | whitespace
考虑一下如何匹配您的示例程序,并考虑如何完成它:
1. Int n=5
2. write n
3. Int fac=1
4. if 0 n goto 8 -- unusual
5. fac := fac * n
6. n := n+1 -- complex
7. goto 4
8. write fac
9. stop
第4行中的if语句不常见,因为其中没有=
或==
。也许这是为了简化语法,它只能接受单个变量或整数,两者之间有空格。也许这是一个错字,你的意思是有一个等号和任意表达。找出哪个并重写语法的ifstatement
部分。
第6行中的赋值很复杂,因为在这里你必须解析任意算术表达式。据我所知,有很多例子可以解决,所以我现在很乐意跳过这个例子。如果你对这个问题感到困惑,那就另外提出一个问题,但希望你能先用其余的方法建立你的解析技巧。
抽象语法树表示构成输入的标记组合。在Haskell中,我们可以定义自己的内容以适应上下文,这将使生活更简单
我实际上是compiling this answer(检查拼写错误等的好方法),这就是为什么我需要在代码顶部的一些声明:
module ParseLang where
import Text.Parsec hiding (Line)
import Text.Parsec.String
import Control.Applicative hiding ((<|>), many)
我们只会制作一个Program
Line
的列表,但强制解析器必须至少有一个。
type Program = [Line]
对于Line
,它需要一个数字和一个语句,但点只是我们不需要存储的语法。我们可以将行号存储为Int
,因此尽管在类型声明中允许使用负数,但解析器也不会接受否定。
data Line = Line {aNum::Int, aStatement :: Statement}
deriving Show
多个选项很容易定义:
data Statement = Declaration VarName Int
| Write VarName
| IfStatement Expression Expression Statement
| Goto LineNumber
| Assignment VarName Expression
| Stop
deriving Show
注意缺少所有语法cruft / connectives /等号只留下改变的位。
我停在那里 - 你可以完成:
data Expression = Expression -- I've left this one for you
deriving Show
type VarName = String -- could use newtype for type safety for these to
type LineNumber = Int
较低级语法不需要在AST中表示,因为我们将使用字符串。
这一点现在很简单。让我们从语法树的底部开始,然后进行处理。
num :: Parser Int
num = read <$> many digit
我们使用了<$>
,这是我们通过导入fmap
获得的Control.Applicative
的同义词。在这里,它使用左侧的纯函数(在本例中为read
)更改解析器返回的值。如果您不习惯fmap
的介绍,请查看this other answer。
让我们创建一个解析器,解析字符串文字,然后解析一些空格:
whitespace = space >> spaces -- a space then optional spaces
lit :: String -> Parser String
lit xs = string xs <* whitespace
现在<*
很有趣。它看起来像<*>
,它实际上组合了两个解析器,并与<$>
结合使用,它实际上将纯函数映射到结果上。 *>
和<*
组合了两个解析器,但忽略了其中一个解析器的输出,因此string "goto" <* whitespace
解析了"goto"
和一些空格,但抛弃了空格。
现在我们准备解析一个goto语句:
goto :: Parser Statement
goto = Goto <$> (lit "goto" *> num)
现在让我们来看看varName
varName :: Parser VarName
varName = (:) <$> letter <*> many (alphaNum <|> oneOf "'_")
有几件事正在发生。
1。 <|>
是另一种选择 - 一个或另一个,所以(alphaNum <|> oneOf "'_")
接受一个字母数字字符或一对无辜字符'
和_
您可能希望包含在变量名称中。
2。 f <$> parser1 <*> parser2
是一种非常好的组合解析器的方法。它运行parser1,然后运行parser2,然后将函数f
映射到它们生成的结果f上。它适用于许多解析器:
--ifstatement = "if" whitespace expression whitespace equals whitespace expression whitespace statement
ifStatement :: Parser Statement
ifstatement = IfStatement
<$> (lit "if" >> expression)
<*> (lit "=" >> expression) -- I put = in, but see below
<*> (whitespace >> statement) -- I'd be happier with a then in here
如果您只允许VarName
或Int
而不是普通Expression
,那么您不需要等号。
以下是你如何把它放在一起:
statement :: Parser Statement
statement = goto
<|> stop
<|> declaration
<|> write
<|> ifStatement
<|> assignment
--program ::= line {[newline line]}
program :: Parser Program
program = many1 line
--line ::= num dot whitespace statement
line :: Parser Line
line = Line <$> (num <* char '.') <*> (statement <* char '\n')
但是每次尝试使用尚未完成的解析器时,我都会给你留下错误信息,所以整个过程都会编译好,你定义的位应该有效。
stop = error "You've not yet defined stop"
declaration = error "You've not yet defined declaration"
write = error "You've not yet defined write"
ifStatement = error "You've not yet defined ifStatement"
assignment = error "You've not yet defined assignment"
expression = error "You've not yet defined expression"