给定 Rascal 中的具体语法,如何生成语言的任意实例?

问题描述

给定一种语言的具体语法,我想定义一个带有签名的函数“实例”str(type[&T]),可以用语法的具体化类型调用并返回语言的有效实例。

例如,使用以下语法:

lexical IntegerLiteral = [0-9]+;           

start Syntax Exp            
  = IntegerLiteral          
  | bracket "(" Exp ")"     
  > left Exp "*" Exp        
  > left Exp "+" Exp        
  ;

instance(#Exp) 的有效返回值可能是“1+(2*3)”。

具体语法定义的具体化类型确实包含有关产生式的信息,但我不确定这种方法是否比专用数据结构更好。关于如何实现它的任何指示?

解决方法

最自然的做法是使用标准库中 Tree 模块中的 ParseTree 数据类型。它是解析器生成的格式,但您也可以自己使用它。要从树中获取字符串,只需将其打印在字符串中,如下所示:

str s = "";

可以在这里找到一个相对完整的随机树生成器:https://github.com/cwi-swat/drambiguity/blob/master/src/GenerateTrees.rsc

实现的核心是这样的:

Tree randomChar(range(int min,int max)) = char(arbInt(max + 1 - min) + min);

Tree randomTree(type[Tree] gr) 
  = randomTree(gr.symbol,toMap({ <s,p> | s <- gr.definitions,/Production p:prod(_,_,_) <- gr.definitions[s]}));

Tree randomTree(\char-class(list[CharRange] ranges),int rec,map[Symbol,set[Production]] _)
  = randomChar(ranges[arbInt(size(ranges))]);

default Tree randomTree(Symbol sort,set[Production]] gr) {
   p = randomAlt(sort,gr[sort],rec);  
   return appl(p,[randomTree(delabel(s),rec + 1,gr) | s <- p.symbols]);
}

default Production randomAlt(Symbol sort,set[Production] alts,int rec) {
  int w(Production p) = rec > 100 ?  p.weight * p.weight : p.weight;
  int total(set[Production] ps) = (1 | it + w(p) | Production p <- ps);
  
  r = arbInt(total(alts));
  
  count = 0;
  for (Production p <- alts) {
    count += w(p);
    if (count >= r) {
      return p;
    }
  } 
  
  throw "could not select a production for <sort> from <alts>";
}

Tree randomChar(range(int min,int max)) = char(arbInt(max + 1 - min) + min);

这是一个简单的递归函数,它从具体化的语法中随机选择产生式。

终止的诀窍在于每条规则的 weight。这是先验计算的,这样每个规则在随机选择中都有自己的权重。我们注意给导致终止的规则集至少有 50% 的机会被选中(与递归规则相反)(此处代码:https://github.com/cwi-swat/drambiguity/blob/master/src/Termination.rsc

Grammar terminationWeights(Grammar g) { 
   deps = dependencies(g.rules);
   weights = ();
   recProds = {p | /p:prod(s,[*_,t,*_],_) := g,<delabel(t),delabel(s)> in deps};
   
   for (nt <- g.rules) {
      prods       = {p | /p:prod(_,_) := g.rules[nt]};
      count       = size(prods);
      recCount    = size(prods & recProds);
      notRecCount = size(prods - recProds);
      
      // at least 50% of the weight should go to non-recursive rules if they exist
      notRecWeight = notRecCount != 0 ? (count * 10) / (2 * notRecCount) : 0;
      recWeight = recCount != 0 ? (count * 10) / (2 * recCount) : 0;
      
      weights += (p : p in recProds ? recWeight : notRecWeight | p <- prods); 
   }
       
   return visit (g) { 
       case p:prod(_,_) => p[weight=weights[p]]
   }
}

@memo 
rel[Symbol,Symbol] dependencies(map[Symbol,Production] gr) 
  = {<delabel(from),delabel(to)> | /prod(Symbol from,[_*,Symbol to,_*],_) := gr}+;

请注意,这种 randomTree 算法不会在非“生产性”的语法上终止(即它们只有像 syntax E = E; 之类的规则

它还可以生成通过消歧规则过滤的树。因此,您可以通过在生成的字符串上运行解析器并检查解析错误来检查这一点。它也可以生成不明确的字符串。

顺便说一下,这段代码的灵感来自伦敦国王学院 Naveneetha Vasudevan 的博士论文。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...