这里是要解析的语言ID的语法:
expr ::= val | const | (expr) | unop expr | expr binop expr
var ::= letter
const ::= {digit}+
unop ::= -
binop ::= /*+-
我正在使用haskell wiki中的示例。
这里没有显示语义和令牌解析器。
exprparser = buildExpressionParser table term <?> "expression"
table = [ [Prefix (m_reservedOp "-" >> return (Uno Oppo))]
,[Infix (m_reservedOp "/" >> return (Bino Quot)) AssocLeft
,Infix (m_reservedOp "*" >> return (Bino Prod)) AssocLeft]
,[Infix (m_reservedOp "-" >> return (Bino Diff)) AssocLeft
,Infix (m_reservedOp "+" >> return (Bino Somm)) AssocLeft]
]
term = m_parens exprparser
<|> fmap Var m_identifier
<|> fmap Con m_natural
减号char出现两次,一次为一元,一次为二进制运算符。
在输入"1--2"
上,解析器仅给出Con 1
而不是预期的"Bino Diff (Con 1) (Uno Oppo (Con 2))"
欢迎任何帮助。Full code here
reservedOp
的目的是创建一个解析器(您将其命名为m_reservedOp
),以解析给定的运算符符号字符串,同时确保它是较长的运算符符号字符串的前缀[[not 。您可以从源代码中的reservedOp
的定义中看到这一点:
reservedOp name =
lexeme $ try $
do{ _ <- string name
; notFollowedBy (opLetter languageDef) <?> ("end of " ++ show name)
}
请注意,仅在提供的name
后没有任何opLetter
符号的情况下,才会对其进行解析。在您的情况下,字符串
"--2"
不能由m_reservedOp "-"
解析,因为即使它以有效的运算符"-"
开头,该字符串也将作为更长的有效运算符"--"
的前缀出现。] >在使用单字符运算符的语言中,您可能根本不想使用
reservedOp
,除非您想在不插入空格的情况下禁止相邻的运算符。只需使用symbol "-"
,它就会始终解析"-"
,无论后面是什么(并消耗后面的空白,如果有的话)。同样,在具有固定集
运算符(即,没有用户定义的运算符)的语言中,您可能不会使用operator
解析器,因此将不需要opStart
或[C0 ]。如果没有reservedOpNames
或reservedOp
,则不会使用operator
解析器,因此也可以将其删除。这可能非常令人困惑,opLetter
文档在解释“保留”机制应该如何工作方面做得很糟糕。这是入门书:让我们从标识符开始,而不是运算符。在一种典型的语言中,它允许用户定义标识符(即,几乎所有语言,因为“变量”和“功能”具有用户定义的名称)并且还可能具有一些不允许用作标识符的保留字,相关设置在`GenLanguageDef中是:
Parsec
使用identStart -- parser for first character of valid identifier identLetter -- second and following characters of valid identifier reservedNames -- list of reserved names not allowed as identifiers
对象创建的lexeme(吸收空白)解析器是:
GenTokenParser
-解析未知的用户定义标识符。它解析从identifier
开始的字符,后跟零个或多个identStart
,直到第一个非identLetter
。 (它从不解析部分标识符,因此它永远不会在表上留下更多identLetter
。)- 另外,它检查标识符是否不在列表
identLetter
中。
reservedNames
-解析给定的字符串。如果字符串是保留字,则不会检查它是否不是较大有效标识符的一部分。因此,symbol
会与symbol "for"
的开头匹配,而这几乎是您想要的。请注意,foreground = "black"
不使用symbol
,identStart
或identLetter
。reservedNames
-解析给定的字符串,然后确保其后没有reserved
。因此,identLetter
将解析m_reserved "for"
,但for (i=1; ...
。通常,提供的字符串将是有效的标识符,但是对此不作任何检查,因此,如果需要,您可以编写foreground = "black"
-用一种具有常见字母数字标识符的语言编写,只要它可以解析m_reserved "15"
后面没有字母或其他数字。另外,也许有些令人惊讶,没有检查提供的字符串是否在"15"
中。如果这对您有意义,那么操作员设置将遵循完全相同的模式。相关设置为:reservedNames
和相关的解析器是:
opStart -- parser for first character of valid operator
opLetter -- valid second and following operator chars, for multichar operators
reservedOpNames -- list of reserved operator names not allowed as user-defined operators
-解析一个未知的,用户定义的运算符,从operator
开始,然后是零个或多个opStart
,直到第一个非opLetter
。因此,应用于字符串opLetter
的operator
将始终占据整个运算符"--2"
,而不仅仅是前缀"--"
。还要进行一次检查,确认所得的运算符不在"-"
列表中。reservedOpNames
-完全与标识符相同。它解析没有检查或引用symbol
,opStart
或opLetter
的字符串,因此reservedOpNames
会很好地解析字符串symbol "-"
的第一个字符,而将第二个"--"
字符保留为稍后的解析器。"-"
-解析给定的字符串,确保其后没有reservedOp
。因此,opLetter
将解析m_reservedOp ":"
的开头,但不解析“:+:y":lst"
+, assuming
opLettermatches
reservedOpNames`。