[英]How to add keyword to acorn or esprima parser
I am working on a language that transpiles to javascript and has a similar syntax.我正在研究一种可以转换为 javascript 并具有类似语法的语言。 However I want to include some new type of block statements.
但是我想包含一些新类型的块语句。 For syntax purposes they are the same as an IfStatement.
出于语法目的,它们与 IfStatement 相同。 How can I get esprima or acorn to parse this program
MyStatement {a=1;}
without throwing an error?我怎样才能让 esprima 或 acorn 解析这个程序
MyStatement {a=1;}
而不抛出错误? Its fine if it calls it an IfStatement.如果将其称为 IfStatement,则很好。 I would prefer not to fork esprima.
我不想分叉esprima。
It turns out, that the plugin capabilities of acorn are not really documented.事实证明,acorn 的插件功能并未真正记录在案。 It seems like forking acorn would be the easiest route.
似乎分叉橡子将是最简单的路线。 In this case, it is as simple as searching for occurances of
_if
and following a similar pattern for _MyStatement
.在这种情况下,它就像搜索
_if
并遵循类似的_MyStatement
模式一样简单。
However it is possible to write a plugin to accomplish what I was trying to do.但是,可以编写一个插件来完成我想要做的事情。 It seems a bit of a hack, but here is the code.
这似乎有点黑客,但这里是代码。 The basic steps are:
基本步骤是:
To exend Parse
and add to the list of keywords that will be recognized by the first pass扩展
Parse
并添加到第一遍识别的关键字列表中
Create a TokenType for the new keyword and add it to the Parser.acorn.keywordTypes
, extend parseStatement
so that it processes the new TokenType为新关键字创建一个 TokenType 并将其添加到
Parser.acorn.keywordTypes
,扩展parseStatement
以便它处理新的 TokenType
Create a handler for the new TokenType which will add information to the Abstract Syntax Tree as required by the keyword functionality and also consume tokens using commands like this.expect(tt.parenR)
to eat a '(' or this.parseExpression()
to process an entire expression.为新的 TokenType 创建一个处理程序,它将根据关键字功能的要求将信息添加到抽象语法树中,并使用像
this.expect(tt.parenR)
这样的命令来消耗令牌以吃掉 '(' 或this.parseExpression()
到处理整个表达式。
Here is the code:这是代码:
var program =
`
MyStatement {
MyStatement(true) {
MyStatement() {
var a = 1;
}
}
if (1) {
var c = 0;
}
}
`;
const acorn = require("acorn");
const Parser = acorn.Parser;
const tt = acorn.tokTypes; //used to access standard token types like "("
const TokenType = acorn.TokenType; //used to create new types of Tokens.
//add a new keyword to Acorn.
Parser.acorn.keywordTypes["MyStatement"] = new TokenType("MyStatement",{keyword: "MyStatement"});
//const isIdentifierStart = acorn.isIdentifierStart;
function wordsRegexp(words) {
return new RegExp("^(?:" + words.replace(/ /g, "|") + ")$")
}
var bruceware = function(Parser) {
return class extends Parser {
parse(program) {
console.log("hooking parse.");
//it appears it is necessary to add keywords here also.
var newKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this const class extends export import super";
newKeywords += " MyStatement";
this.keywords = wordsRegexp(newKeywords);
return(super.parse(program));
}
parseStatement(context, topLevel, exports) {
var starttype = this.type;
console.log("!!!hooking parseStatement", starttype);
if (starttype == Parser.acorn.keywordTypes["MyStatement"]) {
console.log("Parse MyStatement");
var node = this.startNode();
return this.parseMyStatement(node);
}
else {
return(super.parseStatement(context, topLevel, exports));
}
}
parseMyStatement(node) {
console.log("parse MyStatement");
this.next();
//In my language, MyStatement doesn't have to have a parameter. It could be called as `MyStatement { ... }`
if (this.type == tt.parenL) {
node.test = this.parseOptionalParenExpression();
}
else {
node.test = 0; //If there is no test, just make it 0 for now (note that this may break code generation later).
}
node.isMyStatement = true; //set a flag so we know that this if a "MyStatement" instead of an if statement.
//process the body of the block just like a normal if statement for now.
// allow function declarations in branches, but only in non-strict mode
node.consequent = this.parseStatement("if");
//node.alternate = this.eat(acornTypes["else"]) ? this.parseStatement("if") : null;
return this.finishNode(node, "IfStatement")
};
//In my language, MyStatement, optionally has a parameter. It can also by called as MyStatement() { ... }
parseOptionalParenExpression() {
this.expect(tt.parenL);
//see what type it is
console.log("Type: ", this.type);
//allow it to be blank.
var val = 0; //for now just make the condition 0. Note that this may break code generation later.
if (this.type == tt.parenR) {
this.expect(tt.parenR);
}
else {
val = this.parseExpression();
this.expect(tt.parenR);
}
return val
};
}
}
process.stdout.write('\033c'); //cls
var result2 = Parser.extend(bruceware).parse(program); //attempt to parse
console.log(JSON.stringify(result2,null,' ')); //show the results.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.