所以我已经通过json生成了一个解析器:
// mygenerator.js
var Parser = require("jison").Parser;
// a grammar in JSON
var grammar = {
"lex": {
"rules": [
["\s+", "/* skip whitespace */"],
["[a-f0-9]+", "return 'HEX';"]
]
},
"bnf": {
"hex_strings" :[ "hex_strings HEX",
"HEX" ]
}
};
// `grammar` can also be a string that uses jison's grammar format
var parser = new Parser(grammar);
// generate source, ready to be written to disk
var parserSource = parser.generate();
// you can also use the parser directly from memory
// returns true
parser.parse("adfe34bc e82a");
// throws lexical error
parser.parse("adfe34bc zxg");
我的问题是,我现在如何检索AST ?我可以看到我可以对输入运行解析器,但如果它工作,它只返回true,否则失败。
为了记录,我使用JISON: http://zaach.github.com/jison/docs/
我发现了一个比另一个答案更简单、更简洁的方法。
这篇文章分为两个部分:
- 通用方式:阅读如何实现我的方式。
- 实际答案:前面描述的特定于OP请求的方式的实现。
一般方式
-
在开始规则中添加return语句。
的例子:
start : xyz EOF {return $1;} ;
xyz
是另一个产生规则。$1
访问关联的产生规则的第一个符号(终端或非终端)的值。以上代码中$1
包含xyz
的结果 -
将
$$ = ...
语句添加到所有其他规则中。警告:使用
$$ = ...
,不要使用return
!如名称所示,return
将返回指定的值,立即中止进一步的执行。的例子:
multiplication : variable '*' variable {$$ = { type: 'multiplication', arguments: [ $1, $3 ] }; } ;
上面的生成规则将把对象
$$
传递给更高层(即使用该规则的生成规则)。让我们来补充乘法规则,以实现一个可运行的示例:
/* lexical grammar */ %lex %% s+ /* skip whitespace */ [0-9]+("."[0-9]+)?b return 'NUMBER' [a-zA-Z]+ return 'CHARACTER' "*" return '*' <<EOF>> return 'EOF' . return 'INVALID' /lex %start start %% /* language grammar */ start : multiplication EOF {return $1;} ; multiplication : variable '*' variable {$$ = { type: 'multiplication', arguments: [ $1, $3 ] }; } ; variable : 'NUMBER' {$$ = { type: 'number', arguments: [$1] }; } | 'CHARACTER' {$$ = { type: 'character', arguments: [$1] }; } ;
你可以在网上试试:http://zaach.github.io/jison/try/。在本文编辑时(2017年2月12日),在线生成器不幸抛出了一个错误——与您输入的json文件无关。有关如何在本地机器上生成解析器的提示,请参阅步骤3后的附录。
如果你输入例如
a*3
,你得到的对象结构如下:{ "type": "multiplication", "arguments": [ { "type": "character", "arguments": ["a"] }, { "type": "number", "arguments": ["3"] } ] }
-
通过注入自定义对象来清理代码和生成的AST
当使用json生成的解析器时,你可以在语法文件中的'代码块'范围内注入任意对象:
const MyParser = require('./my-parser.js'); MyParser.parser.yy = { MultiplicationTerm /*, AdditionTerm, NegationTerm etc. */ }; let calculation = MyParser.parse("3*4"); // Using the modification below, calculation will now be an object of type MultiplicationTerm
如果
MultiplicationTerm
有一个接受两个因子的构造函数,那么乘法的新部分将是这样的:multiplication : variable '*' variable {$$ = new yy.MultiplicationTerm($1, $3);} ;
关于如何创建json解析器的附录:
下载Jison NPM模块然后你可以通过使用Jison的命令行或在构建文件中运行new jison.Generator(fileContents).generate()
来创建json解析器,并将返回的字符串写入你喜欢的文件,例如my-parser.js
。
应用上面的规则会得到下面的json文件。
据我所知,json文件格式和JavaScript API(如问题中所述)是可以互换的。
还请注意,这个json文件只生成一个平面树(即列表),因为输入格式也只是一个列表(或者如何以逻辑方式嵌套连接的十六进制字符串?)。
/* lexical grammar */
%lex
%%
s+ /* skip whitespace */
[a-f0-9]+ return 'HEX'
<<EOF>> return 'EOF'
. return 'INVALID'
/lex
%start start
%% /* language grammar */
start
: hex_strings EOF
{return $1;}
;
hex_strings
: hex_strings HEX
{$$ = $1.concat([$2]);}
| HEX
{$$ = [$1];}
;
我不太熟悉Jison的内部工作原理,所以我不知道有什么方法可以做到这一点。
但是如果你想用一些蛮力来解决这个问题,试试这个:
首先,创建一个对象来保存ASTfunction jisonAST(name, x) { this.name = name; this.x = x; }
// return the indented AST
jisonAST.prototype.get = function(indent){
// create an indentation for level l
function indentString(l) { var r=""; for(var i=0;i<l;i++){r+=" "}; return r }
var r = indentString(indent) + "["+this.name+": ";
var rem = this.x;
if( rem.length == 1 && !(rem[0] instanceof jisonAST) ) r += "'"+rem[0]+"'";
else for( i in rem ){
if( rem[i] instanceof jisonAST ) r += "n" + rem[i].get(indent+1);
else { r += "n" + indentString(indent+1); r += "'"+rem[i]+"'"; }
}
return r + "]";
}
为Jison的BNF添加一个小的辅助函数
function o( s ){
r = "$$ = new yy.jisonAST('"+s+"',[";
for( i = 1; i <= s.split(" ").length; i++ ){ r += "$"+i+"," }
r = r.slice(0,-1) + "]);";
return [s,r];
}
这样,继续执行示例代码(稍作修改):
var Parser = require("jison").Parser;
// a grammar in JSON
var grammar = {
"lex": {
"rules": [
["\s+", "/* skip whitespace */"],
["[a-f0-9]+", "return 'HEX';"]
]
},
"bnf": {
// had to add a start/end, see below
"start" : [ [ "hex_strings", "return $1" ] ],
"hex_strings" :[
o("hex_strings HEX"),
o("HEX")
]
}
};
var parser = new Parser(grammar);
// expose the AST object to Jison
parser.yy.jisonAST = jisonAST
现在您可以尝试解析:
console.log( parser.parse("adfe34bc e82a 43af").get(0) );
这将给你:
[hex_strings HEX:
[hex_strings HEX:
[HEX: 'adfe34bc']
'e82a']
'43af']
小提示:我必须添加一个"start"规则,以便只有一条语句返回结果。它不干净(因为BNF没有它也能正常工作)。