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

How to generate arbitrary instances of a language given its concrete syntax in Rascal?

鉴于一种语言的具体语法,我想定义一个函数“实例”,其签名为 str (type[&T]) 可以用具体化类型调用语法和 return 语言的有效实例。

例如,使用此语法:

lexical IntegerLiteral = [0-9]+;           

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

实例(#Exp)的有效return可以是“1+(2*3)”。

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

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

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, 0, 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, int rec, map[Symbol, 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 的博士论文。