如何从BNF生成随机程序

时间:2018-04-26 06:23:47

标签: python parsing code-generation bnf

我知道我的问题听起来有点模糊,但我在网上找不到任何教程。我不是要求答案,而是要求更多解释。 BNF的一个例子:

<prog> ::= “int main() { <stat_list> return 0; }”
<stat_list>  ::= <stat>
         | <stat_list> <stat>
<stat>       ::= <cmpd_stat>
         | <if_stat>
         | <iter_stat>
         | <assgn_stat>
         | <decl_stat>
<cmpd_stat>  ::= { <stat_list> }
<if_stat>    ::= if ( <exp> ) <stat>
         | if ( <exp> ) <cmpd_stat>
         | if ( <exp> ) <stat> else <stat>
         | if ( <exp> ) <cmpd_stat> else <stat>
         | if ( <exp> ) <stat> else <cmpd_stat>
         | if ( <exp> ) <cmpd_stat> else <cmpd_stat>

将此转换为python以使我的程序使用上述条件创建随机程序的最简单方法是什么?任何有用网站链接的帮助都将不胜感激。

2 个答案:

答案 0 :(得分:3)

NLTK有一个grammars的包。通常用于句子分析,但没有什么能阻止你使用它来创建一个&#34;程序&#34;遵循该规则。

我认为NLTK只允许你定义一个Context Free Grammar,所以我在这里给你留下一个小例子:

from nltk import CFG
from nltk.parse.generate import generate

#Define your grammar from string
#You can define it using other methods, but I only know this xD

grammar = CFG.fromstring(""" S -> NP VP
  VP -> V NP
  V -> "mata" | "roba"
  NP -> Det N | NP NP
  Det -> "un" | "el" | "con" | "a" | "una"
  N -> "bebé" | "ladrón" | "Obama" | "perrete" | "navastola" | "navaja" | "pistola" """)

''' This grammar creates sentences like:
        El bebé roba a Obama
        Baby steals Obama (in spanish)
'''
#With this we "create" all the possible combinations
grammar.productions()

#Here you can see all the productions (sentences) with 5 words
#created with this grammar
for production in generate(grammar, depth=5):
    print(' '.join(production))

答案 1 :(得分:2)

您可以通过滥用解析器将其转换为生成器来实现此目的。

首先,为您的语言构建一个递归解析器。 (See my SO answer on how to do just that)。当你读到时,暂停 ......我现在假设你明白该怎么做。

你会注意到,这样的解析器充满了来自解析器函数的一个语法规则的调用,以及其他语法规则或原始令牌匹配器的其他函数。

您要做的是修改每个调用,以确定在调用之前,如果函数中仍有某些替代方法,它将以低概率返回“true”。如果调用决定为false,则控件只会传递给解析器的另一部分。如果一个电话决定为真,它实际上是通话;被调用者现在必须以一种返回true并生成相应源代码的方式行事。在某些时候,这将强制调用令牌读取器返回true;令牌读取器被发出随机令牌的打印功能所取代。当你这样做时实际发生的事情是,决定某事是否真实的电话现在只是变成电话;我们不再需要返回状态,因为被调用的函数必须返回true。这会将我们的函数 - returns-bools更改为procedures-returning-void。请参阅下面的示例..

让我们尝试使用这个简单语法的示例来编写一个简单的编程语言 p

p = s ;
s = v '=' e ;
s = 'if' e 'then' s ;
e = v ;
e = v '+' n ;

好的, p 的递归下降解析器(我不是Python人,所以这是伪代码):

function p() { return s(); } // no alternatives
function s() { if v()
               then if match("=")
                    then return e()
                    else return false;
               else if match("if")
                    then if e()
                         then if match("then")
                              then return s()
                              else return false;
                         else return false;
                    else return false;
              }
 function e() { if v()
                then if match ("+")
                     then if n()
                     else return true
                else return false
              }
 function v() { return match_variable_name(); }
 function n() { return match_integer_constant(); }

好的,现在让我们强制调用使用随机返回true或false的硬币翻转函数来判断它们是否会成功。任何形式的构造:

          if <testsomething> then <action x> else <action y>

变成了:

          if flip() then  { <testsomething> <action x> } else <action y>

以及任何形式的构造:

          if  <testsomething> then <action x> else return false

变成了

          { <testsomething>; <action x> }

因为如果要生成可解析的程序,它必须成功。

如果 testsomething 是对另一个语法规则的函数调用,我们就不管它了。对原始令牌匹配的函数调用变为print语句:如果 testsomething 是“match(Q)”, 然后用“print(Q)”替换它;这就是实际生成程序的一部分。

procedure p() { s(); } // no choice, this has to succeed
procedure s() { if flip() // flip == true --> v must succeed
               then { v();
                      print("=") // because if no match, procedure fails
                      e();
                    }
               else { print("if")  // if we get here, must succeed
                      e();
                      print("then"); // because match("then") must succeed
                      s();
                    }
              }
 procedure e() { v(); // because there are no alternatives
                 if flip() then { print("+");
                                  n();
                                }
                 else { }
               }
 procedure v() { print_variable_name(); }
 procedure n() { print_integer_constant(); }

请注意,变量名和整数常量的令牌识别器现在成为打印随机变量名/常量的打印过程。这基本上只是推动“翻转”进入这些程序。

现在这可能会打印任意长的程序,因为翻转可能会强制s重复调用自身。如果翻转是50-50,你有可能在1000中的1次递归,所以可能没问题。但是,您可能会根据到目前为止生成的输出的大小或任何递归的深度来决定偏置每个单独的翻转以选择较短的短语。

现在,在一般情况下,这不会产生语义正确的程序。那是因为我们的解析器是“无上下文”;对其他部分强制生成的代码的一部分没有约束。例如,如果您的语言在使用之前必须声明变量,则此方案不保证在表达式中出现randome-var-X之前将生成random-var-X的声明。

没有简单的方法可以解决这个问题,因为语言语义并不是“容易”的。只是表明解析一个程序(“技术上容易”)并检查正确的语义(“任意硬”,考虑C ++),导致任何同样难以生成的随机程序不会违反语言语义的问题。