语法分析3 - 一步步实现JS语言编译执行
js语言最常见的就是变量定义了,上节中我们实现了基础的单表达式解析计算, 本节将支持变量的定义及变量的引用,实现多个语句一起执行
目标
实现赋值语句, 变量申明和初始化
完善PrimaryAstNode中遗留的Identifier计算问题
完善声明,赋值等表达式计算
词法部分
为支持声明,赋值等操作, 我们需要加入 =
,var
等的token识别
实现声明和赋值语句
有文法规则了, 赋值表达式实现方式同前面的加法,乘法。
声明操作需要组合赋值表达式
/**
* v0.0.3
* 变量声明语句
*
* 简化语法如下
* VariableStatement :
* var VariableDeclaration ;
*
* VariableDeclaration:
* Identifier Initialiser(opt)
*
* Initialiser:
* = AssignmentExpression
*
*/
variable() {
let node = null;
let nextToken = this.lexerLevel.tokenPeek()
let tempToken = nextToken;
if(nextToken && nextToken.type === this.lexerLevel.DfaState.Var) {
node = new DeclarationAstNode("Declaration")
this.lexerLevel.tokenRead();
nextToken = this.lexerLevel.tokenPeek()
if(nextToken && nextToken.type === this.lexerLevel.DfaState.Identifier) {
// 表示var + 标识符
let childId = new PrimaryAstNode(nextToken.type, nextToken.value)
this.lexerLevel.tokenRead();
node.addLeftChild(childId)
// 检查是否有表达式
nextToken = this.lexerLevel.tokenPeek()
if(nextToken && nextToken.type === this.lexerLevel.DfaState.Assignment) {
this.lexerLevel.tokenRead();
let childInit = this.assignment();
if(childInit) {
node.addRightChild(childInit)
} else {
throw Error("no assignment expression after Assignment ")
}
}
nextToken = this.lexerLevel.tokenPeek()
if(nextToken && nextToken.type === this.lexerLevel.DfaState.SemiColon) {
this.lexerLevel.tokenRead();
}
} else {
throw Error("no Identifier after Var ")
}
}
return node;
}
实现多语句支持
在前一节中, 我们还都是对单个语句做ast,本节我们要支持多个语句, 其实就是加一个处理语句的入口
这时候,我们就需要一个根节点了
/**
* 解析生成AST
*/
astParse() {
// 涉及多个语句, 先初始化一个根节点
let node = new AstNode("Program", "");
let nextToken = this.lexerLevel.tokenPeek()
while(nextToken) {
// 1
let cv = this.variable();
if(!cv) {
cv = this.assignment()
}
// 2
if(!cv) {
cv = this.bitwiseShift()
}
if(cv) {
node.addChild(cv)
} else {
throw Error("not support current expression!")
}
nextToken = this.lexerLevel.tokenPeek()
}
return node
}
如何支持变量
这边就涉及到一个变量消解了。 我们现在只需要支持多个语句,并且类型也有限。 可以使用一个对象来存储对应的变量,
this.variables = {}
有了存储空间,那么我们什么时候往里存放值呢?
考虑到js的var定义的变量,有个特性:变量提升
变量提升,是指在 JavaScript 代码执行过程中,JavaScript 引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置默认值,这个默认值就是我们熟悉的 undefined。
有个这个指导, 我们就清楚做以下操作:
const stack = require("../Stack")
const AstNode = require("./AstNode")
// 标识一个变量声明节点
class DeclarationAstNode extends AstNode{
constructor(type, value) {
super(type, value)
this.id = null;
this.init = null
}
/**
*
* @returns {*}
*/
getValue(){
// 仅对变量赋值(真实调用时赋值)
stack.setVal(this.id.value, this.init? this.init.getValue(): undefined);
return stack.getVal(this.id.value);
}
// 左侧是声明的变量名
addLeftChild(child) {
this.id = child;
super.addChild(child)
// v0.0.3版本, 暂不考虑作用域链的情况
// 表示我在词法解析的节点就已经将该变量提升了
stack.addVar(child.value, undefined) // 初始为undefined
}
addRightChild(child) {
this.init = child;
super.addChild(child)
}
}
module.exports = DeclarationAstNode
stack对应的方法内容如下
class Stack {
constructor() {
this.variables = {}
}
addVar(key, value) {
this.variables[key] = value;
}
setVal(key, value) {
this.variables[key] = value;
}
deleteVar(key) {
delete this.variables[key];
}
hasVar(key) {
return this.variables.hasOwnProperty(key)
}
getVal(key) {
return this.variables[key]
}
}
module.exports = new Stack()
测试
- 测试语法树
var a =2;
var b = 3;
a = a * b + 3;
生成的ast结构如下:
AstNode {
type: 'Program',
value: '',
children:
[ DeclarationAstNode {
type: 'Declaration',
value: '',
children: [Array],
parent: [Circular],
id: [PrimaryAstNode],
init: [PrimaryAstNode] },
DeclarationAstNode {
type: 'Declaration',
value: '',
children: [Array],
parent: [Circular],
id: [PrimaryAstNode],
init: [PrimaryAstNode] },
AssignmentAstNode {
type: 'Assignment',
value: '',
children: [Array],
parent: [Circular],
opera: undefined,
left: [PrimaryAstNode],
right: [BinaryAstNode] } ],
parent: null }
- 测试异常, 即变量未定义就使用的
let syntaxLevel2 = new SyntaxLevel1(`
var a =2;
var b = 3;
a = a * c + 3;
`)
console.log( syntaxLevel2.exe())
结果:
Error: Identifier c is not defined
at PrimaryAstNode.getValue (/Users/learn/compilation/ast/PrimaryAstNode.js:21:23)
at BinaryAstNode.getValue (/Users/learn/compilation/ast/BinaryAstNode.js:22:58)
- 测试执行
let syntaxLevel = new SyntaxLevel1(`
var a =2;
var b = 3;
a = a * b + 3;
`)
console.log( syntaxLevel.exe())
结果:
9
标题:语法分析3 - 一步步实现JS语言编译执行
作者:hugh0524
地址:https://blog.uproject.cn/articles/2020/02/27/1582784062569.html
0 0