首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python PLY解析器

Python PLY解析器
EN

Stack Overflow用户
提问于 2011-10-27 05:03:29
回答 2查看 4.6K关注 0票数 2

我试着到处寻找这个问题的答案,但似乎找不到。我正在尝试用Python编写一个解析器,使用PLY作为一种合成语言。我的BNF的简化版本如下所示:

代码语言:javascript
复制
statement-list -> statement ',' statement-list |
                 'print' expr

statement -> ident 'was' 'a' type |
             ident 'became' expr

type -> 'number' | 'letter'

expr -> factor |
       expr '+' factor |
       expr '-' factor

factor -> number | letter | ident

其中数字和字母类似于int和char。

Yacc文档(http://www.dabeaz.com/ply/ply.html#ply_nn23)只显示了简单算术表达式的语法,其中很清楚p应该是什么。

代码语言:javascript
复制
def p_expression_plus(p):
   'expression : expression PLUS term'
    p[0] = p[1] + p[3]

我的问题是,我的BNF中的语句表是怎么做的?我有:

代码语言:javascript
复制
def p_statement_list_comma(p):
    'statement-list : statement COMMA statement-list'

但我真的不确定下一步该放什么。任何帮助都将不胜感激!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-10-27 19:52:16

这真的取决于你如何构造你的代码,以及你想要如何评估它。如果您正在进行计算,只要它以正确的顺序进行计算,您就不会想要在p_statement_list_comma文档字符串之后的任何东西,例如,就像您拥有它一样-语句将无论如何都会被计算,如果需要,您可以保留一个变量的全局字典或类似的东西来跟踪一些状态,如标识符值。

如果你想构建一个解析树,例如,如果你不喜欢ply的求值顺序,那么你可以这样做:

代码语言:javascript
复制
def p_statement_list_comma(p):
    'statement-list : statement COMMA statement-list'
    p[0] = [p[1]] + p[3]

def p_statement_print_expr(p):
    'statement-list : PRINT expr'
    p[0] = [p[2]]

这将为您提供一个语句列表,列表中的最后一个元素是一个表达式。为了简单起见,这里使用了列表;如果你愿意,你也可以使用你自己的类--只要给任何你想要p的python对象赋值,它就会在上面的级别中可用。

如果您希望从yacc.parse返回打印表达式的结果(解析树顶层的值将从yacc.parse返回),您可以这样做:

代码语言:javascript
复制
def p_statement_list_comma(p):
    'statement-list : statement COMMA statement-list'
    p[0] = p[3]

def p_statement_print_expr(p):
    'statement-list : PRINT expr'
    p[0] = p[2]
票数 3
EN

Stack Overflow用户

发布于 2011-10-27 13:40:53

我不能为PLY解决方案说话,但这里有一个使用pyparsing的解决方案。有时,一个pyparsing示例非常有用,即使您最终希望使用其他库来实现解析器,也可以将其作为快速实用的原型/练习。不幸的是,这个示例大量使用了operatorPrecedence方法,它埋没了很多中缀解析的魔力,所以我不知道您将如何轻松地转换它。在名为fourFn.py的示例页面(http://pyparsing.wikispaces.com/Examples)上的pyparsing wiki上可以找到一个更传统的expr/term/factor解析器示例。

代码语言:javascript
复制
bnf = """
statement-list -> statement ',' statement-list

statement -> ident 'was' 'a' type | 
             ident 'became' expr |
             'print' expr |
             'if' conditional-expr statement

type -> 'number' | 'letter' 

expr -> factor | 
       expr '+' factor | 
       expr '-' factor 

factor -> number | letter | ident 
"""

from pyparsing import (CaselessKeyword, Word, nums, alphas, alphanums, operatorPrecedence, 
    Forward, MatchFirst, opAssoc, oneOf, Group, delimitedList)

PRINT, WAS, A, BECAME, NUMBER, LETTER, IF, ELSE, TRUE, FALSE, AND, OR, NOT = map(
    CaselessKeyword,
    "print was a became number letter if else true false and or not".upper().split())
keyword = MatchFirst([PRINT, WAS, A, BECAME, NUMBER, LETTER, IF, ELSE, TRUE, FALSE, AND, OR, NOT])

typeSpecifier = NUMBER | LETTER

number = Word(nums)
ident = ~keyword + Word(alphas, alphanums+'_')
operand = number | ident

expr = operatorPrecedence(operand,
    [
    ('-', 1, opAssoc.RIGHT),
    (oneOf('* /'), 2, opAssoc.LEFT),
    (oneOf('+ -'), 2, opAssoc.LEFT),
    ])

comparisonExpr = operatorPrecedence(expr,
    [
    ("!", 1, opAssoc.RIGHT),
    (oneOf("< > = <= >= !="), 2, opAssoc.LEFT),
    ])

booleanExpr = operatorPrecedence(TRUE | FALSE | comparisonExpr,
    [
    (NOT, 1, opAssoc.RIGHT),
    (AND, 2, opAssoc.LEFT),
    (OR, 2, opAssoc.LEFT),
    ])

statement = Forward()
printStmt  = PRINT + expr
wasaStmt   = ident + WAS + A + typeSpecifier
becameStmt = ident + BECAME + expr
ifStmt = IF + booleanExpr + statement
statement << Group(printStmt | wasaStmt | becameStmt | ifStmt)

statementList = delimitedList(statement)

tests = """\
    x was a number
    y became 2+5
    print y
    print 100*(5+2)
    print 100*5+2
    if 5 > y print 1000
    if y < 10 y became y+1, print y
    """.splitlines()[:-1]

for t in tests:
    print t.strip()
    for s in statementList.parseString(t).asList():
        print(s)
    print

打印:

代码语言:javascript
复制
x was a number
['x', 'WAS', 'A', 'NUMBER']

y became 2+5
['y', 'BECAME', ['2', '+', '5']]

print y
['PRINT', 'y']

print 100*(5+2)
['PRINT', ['100', '*', ['5', '+', '2']]]

print 100*5+2
['PRINT', [['100', '*', '5'], '+', '2']]

if 5 > y print 1000
['IF', ['5', '>', 'y'], ['PRINT', '1000']]

if y < 10 y became y+1, print y
['IF', ['y', '<', '10'], ['y', 'BECAME', ['y', '+', '1']]
['PRINT', 'y']

我随意添加了print作为一种语句,这样它就可以出现在程序主体中的任何地方。此外,我还尝试添加一个IF-THEN语句,这确实显示了添加这样的控制流语句如何开始将您带到编写递归语法的道路上(不需要递归只是为了'was a',‘take’和'print')。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7908898

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档