2016-11-09 99 views
2

我的问题受this one的启发,但对于JavaScript,使用parsimmon解析器 - 组合器库。我想解析缩进敏感的语言,比如python或yaml。使用parsimmon库解析基于缩进的语言

我已经成功地斯卡拉例如转换在回答JavaScript的很轻松了 - 关键是在parsimmon的chain功能,这相当于在Scala的解析器组合的>>运营商 - 他们都需要一个解析器和功能它返回一个解析器,并且第一个解析器的结果被传递给该函数以选择下一个解析器。

但是,我不能完全包围我的头如何使这个递归。这个例子是针对单个块的 - 我不知道如何创建嵌套块,根据需要跟踪去除级别来解析python之类的东西。

+0

我知道我们鼓励代码示例,但我到目前为止在这花了6小时,没有代码,甚至部分还没有工作... – Josh

回答

1

那么,这里有一个方法来做到这一点。这可能不是最好的,它绝对不是直觉(我不知道我明白它为什么可行,而我写道它:)但它似乎是相当强大的。

基本上它说:一个tree是一个line可选后跟一个block。 A block又是tree s的缩进列表。

indent是一个函数,它接受当前缩进级别,并返回一个需要缩进的分析器。返回的解析器返回一堆以前的缩进级别。

我之前说过它很健壮 - 实际上,它的也是健壮。它接受真正应该抛出错误的输入:如果您取消缩进与之前的级别不匹配的缩进级别,它基本上会“向上舍入”到下一个缩进级别。我不确定要修复的逻辑应该在哪里进行 - 相互递归与解析器“链”混合在一起很难遵循!

var {regex, seq} = Parsimmon; 
 

 
function tree(state) { 
 
    return seq(
 
     line, 
 
     block(state).atMost(1).map(x => x[0]? x[0] : []) 
 
    ); 
 
} 
 

 
function block(state) { 
 
    return indent(state) 
 
     .chain(tree).atLeast(1); 
 
} 
 

 
function indent(state) { 
 
    return regex(/\s/).atLeast(state + 1) 
 
     .map(x => x.length) 
 
     .desc('indent'); 
 
} 
 

 
let item = regex(/[^\s].*/).desc('item'); 
 
let line = item.skip(regex(/\n?/)); 
 
let start = block(-1); 
 

 
let result = start.parse('top item 1\n sub item 1\n sub item 2\n' + 
 
    ' even deeper\n sub item 3\ntop item 2'); 
 
console.log(JSON.stringify(result['value'], null, 2));
<script src="https://cdn.rawgit.com/jneen/parsimmon/master/src/parsimmon.js"></script>