你曾经想过创建自己的编程语言吗?在本文中,我将演示如何使用免费工具和PEG.js解析器生成器快速编写编译为JavaScript的简单语言。
什么是解析器生成器
解析器生成器顾名思义就是一个基于语法、语言规范为程序员生成解析器的程序。用特定的语法写成。在本文中,我们将使用PEG.js解析器生成器,该生成器生成一个JavaScript文件,该文件将解析语言的代码并输出AST。
什么是AST
AST是抽象语法树的缩写。这是以工具可以理解的格式表示代码的方式。我们将使用Esprima格式的AST,这是一个输出AST的JavaScript解析器。
JavaScript代码生成
Esprima语法很酷的是,有一些工具可以根据他们的AST生成代码。一个例子是escodegen,它以Esprima AST作为输入和输出JavaScript代码。
简单PEG.js解析器示例
在这里,我将展示如何为 if 语句创建简单的解析器语法。
PEG.js的语法并不复杂,它由规则的名称组成,而不是从规则中执行和返回的JavaScript的匹配和块。
以下是PEG.js文档提供的简单示例:
{ function makeInteger(o) { return parseInt(o.join(""), 10); }} start = additiveadditive = left:multiplicative "+" right:additive { return left + right; } / multiplicativemultiplicative = left:primary "*" right:multiplicative { return left * right; } / primary primary = integer / "(" additive:additive ")" { return additive; }integer "integer" = digits:[0-9]+ { return makeInteger(digits); }
它可以解析和计算简单的算术表达式示例是
10+2*3
,它计算到16。可以在PEG.js在线工具上测试此解析器。
但是我们需要的不是解释代码,返回一个值,而是返回Esprima AST。要查看Esprima AST的外观,可以检查AST资源管理器,选择Esprima作为输出并键入一些JavaScript。
下面以这样的简单代码为例:
if (foo == "bar") { 10 + 10 10 * 20}
JSON格式的输出如下:
{ "type": "Program", "body": [ { "type": "IfStatement", "test": { "type": "BinaryExpression", "operator": "==", "left": { "type": "Identifier", "name": "foo", "range": [ 4, 7 ] }, "right": { "type": "Literal", "value": "bar", "raw": "\"bar\"", "range": [ 11, 16 ] }, "range": [ 4, 16 ] }, "consequent": { "type": "BlockStatement", "body": [ { "type": "ExpressionStatement", "expression": { "type": "BinaryExpression", "operator": "+", "left": { "type": "Literal", "value": 10, "raw": "10", "range": [ 23, 25 ] }, "right": { "type": "Literal", "value": 10, "raw": "10", "range": [ 28, 30 ] }, "range": [ 23, 30 ] }, "range": [ 23, 30 ] }, { "type": "ExpressionStatement", "expression": { "type": "BinaryExpression", "operator": "*", "left": { "type": "Literal", "value": 10, "raw": "10", "range": [ 34, 36 ] }, "right": { "type": "Literal", "value": 20, "raw": "20", "range": [ 39, 41 ] }, "range": [ 34, 41 ] }, "range": [ 34, 41 ] } ], "range": [ 18, 43 ] }, "alternate": null, "range": [ 0, 43 ] } ], "sourceType": "module", "range": [ 0, 43 ]}
你不需要关心“范围”和“原始”。解析器输出的部分。
让我们把JSON分成它的一部分:
if语句
if语句需要格式:
{ "type": "IfStatement", "test": { }, "consequent": { }, "alternate": null}
其中“测试”和“后果”是任何表达式:
if语句条件
条件可以是任何表达式,但在这里我们将有一个二进制表达式,可以比较两件事:
{ "type": "BinaryExpression", "operator": "==", "left": {}, "right": {}}
变量
变量使用率如下:
{ "type": "Identifier", "name": "foo"}
字面字符串
我们代码中使用的字面字符串如下:
{ "type": "Literal", "value": "bar"}
用支架块
if里面的块是这样的创建:
{ "type": "BlockStatement", "body": [ ]}
整个程序
而整个程序都是这样创建的:
{ "type": "Program", "body": [ ]}
PEG 解析器,用于编译为 JavaScript 的自己的语言
对于我们的演示语言,我们将创建类似于这样的代码:
if foo = "bar" then 10 + 10 10 * 20end
我们将创建 AST,然后创建 JavaScript 代码。
if的语法如下所示:
if = "if" _ expression:(comparison / expression) _ "then" body:(statements / _) _ "end" { return { "type": "IfStatement", "test": expression, "consequent": { "type": "BlockStatement", "body": body }, "alternate": null };}
我们有“if”令牌,那么比较或表达式,正文是语句或空格。
比较看起来是这样的:
comparison = _ left:expression _ "==" _ right:expression _ { return { "type": "BinaryExpression", "operator": "==", "left": left, "right": right };}
表达是这样的:
expression = expression:(variable / literal) { return expression; }
变量由三条规则创建:
variable = !keywords variable:name { return { "type": "Identifier", "name": variable }} keywords = "if" / "then" / "end" name = [A-Z_$a-z][A-Z_a-z0-9]* { return text(); }
现在我们来看下语句:
statements = _ head:(if / expression_statement) _ tail:(!"end" _ (if / expression_statement))* { return [head].concat(tail.map(function(element) { return element[2]; })); } expression_statement = expression:expression { return { "type": "ExpressionStatement", "expression": expression };}
最后一件事是字面意思:
literal = value:(string / Integer) { return {"type": "Literal", "value": value };} string = "\"" ([^"] / "\\\\\"")* "\"" { return JSON.parse(text());}Integer "integer" = _ [0-9]+ { return parseInt(text(), 10); }
这就是生成 AST 的整个解析器。在我们有了Esprima AST后,我们要做的就是使用escodegen生成代码。
生成 AST 并创建 JavaScript 代码的代码如下:
const ast = parser.parse(code);const js_code = escodegen.generate(ast);
解析器变量是使用PEG.js生成解析器时给出的名称。
这是我用来编写解析器的简单演示,可以使用语法,并为编译成JavaScript的自己的编程语言生成不同的语法。
解析器生成器演示
这个简单的应用程序将代码保存在LocalStorage中,如果编译时没有错误,则每次更改。因此,可以安全地使用它创建自己的语言。
好了,本文到此结束。如果对编程、计算机、程序员方面感兴趣的话,欢迎私信联系我,随时交流!点个关注,是对我莫大的鼓励!
举报/反馈