我正在尝试用PLY编写一个语法来解析文件中的路径。我正在进行转换减少冲突,我不知道如何更改语法来修复它。 这是我正在尝试解析的文件的示例。路径/文件名可以是任何可接受的linux路径。
file : ../../dir/filename.txt
file : filename.txt
file : filename
所以这是我写的语法。
header : ID COLON path
path : pathexpr filename
pathexpr : PERIOD PERIOD DIVIDE pathexpr
| PERIOD DIVIDE pathexpr
| ID DIVIDE pathexpr
|
filename : ID PERIOD ID
| ID
这是我的代币。我正在使用PLY包含的ctokens库。只是为了省下自己的写作。
t_ID = r'[A-Za-z_][A-Za-z0-9_]*'
t_PERIOD = r'\.'
t_DIVIDE = r'/'
t_COLON = r':'
所以我相信“文件名”规则中存在一个减少冲突的冲突,因为解析器不知道是将令牌缩减为“ID”还是转移为“ID PERIOD ID”。我认为在没有路径(“filename”)的情况下还有另一个问题,它将使用pathexpr中的令牌而不是减少为空。
如何修复语法来处理这些案例?也许我需要更换我的代币?
答案 0 :(得分:0)
我认为您可能正在使用PLY,而不是pyparsing,查看那些“t_xxx”名称。但是这里有一个针对您的问题的pyparsing解决方案,请参阅下面的有用评论:
"""
header : ID COLON path
path : pathexpr filename
pathexpr : PERIOD PERIOD DIVIDE pathexpr
| PERIOD DIVIDE pathexpr
| ID DIVIDE pathexpr
|
filename : ID PERIOD ID
| ID
"""
from pyparsing import *
ID = Word(alphanums)
PERIOD = Literal('.')
DIVIDE = Literal('/')
COLON = Literal(':')
# move this to the top, so we can reference it in a negative
# lookahead while parsing the path
file_name = ID + Optional(PERIOD + ID)
# simple path_element - not sufficient, as it will consume
# trailing ID that should really be part of the filename
path_element = PERIOD+PERIOD | PERIOD | ID
# more complex path_element - adds lookahead to avoid consuming
# filename as a part of the path
path_element = (~(file_name + WordEnd())) + (PERIOD+PERIOD | PERIOD | ID)
# use repetition for these kind of expressions, not recursion
path_expr = path_element + ZeroOrMore(DIVIDE + path_element)
# use Combine so that all the tokens will get returned as a
# contiguous string, not as separate path_elements and slashes
path = Combine(Optional(path_expr + DIVIDE) + file_name)
# define header - note the use of results names, which will allow
# you to access the separate fields by name instead of by position
# (similar to using named groups in regexp's)
header = ID("id") + COLON + path("path")
tests = """\
file: ../../dir/filename.txt
file: filename.txt
file: filename""".splitlines()
for t in tests:
print t
print header.parseString(t).dump()
print
打印
file: ../../dir/filename.txt
['file', ':', '../../dir/filename.txt']
- id: file
- path: ../../dir/filename.txt
file: filename.txt
['file', ':', 'filename.txt']
- id: file
- path: filename.txt
file: filename
['file', ':', 'filename']
- id: file
- path: filename
答案 1 :(得分:0)
简单的解决方案:使用左递归而不是右递归。
LR解析器(如PLY和yacc)更喜欢左递归,因为它避免了必须扩展解析器堆栈。它通常也更接近表达式的语义 - 当你想要实际解释语言时,它很有用,而不仅仅是识别它 - 而且通常,就像在这种情况下一样,它避免了对左因子的需要。 / p>
在这种情况下,例如,通过查找当前找到的目录中的段目录,需要将每个路径段应用于前面的pathexpr
。解析器操作很明确:在$ 1中查找$ 2。你如何为正确的递归版本采取行动?
所以,一个简单的转变:
header : ID COLON path
path : pathexpr filename
pathexpr : pathexpr PERIOD PERIOD DIVIDE
| pathexpr PERIOD DIVIDE
| pathexpr ID DIVIDE
|
filename : ID PERIOD ID
| ID
答案 2 :(得分:0)
我相信这个语法应该可行,并且它还具有能够识别路径部分的附加优势,如扩展,目录,驱动器等。 我还没有制作解析器,只有这个语法。
fullfilepath : path SLASH filename
path : root
| root SLASH directories
root : DRIVE
| PERCENT WIN_DEF_DIR PERCENT
directories : directory
| directory SLASH directories
directory : VALIDNAME
filename : VALIDNAME
| VALIDNAME DOT EXTENSION