我使用ANTLR(使用访问者的JavaScript目标)编写(自己的lang-> JS)转译器。重点放在目标代码生成中的变体。
[更早的SO post,描述了一种更简单情况的解决方案。这一点不同,主要是由于涉及到递归。
语法:
grammar Alang;
...
variableDeclaration : DECL (varDecBl1 | varDecBl2) EOS;
varDecBl1 : ID AS TYP;
varDecBl2 : CMP ID US (SGST varDecBl1+ SGFN);
...
DECL : 'var-declare' ;
EOS : ';' ;
SGST : 'segment-start:' ;
SGFN : 'segment-finish' ;
AS : 'as';
CMP : 'cmp';
US : 'using';
TYP
: 'str'
| 'int'
| 'bool'
;
ID : [a-zA-Z0-9_]+ ;
需要对两种不同的源代码案例进行不同的处理。
源代码1:
var-declare fooString as str;
其目标需要显示为:var fooString;
源代码2:
var-declare cmp barComplex using
segment-start:
barF1 as int
barF2 as bool
barF3 as str
segment-finish;
此目标必须是:var barComplex = new Map();
(因为简单的var声明无法处理值类型)
代码生成使用:
visitVarDecBl1 = function(ctx) {
this.targetCode += `var ${ctx.getChild(0).getText()};`;
return this.visitChildren(ctx);
};
....
visitVarDecBl2 = function(ctx) {
this.targetCode += `var ${ctx.getChild(1).getText()} = new Map();`;
return this.visitChildren(ctx);
};
([targetCode
合并目标代码)
以上适用于案例1。对于情况2,由于递归使用规则var barComplex = new Map()
而最终超出varDecBl1
,该规则再次调用visitVarDecBl1
代码生成实现。错误的结果:
var barComplex = new Map();
var barF1; // <---- wrong one coming from visitVarDecBl1
var barF2; // <---- wrong
var barF3; // <---- wrong
为了解决这个问题,我想尝试的一种方法是将visitVarDecBl1
设置为父ctx的条件。如果parent = variableDeclaration
,则目标代码= var ${ctx.getChild(0).getText()};
。如果parent = varDecBl2
,请跳过代码生成。
但是我无法在ctx有效负载中找到调用规则,因此无法进行字符串比较。使用ctx.parentCtx
之类的东西给我[378 371 204 196 178 168]
(哈希?)。
欢迎输入。 (包括建议采用更好的方法)
[如果有人手头有类似的情况,我会自己发布答案。我自己提取父规则来处理它。
util = require("util");
...
// parentCtx object -> string
const parentCtxStr = util.inspect(ctx.parentCtx);
// snip out parent rule from payload, e.g.: 'varDecBl2 {…, ruleIndex: 16, …}'
const snip = parentCtxStr.slice(0,parentCtxStr.indexOf("{")-1); // extracts varDecBl2 for you
}
现在使用snip
的值如上所述进行处理。
当然,这种方式建立了有效负载结构的耦合方式。如果这种情况发生改变,将来可能会有突破的危险。我希望通过API使用相同的东西,尽管找不到。