├── .gitignore ├── LICENSE ├── README.md ├── __test__ ├── classes │ └── JSObject.spec.js └── helpers │ ├── executor │ ├── 0-add-if-for.spec.js │ ├── 1-logical-break-continue.spec.js │ ├── 2-object-func-return-call.spec.js │ └── 3-new-promise.spec.js │ ├── lexical-analyer.spec.js │ └── syntax-parser.spec.js ├── docs ├── index.html ├── static │ ├── function.js │ ├── prism.css │ └── prism.js └── yzhanjsinterpreter.min.js ├── package-lock.json ├── package.json ├── src ├── classes │ ├── Completion.js │ ├── Environment.js │ ├── JSFunction.js │ ├── JSObject.js │ ├── Promise │ │ ├── PromiseFunction.js │ │ ├── RejectFunction.js │ │ ├── ResolveFunction.js │ │ └── ThenFunction.js │ ├── Reference.js │ └── Task.js ├── data │ └── conf.js ├── helpers │ ├── executor.js │ ├── lexical-analyzer.js │ └── syntax-parser.js └── index.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 馒头饭 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 | # yzhanJSInterpreter 2 | [![npm](https://img.shields.io/npm/v/yzhanjsinterpreter)](https://www.npmjs.com/package/yzhanjsinterpreter) 3 | [![npm](https://img.shields.io/npm/dt/yzhanjsinterpreter)](https://www.npmjs.com/package/yzhanjsinterpreter) 4 | [![GitHub license](https://img.shields.io/github/license/mantoufan/yzhanjsinterpreter)](https://github.com/mantoufan/yzhanjsinterpreter/blob/main/LICENSE) 5 | A JavaScript Interpreter Using JS itself, implement `eval` 6 | JavaScript 解释器,包含词法分析、语法解析和执行。实现 `eval` 7 | ## Demo 8 | You could change JavaScript Code and view the result in realtime. 9 | [Online Demo](https://mantoufan.github.io/yzhanJSInterpreter) 10 | ![DEMO PNG](https://s2.loli.net/2023/06/15/lwu5Cat9gox27dR.png) 11 | ## Setup 12 | ### Node.js 13 | ```javascript 14 | npm i yzhanjsinterpreter 15 | import yzhanJSInterpreter from 'yzhanjsinterpreter' 16 | ``` 17 | ### Browser 18 | ```html 19 | 20 | ``` 21 | ## Usage 22 | ### Declaration Code 23 | ```javascript 24 | const code = `let a = 1; 25 | let b = 1; 26 | a <<= b++; 27 | for (let i = 0; i < 10; i++) { 28 | if (i % 2 === 0) continue; 29 | if ((i | 2) == '11') break; 30 | a++; 31 | } 32 | undefined && 1 ?? (0 || a + b);` 33 | ``` 34 | ### Eval · Evaluate 35 | `eval` is a reserved keyword, so use `evaluate` instead 36 | ```javascript 37 | const evalResult = yzhanJSInterpreter.evaluate(code) 38 | ``` 39 | `evaluate` runs followed 3 steps: 40 | #### 1. Lexical Analyzer 41 | ```javascript 42 | const lexResult = yzhanJSInterpreter.lex(code) 43 | ``` 44 | #### 2. Syntax Parser 45 | ```javascript 46 | const parseResults = yzhanJSInterpreter.parse(lexResult) 47 | ``` 48 | #### 3. Executor 49 | ```javascript 50 | const executeResult = yzhanJSInterpreter.execute(parseResults[0]) 51 | const evalResult = executeResult 52 | ``` 53 | ## Development 54 | ### Unit Testing 55 | ```shell 56 | npm test 57 | ``` 58 | ### Build 59 | ```shell 60 | npm run build 61 | ``` 62 | ### Preview 63 | ```shell 64 | npm run dev 65 | ``` 66 | ## Todo 67 | 1. Travese AST, using child to replace the parent when there is only one parent Node. 68 | 2. Treeshaking: earse unseded declarations. -------------------------------------------------------------------------------- /__test__/classes/JSObject.spec.js: -------------------------------------------------------------------------------- 1 | const JSObject = require('../../src/classes/JSObject') 2 | describe('Test JSObject', () => { 3 | it('Test getProperty with value on itself', () => { 4 | const self = new JSObject() 5 | self.setProperty('a', { 6 | value: 1 7 | }) 8 | expect(self.getProperty('a')).toBe(1) 9 | }) 10 | it('Test getProperty with value on prototype', () => { 11 | const parent = new JSObject() 12 | parent.setProperty('a', { 13 | value: 1 14 | }) 15 | const child = new JSObject() 16 | child.prototype = parent 17 | expect(child.getProperty('a')).toBe(1) 18 | }) 19 | it('Test getProperty with getter on itself', () => { 20 | const self = new JSObject() 21 | self.setProperty('a', { 22 | get() { 23 | return 1 24 | } 25 | }) 26 | expect(self.getProperty('a')).toBe(1) 27 | }) 28 | it('Test getProperty with getter on prototype', () => { 29 | const parent = new JSObject() 30 | parent.setProperty('a', { 31 | get() { 32 | return 1 33 | } 34 | }) 35 | const child = new JSObject() 36 | child.prototype = parent 37 | expect(child.getProperty('a')).toBe(1) 38 | }) 39 | }) -------------------------------------------------------------------------------- /__test__/helpers/executor/0-add-if-for.spec.js: -------------------------------------------------------------------------------- 1 | const { evaluate, globalEnv } = require('../../../src/index') 2 | describe('Test Executor', () => { 3 | const map = new Map([ 4 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 5 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 6 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 7 | ['NewExpression', [['MemberExpression'], ['new', 'NewExpression']]], 8 | ['CallExpression', [ 9 | ['new', 'MemberExpression', '(', ')'], 10 | ['MemberExpression', '(', ')'], 11 | ['CallExpression', '.', 'Identifier'], 12 | ['CallExpression', '[', 'Expression', ']'], 13 | ['CallExpression', '(', 'Arguments', ')'] 14 | ]], 15 | ['LeftHandSideExpression', [['MemberExpression'], ['CallExpression'], ['NewExpression']]], 16 | ['MultiplicativeExpression', [['LeftHandSideExpression'], ['MultiplicativeExpression', '*', 'LeftHandSideExpression'], ['MultiplicativeExpression', '/', 'LeftHandSideExpression'], ['MultiplicativeExpression', '%', 'LeftHandSideExpression']]], 17 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 18 | ['AssignmentExpression', [['AdditiveExpression'], ['LeftHandSideExpression', '=', 'AssignmentExpression']]], 19 | ['Expression', [['AssignmentExpression']]], 20 | ['ExpressionStatement', [['Expression', ';']]], 21 | ['Declaration', [ 22 | ['var', 'Identifier', '=', 'Expression', ';'], 23 | ['let', 'Identifier', '=', 'Expression', ';'], 24 | ['const', 'Identifier', '=', 'Expression', ';'] 25 | ]], 26 | /** Added: Start */ 27 | ['Parameters', [['Identifier'], ['Parameters', ',', 'Identifier']]], 28 | ['FunctionDeclaration', [ 29 | ['function', 'Identifier', '(', ')', '{', 'StatementList', '}'], 30 | ['function', 'Identifier', '(', 'Parameters', ')', '{', 'StatementList', '}'] 31 | ]], 32 | /** Added: End */ 33 | ['Statement', [['ExpressionStatement'], ['IfStatement'], ['ForStatement'], ['Declaration']]], 34 | ['StatementListItem', [['Statement'], ['Declaration']]], 35 | ['StatementList', [['StatementListItem'], ['StatementList', 'StatementListItem']]], 36 | /** Added */ 37 | ['Program', [['StatementList']]], 38 | ]) 39 | const initialState = { 40 | Program: { 41 | EOF: { 42 | $end: true 43 | } 44 | } 45 | } 46 | it('1 + 1', () => { 47 | expect(evaluate('1 + 1', map, initialState)).toEqual({"type": "normal", "value": 2}) 48 | }) 49 | }) 50 | 51 | describe('Test Scope', () => { 52 | const map = new Map([ 53 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 54 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 55 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 56 | ['NewExpression', [['MemberExpression'], ['new', 'NewExpression']]], 57 | ['CallExpression', [ 58 | ['new', 'MemberExpression', '(', ')'], 59 | ['MemberExpression', '(', ')'], 60 | ['CallExpression', '.', 'Identifier'], 61 | ['CallExpression', '[', 'Expression', ']'], 62 | ['CallExpression', '(', 'Arguments', ')'] 63 | ]], 64 | ['LeftHandSideExpression', [['MemberExpression'], ['CallExpression'], ['NewExpression']]], 65 | ['MultiplicativeExpression', [['LeftHandSideExpression'], ['MultiplicativeExpression', '*', 'LeftHandSideExpression'], ['MultiplicativeExpression', '/', 'LeftHandSideExpression'], ['MultiplicativeExpression', '%', 'LeftHandSideExpression']]], 66 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 67 | ['AssignmentExpression', [['AdditiveExpression'], ['LeftHandSideExpression', '=', 'AssignmentExpression']]], 68 | ['Expression', [['AssignmentExpression'], ['Expression', ',', 'AssignmentExpression']]], 69 | ['ExpressionStatement', [['Expression', ';']]], 70 | ['Declaration', [ 71 | ['var', 'Identifier', '=', 'Expression', ';'], 72 | ['let', 'Identifier', '=', 'Expression', ';'], 73 | ['const', 'Identifier', '=', 'Expression', ';'] 74 | ]], 75 | ['Parameters', [['Identifier'], ['Parameters', ',', 'Identifier']]], 76 | ['FunctionDeclaration', [ 77 | ['function', 'Identifier', '(', ')', '{', 'StatementList', '}'], 78 | ['function', 'Identifier', '(', 'Parameters', ')', '{', 'StatementList', '}'] 79 | ]], 80 | /** Added: Start */ 81 | ['IfStatement', [ 82 | ['if', '(', 'Expression', ')', 'Statement'], 83 | ['if', '(', 'Expression', ')', 'else', 'Statement'] 84 | ]], 85 | ['BlockStatement', [ 86 | ['{', '}'], 87 | ['{', 'StatementList', '}'], 88 | ]], 89 | ['ForStatement', [ 90 | ['for', '(', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'] 91 | ]], 92 | /** Added: End */ 93 | ['Statement', [['ExpressionStatement'], ['IfStatement'], ['ForStatement'], ['Declaration'], ['BlockStatement']]], 94 | ['StatementListItem', [['Statement'], ['Declaration']]], 95 | ['StatementList', [['StatementListItem'], ['StatementList', 'StatementListItem']]], 96 | /** Added */ 97 | ['Program', [['StatementList']]], 98 | ]) 99 | const initialState = { 100 | Program: { 101 | EOF: { 102 | $end: true 103 | } 104 | } 105 | } 106 | it('DelcarationStatement', () => { 107 | evaluate('let a = 1', map, initialState) 108 | expect(globalEnv.vars.get('a')).toBe(1) 109 | }) 110 | 111 | it('AdditiveStatement', () => { 112 | evaluate('let b = 1;b = b + 1', map, initialState) 113 | expect(globalEnv.vars.get('b')).toBe(2) 114 | }) 115 | it('Nested Blocks', () => { 116 | expect(evaluate(`{ 117 | let a= 1 118 | { 119 | let b = a + 1 120 | b + 1 121 | } 122 | }`, map, initialState)).toEqual({"type": "normal", "value": 3}) 123 | }) 124 | }) 125 | 126 | describe('Test Statement Parser', () => { 127 | describe('Test IfStatement', () => { 128 | const map = new Map([ 129 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 130 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 131 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 132 | ['NewExpression', [['MemberExpression'], ['new', 'NewExpression']]], 133 | ['CallExpression', [ 134 | ['new', 'MemberExpression', '(', ')'], 135 | ['MemberExpression', '(', ')'], 136 | ['CallExpression', '.', 'Identifier'], 137 | ['CallExpression', '[', 'Expression', ']'], 138 | ['CallExpression', '(', 'Arguments', ')'] 139 | ]], 140 | ['LeftHandSideExpression', [['MemberExpression'], ['CallExpression'], ['NewExpression']]], 141 | ['MultiplicativeExpression', [['LeftHandSideExpression'], ['MultiplicativeExpression', '*', 'LeftHandSideExpression'], ['MultiplicativeExpression', '/', 'LeftHandSideExpression'], ['MultiplicativeExpression', '%', 'LeftHandSideExpression']]], 142 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 143 | ['AssignmentExpression', [['AdditiveExpression'], ['LeftHandSideExpression', '=', 'AssignmentExpression']]], 144 | ['Expression', [['AssignmentExpression'], ['Expression', ',', 'AssignmentExpression']]], 145 | ['ExpressionStatement', [['Expression', ';']]], 146 | ['Declaration', [ 147 | ['var', 'Identifier', '=', 'Expression', ';'], 148 | ['let', 'Identifier', '=', 'Expression', ';'], 149 | ['const', 'Identifier', '=', 'Expression', ';'] 150 | ]], 151 | ['Parameters', [['Identifier'], ['Parameters', ',', 'Identifier']]], 152 | ['FunctionDeclaration', [ 153 | ['function', 'Identifier', '(', ')', '{', 'StatementList', '}'], 154 | ['function', 'Identifier', '(', 'Parameters', ')', '{', 'StatementList', '}'] 155 | ]], 156 | /** Added: Start */ 157 | ['IfStatement', [ 158 | ['if', '(', 'Expression', ')', 'Statement'], 159 | ['if', '(', 'Expression', ')', 'else', 'Statement'] 160 | ]], 161 | ['BlockStatement', [ 162 | ['{', '}'], 163 | ['{', 'StatementList', '}'], 164 | ]], 165 | /** Added: End */ 166 | ['Statement', [['ExpressionStatement'], ['IfStatement'], ['ForStatement'], ['Declaration'], ['BlockStatement']]], 167 | ['StatementListItem', [['Statement'], ['Declaration']]], 168 | ['StatementList', [['StatementListItem'], ['StatementList', 'StatementListItem']]], 169 | /** Added */ 170 | ['Program', [['StatementList']]], 171 | ]) 172 | const initialState = { 173 | Program: { 174 | EOF: { 175 | $end: true 176 | } 177 | } 178 | } 179 | it('IfStatement', () => { 180 | expect(evaluate('if (ture) {const a = 1}', map, initialState)).toEqual({ type: 'normal', value: undefined }) 181 | }) 182 | }) 183 | 184 | describe('Test ForStatement', () => { 185 | const map = new Map([ 186 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 187 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 188 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 189 | ['NewExpression', [['MemberExpression'], ['new', 'NewExpression']]], 190 | ['CallExpression', [ 191 | ['new', 'MemberExpression', '(', ')'], 192 | ['MemberExpression', '(', ')'], 193 | ['CallExpression', '.', 'Identifier'], 194 | ['CallExpression', '[', 'Expression', ']'], 195 | ['CallExpression', '(', 'Arguments', ')'] 196 | ]], 197 | ['LeftHandSideExpression', [['MemberExpression'], ['CallExpression'], ['NewExpression']]], 198 | ['MultiplicativeExpression', [['UpdateExpression'], ['MultiplicativeExpression', '*', 'UpdateExpression'], ['MultiplicativeExpression', '/', 'UpdateExpression'], ['MultiplicativeExpression', '%', 'UpdateExpression']]], 199 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 200 | ['RelationalExpression', [['AdditiveExpression'], ['RelationalExpression', '>', 'AdditiveExpression'], ['RelationalExpression', '<', 'AdditiveExpression']]], 201 | ['AssignmentExpression', [['RelationalExpression'], ['LeftHandSideExpression', '=', 'RelationalExpression']]], 202 | ['Expression', [ 203 | ['AssignmentExpression'], 204 | ['Expression', ',', 'AssignmentExpression'], 205 | ]], 206 | ['UpdateExpression', [ 207 | ['LeftHandSideExpression'], 208 | ['LeftHandSideExpression', '++'], 209 | ['LeftHandSideExpression', '--'], 210 | ['++', 'LeftHandSideExpression'], 211 | ['--', 'LeftHandSideExpression'] 212 | ]], 213 | ['ExpressionStatement', [['Expression', ';']]], 214 | ['Declaration', [ 215 | ['var', 'Identifier', '=', 'Expression', ';'], 216 | ['let', 'Identifier', '=', 'Expression', ';'], 217 | ['const', 'Identifier', '=', 'Expression', ';'] 218 | ]], 219 | ['Parameters', [['Identifier'], ['Parameters', ',', 'Identifier']]], 220 | ['FunctionDeclaration', [ 221 | ['function', 'Identifier', '(', ')', '{', 'StatementList', '}'], 222 | ['function', 'Identifier', '(', 'Parameters', ')', '{', 'StatementList', '}'] 223 | ]], 224 | /** Added: Start */ 225 | ['IfStatement', [ 226 | ['if', '(', 'Expression', ')', 'Statement'], 227 | ['if', '(', 'Expression', ')', 'else', 'Statement'] 228 | ]], 229 | ['BlockStatement', [ 230 | ['{', '}'], 231 | ['{', 'StatementList', '}'], 232 | ]], 233 | ['ForStatement', [ 234 | ['for', '(', 'let', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 235 | ['for', '(', 'var', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 236 | ['for', '(', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'] 237 | ]], 238 | /** Added: End */ 239 | ['Statement', [['ExpressionStatement'], ['IfStatement'], ['ForStatement'], ['Declaration'], ['BlockStatement']]], 240 | ['StatementListItem', [['Statement'], ['Declaration']]], 241 | ['StatementList', [['StatementListItem'], ['StatementList', 'StatementListItem']]], 242 | /** Added */ 243 | ['Program', [['StatementList']]], 244 | ]) 245 | const initialState = { 246 | Program: { 247 | EOF: { 248 | $end: true 249 | } 250 | } 251 | } 252 | 253 | it('ForStatement', () => { 254 | evaluate(` 255 | let a = 0 256 | for(let i = 0; i < 10; i++) {a++} 257 | `, map, initialState) 258 | expect(globalEnv.get('a')).toBe(10) 259 | }) 260 | }) 261 | 262 | describe('Test BreakStatement', () => { 263 | const map = new Map([ 264 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 265 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 266 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 267 | ['NewExpression', [['MemberExpression'], ['new', 'NewExpression']]], 268 | ['CallExpression', [ 269 | ['new', 'MemberExpression', '(', ')'], 270 | ['MemberExpression', '(', ')'], 271 | ['CallExpression', '.', 'Identifier'], 272 | ['CallExpression', '[', 'Expression', ']'], 273 | ['CallExpression', '(', 'Arguments', ')'] 274 | ]], 275 | ['LeftHandSideExpression', [['MemberExpression'], ['CallExpression'], ['NewExpression']]], 276 | ['MultiplicativeExpression', [['UpdateExpression'], ['MultiplicativeExpression', '*', 'UpdateExpression'], ['MultiplicativeExpression', '/', 'UpdateExpression'], ['MultiplicativeExpression', '%', 'UpdateExpression']]], 277 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 278 | ['RelationalExpression', [['AdditiveExpression'], ['RelationalExpression', '>', 'AdditiveExpression'], ['RelationalExpression', '<', 'AdditiveExpression']]], 279 | ['EqualityExpression', [ 280 | ['RelationalExpression'], 281 | ['EqualityExpression', '==', 'RelationalExpression'], 282 | ['EqualityExpression', '!=', 'RelationalExpression'], 283 | ['EqualityExpression', '===', 'RelationalExpression'], 284 | ['EqualityExpression', '!==', 'RelationalExpression'], 285 | ]], 286 | ['AssignmentExpression', [['EqualityExpression'], ['LeftHandSideExpression', '=', 'EqualityExpression']]], 287 | ['Expression', [ 288 | ['AssignmentExpression'], 289 | ['Expression', ',', 'AssignmentExpression'], 290 | ]], 291 | ['UpdateExpression', [ 292 | ['LeftHandSideExpression'], 293 | ['LeftHandSideExpression', '++'], 294 | ['LeftHandSideExpression', '--'], 295 | ['++', 'LeftHandSideExpression'], 296 | ['--', 'LeftHandSideExpression'] 297 | ]], 298 | ['ExpressionStatement', [['Expression', ';']]], 299 | ['Declaration', [ 300 | ['var', 'Identifier', '=', 'Expression', ';'], 301 | ['let', 'Identifier', '=', 'Expression', ';'], 302 | ['const', 'Identifier', '=', 'Expression', ';'] 303 | ]], 304 | ['Parameters', [['Identifier'], ['Parameters', ',', 'Identifier']]], 305 | ['FunctionDeclaration', [ 306 | ['function', 'Identifier', '(', ')', '{', 'StatementList', '}'], 307 | ['function', 'Identifier', '(', 'Parameters', ')', '{', 'StatementList', '}'] 308 | ]], 309 | /** Added: Start */ 310 | ['IfStatement', [ 311 | ['if', '(', 'Expression', ')', 'Statement'], 312 | ['if', '(', 'Expression', ')', 'else', 'Statement'] 313 | ]], 314 | ['BlockStatement', [ 315 | ['{', '}'], 316 | ['{', 'StatementList', '}'], 317 | ]], 318 | ['ForStatement', [ 319 | ['for', '(', 'let', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 320 | ['for', '(', 'var', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 321 | ['for', '(', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'] 322 | ]], 323 | ['BreakStatement', [ 324 | ['break', ';'] 325 | ]], 326 | /** Added: End */ 327 | ['Statement', [['ExpressionStatement'], ['IfStatement'], ['ForStatement'], ['Declaration'], ['BlockStatement'], ['BreakStatement']]], 328 | ['StatementListItem', [['Statement'], ['Declaration']]], 329 | ['StatementList', [['StatementListItem'], ['StatementList', 'StatementListItem']]], 330 | /** Added */ 331 | ['Program', [['StatementList']]], 332 | ]) 333 | const initialState = { 334 | Program: { 335 | EOF: { 336 | $end: true 337 | } 338 | } 339 | } 340 | 341 | it('Test BreakStatement', () => { 342 | evaluate(` 343 | for(let i = 1; i < 10; i++) { 344 | if (i === 5) break; 345 | } 346 | `, map, initialState) 347 | expect(globalEnv.get('i')).toBe(5) 348 | }) 349 | }) 350 | }) -------------------------------------------------------------------------------- /__test__/helpers/executor/1-logical-break-continue.spec.js: -------------------------------------------------------------------------------- 1 | const { evaluate, globalEnv } = require('../../../src/index') 2 | describe('Test Homework', () => { 3 | const map = new Map([ 4 | ['Parameters', [ 5 | ['Identifier'], 6 | ['Parameters', ',', 'Identifier'] 7 | ]], 8 | ['FunctionDeclaration', [ 9 | ['function', 'Identifier', '(', ')', '{', 'StatementList', '}'], 10 | ['function', 'Identifier', '(', 'Parameters', ')', '{', 'StatementList', '}'] 11 | ]], 12 | ['Declaration', [ 13 | ['FunctionDeclaration'], 14 | ['var', 'Identifier', '=', 'Expression', ';'], 15 | ['let', 'Identifier', '=', 'Expression', ';'], 16 | ['const', 'Identifier', '=', 'Expression', ';'] 17 | ]], 18 | ['Literal', [ 19 | ['NumbericLiteral'], 20 | ['StringLiteral'], 21 | ['BooleanLiteral'], 22 | ['NullLiteral'] 23 | ]], 24 | ['Primary', [ 25 | ['(', 'Expression', ')'], 26 | ['Literal'], 27 | ['Identifier'] 28 | ]], 29 | ['MemberExpression', [ 30 | ['Primary'], 31 | ['MemberExpression', '.', 'Identifier'], 32 | ['MemberExpression', '[', 'Expression', ']'] 33 | ]], 34 | ['NewExpression', [ 35 | ['MemberExpression'], 36 | ['new', 'NewExpression'] 37 | ]], 38 | ['CallExpression', [ 39 | ['new', 'MemberExpression', '(', ')'], 40 | ['MemberExpression', '(', ')'], 41 | ['CallExpression', '.', 'Identifier'], 42 | ['CallExpression', '[', 'Expression', ']'], 43 | ['CallExpression', '(', 'Arguments', ')'] 44 | ]], 45 | ['LeftHandSideExpression', [ 46 | ['MemberExpression'], 47 | ['CallExpression'], 48 | ['NewExpression'] 49 | ]], 50 | ['UpdateExpression', [ 51 | ['LeftHandSideExpression'], 52 | ['LeftHandSideExpression', '++'], 53 | ['LeftHandSideExpression', '--'], 54 | ['++', 'LeftHandSideExpression'], 55 | ['--', 'LeftHandSideExpression'] 56 | ]], 57 | ['MultiplicativeExpression', [ 58 | ['UpdateExpression'], 59 | ['MultiplicativeExpression', '*', 'UpdateExpression'], 60 | ['MultiplicativeExpression', '/', 'UpdateExpression'], 61 | ['MultiplicativeExpression', '%', 'UpdateExpression'] 62 | ]], 63 | ['AdditiveExpression', [ 64 | ['MultiplicativeExpression'], 65 | ['AdditiveExpression', '+', 'MultiplicativeExpression'], 66 | ['AdditiveExpression', '-', 'MultiplicativeExpression'] 67 | ]], 68 | ['RelationalExpression', [ 69 | ['AdditiveExpression'], 70 | ['RelationalExpression', '>', 'AdditiveExpression'], 71 | ['RelationalExpression', '<', 'AdditiveExpression'] 72 | ]], 73 | ['EqualityExpression', [ 74 | ['RelationalExpression'], 75 | ['EqualityExpression', '==', 'RelationalExpression'], 76 | ['EqualityExpression', '!=', 'RelationalExpression'], 77 | ['EqualityExpression', '===', 'RelationalExpression'], 78 | ['EqualityExpression', '!==', 'RelationalExpression'], 79 | ]], 80 | ['BitwiseANDExpression', [ 81 | ['EqualityExpression'], 82 | ['BitwiseANDExpression', '&', 'EqualityExpression'] 83 | ]], 84 | ['BitwiseXORExpression', [ 85 | ['BitwiseANDExpression'], 86 | ['BitwiseXORExpression', '^', 'BitwiseANDExpression'] 87 | ]], 88 | ['BitwiseORExpression', [ 89 | ['BitwiseXORExpression'], 90 | ['BitwiseORExpression', '|', 'BitwiseXORExpression'] 91 | ]], 92 | ['LogicalANDExpression', [ 93 | ['BitwiseORExpression'], 94 | ['LogicalANDExpression', '&&', 'BitwiseORExpression'] 95 | ]], 96 | ['LogicalORExpression', [ 97 | ['LogicalANDExpression'], 98 | ['LogicalORExpression', '||', 'LogicalANDExpression'] 99 | ]], 100 | ['CoalesceExpression', [ 101 | ['ShortCircuitExpression', '??', 'BitwiseORExpression'] 102 | ]], 103 | // ['CoalesceExpressionHead', [ 104 | // ['CoalesceExpression'], 105 | // ['BitwiseORExpression'] 106 | // ]], 107 | ['ShortCircuitExpression', [ 108 | ['LogicalORExpression'], 109 | ['CoalesceExpression'] 110 | ]], 111 | ['ConditionalExpression', [ 112 | ['ShortCircuitExpression'], 113 | ['ShortCircuitExpression', '?', 'AssignmentExpression', ':', 'AssignmentExpression'] 114 | ]], 115 | ['AssignmentOperator', [ 116 | ['*='],['/='], ['%='], ['+='], ['-='], 117 | ['<<='], ['>>='], ['>>>='], 118 | ['&='], ['^='], ['|='], ['**='] 119 | ]], 120 | ['AssignmentExpression', [ 121 | ['ConditionalExpression'], 122 | ['LeftHandSideExpression', '=', 'AssignmentExpression'], 123 | ['LeftHandSideExpression', 'AssignmentOperator', 'AssignmentExpression'], 124 | ['LeftHandSideExpression', '&&=', 'AssignmentExpression'], 125 | ['LeftHandSideExpression', '||=', 'AssignmentExpression'], 126 | ['LeftHandSideExpression', '??=', 'AssignmentExpression'], 127 | ]], 128 | ['Expression', [ 129 | ['AssignmentExpression'], 130 | ['Expression', ',', 'AssignmentExpression'], 131 | ]], 132 | ['ExpressionStatement', [ 133 | ['Expression', ';'] 134 | ]], 135 | ['BlockStatement', [ 136 | ['{', '}'], 137 | ['{', 'StatementList', '}'], 138 | ]], 139 | ['IfStatement', [ 140 | ['if', '(', 'Expression', ')', 'Statement'], 141 | ['if', '(', 'Expression', ')', 'else', 'Statement'] 142 | ]], 143 | ['ForStatement', [ 144 | ['for', '(', 'let', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 145 | ['for', '(', 'var', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 146 | ['for', '(', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'] 147 | ]], 148 | ['BreakableStatement', [ 149 | ['ForStatement'] 150 | ]], 151 | ['BreakStatement', [ 152 | ['break', ';'], 153 | ['break', 'Identifier', ';'] 154 | ]], 155 | ['ContinueStatement', [ 156 | ['continue', ';'], 157 | ['continue', 'Identifier', ';'] 158 | ]], 159 | ['Statement', [ 160 | ['BlockStatement'], 161 | ['ExpressionStatement'], 162 | ['IfStatement'], 163 | ['ForStatement'], 164 | ['BreakableStatement'], 165 | ['BreakStatement'], 166 | ['ContinueStatement'], 167 | ['Declaration'], 168 | ]], 169 | ['StatementListItem', [ 170 | ['Statement'], 171 | ['Declaration'] 172 | ]], 173 | ['StatementList', [ 174 | ['StatementListItem'], 175 | ['StatementList', 'StatementListItem'] 176 | ]], 177 | ['Program', [['StatementList']]], 178 | ]) 179 | const initialState = { 180 | Program: { 181 | EOF: { 182 | $end: true 183 | } 184 | } 185 | } 186 | 187 | it('Test EqualityExpression', () => { 188 | expect(evaluate(`'1' == 1 === (1 != '0');`, map, initialState)).toEqual({ 189 | type: 'normal', 190 | value: true 191 | }) 192 | }) 193 | 194 | it('Test BitwiseExpression', () => { 195 | expect(evaluate(`1 | 2 ^ 2 & 1;`, map, initialState)).toEqual({ 196 | type: 'normal', 197 | value: 3 198 | }) 199 | }) 200 | 201 | it('Test LogicalExpression', () => { 202 | expect(evaluate(`(0 ?? 2) || 1 && 2`, map, initialState)).toEqual({ 203 | type: 'normal', 204 | value: 2 205 | }) 206 | }) 207 | 208 | it('Test LogicalExpression with let a = 1 || 2', () => { 209 | evaluate('let a = 1 || 2', map, initialState) 210 | expect(globalEnv.get('a')).toEqual(1) 211 | }) 212 | 213 | it('Test AssignmentOperator', () => { 214 | expect(evaluate(` 215 | let ans = 0 216 | ans += 1 217 | ans *= 2 218 | ans /= 2 219 | ans <<= 2 220 | ans >>= 1 221 | ans >>>= 1 222 | `, map, initialState)).toEqual({ 223 | type: 'normal', 224 | value: 1 225 | }) 226 | }) 227 | 228 | it('Test BreakStatement', () => { 229 | expect(evaluate(` 230 | let ans = 0 231 | for(let i = 0; i < 10; i++) { 232 | if (i === 5) break; 233 | ans++ 234 | } 235 | ans + 0; 236 | `, map, initialState)).toEqual({ 237 | type: 'normal', 238 | value: 5 239 | }) 240 | }) 241 | 242 | it('Test ContinueStatement', () => { 243 | expect(evaluate(` 244 | let ans = 0 245 | for(let i = 0; i < 10; i++) { 246 | if (i % 2 === 0) continue; 247 | ans++; 248 | } 249 | ans + 0; 250 | `)).toEqual({ 251 | type: 'normal', 252 | value: 5 253 | }) 254 | }) 255 | }) -------------------------------------------------------------------------------- /__test__/helpers/executor/2-object-func-return-call.spec.js: -------------------------------------------------------------------------------- 1 | 2 | const { globalEnv, evaluate } = require('../../../src/index') 3 | describe('Test Object And Function', () => { 4 | const map = new Map([ 5 | ['PropertyDefinition', [ 6 | ['StringLiteral', ':', 'Expression'], 7 | ['NumbericLiteral', ':', 'Expression'] 8 | ]], 9 | ['PropertyDefinitionList', [ 10 | ['PropertyDefinition'], 11 | ['PropertyDefinitionList', ':', 'PropertyDefinition'] 12 | ]], 13 | ['ObjectLiteral', [ 14 | ['{', '}'], 15 | ['{', 'PropertyDefinitionList', '}'], 16 | ['{', 'PropertyDefinitionList', ',', '}'], 17 | ]], 18 | ['Literal', [ 19 | ['NumbericLiteral'], 20 | ['StringLiteral'], 21 | ['BooleanLiteral'], 22 | ['NullLiteral'], 23 | ['ObjectLiteral'], 24 | ]], 25 | ['Primary', [ 26 | ['(', 'Expression', ')'], 27 | ['Literal'], 28 | ['Identifier'] 29 | ]], 30 | ['Arguments', [ 31 | ['AssignmentExpression'], 32 | ['Arguments', ',', 'AssignmentExpression'] 33 | ]], 34 | ['MemberExpression', [ 35 | ['Primary'], 36 | ['MemberExpression', '.', 'Identifier'], 37 | ['MemberExpression', '[', 'Expression', ']'], 38 | ['new', 'MemberExpression', '(', 'Arguments', ')'], 39 | ['MemberExpression', '(', ')'], 40 | ]], 41 | ['NewExpression', [ 42 | ['MemberExpression'], 43 | ['new', 'NewExpression'] 44 | ]], 45 | ['CallExpression', [ 46 | ['CallExpression', '.', 'Identifier'], 47 | ['CallExpression', '[', 'Expression', ']'], 48 | ['CallExpression', '(', 'Arguments', ')'] 49 | ]], 50 | ['LeftHandSideExpression', [ 51 | ['MemberExpression'], 52 | ['CallExpression'], 53 | ['NewExpression'] 54 | ]], 55 | ['UpdateExpression', [ 56 | ['LeftHandSideExpression'], 57 | ['LeftHandSideExpression', '++'], 58 | ['LeftHandSideExpression', '--'], 59 | ['++', 'LeftHandSideExpression'], 60 | ['--', 'LeftHandSideExpression'] 61 | ]], 62 | ['MultiplicativeExpression', [ 63 | ['UpdateExpression'], 64 | ['MultiplicativeExpression', '*', 'UpdateExpression'], 65 | ['MultiplicativeExpression', '/', 'UpdateExpression'], 66 | ['MultiplicativeExpression', '%', 'UpdateExpression'] 67 | ]], 68 | ['AdditiveExpression', [ 69 | ['MultiplicativeExpression'], 70 | ['AdditiveExpression', '+', 'MultiplicativeExpression'], 71 | ['AdditiveExpression', '-', 'MultiplicativeExpression'] 72 | ]], 73 | ['RelationalExpression', [ 74 | ['AdditiveExpression'], 75 | ['RelationalExpression', '>', 'AdditiveExpression'], 76 | ['RelationalExpression', '<', 'AdditiveExpression'] 77 | ]], 78 | ['EqualityExpression', [ 79 | ['RelationalExpression'], 80 | ['EqualityExpression', '==', 'RelationalExpression'], 81 | ['EqualityExpression', '!=', 'RelationalExpression'], 82 | ['EqualityExpression', '===', 'RelationalExpression'], 83 | ['EqualityExpression', '!==', 'RelationalExpression'], 84 | ]], 85 | ['BitwiseANDExpression', [ 86 | ['EqualityExpression'], 87 | ['BitwiseANDExpression', '&', 'EqualityExpression'] 88 | ]], 89 | ['BitwiseXORExpression', [ 90 | ['BitwiseANDExpression'], 91 | ['BitwiseXORExpression', '^', 'BitwiseANDExpression'] 92 | ]], 93 | ['BitwiseORExpression', [ 94 | ['BitwiseXORExpression'], 95 | ['BitwiseORExpression', '|', 'BitwiseXORExpression'] 96 | ]], 97 | ['LogicalANDExpression', [ 98 | ['BitwiseORExpression'], 99 | ['LogicalANDExpression', '&&', 'BitwiseORExpression'] 100 | ]], 101 | ['LogicalORExpression', [ 102 | ['LogicalANDExpression'], 103 | ['LogicalORExpression', '||', 'LogicalANDExpression'] 104 | ]], 105 | ['CoalesceExpression', [ 106 | ['ShortCircuitExpression', '??', 'BitwiseORExpression'] 107 | ]], 108 | // ['CoalesceExpressionHead', [ 109 | // ['CoalesceExpression'], 110 | // ['BitwiseORExpression'] 111 | // ]], 112 | ['ShortCircuitExpression', [ 113 | ['LogicalORExpression'], 114 | ['CoalesceExpression'] 115 | ]], 116 | ['ConditionalExpression', [ 117 | ['ShortCircuitExpression'], 118 | ['ShortCircuitExpression', '?', 'AssignmentExpression', ':', 'AssignmentExpression'] 119 | ]], 120 | ['AssignmentOperator', [ 121 | ['*='],['/='], ['%='], ['+='], ['-='], 122 | ['<<='], ['>>='], ['>>>='], 123 | ['&='], ['^='], ['|='], ['**='] 124 | ]], 125 | ['AssignmentExpression', [ 126 | ['ConditionalExpression'], 127 | ['LeftHandSideExpression', '=', 'AssignmentExpression'], 128 | ['LeftHandSideExpression', 'AssignmentOperator', 'AssignmentExpression'], 129 | ['LeftHandSideExpression', '&&=', 'AssignmentExpression'], 130 | ['LeftHandSideExpression', '||=', 'AssignmentExpression'], 131 | ['LeftHandSideExpression', '??=', 'AssignmentExpression'], 132 | ]], 133 | ['Expression', [ 134 | ['AssignmentExpression'], 135 | ['Expression', ',', 'AssignmentExpression'], 136 | ]], 137 | ['Parameters', [ 138 | ['Identifier'], 139 | ['Parameters', ',', 'Identifier'] 140 | ]], 141 | ['FunctionDeclaration', [ 142 | ['function', 'Identifier', '(', ')', 'BlockStatement'], 143 | ['function', 'Identifier', '(', 'Parameters', ')', 'BlockStatement'] 144 | ]], 145 | ['Declaration', [ 146 | ['FunctionDeclaration'], 147 | ['var', 'Identifier', '=', 'Expression', ';'], 148 | ['let', 'Identifier', '=', 'Expression', ';'], 149 | ['const', 'Identifier', '=', 'Expression', ';'] 150 | ]], 151 | ['ExpressionStatement', [ 152 | ['Expression', ';'] 153 | ]], 154 | ['BlockStatement', [ 155 | ['{', '}'], 156 | ['{', 'StatementList', '}'], 157 | ]], 158 | ['IfStatement', [ 159 | ['if', '(', 'Expression', ')', 'Statement'], 160 | ['if', '(', 'Expression', ')', 'else', 'Statement'] 161 | ]], 162 | ['ForStatement', [ 163 | ['for', '(', 'let', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 164 | ['for', '(', 'var', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 165 | ['for', '(', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'] 166 | ]], 167 | ['BreakableStatement', [ 168 | ['ForStatement'] 169 | ]], 170 | ['BreakStatement', [ 171 | ['break', ';'], 172 | ['break', 'Identifier', ';'] 173 | ]], 174 | ['ContinueStatement', [ 175 | ['continue', ';'], 176 | ['continue', 'Identifier', ';'] 177 | ]], 178 | ['Statement', [ 179 | ['BlockStatement'], 180 | ['ExpressionStatement'], 181 | ['IfStatement'], 182 | ['ForStatement'], 183 | ['BreakableStatement'], 184 | ['BreakStatement'], 185 | ['ContinueStatement'], 186 | ['Declaration'], 187 | ]], 188 | ['StatementListItem', [ 189 | ['Statement'], 190 | ['Declaration'] 191 | ]], 192 | ['StatementList', [ 193 | ['StatementListItem'], 194 | ['StatementList', 'StatementListItem'] 195 | ]], 196 | ['Program', [['StatementList']]], 197 | ]) 198 | const initialState = { 199 | Program: { 200 | EOF: { 201 | $end: true 202 | } 203 | } 204 | } 205 | 206 | it('Test JSObject', () => { 207 | evaluate(`let a = {'b' : 1}`, map, initialState) 208 | expect(globalEnv.get('a').getProperty('b')).toBe(1) 209 | }) 210 | 211 | it('Test JSFunction', () => { 212 | evaluate(`function a() { 213 | const b = 1; 214 | }`, map, initialState) 215 | expect(globalEnv.get('a').functionBody.type).toBe('BlockStatement') 216 | }) 217 | }) 218 | 219 | 220 | describe('Test Return Statement', () => { 221 | const map = new Map([ 222 | ['PropertyDefinition', [ 223 | ['StringLiteral', ':', 'Expression'], 224 | ['NumbericLiteral', ':', 'Expression'] 225 | ]], 226 | ['PropertyDefinitionList', [ 227 | ['PropertyDefinition'], 228 | ['PropertyDefinitionList', ':', 'PropertyDefinition'] 229 | ]], 230 | ['ObjectLiteral', [ 231 | ['{', '}'], 232 | ['{', 'PropertyDefinitionList', '}'], 233 | ['{', 'PropertyDefinitionList', ',', '}'], 234 | ]], 235 | ['Literal', [ 236 | ['NumbericLiteral'], 237 | ['StringLiteral'], 238 | ['BooleanLiteral'], 239 | ['NullLiteral'], 240 | ['ObjectLiteral'], 241 | ]], 242 | ['Primary', [ 243 | ['(', 'Expression', ')'], 244 | ['Literal'], 245 | ['Identifier'] 246 | ]], 247 | ['Arguments', [ 248 | ['(', ')'], 249 | ['(', 'ArgumentList', ')'], 250 | ['(', 'ArgumentList', ',', ')'] 251 | ]], 252 | ['ArgumentList', [ 253 | ['AssignmentExpression'], 254 | ['ArgumentList', ',', 'AssignmentExpression'] 255 | ]], 256 | ['MemberExpression', [ 257 | ['Primary'], 258 | ['MemberExpression', '.', 'Identifier'], 259 | ['MemberExpression', '[', 'Expression', ']'], 260 | ['new', 'MemberExpression', 'Arguments'] 261 | ]], 262 | ['NewExpression', [ 263 | ['MemberExpression'], 264 | ['new', 'NewExpression'] 265 | ]], 266 | ['CallExpression', [ 267 | ['CoverCallExpressionAndAsyncArrowHead'], 268 | ['CallExpression', 'Arguments'], 269 | ['CallExpression', '[', 'Expression', ']'], 270 | ['CallExpression', '.', 'Identifier'], 271 | ]], 272 | ['CoverCallExpressionAndAsyncArrowHead', [ 273 | ['MemberExpression', 'Arguments'] 274 | ]], 275 | ['LeftHandSideExpression', [ 276 | ['MemberExpression'], 277 | ['CallExpression'], 278 | ['NewExpression'] 279 | ]], 280 | ['UpdateExpression', [ 281 | ['LeftHandSideExpression'], 282 | ['LeftHandSideExpression', '++'], 283 | ['LeftHandSideExpression', '--'], 284 | ['++', 'LeftHandSideExpression'], 285 | ['--', 'LeftHandSideExpression'] 286 | ]], 287 | ['MultiplicativeExpression', [ 288 | ['UpdateExpression'], 289 | ['MultiplicativeExpression', '*', 'UpdateExpression'], 290 | ['MultiplicativeExpression', '/', 'UpdateExpression'], 291 | ['MultiplicativeExpression', '%', 'UpdateExpression'] 292 | ]], 293 | ['AdditiveExpression', [ 294 | ['MultiplicativeExpression'], 295 | ['AdditiveExpression', '+', 'MultiplicativeExpression'], 296 | ['AdditiveExpression', '-', 'MultiplicativeExpression'] 297 | ]], 298 | ['RelationalExpression', [ 299 | ['AdditiveExpression'], 300 | ['RelationalExpression', '>', 'AdditiveExpression'], 301 | ['RelationalExpression', '<', 'AdditiveExpression'] 302 | ]], 303 | ['EqualityExpression', [ 304 | ['RelationalExpression'], 305 | ['EqualityExpression', '==', 'RelationalExpression'], 306 | ['EqualityExpression', '!=', 'RelationalExpression'], 307 | ['EqualityExpression', '===', 'RelationalExpression'], 308 | ['EqualityExpression', '!==', 'RelationalExpression'], 309 | ]], 310 | ['BitwiseANDExpression', [ 311 | ['EqualityExpression'], 312 | ['BitwiseANDExpression', '&', 'EqualityExpression'] 313 | ]], 314 | ['BitwiseXORExpression', [ 315 | ['BitwiseANDExpression'], 316 | ['BitwiseXORExpression', '^', 'BitwiseANDExpression'] 317 | ]], 318 | ['BitwiseORExpression', [ 319 | ['BitwiseXORExpression'], 320 | ['BitwiseORExpression', '|', 'BitwiseXORExpression'] 321 | ]], 322 | ['LogicalANDExpression', [ 323 | ['BitwiseORExpression'], 324 | ['LogicalANDExpression', '&&', 'BitwiseORExpression'] 325 | ]], 326 | ['LogicalORExpression', [ 327 | ['LogicalANDExpression'], 328 | ['LogicalORExpression', '||', 'LogicalANDExpression'] 329 | ]], 330 | ['CoalesceExpression', [ 331 | ['ShortCircuitExpression', '??', 'BitwiseORExpression'] 332 | ]], 333 | // ['CoalesceExpressionHead', [ 334 | // ['CoalesceExpression'], 335 | // ['BitwiseORExpression'] 336 | // ]], 337 | ['ShortCircuitExpression', [ 338 | ['LogicalORExpression'], 339 | ['CoalesceExpression'] 340 | ]], 341 | ['ConditionalExpression', [ 342 | ['ShortCircuitExpression'], 343 | ['ShortCircuitExpression', '?', 'AssignmentExpression', ':', 'AssignmentExpression'] 344 | ]], 345 | ['AssignmentOperator', [ 346 | ['*='],['/='], ['%='], ['+='], ['-='], 347 | ['<<='], ['>>='], ['>>>='], 348 | ['&='], ['^='], ['|='], ['**='] 349 | ]], 350 | ['AssignmentExpression', [ 351 | ['ConditionalExpression'], 352 | ['LeftHandSideExpression', '=', 'AssignmentExpression'], 353 | ['LeftHandSideExpression', 'AssignmentOperator', 'AssignmentExpression'], 354 | ['LeftHandSideExpression', '&&=', 'AssignmentExpression'], 355 | ['LeftHandSideExpression', '||=', 'AssignmentExpression'], 356 | ['LeftHandSideExpression', '??=', 'AssignmentExpression'], 357 | ]], 358 | ['Expression', [ 359 | ['AssignmentExpression'], 360 | ['Expression', ',', 'AssignmentExpression'], 361 | ]], 362 | ['Parameters', [ 363 | ['Identifier'], 364 | ['Parameters', ',', 'Identifier'] 365 | ]], 366 | ['FunctionDeclaration', [ 367 | ['function', 'Identifier', '(', ')', 'BlockStatement'], 368 | ['function', 'Identifier', '(', 'Parameters', ')', 'BlockStatement'] 369 | ]], 370 | ['Declaration', [ 371 | ['FunctionDeclaration'], 372 | ['var', 'Identifier', '=', 'Expression', ';'], 373 | ['let', 'Identifier', '=', 'Expression', ';'], 374 | ['const', 'Identifier', '=', 'Expression', ';'] 375 | ]], 376 | ['ExpressionStatement', [ 377 | ['Expression', ';'] 378 | ]], 379 | ['BlockStatement', [ 380 | ['{', '}'], 381 | ['{', 'StatementList', '}'], 382 | ]], 383 | ['IfStatement', [ 384 | ['if', '(', 'Expression', ')', 'Statement'], 385 | ['if', '(', 'Expression', ')', 'else', 'Statement'] 386 | ]], 387 | ['ForStatement', [ 388 | ['for', '(', 'let', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 389 | ['for', '(', 'var', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 390 | ['for', '(', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'] 391 | ]], 392 | ['BreakableStatement', [ 393 | ['ForStatement'] 394 | ]], 395 | ['BreakStatement', [ 396 | ['break', ';'], 397 | ['break', 'Identifier', ';'] 398 | ]], 399 | ['ContinueStatement', [ 400 | ['continue', ';'], 401 | ['continue', 'Identifier', ';'] 402 | ]], 403 | ['ReturnStatement', [ 404 | ['return', ';'], 405 | ['return', 'Expression', ';'], 406 | ]], 407 | ['Statement', [ 408 | ['BlockStatement'], 409 | ['ExpressionStatement'], 410 | ['IfStatement'], 411 | ['ForStatement'], 412 | ['BreakableStatement'], 413 | ['BreakStatement'], 414 | ['ContinueStatement'], 415 | ['ReturnStatement'], 416 | ['Declaration'], 417 | ]], 418 | ['StatementListItem', [ 419 | ['Statement'], 420 | ['Declaration'] 421 | ]], 422 | ['StatementList', [ 423 | ['StatementListItem'], 424 | ['StatementList', 'StatementListItem'] 425 | ]], 426 | ['Program', [['StatementList']]], 427 | ]) 428 | const initialState = { 429 | Program: { 430 | EOF: { 431 | $end: true 432 | } 433 | } 434 | } 435 | it('Test CallExpression With ReturnStatement', () => { 436 | expect(evaluate(` 437 | let a = 1 438 | function f(){ 439 | return ++a 440 | } 441 | f() 442 | `, map, initialState)).toEqual({ 443 | type: 'normal', 444 | value: { 445 | type: 'return', 446 | value: 2 447 | } 448 | }) 449 | expect(globalEnv.get('a')).toBe(2) 450 | }) 451 | it('Test CallExpression With ReturnStatement And Scope', () => { 452 | expect(evaluate(` 453 | let a = 1 454 | function f() { 455 | let a = 0 456 | a += 2 457 | return a 458 | } 459 | f() 460 | `, map, initialState)).toEqual({ 461 | type: 'normal', 462 | value: { 463 | type: 'return', 464 | value: 2 465 | } 466 | }) 467 | expect(globalEnv.get('a')).toBe(1) 468 | }) 469 | it('Test CallExpression With ReturnStatement, Scope And Parameters', () => { 470 | expect(evaluate(` 471 | let a = 1 472 | function f(b) { 473 | a += b 474 | return a 475 | } 476 | f(2) 477 | `, map, initialState)).toEqual({ 478 | type: 'normal', 479 | value: { 480 | type: 'return', 481 | value: 3 482 | } 483 | }) 484 | expect(globalEnv.get('a')).toBe(3) 485 | }) 486 | }) -------------------------------------------------------------------------------- /__test__/helpers/executor/3-new-promise.spec.js: -------------------------------------------------------------------------------- 1 | const { globalEnv, evaluate } = require('../../../src/index') 2 | describe('Test Promise', () => { 3 | const map = new Map([ 4 | ['PropertyDefinition', [ 5 | ['StringLiteral', ':', 'Expression'], 6 | ['NumbericLiteral', ':', 'Expression'] 7 | ]], 8 | ['PropertyDefinitionList', [ 9 | ['PropertyDefinition'], 10 | ['PropertyDefinitionList', ':', 'PropertyDefinition'] 11 | ]], 12 | ['ObjectLiteral', [ 13 | ['{', '}'], 14 | ['{', 'PropertyDefinitionList', '}'], 15 | ['{', 'PropertyDefinitionList', ',', '}'], 16 | ]], 17 | ['Literal', [ 18 | ['NumbericLiteral'], 19 | ['StringLiteral'], 20 | ['BooleanLiteral'], 21 | ['NullLiteral'], 22 | ['ObjectLiteral'], 23 | ]], 24 | ['Primary', [ 25 | ['(', 'Expression', ')'], 26 | ['Literal'], 27 | ['Identifier'] 28 | ]], 29 | ['Arguments', [ 30 | ['(', ')'], 31 | ['(', 'ArgumentList', ')'], 32 | ['(', 'ArgumentList', ',', ')'] 33 | ]], 34 | ['ArgumentList', [ 35 | ['AssignmentExpression'], 36 | ['ArgumentList', ',', 'AssignmentExpression'] 37 | ]], 38 | ['MemberExpression', [ 39 | ['Primary'], 40 | ['MemberExpression', '.', 'Identifier'], 41 | ['MemberExpression', '[', 'Expression', ']'], 42 | ['new', 'MemberExpression', 'Arguments'] 43 | ]], 44 | ['NewExpression', [ 45 | ['MemberExpression'], 46 | ['new', 'NewExpression'] 47 | ]], 48 | ['CallExpression', [ 49 | ['CoverCallExpressionAndAsyncArrowHead'], 50 | ['CallExpression', 'Arguments'], 51 | ['CallExpression', '[', 'Expression', ']'], 52 | ['CallExpression', '.', 'Identifier'], 53 | ]], 54 | ['CoverCallExpressionAndAsyncArrowHead', [ 55 | ['MemberExpression', 'Arguments'] 56 | ]], 57 | ['LeftHandSideExpression', [ 58 | ['MemberExpression'], 59 | ['CallExpression'], 60 | ['NewExpression'] 61 | ]], 62 | ['UpdateExpression', [ 63 | ['LeftHandSideExpression'], 64 | ['LeftHandSideExpression', '++'], 65 | ['LeftHandSideExpression', '--'], 66 | ['++', 'LeftHandSideExpression'], 67 | ['--', 'LeftHandSideExpression'] 68 | ]], 69 | ['MultiplicativeExpression', [ 70 | ['UpdateExpression'], 71 | ['MultiplicativeExpression', '*', 'UpdateExpression'], 72 | ['MultiplicativeExpression', '/', 'UpdateExpression'], 73 | ['MultiplicativeExpression', '%', 'UpdateExpression'] 74 | ]], 75 | ['AdditiveExpression', [ 76 | ['MultiplicativeExpression'], 77 | ['AdditiveExpression', '+', 'MultiplicativeExpression'], 78 | ['AdditiveExpression', '-', 'MultiplicativeExpression'] 79 | ]], 80 | ['RelationalExpression', [ 81 | ['AdditiveExpression'], 82 | ['RelationalExpression', '>', 'AdditiveExpression'], 83 | ['RelationalExpression', '<', 'AdditiveExpression'] 84 | ]], 85 | ['EqualityExpression', [ 86 | ['RelationalExpression'], 87 | ['EqualityExpression', '==', 'RelationalExpression'], 88 | ['EqualityExpression', '!=', 'RelationalExpression'], 89 | ['EqualityExpression', '===', 'RelationalExpression'], 90 | ['EqualityExpression', '!==', 'RelationalExpression'], 91 | ]], 92 | ['BitwiseANDExpression', [ 93 | ['EqualityExpression'], 94 | ['BitwiseANDExpression', '&', 'EqualityExpression'] 95 | ]], 96 | ['BitwiseXORExpression', [ 97 | ['BitwiseANDExpression'], 98 | ['BitwiseXORExpression', '^', 'BitwiseANDExpression'] 99 | ]], 100 | ['BitwiseORExpression', [ 101 | ['BitwiseXORExpression'], 102 | ['BitwiseORExpression', '|', 'BitwiseXORExpression'] 103 | ]], 104 | ['LogicalANDExpression', [ 105 | ['BitwiseORExpression'], 106 | ['LogicalANDExpression', '&&', 'BitwiseORExpression'] 107 | ]], 108 | ['LogicalORExpression', [ 109 | ['LogicalANDExpression'], 110 | ['LogicalORExpression', '||', 'LogicalANDExpression'] 111 | ]], 112 | ['CoalesceExpression', [ 113 | ['ShortCircuitExpression', '??', 'BitwiseORExpression'] 114 | ]], 115 | // ['CoalesceExpressionHead', [ 116 | // ['CoalesceExpression'], 117 | // ['BitwiseORExpression'] 118 | // ]], 119 | ['ShortCircuitExpression', [ 120 | ['LogicalORExpression'], 121 | ['CoalesceExpression'] 122 | ]], 123 | ['ConditionalExpression', [ 124 | ['ShortCircuitExpression'], 125 | ['ShortCircuitExpression', '?', 'AssignmentExpression', ':', 'AssignmentExpression'] 126 | ]], 127 | ['AssignmentOperator', [ 128 | ['*='],['/='], ['%='], ['+='], ['-='], 129 | ['<<='], ['>>='], ['>>>='], 130 | ['&='], ['^='], ['|='], ['**='] 131 | ]], 132 | ['AssignmentExpression', [ 133 | ['ConditionalExpression'], 134 | ['LeftHandSideExpression', '=', 'AssignmentExpression'], 135 | ['LeftHandSideExpression', 'AssignmentOperator', 'AssignmentExpression'], 136 | ['LeftHandSideExpression', '&&=', 'AssignmentExpression'], 137 | ['LeftHandSideExpression', '||=', 'AssignmentExpression'], 138 | ['LeftHandSideExpression', '??=', 'AssignmentExpression'], 139 | ]], 140 | ['Expression', [ 141 | ['AssignmentExpression'], 142 | ['Expression', ',', 'AssignmentExpression'], 143 | ]], 144 | ['Parameters', [ 145 | ['Identifier'], 146 | ['Parameters', ',', 'Identifier'] 147 | ]], 148 | ['FunctionDeclaration', [ 149 | ['function', 'Identifier', '(', ')', 'BlockStatement'], 150 | ['function', 'Identifier', '(', 'Parameters', ')', 'BlockStatement'] 151 | ]], 152 | ['Declaration', [ 153 | ['FunctionDeclaration'], 154 | ['var', 'Identifier', '=', 'Expression', ';'], 155 | ['let', 'Identifier', '=', 'Expression', ';'], 156 | ['const', 'Identifier', '=', 'Expression', ';'] 157 | ]], 158 | ['ExpressionStatement', [ 159 | ['Expression', ';'] 160 | ]], 161 | ['BlockStatement', [ 162 | ['{', '}'], 163 | ['{', 'StatementList', '}'], 164 | ]], 165 | ['IfStatement', [ 166 | ['if', '(', 'Expression', ')', 'Statement'], 167 | ['if', '(', 'Expression', ')', 'else', 'Statement'] 168 | ]], 169 | ['ForStatement', [ 170 | ['for', '(', 'let', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 171 | ['for', '(', 'var', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 172 | ['for', '(', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'] 173 | ]], 174 | ['BreakableStatement', [ 175 | ['ForStatement'] 176 | ]], 177 | ['BreakStatement', [ 178 | ['break', ';'], 179 | ['break', 'Identifier', ';'] 180 | ]], 181 | ['ContinueStatement', [ 182 | ['continue', ';'], 183 | ['continue', 'Identifier', ';'] 184 | ]], 185 | ['ReturnStatement', [ 186 | ['return', ';'], 187 | ['return', 'Expression', ';'], 188 | ]], 189 | ['Statement', [ 190 | ['BlockStatement'], 191 | ['ExpressionStatement'], 192 | ['IfStatement'], 193 | ['ForStatement'], 194 | ['BreakableStatement'], 195 | ['BreakStatement'], 196 | ['ContinueStatement'], 197 | ['ReturnStatement'], 198 | ['Declaration'], 199 | ]], 200 | ['StatementListItem', [ 201 | ['Statement'], 202 | ['Declaration'] 203 | ]], 204 | ['StatementList', [ 205 | ['StatementListItem'], 206 | ['StatementList', 'StatementListItem'] 207 | ]], 208 | ['Program', [['StatementList']]], 209 | ]) 210 | const initialState = { 211 | Program: { 212 | EOF: { 213 | $end: true 214 | } 215 | } 216 | } 217 | 218 | it('Test New Promise', () => { 219 | evaluate(` 220 | function callback (resolve){resolve()} 221 | const p = new Promise(callback) 222 | `, map, initialState) 223 | expect(globalEnv.get('p')).toBeTruthy() 224 | }) 225 | 226 | it('Test then function of promise instance using resolve', () => { 227 | evaluate(` 228 | let b = 1 229 | function fn(resolve){ 230 | resolve(1) 231 | } 232 | function then(resolveValue){ 233 | b = b + resolveValue 234 | } 235 | const p = new Promise(fn) 236 | p.then(then) 237 | `, map, initialState) 238 | expect(globalEnv.get('b')).toBe(2) 239 | }) 240 | it('Test then function of promise instance using reject', () => { 241 | evaluate(` 242 | let b = 1 243 | function fn(resolve, reject){ 244 | reject(1) 245 | } 246 | function then(rejectReason){ 247 | b = b + rejectReason 248 | } 249 | const p = new Promise(fn) 250 | p.then(then) 251 | `, map, initialState) 252 | expect(globalEnv.get('b')).toBe(2) 253 | }) 254 | }) -------------------------------------------------------------------------------- /__test__/helpers/lexical-analyer.spec.js: -------------------------------------------------------------------------------- 1 | const { lex } = require('../../src/index') 2 | 3 | describe('Test LR Parser', () => { 4 | test('let a = null + 1 + "str"', () => { 5 | expect(JSON.stringify(lex('let a = null + 1 + "str"'))).toEqual(JSON.stringify([{"type": "let", "value": "let"}, {"type": "Identifier", "value": "a"}, {"type": "=", "value": "="}, {"type": "NulLiteral", "value": "null"}, {"type": "+", "value": "+"}, {"type": "NumbericLiteral", "value": "1"}, {"type": "+", "value": "+"}, {"type": "StringLiteral", "value": "\"str\""}, {"type": "EOF"}])) 6 | }) 7 | }) -------------------------------------------------------------------------------- /__test__/helpers/syntax-parser.spec.js: -------------------------------------------------------------------------------- 1 | const { lex, parse } = require('../../src/index') 2 | 3 | describe('Test Expression Parser', () => { 4 | it('literal', () => { 5 | const map = new Map([ 6 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]] 7 | ]) 8 | const initialState = { 9 | Literal: { 10 | EOF: { 11 | $end: true 12 | } 13 | } 14 | } 15 | const expression = parse(lex("'fanfan'"), map, initialState) 16 | expect(JSON.stringify(expression)).toBe(JSON.stringify([{"type":"Literal","children":[{"type":"StringLiteral","value":"'fanfan'"}]},{"type":"EOF"}])) 17 | }) 18 | 19 | it('Primary', () => { 20 | const map = new Map([ 21 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 22 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]] 23 | ]) 24 | const initialState = { 25 | Primary: { 26 | EOF: { 27 | $end: true 28 | } 29 | } 30 | } 31 | const expression = parse(lex("'fanfan'"), map, initialState) 32 | expect(JSON.stringify(expression)).toBe(JSON.stringify([{"type":"Primary","children":[{"type":"Literal","children":[{"type":"StringLiteral","value":"'fanfan'"}]}]},{"type":"EOF"}])) 33 | }) 34 | it('MemberExpression', () => { 35 | const map = new Map([ 36 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 37 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 38 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 39 | ]) 40 | const initialState = { 41 | MemberExpression: { 42 | EOF: { 43 | $end: true 44 | } 45 | } 46 | } 47 | const expression = parse(lex("a.b"), map, initialState) 48 | expect(JSON.stringify(expression)).toBe(JSON.stringify([{"type":"MemberExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Identifier","value":"a"}]}]},{"type":".","value":"."},{"type":"Identifier","value":"b"}]},{"type":"EOF"}])) 49 | }) 50 | it('AdditiveExpression', () => { 51 | const map = new Map([ 52 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 53 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 54 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 55 | ['MultiplicativeExpression', [['MemberExpression'], ['MultiplicativeExpression', '*', 'MemberExpression'], ['MultiplicativeExpression', '/', 'MemberExpression'], ['MultiplicativeExpression', '%', 'MemberExpression']]], 56 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 57 | ]) 58 | const initialState = { 59 | AdditiveExpression: { 60 | EOF: { 61 | $end: true 62 | } 63 | } 64 | } 65 | const expression = parse(lex("a.b + 2 * 3"), map, initialState) 66 | expect(JSON.stringify(expression)).toBe(JSON.stringify([{"type":"AdditiveExpression","children":[{"type":"AdditiveExpression","children":[{"type":"MultiplicativeExpression","children":[{"type":"MemberExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Identifier","value":"a"}]}]},{"type":".","value":"."},{"type":"Identifier","value":"b"}]}]}]},{"type":"+","value":"+"},{"type":"MultiplicativeExpression","children":[{"type":"MultiplicativeExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"2"}]}]}]}]},{"type":"*","value":"*"},{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"3"}]}]}]}]}]},{"type":"EOF"}])) 67 | }) 68 | it('AssignmentExpression', () => { 69 | const map = new Map([ 70 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 71 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 72 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 73 | ['LeftHandSideExpression', [['MemberExpression']]], 74 | ['MultiplicativeExpression', [['LeftHandSideExpression'], ['MultiplicativeExpression', '*', 'LeftHandSideExpression'], ['MultiplicativeExpression', '/', 'LeftHandSideExpression'], ['MultiplicativeExpression', '%', 'LeftHandSideExpression']]], 75 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 76 | ['AssignmentExpression', [['AdditiveExpression'], ['LeftHandSideExpression', '=', 'AssignmentExpression']]] 77 | ]) 78 | const initialState = { 79 | AssignmentExpression: { 80 | EOF: { 81 | $end: true 82 | } 83 | } 84 | } 85 | const expression = parse(lex("a = 20"), map, initialState) 86 | expect(JSON.stringify(expression)).toBe(JSON.stringify([{"type":"AssignmentExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Identifier","value":"a"}]}]}]},{"type":"=","value":"="},{"type":"AssignmentExpression","children":[{"type":"AdditiveExpression","children":[{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"20"}]}]}]}]}]}]}]}]},{"type":"EOF"}])) 87 | }) 88 | it('Statement', () => { 89 | const map = new Map([ 90 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 91 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 92 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 93 | ['LeftHandSideExpression', [['MemberExpression']]], 94 | ['MultiplicativeExpression', [['LeftHandSideExpression'], ['MultiplicativeExpression', '*', 'LeftHandSideExpression'], ['MultiplicativeExpression', '/', 'LeftHandSideExpression'], ['MultiplicativeExpression', '%', 'LeftHandSideExpression']]], 95 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 96 | ['AssignmentExpression', [['AdditiveExpression'], ['LeftHandSideExpression', '=', 'AssignmentExpression']]], 97 | ['Expression', [['AssignmentExpression']]], 98 | ['ExpressionStatement', [['Expression', ';']]], 99 | ['Declaration', [ 100 | ['var', 'Identifier', '=', 'Expression', ';'], 101 | ['let', 'Identifier', '=', 'Expression', ';'], 102 | ['const', 'Identifier', '=', 'Expression', ';'] 103 | ]], 104 | // ['IfStatement', [ 105 | // ['if', '(', 'Expression', ')', 'Statement'], 106 | // ['if', '(', 'Expression', ')', 'else', 'Statement'] 107 | // ]], 108 | // ['forStatement', [ 109 | // ['for', '(', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'] 110 | // ]], 111 | ['Statement', [['ExpressionStatement'], ['IfStatement'], ['ForStatement'], ['Declaration']]], 112 | ]) 113 | const initialState = { 114 | Statement: { 115 | EOF: { 116 | $end: true 117 | } 118 | } 119 | } 120 | const expression = parse(lex("let a = 1;"), map, initialState) 121 | expect(JSON.stringify(expression)).toBe(JSON.stringify([{"type":"Statement","children":[{"type":"Declaration","children":[{"type":"let","value":"let"},{"type":"Identifier","value":"a"},{"type":"=","value":"="},{"type":"Expression","children":[{"type":"AssignmentExpression","children":[{"type":"AdditiveExpression","children":[{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"1"}]}]}]}]}]}]}]}]},{"type":";","value":";"}]}]},{"type":"EOF"}])) 122 | }) 123 | it('Test StatementList', () => { 124 | const map = new Map([ 125 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 126 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 127 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 128 | ['LeftHandSideExpression', [['MemberExpression']]], 129 | ['MultiplicativeExpression', [['LeftHandSideExpression'], ['MultiplicativeExpression', '*', 'LeftHandSideExpression'], ['MultiplicativeExpression', '/', 'LeftHandSideExpression'], ['MultiplicativeExpression', '%', 'LeftHandSideExpression']]], 130 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 131 | ['AssignmentExpression', [['AdditiveExpression'], ['LeftHandSideExpression', '=', 'AssignmentExpression']]], 132 | ['Expression', [['AssignmentExpression']]], 133 | ['ExpressionStatement', [['Expression', ';']]], 134 | ['Declaration', [ 135 | ['var', 'Identifier', '=', 'Expression', ';'], 136 | ['let', 'Identifier', '=', 'Expression', ';'], 137 | ['const', 'Identifier', '=', 'Expression', ';'] 138 | ]], 139 | ['Statement', [['ExpressionStatement'], ['IfStatement'], ['ForStatement'], ['Declaration']]], 140 | ['StatementListItem', [['Statement'], ['Declaration']]], 141 | ['StatementList', [['StatementListItem'], ['StatementList', 'StatementListItem']]] 142 | ]) 143 | const initialState = { 144 | StatementList: { 145 | EOF: { 146 | $end: true 147 | } 148 | } 149 | } 150 | const expression = parse(lex(` 151 | let a = 1; 152 | const b = 1; 153 | `), map, initialState) 154 | expect(JSON.stringify(expression)).toBe(JSON.stringify([{"type":"StatementList","children":[{"type":"StatementList","children":[{"type":"StatementListItem","children":[{"type":"Statement","children":[{"type":"Declaration","children":[{"type":"let","value":"let"},{"type":"Identifier","value":"a"},{"type":"=","value":"="},{"type":"Expression","children":[{"type":"AssignmentExpression","children":[{"type":"AdditiveExpression","children":[{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"1"}]}]}]}]}]}]}]}]},{"type":";","value":";"}]}]}]}]},{"type":"StatementListItem","children":[{"type":"Statement","children":[{"type":"Declaration","children":[{"type":"const","value":"const"},{"type":"Identifier","value":"b"},{"type":"=","value":"="},{"type":"Expression","children":[{"type":"AssignmentExpression","children":[{"type":"AdditiveExpression","children":[{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"1"}]}]}]}]}]}]}]}]},{"type":";","value":";"}]}]}]}]},{"type":"EOF"}])) 155 | }) 156 | 157 | it('ASI followed EOF and }', () => { 158 | const map = new Map([ 159 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 160 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 161 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 162 | ['LeftHandSideExpression', [['MemberExpression']]], 163 | ['MultiplicativeExpression', [['LeftHandSideExpression'], ['MultiplicativeExpression', '*', 'LeftHandSideExpression'], ['MultiplicativeExpression', '/', 'LeftHandSideExpression'], ['MultiplicativeExpression', '%', 'LeftHandSideExpression']]], 164 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 165 | ['AssignmentExpression', [['AdditiveExpression'], ['LeftHandSideExpression', '=', 'AssignmentExpression']]], 166 | ['Expression', [['AssignmentExpression']]], 167 | ['ExpressionStatement', [['Expression', ';']]], 168 | ['Declaration', [ 169 | ['var', 'Identifier', '=', 'Expression', ';'], 170 | ['let', 'Identifier', '=', 'Expression', ';'], 171 | ['const', 'Identifier', '=', 'Expression', ';'] 172 | ]], 173 | ['Statement', [['ExpressionStatement'], ['IfStatement'], ['ForStatement'], ['Declaration']]], 174 | ['StatementListItem', [['Statement'], ['Declaration']]], 175 | ['StatementList', [['StatementListItem'], ['StatementList', 'StatementListItem']]] 176 | ]) 177 | const initialState = { 178 | StatementList: { 179 | EOF: { 180 | $end: true 181 | } 182 | } 183 | } 184 | const expression = parse(lex('let a = 1 + 2 + 3'), map, initialState) 185 | expect(JSON.stringify(expression)).toBe(JSON.stringify([{"type":"StatementList","children":[{"type":"StatementListItem","children":[{"type":"Statement","children":[{"type":"Declaration","children":[{"type":"let","value":"let"},{"type":"Identifier","value":"a"},{"type":"=","value":"="},{"type":"Expression","children":[{"type":"AssignmentExpression","children":[{"type":"AdditiveExpression","children":[{"type":"AdditiveExpression","children":[{"type":"AdditiveExpression","children":[{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"1"}]}]}]}]}]}]},{"type":"+","value":"+"},{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"2"}]}]}]}]}]}]},{"type":"+","value":"+"},{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"3"}]}]}]}]}]}]}]}]},{"type":";","value":";"}]}]}]}]},{"type":"EOF"}])) 186 | }) 187 | 188 | it('ASI followed Enter', () => { 189 | const map = new Map([ 190 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 191 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 192 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 193 | ['LeftHandSideExpression', [['MemberExpression']]], 194 | ['MultiplicativeExpression', [['LeftHandSideExpression'], ['MultiplicativeExpression', '*', 'LeftHandSideExpression'], ['MultiplicativeExpression', '/', 'LeftHandSideExpression'], ['MultiplicativeExpression', '%', 'LeftHandSideExpression']]], 195 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 196 | ['AssignmentExpression', [['AdditiveExpression'], ['LeftHandSideExpression', '=', 'AssignmentExpression']]], 197 | ['Expression', [['AssignmentExpression']]], 198 | ['ExpressionStatement', [['Expression', ';']]], 199 | ['Declaration', [ 200 | ['var', 'Identifier', '=', 'Expression', ';'], 201 | ['let', 'Identifier', '=', 'Expression', ';'], 202 | ['const', 'Identifier', '=', 'Expression', ';'] 203 | ]], 204 | ['Statement', [['ExpressionStatement'], ['IfStatement'], ['ForStatement'], ['Declaration']]], 205 | ['StatementListItem', [['Statement'], ['Declaration']]], 206 | ['StatementList', [['StatementListItem'], ['StatementList', 'StatementListItem']]] 207 | ]) 208 | const initialState = { 209 | StatementList: { 210 | EOF: { 211 | $end: true 212 | } 213 | } 214 | } 215 | const expression = parse(lex("let a = 1 + 2 + 3\nlet b = 1"), map, initialState) 216 | expect(JSON.stringify(expression)).toBe(JSON.stringify([{"type":"StatementList","children":[{"type":"StatementList","children":[{"type":"StatementListItem","children":[{"type":"Statement","children":[{"type":"Declaration","children":[{"type":"let","value":"let"},{"type":"Identifier","value":"a"},{"type":"=","value":"="},{"type":"Expression","children":[{"type":"AssignmentExpression","children":[{"type":"AdditiveExpression","children":[{"type":"AdditiveExpression","children":[{"type":"AdditiveExpression","children":[{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"1"}]}]}]}]}]}]},{"type":"+","value":"+"},{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"2"}]}]}]}]}]}]},{"type":"+","value":"+"},{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"3"}]}]}]}]}]}]}]}]},{"type":";","value":";"}]}]}]}]},{"type":"StatementListItem","children":[{"type":"Statement","children":[{"type":"Declaration","children":[{"type":"let","value":"let"},{"type":"Identifier","value":"b"},{"type":"=","value":"="},{"type":"Expression","children":[{"type":"AssignmentExpression","children":[{"type":"AdditiveExpression","children":[{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Literal","children":[{"type":"NumbericLiteral","value":"1"}]}]}]}]}]}]}]}]},{"type":";","value":";"}]}]}]}]},{"type":"EOF"}])) 217 | }) 218 | 219 | it('New Expression / Call Expression', () => { 220 | const map = new Map([ 221 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 222 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 223 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 224 | ['NewExpression', [['MemberExpression'], ['new', 'NewExpression']]], 225 | ['CallExpression', [ 226 | ['new', 'MemberExpression', '(', ')'], 227 | ['MemberExpression', '(', ')'], 228 | ['CallExpression', '.', 'Identifier'], 229 | ['CallExpression', '[', 'Expression', ']'], 230 | ['CallExpression', '(', 'Arguments', ')'] 231 | ]], 232 | ['LeftHandSideExpression', [['MemberExpression'], ['CallExpression'], ['NewExpression']]], 233 | ['MultiplicativeExpression', [['LeftHandSideExpression'], ['MultiplicativeExpression', '*', 'LeftHandSideExpression'], ['MultiplicativeExpression', '/', 'LeftHandSideExpression'], ['MultiplicativeExpression', '%', 'LeftHandSideExpression']]], 234 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 235 | ['AssignmentExpression', [['AdditiveExpression'], ['LeftHandSideExpression', '=', 'AssignmentExpression']]], 236 | ['Expression', [['AssignmentExpression']]], 237 | ['ExpressionStatement', [['Expression', ';']]], 238 | ['Declaration', [ 239 | ['var', 'Identifier', '=', 'Expression', ';'], 240 | ['let', 'Identifier', '=', 'Expression', ';'], 241 | ['const', 'Identifier', '=', 'Expression', ';'] 242 | ]], 243 | ['Statement', [['ExpressionStatement'], ['IfStatement'], ['ForStatement'], ['Declaration']]], 244 | ['StatementListItem', [['Statement'], ['Declaration']]], 245 | ['StatementList', [['StatementListItem'], ['StatementList', 'StatementListItem']]] 246 | ]) 247 | const initialState = { 248 | StatementList: { 249 | EOF: { 250 | $end: true 251 | } 252 | } 253 | } 254 | const expression = parse(lex("new Class1()"), map, initialState) 255 | expect(JSON.stringify(expression)).toBe(JSON.stringify([{"type":"StatementList","children":[{"type":"StatementListItem","children":[{"type":"Statement","children":[{"type":"ExpressionStatement","children":[{"type":"Expression","children":[{"type":"AssignmentExpression","children":[{"type":"AdditiveExpression","children":[{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"CallExpression","children":[{"type":"new","value":"new"},{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Identifier","value":"Class1"}]}]},{"type":"(","value":"("},{"type":")","value":")"}]}]}]}]}]}]},{"type":";","value":";"}]}]}]}]},{"type":"EOF"}])) 256 | }) 257 | it('Function Declaration', () => { 258 | const map = new Map([ 259 | ['Literal', [['NumbericLiteral'], ['StringLiteral'], ['BooleanLiteral'], ['NullLiteral']]], 260 | ['Primary', [['(', 'Expression', ')'], ['Literal'], ['Identifier']]], 261 | ['MemberExpression', [['Primary'], ['MemberExpression', '.', 'Identifier'], ['MemberExpression', '[', 'Expression', ']']]], 262 | ['NewExpression', [['MemberExpression'], ['new', 'NewExpression']]], 263 | ['CallExpression', [ 264 | ['new', 'MemberExpression', '(', ')'], 265 | ['MemberExpression', '(', ')'], 266 | ['CallExpression', '.', 'Identifier'], 267 | ['CallExpression', '[', 'Expression', ']'], 268 | ['CallExpression', '(', 'Arguments', ')'] 269 | ]], 270 | ['LeftHandSideExpression', [['MemberExpression'], ['CallExpression'], ['NewExpression']]], 271 | ['MultiplicativeExpression', [['LeftHandSideExpression'], ['MultiplicativeExpression', '*', 'LeftHandSideExpression'], ['MultiplicativeExpression', '/', 'LeftHandSideExpression'], ['MultiplicativeExpression', '%', 'LeftHandSideExpression']]], 272 | ['AdditiveExpression', [['MultiplicativeExpression'], ['AdditiveExpression', '+', 'MultiplicativeExpression'], ['AdditiveExpression', '-', 'MultiplicativeExpression']]], 273 | ['AssignmentExpression', [['AdditiveExpression'], ['LeftHandSideExpression', '=', 'AssignmentExpression']]], 274 | ['Expression', [['AssignmentExpression']]], 275 | ['ExpressionStatement', [['Expression', ';']]], 276 | ['Declaration', [ 277 | ['var', 'Identifier', '=', 'Expression', ';'], 278 | ['let', 'Identifier', '=', 'Expression', ';'], 279 | ['const', 'Identifier', '=', 'Expression', ';'] 280 | ]], 281 | /** Added: Start */ 282 | ['FunctionDeclaration', [ 283 | ['function', 'Identifier', '(', ')', '{', 'StatementList', '}'], 284 | ['function', 'Identifier', '(', 'Parameters', ')', '{', 'StatementList', '}'] 285 | ]], 286 | ['Parameters', [['Identifier'], ['Parameters', ',', 'Identifier']]], 287 | /** Added: End */ 288 | ['Statement', [['ExpressionStatement'], ['IfStatement'], ['ForStatement'], ['Declaration']]], 289 | ['StatementListItem', [['Statement'], ['Declaration']]], 290 | ['StatementList', [['StatementListItem'], ['StatementList', 'StatementListItem']]], 291 | /** Added */ 292 | ['Program', [['StatementList']]] 293 | ]) 294 | const initialState = { 295 | StatementList: { 296 | EOF: { 297 | $end: true 298 | } 299 | } 300 | } 301 | const expression = parse(lex("new Class1()"), map, initialState) 302 | expect(JSON.stringify(expression)).toBe(JSON.stringify([{"type":"StatementList","children":[{"type":"StatementListItem","children":[{"type":"Statement","children":[{"type":"ExpressionStatement","children":[{"type":"Expression","children":[{"type":"AssignmentExpression","children":[{"type":"AdditiveExpression","children":[{"type":"MultiplicativeExpression","children":[{"type":"LeftHandSideExpression","children":[{"type":"CallExpression","children":[{"type":"new","value":"new"},{"type":"MemberExpression","children":[{"type":"Primary","children":[{"type":"Identifier","value":"Class1"}]}]},{"type":"(","value":"("},{"type":")","value":")"}]}]}]}]}]}]},{"type":";","value":";"}]}]}]}]},{"type":"EOF"}])) 303 | }) 304 | }) -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | YZhan JavaScript Interpreter 8 | 9 | 43 | 44 | 45 | 46 |
47 |
48 |

Input JavaScript Code

49 |
50 |
let a = 1;
 51 | let b = 1;
 52 | a <<= b++;
 53 | for (let i = 0; i < 10; i++) {
 54 |   if (i % 2 === 0) continue;
 55 |   if ((i | 2) == '11') break;
 56 |   a++;
 57 | }
 58 | undefined && 1 ?? (0 || a + b);
59 |
60 |
61 |
62 |

Lexical Analyzer

63 |
64 |
65 |
66 |

Syntax Parser

67 |
68 |
69 |
70 |

Executor

71 |
72 |
73 |
74 | 75 | 76 | 77 | 78 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /docs/static/function.js: -------------------------------------------------------------------------------- 1 | function debounce(fn, delay) { 2 | var timer = null 3 | return function () { 4 | if (timer) clearTimeout(timer) 5 | timer = setTimeout(() => { 6 | timer = null 7 | fn.apply(this, arguments) 8 | }, delay) 9 | } 10 | } 11 | 12 | function saveSelection(element) { 13 | var selection = window.getSelection() 14 | if (selection.rangeCount === 0) return null 15 | var range = selection.getRangeAt(0) 16 | var preSelectionRange = range.cloneRange() 17 | preSelectionRange.selectNodeContents(element) 18 | preSelectionRange.setEnd(range.startContainer, range.startOffset) 19 | var start = preSelectionRange.toString().length 20 | 21 | return { 22 | start: start + 2, 23 | end: start + range.toString().length 24 | } 25 | } 26 | 27 | function restoreSelection(element, savedSel) { 28 | if (savedSel === null) return 29 | var selection = window.getSelection() 30 | var charIndex = 0 31 | var range = document.createRange() 32 | range.setStart(element, 0) 33 | range.collapse(true) 34 | var nodeStack = [element] 35 | var node 36 | var foundStart = false 37 | var stop = false 38 | 39 | while (!stop && (node = nodeStack.pop())) { 40 | if (node.nodeType == 3) { 41 | var nextCharIndex = charIndex + node.length 42 | if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) { 43 | range.setStart(node, savedSel.start - charIndex) 44 | foundStart = true 45 | } 46 | if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) { 47 | range.setEnd(node, savedSel.end - charIndex) 48 | stop = true 49 | } 50 | charIndex = nextCharIndex 51 | } else { 52 | var i = node.childNodes.length 53 | while (i--) { 54 | nodeStack.push(node.childNodes[i]) 55 | } 56 | } 57 | } 58 | 59 | selection.removeAllRanges() 60 | selection.addRange(range) 61 | } -------------------------------------------------------------------------------- /docs/static/prism.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.29.0 2 | https://prismjs.com/download.html#themes=prism&languages=clike+javascript&plugins=line-numbers+toolbar+copy-to-clipboard */ 3 | code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help} 4 | pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right} 5 | div.code-toolbar{position:relative}div.code-toolbar>.toolbar{position:absolute;z-index:10;top:.3em;right:.2em;transition:opacity .3s ease-in-out;opacity:0}div.code-toolbar:hover>.toolbar{opacity:1}div.code-toolbar:focus-within>.toolbar{opacity:1}div.code-toolbar>.toolbar>.toolbar-item{display:inline-block}div.code-toolbar>.toolbar>.toolbar-item>a{cursor:pointer}div.code-toolbar>.toolbar>.toolbar-item>button{background:0 0;border:0;color:inherit;font:inherit;line-height:normal;overflow:visible;padding:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}div.code-toolbar>.toolbar>.toolbar-item>a,div.code-toolbar>.toolbar>.toolbar-item>button,div.code-toolbar>.toolbar>.toolbar-item>span{color:#bbb;font-size:.8em;padding:0 .5em;background:#f5f2f0;background:rgba(224,224,224,.2);box-shadow:0 2px 0 0 rgba(0,0,0,.2);border-radius:.5em}div.code-toolbar>.toolbar>.toolbar-item>a:focus,div.code-toolbar>.toolbar>.toolbar-item>a:hover,div.code-toolbar>.toolbar>.toolbar-item>button:focus,div.code-toolbar>.toolbar>.toolbar-item>button:hover,div.code-toolbar>.toolbar>.toolbar-item>span:focus,div.code-toolbar>.toolbar>.toolbar-item>span:hover{color:inherit;text-decoration:none} 6 | -------------------------------------------------------------------------------- /docs/static/prism.js: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.29.0 2 | https://prismjs.com/download.html#themes=prism&languages=clike+javascript&plugins=line-numbers+toolbar+copy-to-clipboard */ 3 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length>e.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index>=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S>=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail&&(jg.reach&&(g.reach=W);var z=w.prev;if(_&&(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),L>1){var I={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,I),g&&I.reach>g.reach&&(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a"+i.content+""},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); 4 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/}; 5 | Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp("(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])"),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp("((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))"),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript; 6 | !function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var e="line-numbers",n=/\n(?!$)/g,t=Prism.plugins.lineNumbers={getLine:function(n,t){if("PRE"===n.tagName&&n.classList.contains(e)){var i=n.querySelector(".line-numbers-rows");if(i){var r=parseInt(n.getAttribute("data-start"),10)||1,s=r+(i.children.length-1);ts&&(t=s);var l=t-r;return i.children[l]}}},resize:function(e){r([e])},assumeViewportIndependence:!0},i=void 0;window.addEventListener("resize",(function(){t.assumeViewportIndependence&&i===window.innerWidth||(i=window.innerWidth,r(Array.prototype.slice.call(document.querySelectorAll("pre.line-numbers"))))})),Prism.hooks.add("complete",(function(t){if(t.code){var i=t.element,s=i.parentNode;if(s&&/pre/i.test(s.nodeName)&&!i.querySelector(".line-numbers-rows")&&Prism.util.isActive(i,e)){i.classList.remove(e),s.classList.add(e);var l,o=t.code.match(n),a=o?o.length+1:1,u=new Array(a+1).join("");(l=document.createElement("span")).setAttribute("aria-hidden","true"),l.className="line-numbers-rows",l.innerHTML=u,s.hasAttribute("data-start")&&(s.style.counterReset="linenumber "+(parseInt(s.getAttribute("data-start"),10)-1)),t.element.appendChild(l),r([s]),Prism.hooks.run("line-numbers",t)}}})),Prism.hooks.add("line-numbers",(function(e){e.plugins=e.plugins||{},e.plugins.lineNumbers=!0}))}function r(e){if(0!=(e=e.filter((function(e){var n,t=(n=e,n?window.getComputedStyle?getComputedStyle(n):n.currentStyle||null:null)["white-space"];return"pre-wrap"===t||"pre-line"===t}))).length){var t=e.map((function(e){var t=e.querySelector("code"),i=e.querySelector(".line-numbers-rows");if(t&&i){var r=e.querySelector(".line-numbers-sizer"),s=t.textContent.split(n);r||((r=document.createElement("span")).className="line-numbers-sizer",t.appendChild(r)),r.innerHTML="0",r.style.display="block";var l=r.getBoundingClientRect().height;return r.innerHTML="",{element:e,lines:s,lineHeights:[],oneLinerHeight:l,sizer:r}}})).filter(Boolean);t.forEach((function(e){var n=e.sizer,t=e.lines,i=e.lineHeights,r=e.oneLinerHeight;i[t.length-1]=void 0,t.forEach((function(e,t){if(e&&e.length>1){var s=n.appendChild(document.createElement("span"));s.style.display="block",s.textContent=e}else i[t]=r}))})),t.forEach((function(e){for(var n=e.sizer,t=e.lineHeights,i=0,r=0;r0&&void 0!==arguments[0]?arguments[0]:"normal",n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.type=t,this.value=n}},543:function(e){function t(e,t){for(var n=0;n","AdditiveExpression"],["RelationalExpression","<","AdditiveExpression"]]],["EqualityExpression",[["RelationalExpression"],["EqualityExpression","==","RelationalExpression"],["EqualityExpression","!=","RelationalExpression"],["EqualityExpression","===","RelationalExpression"],["EqualityExpression","!==","RelationalExpression"]]],["BitwiseANDExpression",[["EqualityExpression"],["BitwiseANDExpression","&","EqualityExpression"]]],["BitwiseXORExpression",[["BitwiseANDExpression"],["BitwiseXORExpression","^","BitwiseANDExpression"]]],["BitwiseORExpression",[["BitwiseXORExpression"],["BitwiseORExpression","|","BitwiseXORExpression"]]],["LogicalANDExpression",[["BitwiseORExpression"],["LogicalANDExpression","&&","BitwiseORExpression"]]],["LogicalORExpression",[["LogicalANDExpression"],["LogicalORExpression","||","LogicalANDExpression"]]],["CoalesceExpression",[["ShortCircuitExpression","??","BitwiseORExpression"]]],["ShortCircuitExpression",[["LogicalORExpression"],["CoalesceExpression"]]],["ConditionalExpression",[["ShortCircuitExpression"],["ShortCircuitExpression","?","AssignmentExpression",":","AssignmentExpression"]]],["AssignmentOperator",[["*="],["/="],["%="],["+="],["-="],["<<="],[">>="],[">>>="],["&="],["^="],["|="],["**="]]],["AssignmentExpression",[["ConditionalExpression"],["LeftHandSideExpression","=","AssignmentExpression"],["LeftHandSideExpression","AssignmentOperator","AssignmentExpression"],["LeftHandSideExpression","&&=","AssignmentExpression"],["LeftHandSideExpression","||=","AssignmentExpression"],["LeftHandSideExpression","??=","AssignmentExpression"]]],["Expression",[["AssignmentExpression"],["Expression",",","AssignmentExpression"]]],["Parameters",[["Identifier"],["Parameters",",","Identifier"]]],["FunctionDeclaration",[["function","Identifier","(",")","BlockStatement"],["function","Identifier","(","Parameters",")","BlockStatement"]]],["Declaration",[["FunctionDeclaration"],["var","Identifier","=","Expression",";"],["let","Identifier","=","Expression",";"],["const","Identifier","=","Expression",";"]]],["ExpressionStatement",[["Expression",";"]]],["BlockStatement",[["{","}"],["{","StatementList","}"]]],["IfStatement",[["if","(","Expression",")","Statement"],["if","(","Expression",")","else","Statement"]]],["ForStatement",[["for","(","let","Expression",";","Expression",";","Expression",")","Statement"],["for","(","var","Expression",";","Expression",";","Expression",")","Statement"],["for","(","Expression",";","Expression",";","Expression",")","Statement"]]],["BreakableStatement",[["ForStatement"]]],["BreakStatement",[["break",";"],["break","Identifier",";"]]],["ContinueStatement",[["continue",";"],["continue","Identifier",";"]]],["ReturnStatement",[["return",";"],["return","Expression",";"]]],["Statement",[["BlockStatement"],["ExpressionStatement"],["IfStatement"],["ForStatement"],["BreakableStatement"],["BreakStatement"],["ContinueStatement"],["ReturnStatement"],["Declaration"]]],["StatementListItem",[["Statement"],["Declaration"]]],["StatementList",[["StatementListItem"],["StatementList","StatementListItem"]]],["Program",[["StatementList"]]]]);t.regStr="\n(?(?:0[xX][0-9a-fA-F]*|\\.[0-9]+|(?:[1-9]+[0-9]*|0)(?:\\.[0-9]*|\\.)?)(?:[eE][+-]{0,1}[0-9]+)?(?![_$a-zA-Z0-9]))|\n(?null(?![_$a-zA-Z0-9]))|\n(?(?:true|false)(?![_$a-zA-Z0-9]))|\n(?\"(?:[^\"\\n\\\\\\r\\u2028\\u2029]|\\\\(?:['\"\\\\bfnrtv\\n\\r\\u2028\\u2029]|\\r\\n)|\\\\x[0-9a-fA-F]{2}|\\\\u[0-9a-fA-F]{4}|\\\\[^0-9ux'\"\\\\bfnrtv\\n\\\\\\r\\u2028\\u2029])*\"|'(?:[^'\\n\\\\\\r\\u2028\\u2029]|\\\\(?:['\"\\\\bfnrtv\\n\\r\\u2028\\u2029]|\\r\\n)|\\\\x[0-9a-fA-F]{2}|\\\\u[0-9a-fA-F]{4}|\\\\[^0-9ux'\"\\\\bfnrtv\\n\\\\\\r\\u2028\\u2029])*')|\n(?>>>=|>>=|<<=|===|!==|>>>|<<|%=|\\*=|-=|\\+=|<=|>=|==|!=|/=|\\^=|\\|=|&&=|\\|\\|=|\\?\\?=|\\|\\||&&|\\?\\?|&=|>>|\\+\\+|--|\\:|}|\\*|&|\\||\\^|!|~|-|\\+|\\?|%|=|>|<|,|;|\\.(?![0-9])|\\]|\\[|\\)|\\(|{)|\n(?break(?![_$a-zA-Z0-9])|else(?![_$a-zA-Z0-9])|new(?![_$a-zA-Z0-9])|var(?![_$a-zA-Z0-9])|let(?![_$a-zA-Z0-9])|const(?![_$a-zA-Z0-9])|case(?![_$a-zA-Z0-9])|finally(?![_$a-zA-Z0-9])|return(?![_$a-zA-Z0-9])|void(?![_$a-zA-Z0-9])|catch(?![_$a-zA-Z0-9])|for(?![_$a-zA-Z0-9])|switch(?![_$a-zA-Z0-9])|while(?![_$a-zA-Z0-9])|continue(?![_$a-zA-Z0-9])|function(?![_$a-zA-Z0-9])|this(?![_$a-zA-Z0-9])|with(?![_$a-zA-Z0-9])|default(?![_$a-zA-Z0-9])|if(?![_$a-zA-Z0-9])|throw(?![_$a-zA-Z0-9])|delete(?![_$a-zA-Z0-9])|in(?![_$a-zA-Z0-9])|try(?![_$a-zA-Z0-9])|do(?![_$a-zA-Z0-9])|instanceof(?![_$a-zA-Z0-9])|typeof(?![_$a-zA-Z0-9]))|\n(?(?:\\n))|\n(?[_&A-Za-z][_&A-Za-z0-9\\\\u200C\\\\u200D]{0,})\n",t.rulesMap=n,t.initialState={Program:{EOF:{$end:!0}}}},214:function(e,t,n){function r(e,t,n,r,i,o,s){try{var u=e[o](s),c=u.value}catch(e){return void n(e)}u.done?t(c):Promise.resolve(c).then(r,i)}function i(e,t){return null!=t&&"undefined"!=typeof Symbol&&t[Symbol.hasInstance]?!!t[Symbol.hasInstance](e):e instanceof t}var o=n(253),s=n(543),u=n(449),c=n(285),a=n(247),l=n(286),f=new s,h={envStack:[f],microTaskQueue:[],runTaskQueue:[],runTask:function(){return(e=function(){return function(e,t){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function u(o){return function(u){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!((i=(i=s.trys).length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0;)this.microTaskQueue.shift().run();return[2]}))},function(){var t=this,n=arguments;return new Promise((function(i,o){var s=e.apply(t,n);function u(e){r(s,i,o,u,c,"next",e)}function c(e){r(s,i,o,u,c,"throw",e)}u(void 0)}))}).apply(this);var e},execute:function(e){return"function"!=typeof this[e.type]&&console.log("ast.type",e.type,this[e.type]),this[e.type](e)},getValue:function(e){var t=this.execute(e);return i(t,o)?t.get():t},preProcesser:{},get currentEnv(){return this.envStack[this.envStack.length-1]},Identifier:function(e){return new o(this.currentEnv,e.value)},Parameters:function(e){if(1===e.children.length)return[e.children[0].value];var t,n=null!==(t=this.execute(e.children[0]))&&void 0!==t?t:[],r=e.children[2].value;return n.concat(r)},FunctionDeclaration:function(e){if(5===e.children.length){var t=e.children[1].value,n=e.children[4],r=new a(n,this,this.currentEnv);this.currentEnv.declare(t),this.currentEnv.set(t,r)}else if(6===e.children.length){var i=e.children[1].value,o=this.execute(e.children[3]),s=e.children[5],c=new a(s,this,this.currentEnv,o);this.currentEnv.declare(i),this.currentEnv.set(i,c)}return new u("normal")},Declaration:function(e){if(1===e.children.length)return this.execute(e.children[0]);this.currentEnv.declare(e.children[1].value),this.currentEnv.set(e.children[1].value,void 0),this.execute(e.children[1]).set(this.execute(e.children[3]))},NumbericLiteral:function(e){return 1*e.value},StringLiteral:function(e){return e.value.slice(1,-1)},BooleanLiteral:function(e){return"true"===e.value},NullLiteral:function(){return null},ObjectLiteral:function(e){var t=this,n=new c;return 3!==e.children.length&&4!==e.children.length||e.children[1].children.forEach((function(e){var r=e.children,o=t.execute(r[0]),s=t.execute(r[2]),u="object"!=typeof s||i(s,c)?{value:s}:s;n.setProperty(o,u)})),n},Literal:function(e){return this.execute(e.children[0])},Primary:function(e){return 1===e.children.length?this.execute(e.children[0]):this.execute(e.children[1])},Arguments:function(e){return 2===e.children.length?[]:3===e.children.length||4===e.children.length?this.execute(e.children[1]):void 0},ArgumentList:function(e){if(1===e.children.length)return[this.getValue(e.children[0])];var t,n=null!==(t=this.execute(e.children[0]))&&void 0!==t?t:[],r=this.getValue(e.children[2].value);return n.concat(r)},MemberExpression:function(e){if(1===e.children.length)return this.execute(e.children[0]);if(3===e.children.length){if("new"===e.children[0].value){var t=this.getValue(e.children[1]),n=this.getValue(e.children[2]);return t.construct(this.currentEnv,n[0])}return this.getValue(e.children[0]).getProperty(e.children[2].value)}return 4===e.children.length?new o(this.execute(e.children[0]),e.children[2]):void 0},NewExpression:function(e){return 1===e.children.length?this.execute(e.children[0]):this.execute(e.children[1])},CallExpression:function(e){if(1===e.children.length)return this.execute(e.children[0]);if(2===e.children.length){var t=this.execute(e.children[0]),n=this.execute(e.children[1]);return t.call(this.currentEnv,n)}},CoverCallExpressionAndAsyncArrowHead:function(e){if(2===e.children.length){var t=this.getValue(e.children[0]),n=this.execute(e.children[1]);return t.call(this.currentEnv,n)}},LeftHandSideExpression:function(e){return this.execute(e.children[0])},UpdateExpression:function(e){if(1===e.children.length)return this.execute(e.children[0]);var t=void 0;if("++"===e.children[0].type){var n=this.execute(e.children[1]);n.set(t=n.get()+1)}else if("--"===e.children[0].type){var r=this.execute(e.children[1]);r.set(t=r.get()-1)}else if("++"===e.children[1].type){var i=this.execute(e.children[0]);i.set((t=i.get())+1)}else if("--"===e.children[1].type){var o=this.execute(e.children[0]);o.set((t=o.get())-1)}return t},MultiplicativeExpression:function(e){if(1===e.children.length)return this.execute(e.children[0]);var t=this.getValue(e.children[0]),n=this.getValue(e.children[2]);return"*"===e.children[1].type?t*n:"/"===e.children[1].type?t/n:t%n},AdditiveExpression:function(e){if(1===e.children.length)return this.execute(e.children[0]);var t=this.getValue(e.children[0]),n=this.getValue(e.children[2]);return"+"===e.children[1].type?t+n:t-n},RelationalExpression:function(e){if(1===e.children.length)return this.execute(e.children[0]);var t=this.getValue(e.children[0]),n=this.getValue(e.children[2]);return">"===e.children[1].type?t>n:t>="===i?t.set(r=t.get()>>n):">>>="===i?t.set(r=t.get()>>>n):"&="===i?t.set(r=t.get()&n):"^="===i?t.set(r=t.get()^n):"|="===i?t.set(r=t.get()|n):"**="===i&&t.set(r=Math.pow(t.get(),n))}else if("&&="===e.children[1].type)t.set(r=t.get()&&n);else if("||="===e.children[1].type)t.set(r=t.get()||n);else if("??="===e.children[1].type){var o;t.set(r=null!==(o=t.get())&&void 0!==o?o:n)}return r},Expression:function(e){return 1===e.children.length?this.execute(e.children[0]):(this.execute(e.children[0]),this.execute(e.children[2]))},ExpressionStatement:function(e){return new u("normal",this.execute(e.children[0]))},BlockStatement:function(e){if(3===e.children.length){this.envStack.push(new s(this.currentEnv));var t=this.execute(e.children[1]);return this.envStack.pop(),t}return new u("normal")},IfStatement:function(e){var t=this.execute(e.children[2]);return 5===e.children.length?t?this.execute(e.children[4]):new u("normal"):t?this.execute(e.children[4]):this.execute(e.children[6])},ForStatement:function(e){var t=2;10===e.children.length&&t++;var n=e.children[t],r=e.children[t+2],i=e.children[t+4],o=e.children[t+6];for(this.execute(n);this.execute(r);){var s=this.execute(o);if("break"===s.type||"return"===s.type)return new u("normal",s.value);this.execute(i)}},BreakableStatement:function(e){if(1===e.children.length)return this.execute(e.children[0])},BreakStatement:function(e){return new u("break")},ContinueStatement:function(e){return new u("continue")},ReturnStatement:function(e){if(2===e.children.length)return new u("return");if(3===e.children.length){var t=this.getValue(e.children[1]);return new u("return",t)}},Statement:function(e){return this.execute(e.children[0])},StatementListItem:function(e){var t,n=this.execute(e.children[0]);return(null===(t=n)||void 0===t?void 0:t.type)?n:new u("normal",n)},StatementList:function(e){if(1===e.children.length)return this.execute(e.children[0]);var t=this.execute(e.children[0]);return"normal"===t.type?this.execute(e.children[1]):t},Program:function(e){return this.execute(e.children[0])}};f.set("Promise",new l(h)),e.exports={executor:h,globalEnv:f}},638:function(e,t,n){var i=n(219).regStr;e.exports=function(e){for(var t=new RegExp(i.replace(/\s+/g,""),"g"),n=[];r=t.exec(e);){var o=Object.keys(r.groups),s=!0,u=!1,c=void 0;try{for(var a,l=o[Symbol.iterator]();!(s=(a=l.next()).done);s=!0){var f=a.value;void 0!==r.groups[f]&&n.push({type:"Punctuator"===f||"Keywords"===f?r.groups[f]:f,value:r.groups[f]})}}catch(e){u=!0,c=e}finally{try{s||null==l.return||l.return()}finally{if(u)throw c}}}return n.push({type:"EOF"}),n}},924:function(e,t,n){var r=n(219),i=r.rulesMap,o=r.initialState;function s(e,t,n){var r=new Set;return JSON.stringify(e,(function(e,n){if("object"==typeof n&&null!==n){if(r.has(n))return"[Circular]";r.add(n)}return t?t(e,n):n}),n)}var u=function(e,t){for(var n=function(){var e=i.shift();return!1===t.has(e)||!0===o.has(e)?"continue":(t.get(e).forEach((function(t){r.push({ruleBody:t,$reduce:e}),i.push(t[0])})),void o.add(e))},r=[],i=[e],o=new Set;0!==i.length;)n();return r},c=new Map,a=function(e,t){c.set(s(e),e);var n=!0,r=!1,i=void 0;try{for(var o,l=Object.keys(e)[Symbol.iterator]();!(n=(o=l.next()).done);n=!0){var f=o.value;f.startsWith("$")||u(f,t).forEach((function(t){var n=t.ruleBody,r=t.$reduce,i=e;n.forEach((function(e){void 0===i[e]&&(i[e]={}),i=i[e]})),i.$reduce=r,i.$count=n.length}))}}catch(e){r=!0,i=e}finally{try{n||null==l.return||l.return()}finally{if(r)throw i}}var h=!0,p=!1,d=void 0;try{for(var v,y=Object.keys(e)[Symbol.iterator]();!(h=(v=y.next()).done);h=!0){var x=v.value;if(!x.startsWith("$")){var g=s(e[x]);c.has(g)?e[x]=c.get(g):a(e[x],t)}}}catch(e){p=!0,d=e}finally{try{h||null==y.return||y.return()}finally{if(p)throw d}}return e};e.exports=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:i,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:o;c.clear(),a(n,t);for(var r=[],s=[n],u=e.length,l=function(e){for(;void 0===s[s.length-1][e.type]&&s[s.length-1].$reduce;)f();if(void 0===s[s.length-1][e.type]){if("EOF"===e.type||"}"===e.type||h)return l({type:";",value:";"}),l(e);throw Error("syntax error type "+e.type)}r.push(e),s.push(s[s.length-1][e.type])},f=function(){for(var e=s[s.length-1],t={type:e.$reduce,children:[]},n=0;n { 16 | newEnv.set(parameter, args[i]) 17 | }) 18 | executor.envStack.push(newEnv) 19 | const res = executor[functionBody.type](functionBody) 20 | executor.envStack.pop() 21 | return res 22 | } 23 | 24 | construct(currentEnv, args) { 25 | const obj = new JSObject() 26 | const res = this.call(currentEnv, args) 27 | if(res instanceof JSObject) return res 28 | return obj 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/classes/JSObject.js: -------------------------------------------------------------------------------- 1 | module.exports = class { 2 | constructor() { 3 | this.properties = new Map() 4 | this.prototype = null 5 | } 6 | #get(property) { 7 | if (property.value !== void 0) return property.value 8 | if (typeof property.get === 'function') return property.get() 9 | return property 10 | } 11 | getProperty(key) { 12 | let cur = this 13 | while (cur !== null) { 14 | const property = cur.properties.get(key) 15 | if (property !== void 0) return this.#get(property) 16 | cur = cur.prototype 17 | } 18 | return void 0 19 | } 20 | setProperty(key, { value, get, set, writable = true, enumerable = true, configurable = true }) { 21 | this.properties.set(key, { 22 | value, 23 | get, 24 | set, 25 | writable, 26 | enumerable, 27 | configurable 28 | }) 29 | } 30 | } -------------------------------------------------------------------------------- /src/classes/Promise/PromiseFunction.js: -------------------------------------------------------------------------------- 1 | const JSObject = require('../JSObject.js') 2 | const JSFunction = require('../JSFunction.js') 3 | const ResolveFunction = require('./ResolveFunction.js') 4 | const RejectFunction = require('./RejectFunction.js') 5 | const ThenFunction = require('./ThenFunction.js') 6 | module.exports = class PromiseFunction extends JSFunction { 7 | constructor(executor) { 8 | super(null, executor) 9 | } 10 | call () { 11 | throw Error('Uncaught TypeError: Promise constructor cannot be invoked without \'new\'') 12 | } 13 | construct(currentEnv, func) { 14 | const promiseInstance = new JSObject() 15 | promiseInstance.setProperty('state', { value: 'pending' }) 16 | const resolve = new ResolveFunction(this.executor, promiseInstance) 17 | const reject = new RejectFunction(this.executor, promiseInstance) 18 | try { 19 | func.call(currentEnv, [resolve, reject]) 20 | } catch(error) { 21 | reject.call(currentEnv, error) 22 | } 23 | const then = new ThenFunction(this.executor, promiseInstance, resolve, reject) 24 | promiseInstance.setProperty('then', { value: then }) 25 | return promiseInstance 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/classes/Promise/RejectFunction.js: -------------------------------------------------------------------------------- 1 | const JSFunction = require('../JSFunction.js') 2 | const Task = require('../Task.js') 3 | module.exports = class extends JSFunction { 4 | constructor(executor, promiseInstance) { 5 | super(null, executor) 6 | this.promiseInstance = promiseInstance 7 | this.then = null // will pass from (then instance).call 8 | } 9 | call(currentEnv, args) { 10 | const rejectReason = args[0] 11 | const { then, executor, promiseInstance } = this 12 | if (then !== null) executor.microTaskQueue.push(new Task(currentEnv, then, [rejectReason])) 13 | promiseInstance.setProperty('state', { value: 'rejected' }) 14 | promiseInstance.setProperty('rejectReason', { value: rejectReason }) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/classes/Promise/ResolveFunction.js: -------------------------------------------------------------------------------- 1 | const JSFunction = require('../JSFunction.js') 2 | const Task = require('../Task.js') 3 | module.exports = class extends JSFunction { 4 | constructor(executor, promiseInstance) { 5 | super(null, executor) 6 | this.promiseInstance = promiseInstance 7 | this.then = null // will pass from (then instance).call 8 | } 9 | call(currentEnv, args) { 10 | const resolvedValue = args[0] 11 | const { then, executor, promiseInstance } = this 12 | if (then !== null) executor.microTaskQueue.push(new Task(currentEnv, then, [resolvedValue])) 13 | promiseInstance.setProperty('state', { value: 'fulfilled' }) 14 | promiseInstance.setProperty('resolvedValue', { value: resolvedValue }) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/classes/Promise/ThenFunction.js: -------------------------------------------------------------------------------- 1 | const JSFunction = require('../JSFunction.js') 2 | const Task = require('../Task.js') 3 | module.exports = class extends JSFunction { 4 | constructor(executor, promiseInstance, resolve, reject) { 5 | super(null, executor) 6 | this.promiseInstance = promiseInstance 7 | this.resolve = resolve 8 | this.reject = reject 9 | } 10 | call(currentEnv, args) { 11 | const callback = args[0] 12 | const { executor, promiseInstance, resolve, reject } = this 13 | if (promiseInstance.getProperty('state') === 'fulfilled') { 14 | const resolvedValue = promiseInstance.getProperty('resolvedValue') 15 | executor.microTaskQueue.push(new Task(currentEnv, callback, [resolvedValue])) 16 | } else if (promiseInstance.getProperty('state') === 'rejected') { 17 | const rejectReason = promiseInstance.getProperty('rejectReason') 18 | executor.microTaskQueue.push(new Task(currentEnv, callback, [rejectReason])) 19 | } else { 20 | resolve.then = callback 21 | reject.then = callback 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/classes/Reference.js: -------------------------------------------------------------------------------- 1 | module.exports = class { 2 | constructor (object, property) { 3 | this.object = object 4 | this.property = property 5 | } 6 | set(val) { 7 | this.object.set(this.property, val) 8 | } 9 | get() { 10 | return this.object.get(this.property) 11 | } 12 | } -------------------------------------------------------------------------------- /src/classes/Task.js: -------------------------------------------------------------------------------- 1 | module.exports = class { 2 | constructor(env, func, args) { 3 | this.env = env 4 | this.func = func 5 | this.args = args 6 | } 7 | run() { 8 | this.func.call(this.env, this.args) 9 | } 10 | } -------------------------------------------------------------------------------- /src/data/conf.js: -------------------------------------------------------------------------------- 1 | const regStr = ` 2 | (?(?:0[xX][0-9a-fA-F]*|\\.[0-9]+|(?:[1-9]+[0-9]*|0)(?:\\.[0-9]*|\\.)?)(?:[eE][+-]{0,1}[0-9]+)?(?![_$a-zA-Z0-9]))| 3 | (?null(?![_$a-zA-Z0-9]))| 4 | (?(?:true|false)(?![_$a-zA-Z0-9]))| 5 | (?"(?:[^"\\n\\\\\\r\\u2028\\u2029]|\\\\(?:['"\\\\bfnrtv\\n\\r\\u2028\\u2029]|\\r\\n)|\\\\x[0-9a-fA-F]{2}|\\\\u[0-9a-fA-F]{4}|\\\\[^0-9ux'"\\\\bfnrtv\\n\\\\\\r\\u2028\\u2029])*"|'(?:[^'\\n\\\\\\r\\u2028\\u2029]|\\\\(?:['"\\\\bfnrtv\\n\\r\\u2028\\u2029]|\\r\\n)|\\\\x[0-9a-fA-F]{2}|\\\\u[0-9a-fA-F]{4}|\\\\[^0-9ux'"\\\\bfnrtv\\n\\\\\\r\\u2028\\u2029])*')| 6 | (?>>>=|>>=|<<=|===|!==|>>>|<<|%=|\\*=|-=|\\+=|<=|>=|==|!=|/=|\\^=|\\|=|&&=|\\|\\|=|\\?\\?=|\\|\\||&&|\\?\\?|&=|>>|\\+\\+|--|\\:|}|\\*|&|\\||\\^|!|~|-|\\+|\\?|%|=|>|<|,|;|\\.(?![0-9])|\\]|\\[|\\)|\\(|{)| 7 | (?break(?![_$a-zA-Z0-9])|else(?![_$a-zA-Z0-9])|new(?![_$a-zA-Z0-9])|var(?![_$a-zA-Z0-9])|let(?![_$a-zA-Z0-9])|const(?![_$a-zA-Z0-9])|case(?![_$a-zA-Z0-9])|finally(?![_$a-zA-Z0-9])|return(?![_$a-zA-Z0-9])|void(?![_$a-zA-Z0-9])|catch(?![_$a-zA-Z0-9])|for(?![_$a-zA-Z0-9])|switch(?![_$a-zA-Z0-9])|while(?![_$a-zA-Z0-9])|continue(?![_$a-zA-Z0-9])|function(?![_$a-zA-Z0-9])|this(?![_$a-zA-Z0-9])|with(?![_$a-zA-Z0-9])|default(?![_$a-zA-Z0-9])|if(?![_$a-zA-Z0-9])|throw(?![_$a-zA-Z0-9])|delete(?![_$a-zA-Z0-9])|in(?![_$a-zA-Z0-9])|try(?![_$a-zA-Z0-9])|do(?![_$a-zA-Z0-9])|instanceof(?![_$a-zA-Z0-9])|typeof(?![_$a-zA-Z0-9]))| 8 | (?(?:\\n))| 9 | (?[_&A-Za-z][_&A-Za-z0-9\\\\u200C\\\\u200D]{0,}) 10 | ` 11 | 12 | const rulesMap = new Map([ 13 | ['PropertyDefinition', [ 14 | ['StringLiteral', ':', 'Expression'], 15 | ['NumbericLiteral', ':', 'Expression'] 16 | ]], 17 | ['PropertyDefinitionList', [ 18 | ['PropertyDefinition'], 19 | ['PropertyDefinitionList', ':', 'PropertyDefinition'] 20 | ]], 21 | ['ObjectLiteral', [ 22 | ['{', '}'], 23 | ['{', 'PropertyDefinitionList', '}'], 24 | ['{', 'PropertyDefinitionList', ',', '}'], 25 | ]], 26 | ['Literal', [ 27 | ['NumbericLiteral'], 28 | ['StringLiteral'], 29 | ['BooleanLiteral'], 30 | ['NullLiteral'], 31 | ['ObjectLiteral'], 32 | ]], 33 | ['Primary', [ 34 | ['(', 'Expression', ')'], 35 | ['Literal'], 36 | ['Identifier'] 37 | ]], 38 | ['Arguments', [ 39 | ['(', ')'], 40 | ['(', 'ArgumentList', ')'], 41 | ['(', 'ArgumentList', ',', ')'] 42 | ]], 43 | ['ArgumentList', [ 44 | ['AssignmentExpression'], 45 | ['ArgumentList', ',', 'AssignmentExpression'] 46 | ]], 47 | ['MemberExpression', [ 48 | ['Primary'], 49 | ['MemberExpression', '.', 'Identifier'], 50 | ['MemberExpression', '[', 'Expression', ']'], 51 | ['new', 'MemberExpression', 'Arguments'] 52 | ]], 53 | ['NewExpression', [ 54 | ['MemberExpression'], 55 | ['new', 'NewExpression'] 56 | ]], 57 | ['CallExpression', [ 58 | ['CoverCallExpressionAndAsyncArrowHead'], 59 | ['CallExpression', 'Arguments'], 60 | ['CallExpression', '[', 'Expression', ']'], 61 | ['CallExpression', '.', 'Identifier'], 62 | ]], 63 | ['CoverCallExpressionAndAsyncArrowHead', [ 64 | ['MemberExpression', 'Arguments'] 65 | ]], 66 | ['LeftHandSideExpression', [ 67 | ['MemberExpression'], 68 | ['CallExpression'], 69 | ['NewExpression'] 70 | ]], 71 | ['UpdateExpression', [ 72 | ['LeftHandSideExpression'], 73 | ['LeftHandSideExpression', '++'], 74 | ['LeftHandSideExpression', '--'], 75 | ['++', 'LeftHandSideExpression'], 76 | ['--', 'LeftHandSideExpression'] 77 | ]], 78 | ['MultiplicativeExpression', [ 79 | ['UpdateExpression'], 80 | ['MultiplicativeExpression', '*', 'UpdateExpression'], 81 | ['MultiplicativeExpression', '/', 'UpdateExpression'], 82 | ['MultiplicativeExpression', '%', 'UpdateExpression'] 83 | ]], 84 | ['AdditiveExpression', [ 85 | ['MultiplicativeExpression'], 86 | ['AdditiveExpression', '+', 'MultiplicativeExpression'], 87 | ['AdditiveExpression', '-', 'MultiplicativeExpression'] 88 | ]], 89 | ['RelationalExpression', [ 90 | ['AdditiveExpression'], 91 | ['RelationalExpression', '>', 'AdditiveExpression'], 92 | ['RelationalExpression', '<', 'AdditiveExpression'] 93 | ]], 94 | ['EqualityExpression', [ 95 | ['RelationalExpression'], 96 | ['EqualityExpression', '==', 'RelationalExpression'], 97 | ['EqualityExpression', '!=', 'RelationalExpression'], 98 | ['EqualityExpression', '===', 'RelationalExpression'], 99 | ['EqualityExpression', '!==', 'RelationalExpression'], 100 | ]], 101 | ['BitwiseANDExpression', [ 102 | ['EqualityExpression'], 103 | ['BitwiseANDExpression', '&', 'EqualityExpression'] 104 | ]], 105 | ['BitwiseXORExpression', [ 106 | ['BitwiseANDExpression'], 107 | ['BitwiseXORExpression', '^', 'BitwiseANDExpression'] 108 | ]], 109 | ['BitwiseORExpression', [ 110 | ['BitwiseXORExpression'], 111 | ['BitwiseORExpression', '|', 'BitwiseXORExpression'] 112 | ]], 113 | ['LogicalANDExpression', [ 114 | ['BitwiseORExpression'], 115 | ['LogicalANDExpression', '&&', 'BitwiseORExpression'] 116 | ]], 117 | ['LogicalORExpression', [ 118 | ['LogicalANDExpression'], 119 | ['LogicalORExpression', '||', 'LogicalANDExpression'] 120 | ]], 121 | ['CoalesceExpression', [ 122 | ['ShortCircuitExpression', '??', 'BitwiseORExpression'] 123 | ]], 124 | // ['CoalesceExpressionHead', [ 125 | // ['CoalesceExpression'], 126 | // ['BitwiseORExpression'] 127 | // ]], 128 | ['ShortCircuitExpression', [ 129 | ['LogicalORExpression'], 130 | ['CoalesceExpression'] 131 | ]], 132 | ['ConditionalExpression', [ 133 | ['ShortCircuitExpression'], 134 | ['ShortCircuitExpression', '?', 'AssignmentExpression', ':', 'AssignmentExpression'] 135 | ]], 136 | ['AssignmentOperator', [ 137 | ['*='],['/='], ['%='], ['+='], ['-='], 138 | ['<<='], ['>>='], ['>>>='], 139 | ['&='], ['^='], ['|='], ['**='] 140 | ]], 141 | ['AssignmentExpression', [ 142 | ['ConditionalExpression'], 143 | ['LeftHandSideExpression', '=', 'AssignmentExpression'], 144 | ['LeftHandSideExpression', 'AssignmentOperator', 'AssignmentExpression'], 145 | ['LeftHandSideExpression', '&&=', 'AssignmentExpression'], 146 | ['LeftHandSideExpression', '||=', 'AssignmentExpression'], 147 | ['LeftHandSideExpression', '??=', 'AssignmentExpression'], 148 | ]], 149 | ['Expression', [ 150 | ['AssignmentExpression'], 151 | ['Expression', ',', 'AssignmentExpression'], 152 | ]], 153 | ['Parameters', [ 154 | ['Identifier'], 155 | ['Parameters', ',', 'Identifier'] 156 | ]], 157 | ['FunctionDeclaration', [ 158 | ['function', 'Identifier', '(', ')', 'BlockStatement'], 159 | ['function', 'Identifier', '(', 'Parameters', ')', 'BlockStatement'] 160 | ]], 161 | ['Declaration', [ 162 | ['FunctionDeclaration'], 163 | ['var', 'Identifier', '=', 'Expression', ';'], 164 | ['let', 'Identifier', '=', 'Expression', ';'], 165 | ['const', 'Identifier', '=', 'Expression', ';'] 166 | ]], 167 | ['ExpressionStatement', [ 168 | ['Expression', ';'] 169 | ]], 170 | ['BlockStatement', [ 171 | ['{', '}'], 172 | ['{', 'StatementList', '}'], 173 | ]], 174 | ['IfStatement', [ 175 | ['if', '(', 'Expression', ')', 'Statement'], 176 | ['if', '(', 'Expression', ')', 'else', 'Statement'] 177 | ]], 178 | ['ForStatement', [ 179 | ['for', '(', 'let', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 180 | ['for', '(', 'var', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'], 181 | ['for', '(', 'Expression', ';', 'Expression', ';', 'Expression', ')', 'Statement'] 182 | ]], 183 | ['BreakableStatement', [ 184 | ['ForStatement'] 185 | ]], 186 | ['BreakStatement', [ 187 | ['break', ';'], 188 | ['break', 'Identifier', ';'] 189 | ]], 190 | ['ContinueStatement', [ 191 | ['continue', ';'], 192 | ['continue', 'Identifier', ';'] 193 | ]], 194 | ['ReturnStatement', [ 195 | ['return', ';'], 196 | ['return', 'Expression', ';'], 197 | ]], 198 | ['Statement', [ 199 | ['BlockStatement'], 200 | ['ExpressionStatement'], 201 | ['IfStatement'], 202 | ['ForStatement'], 203 | ['BreakableStatement'], 204 | ['BreakStatement'], 205 | ['ContinueStatement'], 206 | ['ReturnStatement'], 207 | ['Declaration'], 208 | ]], 209 | ['StatementListItem', [ 210 | ['Statement'], 211 | ['Declaration'] 212 | ]], 213 | ['StatementList', [ 214 | ['StatementListItem'], 215 | ['StatementList', 'StatementListItem'] 216 | ]], 217 | ['Program', [['StatementList']]], 218 | ]) 219 | 220 | const initialState = { 221 | Program: { 222 | EOF: { 223 | $end: true 224 | } 225 | } 226 | } 227 | 228 | exports.regStr = regStr 229 | exports.rulesMap = rulesMap 230 | exports.initialState = initialState -------------------------------------------------------------------------------- /src/helpers/executor.js: -------------------------------------------------------------------------------- 1 | const Reference = require('../classes/Reference') 2 | const Enviroment = require('../classes/Environment') 3 | const Completion = require('../classes/Completion') 4 | const JSObject = require('../classes/JSObject') 5 | const JSFunction = require('../classes/JSFunction') 6 | const PromiseFunction = require('../classes/Promise/PromiseFunction') 7 | 8 | const globalEnv = new Enviroment() 9 | 10 | const executor = { 11 | envStack: [globalEnv], 12 | microTaskQueue: [], 13 | runTaskQueue: [], 14 | async runTask() { // drain 15 | while (this.microTaskQueue.length > 0) { 16 | const microTask = this.microTaskQueue.shift() 17 | microTask.run() 18 | } 19 | }, 20 | execute(ast) { 21 | if (typeof this[ast.type] !== 'function') console.log('ast.type', ast.type, this[ast.type]) 22 | return this[ast.type](ast) 23 | }, 24 | getValue(node) { 25 | const value = this.execute(node) 26 | if (value instanceof Reference) return value.get() 27 | return value 28 | }, 29 | preProcesser: {}, // Preparse: Find VariableDeclaration FuncitonDeclaration 30 | get currentEnv(){ 31 | return this.envStack[this.envStack.length - 1] 32 | }, 33 | Identifier(node) { 34 | return new Reference(this.currentEnv, node.value) 35 | }, 36 | Parameters(node) { 37 | if (node.children.length === 1) { 38 | return [node.children[0].value] 39 | } else { 40 | const left = this.execute(node.children[0]) ?? [] 41 | const right = node.children[2].value 42 | return left.concat(right) 43 | } 44 | }, 45 | FunctionDeclaration(node) { 46 | if (node.children.length === 5) { 47 | const functionName = node.children[1].value 48 | const functionBody = node.children[4] 49 | const jsFunction = new JSFunction(functionBody, this, this.currentEnv) 50 | this.currentEnv.declare(functionName) 51 | this.currentEnv.set(functionName, jsFunction) 52 | } else if (node.children.length === 6) { 53 | const functionName = node.children[1].value 54 | const parameters = this.execute(node.children[3]) 55 | const functionBody = node.children[5] 56 | const jsFunction = new JSFunction(functionBody, this, this.currentEnv, parameters) 57 | this.currentEnv.declare(functionName) 58 | this.currentEnv.set(functionName, jsFunction) 59 | } 60 | return new Completion('normal') 61 | }, 62 | Declaration(node) { 63 | if (node.children.length === 1) return this.execute(node.children[0]) 64 | this.currentEnv.declare(node.children[1].value) 65 | this.currentEnv.set(node.children[1].value, void 0) 66 | const ref = this.execute(node.children[1]) 67 | ref.set(this.execute(node.children[3])) 68 | }, 69 | NumbericLiteral(node) { 70 | return node.value * 1 71 | }, 72 | StringLiteral(node) { 73 | return node.value.slice(1, -1) 74 | }, 75 | BooleanLiteral(node) { 76 | return node.value === 'true' 77 | }, 78 | NullLiteral() { 79 | return null 80 | }, 81 | ObjectLiteral(node) { 82 | const jsObject = new JSObject() 83 | if (node.children.length === 3 || node.children.length === 4) { 84 | const propertyDefinitionList = node.children[1].children 85 | propertyDefinitionList.forEach(({ children: propertyDefinition }) => { 86 | const propertyName = this.execute(propertyDefinition[0]) 87 | const res = this.execute(propertyDefinition[2]) 88 | const propertyValue = typeof res !== 'object' || res instanceof JSObject ? { 89 | value: res 90 | } : res 91 | jsObject.setProperty(propertyName, propertyValue) 92 | }) 93 | } 94 | return jsObject 95 | }, 96 | Literal(node) { 97 | return this.execute(node.children[0]) 98 | }, 99 | Primary(node) { 100 | if (node.children.length === 1) { 101 | return this.execute(node.children[0]) 102 | } else { 103 | return this.execute(node.children[1]) 104 | } 105 | }, 106 | Arguments(node) { 107 | if (node.children.length === 2) return [] 108 | if (node.children.length === 3 || node.children.length === 4) { 109 | return this.execute(node.children[1]) 110 | } 111 | }, 112 | ArgumentList(node) { 113 | if (node.children.length === 1) { 114 | return [this.getValue(node.children[0])] 115 | } else { 116 | const left = this.execute(node.children[0]) ?? [] 117 | const right = this.getValue(node.children[2].value) 118 | return left.concat(right) 119 | } 120 | }, 121 | MemberExpression(node) { 122 | if (node.children.length === 1) { 123 | return this.execute(node.children[0]) 124 | } else if (node.children.length === 3) { 125 | if (node.children[0].value === 'new') { 126 | const jsFunction = this.getValue(node.children[1]) 127 | const args = this.getValue(node.children[2]) 128 | return jsFunction.construct(this.currentEnv, args[0]) 129 | } else { 130 | const obj = this.getValue(node.children[0]) 131 | return obj.getProperty(node.children[2].value) 132 | } 133 | } else if (node.children.length === 4) { 134 | return new Reference(this.execute(node.children[0]), node.children[2]) 135 | } 136 | }, 137 | NewExpression(node) { 138 | if (node.children.length === 1) { 139 | return this.execute(node.children[0]) 140 | } else { 141 | return this.execute(node.children[1]) 142 | } 143 | }, 144 | CallExpression(node) { 145 | if (node.children.length === 1) { 146 | return this.execute(node.children[0]) 147 | } else if (node.children.length === 2) { 148 | const jsFunction = this.execute(node.children[0]) 149 | const args = this.execute(node.children[1]) 150 | return jsFunction.call(this.currentEnv, args) // 1 151 | } 152 | }, 153 | CoverCallExpressionAndAsyncArrowHead(node) { 154 | if (node.children.length === 2) { 155 | const jsFunction = this.getValue(node.children[0]) 156 | const args = this.execute(node.children[1]) 157 | return jsFunction.call(this.currentEnv, args) // 2 158 | } 159 | }, 160 | LeftHandSideExpression(node) { 161 | return this.execute(node.children[0]) 162 | }, 163 | UpdateExpression(node) { 164 | if (node.children.length === 1) { 165 | return this.execute(node.children[0]) 166 | } else { 167 | let res = void 0 168 | if (node.children[0].type === '++') { // ++i 169 | const ref = this.execute(node.children[1]) 170 | ref.set(res = ref.get() + 1) 171 | } else if (node.children[0].type === '--') { // --i 172 | const ref = this.execute(node.children[1]) 173 | ref.set(res = ref.get() - 1) 174 | } else if (node.children[1].type === '++') { // i++ 175 | const ref = this.execute(node.children[0]) 176 | ref.set((res = ref.get()) + 1) 177 | } else if (node.children[1].type === '--') { // i-- 178 | const ref = this.execute(node.children[0]) 179 | ref.set((res = ref.get()) - 1) 180 | } 181 | return res 182 | } 183 | }, 184 | MultiplicativeExpression(node) { 185 | if (node.children.length === 1) { 186 | return this.execute(node.children[0]) 187 | } else { 188 | const left = this.getValue(node.children[0]) 189 | const right = this.getValue(node.children[2]) 190 | if (node.children[1].type === '*') { 191 | return left * right 192 | } else if (node.children[1].type === '/') { 193 | return left / right 194 | } else { 195 | return left % right 196 | } 197 | } 198 | }, 199 | AdditiveExpression(node) { 200 | if (node.children.length === 1) { 201 | return this.execute(node.children[0]) 202 | } else { 203 | const left = this.getValue(node.children[0]) 204 | const right = this.getValue(node.children[2]) 205 | if (node.children[1].type === '+') { 206 | return left + right 207 | } else { 208 | return left - right 209 | } 210 | } 211 | }, 212 | RelationalExpression(node) { 213 | if (node.children.length === 1) { 214 | return this.execute(node.children[0]) 215 | } else { 216 | const left = this.getValue(node.children[0]) 217 | const right = this.getValue(node.children[2]) 218 | if (node.children[1].type === '>') { 219 | return left > right 220 | } else { 221 | return left < right 222 | } 223 | } 224 | }, 225 | EqualityExpression(node) { 226 | if (node.children.length === 1) { 227 | return this.execute(node.children[0]) 228 | } else { 229 | const left = this.getValue(node.children[0]) 230 | const right = this.getValue(node.children[2]) 231 | const type = node.children[1].type 232 | if (type === '==') { 233 | return left == right 234 | } else if (type === '!=') { 235 | return left != right 236 | } else if (type === '===') { 237 | return left === right 238 | } else if (type === '!==') { 239 | return left !== right 240 | } 241 | } 242 | }, 243 | BitwiseANDExpression(node) { 244 | if (node.children.length === 1) { 245 | return this.execute(node.children[0]) 246 | } else { 247 | const left = this.getValue(node.children[0]) 248 | const right = this.getValue(node.children[2]) 249 | return left & right 250 | } 251 | }, 252 | BitwiseXORExpression(node) { 253 | if (node.children.length === 1) { 254 | return this.execute(node.children[0]) 255 | } else { 256 | const left = this.getValue(node.children[0]) 257 | const right = this.getValue(node.children[2]) 258 | return left ^ right 259 | } 260 | }, 261 | BitwiseORExpression(node) { 262 | if (node.children.length === 1) { 263 | return this.execute(node.children[0]) 264 | } else { 265 | const left = this.getValue(node.children[0]) 266 | const right = this.getValue(node.children[2]) 267 | return left | right 268 | } 269 | }, 270 | LogicalANDExpression(node) { 271 | if (node.children.length === 1) { 272 | return this.execute(node.children[0]) 273 | } else { 274 | const left = this.getValue(node.children[0]) 275 | const right = this.getValue(node.children[2]) 276 | return left && right 277 | } 278 | }, 279 | LogicalORExpression(node) { 280 | if (node.children.length === 1) { 281 | return this.execute(node.children[0]) 282 | } else { 283 | const left = this.getValue(node.children[0]) 284 | const right = this.getValue(node.children[2]) 285 | return left || right 286 | } 287 | }, 288 | CoalesceExpression(node) { 289 | if (node.children.length === 1) { 290 | return this.execute(node.children[0]) 291 | } else { 292 | const left = this.getValue(node.children[0]) 293 | const right = this.getValue(node.children[2]) 294 | return left ?? right 295 | } 296 | }, 297 | CoalesceExpressionHead(node) { 298 | if (node.children.length === 1) { 299 | return this.execute(node.children[0]) 300 | } 301 | }, 302 | ShortCircuitExpression(node) { 303 | if (node.children.length === 1) { 304 | return this.execute(node.children[0]) 305 | } 306 | }, 307 | ConditionalExpression(node) { 308 | if (node.children.length === 1) { 309 | return this.execute(node.children[0]) 310 | } else { 311 | const flag = this.execute(node.children[0]) 312 | if (flag) { 313 | return this.execute(node.children[2]) 314 | } else { 315 | return this.execute(node.children[4]) 316 | } 317 | } 318 | }, 319 | AssignmentOperator(node) { 320 | return node.children[0].type 321 | }, 322 | AssignmentExpression(node) { 323 | if (node.children.length === 1) { 324 | return this.execute(node.children[0]) 325 | } else { 326 | const ref = this.execute(node.children[0]) 327 | const right = this.getValue(node.children[2]) 328 | let res = void 0 329 | if (node.children[1].type === '=') { 330 | ref.set(res = right) 331 | } else if (node.children[1].type === 'AssignmentOperator') { 332 | const type = this.execute(node.children[1]) 333 | if (type === '*=') { 334 | ref.set(res = ref.get() * right) 335 | } else if (type === '/=') { 336 | ref.set(res = ref.get() / right) 337 | } else if (type === '%=') { 338 | ref.set(res = ref.get() % right) 339 | } else if (type === '+=') { 340 | ref.set(res = ref.get() + right) 341 | } else if (type === '-=') { 342 | ref.set(res = ref.get() - right) 343 | } else if (type === '<<=') { 344 | ref.set(res = ref.get() << right) 345 | } else if (type === '>>=') { 346 | ref.set(res = ref.get() >> right) 347 | } else if (type === '>>>=') { 348 | ref.set(res = ref.get() >>> right) 349 | } else if (type === '&=') { 350 | ref.set(res = ref.get() & right) 351 | } else if (type === '^=') { 352 | ref.set(res = ref.get() ^ right) 353 | } else if (type === '|=') { 354 | ref.set(res = ref.get() | right) 355 | } else if (type === '**=') { 356 | ref.set(res = ref.get() ** right) 357 | } 358 | } else if (node.children[1].type === '&&=') { 359 | ref.set(res = ref.get() && right) 360 | } else if (node.children[1].type === '||=') { 361 | ref.set(res = ref.get() || right) 362 | } else if (node.children[1].type === '??=') { 363 | ref.set(res = ref.get() ?? right) 364 | } 365 | return res 366 | } 367 | }, 368 | Expression(node) { 369 | if (node.children.length === 1) { 370 | return this.execute(node.children[0]) 371 | } else { 372 | this.execute(node.children[0]) 373 | return this.execute(node.children[2]) 374 | } 375 | }, 376 | ExpressionStatement(node) { 377 | return new Completion('normal', this.execute(node.children[0])) 378 | }, 379 | BlockStatement(node) { 380 | if (node.children.length === 3) { 381 | this.envStack.push(new Enviroment(this.currentEnv)) 382 | const ref = this.execute(node.children[1]) 383 | this.envStack.pop() 384 | return ref 385 | } 386 | return new Completion('normal') 387 | }, 388 | IfStatement(node) { 389 | const flag = this.execute(node.children[2]) 390 | if (node.children.length === 5) { 391 | if (flag) { 392 | return this.execute(node.children[4]) 393 | } else { 394 | return new Completion('normal') 395 | } 396 | } else { 397 | if (flag) { 398 | return this.execute(node.children[4]) 399 | } else { 400 | return this.execute(node.children[6]) 401 | } 402 | } 403 | }, 404 | ForStatement(node) { 405 | let start = 2 406 | if (node.children.length === 10) start++ 407 | const initialExpression = node.children[start] 408 | const conditionalExpression = node.children[start + 2] 409 | const finalExpression = node.children[start + 4] 410 | const cycleBody = node.children[start + 6] 411 | this.execute(initialExpression) 412 | while (this.execute(conditionalExpression)) { 413 | const completion = this.execute(cycleBody) 414 | if (completion.type === 'break' || completion.type === 'return') { 415 | return new Completion('normal', completion.value) 416 | } 417 | this.execute(finalExpression) 418 | } 419 | }, 420 | BreakableStatement(node) { 421 | if (node.children.length === 1) { 422 | return this.execute(node.children[0]) 423 | } 424 | }, 425 | BreakStatement(node) { 426 | return new Completion('break') 427 | }, 428 | ContinueStatement(node) { 429 | return new Completion('continue') 430 | }, 431 | ReturnStatement(node) { 432 | if (node.children.length === 2) { 433 | return new Completion('return') 434 | } else if (node.children.length === 3) { 435 | const value = this.getValue(node.children[1]) 436 | return new Completion('return', value) 437 | } 438 | }, 439 | Statement(node) { 440 | return this.execute(node.children[0]) 441 | }, 442 | StatementListItem(node) { 443 | const res = this.execute(node.children[0]) 444 | return res?.type ? res : new Completion('normal', res) 445 | }, 446 | StatementList(node) { 447 | if (node.children.length === 1) { 448 | return this.execute(node.children[0]) 449 | } else { 450 | const completion = this.execute(node.children[0]) 451 | if (completion.type === 'normal') { 452 | return this.execute(node.children[1]) 453 | } 454 | return completion 455 | } 456 | }, 457 | Program(node) { 458 | return this.execute(node.children[0]) 459 | } 460 | } 461 | 462 | globalEnv.set('Promise', new PromiseFunction(executor)) 463 | 464 | module.exports = { 465 | executor, 466 | globalEnv 467 | } -------------------------------------------------------------------------------- /src/helpers/lexical-analyzer.js: -------------------------------------------------------------------------------- 1 | const { regStr } = require('../data/conf.js') 2 | 3 | function getRegExp() { 4 | return new RegExp(regStr.replace(/\s+/g, ''), 'g') 5 | } 6 | 7 | module.exports = function(str) { 8 | const regExp = getRegExp() 9 | const list = [] 10 | while (r = regExp.exec(str)) { 11 | const groupKeys = Object.keys(r.groups) 12 | for (const groupKey of groupKeys) { 13 | if (r.groups[groupKey] === void 0) continue 14 | list.push({ 15 | type: groupKey === 'Punctuator' || groupKey === 'Keywords' ? r.groups[groupKey] : groupKey, 16 | value: r.groups[groupKey] 17 | }) 18 | } 19 | } 20 | list.push({ 21 | type: 'EOF' 22 | }) 23 | return list 24 | } -------------------------------------------------------------------------------- /src/helpers/syntax-parser.js: -------------------------------------------------------------------------------- 1 | const { rulesMap: defaultRulesMap, initialState: defaultInitialState } = require('../data/conf.js') 2 | 3 | function stringify(obj, replacer, space) { 4 | const cache = new Set() 5 | 6 | return JSON.stringify(obj, function(key, value) { 7 | if (typeof value === 'object' && value !== null) { 8 | if (cache.has(value)) { 9 | return '[Circular]' 10 | } 11 | cache.add(value) 12 | } 13 | if (replacer) { 14 | return replacer(key, value) 15 | } 16 | return value 17 | }, space) 18 | } 19 | 20 | const getClosure = (symbol, rulesMap) => { 21 | const rules = [] 22 | const pool = [symbol] 23 | const visited = new Set() 24 | while (pool.length !== 0) { 25 | const current = pool.shift() 26 | if (rulesMap.has(current) === false) continue 27 | if (visited.has(current) === true) continue 28 | const ruleBodys = rulesMap.get(current) 29 | ruleBodys.forEach(ruleBody => { 30 | // if (visited.has(ruleBody[0]) === true) return 31 | rules.push({ruleBody, $reduce: current}) 32 | pool.push(ruleBody[0]) 33 | }) 34 | visited.add(current) 35 | } 36 | return rules 37 | } 38 | 39 | const visited = new Map() 40 | const getClosureState = function(state, rulesMap) { 41 | visited.set(stringify(state), state) 42 | for (const key of Object.keys(state)) { 43 | if (key.startsWith('$')) continue 44 | const closure = getClosure(key, rulesMap) 45 | closure.forEach(item => { 46 | const {ruleBody, $reduce: reduce} = item 47 | let current = state 48 | ruleBody.forEach(symbol => { 49 | if (current[symbol] === void 0) current[symbol] = {} 50 | current = current[symbol] 51 | }) 52 | current.$reduce = reduce 53 | current.$count = ruleBody.length 54 | }) 55 | } 56 | for (const key of Object.keys(state)) { 57 | if (key.startsWith('$')) continue 58 | const id = stringify(state[key]) 59 | if (visited.has(id)) { 60 | state[key] = visited.get(id) 61 | } else { 62 | getClosureState(state[key], rulesMap) 63 | } 64 | } 65 | return state 66 | } 67 | 68 | module.exports = function(list, rulesMap = defaultRulesMap, initialState = defaultInitialState) { 69 | visited.clear() 70 | getClosureState(initialState, rulesMap) 71 | const stack = [] 72 | const stateStack = [ initialState ] 73 | const n = list.length 74 | 75 | const shift = symbol => { 76 | while ( 77 | (// console.log(stateStack[stateStack.length - 1], symbol), 78 | stateStack[stateStack.length - 1][symbol.type] === void 0) && 79 | stateStack[stateStack.length - 1].$reduce 80 | ) { 81 | reduce() 82 | } 83 | 84 | // console.log('symbol.type', symbol.type) 85 | if (stateStack[stateStack.length - 1][symbol.type] === void 0) { 86 | if (symbol.type === 'EOF' || symbol.type === '}' || hasLineTerminator) { 87 | shift({ 88 | type: ';', 89 | value: ';', 90 | }) 91 | return shift(symbol) 92 | } 93 | throw Error('syntax error' + ' type ' + symbol.type) 94 | } 95 | stack.push(symbol) 96 | stateStack.push(stateStack[stateStack.length - 1][symbol.type]) 97 | } 98 | 99 | const reduce = () => { 100 | const currentState = stateStack[stateStack.length - 1] 101 | const symbol = { 102 | type: currentState.$reduce, 103 | children: [] 104 | } 105 | for (let i = 0; i < currentState.$count; i++) { 106 | symbol.children.unshift(stack.pop()) 107 | stateStack.pop() 108 | } 109 | shift(symbol) 110 | } 111 | 112 | let hasLineTerminator = false 113 | for (let i = 0; i < n; i++) { 114 | const symbol = list[i] 115 | if (symbol.type === 'LineTerminator') { 116 | hasLineTerminator = true 117 | } else { 118 | shift(symbol) 119 | hasLineTerminator = false 120 | } 121 | } 122 | return stack 123 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const lex = require('./helpers/lexical-analyzer') 2 | const parse = require('./helpers/syntax-parser') 3 | const { executor, globalEnv } = require('./helpers/executor') 4 | 5 | module.exports = { 6 | lex, 7 | parse, 8 | executor, 9 | globalEnv, 10 | execute(ast) { 11 | return executor.execute(ast) 12 | }, 13 | evaluate(code, map, initialState) { 14 | const res = executor.execute(parse(lex(code), map, initialState)[0]) 15 | executor.runTask() 16 | return res 17 | } 18 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path') 2 | module.exports = { 3 | mode: 'development', 4 | entry: './src/index.js', 5 | output: { 6 | filename: 'yzhanjsinterpreter.min.js', 7 | path: resolve('docs'), 8 | library: 'yzhanJSInterpreter', 9 | libraryTarget: 'umd', 10 | globalObject: 'this', 11 | environment: { 12 | arrowFunction: false, 13 | bigIntLiteral: false, 14 | const: false, 15 | destructuring: false, 16 | dynamicImport: false, 17 | forOf: false, 18 | module: false, 19 | optionalChaining: false, 20 | templateLiteral: false 21 | } 22 | }, 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.js$/, 27 | include: resolve('src'), 28 | use: ['swc-loader'] 29 | } 30 | ] 31 | }, 32 | devServer: { 33 | hot: true, 34 | open: true, 35 | port: 3000, 36 | static: resolve('docs'), 37 | } 38 | } --------------------------------------------------------------------------------