2016-11-09 46 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>