Compiler construction 如何从正式语法生成句子?
从语法生成句子的常用方法是什么? 我想要一种与解析器相反的算法。也就是说,给定一个正式的上下文无关语法(比如LL),我想生成一个符合该语法的任意句子。我在这里用句子来表示任何有效的文本体,所以它实际上可以是一个完整的程序(即使它没有任何意义,只要它在语法上是正确的) 语法示例:Compiler construction 如何从正式语法生成句子?,compiler-construction,computer-science,grammar,parsing,Compiler Construction,Computer Science,Grammar,Parsing,从语法生成句子的常用方法是什么? 我想要一种与解析器相反的算法。也就是说,给定一个正式的上下文无关语法(比如LL),我想生成一个符合该语法的任意句子。我在这里用句子来表示任何有效的文本体,所以它实际上可以是一个完整的程序(即使它没有任何意义,只要它在语法上是正确的) 语法示例: program : <imports> NEWLINE? <namespace> imports : ("import" <identifier> NEWLINE)* nam
program : <imports> NEWLINE? <namespace>
imports : ("import" <identifier> NEWLINE)*
namespace : "namespace " <identifier> NEWLINE "{" <classes> "}"
identifier: (A-Za-z_) (A-Za-z0-9_)*
...
不是答案,但请查看有关语法生成的wikipedia条目:
它描述了一些常用的算法。我的第一个建议是广度优先搜索。只需建立一个规则图并搜索它们。您将开始从尽可能小的程序开始吐出程序,然后慢慢变大。不过,您可能会发现,对于给定数量的规则,您的语法将以指数形式吐出更多的程序,并且在使用DFS的程序中,您可能无法获得超过30个左右的令牌 深度优先搜索的问题是,第二次使用左递归规则时,搜索将陷入无限循环 另一个大问题是语法正确的程序与语义正确的程序相去甚远。除了最基本的情况外,生成后一种类型可能在所有情况下都是完全不可行的。虽然这个想法很好(我以前多次考虑过),但现实是,如果没有一些示例数据和/或大量生成器约束/工作限制,这是一项相当大的工作
人们可能会发现手写样本更容易。:) 在我的头顶上: 我会使用一些关于如何处理范围(
(…)
:可能随机选取)选项(?
:参见下文[])、重复(“'Poisson分布?)的启发式方法递归地工作(基本上与递归体面解析器相反)。文本(“…”
)被简单地写入输出,子关键字(`')生成递归
这应该不会太难,除非你想保证某种程度的全面覆盖。即使这样,仅仅生成一堆数据也会有帮助
[*]在处理以下规则时,您需要在少于50%的时间内包含可选项,以防止无限回归
nonterm: otherstuff <nonterm>?
非术语:其他东西?
好的,顺其自然
同样地,通过重复,抛出一个强收敛的分布
如果输入语法以BNF形式呈现,则需要首先对其进行解析。最简单的方法是使用一个映射
(名称、字符串)
,然后从最高级别的标记开始(您可能会认为这意味着第一个标记…)
这将为您提供:
(“节目”、“新线?”)
(“进口”,“进口”新行)*)
当你以“程序”开始时,点击“所以你重现……回来时,点击“新线?”,所以掷骰子,写或不写,点击“所以重现……回来时你完成了”
我发现自己怀疑以前有人这样做过。如果你只需要输出,我会在网上搜索。。。也许,尽管大量的解析器生成器将搜索空间搞得一团糟。。。也试试看
顺便说一句,你们当地的大学可能在线订阅了这些期刊,所以你们可以在图书馆免费获得它们。我不知道有什么“通用”算法可以做到这一点。随机程序生成用于遗传编程,因此您可以寻找基于语法的GP系统,并了解它们如何处理程序生成。我将执行递归规则生成算法,如伪代码:
void GenerateRule(someRule)
{
foreach (part in someRule.Parts)
{
if (part.IsLiteral) OutputLiteral(part);
if (part.IsIdentifier) Output(GenerateIdentifier(part)));
if (part.IsRule) GenerateRule(part.Rule);
}
}
这假设您已经将所有部分读入到某个数据结构中。您还需要处理重复(随机生成重复出现的次数)和可选规则(掷硬币看是否有)
编辑:哦,如果规则有多个选项,您只需选择其中一个选项,并以相同的方式进行处理。因此,如果某个规则是(文字变量),您可以在这两个规则之间随机选择。您将遇到的问题是,图形的递归性质使得您可以生成无限大的正确语法。您可能需要在语法中设置节点类型的散列,并设置允许自己命中该节点的次数和限制。然后首先深入到你的内心内容。你的解决方案应该遵循语法的归纳结构。如何为以下每一项生成随机话语
- 终端符号
- 非终结符
- 右侧序列
- 选择右手边
- 右侧星形闭合
处理无限递归有点冒险。最简单的方法是生成一系列的话语,并保持深度截止。或者,如果你使用像哈斯克尔这样的懒惰语言,你可以生成所有的话语,并根据你的喜好剥离尽可能多的有限的话语(这是一个比原始问题更棘手的问题,但非常有趣)。像往常一样,我建议不要重新发明轮子。我为ARM assembler编写了其中一个,但我对此表示遗憾(软件:实践与经验2007年4月): “回顾过去,本应使用现成的表达式生成器生成随机ARM汇编指令以进行比较。相反,Perl脚本是以增量方式构建的,采用每个ARM指令定义并生成实例。然而,增量内部方法的一个优点是,简单的替换可以检测到删除了简单的bug,bug搜索可以继续递增
void GenerateRule(someRule)
{
foreach (part in someRule.Parts)
{
if (part.IsLiteral) OutputLiteral(part);
if (part.IsIdentifier) Output(GenerateIdentifier(part)));
if (part.IsRule) GenerateRule(part.Rule);
}
}
from nltk import parse_cfg, ChartParser
from random import choice
def produce(grammar, symbol):
words = []
productions = grammar.productions(lhs = symbol)
production = choice(productions)
for sym in production.rhs():
if isinstance(sym, str):
words.append(sym)
else:
words.extend(produce(grammar, sym))
return words
grammar = parse_cfg('''
S -> NP VP
PP -> P NP
NP -> Det N | Det N PP | 'I'
VP -> V NP | VP PP
V -> 'shot' | 'killed' | 'wounded'
Det -> 'an' | 'my'
N -> 'elephant' | 'pajamas' | 'cat' | 'dog'
P -> 'in' | 'outside'
''')
parser = ChartParser(grammar)
gr = parser.grammar()
print ' '.join(produce(gr, gr.start()))