我想为包含几个部分的文件创建一个语法(如下面的PARAGRAPH).
一个部分以其关键字(例如PARAGRAPH)开头,后跟一个标题(此处为标题),其内容在以下行中,一行内容是该部分的一行.它就像一个带有标题,列和行的表.
在下面的示例(tablefile)中,我将限制部分包含一列和一行.
tablefile := paragraph*
paragraph := PARAGRAPH title CR
TAB content
title, content := \w+
Run Code Online (Sandbox Code Playgroud)
由于我需要处理换行符和表格,我需要将默认空格设置为''.
def grammar():
'''
Bottom-up grammar definition
'''
ParserElement.setDefaultWhitespaceChars(' ')
TAB = White("\t").suppress()
CR = LineEnd().setName("Carriage Return").suppress()
PARAGRAPH = 'PARAGRAPH'
title = Word(alphas)
content = Word(alphas)
paragraph = (PARAGRAPH + title + CR
+ TAB + content)
tablefile = OneOrMore(paragraph)
tablefile.parseWithTabs()
return tablefile
Run Code Online (Sandbox Code Playgroud)
这个虚拟示例很容易匹配:
PARAGRAPH someTitle
thisIsContent
Run Code Online (Sandbox Code Playgroud)
另外这个:
PARAGRAPH someTitle
thisIsContent
PARAGRAPH otherTitle
thisIsOtherContent
Run Code Online (Sandbox Code Playgroud)
它PARAGRAPH在第一个内容之后等待,并偶然发现换行符(记住setDefaultWhitespaceChars(' ')).我是否被迫CR?在最后添加paragraph?什么是忽略这种最后换行符的更好方法?
此外,我想允许选项卡和空格在文件中的任何位置没有干扰.唯一需要的行为是启动一个段落内容TAB,并PARAGRAPH开始该行.这也意味着在段落中和段落之间跳过空白行(带有制表符和空格或没有).
因此我添加了这一行:
tablefile.ignore(LineStart() + ZeroOrMore(White(' \t')) + LineEnd())
Run Code Online (Sandbox Code Playgroud)
但是我刚刚曝光的每一个要求似乎都违背了我设置默认空格的需要,' '并让我陷入了死胡同.
实际上,这会导致一切崩溃:
tablefile.ignore(CR)
tablefile.ignore(TAB)
Run Code Online (Sandbox Code Playgroud)
如果我想在文本中的任何地方\t被忽略, wherever in the text but at the start of lines.
I will have to add them to the default white space characters.
Thus, I have found a way to forbid every white space character at the start of the line.
By using leaveWhitespace方法.此方法在匹配令牌之前保留它遇到的空格.因此,我可以将一些令牌粘贴到行首.
ParserElement.setDefaultWhitespaceChars('\t ')
SOL = LineStart().suppress()
EOL = LineEnd().suppress()
title = Word()
content = Word()
PARAGRAPH = Keyword('PARAGRAPH').leaveWhitespace()
TAB = Literal('\t').leaveWhitespace()
paragraph = (SOL + PARAGRAPH + title + EOL
+ SOL + TAB + content + EOL)
Run Code Online (Sandbox Code Playgroud)
有了这个解决方案,我在文本中的任何地方都用TAB解决了我的问题.
delimitedList经过一番思考后,我达到了PaulMcGuire()的解决方案.我遇到了一些问题.
实际上,这里有两种不同的方式来声明两段之间的换行符分隔符.在我看来,它们应该是等价的.在实践中,他们不是?
崩溃测试(如果你运行它,不要忘记用标签更改空格):
PARAGRAPH titleone
content1
PARAGRAPH titletwo
content2
Run Code Online (Sandbox Code Playgroud)
两个例子之间的共同点:
ParserElement.setDefaultWhitespaceChars('\t ')
SOL = LineStart().suppress()
EOL = LineEnd().suppress()
title = Word()
content = Word()
PARAGRAPH = Keyword('PARAGRAPH').leaveWhitespace()
TAB = Literal('\t').leaveWhitespace()
Run Code Online (Sandbox Code Playgroud)
第一个例子,工作一个:
paragraph = (SOL + PARAGRAPH + title + EOL
+ SOL + TAB + content + EOL)
tablefile = ZeroOrMore(paragraph)
Run Code Online (Sandbox Code Playgroud)
第二个例子,不工作:
paragraph = (SOL + PARAGRAPH + title + EOL
+ SOL + TAB + content)
tablefile = delimitedList(paragraph, delim=EOL)
Run Code Online (Sandbox Code Playgroud)
它们不应该是等价的吗?第二次提出异常:
Expected end of text (at char 66), (line:4, col:1)
这对我来说不是一个大问题,因为我终于可以退出,将EOL放在我语法的每个段落的末尾.但我想突出这一点.
我的另一个要求是忽略空行,包含空格(' \t').
一个简单的语法是:
ParserElement.setDefaultWhitespaceChars(' \t')
SOL = LineStart().suppress()
EOL = LineEnd().suppress()
word = Word('a')
entry = SOL + word + EOL
grammar = ZeroOrMore(entry)
grammar.ignore(SOL + EOL)
Run Code Online (Sandbox Code Playgroud)
最后,文件每行可以包含一个单词,任何地方都有空格.它应该忽略空白行.
幸运的是,确实如此.但它不受默认空格声明的影响.包含空格或制表符的空行将导致解析器引发解析异常.
这种行为绝对不是我所期望的.是指定的吗?这个简单的尝试有没有错误?
我可以在这个帖子中看到PaulMcGuire并没有试图忽略空行,而是在类似makefile的语法分析器(NL = LineEnd().suppress())中对它们进行标记化.
makefile_parser = ZeroOrMore( symbol_assignment
| task_definition
| NL )
Run Code Online (Sandbox Code Playgroud)
我现在唯一的解决方案是预处理文件并删除空白行中包含的空格,因为pyparsing正确地忽略空白行,其中没有空格.
import os
preprocessed_file = os.tmpfile()
with open(filename, 'r') as file:
for line in file:
# Use rstrip to preserve heading TAB at start of a paragraph line
preprocessed_file.write(line.rstrip() + '\n')
preprocessed_file.seek(0)
grammar.parseFile(preprocessed_file, parseAll=True)
Run Code Online (Sandbox Code Playgroud)
您的 BNF 仅包含 CR,但您解析代码以使用 LF 终止。这是故意的吗?BNF 支持LF (Unix)、CR (Mac) 和 CRLF (Win) EOL:
Rule_|_Def.__|_Meaning___
CR | %x0D | carriage return
LF | %x0A | linefeed
CRLF | CR LF | Internet standard newline
Run Code Online (Sandbox Code Playgroud)