我知道這是一個古老的線程,但我只是想一些PEGjs代碼添加到答案。該代碼將解析一段文本並將其「嵌套」爲一種「AST-ish」結構。它只會變得很深,看起來很醜,而且它並不真正使用返回值來創建正確的結構,而是保留語法的內存樹,最後它會返回。這很可能會變得笨拙,並導致一些性能問題,但至少它能做到它應該達到的目標。
注意:請確保您有製表符而不是空格!
{
var indentStack = [],
rootScope = {
value: "PROGRAM",
values: [],
scopes: []
};
function addToRootScope(text) {
// Here we wiggle with the form and append the new
// scope to the rootScope.
if (!text) return;
if (indentStack.length === 0) {
rootScope.scopes.unshift({
text: text,
statements: []
});
}
else {
rootScope.scopes[0].statements.push(text);
}
}
}
/* Add some grammar */
start
= lines: (line EOL+)*
{
return rootScope;
}
line
= line: (samedent t:text { addToRootScope(t); }) &EOL
/line: (indent t:text { addToRootScope(t); }) &EOL
/line: (dedent t:text { addToRootScope(t); }) &EOL
/line: [ \t]* &EOL
/EOF
samedent
= i:[\t]* &{ return i.length === indentStack.length; }
{
console.log("s:", i.length, " level:", indentStack.length);
}
indent
= i:[\t]+ &{ return i.length > indentStack.length; }
{
indentStack.push("");
console.log("i:", i.length, " level:", indentStack.length);
}
dedent
= i:[\t]* &{ return i.length < indentStack.length; }
{
for (var j = 0; j < i.length + 1; j++) {
indentStack.pop();
}
console.log("d:", i.length + 1, " level:", indentStack.length);
}
text
= numbers: number+ { return numbers.join(""); }
/txt: character+ { return txt.join(""); }
number
= $[0-9]
character
= $[ a-zA-Z->+]
__
= [ ]+
_
= [ ]*
EOF
= !.
EOL
= "\r\n"
/"\n"
/"\r"
我不熟悉Citrus和Treetop,但雖然PEG.js是一個整潔的小工具,但對於這種類型的解釋IMO來說太短了。另外,我不認爲有人會發佈一個(相當)簡單的語法文件(帶有嵌入的動作)能夠解釋您描述的這種語言,因爲除了定義語法之外,還有相當多的代碼涉及:走AST,將數據保存在不同的作用域,在作用域中解析變量,甚至可能在彈出作用域時發現某個變量。 – 2010-11-18 21:17:08
P.S.你以某種方式問你的問題,就好像你自己有答案一樣。這是一個真正的問題,還是一個難題?如果這是一個真正的問題,我建議你給[語言實現模式:創建你自己的特定領域和一般編程語言](http://www.pragprog.com/titles/tpdsl/language-implementation-patterns)一試:它還解釋瞭如何像Python一樣的語言可以被解釋(至少是「縮進」部分,就是這樣)。 – 2010-11-19 07:02:44
嗨巴特,感謝您的書鏈接。 Unfortunatley我沒有答案。我意識到,爲上述示例中給出的語言創建解釋器並不是微不足道的,但這不是我期望的。我只對如何處理縮進部分/解析問題感興趣。事實上,我可以編寫一個手寫解析器來跟蹤縮進級別,但是我不知何故將失敗的概念映射到PEG上。任何幫助表示讚賞。 Matt – Matt 2010-11-20 12:11:48