Python PLY 解析器

标签 python parsing yacc ply

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

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[0] 应该是什么。

def p_expression_plus(p):
   'expression : expression PLUS term'
    p[0] = p[1] + p[3]

我的问题是我应该如何处理 BNF 中的语句列表?我有:

def p_statement_list_comma(p):
    'statement-list : statement COMMA statement-list'

但我真的不确定接下来要放什么。 非常感谢任何帮助!

最佳答案

我不能代表 PLY 解决方案,但这是一个使用 pyparsing 的解决方案。有时,一个 pyparsing 示例可能很有用,即使您最终想使用其他一些库来实现您的解析器,作为一个快速而肮脏的原型(prototype)/练习。不幸的是,这个例子大量使用了 operatorPrecedence 方法,它隐藏了很多中缀解析魔法,所以我不知道你能多容易地翻译它。可以在示例页面 ( http://pyparsing.wikispaces.com/Examples ) 的 pyparsing wiki 上找到更传统的 expr/term/factor 解析器示例,标题为 fourFn.py .

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

打印:

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 语句,这个确实展示了添加这样一个控制流语句如何开始带你走上编写递归语法的道路(不需要递归只是为了"is"、“成为”和“打印”)。

关于Python PLY 解析器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7908898/

相关文章:

java - 在 Android 上使用 Sax 解析 Xml

c - C 语言的 vhdl 解析器

c# - 解析 FtpWebRequest ListDirectoryDe​​tails 行

c - 为什么 yacc 或 bison 将 $1 翻译为 yyvsp[(1) - (1)].s?

objective-c - 将 Bison/Flex/Yacc 集成到 XCode 中

yacc 中的 Lisp 语法

python - 从 csv 添加特定字段

python - 带 datetime64[ns] 轴的 Matplotlib 多彩线

Python:如何使用argparse在子解析器中具有互斥组?

python - Spacy - 具有不同属性的短语匹配器