调试Pyparsing语法

18 投票
1 回答
5342 浏览
提问于 2025-04-15 16:33

我正在为一个虚构的编程语言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

1 个回答

33

1) 把 Literal("if") 改成 Keyword("if")(一直改到 Literal("void")),这样可以避免把变量名为 "ifactor" 的开头 "if" 匹配上。

2) numsalphasalphanums 不是表达式,它们是字符串,可以和 Word 类一起用来定义一些典型的字符集合,比如定义“单词”的时候,像“一个数字是由 nums 组成的单词”,或者“一个标识符是以字母开头,后面跟着零个或多个字母数字的单词”。所以,不要这样写:

number = nums
identifier = alphas + OneOrMore(alphanums)

你应该这样写:

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

3) 不要用 Combine,我觉得你应该用 Group。当你希望匹配的标记是连续的,没有空格分隔时,才用 Combine,它会把这些标记连接起来,返回一个单一的字符串。Combine 通常在这样的情况下使用:

realnum = Combine(Word(nums) + "." + Word(nums))

如果不使用 Combine,解析 "3.14" 会返回字符串列表 ['3', '.', '14'],所以我们加上 Combine,这样解析的结果 realnum 就是 '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 使用的演示 这里 - 查看标题为“解析列表”的部分,了解递归是如何处理的,步骤更详细。

撰写回答