├── .gitignore ├── Article.md ├── LICENSE ├── README.md ├── demo.js ├── dist ├── demo.js ├── demo.map ├── index.js └── index.map ├── package.json └── src ├── es_versions ├── es5.js └── index.js ├── index.js ├── iterator.js ├── scope.js ├── signal.js ├── standard.js └── value.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .cache/ -------------------------------------------------------------------------------- /Article.md: -------------------------------------------------------------------------------- 1 | 说起编译原理,印象往往只停留在本科时那些枯燥的课程和晦涩的概念。作为前端开发者,编译原理似乎离我们很远,对它的理解很可能仅仅局限于“抽象语法树(AST)”。但这仅仅是个开头而已。编译原理的使用,甚至能让我们利用JS直接写一个能运行JS代码的解释器。 2 | 3 | > 项目地址:https://github.com/jrainlau/canjs 4 | > 5 | > 在线体验:https://codepen.io/jrainlau/pen/YRgQXo 6 | 7 | ## 为什么要用JS写JS的解释器 8 | 接触过小程序开发的同学应该知道,小程序运行的环境禁止`new Function`,`eval`等方法的使用,导致我们无法直接执行字符串形式的动态代码。此外,许多平台也对这些JS自带的可执行动态代码的方法进行了限制,那么我们是没有任何办法了吗?既然如此,我们便可以用JS写一个解析器,让JS自己去运行自己。 9 | 10 | 在开始之前,我们先简单回顾一下编译原理的一些概念。 11 | 12 | ## 什么是编译器 13 | 说到编译原理,肯定离不开编译器。简单来说,当一段代码经过编译器的词法分析、语法分析等阶段之后,会生成一个树状结构的“抽象语法树(AST)”,该语法树的每一个节点都对应着代码当中不同含义的片段。 14 | 15 | 比如有这么一段代码: 16 | ```javascript 17 | const a = 1 18 | console.log(a) 19 | ``` 20 | 经过编译器处理后,它的AST长这样: 21 | ``` 22 | { 23 | "type": "Program", 24 | "start": 0, 25 | "end": 26, 26 | "body": [ 27 | { 28 | "type": "VariableDeclaration", 29 | "start": 0, 30 | "end": 11, 31 | "declarations": [ 32 | { 33 | "type": "VariableDeclarator", 34 | "start": 6, 35 | "end": 11, 36 | "id": { 37 | "type": "Identifier", 38 | "start": 6, 39 | "end": 7, 40 | "name": "a" 41 | }, 42 | "init": { 43 | "type": "Literal", 44 | "start": 10, 45 | "end": 11, 46 | "value": 1, 47 | "raw": "1" 48 | } 49 | } 50 | ], 51 | "kind": "const" 52 | }, 53 | { 54 | "type": "ExpressionStatement", 55 | "start": 12, 56 | "end": 26, 57 | "expression": { 58 | "type": "CallExpression", 59 | "start": 12, 60 | "end": 26, 61 | "callee": { 62 | "type": "MemberExpression", 63 | "start": 12, 64 | "end": 23, 65 | "object": { 66 | "type": "Identifier", 67 | "start": 12, 68 | "end": 19, 69 | "name": "console" 70 | }, 71 | "property": { 72 | "type": "Identifier", 73 | "start": 20, 74 | "end": 23, 75 | "name": "log" 76 | }, 77 | "computed": false 78 | }, 79 | "arguments": [ 80 | { 81 | "type": "Identifier", 82 | "start": 24, 83 | "end": 25, 84 | "name": "a" 85 | } 86 | ] 87 | } 88 | } 89 | ], 90 | "sourceType": "module" 91 | } 92 | ``` 93 | > 常见的JS编译器有`babylon`,`acorn`等等,感兴趣的同学可以在[AST explorer][1]这个网站自行体验。 94 | 95 | 可以看到,编译出来的AST详细记录了代码中所有语义代码的类型、起始位置等信息。这段代码除了根节点`Program`外,主体包含了两个节点`VariableDeclaration`和`ExpressionStatement`,而这些节点里面又包含了不同的子节点。 96 | 97 | 正是由于AST详细记录了代码的语义化信息,所以Babel,Webpack,Sass,Less等工具可以针对代码进行非常智能的处理。 98 | 99 | ## 什么是解释器 100 | 如同翻译人员不仅能看懂一门外语,也能对其艺术加工后把它翻译成母语一样,人们把能够将代码转化成AST的工具叫做“编译器”,而把能够将AST翻译成目标语言并运行的工具叫做“解释器”。 101 | 102 | 在编译原理的课程中,我们思考过这么一个问题:如何让计算机运行算数表达式`1+2+3`: 103 | ``` 104 | 1 + 2 + 3 105 | ``` 106 | 当机器执行的时候,它可能会是这样的机器码: 107 | ``` 108 | 1 PUSH 1 109 | 2 PUSH 2 110 | 3 ADD 111 | 4 PUSH 3 112 | 5 ADD 113 | ``` 114 | 而运行这段机器码的程序,就是解释器。 115 | 116 | 在这篇文章中,我们不会搞出机器码这样复杂的东西,仅仅是使用JS在其runtime环境下去解释JS代码的AST。由于解释器使用JS编写,所以我们可以大胆使用JS自身的语言特性,比如this绑定、new关键字等等,完全不需要对它们进行额外处理,也因此让JS解释器的实现变得非常简单。 117 | 118 | 在回顾了编译原理的基本概念之后,我们就可以着手进行开发了。 119 | 120 | ## 节点遍历器 121 | 通过分析上文的AST,可以看到每一个节点都会有一个类型属性`type`,不同类型的节点需要不同的处理方式,处理这些节点的程序,就是“节点处理器(`nodeHandler`)” 122 | 123 | 定义一个节点处理器: 124 | ```javascript 125 | const nodeHandler = { 126 | Program () {}, 127 | VariableDeclaration () {}, 128 | ExpressionStatement () {}, 129 | MemberExpression () {}, 130 | CallExpression () {}, 131 | Identifier () {} 132 | } 133 | ``` 134 | 关于节点处理器的具体实现,会在后文进行详细探讨,这里暂时不作展开。 135 | 136 | 有了节点处理器,我们便需要去遍历AST当中的每一个节点,递归地调用节点处理器,直到完成对整棵语法书的处理。 137 | 138 | 定义一个节点遍历器(`NodeIterator`): 139 | ```javascript 140 | class NodeIterator { 141 | constructor (node) { 142 | this.node = node 143 | this.nodeHandler = nodeHandler 144 | } 145 | 146 | traverse (node) { 147 | // 根据节点类型找到节点处理器当中对应的函数 148 | const _eval = this.nodeHandler[node.type] 149 | // 若找不到则报错 150 | if (!_eval) { 151 | throw new Error(`canjs: Unknown node type "${node.type}".`) 152 | } 153 | // 运行处理函数 154 | return _eval(node) 155 | } 156 | 157 | } 158 | ``` 159 | 160 | 理论上,节点遍历器这样设计就可以了,但仔细推敲,发现漏了一个很重要的东西——作用域处理。 161 | 162 | 回到节点处理器的`VariableDeclaration()`方法,它用来处理诸如`const a = 1`这样的变量声明节点。假设它的代码如下: 163 | ```javascript 164 | VariableDeclaration (node) { 165 | for (const declaration of node.declarations) { 166 | const { name } = declaration.id 167 | const value = declaration.init ? traverse(declaration.init) : undefined 168 | // 问题来了,拿到了变量的名称和值,然后把它保存到哪里去呢? 169 | // ... 170 | } 171 | }, 172 | ``` 173 | 问题在于,处理完变量声明节点以后,理应把这个变量保存起来。按照JS语言特性,这个变量应该存放在一个作用域当中。在JS解析器的实现过程中,这个作用域可以被定义为一个`scope`对象。 174 | 175 | 改写节点遍历器,为其新增一个`scope`对象 176 | ```javascript 177 | class NodeIterator { 178 | constructor (node, scope = {}) { 179 | this.node = node 180 | this.scope = scope 181 | this.nodeHandler = nodeHandler 182 | } 183 | 184 | traverse (node, options = {}) { 185 | const scope = options.scope || this.scope 186 | const nodeIterator = new NodeIterator(node, scope) 187 | const _eval = this.nodeHandler[node.type] 188 | if (!_eval) { 189 | throw new Error(`canjs: Unknown node type "${node.type}".`) 190 | } 191 | return _eval(nodeIterator) 192 | } 193 | 194 | createScope (blockType = 'block') { 195 | return new Scope(blockType, this.scope) 196 | } 197 | } 198 | ``` 199 | 然后节点处理函数`VariableDeclaration()`就可以通过`scope`保存变量了: 200 | ```javascript 201 | VariableDeclaration (nodeIterator) { 202 | const kind = nodeIterator.node.kind 203 | for (const declaration of nodeIterator.node.declarations) { 204 | const { name } = declaration.id 205 | const value = declaration.init ? nodeIterator.traverse(declaration.init) : undefined 206 | // 在作用域当中定义变量 207 | // 如果当前是块级作用域且变量用var定义,则定义到父级作用域 208 | if (nodeIterator.scope.type === 'block' && kind === 'var') { 209 | nodeIterator.scope.parentScope.declare(name, value, kind) 210 | } else { 211 | nodeIterator.scope.declare(name, value, kind) 212 | } 213 | } 214 | }, 215 | ``` 216 | 217 | 关于作用域的处理,可以说是整个JS解释器最难的部分。接下来我们将对作用域处理进行深入的剖析。 218 | 219 | ## 作用域处理 220 | 考虑到这样一种情况: 221 | ```javascript 222 | const a = 1 223 | { 224 | const b = 2 225 | console.log(a) 226 | } 227 | console.log(b) 228 | ``` 229 | 运行结果必然是能够打印出`a`的值,然后报错:`Uncaught ReferenceError: b is not defined` 230 | 231 | 这段代码就是涉及到了作用域的问题。块级作用域或者函数作用域可以读取其父级作用域当中的变量,反之则不行,所以对于作用域我们不能简单地定义一个空对象,而是要专门进行处理。 232 | 233 | 定义一个作用域基类`Scope`: 234 | ```javascript 235 | class Scope { 236 | constructor (type, parentScope) { 237 | // 作用域类型,区分函数作用域function和块级作用域block 238 | this.type = type 239 | // 父级作用域 240 | this.parentScope = parentScope 241 | // 全局作用域 242 | this.globalDeclaration = standardMap 243 | // 当前作用域的变量空间 244 | this.declaration = Object.create(null) 245 | } 246 | 247 | /* 248 | * get/set方法用于获取/设置当前作用域中对应name的变量值 249 | 符合JS语法规则,优先从当前作用域去找,若找不到则到父级作用域去找,然后到全局作用域找。 250 | 如果都没有,就报错 251 | */ 252 | get (name) { 253 | if (this.declaration[name]) { 254 | return this.declaration[name] 255 | } else if (this.parentScope) { 256 | return this.parentScope.get(name) 257 | } else if (this.globalDeclaration[name]) { 258 | return this.globalDeclaration[name] 259 | } 260 | throw new ReferenceError(`${name} is not defined`) 261 | } 262 | 263 | set (name, value) { 264 | if (this.declaration[name]) { 265 | this.declaration[name] = value 266 | } else if (this.parentScope[name]) { 267 | this.parentScope.set(name, value) 268 | } else { 269 | throw new ReferenceError(`${name} is not defined`) 270 | } 271 | } 272 | 273 | /** 274 | * 根据变量的kind调用不同的变量定义方法 275 | */ 276 | declare (name, value, kind = 'var') { 277 | if (kind === 'var') { 278 | return this.varDeclare(name, value) 279 | } else if (kind === 'let') { 280 | return this.letDeclare(name, value) 281 | } else if (kind === 'const') { 282 | return this.constDeclare(name, value) 283 | } else { 284 | throw new Error(`canjs: Invalid Variable Declaration Kind of "${kind}"`) 285 | } 286 | } 287 | 288 | varDeclare (name, value) { 289 | let scope = this 290 | // 若当前作用域存在非函数类型的父级作用域时,就把变量定义到父级作用域 291 | while (scope.parentScope && scope.type !== 'function') { 292 | scope = scope.parentScope 293 | } 294 | this.declaration[name] = new SimpleValue(value, 'var') 295 | return this.declaration[name] 296 | } 297 | 298 | letDeclare (name, value) { 299 | // 不允许重复定义 300 | if (this.declaration[name]) { 301 | throw new SyntaxError(`Identifier ${name} has already been declared`) 302 | } 303 | this.declaration[name] = new SimpleValue(value, 'let') 304 | return this.declaration[name] 305 | } 306 | 307 | constDeclare (name, value) { 308 | // 不允许重复定义 309 | if (this.declaration[name]) { 310 | throw new SyntaxError(`Identifier ${name} has already been declared`) 311 | } 312 | this.declaration[name] = new SimpleValue(value, 'const') 313 | return this.declaration[name] 314 | } 315 | } 316 | ``` 317 | 318 | 这里使用了一个叫做`simpleValue()`的函数来定义变量值,主要用于处理常量: 319 | ```javascript 320 | class SimpleValue { 321 | constructor (value, kind = '') { 322 | this.value = value 323 | this.kind = kind 324 | } 325 | 326 | set (value) { 327 | // 禁止重新对const类型变量赋值 328 | if (this.kind === 'const') { 329 | throw new TypeError('Assignment to constant variable') 330 | } else { 331 | this.value = value 332 | } 333 | } 334 | 335 | get () { 336 | return this.value 337 | } 338 | } 339 | ``` 340 | 处理作用域问题思路,关键的地方就是在于JS语言本身寻找变量的特性——优先当前作用域,父作用域次之,全局作用域最后。反过来,在节点处理函数`VariableDeclaration()`里,如果遇到块级作用域且关键字为`var`,则需要把这个变量也定义到父级作用域当中,这也就是我们常说的“全局变量污染”。 341 | 342 | ### JS标准库注入 343 | 细心的读者会发现,在定义`Scope`基类的时候,其全局作用域`globalScope`被赋值了一个`standardMap`对象,这个对象就是JS标准库。 344 | 345 | 简单来说,JS标准库就是JS这门语言本身所带有的一系列方法和属性,如常用的`setTimeout`,`console.log`等等。为了让解析器也能够执行这些方法,所以我们需要为其注入标准库: 346 | ```javascript 347 | const standardMap = { 348 | console: new SimpleValue(console) 349 | } 350 | ``` 351 | 这样就相当于往解析器的全局作用域当中注入了`console`这个对象,也就可以直接被使用了。 352 | 353 | ## 节点处理器 354 | 在处理完节点遍历器、作用域处理的工作之后,便可以来编写节点处理器了。顾名思义,节点处理器是专门用来处理AST节点的,上文反复提及的`VariableDeclaration()`方法便是其中一个。下面将对部分关键的节点处理器进行讲解。 355 | 356 | 在开发节点处理器之前,需要用到一个工具,用于判断JS语句当中的`return`,`break`,`continue`关键字。 357 | 358 | ### 关键字判断工具`Signal` 359 | 定义一个`Signal`基类: 360 | ```javascript 361 | class Signal { 362 | constructor (type, value) { 363 | this.type = type 364 | this.value = value 365 | } 366 | 367 | static Return (value) { 368 | return new Signal('return', value) 369 | } 370 | 371 | static Break (label = null) { 372 | return new Signal('break', label) 373 | } 374 | 375 | static Continue (label) { 376 | return new Signal('continue', label) 377 | } 378 | 379 | static isReturn(signal) { 380 | return signal instanceof Signal && signal.type === 'return' 381 | } 382 | 383 | static isContinue(signal) { 384 | return signal instanceof Signal && signal.type === 'continue' 385 | } 386 | 387 | static isBreak(signal) { 388 | return signal instanceof Signal && signal.type === 'break' 389 | } 390 | 391 | static isSignal (signal) { 392 | return signal instanceof Signal 393 | } 394 | } 395 | ``` 396 | 有了它,就可以对语句当中的关键字进行判断处理,接下来会有大用处。 397 | 398 | ### 变量定义节点处理器——`VariableDeclaration()` 399 | 最常用的节点处理器之一,负责把变量注册到正确的作用域。 400 | ```javascript 401 | VariableDeclaration (nodeIterator) { 402 | const kind = nodeIterator.node.kind 403 | for (const declaration of nodeIterator.node.declarations) { 404 | const { name } = declaration.id 405 | const value = declaration.init ? nodeIterator.traverse(declaration.init) : undefined 406 | // 在作用域当中定义变量 407 | // 若为块级作用域且关键字为var,则需要做全局污染 408 | if (nodeIterator.scope.type === 'block' && kind === 'var') { 409 | nodeIterator.scope.parentScope.declare(name, value, kind) 410 | } else { 411 | nodeIterator.scope.declare(name, value, kind) 412 | } 413 | } 414 | }, 415 | ``` 416 | 417 | ### 标识符节点处理器——`Identifier()` 418 | 专门用于从作用域中获取标识符的值。 419 | ```javascript 420 | Identifier (nodeIterator) { 421 | if (nodeIterator.node.name === 'undefined') { 422 | return undefined 423 | } 424 | return nodeIterator.scope.get(nodeIterator.node.name).value 425 | }, 426 | ``` 427 | 428 | ### 字符节点处理器——`Literal()` 429 | 返回字符节点的值。 430 | ```javascript 431 | Literal (nodeIterator) { 432 | return nodeIterator.node.value 433 | } 434 | ``` 435 | 436 | ### 表达式调用节点处理器——`CallExpression()` 437 | 用于处理表达式调用节点的处理器,如处理`func()`,`console.log()`等。 438 | ```javascript 439 | CallExpression (nodeIterator) { 440 | // 遍历callee获取函数体 441 | const func = nodeIterator.traverse(nodeIterator.node.callee) 442 | // 获取参数 443 | const args = nodeIterator.node.arguments.map(arg => nodeIterator.traverse(arg)) 444 | 445 | let value 446 | if (nodeIterator.node.callee.type === 'MemberExpression') { 447 | value = nodeIterator.traverse(nodeIterator.node.callee.object) 448 | } 449 | // 返回函数运行结果 450 | return func.apply(value, args) 451 | }, 452 | ``` 453 | 454 | ### 表达式节点处理器——`MemberExpression()` 455 | 区分于上面的“表达式调用节点处理器”,表达式节点指的是`person.say`,`console.log`这种函数表达式。 456 | ```javascript 457 | MemberExpression (nodeIterator) { 458 | // 获取对象,如console 459 | const obj = nodeIterator.traverse(nodeIterator.node.object) 460 | // 获取对象的方法,如log 461 | const name = nodeIterator.node.property.name 462 | // 返回表达式,如console.log 463 | return obj[name] 464 | } 465 | ``` 466 | 467 | ### 块级声明节点处理器——`BlockStatement()` 468 | 非常常用的处理器,专门用于处理块级声明节点,如函数、循环、`try...catch...`当中的情景。 469 | ```javascript 470 | BlockStatement (nodeIterator) { 471 | // 先定义一个块级作用域 472 | let scope = nodeIterator.createScope('block') 473 | 474 | // 处理块级节点内的每一个节点 475 | for (const node of nodeIterator.node.body) { 476 | if (node.type === 'VariableDeclaration' && node.kind === 'var') { 477 | for (const declaration of node.declarations) { 478 | scope.declare(declaration.id.name, declaration.init.value, node.kind) 479 | } 480 | } else if (node.type === 'FunctionDeclaration') { 481 | nodeIterator.traverse(node, { scope }) 482 | } 483 | } 484 | 485 | // 提取关键字(return, break, continue) 486 | for (const node of nodeIterator.node.body) { 487 | if (node.type === 'FunctionDeclaration') { 488 | continue 489 | } 490 | const signal = nodeIterator.traverse(node, { scope }) 491 | if (Signal.isSignal(signal)) { 492 | return signal 493 | } 494 | } 495 | } 496 | ``` 497 | 498 | 可以看到这个处理器里面有两个`for...of`循环。第一个用于处理块级内语句,第二个专门用于识别关键字,如循环体内部的`break`,`continue`或者函数体内部的`return`。 499 | 500 | ### 函数定义节点处理器——`FunctionDeclaration()` 501 | 往作用当中声明一个和函数名相同的变量,值为所定义的函数: 502 | ```javascript 503 | FunctionDeclaration (nodeIterator) { 504 | const fn = NodeHandler.FunctionExpression(nodeIterator) 505 | nodeIterator.scope.varDeclare(nodeIterator.node.id.name, fn) 506 | return fn 507 | } 508 | ``` 509 | 510 | ### 函数表达式节点处理器——`FunctionExpression()` 511 | 用于定义一个函数: 512 | ```javascript 513 | FunctionExpression (nodeIterator) { 514 | const node = nodeIterator.node 515 | /** 516 | * 1、定义函数需要先为其定义一个函数作用域,且允许继承父级作用域 517 | * 2、注册`this`, `arguments`和形参到作用域的变量空间 518 | * 3、检查return关键字 519 | * 4、定义函数名和长度 520 | */ 521 | const fn = function () { 522 | const scope = nodeIterator.createScope('function') 523 | scope.constDeclare('this', this) 524 | scope.constDeclare('arguments', arguments) 525 | 526 | node.params.forEach((param, index) => { 527 | const name = param.name 528 | scope.varDeclare(name, arguments[index]) 529 | }) 530 | 531 | const signal = nodeIterator.traverse(node.body, { scope }) 532 | if (Signal.isReturn(signal)) { 533 | return signal.value 534 | } 535 | } 536 | 537 | Object.defineProperties(fn, { 538 | name: { value: node.id ? node.id.name : '' }, 539 | length: { value: node.params.length } 540 | }) 541 | 542 | return fn 543 | } 544 | ``` 545 | 546 | ### this表达式处理器——`ThisExpression()` 547 | 该处理器直接使用JS语言自身的特性,把`this`关键字从作用域中取出即可。 548 | ```javascript 549 | ThisExpression (nodeIterator) { 550 | const value = nodeIterator.scope.get('this') 551 | return value ? value.value : null 552 | } 553 | ``` 554 | 555 | ### new表达式处理器——`NewExpression()` 556 | 和`this`表达式类似,也是直接沿用JS的语言特性,获取函数和参数之后,通过`bind`关键字生成一个构造函数,并返回。 557 | ```javascript 558 | NewExpression (nodeIterator) { 559 | const func = nodeIterator.traverse(nodeIterator.node.callee) 560 | const args = nodeIterator.node.arguments.map(arg => nodeIterator.traverse(arg)) 561 | return new (func.bind(null, ...args)) 562 | } 563 | ``` 564 | 565 | ### For循环节点处理器——`ForStatement()` 566 | For循环的三个参数对应着节点的`init`,`test`,`update`属性,对着三个属性分别调用节点处理器处理,并放回JS原生的for循环当中即可。 567 | ```javascript 568 | ForStatement (nodeIterator) { 569 | const node = nodeIterator.node 570 | let scope = nodeIterator.scope 571 | if (node.init && node.init.type === 'VariableDeclaration' && node.init.kind !== 'var') { 572 | scope = nodeIterator.createScope('block') 573 | } 574 | 575 | for ( 576 | node.init && nodeIterator.traverse(node.init, { scope }); 577 | node.test ? nodeIterator.traverse(node.test, { scope }) : true; 578 | node.update && nodeIterator.traverse(node.update, { scope }) 579 | ) { 580 | const signal = nodeIterator.traverse(node.body, { scope }) 581 | 582 | if (Signal.isBreak(signal)) { 583 | break 584 | } else if (Signal.isContinue(signal)) { 585 | continue 586 | } else if (Signal.isReturn(signal)) { 587 | return signal 588 | } 589 | } 590 | } 591 | ``` 592 | 同理,`for...in`,`while`和`do...while`循环也是类似的处理方式,这里不再赘述。 593 | 594 | ### If声明节点处理器——`IfStatemtnt()` 595 | 处理If语句,包括`if`,`if...else`,`if...elseif...else`。 596 | ```javascript 597 | IfStatement (nodeIterator) { 598 | if (nodeIterator.traverse(nodeIterator.node.test)) { 599 | return nodeIterator.traverse(nodeIterator.node.consequent) 600 | } else if (nodeIterator.node.alternate) { 601 | return nodeIterator.traverse(nodeIterator.node.alternate) 602 | } 603 | } 604 | ``` 605 | 同理,`switch`语句、三目表达式也是类似的处理方式。 606 | 607 | --- 608 | 609 | 上面列出了几个比较重要的节点处理器,在es5当中还有很多节点需要处理,详细内容可以访问[这个地址](https://github.com/jrainlau/canjs/blob/master/src/es_versions/es5.js)一探究竟。 610 | 611 | ## 定义调用方式 612 | 经过了上面的所有步骤,解析器已经具备处理es5代码的能力,接下来就是对这些散装的内容进行组装,最终定义一个方便用户调用的办法。 613 | 614 | ```javascript 615 | const { Parser } = require('acorn') 616 | const NodeIterator = require('./iterator') 617 | const Scope = require('./scope') 618 | 619 | class Canjs { 620 | constructor (code = '', extraDeclaration = {}) { 621 | this.code = code 622 | this.extraDeclaration = extraDeclaration 623 | this.ast = Parser.parse(code) 624 | this.nodeIterator = null 625 | this.init() 626 | } 627 | 628 | init () { 629 | // 定义全局作用域,该作用域类型为函数作用域 630 | const globalScope = new Scope('function') 631 | // 根据入参定义标准库之外的全局变量 632 | Object.keys(this.extraDeclaration).forEach((key) => { 633 | globalScope.addDeclaration(key, this.extraDeclaration[key]) 634 | }) 635 | this.nodeIterator = new NodeIterator(null, globalScope) 636 | } 637 | 638 | run () { 639 | return this.nodeIterator.traverse(this.ast) 640 | } 641 | } 642 | ``` 643 | 这里我们定义了一个名为`Canjs`的基类,接受字符串形式的JS代码,同时可定义标准库之外的变量。当运行`run()`方法的时候就可以得到运行结果。 644 | 645 | ## 后续 646 | 至此,整个JS解析器已经完成,可以很好地运行ES5的代码(可能还有bug没有发现)。但是在当前的实现中,所有的运行结果都是放在一个类似沙盒的地方,无法对外界产生影响。如果要把运行结果取出来,可能的办法有两种。第一种是传入一个全局的变量,把影响作用在这个全局变量当中,借助它把结果带出来;另外一种则是让解析器支持`export`语法,能够把`export`语句声明的结果返回,感兴趣的读者可以自行研究。 647 | 648 | 最后,这个JS解析器已经在我的Github上开源,欢迎前来交流~ 649 | 650 | https://github.com/jrainlau/canjs 651 | 652 | ## 参考资料 653 | [从零开始写一个Javascript解析器](https://juejin.im/post/5aa25be1518825557b4c5720#heading-11) 654 | 655 | [jkeylu/evil-eval](https://github.com/jkeylu/evil-eval) 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | [1]: https://astexplorer.net -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 JrainLau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CanJS 2 | 3 | `CanJS` is a javascript interpreter, which can run JS code in JS. 4 | 5 | Relate article: [《 6 | 前端与编译原理——用JS写一个JS解释器》](http://sfau.lt/b5bkvoY) 7 | 8 | ## Install 9 | 10 | ``` sh 11 | git clone https://github.com/jrainlau/canjs.git 12 | ``` 13 | 14 | ## Usage 15 | It's fine to run the JS code in string directly. 16 | 17 | ```javascript 18 | const Canjs = require('./dist/index.js') 19 | 20 | new Canjs(` 21 | console.log('Hello World!') 22 | `).run() 23 | ``` 24 | 25 | `CanJS` uses ES5 standard library, but you can also provide custom variables to it: 26 | 27 | ```javascript 28 | const Canjs = require('./dist/index.js') 29 | 30 | const wx = { 31 | name: 'wx' 32 | } 33 | 34 | new Canjs(` 35 | console.log(wx.name) 36 | `, { wx }).run() 37 | ``` 38 | 39 | ## License 40 | MIT -------------------------------------------------------------------------------- /demo.js: -------------------------------------------------------------------------------- 1 | const Canjs = require('./src') 2 | 3 | try { 4 | global.Canjs = Canjs 5 | } catch (e) {} 6 | 7 | try { 8 | window.Canjs = Canjs 9 | } catch (e) {} 10 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | parcelRequire=function(e,r,n,t){var i="function"==typeof parcelRequire&&parcelRequire,o="function"==typeof require&&require;function u(n,t){if(!r[n]){if(!e[n]){var f="function"==typeof parcelRequire&&parcelRequire;if(!t&&f)return f(n,!0);if(i)return i(n,!0);if(o&&"string"==typeof n)return o(n);var c=new Error("Cannot find module '"+n+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[n][1][r]||r},p.cache={};var l=r[n]=new u.Module(n);e[n][0].call(l.exports,p,l,l.exports,this)}return r[n].exports;function p(e){return u(p.resolve(e))}}u.isParcelRequire=!0,u.Module=function(e){this.id=e,this.bundle=u,this.exports={}},u.modules=e,u.cache=r,u.parent=i,u.register=function(r,n){e[r]=[function(e,r){r.exports=n},{}]};for(var f=0;ft)return!1;if((i+=e[s+1])>=t)return!0}}function u(t,e){return t<65?36===t:t<91||(t<97?95===t:t<123||(t<=65535?t>=170&&n.test(String.fromCharCode(t)):!1!==e&&c(t,h)))}function l(t,e){return t<48?36===t:t<58||!(t<65)&&(t<91||(t<97?95===t:t<123||(t<=65535?t>=170&&o.test(String.fromCharCode(t)):!1!==e&&(c(t,h)||c(t,p)))))}var d=function(t,e){void 0===e&&(e={}),this.label=t,this.keyword=e.keyword,this.beforeExpr=!!e.beforeExpr,this.startsExpr=!!e.startsExpr,this.isLoop=!!e.isLoop,this.isAssign=!!e.isAssign,this.prefix=!!e.prefix,this.postfix=!!e.postfix,this.binop=e.binop||null,this.updateContext=null};function f(t,e){return new d(t,{beforeExpr:!0,binop:e})}exports.TokenType=d;var m={beforeExpr:!0},x={startsExpr:!0},g={};function v(t,e){return void 0===e&&(e={}),e.keyword=t,g[t]=new d(t,e)}exports.keywordTypes=g;var y={num:new d("num",x),regexp:new d("regexp",x),string:new d("string",x),name:new d("name",x),eof:new d("eof"),bracketL:new d("[",{beforeExpr:!0,startsExpr:!0}),bracketR:new d("]"),braceL:new d("{",{beforeExpr:!0,startsExpr:!0}),braceR:new d("}"),parenL:new d("(",{beforeExpr:!0,startsExpr:!0}),parenR:new d(")"),comma:new d(",",m),semi:new d(";",m),colon:new d(":",m),dot:new d("."),question:new d("?",m),arrow:new d("=>",m),template:new d("template"),invalidTemplate:new d("invalidTemplate"),ellipsis:new d("...",m),backQuote:new d("`",x),dollarBraceL:new d("${",{beforeExpr:!0,startsExpr:!0}),eq:new d("=",{beforeExpr:!0,isAssign:!0}),assign:new d("_=",{beforeExpr:!0,isAssign:!0}),incDec:new d("++/--",{prefix:!0,postfix:!0,startsExpr:!0}),prefix:new d("!/~",{beforeExpr:!0,prefix:!0,startsExpr:!0}),logicalOR:f("||",1),logicalAND:f("&&",2),bitwiseOR:f("|",3),bitwiseXOR:f("^",4),bitwiseAND:f("&",5),equality:f("==/!=/===/!==",6),relational:f("/<=/>=",7),bitShift:f("<>/>>>",8),plusMin:new d("+/-",{beforeExpr:!0,binop:9,prefix:!0,startsExpr:!0}),modulo:f("%",10),star:f("*",10),slash:f("/",10),starstar:new d("**",{beforeExpr:!0}),_break:v("break"),_case:v("case",m),_catch:v("catch"),_continue:v("continue"),_debugger:v("debugger"),_default:v("default",m),_do:v("do",{isLoop:!0,beforeExpr:!0}),_else:v("else",m),_finally:v("finally"),_for:v("for",{isLoop:!0}),_function:v("function",x),_if:v("if"),_return:v("return",m),_switch:v("switch"),_throw:v("throw",m),_try:v("try"),_var:v("var"),_const:v("const"),_while:v("while",{isLoop:!0}),_with:v("with"),_new:v("new",{beforeExpr:!0,startsExpr:!0}),_this:v("this",x),_super:v("super",x),_class:v("class",x),_extends:v("extends",m),_export:v("export"),_import:v("import"),_null:v("null",x),_true:v("true",x),_false:v("false",x),_in:v("in",{beforeExpr:!0,binop:7}),_instanceof:v("instanceof",{beforeExpr:!0,binop:7}),_typeof:v("typeof",{beforeExpr:!0,prefix:!0,startsExpr:!0}),_void:v("void",{beforeExpr:!0,prefix:!0,startsExpr:!0}),_delete:v("delete",{beforeExpr:!0,prefix:!0,startsExpr:!0})};exports.tokTypes=y;var _=/\r\n?|\n|\u2028|\u2029/;exports.lineBreak=_;var b=new RegExp(_.source,"g");function k(t,e){return 10===t||13===t||!e&&(8232===t||8233===t)}exports.lineBreakG=b;var S=/[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;exports.nonASCIIwhitespace=S;var C=/(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g,E=Object.prototype,w=E.hasOwnProperty,A=E.toString;function I(t,e){return w.call(t,e)}var P=Array.isArray||function(t){return"[object Array]"===A.call(t)},T=function(t,e){this.line=t,this.column=e};exports.Position=T,T.prototype.offset=function(t){return new T(this.line,this.column+t)};var L=function(t,e,i){this.start=e,this.end=i,null!==t.sourceFile&&(this.source=t.sourceFile)};function N(t,e){for(var i=1,s=0;;){b.lastIndex=s;var r=b.exec(t);if(!(r&&r.index=2015&&(e.ecmaVersion-=2009),null==e.allowReserved&&(e.allowReserved=e.ecmaVersion<5),P(e.onToken)){var s=e.onToken;e.onToken=function(t){return s.push(t)}}return P(e.onComment)&&(e.onComment=O(e,e.onComment)),e}function O(t,e){return function(i,s,r,a,n,o){var h={type:i?"Block":"Line",value:s,start:r,end:a};t.locations&&(h.loc=new L(this,n,o)),t.ranges&&(h.range=[r,a]),e.push(h)}}exports.defaultOptions=V;var D=1,B=2,M=D|B,U=4,F=8,q=16,G=32,H=64,j=128;function W(t,e){return B|(t?U:0)|(e?F:0)}var z=0,Q=1,K=2,X=3,Y=4,Z=5;function $(t){return new RegExp("^(?:"+t.replace(/ /g,"|")+")$")}var J=function(e,s,r){this.options=e=R(e),this.sourceFile=e.sourceFile,this.keywords=$(i[e.ecmaVersion>=6?6:5]);var a="";if(!e.allowReserved){for(var n=e.ecmaVersion;!(a=t[n]);n--);"module"===e.sourceType&&(a+=" await")}this.reservedWords=$(a);var o=(a?a+" ":"")+t.strict;this.reservedWordsStrict=$(o),this.reservedWordsStrictBind=$(o+" "+t.strictBind),this.input=String(s),this.containsEsc=!1,r?(this.pos=r,this.lineStart=this.input.lastIndexOf("\n",r-1)+1,this.curLine=this.input.slice(0,this.lineStart).split(_).length):(this.pos=this.lineStart=0,this.curLine=1),this.type=y.eof,this.value=null,this.start=this.end=this.pos,this.startLoc=this.endLoc=this.curPosition(),this.lastTokEndLoc=this.lastTokStartLoc=null,this.lastTokStart=this.lastTokEnd=this.pos,this.context=this.initialContext(),this.exprAllowed=!0,this.inModule="module"===e.sourceType,this.strict=this.inModule||this.strictDirective(this.pos),this.potentialArrowAt=-1,this.yieldPos=this.awaitPos=0,this.labels=[],0===this.pos&&e.allowHashBang&&"#!"===this.input.slice(0,2)&&this.skipLineComment(2),this.scopeStack=[],this.enterScope(D),this.regexpState=null};exports.Parser=J;var tt={inFunction:{configurable:!0},inGenerator:{configurable:!0},inAsync:{configurable:!0},allowSuper:{configurable:!0},allowDirectSuper:{configurable:!0}};J.prototype.parse=function(){var t=this.options.program||this.startNode();return this.nextToken(),this.parseTopLevel(t)},tt.inFunction.get=function(){return(this.currentVarScope().flags&B)>0},tt.inGenerator.get=function(){return(this.currentVarScope().flags&F)>0},tt.inAsync.get=function(){return(this.currentVarScope().flags&U)>0},tt.allowSuper.get=function(){return(this.currentThisScope().flags&H)>0},tt.allowDirectSuper.get=function(){return(this.currentThisScope().flags&j)>0},J.prototype.inNonArrowFunction=function(){return(this.currentThisScope().flags&B)>0},J.extend=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];for(var i=this,s=0;s-1&&this.raiseRecoverable(t.trailingComma,"Comma is not permitted after the rest element");var i=e?t.parenthesizedAssign:t.parenthesizedBind;i>-1&&this.raiseRecoverable(i,"Parenthesized pattern")}},et.checkExpressionErrors=function(t,e){if(!t)return!1;var i=t.shorthandAssign,s=t.doubleProto;if(!e)return i>=0||s>=0;i>=0&&this.raise(i,"Shorthand property assignments are valid only in destructuring patterns"),s>=0&&this.raiseRecoverable(s,"Redefinition of __proto__ property")},et.checkYieldAwaitInDefaultParams=function(){this.yieldPos&&(!this.awaitPos||this.yieldPos=6&&(t.sourceType=this.options.sourceType),this.finishNode(t,"Program")};var at={kind:"loop"},nt={kind:"switch"};rt.isLet=function(){if(this.options.ecmaVersion<6||!this.isContextual("let"))return!1;C.lastIndex=this.pos;var t=C.exec(this.input),e=this.pos+t[0].length,i=this.input.charCodeAt(e);if(91===i||123===i)return!0;if(u(i,!0)){for(var r=e+1;l(this.input.charCodeAt(r),!0);)++r;var a=this.input.slice(e,r);if(!s.test(a))return!0}return!1},rt.isAsyncFunction=function(){if(this.options.ecmaVersion<8||!this.isContextual("async"))return!1;C.lastIndex=this.pos;var t=C.exec(this.input),e=this.pos+t[0].length;return!(_.test(this.input.slice(this.pos,e))||"function"!==this.input.slice(e,e+8)||e+8!==this.input.length&&l(this.input.charAt(e+8)))},rt.parseStatement=function(t,e,i){var s,r=this.type,a=this.startNode();switch(this.isLet()&&(r=y._var,s="let"),r){case y._break:case y._continue:return this.parseBreakContinueStatement(a,r.keyword);case y._debugger:return this.parseDebuggerStatement(a);case y._do:return this.parseDoStatement(a);case y._for:return this.parseForStatement(a);case y._function:return t&&(this.strict||"if"!==t)&&this.options.ecmaVersion>=6&&this.unexpected(),this.parseFunctionStatement(a,!1,!t);case y._class:return t&&this.unexpected(),this.parseClass(a,!0);case y._if:return this.parseIfStatement(a);case y._return:return this.parseReturnStatement(a);case y._switch:return this.parseSwitchStatement(a);case y._throw:return this.parseThrowStatement(a);case y._try:return this.parseTryStatement(a);case y._const:case y._var:return s=s||this.value,t&&"var"!==s&&this.unexpected(),this.parseVarStatement(a,s);case y._while:return this.parseWhileStatement(a);case y._with:return this.parseWithStatement(a);case y.braceL:return this.parseBlock(!0,a);case y.semi:return this.parseEmptyStatement(a);case y._export:case y._import:return this.options.allowImportExportEverywhere||(e||this.raise(this.start,"'import' and 'export' may only appear at the top level"),this.inModule||this.raise(this.start,"'import' and 'export' may appear only with 'sourceType: module'")),r===y._import?this.parseImport(a):this.parseExport(a,i);default:if(this.isAsyncFunction())return t&&this.unexpected(),this.next(),this.parseFunctionStatement(a,!0,!t);var n=this.value,o=this.parseExpression();return r===y.name&&"Identifier"===o.type&&this.eat(y.colon)?this.parseLabeledStatement(a,n,o,t):this.parseExpressionStatement(a,o)}},rt.parseBreakContinueStatement=function(t,e){var i="break"===e;this.next(),this.eat(y.semi)||this.insertSemicolon()?t.label=null:this.type!==y.name?this.unexpected():(t.label=this.parseIdent(),this.semicolon());for(var s=0;s=6?this.eat(y.semi):this.semicolon(),this.finishNode(t,"DoWhileStatement")},rt.parseForStatement=function(t){this.next();var e=this.options.ecmaVersion>=9&&(this.inAsync||!this.inFunction&&this.options.allowAwaitOutsideFunction)&&this.eatContextual("await")?this.lastTokStart:-1;if(this.labels.push(at),this.enterScope(0),this.expect(y.parenL),this.type===y.semi)return e>-1&&this.unexpected(e),this.parseFor(t,null);var i=this.isLet();if(this.type===y._var||this.type===y._const||i){var s=this.startNode(),r=i?"let":this.value;return this.next(),this.parseVar(s,!0,r),this.finishNode(s,"VariableDeclaration"),!(this.type===y._in||this.options.ecmaVersion>=6&&this.isContextual("of"))||1!==s.declarations.length||"var"!==r&&s.declarations[0].init?(e>-1&&this.unexpected(e),this.parseFor(t,s)):(this.options.ecmaVersion>=9&&(this.type===y._in?e>-1&&this.unexpected(e):t.await=e>-1),this.parseForIn(t,s))}var a=new st,n=this.parseExpression(!0,a);return this.type===y._in||this.options.ecmaVersion>=6&&this.isContextual("of")?(this.options.ecmaVersion>=9&&(this.type===y._in?e>-1&&this.unexpected(e):t.await=e>-1),this.toAssignable(n,!1,a),this.checkLVal(n),this.parseForIn(t,n)):(this.checkExpressionErrors(a,!0),e>-1&&this.unexpected(e),this.parseFor(t,n))},rt.parseFunctionStatement=function(t,e,i){return this.next(),this.parseFunction(t,ht|(i?0:pt),!1,e)},rt.parseIfStatement=function(t){return this.next(),t.test=this.parseParenExpression(),t.consequent=this.parseStatement("if"),t.alternate=this.eat(y._else)?this.parseStatement("if"):null,this.finishNode(t,"IfStatement")},rt.parseReturnStatement=function(t){return this.inFunction||this.options.allowReturnOutsideFunction||this.raise(this.start,"'return' outside of function"),this.next(),this.eat(y.semi)||this.insertSemicolon()?t.argument=null:(t.argument=this.parseExpression(),this.semicolon()),this.finishNode(t,"ReturnStatement")},rt.parseSwitchStatement=function(t){var e;this.next(),t.discriminant=this.parseParenExpression(),t.cases=[],this.expect(y.braceL),this.labels.push(nt),this.enterScope(0);for(var i=!1;this.type!==y.braceR;)if(this.type===y._case||this.type===y._default){var s=this.type===y._case;e&&this.finishNode(e,"SwitchCase"),t.cases.push(e=this.startNode()),e.consequent=[],this.next(),s?e.test=this.parseExpression():(i&&this.raiseRecoverable(this.lastTokStart,"Multiple default clauses"),i=!0,e.test=null),this.expect(y.colon)}else e||this.unexpected(),e.consequent.push(this.parseStatement(null));return this.exitScope(),e&&this.finishNode(e,"SwitchCase"),this.next(),this.labels.pop(),this.finishNode(t,"SwitchStatement")},rt.parseThrowStatement=function(t){return this.next(),_.test(this.input.slice(this.lastTokEnd,this.start))&&this.raise(this.lastTokEnd,"Illegal newline after throw"),t.argument=this.parseExpression(),this.semicolon(),this.finishNode(t,"ThrowStatement")};var ot=[];rt.parseTryStatement=function(t){if(this.next(),t.block=this.parseBlock(),t.handler=null,this.type===y._catch){var e=this.startNode();if(this.next(),this.eat(y.parenL)){e.param=this.parseBindingAtom();var i="Identifier"===e.param.type;this.enterScope(i?G:0),this.checkLVal(e.param,i?Y:K),this.expect(y.parenR)}else this.options.ecmaVersion<10&&this.unexpected(),e.param=null,this.enterScope(0);e.body=this.parseBlock(!1),this.exitScope(),t.handler=this.finishNode(e,"CatchClause")}return t.finalizer=this.eat(y._finally)?this.parseBlock():null,t.handler||t.finalizer||this.raise(t.start,"Missing catch or finally clause"),this.finishNode(t,"TryStatement")},rt.parseVarStatement=function(t,e){return this.next(),this.parseVar(t,!1,e),this.semicolon(),this.finishNode(t,"VariableDeclaration")},rt.parseWhileStatement=function(t){return this.next(),t.test=this.parseParenExpression(),this.labels.push(at),t.body=this.parseStatement("while"),this.labels.pop(),this.finishNode(t,"WhileStatement")},rt.parseWithStatement=function(t){return this.strict&&this.raise(this.start,"'with' in strict mode"),this.next(),t.object=this.parseParenExpression(),t.body=this.parseStatement("with"),this.finishNode(t,"WithStatement")},rt.parseEmptyStatement=function(t){return this.next(),this.finishNode(t,"EmptyStatement")},rt.parseLabeledStatement=function(t,e,i,s){for(var r=0,a=this.labels;r=0;o--){var h=this.labels[o];if(h.statementStart!==t.start)break;h.statementStart=this.start,h.kind=n}return this.labels.push({name:e,kind:n,statementStart:this.start}),t.body=this.parseStatement(s),("ClassDeclaration"===t.body.type||"VariableDeclaration"===t.body.type&&"var"!==t.body.kind||"FunctionDeclaration"===t.body.type&&(this.strict||t.body.generator||t.body.async))&&this.raiseRecoverable(t.body.start,"Invalid labeled declaration"),this.labels.pop(),t.label=i,this.finishNode(t,"LabeledStatement")},rt.parseExpressionStatement=function(t,e){return t.expression=e,this.semicolon(),this.finishNode(t,"ExpressionStatement")},rt.parseBlock=function(t,e){for(void 0===t&&(t=!0),void 0===e&&(e=this.startNode()),e.body=[],this.expect(y.braceL),t&&this.enterScope(0);!this.eat(y.braceR);){var i=this.parseStatement(null);e.body.push(i)}return t&&this.exitScope(),this.finishNode(e,"BlockStatement")},rt.parseFor=function(t,e){return t.init=e,this.expect(y.semi),t.test=this.type===y.semi?null:this.parseExpression(),this.expect(y.semi),t.update=this.type===y.parenR?null:this.parseExpression(),this.expect(y.parenR),this.exitScope(),t.body=this.parseStatement("for"),this.labels.pop(),this.finishNode(t,"ForStatement")},rt.parseForIn=function(t,e){var i=this.type===y._in?"ForInStatement":"ForOfStatement";return this.next(),"ForInStatement"===i&&("AssignmentPattern"===e.type||"VariableDeclaration"===e.type&&null!=e.declarations[0].init&&(this.strict||"Identifier"!==e.declarations[0].id.type))&&this.raise(e.start,"Invalid assignment in for-in loop head"),t.left=e,t.right="ForInStatement"===i?this.parseExpression():this.parseMaybeAssign(),this.expect(y.parenR),this.exitScope(),t.body=this.parseStatement("for"),this.labels.pop(),this.finishNode(t,i)},rt.parseVar=function(t,e,i){for(t.declarations=[],t.kind=i;;){var s=this.startNode();if(this.parseVarId(s,i),this.eat(y.eq)?s.init=this.parseMaybeAssign(e):"const"!==i||this.type===y._in||this.options.ecmaVersion>=6&&this.isContextual("of")?"Identifier"===s.id.type||e&&(this.type===y._in||this.isContextual("of"))?s.init=null:this.raise(this.lastTokEnd,"Complex binding patterns require an initialization value"):this.unexpected(),t.declarations.push(this.finishNode(s,"VariableDeclarator")),!this.eat(y.comma))break}return t},rt.parseVarId=function(t,e){t.id=this.parseBindingAtom(e),this.checkLVal(t.id,"var"===e?Q:K,!1)};var ht=1,pt=2,ct=4;rt.parseFunction=function(t,e,i,s){this.initFunction(t),(this.options.ecmaVersion>=9||this.options.ecmaVersion>=6&&!s)&&(t.generator=this.eat(y.star)),this.options.ecmaVersion>=8&&(t.async=!!s),e&ht&&(t.id=e&ct&&this.type!==y.name?null:this.parseIdent(),!t.id||e&pt||this.checkLVal(t.id,this.inModule&&!this.inFunction?K:X));var r=this.yieldPos,a=this.awaitPos;return this.yieldPos=0,this.awaitPos=0,this.enterScope(W(t.async,t.generator)),e&ht||(t.id=this.type===y.name?this.parseIdent():null),this.parseFunctionParams(t),this.parseFunctionBody(t,i),this.yieldPos=r,this.awaitPos=a,this.finishNode(t,e&ht?"FunctionDeclaration":"FunctionExpression")},rt.parseFunctionParams=function(t){this.expect(y.parenL),t.params=this.parseBindingList(y.parenR,!1,this.options.ecmaVersion>=8),this.checkYieldAwaitInDefaultParams()},rt.parseClass=function(t,e){this.next(),this.parseClassId(t,e),this.parseClassSuper(t);var i=this.startNode(),s=!1;for(i.body=[],this.expect(y.braceL);!this.eat(y.braceR);){var r=this.parseClassElement(null!==t.superClass);r&&(i.body.push(r),"MethodDefinition"===r.type&&"constructor"===r.kind&&(s&&this.raise(r.start,"Duplicate constructor in the same class"),s=!0))}return t.body=this.finishNode(i,"ClassBody"),this.finishNode(t,e?"ClassDeclaration":"ClassExpression")},rt.parseClassElement=function(t){var e=this;if(this.eat(y.semi))return null;var i=this.startNode(),s=function(t,s){void 0===s&&(s=!1);var r=e.start,a=e.startLoc;return!!e.eatContextual(t)&&(!(e.type===y.parenL||s&&e.canInsertSemicolon())||(i.key&&e.unexpected(),i.computed=!1,i.key=e.startNodeAt(r,a),i.key.name=t,e.finishNode(i.key,"Identifier"),!1))};i.kind="method",i.static=s("static");var r=this.eat(y.star),a=!1;r||(this.options.ecmaVersion>=8&&s("async",!0)?(a=!0,r=this.options.ecmaVersion>=9&&this.eat(y.star)):s("get")?i.kind="get":s("set")&&(i.kind="set")),i.key||this.parsePropertyName(i);var n=i.key,o=!1;return i.computed||i.static||!("Identifier"===n.type&&"constructor"===n.name||"Literal"===n.type&&"constructor"===n.value)?i.static&&"Identifier"===n.type&&"prototype"===n.name&&this.raise(n.start,"Classes may not have a static property named prototype"):("method"!==i.kind&&this.raise(n.start,"Constructor can't have get/set modifier"),r&&this.raise(n.start,"Constructor can't be a generator"),a&&this.raise(n.start,"Constructor can't be an async method"),i.kind="constructor",o=t),this.parseClassMethod(i,r,a,o),"get"===i.kind&&0!==i.value.params.length&&this.raiseRecoverable(i.value.start,"getter should have no params"),"set"===i.kind&&1!==i.value.params.length&&this.raiseRecoverable(i.value.start,"setter should have exactly one param"),"set"===i.kind&&"RestElement"===i.value.params[0].type&&this.raiseRecoverable(i.value.params[0].start,"Setter cannot use rest params"),i},rt.parseClassMethod=function(t,e,i,s){return t.value=this.parseMethod(e,i,s),this.finishNode(t,"MethodDefinition")},rt.parseClassId=function(t,e){t.id=this.type===y.name?this.parseIdent():!0===e?this.unexpected():null},rt.parseClassSuper=function(t){t.superClass=this.eat(y._extends)?this.parseExprSubscripts():null},rt.parseExport=function(t,e){if(this.next(),this.eat(y.star))return this.expectContextual("from"),this.type!==y.string&&this.unexpected(),t.source=this.parseExprAtom(),this.semicolon(),this.finishNode(t,"ExportAllDeclaration");if(this.eat(y._default)){var i;if(this.checkExport(e,"default",this.lastTokStart),this.type===y._function||(i=this.isAsyncFunction())){var s=this.startNode();this.next(),i&&this.next(),t.declaration=this.parseFunction(s,ht|ct,!1,i,!0)}else if(this.type===y._class){var r=this.startNode();t.declaration=this.parseClass(r,"nullableID")}else t.declaration=this.parseMaybeAssign(),this.semicolon();return this.finishNode(t,"ExportDefaultDeclaration")}if(this.shouldParseExportStatement())t.declaration=this.parseStatement(null),"VariableDeclaration"===t.declaration.type?this.checkVariableExport(e,t.declaration.declarations):this.checkExport(e,t.declaration.id.name,t.declaration.id.start),t.specifiers=[],t.source=null;else{if(t.declaration=null,t.specifiers=this.parseExportSpecifiers(e),this.eatContextual("from"))this.type!==y.string&&this.unexpected(),t.source=this.parseExprAtom();else{for(var a=0,n=t.specifiers;a=6&&t)switch(t.type){case"Identifier":this.inAsync&&"await"===t.name&&this.raise(t.start,"Can not use 'await' as identifier inside an async function");break;case"ObjectPattern":case"ArrayPattern":case"RestElement":break;case"ObjectExpression":t.type="ObjectPattern",i&&this.checkPatternErrors(i,!0);for(var s=0,r=t.properties;s=8&&!a&&"async"===n.name&&!this.canInsertSemicolon()&&this.eat(y._function))return this.parseFunction(this.startNodeAt(s,r),0,!1,!0);if(i&&!this.canInsertSemicolon()){if(this.eat(y.arrow))return this.parseArrowExpression(this.startNodeAt(s,r),[n],!1);if(this.options.ecmaVersion>=8&&"async"===n.name&&this.type===y.name&&!a)return n=this.parseIdent(),!this.canInsertSemicolon()&&this.eat(y.arrow)||this.unexpected(),this.parseArrowExpression(this.startNodeAt(s,r),[n],!0)}return n;case y.regexp:var o=this.value;return(e=this.parseLiteral(o.value)).regex={pattern:o.pattern,flags:o.flags},e;case y.num:case y.string:return this.parseLiteral(this.value);case y._null:case y._true:case y._false:return(e=this.startNode()).value=this.type===y._null?null:this.type===y._true,e.raw=this.type.keyword,this.next(),this.finishNode(e,"Literal");case y.parenL:var h=this.start,p=this.parseParenAndDistinguishExpression(i);return t&&(t.parenthesizedAssign<0&&!this.isSimpleAssignTarget(p)&&(t.parenthesizedAssign=h),t.parenthesizedBind<0&&(t.parenthesizedBind=h)),p;case y.bracketL:return e=this.startNode(),this.next(),e.elements=this.parseExprList(y.bracketR,!0,!0,t),this.finishNode(e,"ArrayExpression");case y.braceL:return this.parseObj(!1,t);case y._function:return e=this.startNode(),this.next(),this.parseFunction(e,0);case y._class:return this.parseClass(this.startNode(),!1);case y._new:return this.parseNew();case y.backQuote:return this.parseTemplate();default:this.unexpected()}},lt.parseLiteral=function(t){var e=this.startNode();return e.value=t,e.raw=this.input.slice(this.start,this.end),this.next(),this.finishNode(e,"Literal")},lt.parseParenExpression=function(){this.expect(y.parenL);var t=this.parseExpression();return this.expect(y.parenR),t},lt.parseParenAndDistinguishExpression=function(t){var e,i=this.start,s=this.startLoc,r=this.options.ecmaVersion>=8;if(this.options.ecmaVersion>=6){this.next();var a,n=this.start,o=this.startLoc,h=[],p=!0,c=!1,u=new st,l=this.yieldPos,d=this.awaitPos;for(this.yieldPos=0,this.awaitPos=0;this.type!==y.parenR;){if(p?p=!1:this.expect(y.comma),r&&this.afterTrailingComma(y.parenR,!0)){c=!0;break}if(this.type===y.ellipsis){a=this.start,h.push(this.parseParenItem(this.parseRestBinding())),this.type===y.comma&&this.raise(this.start,"Comma is not permitted after the rest element");break}h.push(this.parseMaybeAssign(!1,u,this.parseParenItem))}var f=this.start,m=this.startLoc;if(this.expect(y.parenR),t&&!this.canInsertSemicolon()&&this.eat(y.arrow))return this.checkPatternErrors(u,!1),this.checkYieldAwaitInDefaultParams(),this.yieldPos=l,this.awaitPos=d,this.parseParenArrowList(i,s,h);h.length&&!c||this.unexpected(this.lastTokStart),a&&this.unexpected(a),this.checkExpressionErrors(u,!0),this.yieldPos=l||this.yieldPos,this.awaitPos=d||this.awaitPos,h.length>1?((e=this.startNodeAt(n,o)).expressions=h,this.finishNodeAt(e,"SequenceExpression",f,m)):e=h[0]}else e=this.parseParenExpression();if(this.options.preserveParens){var x=this.startNodeAt(i,s);return x.expression=e,this.finishNode(x,"ParenthesizedExpression")}return e},lt.parseParenItem=function(t){return t},lt.parseParenArrowList=function(t,e,i){return this.parseArrowExpression(this.startNodeAt(t,e),i)};var dt=[];lt.parseNew=function(){var t=this.startNode(),e=this.parseIdent(!0);if(this.options.ecmaVersion>=6&&this.eat(y.dot)){t.meta=e;var i=this.containsEsc;return t.property=this.parseIdent(!0),("target"!==t.property.name||i)&&this.raiseRecoverable(t.property.start,"The only valid meta property for new is new.target"),this.inNonArrowFunction()||this.raiseRecoverable(t.start,"new.target can only be used in functions"),this.finishNode(t,"MetaProperty")}var s=this.start,r=this.startLoc;return t.callee=this.parseSubscripts(this.parseExprAtom(),s,r,!0),this.eat(y.parenL)?t.arguments=this.parseExprList(y.parenR,this.options.ecmaVersion>=8,!1):t.arguments=dt,this.finishNode(t,"NewExpression")},lt.parseTemplateElement=function(t){var e=t.isTagged,i=this.startNode();return this.type===y.invalidTemplate?(e||this.raiseRecoverable(this.start,"Bad escape sequence in untagged template literal"),i.value={raw:this.value,cooked:null}):i.value={raw:this.input.slice(this.start,this.end).replace(/\r\n?/g,"\n"),cooked:this.value},this.next(),i.tail=this.type===y.backQuote,this.finishNode(i,"TemplateElement")},lt.parseTemplate=function(t){void 0===t&&(t={});var e=t.isTagged;void 0===e&&(e=!1);var i=this.startNode();this.next(),i.expressions=[];var s=this.parseTemplateElement({isTagged:e});for(i.quasis=[s];!s.tail;)this.type===y.eof&&this.raise(this.pos,"Unterminated template literal"),this.expect(y.dollarBraceL),i.expressions.push(this.parseExpression()),this.expect(y.braceR),i.quasis.push(s=this.parseTemplateElement({isTagged:e}));return this.next(),this.finishNode(i,"TemplateLiteral")},lt.isAsyncProp=function(t){return!t.computed&&"Identifier"===t.key.type&&"async"===t.key.name&&(this.type===y.name||this.type===y.num||this.type===y.string||this.type===y.bracketL||this.type.keyword||this.options.ecmaVersion>=9&&this.type===y.star)&&!_.test(this.input.slice(this.lastTokEnd,this.start))},lt.parseObj=function(t,e){var i=this.startNode(),s=!0,r={};for(i.properties=[],this.next();!this.eat(y.braceR);){if(s)s=!1;else if(this.expect(y.comma),this.afterTrailingComma(y.braceR))break;var a=this.parseProperty(t,e);t||this.checkPropClash(a,r,e),i.properties.push(a)}return this.finishNode(i,t?"ObjectPattern":"ObjectExpression")},lt.parseProperty=function(t,e){var i,s,r,a,n=this.startNode();if(this.options.ecmaVersion>=9&&this.eat(y.ellipsis))return t?(n.argument=this.parseIdent(!1),this.type===y.comma&&this.raise(this.start,"Comma is not permitted after the rest element"),this.finishNode(n,"RestElement")):(this.type===y.parenL&&e&&(e.parenthesizedAssign<0&&(e.parenthesizedAssign=this.start),e.parenthesizedBind<0&&(e.parenthesizedBind=this.start)),n.argument=this.parseMaybeAssign(!1,e),this.type===y.comma&&e&&e.trailingComma<0&&(e.trailingComma=this.start),this.finishNode(n,"SpreadElement"));this.options.ecmaVersion>=6&&(n.method=!1,n.shorthand=!1,(t||e)&&(r=this.start,a=this.startLoc),t||(i=this.eat(y.star)));var o=this.containsEsc;return this.parsePropertyName(n),!t&&!o&&this.options.ecmaVersion>=8&&!i&&this.isAsyncProp(n)?(s=!0,i=this.options.ecmaVersion>=9&&this.eat(y.star),this.parsePropertyName(n,e)):s=!1,this.parsePropertyValue(n,t,i,s,r,a,e,o),this.finishNode(n,"Property")},lt.parsePropertyValue=function(t,e,i,s,r,a,n,o){if((i||s)&&this.type===y.colon&&this.unexpected(),this.eat(y.colon))t.value=e?this.parseMaybeDefault(this.start,this.startLoc):this.parseMaybeAssign(!1,n),t.kind="init";else if(this.options.ecmaVersion>=6&&this.type===y.parenL)e&&this.unexpected(),t.kind="init",t.method=!0,t.value=this.parseMethod(i,s);else if(e||o||!(this.options.ecmaVersion>=5)||t.computed||"Identifier"!==t.key.type||"get"!==t.key.name&&"set"!==t.key.name||this.type===y.comma||this.type===y.braceR)this.options.ecmaVersion>=6&&!t.computed&&"Identifier"===t.key.type?(this.checkUnreserved(t.key),t.kind="init",e?t.value=this.parseMaybeDefault(r,a,t.key):this.type===y.eq&&n?(n.shorthandAssign<0&&(n.shorthandAssign=this.start),t.value=this.parseMaybeDefault(r,a,t.key)):t.value=t.key,t.shorthand=!0):this.unexpected();else{(i||s)&&this.unexpected(),t.kind=t.key.name,this.parsePropertyName(t),t.value=this.parseMethod(!1);var h="get"===t.kind?0:1;if(t.value.params.length!==h){var p=t.value.start;"get"===t.kind?this.raiseRecoverable(p,"getter should have no params"):this.raiseRecoverable(p,"setter should have exactly one param")}else"set"===t.kind&&"RestElement"===t.value.params[0].type&&this.raiseRecoverable(t.value.params[0].start,"Setter cannot use rest params")}},lt.parsePropertyName=function(t){if(this.options.ecmaVersion>=6){if(this.eat(y.bracketL))return t.computed=!0,t.key=this.parseMaybeAssign(),this.expect(y.bracketR),t.key;t.computed=!1}return t.key=this.type===y.num||this.type===y.string?this.parseExprAtom():this.parseIdent(!0)},lt.initFunction=function(t){t.id=null,this.options.ecmaVersion>=6&&(t.generator=t.expression=!1),this.options.ecmaVersion>=8&&(t.async=!1)},lt.parseMethod=function(t,e,i){var s=this.startNode(),r=this.yieldPos,a=this.awaitPos;return this.initFunction(s),this.options.ecmaVersion>=6&&(s.generator=t),this.options.ecmaVersion>=8&&(s.async=!!e),this.yieldPos=0,this.awaitPos=0,this.enterScope(W(e,s.generator)|H|(i?j:0)),this.expect(y.parenL),s.params=this.parseBindingList(y.parenR,!1,this.options.ecmaVersion>=8),this.checkYieldAwaitInDefaultParams(),this.parseFunctionBody(s,!1),this.yieldPos=r,this.awaitPos=a,this.finishNode(s,"FunctionExpression")},lt.parseArrowExpression=function(t,e,i){var s=this.yieldPos,r=this.awaitPos;return this.enterScope(W(i,!1)|q),this.initFunction(t),this.options.ecmaVersion>=8&&(t.async=!!i),this.yieldPos=0,this.awaitPos=0,t.params=this.toAssignableList(e,!0),this.parseFunctionBody(t,!0),this.yieldPos=s,this.awaitPos=r,this.finishNode(t,"ArrowFunctionExpression")},lt.parseFunctionBody=function(t,e){var i=e&&this.type!==y.braceL,s=this.strict,r=!1;if(i)t.body=this.parseMaybeAssign(),t.expression=!0,this.checkParams(t,!1);else{var a=this.options.ecmaVersion>=7&&!this.isSimpleParamList(t.params);s&&!a||(r=this.strictDirective(this.end))&&a&&this.raiseRecoverable(t.start,"Illegal 'use strict' directive in function with non-simple parameter list");var n=this.labels;this.labels=[],r&&(this.strict=!0),this.checkParams(t,!s&&!r&&!e&&this.isSimpleParamList(t.params)),t.body=this.parseBlock(!1),t.expression=!1,this.adaptDirectivePrologue(t.body.body),this.labels=n}this.exitScope(),this.strict&&t.id&&this.checkLVal(t.id,Z),this.strict=s},lt.isSimpleParamList=function(t){for(var e=0,i=t;e-1||r.var.indexOf(t)>-1,r.lexical.push(t)}else if(e===Y){this.currentScope().lexical.push(t)}else if(e===X){var a=this.currentScope();s=a.lexical.indexOf(t)>-1,a.var.push(t)}else for(var n=this.scopeStack.length-1;n>=0;--n){var o=this.scopeStack[n];if(o.lexical.indexOf(t)>-1&&!(o.flags&G)&&o.lexical[0]===t&&(s=!0),o.var.push(t),o.flags&M)break}s&&this.raiseRecoverable(i,"Identifier '"+t+"' has already been declared")},mt.currentScope=function(){return this.scopeStack[this.scopeStack.length-1]},mt.currentVarScope=function(){for(var t=this.scopeStack.length-1;;t--){var e=this.scopeStack[t];if(e.flags&M)return e}},mt.currentThisScope=function(){for(var t=this.scopeStack.length-1;;t--){var e=this.scopeStack[t];if(e.flags&M&&!(e.flags&q))return e}};var gt=function(t,e,i){this.type="",this.start=e,this.end=0,t.options.locations&&(this.loc=new L(t,i)),t.options.directSourceFile&&(this.sourceFile=t.options.directSourceFile),t.options.ranges&&(this.range=[e,0])};exports.Node=gt;var vt=J.prototype;function yt(t,e,i,s){return t.type=e,t.end=i,this.options.locations&&(t.loc.end=s),this.options.ranges&&(t.range[1]=i),t}vt.startNode=function(){return new gt(this,this.start,this.startLoc)},vt.startNodeAt=function(t,e){return new gt(this,t,e)},vt.finishNode=function(t,e){return yt.call(this,t,e,this.lastTokEnd,this.lastTokEndLoc)},vt.finishNodeAt=function(t,e,i,s){return yt.call(this,t,e,i,s)};var _t=function(t,e,i,s,r){this.token=t,this.isExpr=!!e,this.preserveSpace=!!i,this.override=s,this.generator=!!r};exports.TokContext=_t;var bt={b_stat:new _t("{",!1),b_expr:new _t("{",!0),b_tmpl:new _t("${",!1),p_stat:new _t("(",!1),p_expr:new _t("(",!0),q_tmpl:new _t("`",!0,!0,function(t){return t.tryReadTemplateToken()}),f_stat:new _t("function",!1),f_expr:new _t("function",!0),f_expr_gen:new _t("function",!0,!1,null,!0),f_gen:new _t("function",!1,!1,null,!0)};exports.tokContexts=bt;var kt=J.prototype;kt.initialContext=function(){return[bt.b_stat]},kt.braceIsBlock=function(t){var e=this.curContext();return e===bt.f_expr||e===bt.f_stat||(t!==y.colon||e!==bt.b_stat&&e!==bt.b_expr?t===y._return||t===y.name&&this.exprAllowed?_.test(this.input.slice(this.lastTokEnd,this.start)):t===y._else||t===y.semi||t===y.eof||t===y.parenR||t===y.arrow||(t===y.braceL?e===bt.b_stat:t!==y._var&&t!==y._const&&t!==y.name&&!this.exprAllowed):!e.isExpr)},kt.inGeneratorContext=function(){for(var t=this.context.length-1;t>=1;t--){var e=this.context[t];if("function"===e.token)return e.generator}return!1},kt.updateContext=function(t){var e,i=this.type;i.keyword&&t===y.dot?this.exprAllowed=!1:(e=i.updateContext)?e.call(this,t):this.exprAllowed=i.beforeExpr},y.parenR.updateContext=y.braceR.updateContext=function(){if(1!==this.context.length){var t=this.context.pop();t===bt.b_stat&&"function"===this.curContext().token&&(t=this.context.pop()),this.exprAllowed=!t.isExpr}else this.exprAllowed=!0},y.braceL.updateContext=function(t){this.context.push(this.braceIsBlock(t)?bt.b_stat:bt.b_expr),this.exprAllowed=!0},y.dollarBraceL.updateContext=function(){this.context.push(bt.b_tmpl),this.exprAllowed=!0},y.parenL.updateContext=function(t){var e=t===y._if||t===y._for||t===y._with||t===y._while;this.context.push(e?bt.p_stat:bt.p_expr),this.exprAllowed=!0},y.incDec.updateContext=function(){},y._function.updateContext=y._class.updateContext=function(t){!t.beforeExpr||t===y.semi||t===y._else||t===y._return&&_.test(this.input.slice(this.lastTokEnd,this.start))||(t===y.colon||t===y.braceL)&&this.curContext()===bt.b_stat?this.context.push(bt.f_stat):this.context.push(bt.f_expr),this.exprAllowed=!1},y.backQuote.updateContext=function(){this.curContext()===bt.q_tmpl?this.context.pop():this.context.push(bt.q_tmpl),this.exprAllowed=!1},y.star.updateContext=function(t){if(t===y._function){var e=this.context.length-1;this.context[e]===bt.f_expr?this.context[e]=bt.f_expr_gen:this.context[e]=bt.f_gen}this.exprAllowed=!0},y.name.updateContext=function(t){var e=!1;this.options.ecmaVersion>=6&&t!==y.dot&&("of"===this.value&&!this.exprAllowed||"yield"===this.value&&this.inGeneratorContext())&&(e=!0),this.exprAllowed=e};var St={$LONE:["ASCII","ASCII_Hex_Digit","AHex","Alphabetic","Alpha","Any","Assigned","Bidi_Control","Bidi_C","Bidi_Mirrored","Bidi_M","Case_Ignorable","CI","Cased","Changes_When_Casefolded","CWCF","Changes_When_Casemapped","CWCM","Changes_When_Lowercased","CWL","Changes_When_NFKC_Casefolded","CWKCF","Changes_When_Titlecased","CWT","Changes_When_Uppercased","CWU","Dash","Default_Ignorable_Code_Point","DI","Deprecated","Dep","Diacritic","Dia","Emoji","Emoji_Component","Emoji_Modifier","Emoji_Modifier_Base","Emoji_Presentation","Extender","Ext","Grapheme_Base","Gr_Base","Grapheme_Extend","Gr_Ext","Hex_Digit","Hex","IDS_Binary_Operator","IDSB","IDS_Trinary_Operator","IDST","ID_Continue","IDC","ID_Start","IDS","Ideographic","Ideo","Join_Control","Join_C","Logical_Order_Exception","LOE","Lowercase","Lower","Math","Noncharacter_Code_Point","NChar","Pattern_Syntax","Pat_Syn","Pattern_White_Space","Pat_WS","Quotation_Mark","QMark","Radical","Regional_Indicator","RI","Sentence_Terminal","STerm","Soft_Dotted","SD","Terminal_Punctuation","Term","Unified_Ideograph","UIdeo","Uppercase","Upper","Variation_Selector","VS","White_Space","space","XID_Continue","XIDC","XID_Start","XIDS"],General_Category:["Cased_Letter","LC","Close_Punctuation","Pe","Connector_Punctuation","Pc","Control","Cc","cntrl","Currency_Symbol","Sc","Dash_Punctuation","Pd","Decimal_Number","Nd","digit","Enclosing_Mark","Me","Final_Punctuation","Pf","Format","Cf","Initial_Punctuation","Pi","Letter","L","Letter_Number","Nl","Line_Separator","Zl","Lowercase_Letter","Ll","Mark","M","Combining_Mark","Math_Symbol","Sm","Modifier_Letter","Lm","Modifier_Symbol","Sk","Nonspacing_Mark","Mn","Number","N","Open_Punctuation","Ps","Other","C","Other_Letter","Lo","Other_Number","No","Other_Punctuation","Po","Other_Symbol","So","Paragraph_Separator","Zp","Private_Use","Co","Punctuation","P","punct","Separator","Z","Space_Separator","Zs","Spacing_Mark","Mc","Surrogate","Cs","Symbol","S","Titlecase_Letter","Lt","Unassigned","Cn","Uppercase_Letter","Lu"],Script:["Adlam","Adlm","Ahom","Anatolian_Hieroglyphs","Hluw","Arabic","Arab","Armenian","Armn","Avestan","Avst","Balinese","Bali","Bamum","Bamu","Bassa_Vah","Bass","Batak","Batk","Bengali","Beng","Bhaiksuki","Bhks","Bopomofo","Bopo","Brahmi","Brah","Braille","Brai","Buginese","Bugi","Buhid","Buhd","Canadian_Aboriginal","Cans","Carian","Cari","Caucasian_Albanian","Aghb","Chakma","Cakm","Cham","Cherokee","Cher","Common","Zyyy","Coptic","Copt","Qaac","Cuneiform","Xsux","Cypriot","Cprt","Cyrillic","Cyrl","Deseret","Dsrt","Devanagari","Deva","Duployan","Dupl","Egyptian_Hieroglyphs","Egyp","Elbasan","Elba","Ethiopic","Ethi","Georgian","Geor","Glagolitic","Glag","Gothic","Goth","Grantha","Gran","Greek","Grek","Gujarati","Gujr","Gurmukhi","Guru","Han","Hani","Hangul","Hang","Hanunoo","Hano","Hatran","Hatr","Hebrew","Hebr","Hiragana","Hira","Imperial_Aramaic","Armi","Inherited","Zinh","Qaai","Inscriptional_Pahlavi","Phli","Inscriptional_Parthian","Prti","Javanese","Java","Kaithi","Kthi","Kannada","Knda","Katakana","Kana","Kayah_Li","Kali","Kharoshthi","Khar","Khmer","Khmr","Khojki","Khoj","Khudawadi","Sind","Lao","Laoo","Latin","Latn","Lepcha","Lepc","Limbu","Limb","Linear_A","Lina","Linear_B","Linb","Lisu","Lycian","Lyci","Lydian","Lydi","Mahajani","Mahj","Malayalam","Mlym","Mandaic","Mand","Manichaean","Mani","Marchen","Marc","Masaram_Gondi","Gonm","Meetei_Mayek","Mtei","Mende_Kikakui","Mend","Meroitic_Cursive","Merc","Meroitic_Hieroglyphs","Mero","Miao","Plrd","Modi","Mongolian","Mong","Mro","Mroo","Multani","Mult","Myanmar","Mymr","Nabataean","Nbat","New_Tai_Lue","Talu","Newa","Nko","Nkoo","Nushu","Nshu","Ogham","Ogam","Ol_Chiki","Olck","Old_Hungarian","Hung","Old_Italic","Ital","Old_North_Arabian","Narb","Old_Permic","Perm","Old_Persian","Xpeo","Old_South_Arabian","Sarb","Old_Turkic","Orkh","Oriya","Orya","Osage","Osge","Osmanya","Osma","Pahawh_Hmong","Hmng","Palmyrene","Palm","Pau_Cin_Hau","Pauc","Phags_Pa","Phag","Phoenician","Phnx","Psalter_Pahlavi","Phlp","Rejang","Rjng","Runic","Runr","Samaritan","Samr","Saurashtra","Saur","Sharada","Shrd","Shavian","Shaw","Siddham","Sidd","SignWriting","Sgnw","Sinhala","Sinh","Sora_Sompeng","Sora","Soyombo","Soyo","Sundanese","Sund","Syloti_Nagri","Sylo","Syriac","Syrc","Tagalog","Tglg","Tagbanwa","Tagb","Tai_Le","Tale","Tai_Tham","Lana","Tai_Viet","Tavt","Takri","Takr","Tamil","Taml","Tangut","Tang","Telugu","Telu","Thaana","Thaa","Thai","Tibetan","Tibt","Tifinagh","Tfng","Tirhuta","Tirh","Ugaritic","Ugar","Vai","Vaii","Warang_Citi","Wara","Yi","Yiii","Zanabazar_Square","Zanb"]};Array.prototype.push.apply(St.$LONE,St.General_Category),St.gc=St.General_Category,St.sc=St.Script_Extensions=St.scx=St.Script;var Ct=J.prototype,Et=function(t){this.parser=t,this.validFlags="gim"+(t.options.ecmaVersion>=6?"uy":"")+(t.options.ecmaVersion>=9?"s":""),this.source="",this.flags="",this.start=0,this.switchU=!1,this.switchN=!1,this.pos=0,this.lastIntValue=0,this.lastStringValue="",this.lastAssertionIsQuantifiable=!1,this.numCapturingParens=0,this.maxBackReference=0,this.groupNames=[],this.backReferenceNames=[]};function wt(t){return t<=65535?String.fromCharCode(t):(t-=65536,String.fromCharCode(55296+(t>>10),56320+(1023&t)))}function At(t){return 36===t||t>=40&&t<=43||46===t||63===t||t>=91&&t<=94||t>=123&&t<=125}function It(t){return u(t,!0)||36===t||95===t}function Pt(t){return l(t,!0)||36===t||95===t||8204===t||8205===t}function Tt(t){return t>=65&&t<=90||t>=97&&t<=122}function Lt(t){return t>=0&&t<=1114111}function Nt(t){return 100===t||68===t||115===t||83===t||119===t||87===t}function Vt(t){return Tt(t)||95===t}function Rt(t){return Vt(t)||Ot(t)}function Ot(t){return t>=48&&t<=57}function Dt(t){return t>=48&&t<=57||t>=65&&t<=70||t>=97&&t<=102}function Bt(t){return t>=65&&t<=70?t-65+10:t>=97&&t<=102?t-97+10:t-48}function Mt(t){return t>=48&&t<=55}Et.prototype.reset=function(t,e,i){var s=-1!==i.indexOf("u");this.start=0|t,this.source=e+"",this.flags=i,this.switchU=s&&this.parser.options.ecmaVersion>=6,this.switchN=s&&this.parser.options.ecmaVersion>=9},Et.prototype.raise=function(t){this.parser.raiseRecoverable(this.start,"Invalid regular expression: /"+this.source+"/: "+t)},Et.prototype.at=function(t){var e=this.source,i=e.length;if(t>=i)return-1;var s=e.charCodeAt(t);return!this.switchU||s<=55295||s>=57344||t+1>=i?s:(s<<10)+e.charCodeAt(t+1)-56613888},Et.prototype.nextIndex=function(t){var e=this.source,i=e.length;if(t>=i)return i;var s=e.charCodeAt(t);return!this.switchU||s<=55295||s>=57344||t+1>=i?t+1:t+2},Et.prototype.current=function(){return this.at(this.pos)},Et.prototype.lookahead=function(){return this.at(this.nextIndex(this.pos))},Et.prototype.advance=function(){this.pos=this.nextIndex(this.pos)},Et.prototype.eat=function(t){return this.current()===t&&(this.advance(),!0)},Ct.validateRegExpFlags=function(t){for(var e=t.validFlags,i=t.flags,s=0;s-1&&this.raise(t.start,"Duplicate regular expression flag")}},Ct.validateRegExpPattern=function(t){this.regexp_pattern(t),!t.switchN&&this.options.ecmaVersion>=9&&t.groupNames.length>0&&(t.switchN=!0,this.regexp_pattern(t))},Ct.regexp_pattern=function(t){t.pos=0,t.lastIntValue=0,t.lastStringValue="",t.lastAssertionIsQuantifiable=!1,t.numCapturingParens=0,t.maxBackReference=0,t.groupNames.length=0,t.backReferenceNames.length=0,this.regexp_disjunction(t),t.pos!==t.source.length&&(t.eat(41)&&t.raise("Unmatched ')'"),(t.eat(93)||t.eat(125))&&t.raise("Lone quantifier brackets")),t.maxBackReference>t.numCapturingParens&&t.raise("Invalid escape");for(var e=0,i=t.backReferenceNames;e=9&&(i=t.eat(60)),t.eat(61)||t.eat(33))return this.regexp_disjunction(t),t.eat(41)||t.raise("Unterminated group"),t.lastAssertionIsQuantifiable=!i,!0}return t.pos=e,!1},Ct.regexp_eatQuantifier=function(t,e){return void 0===e&&(e=!1),!!this.regexp_eatQuantifierPrefix(t,e)&&(t.eat(63),!0)},Ct.regexp_eatQuantifierPrefix=function(t,e){return t.eat(42)||t.eat(43)||t.eat(63)||this.regexp_eatBracedQuantifier(t,e)},Ct.regexp_eatBracedQuantifier=function(t,e){var i=t.pos;if(t.eat(123)){var s=0,r=-1;if(this.regexp_eatDecimalDigits(t)&&(s=t.lastIntValue,t.eat(44)&&this.regexp_eatDecimalDigits(t)&&(r=t.lastIntValue),t.eat(125)))return-1!==r&&r=9?this.regexp_groupSpecifier(t):63===t.current()&&t.raise("Invalid group"),this.regexp_disjunction(t),t.eat(41))return t.numCapturingParens+=1,!0;t.raise("Unterminated group")}return!1},Ct.regexp_eatExtendedAtom=function(t){return t.eat(46)||this.regexp_eatReverseSolidusAtomEscape(t)||this.regexp_eatCharacterClass(t)||this.regexp_eatUncapturingGroup(t)||this.regexp_eatCapturingGroup(t)||this.regexp_eatInvalidBracedQuantifier(t)||this.regexp_eatExtendedPatternCharacter(t)},Ct.regexp_eatInvalidBracedQuantifier=function(t){return this.regexp_eatBracedQuantifier(t,!0)&&t.raise("Nothing to repeat"),!1},Ct.regexp_eatSyntaxCharacter=function(t){var e=t.current();return!!At(e)&&(t.lastIntValue=e,t.advance(),!0)},Ct.regexp_eatPatternCharacters=function(t){for(var e=t.pos,i=0;-1!==(i=t.current())&&!At(i);)t.advance();return t.pos!==e},Ct.regexp_eatExtendedPatternCharacter=function(t){var e=t.current();return!(-1===e||36===e||e>=40&&e<=43||46===e||63===e||91===e||94===e||124===e)&&(t.advance(),!0)},Ct.regexp_groupSpecifier=function(t){if(t.eat(63)){if(this.regexp_eatGroupName(t))return-1!==t.groupNames.indexOf(t.lastStringValue)&&t.raise("Duplicate capture group name"),void t.groupNames.push(t.lastStringValue);t.raise("Invalid group")}},Ct.regexp_eatGroupName=function(t){if(t.lastStringValue="",t.eat(60)){if(this.regexp_eatRegExpIdentifierName(t)&&t.eat(62))return!0;t.raise("Invalid capture group name")}return!1},Ct.regexp_eatRegExpIdentifierName=function(t){if(t.lastStringValue="",this.regexp_eatRegExpIdentifierStart(t)){for(t.lastStringValue+=wt(t.lastIntValue);this.regexp_eatRegExpIdentifierPart(t);)t.lastStringValue+=wt(t.lastIntValue);return!0}return!1},Ct.regexp_eatRegExpIdentifierStart=function(t){var e=t.pos,i=t.current();return t.advance(),92===i&&this.regexp_eatRegExpUnicodeEscapeSequence(t)&&(i=t.lastIntValue),It(i)?(t.lastIntValue=i,!0):(t.pos=e,!1)},Ct.regexp_eatRegExpIdentifierPart=function(t){var e=t.pos,i=t.current();return t.advance(),92===i&&this.regexp_eatRegExpUnicodeEscapeSequence(t)&&(i=t.lastIntValue),Pt(i)?(t.lastIntValue=i,!0):(t.pos=e,!1)},Ct.regexp_eatAtomEscape=function(t){return!!(this.regexp_eatBackReference(t)||this.regexp_eatCharacterClassEscape(t)||this.regexp_eatCharacterEscape(t)||t.switchN&&this.regexp_eatKGroupName(t))||(t.switchU&&(99===t.current()&&t.raise("Invalid unicode escape"),t.raise("Invalid escape")),!1)},Ct.regexp_eatBackReference=function(t){var e=t.pos;if(this.regexp_eatDecimalEscape(t)){var i=t.lastIntValue;if(t.switchU)return i>t.maxBackReference&&(t.maxBackReference=i),!0;if(i<=t.numCapturingParens)return!0;t.pos=e}return!1},Ct.regexp_eatKGroupName=function(t){if(t.eat(107)){if(this.regexp_eatGroupName(t))return t.backReferenceNames.push(t.lastStringValue),!0;t.raise("Invalid named reference")}return!1},Ct.regexp_eatCharacterEscape=function(t){return this.regexp_eatControlEscape(t)||this.regexp_eatCControlLetter(t)||this.regexp_eatZero(t)||this.regexp_eatHexEscapeSequence(t)||this.regexp_eatRegExpUnicodeEscapeSequence(t)||!t.switchU&&this.regexp_eatLegacyOctalEscapeSequence(t)||this.regexp_eatIdentityEscape(t)},Ct.regexp_eatCControlLetter=function(t){var e=t.pos;if(t.eat(99)){if(this.regexp_eatControlLetter(t))return!0;t.pos=e}return!1},Ct.regexp_eatZero=function(t){return 48===t.current()&&!Ot(t.lookahead())&&(t.lastIntValue=0,t.advance(),!0)},Ct.regexp_eatControlEscape=function(t){var e=t.current();return 116===e?(t.lastIntValue=9,t.advance(),!0):110===e?(t.lastIntValue=10,t.advance(),!0):118===e?(t.lastIntValue=11,t.advance(),!0):102===e?(t.lastIntValue=12,t.advance(),!0):114===e&&(t.lastIntValue=13,t.advance(),!0)},Ct.regexp_eatControlLetter=function(t){var e=t.current();return!!Tt(e)&&(t.lastIntValue=e%32,t.advance(),!0)},Ct.regexp_eatRegExpUnicodeEscapeSequence=function(t){var e=t.pos;if(t.eat(117)){if(this.regexp_eatFixedHexDigits(t,4)){var i=t.lastIntValue;if(t.switchU&&i>=55296&&i<=56319){var s=t.pos;if(t.eat(92)&&t.eat(117)&&this.regexp_eatFixedHexDigits(t,4)){var r=t.lastIntValue;if(r>=56320&&r<=57343)return t.lastIntValue=1024*(i-55296)+(r-56320)+65536,!0}t.pos=s,t.lastIntValue=i}return!0}if(t.switchU&&t.eat(123)&&this.regexp_eatHexDigits(t)&&t.eat(125)&&Lt(t.lastIntValue))return!0;t.switchU&&t.raise("Invalid unicode escape"),t.pos=e}return!1},Ct.regexp_eatIdentityEscape=function(t){if(t.switchU)return!!this.regexp_eatSyntaxCharacter(t)||!!t.eat(47)&&(t.lastIntValue=47,!0);var e=t.current();return!(99===e||t.switchN&&107===e)&&(t.lastIntValue=e,t.advance(),!0)},Ct.regexp_eatDecimalEscape=function(t){t.lastIntValue=0;var e=t.current();if(e>=49&&e<=57){do{t.lastIntValue=10*t.lastIntValue+(e-48),t.advance()}while((e=t.current())>=48&&e<=57);return!0}return!1},Ct.regexp_eatCharacterClassEscape=function(t){var e=t.current();if(Nt(e))return t.lastIntValue=-1,t.advance(),!0;if(t.switchU&&this.options.ecmaVersion>=9&&(80===e||112===e)){if(t.lastIntValue=-1,t.advance(),t.eat(123)&&this.regexp_eatUnicodePropertyValueExpression(t)&&t.eat(125))return!0;t.raise("Invalid property name")}return!1},Ct.regexp_eatUnicodePropertyValueExpression=function(t){var e=t.pos;if(this.regexp_eatUnicodePropertyName(t)&&t.eat(61)){var i=t.lastStringValue;if(this.regexp_eatUnicodePropertyValue(t)){var s=t.lastStringValue;return this.regexp_validateUnicodePropertyNameAndValue(t,i,s),!0}}if(t.pos=e,this.regexp_eatLoneUnicodePropertyNameOrValue(t)){var r=t.lastStringValue;return this.regexp_validateUnicodePropertyNameOrValue(t,r),!0}return!1},Ct.regexp_validateUnicodePropertyNameAndValue=function(t,e,i){St.hasOwnProperty(e)&&-1!==St[e].indexOf(i)||t.raise("Invalid property name")},Ct.regexp_validateUnicodePropertyNameOrValue=function(t,e){-1===St.$LONE.indexOf(e)&&t.raise("Invalid property name")},Ct.regexp_eatUnicodePropertyName=function(t){var e=0;for(t.lastStringValue="";Vt(e=t.current());)t.lastStringValue+=wt(e),t.advance();return""!==t.lastStringValue},Ct.regexp_eatUnicodePropertyValue=function(t){var e=0;for(t.lastStringValue="";Rt(e=t.current());)t.lastStringValue+=wt(e),t.advance();return""!==t.lastStringValue},Ct.regexp_eatLoneUnicodePropertyNameOrValue=function(t){return this.regexp_eatUnicodePropertyValue(t)},Ct.regexp_eatCharacterClass=function(t){if(t.eat(91)){if(t.eat(94),this.regexp_classRanges(t),t.eat(93))return!0;t.raise("Unterminated character class")}return!1},Ct.regexp_classRanges=function(t){for(;this.regexp_eatClassAtom(t);){var e=t.lastIntValue;if(t.eat(45)&&this.regexp_eatClassAtom(t)){var i=t.lastIntValue;!t.switchU||-1!==e&&-1!==i||t.raise("Invalid character class"),-1!==e&&-1!==i&&e>i&&t.raise("Range out of order in character class")}}},Ct.regexp_eatClassAtom=function(t){var e=t.pos;if(t.eat(92)){if(this.regexp_eatClassEscape(t))return!0;if(t.switchU){var i=t.current();(99===i||Mt(i))&&t.raise("Invalid class escape"),t.raise("Invalid escape")}t.pos=e}var s=t.current();return 93!==s&&(t.lastIntValue=s,t.advance(),!0)},Ct.regexp_eatClassEscape=function(t){var e=t.pos;if(t.eat(98))return t.lastIntValue=8,!0;if(t.switchU&&t.eat(45))return t.lastIntValue=45,!0;if(!t.switchU&&t.eat(99)){if(this.regexp_eatClassControlLetter(t))return!0;t.pos=e}return this.regexp_eatCharacterClassEscape(t)||this.regexp_eatCharacterEscape(t)},Ct.regexp_eatClassControlLetter=function(t){var e=t.current();return!(!Ot(e)&&95!==e)&&(t.lastIntValue=e%32,t.advance(),!0)},Ct.regexp_eatHexEscapeSequence=function(t){var e=t.pos;if(t.eat(120)){if(this.regexp_eatFixedHexDigits(t,2))return!0;t.switchU&&t.raise("Invalid escape"),t.pos=e}return!1},Ct.regexp_eatDecimalDigits=function(t){var e=t.pos,i=0;for(t.lastIntValue=0;Ot(i=t.current());)t.lastIntValue=10*t.lastIntValue+(i-48),t.advance();return t.pos!==e},Ct.regexp_eatHexDigits=function(t){var e=t.pos,i=0;for(t.lastIntValue=0;Dt(i=t.current());)t.lastIntValue=16*t.lastIntValue+Bt(i),t.advance();return t.pos!==e},Ct.regexp_eatLegacyOctalEscapeSequence=function(t){if(this.regexp_eatOctalDigit(t)){var e=t.lastIntValue;if(this.regexp_eatOctalDigit(t)){var i=t.lastIntValue;e<=3&&this.regexp_eatOctalDigit(t)?t.lastIntValue=64*e+8*i+t.lastIntValue:t.lastIntValue=8*e+i}else t.lastIntValue=e;return!0}return!1},Ct.regexp_eatOctalDigit=function(t){var e=t.current();return Mt(e)?(t.lastIntValue=e-48,t.advance(),!0):(t.lastIntValue=0,!1)},Ct.regexp_eatFixedHexDigits=function(t,e){var i=t.pos;t.lastIntValue=0;for(var s=0;s>10),56320+(1023&t)))}Ft.next=function(){this.options.onToken&&this.options.onToken(new Ut(this)),this.lastTokEnd=this.end,this.lastTokStart=this.start,this.lastTokEndLoc=this.endLoc,this.lastTokStartLoc=this.startLoc,this.nextToken()},Ft.getToken=function(){return this.next(),new Ut(this)},"undefined"!=typeof Symbol&&(Ft[Symbol.iterator]=function(){var t=this;return{next:function(){var e=t.getToken();return{done:e.type===y.eof,value:e}}}}),Ft.curContext=function(){return this.context[this.context.length-1]},Ft.nextToken=function(){var t=this.curContext();return t&&t.preserveSpace||this.skipSpace(),this.start=this.pos,this.options.locations&&(this.startLoc=this.curPosition()),this.pos>=this.input.length?this.finishToken(y.eof):t.override?t.override(this):void this.readToken(this.fullCharCodeAtPos())},Ft.readToken=function(t){return u(t,this.options.ecmaVersion>=6)||92===t?this.readWord():this.getTokenFromCode(t)},Ft.fullCharCodeAtPos=function(){var t=this.input.charCodeAt(this.pos);return t<=55295||t>=57344?t:(t<<10)+this.input.charCodeAt(this.pos+1)-56613888},Ft.skipBlockComment=function(){var t,e=this.options.onComment&&this.curPosition(),i=this.pos,s=this.input.indexOf("*/",this.pos+=2);if(-1===s&&this.raise(this.pos-2,"Unterminated comment"),this.pos=s+2,this.options.locations)for(b.lastIndex=i;(t=b.exec(this.input))&&t.index8&&t<14||t>=5760&&S.test(String.fromCharCode(t))))break t;++this.pos}}},Ft.finishToken=function(t,e){this.end=this.pos,this.options.locations&&(this.endLoc=this.curPosition());var i=this.type;this.type=t,this.value=e,this.updateContext(i)},Ft.readToken_dot=function(){var t=this.input.charCodeAt(this.pos+1);if(t>=48&&t<=57)return this.readNumber(!0);var e=this.input.charCodeAt(this.pos+2);return this.options.ecmaVersion>=6&&46===t&&46===e?(this.pos+=3,this.finishToken(y.ellipsis)):(++this.pos,this.finishToken(y.dot))},Ft.readToken_slash=function(){var t=this.input.charCodeAt(this.pos+1);return this.exprAllowed?(++this.pos,this.readRegexp()):61===t?this.finishOp(y.assign,2):this.finishOp(y.slash,1)},Ft.readToken_mult_modulo_exp=function(t){var e=this.input.charCodeAt(this.pos+1),i=1,s=42===t?y.star:y.modulo;return this.options.ecmaVersion>=7&&42===t&&42===e&&(++i,s=y.starstar,e=this.input.charCodeAt(this.pos+2)),61===e?this.finishOp(y.assign,i+1):this.finishOp(s,i)},Ft.readToken_pipe_amp=function(t){var e=this.input.charCodeAt(this.pos+1);return e===t?this.finishOp(124===t?y.logicalOR:y.logicalAND,2):61===e?this.finishOp(y.assign,2):this.finishOp(124===t?y.bitwiseOR:y.bitwiseAND,1)},Ft.readToken_caret=function(){return 61===this.input.charCodeAt(this.pos+1)?this.finishOp(y.assign,2):this.finishOp(y.bitwiseXOR,1)},Ft.readToken_plus_min=function(t){var e=this.input.charCodeAt(this.pos+1);return e===t?45!==e||this.inModule||62!==this.input.charCodeAt(this.pos+2)||0!==this.lastTokEnd&&!_.test(this.input.slice(this.lastTokEnd,this.pos))?this.finishOp(y.incDec,2):(this.skipLineComment(3),this.skipSpace(),this.nextToken()):61===e?this.finishOp(y.assign,2):this.finishOp(y.plusMin,1)},Ft.readToken_lt_gt=function(t){var e=this.input.charCodeAt(this.pos+1),i=1;return e===t?(i=62===t&&62===this.input.charCodeAt(this.pos+2)?3:2,61===this.input.charCodeAt(this.pos+i)?this.finishOp(y.assign,i+1):this.finishOp(y.bitShift,i)):33!==e||60!==t||this.inModule||45!==this.input.charCodeAt(this.pos+2)||45!==this.input.charCodeAt(this.pos+3)?(61===e&&(i=2),this.finishOp(y.relational,i)):(this.skipLineComment(4),this.skipSpace(),this.nextToken())},Ft.readToken_eq_excl=function(t){var e=this.input.charCodeAt(this.pos+1);return 61===e?this.finishOp(y.equality,61===this.input.charCodeAt(this.pos+2)?3:2):61===t&&62===e&&this.options.ecmaVersion>=6?(this.pos+=2,this.finishToken(y.arrow)):this.finishOp(61===t?y.eq:y.prefix,1)},Ft.getTokenFromCode=function(t){switch(t){case 46:return this.readToken_dot();case 40:return++this.pos,this.finishToken(y.parenL);case 41:return++this.pos,this.finishToken(y.parenR);case 59:return++this.pos,this.finishToken(y.semi);case 44:return++this.pos,this.finishToken(y.comma);case 91:return++this.pos,this.finishToken(y.bracketL);case 93:return++this.pos,this.finishToken(y.bracketR);case 123:return++this.pos,this.finishToken(y.braceL);case 125:return++this.pos,this.finishToken(y.braceR);case 58:return++this.pos,this.finishToken(y.colon);case 63:return++this.pos,this.finishToken(y.question);case 96:if(this.options.ecmaVersion<6)break;return++this.pos,this.finishToken(y.backQuote);case 48:var e=this.input.charCodeAt(this.pos+1);if(120===e||88===e)return this.readRadixNumber(16);if(this.options.ecmaVersion>=6){if(111===e||79===e)return this.readRadixNumber(8);if(98===e||66===e)return this.readRadixNumber(2)}case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return this.readNumber(!1);case 34:case 39:return this.readString(t);case 47:return this.readToken_slash();case 37:case 42:return this.readToken_mult_modulo_exp(t);case 124:case 38:return this.readToken_pipe_amp(t);case 94:return this.readToken_caret();case 43:case 45:return this.readToken_plus_min(t);case 60:case 62:return this.readToken_lt_gt(t);case 61:case 33:return this.readToken_eq_excl(t);case 126:return this.finishOp(y.prefix,1)}this.raise(this.pos,"Unexpected character '"+qt(t)+"'")},Ft.finishOp=function(t,e){var i=this.input.slice(this.pos,this.pos+e);return this.pos+=e,this.finishToken(t,i)},Ft.readRegexp=function(){for(var t,e,i=this.pos;;){this.pos>=this.input.length&&this.raise(i,"Unterminated regular expression");var s=this.input.charAt(this.pos);if(_.test(s)&&this.raise(i,"Unterminated regular expression"),t)t=!1;else{if("["===s)e=!0;else if("]"===s&&e)e=!1;else if("/"===s&&!e)break;t="\\"===s}++this.pos}var r=this.input.slice(i,this.pos);++this.pos;var a=this.pos,n=this.readWord1();this.containsEsc&&this.unexpected(a);var o=this.regexpState||(this.regexpState=new Et(this));o.reset(i,r,n),this.validateRegExpFlags(o),this.validateRegExpPattern(o);var h=null;try{h=new RegExp(r,n)}catch(p){}return this.finishToken(y.regexp,{pattern:r,flags:n,value:h})},Ft.readInt=function(t,e){for(var i=this.pos,s=0,r=0,a=null==e?1/0:e;r=97?n-97+10:n>=65?n-65+10:n>=48&&n<=57?n-48:1/0)>=t)break;++this.pos,s=s*t+o}return this.pos===i||null!=e&&this.pos-i!==e?null:s},Ft.readRadixNumber=function(t){this.pos+=2;var e=this.readInt(t);return null==e&&this.raise(this.start+2,"Expected number in radix "+t),u(this.fullCharCodeAtPos())&&this.raise(this.pos,"Identifier directly after number"),this.finishToken(y.num,e)},Ft.readNumber=function(t){var e=this.pos;t||null!==this.readInt(10)||this.raise(e,"Invalid number");var i=this.pos-e>=2&&48===this.input.charCodeAt(e);i&&this.strict&&this.raise(e,"Invalid number"),i&&/[89]/.test(this.input.slice(e,this.pos))&&(i=!1);var s=this.input.charCodeAt(this.pos);46!==s||i||(++this.pos,this.readInt(10),s=this.input.charCodeAt(this.pos)),69!==s&&101!==s||i||(43!==(s=this.input.charCodeAt(++this.pos))&&45!==s||++this.pos,null===this.readInt(10)&&this.raise(e,"Invalid number")),u(this.fullCharCodeAtPos())&&this.raise(this.pos,"Identifier directly after number");var r=this.input.slice(e,this.pos),a=i?parseInt(r,8):parseFloat(r);return this.finishToken(y.num,a)},Ft.readCodePoint=function(){var t;if(123===this.input.charCodeAt(this.pos)){this.options.ecmaVersion<6&&this.unexpected();var e=++this.pos;t=this.readHexChar(this.input.indexOf("}",this.pos)-this.pos),++this.pos,t>1114111&&this.invalidStringToken(e,"Code point out of bounds")}else t=this.readHexChar(4);return t},Ft.readString=function(t){for(var e="",i=++this.pos;;){this.pos>=this.input.length&&this.raise(this.start,"Unterminated string constant");var s=this.input.charCodeAt(this.pos);if(s===t)break;92===s?(e+=this.input.slice(i,this.pos),e+=this.readEscapedChar(!1),i=this.pos):(k(s,this.options.ecmaVersion>=10)&&this.raise(this.start,"Unterminated string constant"),++this.pos)}return e+=this.input.slice(i,this.pos++),this.finishToken(y.string,e)};var Gt={};Ft.tryReadTemplateToken=function(){this.inTemplateElement=!0;try{this.readTmplToken()}catch(t){if(t!==Gt)throw t;this.readInvalidTemplateToken()}this.inTemplateElement=!1},Ft.invalidStringToken=function(t,e){if(this.inTemplateElement&&this.options.ecmaVersion>=9)throw Gt;this.raise(t,e)},Ft.readTmplToken=function(){for(var t="",e=this.pos;;){this.pos>=this.input.length&&this.raise(this.start,"Unterminated template");var i=this.input.charCodeAt(this.pos);if(96===i||36===i&&123===this.input.charCodeAt(this.pos+1))return this.pos!==this.start||this.type!==y.template&&this.type!==y.invalidTemplate?(t+=this.input.slice(e,this.pos),this.finishToken(y.template,t)):36===i?(this.pos+=2,this.finishToken(y.dollarBraceL)):(++this.pos,this.finishToken(y.backQuote));if(92===i)t+=this.input.slice(e,this.pos),t+=this.readEscapedChar(!0),e=this.pos;else if(k(i)){switch(t+=this.input.slice(e,this.pos),++this.pos,i){case 13:10===this.input.charCodeAt(this.pos)&&++this.pos;case 10:t+="\n";break;default:t+=String.fromCharCode(i)}this.options.locations&&(++this.curLine,this.lineStart=this.pos),e=this.pos}else++this.pos}},Ft.readInvalidTemplateToken=function(){for(;this.pos=48&&e<=55){var i=this.input.substr(this.pos-1,3).match(/^[0-7]+/)[0],s=parseInt(i,8);return s>255&&(i=i.slice(0,-1),s=parseInt(i,8)),this.pos+=i.length-1,e=this.input.charCodeAt(this.pos),"0"===i&&56!==e&&57!==e||!this.strict&&!t||this.invalidStringToken(this.pos-1-i.length,t?"Octal literal in template string":"Octal literal in strict mode"),String.fromCharCode(s)}return String.fromCharCode(e)}},Ft.readHexChar=function(t){var e=this.pos,i=this.readInt(16,t);return null===i&&this.invalidStringToken(e,"Bad character escape sequence"),i},Ft.readWord1=function(){this.containsEsc=!1;for(var t="",e=!0,i=this.pos,s=this.options.ecmaVersion>=6;this.pos0&&void 0!==arguments[0]?arguments[0]:null)}},{key:"Continue",value:function(n){return new e("continue",n)}},{key:"isReturn",value:function(n){return n instanceof e&&"return"===n.type}},{key:"isContinue",value:function(n){return n instanceof e&&"continue"===n.type}},{key:"isBreak",value:function(n){return n instanceof e&&"break"===n.type}},{key:"isSignal",value:function(n){return n instanceof e}}]),e}();module.exports=u; 5 | },{}],"ulFJ":[function(require,module,exports) { 6 | function e(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function t(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:"";e(this,t),this.value=n,this.kind=i}return n(t,[{key:"set",value:function(e){if("const"===this.kind)throw new TypeError("Assignment to constant variable");this.value=e}},{key:"get",value:function(){return this.value}}]),t}(),o=function(){function t(n,i){e(this,t),this.obj=n,this.prop=i}return n(t,[{key:"set",value:function(e){this.obj[this.prop]=e}},{key:"get",value:function(){return this.obj[this.prop]}}]),t}();module.exports.SimpleValue=i,module.exports.MemberValue=o; 7 | },{}],"7lST":[function(require,module,exports) { 8 | function e(r){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(r)}function r(e){return o(e)||t(e)||n()}function n(){throw new TypeError("Invalid attempt to spread non-iterable instance")}function t(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}function o(e){if(Array.isArray(e)){for(var r=0,n=new Array(e.length);r>=":function(e,r){return e instanceof u?e.obj[e.prop]>>=r:e.value>>=r},">>>=":function(e,r){return e instanceof u?e.obj[e.prop]>>>=r:e.value>>>=r},"|=":function(e,r){return e instanceof u?e.obj[e.prop]|=r:e.value|=r},"^=":function(e,r){return e instanceof u?e.obj[e.prop]^=r:e.value^=r},"&=":function(e,r){return e instanceof u?e.obj[e.prop]&=r:e.value&=r}},AssignmentExpression:function(e){var r=e.node,n=f(r.left,e);return s.AssignmentExpressionOperatortraverseMap[r.operator](n,e.traverse(r.right))},UnaryExpressionOperatortraverseMap:{"-":function(e){return-e.traverse(e.node.argument)},"+":function(e){return+e.traverse(e.node.argument)},"!":function(e){return!e.traverse(e.node.argument)},"~":function(e){return~e.traverse(e.node.argument)},typeof:function(r){if("Identifier"!==r.node.argument.type)return e(r.traverse(r.node.argument));try{var n=r.scope.get(r.node.argument.name);return n?e(n.value):"undefined"}catch(t){if(t.message==="".concat(r.node.argument.name," is not defined"))return"undefined";throw t}},void:function(e){e.traverse(e.node.argument)},delete:function(e){var r=e.node.argument;return"MemberExpression"===r.type?delete e.traverse(r.object)[c(r,e)]:"Identifier"!==r.type&&("Literal"===r.type||void 0)}},UnaryExpression:function(e){return s.UnaryExpressionOperatortraverseMap[e.node.operator](e)},BinaryExpressionOperatortraverseMap:{"==":function(e,r){return e==r},"!=":function(e,r){return e!=r},"===":function(e,r){return e===r},"!==":function(e,r){return e!==r},"<":function(e,r){return e":function(e,r){return e>r},">=":function(e,r){return e>=r},"<<":function(e,r){return e<>":function(e,r){return e>>r},">>>":function(e,r){return e>>>r},"+":function(e,r){return e+r},"-":function(e,r){return e-r},"*":function(e,r){return e*r},"/":function(e,r){return e/r},"%":function(e,r){return e%r},"**":function(e,r){throw new Error('canjs: es5 doesn\'t supports operator "**"')},"|":function(e,r){return e|r},"^":function(e,r){return e^r},"&":function(e,r){return e&r},in:function(e,r){return e in r},instanceof:function(e,r){return e instanceof r}},BinaryExpression:function(e){var r=e.traverse(e.node.left),n=e.traverse(e.node.right);return s.BinaryExpressionOperatortraverseMap[e.node.operator](r,n)},LogicalExpressionOperatortraverseMap:{"||":function(e,r){return e||r},"&&":function(e,r){return e&&r}},LogicalExpression:function(e){var r=e.traverse(e.node.left),n=e.traverse(e.node.right);return s.LogicalExpressionOperatortraverseMap[e.node.operator](r,n)},ForStatement:function(e){var r=e.node,n=e.scope;for(r.init&&"VariableDeclaration"===r.init.type&&"var"!==r.init.kind&&(n=e.createScope("block")),r.init&&e.traverse(r.init,{scope:n});!r.test||e.traverse(r.test,{scope:n});r.update&&e.traverse(r.update,{scope:n})){var t=e.traverse(r.body,{scope:n});if(a.isBreak(t))break;if(!a.isContinue(t)&&a.isReturn(t))return t}},ForInStatement:function(e){var r,n=e.node,t=n.left,o=n.right,i=n.body,u=e.scope;if("VariableDeclaration"===t.type){var s=t.declarations[0].id;r=u.declare(s.name,void 0,t.kind)}else{if("Identifier"!==t.type)throw new Error('canjs: [ForInStatement] Unsupported left type "'.concat(t.type,'"'));r=u.get(t.name,!0)}for(var c in e.traverse(o)){r.value=c;var f=e.traverse(i,{scope:u});if(a.isBreak(f))break;if(!a.isContinue(f)&&a.isReturn(f))return f}},WhileStatement:function(e){for(;e.traverse(e.node.test);){var r=e.traverse(e.node.body);if(a.isBreak(r))break;if(!a.isContinue(r)&&a.isReturn(r))return r}},DoWhileStatement:function(e){do{var r=e.traverse(e.node.body);if(a.isBreak(r))break;if(!a.isContinue(r)&&a.isReturn(r))return r}while(e.traverse(e.node.test))},ReturnStatement:function(e){var r;return e.node.argument&&(r=e.traverse(e.node.argument)),a.Return(r)},BreakStatement:function(e){var r;return e.node.label&&(r=e.node.label.name),a.Break(r)},ContinueStatement:function(e){var r;return e.node.label&&(r=e.node.label.name),a.Continue(r)},IfStatement:function(e){return e.traverse(e.node.test)?e.traverse(e.node.consequent):e.node.alternate?e.traverse(e.node.alternate):void 0},SwitchStatement:function(e){var r=e.traverse(e.node.discriminant),n=!0,t=!1,o=void 0;try{for(var i,u=e.node.cases[Symbol.iterator]();!(n=(i=u.next()).done);n=!0){var s=i.value;if(!s.test||r===e.traverse(s.test)){var c=e.traverse(s);if(a.isBreak(c))break;if(a.isContinue(c))continue;if(a.isReturn(c))return c}}}catch(f){t=!0,o=f}finally{try{n||null==u.return||u.return()}finally{if(t)throw o}}},SwitchCase:function(e){var r=!0,n=!1,t=void 0;try{for(var o,i=e.node.consequent[Symbol.iterator]();!(r=(o=i.next()).done);r=!0){var u=o.value,s=e.traverse(u);if(a.isSignal(s))return s}}catch(c){n=!0,t=c}finally{try{r||null==i.return||i.return()}finally{if(n)throw t}}},ConditionalExpression:function(e){return e.traverse(e.node.test)?e.traverse(e.node.consequent):e.traverse(e.node.alternate)},ThrowStatement:function(e){throw e.traverse(e.node.argument)},TryStatement:function(e){var r=e.node,n=r.block,t=r.handler,o=r.finalizer;try{return e.traverse(n)}catch(u){if(t){var a=t.param,i=e.createScope("block");return i.letDeclare(a.name,u),e.traverse(t,{scope:i})}throw u}finally{if(o)return e.traverse(o)}},CatchClause:function(e){return e.traverse(e.node.body)}};function c(e,r){return e.computed?r.traverse(e.property):e.property.name}function f(e,r){if("Identifier"===e.type)return r.scope.get(e.name);if("MemberExpression"===e.type){var n=r.traverse(e.object),t=c(e,r);return new u(n,t)}throw new Error('canjs: Not support to get value of node type "'.concat(e.type,'"'))}module.exports=s; 9 | },{"../signal":"HrMX","../value":"ulFJ"}],"wMLc":[function(require,module,exports) { 10 | function e(e){for(var t=1;t2&&void 0!==arguments[2]?arguments[2]:"var";if("var"===r)return this.varDeclare(e,t);if("let"===r)return this.letDeclare(e,t);if("const"===r)return this.constDeclare(e,t);throw new Error('canjs: Invalid Variable Declaration Kind of "'.concat(r,'"'))}},{key:"varDeclare",value:function(e,t){for(var r=this;r.parentScope&&"function"!==r.type;)r=r.parentScope;return r.declaration[e]=new i(t,"var"),r.declaration[e]}},{key:"letDeclare",value:function(e,t){if(this.declaration[e])throw new SyntaxError("Identifier ".concat(e," has already been declared"));return this.declaration[e]=new i(t,"let"),this.declaration[e]}},{key:"constDeclare",value:function(e,t){if(this.declaration[e])throw new SyntaxError("Identifier ".concat(e," has already been declared"));return this.declaration[e]=new i(t,"const"),this.declaration[e]}}]),t}();module.exports=o; 16 | },{"./standard":"XgbH","./value":"ulFJ"}],"A7S/":[function(require,module,exports) { 17 | function e(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}function n(e,n){for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:{}).scope||this.scope),t=this.nodeHandler[e.type];if(!t)throw new Error('canjs: Unknown node type "'.concat(e.type,'".'));return t(r)}},{key:"createScope",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"block";return new o(e,this.scope)}}]),n}();module.exports=i; 18 | },{"./es_versions":"wMLc","./scope":"oeM6"}],"Focm":[function(require,module,exports) { 19 | function e(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function t(e,t){for(var r=0;r0&&void 0!==arguments[0]?arguments[0]:"",n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};e(this,t),this.code=r,this.extraDeclaration=n,this.ast=a.parse(r),this.nodeIterator=null,this.init()}return r(t,[{key:"init",value:function(){var e=this,t=new o("function");Object.keys(this.extraDeclaration).forEach(function(r){t.addDeclaration(r,e.extraDeclaration[r])}),this.nodeIterator=new i(null,t)}},{key:"run",value:function(){return this.nodeIterator.traverse(this.ast)}}]),t}();module.exports=u; 20 | },{"acorn":"9L7B","./iterator":"A7S/","./scope":"oeM6"}]},{},["Focm"], "Canjs") 21 | //# sourceMappingURL=/index.map -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "canjs", 3 | "version": "1.0.0", 4 | "description": "CanJS is a javascript interpreter, which can run JS code in JS.", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "build": "parcel build ./src/index.js --global Canjs", 8 | "build:demo": "parcel build ./demo.js -o demo.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/jrainlau/canjs.git" 13 | }, 14 | "keywords": [], 15 | "author": "JrainLau", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/jrainlau/canjs/issues" 19 | }, 20 | "homepage": "https://github.com/jrainlau/canjs#readme", 21 | "dependencies": { 22 | "acorn": "^6.0.4" 23 | }, 24 | "devDependencies": { 25 | "parcel": "^1.10.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/es_versions/es5.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jrainlau 3 | * @desc 节点处理器,处理AST当中的节点 4 | */ 5 | 6 | const Signal = require('../signal') 7 | const { MemberValue } = require('../value') 8 | 9 | const NodeHandler = { 10 | Program (nodeIterator) { 11 | for (const node of nodeIterator.node.body) { 12 | nodeIterator.traverse(node) 13 | } 14 | }, 15 | VariableDeclaration (nodeIterator) { 16 | const kind = nodeIterator.node.kind 17 | for (const declaration of nodeIterator.node.declarations) { 18 | const { name } = declaration.id 19 | const value = declaration.init ? nodeIterator.traverse(declaration.init) : undefined 20 | // 在作用域当中定义变量 21 | if (nodeIterator.scope.type === 'block' && kind === 'var') { 22 | nodeIterator.scope.parentScope.declare(name, value, kind) 23 | } else { 24 | nodeIterator.scope.declare(name, value, kind) 25 | } 26 | } 27 | }, 28 | Identifier (nodeIterator) { 29 | if (nodeIterator.node.name === 'undefined') { 30 | return undefined 31 | } 32 | return nodeIterator.scope.get(nodeIterator.node.name).value 33 | }, 34 | Literal (nodeIterator) { 35 | return nodeIterator.node.value 36 | }, 37 | 38 | ExpressionStatement (nodeIterator) { 39 | return nodeIterator.traverse(nodeIterator.node.expression) 40 | }, 41 | CallExpression (nodeIterator) { 42 | const func = nodeIterator.traverse(nodeIterator.node.callee) 43 | const args = nodeIterator.node.arguments.map(arg => nodeIterator.traverse(arg)) 44 | 45 | let value 46 | if (nodeIterator.node.callee.type === 'MemberExpression') { 47 | value = nodeIterator.traverse(nodeIterator.node.callee.object) 48 | } 49 | return func.apply(value, args) 50 | }, 51 | MemberExpression (nodeIterator) { 52 | const obj = nodeIterator.traverse(nodeIterator.node.object) 53 | const name = nodeIterator.node.property.name 54 | return obj[name] 55 | }, 56 | ObjectExpression (nodeIterator) { 57 | const obj = {} 58 | for (const prop of nodeIterator.node.properties) { 59 | let key 60 | if (prop.key.type === 'Literal') { 61 | key = `${prop.key.value}` 62 | } else if (prop.key.type === 'Identifier') { 63 | key = prop.key.name 64 | } else { 65 | throw new Error(`canjs: [ObjectExpression] Unsupported property key type "${prop.key.type}"`) 66 | } 67 | obj[key] = nodeIterator.traverse(prop.value) 68 | } 69 | return obj 70 | }, 71 | ArrayExpression (nodeIterator) { 72 | return nodeIterator.node.elements.map(ele => nodeIterator.traverse(ele)) 73 | }, 74 | 75 | BlockStatement (nodeIterator) { 76 | let scope = nodeIterator.createScope('block') 77 | 78 | // 处理块级节点内的每一个节点 79 | for (const node of nodeIterator.node.body) { 80 | if (node.type === 'FunctionDeclaration') { 81 | nodeIterator.traverse(node, { scope }) 82 | } else if (node.type === 'VariableDeclaration' && node.kind === 'var') { 83 | for (const declaration of node.declarations) { 84 | if (declaration.init) { 85 | scope.declare(declaration.id.name, nodeIterator.traverse(declaration.init, { scope }), node.kind) 86 | } else { 87 | scope.declare(declaration.id.name, undefined, node.kind) 88 | } 89 | } 90 | } 91 | } 92 | 93 | // 提取关键字(return, break, continue) 94 | for (const node of nodeIterator.node.body) { 95 | if (node.type === 'FunctionDeclaration') { 96 | continue 97 | } 98 | const signal = nodeIterator.traverse(node, { scope }) 99 | if (Signal.isSignal(signal)) { 100 | return signal 101 | } 102 | } 103 | }, 104 | FunctionDeclaration (nodeIterator) { 105 | const fn = NodeHandler.FunctionExpression(nodeIterator) 106 | nodeIterator.scope.varDeclare(nodeIterator.node.id.name, fn) 107 | return fn 108 | }, 109 | FunctionExpression (nodeIterator) { 110 | const node = nodeIterator.node 111 | /** 112 | * 1、定义函数需要先为其定义一个函数作用域,且允许继承父级作用域 113 | * 2、注册`this`, `arguments`和形参到作用域的变量空间 114 | * 3、检查return关键字 115 | * 4、定义函数名和长度 116 | */ 117 | const fn = function () { 118 | const scope = nodeIterator.createScope('function') 119 | scope.constDeclare('this', this) 120 | scope.constDeclare('arguments', arguments) 121 | 122 | node.params.forEach((param, index) => { 123 | const name = param.name 124 | scope.varDeclare(name, arguments[index]) 125 | }) 126 | 127 | const signal = nodeIterator.traverse(node.body, { scope }) 128 | if (Signal.isReturn(signal)) { 129 | return signal.value 130 | } 131 | } 132 | 133 | Object.defineProperties(fn, { 134 | name: { value: node.id ? node.id.name : '' }, 135 | length: { value: node.params.length } 136 | }) 137 | 138 | return fn 139 | }, 140 | ThisExpression (nodeIterator) { 141 | const value = nodeIterator.scope.get('this') 142 | return value ? value.value : null 143 | }, 144 | NewExpression (nodeIterator) { 145 | const func = nodeIterator.traverse(nodeIterator.node.callee) 146 | const args = nodeIterator.node.arguments.map(arg => nodeIterator.traverse(arg)) 147 | return new (func.bind(null, ...args)) 148 | }, 149 | 150 | UpdateExpression (nodeIterator) { 151 | const { operator, prefix } = nodeIterator.node 152 | const { name } = nodeIterator.node.argument 153 | let val = nodeIterator.scope.get(name).value 154 | 155 | operator === "++" ? nodeIterator.scope.set(name, val + 1) : nodeIterator.scope.set(name, val - 1) 156 | 157 | if (operator === "++" && prefix) { 158 | return ++val 159 | } else if (operator === "++" && !prefix) { 160 | return val++ 161 | } else if (operator === "--" && prefix) { 162 | return --val 163 | } else { 164 | return val-- 165 | } 166 | }, 167 | AssignmentExpressionOperatortraverseMap: { 168 | '=': (value, v) => value instanceof MemberValue ? value.obj[value.prop] = v : value.value = v, 169 | '+=': (value, v) => value instanceof MemberValue ? value.obj[value.prop] += v : value.value += v, 170 | '-=': (value, v) => value instanceof MemberValue ? value.obj[value.prop] -= v : value.value -= v, 171 | '*=': (value, v) => value instanceof MemberValue ? value.obj[value.prop] *= v : value.value *= v, 172 | '/=': (value, v) => value instanceof MemberValue ? value.obj[value.prop] /= v : value.value /= v, 173 | '%=': (value, v) => value instanceof MemberValue ? value.obj[value.prop] %= v : value.value %= v, 174 | '**=': () => { throw new Error('canjs: es5 doen\'t supports operator "**=') }, 175 | '<<=': (value, v) => value instanceof MemberValue ? value.obj[value.prop] <<= v : value.value <<= v, 176 | '>>=': (value, v) => value instanceof MemberValue ? value.obj[value.prop] >>= v : value.value >>= v, 177 | '>>>=': (value, v) => value instanceof MemberValue ? value.obj[value.prop] >>>= v : value.value >>>= v, 178 | '|=': (value, v) => value instanceof MemberValue ? value.obj[value.prop] |= v : value.value |= v, 179 | '^=': (value, v) => value instanceof MemberValue ? value.obj[value.prop] ^= v : value.value ^= v, 180 | '&=': (value, v) => value instanceof MemberValue ? value.obj[value.prop] &= v : value.value &= v 181 | }, 182 | AssignmentExpression (nodeIterator) { 183 | const node = nodeIterator.node 184 | const value = getIdentifierOrMemberExpressionValue(node.left, nodeIterator) 185 | return NodeHandler.AssignmentExpressionOperatortraverseMap[node.operator](value, nodeIterator.traverse(node.right)) 186 | }, 187 | UnaryExpressionOperatortraverseMap: { 188 | '-': (nodeIterator) => -nodeIterator.traverse(nodeIterator.node.argument), 189 | '+': (nodeIterator) => +nodeIterator.traverse(nodeIterator.node.argument), 190 | '!': (nodeIterator) => !nodeIterator.traverse(nodeIterator.node.argument), 191 | '~': (nodeIterator) => ~nodeIterator.traverse(nodeIterator.node.argument), 192 | 'typeof': (nodeIterator) => { 193 | if (nodeIterator.node.argument.type === 'Identifier') { 194 | try { 195 | const value = nodeIterator.scope.get(nodeIterator.node.argument.name) 196 | return value ? typeof value.value : 'undefined' 197 | } catch (err) { 198 | if (err.message === `${nodeIterator.node.argument.name} is not defined`) { 199 | return 'undefined' 200 | } else { 201 | throw err 202 | } 203 | } 204 | } else { 205 | return typeof nodeIterator.traverse(nodeIterator.node.argument) 206 | } 207 | }, 208 | 'void': (nodeIterator) => void nodeIterator.traverse(nodeIterator.node.argument), 209 | 'delete': (nodeIterator) => { 210 | const argument = nodeIterator.node.argument 211 | if (argument.type === 'MemberExpression') { 212 | const obj = nodeIterator.traverse(argument.object) 213 | const name = getPropertyName(argument, nodeIterator) 214 | return delete obj[name] 215 | } else if (argument.type === 'Identifier') { 216 | return false 217 | } else if (argument.type === 'Literal') { 218 | return true 219 | } 220 | } 221 | }, 222 | UnaryExpression (nodeIterator) { 223 | return NodeHandler.UnaryExpressionOperatortraverseMap[nodeIterator.node.operator](nodeIterator) 224 | }, 225 | BinaryExpressionOperatortraverseMap: { 226 | '==': (a, b) => a == b, 227 | '!=': (a, b) => a != b, 228 | '===': (a, b) => a === b, 229 | '!==': (a, b) => a !== b, 230 | '<': (a, b) => a < b, 231 | '<=': (a, b) => a <= b, 232 | '>': (a, b) => a > b, 233 | '>=': (a, b) => a >= b, 234 | '<<': (a, b) => a << b, 235 | '>>': (a, b) => a >> b, 236 | '>>>': (a, b) => a >>> b, 237 | '+': (a, b) => a + b, 238 | '-': (a, b) => a - b, 239 | '*': (a, b) => a * b, 240 | '/': (a, b) => a / b, 241 | '%': (a, b) => a % b, 242 | '**': (a, b) => { throw new Error('canjs: es5 doesn\'t supports operator "**"') }, 243 | '|': (a, b) => a | b, 244 | '^': (a, b) => a ^ b, 245 | '&': (a, b) => a & b, 246 | 'in': (a, b) => a in b, 247 | 'instanceof': (a, b) => a instanceof b 248 | }, 249 | BinaryExpression (nodeIterator) { 250 | const a = nodeIterator.traverse(nodeIterator.node.left) 251 | const b = nodeIterator.traverse(nodeIterator.node.right) 252 | return NodeHandler.BinaryExpressionOperatortraverseMap[nodeIterator.node.operator](a, b) 253 | }, 254 | LogicalExpressionOperatortraverseMap: { 255 | '||': (a, b) => a || b, 256 | '&&': (a, b) => a && b 257 | }, 258 | LogicalExpression (nodeIterator) { 259 | const a = nodeIterator.traverse(nodeIterator.node.left) 260 | if (a) { 261 | if (nodeIterator.node.operator == '||') { 262 | return true; 263 | } 264 | } 265 | else if (nodeIterator.node.operator == '&&') { 266 | return false; 267 | } 268 | 269 | const b = nodeIterator.traverse(nodeIterator.node.right) 270 | return NodeHandler.LogicalExpressionOperatortraverseMap[nodeIterator.node.operator](a, b) 271 | }, 272 | 273 | ForStatement (nodeIterator) { 274 | const node = nodeIterator.node 275 | let scope = nodeIterator.scope 276 | if (node.init && node.init.type === 'VariableDeclaration' && node.init.kind !== 'var') { 277 | scope = nodeIterator.createScope('block') 278 | } 279 | 280 | for ( 281 | node.init && nodeIterator.traverse(node.init, { scope }); 282 | node.test ? nodeIterator.traverse(node.test, { scope }) : true; 283 | node.update && nodeIterator.traverse(node.update, { scope }) 284 | ) { 285 | const signal = nodeIterator.traverse(node.body, { scope }) 286 | 287 | if (Signal.isBreak(signal)) { 288 | break 289 | } else if (Signal.isContinue(signal)) { 290 | continue 291 | } else if (Signal.isReturn(signal)) { 292 | return signal 293 | } 294 | } 295 | }, 296 | ForInStatement (nodeIterator) { 297 | const { left, right, body } = nodeIterator.node 298 | let scope = nodeIterator.scope 299 | 300 | let value 301 | if (left.type === 'VariableDeclaration') { 302 | const id = left.declarations[0].id 303 | value = scope.declare(id.name, undefined, left.kind) 304 | } else if (left.type === 'Identifier') { 305 | value = scope.get(left.name, true) 306 | } else { 307 | throw new Error(`canjs: [ForInStatement] Unsupported left type "${left.type}"`) 308 | } 309 | 310 | for (const key in nodeIterator.traverse(right)) { 311 | value.value = key 312 | const signal = nodeIterator.traverse(body, { scope }) 313 | 314 | if (Signal.isBreak(signal)) { 315 | break 316 | } else if (Signal.isContinue(signal)) { 317 | continue 318 | } else if (Signal.isReturn(signal)) { 319 | return signal 320 | } 321 | } 322 | }, 323 | WhileStatement (nodeIterator) { 324 | while (nodeIterator.traverse(nodeIterator.node.test)) { 325 | const signal = nodeIterator.traverse(nodeIterator.node.body) 326 | 327 | if (Signal.isBreak(signal)) { 328 | break 329 | } else if (Signal.isContinue(signal)) { 330 | continue 331 | } else if (Signal.isReturn(signal)) { 332 | return signal 333 | } 334 | } 335 | }, 336 | DoWhileStatement (nodeIterator) { 337 | do { 338 | const signal = nodeIterator.traverse(nodeIterator.node.body) 339 | 340 | if (Signal.isBreak(signal)) { 341 | break 342 | } else if (Signal.isContinue(signal)) { 343 | continue 344 | } else if (Signal.isReturn(signal)) { 345 | return signal 346 | } 347 | } while (nodeIterator.traverse(nodeIterator.node.test)) 348 | }, 349 | 350 | ReturnStatement (nodeIterator) { 351 | let value 352 | if (nodeIterator.node.argument) { 353 | value = nodeIterator.traverse(nodeIterator.node.argument) 354 | } 355 | return Signal.Return(value) 356 | }, 357 | BreakStatement (nodeIterator) { 358 | let label 359 | if (nodeIterator.node.label) { 360 | label = nodeIterator.node.label.name 361 | } 362 | return Signal.Break(label) 363 | }, 364 | ContinueStatement (nodeIterator) { 365 | let label 366 | if (nodeIterator.node.label) { 367 | label = nodeIterator.node.label.name 368 | } 369 | return Signal.Continue(label) 370 | }, 371 | 372 | IfStatement (nodeIterator) { 373 | if (nodeIterator.traverse(nodeIterator.node.test)) { 374 | return nodeIterator.traverse(nodeIterator.node.consequent) 375 | } else if (nodeIterator.node.alternate) { 376 | return nodeIterator.traverse(nodeIterator.node.alternate) 377 | } 378 | }, 379 | SwitchStatement (nodeIterator) { 380 | const discriminant = nodeIterator.traverse(nodeIterator.node.discriminant) 381 | 382 | for (const theCase of nodeIterator.node.cases) { 383 | if (!theCase.test || discriminant === nodeIterator.traverse(theCase.test)) { 384 | const signal = nodeIterator.traverse(theCase) 385 | 386 | if (Signal.isBreak(signal)) { 387 | break 388 | } else if (Signal.isContinue(signal)) { 389 | continue 390 | } else if (Signal.isReturn(signal)) { 391 | return signal 392 | } 393 | } 394 | } 395 | }, 396 | SwitchCase (nodeIterator) { 397 | for (const node of nodeIterator.node.consequent) { 398 | const signal = nodeIterator.traverse(node) 399 | if (Signal.isSignal(signal)) { 400 | return signal 401 | } 402 | } 403 | }, 404 | ConditionalExpression (nodeIterator) { 405 | return nodeIterator.traverse(nodeIterator.node.test) 406 | ? nodeIterator.traverse(nodeIterator.node.consequent) 407 | : nodeIterator.traverse(nodeIterator.node.alternate) 408 | }, 409 | 410 | ThrowStatement(nodeIterator) { 411 | throw nodeIterator.traverse(nodeIterator.node.argument) 412 | }, 413 | TryStatement(nodeIterator) { 414 | const { block, handler, finalizer } = nodeIterator.node 415 | try { 416 | return nodeIterator.traverse(block) 417 | } catch (err) { 418 | if (handler) { 419 | const param = handler.param 420 | const scope = nodeIterator.createScope('block') 421 | scope.letDeclare(param.name, err) 422 | return nodeIterator.traverse(handler, { scope }) 423 | } 424 | throw err 425 | } finally { 426 | if (finalizer) { 427 | return nodeIterator.traverse(finalizer) 428 | } 429 | } 430 | }, 431 | CatchClause(nodeIterator) { 432 | return nodeIterator.traverse(nodeIterator.node.body); 433 | } 434 | } 435 | 436 | function getPropertyName (node, nodeIterator) { 437 | if (node.computed) { 438 | return nodeIterator.traverse(node.property) 439 | } else { 440 | return node.property.name 441 | } 442 | } 443 | 444 | function getIdentifierOrMemberExpressionValue(node, nodeIterator) { 445 | if (node.type === 'Identifier') { 446 | return nodeIterator.scope.get(node.name) 447 | } else if (node.type === 'MemberExpression') { 448 | const obj = nodeIterator.traverse(node.object) 449 | const name = getPropertyName(node, nodeIterator) 450 | return new MemberValue(obj, name) 451 | } else { 452 | throw new Error(`canjs: Not support to get value of node type "${node.type}"`) 453 | } 454 | } 455 | 456 | module.exports = NodeHandler 457 | -------------------------------------------------------------------------------- /src/es_versions/index.js: -------------------------------------------------------------------------------- 1 | const es5 = require('./es5') 2 | 3 | module.exports = { 4 | ...es5 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jrainlau 3 | * @desc Canjs类 4 | * 5 | * @class 6 | * 7 | * 传入字符串形式的es5代码,可选的新增全局变量 8 | * 运行`.run()`方法即可输出运行结果 9 | * 10 | * eg: new Canjs('console.log("Hello World!")').run() 11 | */ 12 | 13 | const { Parser } = require('acorn') 14 | const NodeIterator = require('./iterator') 15 | const Scope = require('./scope') 16 | 17 | class Canjs { 18 | constructor (code = '', extraDeclaration = {}) { 19 | this.code = code 20 | this.extraDeclaration = extraDeclaration 21 | this.ast = Parser.parse(code) 22 | this.nodeIterator = null 23 | this.init() 24 | } 25 | 26 | init () { 27 | const globalScope = new Scope('function') 28 | Object.keys(this.extraDeclaration).forEach((key) => { 29 | globalScope.addDeclaration(key, this.extraDeclaration[key]) 30 | }) 31 | this.nodeIterator = new NodeIterator(null, globalScope) 32 | } 33 | 34 | run () { 35 | return this.nodeIterator.traverse(this.ast) 36 | } 37 | } 38 | 39 | module.exports = Canjs 40 | -------------------------------------------------------------------------------- /src/iterator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jrainlau 3 | * @desc 节点遍历器,递归遍历AST内的每一个节点并调用对应的方法进行解析 4 | * 5 | * @class 6 | * 7 | * 针对AST节点进行解析,根据节点类型调用“节点处理器”(nodeHandler)对应的方法。 8 | * 在进行解析的时候,会传入节点和节点对应的作用域。 9 | * 10 | * 另外也提供了创建作用域的方法(createScope),可用于创建函数作用域或者块级作用域。 11 | */ 12 | 13 | const nodeHandler = require('./es_versions') 14 | const Scope = require('./scope') 15 | 16 | class NodeIterator { 17 | constructor (node, scope) { 18 | this.node = node 19 | this.scope = scope 20 | this.nodeHandler = nodeHandler 21 | } 22 | 23 | traverse (node, options = {}) { 24 | const scope = options.scope || this.scope 25 | const nodeIterator = new NodeIterator(node, scope) 26 | const _eval = this.nodeHandler[node.type] 27 | if (!_eval) { 28 | throw new Error(`canjs: Unknown node type "${node.type}".`) 29 | } 30 | return _eval(nodeIterator) 31 | } 32 | 33 | createScope (blockType = 'block') { 34 | return new Scope(blockType, this.scope) 35 | } 36 | } 37 | 38 | module.exports = NodeIterator 39 | -------------------------------------------------------------------------------- /src/scope.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jrainlau 3 | * @desc 管理作用域 4 | * 5 | * @class 6 | * 7 | * 每次对节点的处理,都要考虑其作用域的问题。Scope实例会定义该作用域为函数作用域(function)或者块级作用域(block)。 8 | * 每次新建Scope实例,都会为当前节点创建一个全新的“作用域变量空间”(declaration),任何在此作用域内定义的变量都会存放在这个空间当中 9 | * 此外,新建Scope实例也会保存其父级作用域。 10 | */ 11 | 12 | const standardMap = require('./standard') 13 | const { SimpleValue } = require('./value') 14 | 15 | class Scope { 16 | constructor (type, parentScope) { 17 | this.type = type 18 | this.parentScope = parentScope 19 | this.globalDeclaration = standardMap 20 | this.declaration = Object.create(null) // 每次都新建一个全新的作用域 21 | } 22 | 23 | addDeclaration (name, value) { 24 | this.globalDeclaration[name] = new SimpleValue(value) 25 | } 26 | 27 | get (name) { 28 | if (this.declaration[name]) { 29 | return this.declaration[name] 30 | } else if (this.parentScope) { 31 | return this.parentScope.get(name) 32 | } else if (this.globalDeclaration[name]) { 33 | return this.globalDeclaration[name] 34 | } 35 | throw new ReferenceError(`${name} is not defined`) 36 | } 37 | 38 | set (name, value) { 39 | if (this.declaration[name]) { 40 | this.declaration[name].set(value) 41 | } else if (this.parentScope) { 42 | this.parentScope.set(name, value) 43 | } else if (this.globalDeclaration[name]) { 44 | return this.globalDeclaration.set(name, value) 45 | } else { 46 | throw new ReferenceError(`${name} is not defined`) 47 | } 48 | } 49 | 50 | declare (name, value, kind = 'var') { 51 | if (kind === 'var') { 52 | return this.varDeclare(name, value) 53 | } else if (kind === 'let') { 54 | return this.letDeclare(name, value) 55 | } else if (kind === 'const') { 56 | return this.constDeclare(name, value) 57 | } else { 58 | throw new Error(`canjs: Invalid Variable Declaration Kind of "${kind}"`) 59 | } 60 | } 61 | 62 | varDeclare (name, value) { 63 | let scope = this 64 | // 若当前作用域存在非函数类型的父级作用域时,就把变量定义到父级作用域 65 | while (scope.parentScope && scope.type !== 'function') { 66 | scope = scope.parentScope 67 | } 68 | scope.declaration[name] = new SimpleValue(value, 'var') 69 | return scope.declaration[name] 70 | } 71 | 72 | letDeclare (name, value) { 73 | // 不允许重复定义 74 | if (this.declaration[name]) { 75 | throw new SyntaxError(`Identifier ${name} has already been declared`) 76 | } 77 | this.declaration[name] = new SimpleValue(value, 'let') 78 | return this.declaration[name] 79 | } 80 | 81 | constDeclare (name, value) { 82 | // 不允许重复定义 83 | if (this.declaration[name]) { 84 | throw new SyntaxError(`Identifier ${name} has already been declared`) 85 | } 86 | this.declaration[name] = new SimpleValue(value, 'const') 87 | return this.declaration[name] 88 | } 89 | } 90 | 91 | module.exports = Scope 92 | -------------------------------------------------------------------------------- /src/signal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: Jrainlau 3 | * @desc: 判断、记录关键标记语句(return, break, continue) 4 | * 5 | * @class 6 | */ 7 | 8 | class Signal { 9 | constructor (type, value) { 10 | this.type = type 11 | this.value = value 12 | } 13 | 14 | static Return (value) { 15 | return new Signal('return', value) 16 | } 17 | 18 | static Break (label = null) { 19 | return new Signal('break', label) 20 | } 21 | 22 | static Continue (label) { 23 | return new Signal('continue', label) 24 | } 25 | 26 | static isReturn(signal) { 27 | return signal instanceof Signal && signal.type === 'return' 28 | } 29 | 30 | static isContinue(signal) { 31 | return signal instanceof Signal && signal.type === 'continue' 32 | } 33 | 34 | static isBreak(signal) { 35 | return signal instanceof Signal && signal.type === 'break' 36 | } 37 | 38 | static isSignal (signal) { 39 | return signal instanceof Signal 40 | } 41 | } 42 | 43 | module.exports = Signal 44 | -------------------------------------------------------------------------------- /src/standard.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: JrainLau 3 | * @desc: es5标准库,提供es5所支持的内置对象/方法 4 | */ 5 | 6 | const { SimpleValue } = require('./value') 7 | 8 | let windowObj = null 9 | let globalObj = null 10 | 11 | try { 12 | windowObj = window 13 | } catch (e) {} 14 | 15 | try { 16 | globalObj = global 17 | } catch (e) {} 18 | 19 | const standardMap = { 20 | // Function properties 21 | isFinite: new SimpleValue(isFinite), 22 | isNaN: new SimpleValue(isNaN), 23 | parseFloat: new SimpleValue(parseFloat), 24 | parseInt: new SimpleValue(parseInt), 25 | decodeURI: new SimpleValue(decodeURI), 26 | decodeURIComponent: new SimpleValue(decodeURIComponent), 27 | encodeURI: new SimpleValue(encodeURI), 28 | encodeURIComponent: new SimpleValue(encodeURIComponent), 29 | 30 | // Fundamental objects 31 | Object: new SimpleValue(Object), 32 | Function: new SimpleValue(Function), 33 | Boolean: new SimpleValue(Boolean), 34 | Symbol: new SimpleValue(Symbol), 35 | Error: new SimpleValue(Error), 36 | EvalError: new SimpleValue(EvalError), 37 | RangeError: new SimpleValue(RangeError), 38 | ReferenceError: new SimpleValue(ReferenceError), 39 | SyntaxError: new SimpleValue(SyntaxError), 40 | TypeError: new SimpleValue(TypeError), 41 | URIError: new SimpleValue(URIError), 42 | 43 | // Numbers and dates 44 | Number: new SimpleValue(Number), 45 | Math: new SimpleValue(Math), 46 | Date: new SimpleValue(Date), 47 | 48 | // Text processing 49 | String: new SimpleValue(String), 50 | RegExp: new SimpleValue(RegExp), 51 | 52 | // Indexed collections 53 | Array: new SimpleValue(Array), 54 | Int8Array: new SimpleValue(Int8Array), 55 | Uint8Array: new SimpleValue(Uint8Array), 56 | Uint8ClampedArray: new SimpleValue(Uint8ClampedArray), 57 | Int16Array: new SimpleValue(Int16Array), 58 | Uint16Array: new SimpleValue(Uint16Array), 59 | Int32Array: new SimpleValue(Int32Array), 60 | Uint32Array: new SimpleValue(Uint32Array), 61 | Float32Array: new SimpleValue(Float32Array), 62 | Float64Array: new SimpleValue(Float64Array), 63 | 64 | // Structured data 65 | ArrayBuffer: new SimpleValue(ArrayBuffer), 66 | DataView: new SimpleValue(DataView), 67 | JSON: new SimpleValue(JSON), 68 | 69 | // // Other 70 | window: new SimpleValue(windowObj), 71 | global: new SimpleValue(globalObj), 72 | console: new SimpleValue(console), 73 | setTimeout: new SimpleValue(setTimeout), 74 | clearTimeout: new SimpleValue(clearTimeout), 75 | setInterval: new SimpleValue(setInterval), 76 | clearInterval: new SimpleValue(clearInterval) 77 | } 78 | 79 | module.exports = standardMap 80 | -------------------------------------------------------------------------------- /src/value.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jrainlau 3 | * @desc 和变量保存相关 4 | */ 5 | 6 | /** 7 | * 创建一个普通变量值 8 | * 9 | * @class 10 | * @param any value 值 11 | * @param string kind 变量定义符(var, let, const) 12 | * @method set 设置值 13 | * @method get 获取值 14 | */ 15 | class SimpleValue { 16 | constructor (value, kind = '') { 17 | this.value = value 18 | this.kind = kind 19 | } 20 | 21 | set (value) { 22 | // 禁止重新对const类型变量赋值 23 | if (this.kind === 'const') { 24 | throw new TypeError('Assignment to constant variable') 25 | } else { 26 | this.value = value 27 | } 28 | } 29 | 30 | get () { 31 | return this.value 32 | } 33 | } 34 | 35 | /** 36 | * 创建一个类变量 37 | * 38 | * @class 39 | * @param any obj 类 40 | * @param prop any 属性 41 | * @method set 设置类的属性的值 42 | * @method get 获取类的属性的值 43 | */ 44 | class MemberValue { 45 | constructor(obj, prop) { 46 | this.obj = obj 47 | this.prop = prop 48 | } 49 | 50 | set (value) { 51 | this.obj[this.prop] = value 52 | } 53 | 54 | get () { 55 | return this.obj[this.prop] 56 | } 57 | } 58 | 59 | module.exports.SimpleValue = SimpleValue 60 | module.exports.MemberValue = MemberValue 61 | --------------------------------------------------------------------------------