调试Pyparsing语法

18

我正在构建一个名为C--(不是真正的C--语言)的虚构编程语言的解析器。我已经到了需要将语言的语法转换成Pyparsing可以接受的东西的阶段。不幸的是,当我来解析我的输入字符串(该字符串正确并且不应导致Pyparsing出错)时,它无法正确解析。我担心这是由于我的语法存在错误,但由于我第一次使用Pyparsing,我似乎看不到自己的错误在哪里。

我已经上传了我正在转换的语法这里供大家阅读。

编辑: 已根据Paul的建议更新。

这是我目前拥有的语法(语法定义的前两行非常糟糕,我知道):

# Lexical structure definition
ifS = Keyword('if')
elseS = Keyword('else')
whileS = Keyword('while')
returnS = Keyword('return')
intVar = Keyword('int')
voidKeyword = Keyword('void')
sumdiff = Literal('+') | Literal('-')
prodquot = Literal('*') | Literal('/')
relation = Literal('<=') | Literal('<') | Literal('==') | \
           Literal('!=') | Literal('>') | Literal('=>')
lbrace = Literal('{')
rbrace = Literal('}')
lparn = Literal('(')
rparn = Literal(')')
semi = Literal(';')
comma = Literal(',')
number = Word(nums)
identifier = Word(alphas, alphanums)

# Syntax definition
term = ''
statement = ''
variable    =   intVar + identifier + semi
locals      =   ZeroOrMore(variable)
expr        =   term | OneOrMore(Group(sumdiff + term))
args        =   ZeroOrMore(OneOrMore(Group(expr + comma)) | expr)
funccall    =   Group(identifier + lparn + args + rparn)
factor      =   Group(lparn + expr + rparn) | identifier | funccall | number
term        =   factor | OneOrMore(prodquot + factor)
cond        =   Group(lparn + expr + relation + expr + rparn)
returnState =   Group(returnS + semi) | Combine(returnS + expr + semi)
assignment  =   Group(identifier + '=' + expr + semi)
proccall    =   Group(identifier + lparn + args + rparn + semi)
block       =   Group(lbrace + locals + statement + rbrace)
iteration   =   Group(whileS + cond + block)
selection   =   Group(ifS + cond + block) | Group(ifS + cond + block + elseS + block)
statement   =   OneOrMore(proccall | assignment | selection | iteration | returnState)
param       =   Group(intVar + identifier)
paramlist   =   OneOrMore(Combine(param + comma)) | param
params      =   paramlist | voidKeyword
procedure   =   Group(voidKeyword + identifier + lparn + params + rparn + block)
function    =   Group(intVar + identifier + lparn + params + rparn + block)
declaration =   variable | function | procedure
program     =   OneOrMore(declaration)

我想知道我在翻译语法时是否有任何错误,并且有什么改进措施可以让它更简化,同时遵循我所获得的语法。

编辑 2:已更新以包含新错误。

这是我正在解析的输入字符串:

int larger ( int first , int second ) { 
if ( first > second ) { 
return first ; 
} else { 
return second ; 
} 
} 

void main ( void ) { 
int count ; 
int sum ; 
int max ; 
int x ; 

x = input ( ) ; 
max = x ; 
sum = 0 ; 
count = 0 ; 

while ( x != 0 ) { 
count = count + 1 ; 
sum = sum + x ; 
max = larger ( max , x ) ; 
x = input ( ) ; 
} 

output ( count ) ; 
output ( sum ) ; 
output ( max ) ; 
} 

这是我从终端运行程序时收到的错误信息:

/Users/Joe/Documents/Eclipse Projects/Parser/src/pyparsing.py:1156: SyntaxWarning: null string passed to Literal; use Empty() instead
other = Literal( other )
/Users/Joe/Documents/Eclipse Projects/Parser/src/pyparsing.py:1258: SyntaxWarning: null string passed to Literal; use Empty() instead
other = Literal( other )
Expected ")" (at char 30), (line:6, col:26)
None

你有什么线索可以提供给我们吗?比如说,你是否收到了任何错误信息?你说“它没有正确解析”,但是你怎么知道的呢?是否出现了错误?它是否生成了不正确的AST?需要更多的信息。 - Greg Hewgill
抱歉。我已经更新了我的问题,包括我正在尝试解析的输入字符串和我在终端中收到的错误消息。 - greenie
1个回答

33

1)将Literal("if")更改为Keyword("if")(以此类推,一直到Literal("void")),以防止匹配变量名为"ifactor"的前导"if"。

2)numsalphasalphanums不是表达式,它们是字符串,可以与Word类一起使用,定义一些典型的字符集,如定义“数字是由nums组成的单词”,或者“标识符是以alpha开始,后跟零个或多个alphanum的单词”。因此,代码应该改为:

number = nums
identifier = alphas + OneOrMore(alphanums)

你想要什么

number = Word(nums)
identifier = Word(alphas, alphanums)

3) 我认为您想要使用Group而不是Combine。当您希望匹配的标记是连续的且没有间隔空格时,请使用Combine,它将连接这些标记并将它们作为单个字符串返回。 Combine通常在以下情况下使用:

realnum = Combine(Word(nums) + "." + Word(nums))
没有Combine,对于解析"3.14"将返回字符串列表['3', '.', '14'],因此我们添加了Combine以便实数的解析结果是'3.14'(您随后可以将其传递给解析操作以转换为实际浮点值3.14)。Combine的中间无空格强制执行也使我们不会意外解析'The answer is 3. 10 is too much.'并认为"3. 10"表示实数。
4) 这不应该导致你的错误,但你的输入字符串有很多额外的空格。如果你让你的语法工作,你应该能够像解析"int x ;"一样解析"int x;"
希望这些提示中的一些能够帮助您。您阅读过任何关于Pyparsing的在线文章或教程吗?请查看在线示例。您需要充分掌握WordLiteralCombine等如何执行它们各自的解析任务。
5) 您已经错误地实现了term和statement的递归定义。而不是向它们分配'',请编写:
term = Forward()
statement = Forward()

然后当您实际使用递归定义来定义它们时,请使用<<运算符(并确保将右侧表达式括在()中)。

term << (... term definition ...)
statement << (... statement definition ...)

你可以在这里找到一个递归解析器的示例,以及关于基本pyparsing用法的演示这里 - 在标题为“Parsing Lists”的部分中可以了解有关如何处理递归的更多逐步说明。


谢谢Paul。我已经将所有的Combine替换为Group,现在我得到了一些不同的错误,我认为这些错误与我的规则编写方式有关,而不是我使用的语法。我已经更新了我的原始帖子以反映你建议的更改和我遇到的新错误。 - greenie
非常棒,Paul。非常感谢你。我希望我能多加一点 +1 :) - greenie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接