├── .gitignore ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── bin ├── ast.js ├── devel.js └── eval.js ├── cli.js ├── index.js ├── package.json ├── src ├── environment.js ├── evaluator.js ├── global-env-builder.js ├── parser.js └── tokenizer.js └── test ├── evaluator ├── test-binaryexpression.js ├── test-blockstatement.js ├── test-class-declaration-and-new-call-and-member.js ├── test-comment.js ├── test-declaration.js ├── test-equality.js ├── test-function-declaration-and-function-call.js ├── test-if-statement.js ├── test-iteration.js ├── test-literal.js ├── test-logical.js ├── test-relational.js ├── test-statement.js └── test-unary.js ├── parser ├── test-assignment.js ├── test-binaryexpression.js ├── test-blockstatement.js ├── test-class-declaration.js ├── test-comment.js ├── test-declaration.js ├── test-equality.js ├── test-function-call.js ├── test-function-declaration.js ├── test-if-statement.js ├── test-iteration.js ├── test-list.js ├── test-literal.js ├── test-logical.js ├── test-map.js ├── test-member.js ├── test-new-call.js ├── test-relational.js ├── test-statement.js ├── test-tuple.js ├── test-unary.js └── test-whitespace.js ├── script ├── 01-sum.toy ├── 02-fib.toy ├── 03-class.toy ├── 04-line.toy └── 05-list.toy ├── test-evaluator.js ├── test-parser.js └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directories 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}/bin/eval.js", 15 | "args": [ 16 | "test/script/01-sum.toy" 17 | ] 18 | }, 19 | { 20 | "type": "pwa-node", 21 | "request": "launch", 22 | "name": "Devel", 23 | "skipFiles": [ 24 | "/**" 25 | ], 26 | "program": "${workspaceFolder}/bin/devel.js" 27 | }, 28 | { 29 | "type": "pwa-node", 30 | "request": "launch", 31 | "name": "Unit Tests", 32 | "skipFiles": [ 33 | "/**" 34 | ], 35 | "program": "${workspaceFolder}/test/test.js" 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # (Practice) Toy Language Interpreter - JS 2 | 3 | 4 | 5 | 6 | 7 | - [(Practice) Toy Language Interpreter - JS](#practice-toy-language-interpreter-js) 8 | - [单元测试](#单元测试) 9 | - [运行指定脚本文件](#运行指定脚本文件) 10 | - [打印指定脚本的 AST](#打印指定脚本的-ast) 11 | - [安装 CLI 程序](#安装-cli-程序) 12 | - [程序示例](#程序示例) 13 | - [一般函数的调用](#一般函数的调用) 14 | - [函数的递归调用](#函数的递归调用) 15 | - [类与对象](#类与对象) 16 | - [类的组合](#类的组合) 17 | - [列表的实现](#列表的实现) 18 | - [语法](#语法) 19 | - [数据类型](#数据类型) 20 | - [操作符](#操作符) 21 | - [语句](#语句) 22 | - [内置函数](#内置函数) 23 | - [I/O](#io) 24 | - [数学函数](#数学函数) 25 | - [内置常数](#内置常数) 26 | 27 | 28 | 29 | 练习单纯使用 JS 编写简单的 _玩具语言_ 解析器。 30 | 31 | > 注:本项目是阅读和学习《Building a Parser from scratch》时的随手练习,并无实际用途。程序的原理、讲解和代码的原始出处请移步 http://dmitrysoshnikov.com/courses/parser-from-scratch/ 32 | 33 | ## 单元测试 34 | 35 | ```bash 36 | $ npm test 37 | ``` 38 | 39 | 或者 40 | 41 | ```bash 42 | $ node test/test.js 43 | ``` 44 | 45 | ## 运行指定脚本文件 46 | 47 | ```bash 48 | $ npm run eval script.toy 49 | ``` 50 | 51 | 或者 52 | 53 | ```bash 54 | $ node bin/eval.js script.toy 55 | ``` 56 | 57 | 其中 `script.toy` 是脚本文件的名称(可以是相对或绝对路径)。可以试试运行 `test/evalutor` 文件夹里面的示例脚本,比如: 58 | 59 | ```bash 60 | $ npm run eval test/script/01-sum.toy 61 | ``` 62 | 63 | 如无意外,应该能看到输出 `5050`。 64 | 65 | ## 打印指定脚本的 AST 66 | 67 | ```bash 68 | $ npm run ast script.toy 69 | ``` 70 | 71 | 或者 72 | 73 | ```bash 74 | $ node bin/ast.js script.toy 75 | ``` 76 | 77 | 其中 `script.toy` 是脚本文件的名称(可以是相对或绝对路径)。 78 | 79 | ## 安装 CLI 程序 80 | 81 | 首先安装本项目到全局: 82 | 83 | `$ sudo npm install -g ./practice-toy-lang-interpreter-js` 84 | 85 | 其中 `./practice-toy-lang-interpreter-js` 是本项目源码的目录(可以是相对或绝对路径),如果安装过程停住了,可以按 `Ctrl+C` 中止,实际上已经成功安装。 86 | 87 | 然后就可以在任意文件路径下使用命令 `toy-js` 执行脚本,比如: 88 | 89 | `$ toy-js script.toy` 90 | 91 | ## 程序示例 92 | 93 | ### 一般函数的调用 94 | 95 | 计算 1..100 的和: 96 | 97 | ```js 98 | let sum; 99 | for (let i = 1; i <= 100; i = i + 1) { 100 | sum = sum + i; 101 | } 102 | 103 | print(sum); // 5050 104 | ``` 105 | 106 | 将上面的代码保存到文件(比如 `~/temp/script.toy`),然后在本项目的源码根目录执行下面命令即可执行上面的源代码: 107 | 108 | ```bash 109 | $ npm run eval ~/temp/script.toy 110 | ``` 111 | 112 | 或者 113 | 114 | ```bash 115 | $ node bin/eval.js ~/temp/script.toy 116 | ``` 117 | 118 | 如无意外,将会看到输出 `5050`。 119 | 120 | 已创建好的源代码文件在 `test/script` 文件夹里,所以也可以直接运行: 121 | 122 | ```bash 123 | $ npm run eval test/script/01-sum.toy 124 | ``` 125 | 126 | ### 函数的递归调用 127 | 128 | 计算斐波那契数列(Fibonacci sequence)(即:0、1、1、2、3、5、8、13、21、34、55...)第 9 个(从 0 开始)数: 129 | 130 | ```js 131 | function fib(n) { 132 | if (n==0) { 133 | 0; 134 | }else if(n==1) { 135 | 1; 136 | }else { 137 | fib(n-1) + fib(n-2); 138 | } 139 | } 140 | 141 | print(fib(9)); // 34 142 | ``` 143 | 144 | 运行: 145 | 146 | ```bash 147 | $ npm run eval test/script/02-fib.toy 148 | ``` 149 | 150 | ### 类与对象 151 | 152 | ```js 153 | class Num { 154 | let val; 155 | 156 | constructor(this, x) { 157 | this.val = x; 158 | } 159 | 160 | function add(this, y) { 161 | this.val += y; 162 | } 163 | } 164 | 165 | let a = new Num(1); 166 | let b = new Num(2); 167 | 168 | a.add(3); 169 | b.add(5); 170 | 171 | print(a.val + b.val); // 11 172 | ``` 173 | 174 | 运行: 175 | 176 | ```bash 177 | $ npm run eval test/script/03-class.toy 178 | ``` 179 | 180 | ### 类的组合 181 | 182 | ```js 183 | class Point { 184 | let x,y; 185 | constructor(this, x, y) { 186 | this.x = x; 187 | this.y = y; 188 | } 189 | } 190 | 191 | class Line { 192 | let p1,p2; 193 | constructor(this, p1, p2) { 194 | this.p1 = p1; 195 | this.p2 = p2; 196 | } 197 | 198 | function length(this) { 199 | sqrt( 200 | pow(this.p1.x - this.p2.x, 201 | 2) 202 | + 203 | pow(this.p1.y - this.p2.y, 204 | 2) 205 | ); 206 | } 207 | } 208 | 209 | let p1 = new Point(2,3); 210 | let p2 = new Point(5,7); 211 | 212 | let n = new Line(p1, p2); 213 | 214 | print(n.length()); // 5 215 | ``` 216 | 217 | 运行: 218 | 219 | ```bash 220 | $ npm run eval test/script/04-line.toy 221 | ``` 222 | 223 | ### 列表的实现 224 | 225 | 单向链表 226 | 227 | ```js 228 | class Node { 229 | let val, next; 230 | constructor(this, val, next) { 231 | this.val = val; 232 | this.next = next; 233 | } 234 | } 235 | 236 | class List { 237 | let head; 238 | 239 | function push(this, val) { 240 | this.head = new Node(val, this.head); 241 | } 242 | 243 | function pop(this) { 244 | if (this.head != null) { 245 | let val = this.head.val; 246 | this.head = this.head.next; 247 | val; 248 | } 249 | } 250 | 251 | function length(this) { 252 | let i = 0; 253 | let n = this.head; 254 | while(n != null) { 255 | i+=1; 256 | n=n.next; 257 | } 258 | i; 259 | } 260 | 261 | function to_string(this) { 262 | let s = ""; 263 | let n = this.head; 264 | while(n != null) { 265 | s += n.val + ","; 266 | n=n.next; 267 | } 268 | s; 269 | } 270 | } 271 | 272 | let list = new List(); 273 | 274 | print("push: 3,5,7,9"); 275 | list.push(3); 276 | list.push(5); 277 | list.push(7); 278 | list.push(9); 279 | 280 | print("length: " + list.length()); 281 | print("items: " + list.to_string()); 282 | 283 | print("pop: " + list.pop()); 284 | print("pop: " + list.pop()); 285 | 286 | print("length: " + list.length()); 287 | print("items: " + list.to_string()); 288 | ``` 289 | 290 | 运行: 291 | 292 | ```bash 293 | $ npm run eval test/script/05-list.py 294 | ``` 295 | 296 | ## 语法 297 | 298 | ### 数据类型 299 | 300 | * 数字:整数和浮点数,如:`123`, `2.718` 301 | * 字符串:如 `"foobar"` 302 | * 布尔:如 `true`,`false` 303 | * 元组:`(1,)` `(1,"foo",true)` 304 | * 列表:`[1,2,3]` 305 | * 映射:`#{a: 1, b: 2}` 306 | 307 | ### 操作符 308 | 309 | * 四则运算 `+` `-` `*` `/`,如 `1+2*3`, `(1+2)*3` 310 | * 大小比较 `>` `>=` `<` `<=`,如 `3>2` 311 | * 相等比较 `==` `!=`,如 `3>3==true` 312 | * 逻辑运算 `&&` `||`,如 `true && false`, `x>0 || y>0` 313 | * 一元运算 `!` `+` `-`,如 `!false`,`-123`, `+100` 314 | 315 | ### 语句 316 | 317 | * 声明语句 `let`,如 `let a = 1`,`let a, b`, `let a, b=2`, `let y=x+3` 318 | * 赋值语句 `=` `+=` `-=` `*=` `/=`,如 `x=1`,`y=2*x+3`, `x+=1` 319 | * 条件语句 `if`,如 320 | - ```js 321 | if (x) a=0` 322 | ``` 323 | - ```js 324 | if (x) 325 | a = 0; 326 | else 327 | a = 1; 328 | ``` 329 | - ```js 330 | if (x) { 331 | a=0; 332 | } 333 | ``` 334 | - ```js 335 | if (x) 336 | if (y) 337 | 2; 338 | else 339 | 8; 340 | ``` 341 | - ```js 342 | if (p){ 343 | 1; 344 | }else if (q) { 345 | 2; 346 | } 347 | ``` 348 | * 循环语句 349 | - ```js 350 | while(x>10) { 351 | x -=1; 352 | } 353 | ``` 354 | - ```js 355 | do{ 356 | x+=1; 357 | }while(x<10); 358 | ``` 359 | - ```js 360 | for(let i =0;i<10;i+=1) { 361 | x+=i; 362 | } 363 | ``` 364 | * 函数声明 365 | ```js 366 | function double(x) { 367 | return x*2; 368 | } 369 | ``` 370 | * 成员访问 `a.b.c`, `a[1]`, `b[1][2]` 371 | * 函数调用 `foo()`, `list.add(1)`, `ele[2]("foo")`, `make_func(1)(2)` 372 | * 类 373 | ```js 374 | class Point { 375 | constructor(x,y) { 376 | this.x = x; 377 | this.y = y; 378 | } 379 | 380 | function calc() { 381 | return 2; 382 | } 383 | } 384 | ``` 385 | * 类的继承 386 | ```js 387 | class Point3D extends Point { 388 | constructor(x,y,z) { 389 | super(x,y); 390 | } 391 | 392 | function calc() { 393 | return super.calc() + 1; 394 | } 395 | } 396 | ``` 397 | * 类的实例化 398 | ```js 399 | new Point(1, 2); 400 | ``` 401 | 402 | ## 内置函数 403 | 404 | ### I/O 405 | 406 | * print(s) 打印一个字符串,如果参数 `s` 是数字,则会按字面的值转换为字符串(即,并非按照数字的 ascii 或者 unicode code point 来转换)。函数返回 null。 407 | * printf(s, [v1, v2, ...]) 打印带格式的字符串,字符串的格式同 JavaScript,如 `string %s, num %d`,第二个参数是一个列表。函数返回 null。 408 | 409 | * read_file(file_name) 读取指定的文本文件,返回 String。 410 | * write_file(file_name, text) 将 String 写入到指定的文件,返回 null。 411 | 412 | ### 数学函数 413 | 414 | * abs(x) 计算绝对值,返回 number 415 | * ceil(x) 向上取整,返回 number 416 | * floor(x) 向下取整,返回 number 417 | * round(x) 四舍五入取整,返回 number 418 | * trunc(x) 取整,返回 number 419 | 420 | * log(x) 计算 log 10(x),返回 number 421 | * ln(x) 计算 log e(x),返回 number 422 | * pow(x, y) 幂函数,返回 number 423 | * sqrt(x) 平方根,返回 number 424 | 425 | * random() 获取随机数,返回 [0,1) 426 | 427 | * sin(x) 正弦,返回 number 428 | * cos(x) 余弦,返回 number 429 | * tan(x) 正切,返回 number 430 | * asin(x) 反正弦,返回 number 431 | * acos(x) 反余弦,返回 number 432 | * atan(x) 反正切,返回 number 433 | 434 | ## 内置常数 435 | 436 | * E 自然对数 437 | * PI 圆周率 -------------------------------------------------------------------------------- /bin/ast.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | 10 | import fs from 'fs'; 11 | 12 | import { Parser } from '../src/parser.js'; 13 | 14 | function printUsage() { 15 | console.log(` 16 | usage: 17 | $ npm run ast script_file 18 | `); 19 | } 20 | 21 | function printAst(string) { 22 | const parser = new Parser(); 23 | const ast = parser.parse(string); 24 | console.log(JSON.stringify(ast, null, 2)); 25 | } 26 | 27 | function printAstFromFile(file) { 28 | const string = fs.readFileSync(file, 'utf-8'); 29 | printAst(string); 30 | } 31 | 32 | let args = process.argv; 33 | 34 | if (args.length !== 3) { 35 | printUsage(); 36 | process.exit(1); 37 | } 38 | 39 | printAstFromFile(args[2]); 40 | -------------------------------------------------------------------------------- /bin/devel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | /** 10 | * 用于手动调试 Parser 11 | */ 12 | 13 | import { Parser } from '../src/parser.js'; 14 | 15 | function printUsage() { 16 | console.log(` 17 | usage: 18 | $ npm run devel 19 | `); 20 | } 21 | 22 | function printAst(string) { 23 | const parser = new Parser(); 24 | const ast = parser.parse(string); 25 | console.log(JSON.stringify(ast, null, 2)); 26 | } 27 | 28 | let args = process.argv; 29 | 30 | if (args.length !== 2) { 31 | printUsage(); 32 | process.exit(1); 33 | } 34 | 35 | // 这里写上需要调试的 toy 语言代码 36 | const string = ` 37 | print("Hello world!"); 38 | `; 39 | 40 | printAst(string); -------------------------------------------------------------------------------- /bin/eval.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { Evaluator } from '../src/evaluator.js'; 10 | 11 | function printUsage() { 12 | console.log(` 13 | usage: 14 | $ npm run eval script_file 15 | `); 16 | } 17 | 18 | let args = process.argv; 19 | 20 | if (args.length !== 3) { 21 | printUsage(); 22 | process.exit(1); 23 | } 24 | 25 | const evaluator = new Evaluator(); 26 | let value = evaluator.evalFile(args[2]); 27 | 28 | console.log('> program return value:'); 29 | console.log(JSON.stringify(value, null, 2)); -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { Evaluator } from './src/evaluator.js'; 4 | 5 | function printUsage() { 6 | console.log(` 7 | usage: 8 | $ toy-js script_file.toy 9 | `); 10 | } 11 | 12 | let args = process.argv; 13 | 14 | if (args.length !== 3) { 15 | printUsage(); 16 | process.exit(); 17 | } 18 | 19 | const evaluator = new Evaluator(); 20 | evaluator.evalFile(args[2]); 21 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { Evaluator } from './src/evaluator.js'; 10 | import { Parser } from './src/parser.js'; 11 | 12 | export { Evaluator, Parser }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "practice-toy-lang-interpreter-js", 3 | "version": "1.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "node test/test.js", 9 | "ast": "node bin/ast.js", 10 | "eval": "node bin/eval.js", 11 | "devel": "node bin/devel.js" 12 | }, 13 | "bin": { 14 | "toy-js": "./cli.js" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/hemashushu/practice-toy-lang-interpreter-js.git" 19 | }, 20 | "author": "Hemashushu ", 21 | "keywords": [ 22 | "interpreter" 23 | ], 24 | "license": "MPL-2.0", 25 | "dependencies": {}, 26 | "devDependencies": {}, 27 | "bugs": { 28 | "url": "https://github.com/hemashushu/practice-toy-lang-interpreter-js/issues" 29 | }, 30 | "homepage": "https://github.com/hemashushu/practice-toy-lang-interpreter-js#readme" 31 | } -------------------------------------------------------------------------------- /src/environment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | class Environment { 10 | constructor(parent = null, records = new Map()) { 11 | this.parent = parent; 12 | this.records = records; 13 | } 14 | 15 | define(name, value) { 16 | if (this.records.has(name)) { 17 | throw new SyntaxError(`Identifier "${name}" has already been declared.`); 18 | } 19 | 20 | this.records.set(name, value); 21 | return value; 22 | } 23 | 24 | /** 25 | * 26 | * @returns return undefined when name not found. 27 | */ 28 | getValue(name) { 29 | return this.records.get(name); 30 | } 31 | 32 | setValue(name, value) { 33 | this.records.set(name, value); 34 | } 35 | 36 | lookup(name) { 37 | return this.resolve(name).getValue(name); 38 | } 39 | 40 | assign(name, value) { 41 | this.resolve(name).setValue(name, value); 42 | return value; 43 | } 44 | 45 | /** 46 | * return the specified environment in which 47 | * an identifier is defined. 48 | * @param {*} name 49 | */ 50 | resolve(name) { 51 | if (this.records.has(name)){ 52 | return this; 53 | } 54 | 55 | if (this.parent == null) { 56 | throw new ReferenceError(`Identifier "${name}" is not defined.`); 57 | } 58 | 59 | return this.parent.resolve(name); 60 | } 61 | } 62 | 63 | export { Environment }; -------------------------------------------------------------------------------- /src/global-env-builder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { readFileSync, writeFileSync } from 'fs'; 10 | 11 | import { Environment } from './environment.js'; 12 | 13 | class GlobalEnvironmentBuilder { 14 | static build() { 15 | const records = new Map(); 16 | GlobalEnvironmentBuilder.addBuiltinFunctions(records); 17 | GlobalEnvironmentBuilder.addBuiltinConstants(records); 18 | 19 | return new Environment(null, records); 20 | } 21 | 22 | static addBuiltinFunctions(records) { 23 | 24 | /* ******** I/O ******** */ 25 | 26 | records.set('print', (v) => { 27 | console.log(v); 28 | return null; 29 | }); 30 | 31 | records.set('printf', (s, list) => { 32 | console.log(s, ...list); 33 | return null; 34 | }); 35 | 36 | records.set('read_file', (fullname) => { 37 | // https://nodejs.org/api/fs.html#fsreadfilesyncpath-options 38 | return readFileSync(fullname, 'utf-8'); 39 | }); 40 | 41 | records.set('write_file', (fullname, text) => { 42 | // https://nodejs.org/api/fs.html#fswritefilesyncfile-data-options 43 | writeFileSync(fullname, text, 'utf-8'); 44 | return null; 45 | }); 46 | 47 | /* ******** Math ******** */ 48 | 49 | // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math 50 | 51 | records.set('abs', x => { 52 | return Math.abs(x); 53 | }); 54 | 55 | /* ******** 取整函数 ******** */ 56 | 57 | records.set('ceil', x => { 58 | return Math.ceil(x); 59 | }); 60 | 61 | records.set('floor', x => { 62 | return Math.floor(x); 63 | }); 64 | 65 | records.set('round', x => { 66 | return Math.round(x); 67 | }); 68 | 69 | records.set('trunc', x => { 70 | return Math.trunc(x); 71 | }); 72 | 73 | /* ******** 对数/幂/平方根 ******** */ 74 | 75 | records.set('log', x => { // log base 10 76 | return Math.log10(x); 77 | }); 78 | 79 | records.set('ln', x => { // log base e 80 | return Math.log(x); 81 | }); 82 | 83 | records.set('pow', (base, exp) => { 84 | return Math.pow(base, exp) 85 | }); 86 | 87 | records.set('sqrt', x => { 88 | return Math.sqrt(x); 89 | }); 90 | 91 | /* ******** 随机数 ******** */ 92 | 93 | records.set('random', () => { 94 | return Math.random(); // 返回在范围 [0,1) 里的随机数 95 | }); 96 | 97 | /* ******** 三角函数 ******** */ 98 | 99 | records.set('sin', x => { 100 | return Math.sin(x); // 单位:弧度 101 | }); 102 | 103 | records.set('cos', x => { 104 | return Math.cos(x); 105 | }); 106 | 107 | records.set('tan', x => { 108 | return Math.tan(x); 109 | }); 110 | 111 | records.set('asin', x => { 112 | return Math.asin(x); 113 | }); 114 | 115 | records.set('acos', x => { 116 | return Math.acos(x); 117 | }); 118 | 119 | records.set('atan', x => { 120 | return Math.atan(x); 121 | }); 122 | } 123 | 124 | static addBuiltinConstants(records) { 125 | records.set('E', Math.E); 126 | records.set('PI', Math.PI); 127 | } 128 | } 129 | 130 | export { GlobalEnvironmentBuilder }; -------------------------------------------------------------------------------- /src/tokenizer.js: -------------------------------------------------------------------------------- 1 | // original from https://github.com/DmitrySoshnikov/letter-rdp-source 2 | 3 | /** 4 | * tokenizer spec 5 | */ 6 | const Spec = [ 7 | // Whitespace 8 | [/^\s+/, null], 9 | 10 | // Comments 11 | [/^\/\/.*/, null], // single line comment "//..." 12 | [/^\/\*[\s\S]*?\*\//, null], // multi line comment "/*...*/" 13 | 14 | // Logical operators 15 | [/^&&/,'LOGICAL_AND'], // && 16 | [/^\|\|/,'LOGICAL_OR'], // || 17 | 18 | // Equality operators 19 | [/^[!=]=/, 'EQUALITY_OPERATOR'], // ==, != 20 | 21 | // Relational operators 22 | [/^[><]=?/, 'RELATIONAL_OPERATOR'], // >= <= > < 23 | 24 | // Assignment operators 25 | [/^[*/+-]=/, 'COMPLEX_ASSIGN'], // +=, -=, *=, /= 26 | [/^=/, 'SIMPLE_ASSIGN'], // = 27 | 28 | // Math operators 29 | [/^[+-]/, 'ADDITIVE_OPERATOR'], // + - 30 | [/^[*\/]/, 'MULTIPLICATIVE_OPERATOR'], // * / 31 | 32 | // Unary operators 33 | [/^!/,'LOGICAL_NOT'], // "!" 34 | 35 | [/^#{/, '#{'], // #{ // map start 36 | [/^:/, ':'], // : 37 | 38 | // Symbols, delimiters 39 | [/^;/, ';'], // ; 40 | [/^{/, '{'], // { 41 | [/^}/, '}'], // } 42 | [/^\(/, '('], // ( 43 | [/^\)/, ')'], // ) 44 | [/^\[/, '['], // [ 45 | [/^\]/, ']'], // ] 46 | 47 | [/^,/, ','], // , 48 | [/^\./, '.'], // . 49 | 50 | // Keywords 51 | [/^\blet\b/, 'let'], // let 52 | [/^\bif\b/, 'if'], // if 53 | [/^\belse\b/, 'else'], // else 54 | [/^\bwhile\b/,'while'], // while 55 | [/^\bdo\b/,'do'], // do 56 | [/^\bfor\b/,'for'], // for 57 | [/^\bfunction\b/,'function'], // function 58 | [/^\bconstructor\b/,'constructor'], // constructor 59 | [/^\breturn\b/,'return'], // return 60 | [/^\bclass\b/,'class'], // class 61 | [/^\bextends\b/,'extends'], // extends 62 | [/^\bnew\b/,'new'], // new 63 | 64 | [/^\btrue\b/, 'TRUE'], // true 65 | [/^\bfalse\b/, 'FALSE'], // false 66 | [/^\bnull\b/, 'NULL'], // null 67 | 68 | // Number 69 | [/^\d[\d.e_-]*/, 'NUMBER'], 70 | 71 | // String 72 | [/^".*?(?, All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testAdditiveExpression() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | 1+2; 18 | `), 3); 19 | 20 | assert.equal(evaluator.evalString(` 21 | 1+2+3; 22 | `), 6); 23 | } 24 | 25 | function testMultiplicativeExpression() { 26 | let evaluator = new Evaluator(); 27 | 28 | assert.equal(evaluator.evalString(` 29 | 1*2; 30 | `), 2); 31 | 32 | assert.equal(evaluator.evalString(` 33 | 1+2*3; 34 | `), 7); 35 | } 36 | 37 | function testParenthesizedExpression() { 38 | let evaluator = new Evaluator(); 39 | 40 | assert.equal(evaluator.evalString(` 41 | (1+2)*3; 42 | `), 9); 43 | 44 | assert.equal(evaluator.evalString(` 45 | 8*(2+3)+4; 46 | `), 44); 47 | } 48 | 49 | function testBinaryExpression() { 50 | testAdditiveExpression(); 51 | testMultiplicativeExpression(); 52 | testParenthesizedExpression(); 53 | 54 | console.log('testBinaryExpression() passed.'); 55 | } 56 | 57 | export { testBinaryExpression }; -------------------------------------------------------------------------------- /test/evaluator/test-blockstatement.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testSingleBlockStatement() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | { 18 | 123; 19 | "foo"; 20 | } 21 | `), 'foo'); 22 | } 23 | 24 | function testMultiBlockStatements() { 25 | let evaluator = new Evaluator(); 26 | 27 | assert.equal(evaluator.evalString(` 28 | { 29 | "foo"; 30 | } 31 | 123; 32 | { 33 | "bar"; 34 | } 35 | `), 'bar'); 36 | } 37 | 38 | function testNestedBlockStatements() { 39 | let evaluator = new Evaluator(); 40 | 41 | assert.equal(evaluator.evalString(` 42 | { 43 | "foo"; 44 | { 45 | 123; 46 | } 47 | } 48 | `), 123); 49 | } 50 | 51 | function testEmptyStatement() { 52 | let evaluator = new Evaluator(); 53 | 54 | assert.equal(evaluator.evalString(` 55 | 123; 56 | ; 57 | `), null); 58 | } 59 | 60 | function testEmptyBlockStatement() { 61 | let evaluator = new Evaluator(); 62 | 63 | assert.equal(evaluator.evalString(` 64 | {} 65 | `), null); 66 | } 67 | 68 | function testBlockStatement() { 69 | testSingleBlockStatement(); 70 | testMultiBlockStatements(); 71 | testNestedBlockStatements(); 72 | 73 | testEmptyStatement(); 74 | testEmptyBlockStatement(); 75 | 76 | console.log('testBlockStatement() passed.'); 77 | } 78 | 79 | export { testBlockStatement }; -------------------------------------------------------------------------------- /test/evaluator/test-class-declaration-and-new-call-and-member.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testSimpleClass() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | class Num { 18 | } 19 | 20 | let a = new Num(); 21 | a != null; 22 | `), true); 23 | 24 | assert.equal(evaluator.evalString(` 25 | class Num { 26 | let val; 27 | 28 | constructor(this) { 29 | this.val = 1; 30 | } 31 | } 32 | 33 | let a = new Num(); 34 | a.val; 35 | `), 1); 36 | 37 | assert.equal(evaluator.evalString(` 38 | class Num { 39 | let val; 40 | 41 | constructor(this, x) { 42 | this.val = x; 43 | } 44 | } 45 | 46 | let a = new Num(2); 47 | a.val; 48 | `), 2); 49 | 50 | assert.equal(evaluator.evalString(` 51 | class Num { 52 | let val; 53 | 54 | constructor(this, x) { 55 | this.val = x; 56 | } 57 | 58 | function add(this, y) { 59 | this.val += y; 60 | } 61 | 62 | function get(this) { 63 | this.val; 64 | } 65 | } 66 | 67 | let a = new Num(3); 68 | a.add(2); 69 | a.get(); 70 | `), 5); 71 | 72 | } 73 | 74 | function testMultipleInstance() { 75 | let evaluator = new Evaluator(); 76 | 77 | assert.equal(evaluator.evalString(` 78 | class Num { 79 | let val; 80 | 81 | constructor(this, x) { 82 | this.val = x; 83 | } 84 | } 85 | 86 | let a = new Num(1); 87 | let b = new Num(2); 88 | a.val + b.val; 89 | `), 3); 90 | 91 | assert.equal(evaluator.evalString(` 92 | class Num { 93 | let val; 94 | 95 | constructor(this, x) { 96 | this.val = x; 97 | } 98 | } 99 | 100 | let a = new Num(1); 101 | let b = new Num(2); 102 | a.val = 3; 103 | b.val = 5; 104 | a.val + b.val; 105 | `), 8); 106 | 107 | assert.equal(evaluator.evalString(` 108 | class Num { 109 | let val; 110 | 111 | constructor(this, x) { 112 | this.val = x; 113 | } 114 | 115 | function add(this, y) { 116 | this.val += y; 117 | } 118 | } 119 | 120 | let a = new Num(1); 121 | let b = new Num(2); 122 | a.add(3); 123 | b.add(5); 124 | a.val + b.val; 125 | `), 11); 126 | } 127 | 128 | function testCombineClass() { 129 | 130 | let evaluator = new Evaluator(); 131 | 132 | assert.equal(evaluator.evalString(` 133 | class Point { 134 | let x,y; 135 | constructor(this, x, y) { 136 | this.x = x; 137 | this.y = y; 138 | } 139 | } 140 | 141 | class Line { 142 | let p1,p2; 143 | constructor(this, p1, p2) { 144 | this.p1 = p1; 145 | this.p2 = p2; 146 | } 147 | 148 | function length(this) { 149 | sqrt( 150 | pow(this.p1.x - this.p2.x, 151 | 2) 152 | + 153 | pow(this.p1.y - this.p2.y, 154 | 2) 155 | ); 156 | } 157 | } 158 | 159 | let p1 = new Point(2,3); 160 | let p2 = new Point(5,7); 161 | let n = new Line(p1, p2); 162 | n.length(); 163 | `), 5); 164 | } 165 | 166 | function testClassDeclarationAndNewCallAndMember() { 167 | testSimpleClass(); 168 | testMultipleInstance(); 169 | testCombineClass(); 170 | 171 | console.log('testClassDeclarationAndNewCallAndMember() passed.'); 172 | } 173 | 174 | export { testClassDeclarationAndNewCallAndMember }; -------------------------------------------------------------------------------- /test/evaluator/test-comment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testSingleLineComment() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | // comment 1 18 | 555; // comment 2 19 | `), 555); 20 | } 21 | 22 | function testMultiLineComment() { 23 | let evaluator = new Evaluator(); 24 | 25 | assert.equal(evaluator.evalString(` 26 | /** 27 | * comment 28 | */ 29 | 555 /* "also comment" */ ; 30 | `), 555); 31 | } 32 | 33 | function testComment() { 34 | testSingleLineComment(); 35 | testMultiLineComment(); 36 | 37 | console.log('testComment() passed.'); 38 | } 39 | 40 | export { testComment }; -------------------------------------------------------------------------------- /test/evaluator/test-declaration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testSingleVariableDeclaration() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | let x; 18 | `), null); 19 | 20 | assert.equal(evaluator.evalString(` 21 | let y = 1; 22 | `), 1); 23 | } 24 | 25 | function testMultipleVariableDeclaration() { 26 | let evaluator = new Evaluator(); 27 | 28 | assert.equal(evaluator.evalString(` 29 | let a, b; 30 | `), null); 31 | 32 | assert.equal(evaluator.evalString(` 33 | let m, n=2; 34 | `), 2); 35 | } 36 | 37 | function testVariableDeclarationWithExpressionInitializer() { 38 | let evaluator = new Evaluator(); 39 | 40 | assert.equal(evaluator.evalString(` 41 | let x=2; // ADD 42 | let z=x+1; 43 | `), 3); 44 | } 45 | 46 | function testReadVariable_ADD() { 47 | let evaluator = new Evaluator(); 48 | 49 | assert.equal(evaluator.evalString(` 50 | let a=1; 51 | let b=2; 52 | let c=3; 53 | a; 54 | `), 1); 55 | 56 | assert.equal(evaluator.evalString(` 57 | let a=1; 58 | let b=2; 59 | let c=3; 60 | b; 61 | `), 2); 62 | 63 | assert.equal(evaluator.evalString(` 64 | let a=1; 65 | let b=2; 66 | let c=3; 67 | c; 68 | `), 3); 69 | } 70 | 71 | function testAssignVariable_ADD() { 72 | let evaluator = new Evaluator(); 73 | 74 | assert.equal(evaluator.evalString(` 75 | let a=1; 76 | a; 77 | `), 1); 78 | 79 | assert.equal(evaluator.evalString(` 80 | let a; 81 | a=2; 82 | `), 2); 83 | 84 | assert.equal(evaluator.evalString(` 85 | let a=1; 86 | a=3; 87 | a; 88 | `), 3); 89 | 90 | assert.equal(evaluator.evalString(` 91 | let a=1; 92 | a=4; 93 | 0; 94 | a; 95 | `), 4); 96 | 97 | assert.equal(evaluator.evalString(` 98 | let a=5; 99 | let b; 100 | 0; 101 | a; 102 | `), 5); 103 | 104 | assert.equal(evaluator.evalString(` 105 | let a=5; 106 | a+=1; 107 | `), 6); 108 | 109 | assert.equal(evaluator.evalString(` 110 | let a=5; 111 | a*=2; 112 | `), 10); 113 | } 114 | 115 | function testVariableScope_ADD() { 116 | let evaluator = new Evaluator(); 117 | 118 | assert.equal(evaluator.evalString(` 119 | let a=1; 120 | { 121 | 0; 122 | a; 123 | } 124 | `), 1); 125 | 126 | assert.equal(evaluator.evalString(` 127 | let a=2; 128 | { 129 | let a=3; 130 | } 131 | a; 132 | `), 2); 133 | 134 | assert.equal(evaluator.evalString(` 135 | let a=4; 136 | { 137 | a=5; 138 | 0; 139 | } 140 | a; 141 | `), 5); 142 | } 143 | 144 | function testException_ADD() { 145 | let evaluator = new Evaluator(); 146 | 147 | try { 148 | evaluator.evalString(` 149 | let a; 150 | b; 151 | `); 152 | assert.fail(); 153 | 154 | } catch (err) { 155 | // 156 | } 157 | 158 | try { 159 | evaluator.evalString(` 160 | let a; 161 | let a; 162 | `); 163 | assert.fail(); 164 | 165 | } catch (err) { 166 | // 167 | } 168 | 169 | try { 170 | evaluator.evalString(` 171 | { 172 | let a=1; 173 | } 174 | a; 175 | `); 176 | assert.fail(); 177 | 178 | } catch (err) { 179 | // 180 | } 181 | } 182 | 183 | function testDeclarationStatement() { 184 | testSingleVariableDeclaration(); 185 | testMultipleVariableDeclaration(); 186 | testVariableDeclarationWithExpressionInitializer(); 187 | 188 | // ADD 189 | testReadVariable_ADD(); 190 | testAssignVariable_ADD(); 191 | testVariableScope_ADD(); 192 | testException_ADD(); 193 | 194 | console.log('testDeclarationStatement() passed.'); 195 | } 196 | 197 | export { testDeclarationStatement }; -------------------------------------------------------------------------------- /test/evaluator/test-equality.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testSimpleEqualityExpression() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | let x = true; // ADD 18 | x == true; 19 | `), true); 20 | 21 | // ADD 22 | assert.equal(evaluator.evalString(` 23 | let x = false; // ADD 24 | x == true; 25 | `), false); 26 | 27 | 28 | assert.equal(evaluator.evalString(` 29 | let y=0; // ADD 30 | y != 0; 31 | `), false); 32 | 33 | // ADD 34 | assert.equal(evaluator.evalString(` 35 | let y=2; // ADD 36 | y != 0; 37 | `), true); 38 | } 39 | 40 | function testComplexEqualityExpression() { 41 | let evaluator = new Evaluator(); 42 | 43 | assert.equal(evaluator.evalString(` 44 | let m = 2; // ADD 45 | let n = 1; // ADD 46 | m > n == false; 47 | `), false); 48 | 49 | // ADD 50 | assert.equal(evaluator.evalString(` 51 | let m = 2; // ADD 52 | let n = 3; // ADD 53 | m > n == false; 54 | `), true); 55 | 56 | assert.equal(evaluator.evalString(` 57 | let i = 0; // ADD 58 | let j = 0; // ADD 59 | i < 1 == j < 2; 60 | `), true); 61 | 62 | // ADD 63 | assert.equal(evaluator.evalString(` 64 | let i = 1; // ADD 65 | let j = 1; // ADD 66 | i < 1 == j < 2; 67 | `), false); 68 | 69 | // ADD 70 | assert.equal(evaluator.evalString(` 71 | let i = 2; // ADD 72 | let j = 2; // ADD 73 | i < 1 == j < 2; 74 | `), true); 75 | 76 | assert.equal(evaluator.evalString(` 77 | let p = null; // ADD 78 | let q = 2; // ADD 79 | p = q * 3 != 6; 80 | `), false); 81 | 82 | // ADD 83 | assert.equal(evaluator.evalString(` 84 | let p = null; // ADD 85 | let q = 3; // ADD 86 | p = q * 3 != 6; 87 | `), true); 88 | } 89 | 90 | function testEquality() { 91 | testSimpleEqualityExpression(); 92 | testComplexEqualityExpression(); 93 | 94 | console.log('testEquality() passed.'); 95 | } 96 | 97 | export { testEquality }; -------------------------------------------------------------------------------- /test/evaluator/test-function-declaration-and-function-call.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testFunctionDeclarationAndFunctionCall() { 14 | 15 | let evaluator = new Evaluator(); 16 | 17 | assert.equal(evaluator.evalString(` 18 | function double(x) { 19 | x*2; 20 | } 21 | double(3); 22 | `), 6); 23 | 24 | assert.equal(evaluator.evalString(` 25 | function add(m, n) { 26 | m+n; // ADD 27 | } 28 | add(2, 3); 29 | `), 5); 30 | 31 | assert.equal(evaluator.evalString(` 32 | function empty() { 33 | 34 | } 35 | empty(); 36 | `), null); 37 | 38 | console.log('testFunctionDeclarationAndFunctionCall() passed.'); 39 | } 40 | 41 | export { testFunctionDeclarationAndFunctionCall }; -------------------------------------------------------------------------------- /test/evaluator/test-if-statement.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testSimpleIfStatement() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | let a = 3; // ADD 18 | let x = true; // ADD 19 | if (x) a=0; 20 | a; // ADD 21 | `), 0); 22 | 23 | // ADD 24 | assert.equal(evaluator.evalString(` 25 | let a = 3; // ADD 26 | let x = false; // ADD 27 | if (x) a=0; 28 | a; // ADD 29 | `), 3); 30 | 31 | assert.equal(evaluator.evalString(` 32 | let a = 3; // ADD 33 | let x = true; // ADD 34 | if (x) 35 | a = 0; 36 | else 37 | a = 1; 38 | a; // ADD 39 | `), 0); 40 | 41 | // ADD 42 | assert.equal(evaluator.evalString(` 43 | let a = 3; // ADD 44 | let x = false; // ADD 45 | if (x) 46 | a = 0; 47 | else 48 | a = 1; 49 | a; // ADD 50 | `), 1); 51 | } 52 | 53 | function testIfStatementWithBlockStatement() { 54 | let evaluator = new Evaluator(); 55 | 56 | assert.equal(evaluator.evalString(` 57 | let a = 3; // ADD 58 | let x = true; // ADD 59 | if (x) { 60 | a=0; 61 | } 62 | a; // ADD 63 | `), 0); 64 | 65 | // ADD 66 | assert.equal(evaluator.evalString(` 67 | let a = 3; // ADD 68 | let x = false; // ADD 69 | if (x) { 70 | a=0; 71 | } 72 | a; // ADD 73 | `), 3); 74 | 75 | assert.equal(evaluator.evalString(` 76 | let a = 3; // ADD 77 | let x = false; // ADD 78 | if (x) { 79 | a=0; 80 | }else { 81 | a=1; 82 | } 83 | a; // ADD 84 | `), 1); 85 | 86 | // ADD 87 | assert.equal(evaluator.evalString(` 88 | let a = 3; // ADD 89 | let x = true; // ADD 90 | if (x) { 91 | a=0; 92 | }else { 93 | a=1; 94 | } 95 | a; // ADD 96 | `), 0); 97 | } 98 | 99 | function testCascadingIfStatement() { 100 | let evaluator = new Evaluator(); 101 | 102 | assert.equal(evaluator.evalString(` 103 | let x = true; // ADD 104 | let y = true; // ADD 105 | if (x) 106 | if (y) 107 | 2; 108 | else 109 | 8; 110 | `), 2); 111 | 112 | // ADD 113 | assert.equal(evaluator.evalString(` 114 | let x = true; // ADD 115 | let y = false; // ADD 116 | if (x) 117 | if (y) 118 | 2; 119 | else 120 | 8; 121 | `), 8); 122 | 123 | // ADD 124 | assert.equal(evaluator.evalString(` 125 | let x = false; // ADD 126 | let y = true; // ADD 127 | if (x) 128 | if (y) 129 | 2; 130 | else 131 | 8; 132 | `), null); 133 | 134 | // ADD 135 | assert.equal(evaluator.evalString(` 136 | let x = false; // ADD 137 | let y = false; // ADD 138 | if (x) 139 | if (y) 140 | 2; 141 | else 142 | 8; 143 | `), null); 144 | 145 | assert.equal(evaluator.evalString(` 146 | let p = true; // ADD 147 | let q = true; // ADD 148 | if (p){ 149 | 1; 150 | }else if (q) { 151 | 2; 152 | } 153 | `), 1); 154 | 155 | // ADD 156 | assert.equal(evaluator.evalString(` 157 | let p = true; // ADD 158 | let q = false; // ADD 159 | if (p){ 160 | 1; 161 | }else if (q) { 162 | 2; 163 | } 164 | `), 1); 165 | 166 | // ADD 167 | assert.equal(evaluator.evalString(` 168 | let p = false; // ADD 169 | let q = true; // ADD 170 | if (p){ 171 | 1; 172 | }else if (q) { 173 | 2; 174 | } 175 | `), 2); 176 | 177 | // ADD 178 | assert.equal(evaluator.evalString(` 179 | let p = false; // ADD 180 | let q = false; // ADD 181 | if (p){ 182 | 1; 183 | }else if (q) { 184 | 2; 185 | } 186 | `), null); 187 | 188 | assert.equal(evaluator.evalString(` 189 | let m = true; // ADD 190 | let n = true; // ADD 191 | if (m) if (n) {1;} else {2;} else {3;} 192 | `), 1); 193 | 194 | // ADD 195 | assert.equal(evaluator.evalString(` 196 | let m = true; // ADD 197 | let n = false; // ADD 198 | if (m) if (n) {1;} else {2;} else {3;} 199 | `), 2); 200 | 201 | // ADD 202 | assert.equal(evaluator.evalString(` 203 | let m = false; // ADD 204 | let n = true; // ADD 205 | if (m) if (n) {1;} else {2;} else {3;} 206 | `), 3); 207 | 208 | // ADD 209 | assert.equal(evaluator.evalString(` 210 | let m = false; // ADD 211 | let n = false; // ADD 212 | if (m) if (n) {1;} else {2;} else {3;} 213 | `), 3); 214 | } 215 | 216 | function testIfStatement() { 217 | testSimpleIfStatement(); 218 | testIfStatementWithBlockStatement(); 219 | testCascadingIfStatement(); 220 | 221 | console.log('testIfStatement() passed.'); 222 | } 223 | 224 | export { testIfStatement }; -------------------------------------------------------------------------------- /test/evaluator/test-iteration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testWhileStatement() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | let x = 20; // ADD 18 | while(x>10) { 19 | x -=1; 20 | } 21 | x; // ADD 22 | `), 10); 23 | } 24 | 25 | function testDoWhileStatement() { 26 | let evaluator = new Evaluator(); 27 | 28 | assert.equal(evaluator.evalString(` 29 | let x = 0; // ADD 30 | do{ 31 | x+=1; 32 | }while(x<10); 33 | x; // ADD 34 | `), 10); 35 | } 36 | 37 | function testForStatement() { 38 | let evaluator = new Evaluator(); 39 | 40 | assert.equal(evaluator.evalString(` 41 | let x = 0; // ADD 42 | for(let i =0;i<10;i+=1) { 43 | x+=i; 44 | } 45 | x; // ADD 46 | `), 45); 47 | } 48 | 49 | function testIteration() { 50 | testWhileStatement(); 51 | testDoWhileStatement(); 52 | testForStatement(); 53 | 54 | console.log('testIteration() passed.'); 55 | } 56 | 57 | export { testIteration }; -------------------------------------------------------------------------------- /test/evaluator/test-literal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testNumericLiteral() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | 123; 18 | `), 123); 19 | 20 | assert.equal(evaluator.evalString(` 21 | 2.718; 22 | `), 2.718); 23 | } 24 | 25 | function testStringLiteral() { 26 | let evaluator = new Evaluator(); 27 | 28 | assert.equal(evaluator.evalString(` 29 | "foo"; 30 | `), 'foo'); 31 | 32 | assert.equal(evaluator.evalString(` 33 | ""; 34 | `), ""); 35 | 36 | assert.equal(evaluator.evalString(` 37 | "foo bar"; 38 | `), 'foo bar'); 39 | 40 | assert.equal(evaluator.evalString(` 41 | "foo\\"bar"; 42 | `), 'foo\\"bar'); // todo:: escaped char 43 | } 44 | 45 | function testLiteral() { 46 | testNumericLiteral(); 47 | testStringLiteral(); 48 | 49 | console.log('testLiteral() passed.'); 50 | } 51 | 52 | export { testLiteral }; -------------------------------------------------------------------------------- /test/evaluator/test-logical.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testLogicalAnd() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | true && false; 18 | `), false); 19 | 20 | // ADD 21 | assert.equal(evaluator.evalString(` 22 | true && true; 23 | `), true); 24 | 25 | assert.equal(evaluator.evalString(` 26 | let x=0, y=0; // ADD 27 | x>0 && y != 0; 28 | `), false); 29 | 30 | // ADD 31 | assert.equal(evaluator.evalString(` 32 | let x=1, y=1; // ADD 33 | x>0 && y != 0; 34 | `), true); 35 | } 36 | 37 | function testLogicalOr() { 38 | let evaluator = new Evaluator(); 39 | 40 | assert.equal(evaluator.evalString(` 41 | let m=0, n=0, p=0, q=0; // ADD 42 | m > n || p == q; 43 | `), true); 44 | 45 | // ADD 46 | assert.equal(evaluator.evalString(` 47 | let m=1, n=0, p=1, q=0; // ADD 48 | m > n || p == q; 49 | `), true); 50 | 51 | // ADD 52 | assert.equal(evaluator.evalString(` 53 | let m=0, n=0, p=1, q=0; // ADD 54 | m > n || p == q; 55 | `), false); 56 | 57 | // ADD 58 | assert.equal(evaluator.evalString(` 59 | let m=0, n=0, p=1, q=0; // ADD 60 | m > n || p == q; 61 | `), false); 62 | } 63 | 64 | function testLogical() { 65 | testLogicalAnd(); 66 | testLogicalOr(); 67 | 68 | console.log('testLogical() passed.'); 69 | } 70 | 71 | export { testLogical }; -------------------------------------------------------------------------------- /test/evaluator/test-relational.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testSingleRelationalExpression() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | let x=0; // ADD 18 | x>0; 19 | `), false); 20 | 21 | // ADD 22 | assert.equal(evaluator.evalString(` 23 | let x=2; // ADD 24 | x>0; 25 | `), true); 26 | 27 | assert.equal(evaluator.evalString(` 28 | let y=1; // ADD 29 | y+1>9; 30 | `), false); 31 | 32 | // ADD 33 | assert.equal(evaluator.evalString(` 34 | let y=9; // ADD 35 | y+1>9; 36 | `), true); 37 | 38 | assert.equal(evaluator.evalString(` 39 | let i=1; // ADD 40 | let z=null; // ADD 41 | z=i>1; 42 | `), false); 43 | 44 | // ADD 45 | assert.equal(evaluator.evalString(` 46 | let i=2; // ADD 47 | let z=null; // ADD 48 | z=i>1; 49 | `), true); 50 | } 51 | 52 | function testRelationalExpressionWithinIfStatement() { 53 | let evaluator = new Evaluator(); 54 | 55 | assert.equal(evaluator.evalString(` 56 | let x = 0; // ADD 57 | if (x<=0){ 58 | 1; 59 | } 60 | `), 1); 61 | 62 | // ADD 63 | assert.equal(evaluator.evalString(` 64 | let x = 1; // ADD 65 | if (x<=0){ 66 | 1; 67 | } 68 | `), null); 69 | } 70 | 71 | function testRelational() { 72 | testSingleRelationalExpression(); 73 | testRelationalExpressionWithinIfStatement(); 74 | 75 | console.log('testRelational() passed.'); 76 | } 77 | 78 | export { testRelational }; -------------------------------------------------------------------------------- /test/evaluator/test-statement.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testMultiStatements() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | "hello"; 18 | 123; 19 | `), 123); 20 | } 21 | 22 | function testStatement() { 23 | testMultiStatements(); 24 | 25 | console.log('testStatement() passed.'); 26 | } 27 | 28 | export { testStatement }; -------------------------------------------------------------------------------- /test/evaluator/test-unary.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Evaluator } from "../../src/evaluator.js"; 12 | 13 | function testUnaryNot() { 14 | let evaluator = new Evaluator(); 15 | 16 | assert.equal(evaluator.evalString(` 17 | !false; 18 | `), true); 19 | 20 | assert.equal(evaluator.evalString(` 21 | let m=true; // ADD 22 | !m; 23 | `), false); 24 | 25 | assert.equal(evaluator.evalString(` 26 | let a=0, b=0; // ADD 27 | !(a>b); 28 | `), true); 29 | 30 | // ADD 31 | assert.equal(evaluator.evalString(` 32 | let a=1, b=0; // ADD 33 | !(a>b); 34 | `), false); 35 | } 36 | 37 | function testUnaryNegative() { 38 | let evaluator = new Evaluator(); 39 | 40 | assert.equal(evaluator.evalString(` 41 | let x =2; // ADD 42 | -x; 43 | `), -2); 44 | 45 | assert.equal(evaluator.evalString(` 46 | +123; 47 | `), 123); 48 | 49 | assert.equal(evaluator.evalString(` 50 | let y=5; // ADD 51 | -(y+10); 52 | `), -15); 53 | 54 | assert.equal(evaluator.evalString(` 55 | 5+-2; 56 | `), 3); 57 | } 58 | 59 | function testUnaryChain() { 60 | let evaluator = new Evaluator(); 61 | 62 | assert.equal(evaluator.evalString(` 63 | let x =2; // ADD 64 | !-x; 65 | `), false); 66 | 67 | assert.equal(evaluator.evalString(` 68 | !!false; 69 | `), false); 70 | } 71 | 72 | function testUnary() { 73 | testUnaryNot(); 74 | testUnaryNegative(); 75 | testUnaryChain(); 76 | 77 | console.log('testUnary() passed.'); 78 | } 79 | 80 | export { testUnary }; -------------------------------------------------------------------------------- /test/parser/test-assignment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testIdentifier() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | i; 19 | `), 20 | { 21 | "type": "Program", 22 | "body": [ 23 | { 24 | "type": "ExpressionStatement", 25 | "expression": { 26 | "type": "Identifier", 27 | "name": "i" 28 | } 29 | } 30 | ] 31 | } 32 | ); 33 | 34 | assert.deepEqual(parser.parse( 35 | ` 36 | x+1; 37 | `), 38 | { 39 | "type": "Program", 40 | "body": [ 41 | { 42 | "type": "ExpressionStatement", 43 | "expression": { 44 | "type": "BinaryExpression", 45 | "operator": "+", 46 | "left": { 47 | "type": "Identifier", 48 | "name": "x" 49 | }, 50 | "right": { 51 | "type": "NumericLiteral", 52 | "value": 1 53 | } 54 | } 55 | } 56 | ] 57 | } 58 | ); 59 | } 60 | 61 | function testSimpleAssignmentExpression() { 62 | let parser = new Parser(); 63 | 64 | assert.deepEqual(parser.parse( 65 | ` 66 | x=1; 67 | `), 68 | { 69 | "type": "Program", 70 | "body": [ 71 | { 72 | "type": "ExpressionStatement", 73 | "expression": { 74 | "type": "AssignmentExpression", 75 | "operator": "=", 76 | "left": { 77 | "type": "Identifier", 78 | "name": "x" 79 | }, 80 | "right": { 81 | "type": "NumericLiteral", 82 | "value": 1 83 | } 84 | } 85 | } 86 | ] 87 | } 88 | ); 89 | 90 | assert.deepEqual(parser.parse( 91 | ` 92 | x =1+2; 93 | `), 94 | { 95 | "type": "Program", 96 | "body": [ 97 | { 98 | "type": "ExpressionStatement", 99 | "expression": { 100 | "type": "AssignmentExpression", 101 | "operator": "=", 102 | "left": { 103 | "type": "Identifier", 104 | "name": "x" 105 | }, 106 | "right": { 107 | "type": "BinaryExpression", 108 | "operator": "+", 109 | "left": { 110 | "type": "NumericLiteral", 111 | "value": 1 112 | }, 113 | "right": { 114 | "type": "NumericLiteral", 115 | "value": 2 116 | } 117 | } 118 | } 119 | } 120 | 121 | ] 122 | } 123 | ); 124 | } 125 | 126 | function testComplexAssignmentExpression() { 127 | let parser = new Parser(); 128 | 129 | assert.deepEqual(parser.parse( 130 | ` 131 | x+=1; 132 | `), 133 | { 134 | "type": "Program", 135 | "body": [ 136 | { 137 | "type": "ExpressionStatement", 138 | "expression": { 139 | "type": "AssignmentExpression", 140 | "operator": "+=", 141 | "left": { 142 | "type": "Identifier", 143 | "name": "x" 144 | }, 145 | "right": { 146 | "type": "NumericLiteral", 147 | "value": 1 148 | } 149 | } 150 | } 151 | ] 152 | } 153 | ); 154 | 155 | assert.deepEqual(parser.parse( 156 | ` 157 | x *= i+2; 158 | `), 159 | { 160 | "type": "Program", 161 | "body": [ 162 | { 163 | "type": "ExpressionStatement", 164 | "expression": { 165 | "type": "AssignmentExpression", 166 | "operator": "*=", 167 | "left": { 168 | "type": "Identifier", 169 | "name": "x" 170 | }, 171 | "right": { 172 | "type": "BinaryExpression", 173 | "operator": "+", 174 | "left": { 175 | "type": "Identifier", 176 | "name": "i" 177 | }, 178 | "right": { 179 | "type": "NumericLiteral", 180 | "value": 2 181 | } 182 | } 183 | } 184 | } 185 | ] 186 | } 187 | ); 188 | } 189 | 190 | function testChainAssignmentExpression() { 191 | let parser = new Parser(); 192 | 193 | assert.deepEqual(parser.parse( 194 | ` 195 | x=y=1; 196 | `), 197 | { 198 | "type": "Program", 199 | "body": [ 200 | { 201 | "type": "ExpressionStatement", 202 | "expression": { 203 | "type": "AssignmentExpression", 204 | "operator": "=", 205 | "left": { 206 | "type": "Identifier", 207 | "name": "x" 208 | }, 209 | "right": { 210 | "type": "AssignmentExpression", 211 | "operator": "=", 212 | "left": { 213 | "type": "Identifier", 214 | "name": "y" 215 | }, 216 | "right": { 217 | "type": "NumericLiteral", 218 | "value": 1 219 | } 220 | } 221 | } 222 | } 223 | ] 224 | } 225 | ); 226 | } 227 | 228 | function testAssignmentExpression() { 229 | testIdentifier(); 230 | testSimpleAssignmentExpression(); 231 | testComplexAssignmentExpression(); 232 | testChainAssignmentExpression(); 233 | 234 | console.log('testAssignmentExpression() passed.'); 235 | } 236 | 237 | export { testAssignmentExpression }; -------------------------------------------------------------------------------- /test/parser/test-binaryexpression.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testAdditiveExpression() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | 1+2; 19 | `), 20 | { 21 | "type": "Program", 22 | "body": [ 23 | { 24 | "type": "ExpressionStatement", 25 | "expression": { 26 | "type": "BinaryExpression", 27 | "operator": "+", 28 | "left": { 29 | "type": "NumericLiteral", 30 | "value": 1 31 | }, 32 | "right": { 33 | "type": "NumericLiteral", 34 | "value": 2 35 | } 36 | } 37 | } 38 | ] 39 | } 40 | ); 41 | 42 | assert.deepEqual(parser.parse( 43 | ` 44 | 1+2+3; 45 | `), 46 | { 47 | "type": "Program", 48 | "body": [ 49 | { 50 | "type": "ExpressionStatement", 51 | "expression": { 52 | "type": "BinaryExpression", 53 | "operator": "+", 54 | "left": { 55 | "type": "BinaryExpression", 56 | "operator": "+", 57 | "left": { 58 | "type": "NumericLiteral", 59 | "value": 1 60 | }, 61 | "right": { 62 | "type": "NumericLiteral", 63 | "value": 2 64 | } 65 | }, 66 | "right": { 67 | "type": "NumericLiteral", 68 | "value": 3 69 | } 70 | } 71 | } 72 | ] 73 | } 74 | ); 75 | } 76 | 77 | function testMultiplicativeExpression() { 78 | let parser = new Parser(); 79 | 80 | assert.deepEqual(parser.parse( 81 | ` 82 | 1*2; 83 | `), 84 | { 85 | "type": "Program", 86 | "body": [ 87 | { 88 | "type": "ExpressionStatement", 89 | "expression": { 90 | "type": "BinaryExpression", 91 | "operator": "*", 92 | "left": { 93 | "type": "NumericLiteral", 94 | "value": 1 95 | }, 96 | "right": { 97 | "type": "NumericLiteral", 98 | "value": 2 99 | } 100 | } 101 | } 102 | ] 103 | } 104 | ); 105 | 106 | assert.deepEqual(parser.parse( 107 | ` 108 | 1+2*3; 109 | `), 110 | { 111 | "type": "Program", 112 | "body": [ 113 | { 114 | "type": "ExpressionStatement", 115 | "expression": { 116 | "type": "BinaryExpression", 117 | "operator": "+", 118 | "left": { 119 | "type": "NumericLiteral", 120 | "value": 1 121 | }, 122 | "right": { 123 | "type": "BinaryExpression", 124 | "operator": "*", 125 | "left": { 126 | "type": "NumericLiteral", 127 | "value": 2 128 | }, 129 | "right": { 130 | "type": "NumericLiteral", 131 | "value": 3 132 | } 133 | }, 134 | } 135 | } 136 | ] 137 | } 138 | ); 139 | } 140 | 141 | function testParenthesizedExpression() { 142 | let parser = new Parser(); 143 | 144 | assert.deepEqual(parser.parse( 145 | ` 146 | (1+2)*3; 147 | `), 148 | { 149 | "type": "Program", 150 | "body": [ 151 | { 152 | "type": "ExpressionStatement", 153 | "expression": { 154 | "type": "BinaryExpression", 155 | "operator": "*", 156 | "left": { 157 | "type": "BinaryExpression", 158 | "operator": "+", 159 | "left": { 160 | "type": "NumericLiteral", 161 | "value": 1 162 | }, 163 | "right": { 164 | "type": "NumericLiteral", 165 | "value": 2 166 | } 167 | }, 168 | "right": { 169 | "type": "NumericLiteral", 170 | "value": 3 171 | }, 172 | } 173 | } 174 | ] 175 | } 176 | ); 177 | 178 | assert.deepEqual(parser.parse( 179 | ` 180 | 8*(2+3)+4; 181 | `), 182 | { 183 | "type": "Program", 184 | "body": [ 185 | { 186 | "type": "ExpressionStatement", 187 | "expression": { 188 | type: "BinaryExpression", 189 | operator: '+', 190 | left: { 191 | "type": "BinaryExpression", 192 | "operator": "*", 193 | "left": { 194 | "type": "NumericLiteral", 195 | "value": 8 196 | }, 197 | "right": { 198 | "type": "BinaryExpression", 199 | "operator": "+", 200 | "left": { 201 | "type": "NumericLiteral", 202 | "value": 2 203 | }, 204 | "right": { 205 | "type": "NumericLiteral", 206 | "value": 3 207 | } 208 | }, 209 | 210 | }, 211 | right: { 212 | type: 'NumericLiteral', 213 | value: 4 214 | } 215 | } 216 | } 217 | ] 218 | } 219 | ); 220 | } 221 | 222 | function testBinaryExpression() { 223 | testAdditiveExpression(); 224 | testMultiplicativeExpression(); 225 | testParenthesizedExpression(); 226 | 227 | console.log('testBinaryExpression() passed.'); 228 | } 229 | 230 | export { testBinaryExpression }; -------------------------------------------------------------------------------- /test/parser/test-blockstatement.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testSingleBlockStatement() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | { 19 | 123; 20 | "foo"; 21 | } 22 | `), { 23 | type: 'Program', 24 | body: [{ 25 | type: 'BlockStatement', 26 | body: [{ 27 | type: 'ExpressionStatement', 28 | expression: { 29 | type: 'NumericLiteral', 30 | value: 123 31 | } 32 | }, { 33 | type: 'ExpressionStatement', 34 | expression: { 35 | type: 'StringLiteral', 36 | value: "foo" 37 | } 38 | }] 39 | }] 40 | }); 41 | } 42 | 43 | function testMultiBlockStatements() { 44 | let parser = new Parser(); 45 | 46 | assert.deepEqual(parser.parse( 47 | ` 48 | { 49 | "foo"; 50 | } 51 | 123; 52 | { 53 | "bar"; 54 | } 55 | `), { 56 | type: 'Program', 57 | body: [{ 58 | type: 'BlockStatement', 59 | body: [{ 60 | type: 'ExpressionStatement', 61 | expression: { 62 | type: 'StringLiteral', 63 | value: "foo" 64 | } 65 | }] 66 | }, { 67 | type: 'ExpressionStatement', 68 | expression: { 69 | type: 'NumericLiteral', 70 | value: 123 71 | } 72 | }, { 73 | type: 'BlockStatement', 74 | body: [{ 75 | type: 'ExpressionStatement', 76 | expression: { 77 | type: 'StringLiteral', 78 | value: "bar" 79 | } 80 | }] 81 | }] 82 | }); 83 | } 84 | 85 | function testNestedBlockStatements() { 86 | let parser = new Parser(); 87 | 88 | assert.deepEqual(parser.parse( 89 | ` 90 | { 91 | "foo"; 92 | { 93 | 123; 94 | } 95 | } 96 | `), { 97 | type: 'Program', 98 | body: [{ 99 | type: 'BlockStatement', 100 | body: [{ 101 | type: 'ExpressionStatement', 102 | expression: { 103 | type: 'StringLiteral', 104 | value: "foo" 105 | } 106 | }, 107 | { 108 | type: 'BlockStatement', 109 | body: [{ 110 | type: 'ExpressionStatement', 111 | expression: { 112 | type: 'NumericLiteral', 113 | value: 123 114 | } 115 | }] 116 | }] 117 | }] 118 | }); 119 | } 120 | 121 | function testEmptyStatement() { 122 | let parser = new Parser(); 123 | 124 | assert.deepEqual(parser.parse( 125 | ` 126 | 123; 127 | ; 128 | `), { 129 | type: 'Program', 130 | body: [ 131 | { 132 | type: 'ExpressionStatement', 133 | expression: 134 | { 135 | type: 'NumericLiteral', 136 | value: 123 137 | } 138 | }, 139 | { 140 | type: 'EmptyStatement' 141 | } 142 | ] 143 | }); 144 | } 145 | 146 | function testEmptyBlockStatement() { 147 | let parser = new Parser(); 148 | 149 | assert.deepEqual(parser.parse( 150 | ` 151 | {} 152 | `), { 153 | type: 'Program', 154 | body: [ 155 | { 156 | type: 'BlockStatement', 157 | body: [] 158 | } 159 | ] 160 | }); 161 | } 162 | 163 | function testBlockStatement() { 164 | testSingleBlockStatement(); 165 | testMultiBlockStatements(); 166 | testNestedBlockStatements(); 167 | 168 | testEmptyStatement(); 169 | testEmptyBlockStatement(); 170 | 171 | console.log('testBlockStatement() passed.'); 172 | } 173 | 174 | export { testBlockStatement }; -------------------------------------------------------------------------------- /test/parser/test-class-declaration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testSimpleClassDeclaration() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | class Animal { 19 | 20 | } 21 | `), 22 | { 23 | "type": "Program", 24 | "body": [ 25 | { 26 | "type": "ClassDeclaration", 27 | "id": { 28 | "type": "Identifier", 29 | "name": "Animal" 30 | }, 31 | "superClass": null, 32 | "body": { 33 | "type": "BlockStatement", 34 | "body": [] 35 | } 36 | } 37 | ] 38 | } 39 | ); 40 | 41 | assert.deepEqual(parser.parse( 42 | ` 43 | class Point { 44 | constructor(x,y) { 45 | this.x = x; 46 | this.y = y; 47 | } 48 | 49 | function calc() { 50 | return 2; 51 | } 52 | } 53 | `), 54 | { 55 | "type": "Program", 56 | "body": [ 57 | { 58 | "type": "ClassDeclaration", 59 | "id": { 60 | "type": "Identifier", 61 | "name": "Point" 62 | }, 63 | "superClass": null, 64 | "body": { 65 | "type": "BlockStatement", 66 | "body": [ 67 | { 68 | "type": "Constructor", 69 | // "name": { 70 | // "type": "Identifier", 71 | // "name": "constructor" 72 | // }, 73 | "params": [ 74 | { 75 | "type": "Identifier", 76 | "name": "x" 77 | }, 78 | { 79 | "type": "Identifier", 80 | "name": "y" 81 | } 82 | ], 83 | "body": { 84 | "type": "BlockStatement", 85 | "body": [ 86 | { 87 | "type": "ExpressionStatement", 88 | "expression": { 89 | "type": "AssignmentExpression", 90 | "operator": "=", 91 | "left": { 92 | "type": "MemberExpression", 93 | "computed": false, 94 | "object": { 95 | "type": "Identifier", 96 | "name": "this" 97 | }, 98 | "property": { 99 | "type": "Identifier", 100 | "name": "x" 101 | } 102 | }, 103 | "right": { 104 | "type": "Identifier", 105 | "name": "x" 106 | } 107 | } 108 | }, 109 | { 110 | "type": "ExpressionStatement", 111 | "expression": { 112 | "type": "AssignmentExpression", 113 | "operator": "=", 114 | "left": { 115 | "type": "MemberExpression", 116 | "computed": false, 117 | "object": { 118 | "type": "Identifier", 119 | "name": "this" 120 | }, 121 | "property": { 122 | "type": "Identifier", 123 | "name": "y" 124 | } 125 | }, 126 | "right": { 127 | "type": "Identifier", 128 | "name": "y" 129 | } 130 | } 131 | } 132 | ] 133 | } 134 | }, 135 | { 136 | "type": "FunctionDeclaration", 137 | "name": { 138 | "type": "Identifier", 139 | "name": "calc" 140 | }, 141 | "params": [], 142 | "body": { 143 | "type": "BlockStatement", 144 | "body": [ 145 | { 146 | "type": "ReturnStatement", 147 | "argument": { 148 | "type": "NumericLiteral", 149 | "value": 2 150 | } 151 | } 152 | ] 153 | } 154 | } 155 | ] 156 | } 157 | } 158 | ] 159 | } 160 | ); 161 | } 162 | 163 | function testInheritedClassDeclaration() { 164 | let parser = new Parser(); 165 | 166 | assert.deepEqual(parser.parse( 167 | ` 168 | class Cat extends Animal{ 169 | 170 | } 171 | `), 172 | { 173 | "type": "Program", 174 | "body": [ 175 | { 176 | "type": "ClassDeclaration", 177 | "id": { 178 | "type": "Identifier", 179 | "name": "Cat" 180 | }, 181 | "superClass": { 182 | "type": "Identifier", 183 | "name": "Animal" 184 | }, 185 | "body": { 186 | "type": "BlockStatement", 187 | "body": [] 188 | } 189 | } 190 | ] 191 | } 192 | ); 193 | 194 | assert.deepEqual(parser.parse( 195 | ` 196 | class Point3D extends Point { 197 | constructor(x,y,z) { 198 | super(x,y); 199 | } 200 | 201 | function calc() { 202 | return super.calc() + 1; 203 | } 204 | } 205 | `), 206 | 207 | { 208 | "type": "Program", 209 | "body": [ 210 | { 211 | "type": "ClassDeclaration", 212 | "id": { 213 | "type": "Identifier", 214 | "name": "Point3D" 215 | }, 216 | "superClass": { 217 | "type": "Identifier", 218 | "name": "Point" 219 | }, 220 | "body": { 221 | "type": "BlockStatement", 222 | "body": [ 223 | { 224 | "type": "Constructor", 225 | // "name": { 226 | // "type": "Identifier", 227 | // "name": "constructor" 228 | // }, 229 | "params": [ 230 | { 231 | "type": "Identifier", 232 | "name": "x" 233 | }, 234 | { 235 | "type": "Identifier", 236 | "name": "y" 237 | }, 238 | { 239 | "type": "Identifier", 240 | "name": "z" 241 | } 242 | ], 243 | "body": { 244 | "type": "BlockStatement", 245 | "body": [ 246 | { 247 | "type": "ExpressionStatement", 248 | "expression": { 249 | "type": "CallExpression", 250 | "callee": { 251 | "type": "Identifier", 252 | "name": "super" 253 | }, 254 | "arguments": [ 255 | { 256 | "type": "Identifier", 257 | "name": "x" 258 | }, 259 | { 260 | "type": "Identifier", 261 | "name": "y" 262 | } 263 | ] 264 | } 265 | } 266 | ] 267 | } 268 | }, 269 | { 270 | "type": "FunctionDeclaration", 271 | "name": { 272 | "type": "Identifier", 273 | "name": "calc" 274 | }, 275 | "params": [], 276 | "body": { 277 | "type": "BlockStatement", 278 | "body": [ 279 | { 280 | "type": "ReturnStatement", 281 | "argument": { 282 | "type": "BinaryExpression", 283 | "operator": "+", 284 | "left": { 285 | "type": "CallExpression", 286 | "callee": { 287 | "type": "MemberExpression", 288 | "computed": false, 289 | "object": { 290 | "type": "Identifier", 291 | "name": "super" 292 | }, 293 | "property": { 294 | "type": "Identifier", 295 | "name": "calc" 296 | } 297 | }, 298 | "arguments": [] 299 | }, 300 | "right": { 301 | "type": "NumericLiteral", 302 | "value": 1 303 | } 304 | } 305 | } 306 | ] 307 | } 308 | } 309 | ] 310 | } 311 | } 312 | ] 313 | } 314 | ); 315 | } 316 | 317 | function testClassDeclaration() { 318 | testSimpleClassDeclaration(); 319 | testInheritedClassDeclaration(); 320 | 321 | console.log('testClassDeclaration() passed.'); 322 | } 323 | 324 | export { testClassDeclaration }; -------------------------------------------------------------------------------- /test/parser/test-comment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testSingleLineComment() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | // comment 1 19 | 555; // comment 2 20 | `), { 21 | type: 'Program', 22 | body: [{ 23 | type: 'ExpressionStatement', 24 | expression: { 25 | type: 'NumericLiteral', 26 | value: 555 27 | } 28 | }] 29 | }); 30 | } 31 | 32 | function testMultiLineComment() { 33 | let parser = new Parser(); 34 | 35 | assert.deepEqual(parser.parse( 36 | ` 37 | /** 38 | * comment 39 | */ 40 | 555 /* "also comment" */ ; 41 | `), { 42 | type: 'Program', 43 | body: [{ 44 | type: 'ExpressionStatement', 45 | expression: { 46 | type: 'NumericLiteral', 47 | value: 555 48 | } 49 | }] 50 | }); 51 | } 52 | 53 | function testComment() { 54 | testSingleLineComment(); 55 | testMultiLineComment(); 56 | 57 | console.log('testComment() passed.'); 58 | } 59 | 60 | export { testComment }; -------------------------------------------------------------------------------- /test/parser/test-declaration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testSingleVariableDeclaration() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | let x; 19 | `), 20 | { 21 | "type": "Program", 22 | "body": [ 23 | { 24 | "type": "VariableStatement", 25 | "declarations": [ 26 | { 27 | type: 'VariableDeclaration', 28 | id: { 29 | type: 'Identifier', 30 | name: 'x' 31 | }, 32 | init: null 33 | } 34 | ] 35 | } 36 | ] 37 | } 38 | ); 39 | 40 | assert.deepEqual(parser.parse( 41 | ` 42 | let y = 1; 43 | `), 44 | { 45 | "type": "Program", 46 | "body": [ 47 | { 48 | "type": "VariableStatement", 49 | "declarations": [ 50 | { 51 | type: 'VariableDeclaration', 52 | id: { 53 | type: 'Identifier', 54 | name: 'y' 55 | }, 56 | init: { 57 | type: 'NumericLiteral', 58 | value: 1 59 | } 60 | } 61 | ] 62 | } 63 | ] 64 | } 65 | ); 66 | } 67 | 68 | function testMultipleVariableDeclaration() { 69 | let parser = new Parser(); 70 | 71 | assert.deepEqual(parser.parse( 72 | ` 73 | let a, b; 74 | `), 75 | { 76 | "type": "Program", 77 | "body": [ 78 | { 79 | "type": "VariableStatement", 80 | "declarations": [ 81 | { 82 | type: 'VariableDeclaration', 83 | id: { 84 | type: 'Identifier', 85 | name: 'a' 86 | }, 87 | init: null 88 | }, 89 | { 90 | type: 'VariableDeclaration', 91 | id: { 92 | type: 'Identifier', 93 | name: 'b' 94 | }, 95 | init: null 96 | } 97 | ] 98 | } 99 | ] 100 | } 101 | ); 102 | 103 | assert.deepEqual(parser.parse( 104 | ` 105 | let m, n=2; 106 | `), 107 | { 108 | "type": "Program", 109 | "body": [ 110 | { 111 | "type": "VariableStatement", 112 | "declarations": [ 113 | { 114 | type: 'VariableDeclaration', 115 | id: { 116 | type: 'Identifier', 117 | name: 'm' 118 | }, 119 | init: null 120 | }, 121 | { 122 | type: 'VariableDeclaration', 123 | id: { 124 | type: 'Identifier', 125 | name: 'n' 126 | }, 127 | init: { 128 | type: 'NumericLiteral', 129 | value: 2 130 | } 131 | } 132 | ] 133 | } 134 | ] 135 | } 136 | ); 137 | } 138 | 139 | function testVariableDeclarationWithExpressionInitializer() { 140 | let parser = new Parser(); 141 | 142 | assert.deepEqual(parser.parse( 143 | ` 144 | let z=x+1; 145 | `), 146 | { 147 | "type": "Program", 148 | "body": [ 149 | { 150 | "type": "VariableStatement", 151 | "declarations": [ 152 | { 153 | "type": "VariableDeclaration", 154 | "id": { 155 | "type": "Identifier", 156 | "name": "z" 157 | }, 158 | "init": { 159 | "type": "BinaryExpression", 160 | "operator": "+", 161 | "left": { 162 | "type": "Identifier", 163 | "name": "x" 164 | }, 165 | "right": { 166 | "type": "NumericLiteral", 167 | "value": 1 168 | } 169 | } 170 | } 171 | ] 172 | } 173 | ] 174 | } 175 | ); 176 | 177 | } 178 | 179 | function testDeclarationStatement() { 180 | testSingleVariableDeclaration(); 181 | testMultipleVariableDeclaration(); 182 | testVariableDeclarationWithExpressionInitializer(); 183 | 184 | console.log('testDeclarationStatement() passed.'); 185 | } 186 | 187 | export { testDeclarationStatement }; -------------------------------------------------------------------------------- /test/parser/test-equality.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testSimpleEqualityExpression() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | x == true; 19 | `), 20 | { 21 | "type": "Program", 22 | "body": [ 23 | { 24 | "type": "ExpressionStatement", 25 | "expression": { 26 | "type": "BinaryExpression", 27 | "operator": "==", 28 | "left": { 29 | "type": "Identifier", 30 | "name": "x" 31 | }, 32 | "right": { 33 | "type": "BooleanLiteral", 34 | "value": true 35 | } 36 | } 37 | } 38 | ] 39 | } 40 | ); 41 | 42 | assert.deepEqual(parser.parse( 43 | ` 44 | y != 0; 45 | `), 46 | { 47 | "type": "Program", 48 | "body": [ 49 | { 50 | "type": "ExpressionStatement", 51 | "expression": { 52 | "type": "BinaryExpression", 53 | "operator": "!=", 54 | "left": { 55 | "type": "Identifier", 56 | "name": "y" 57 | }, 58 | "right": { 59 | "type": "NumericLiteral", 60 | "value": 0 61 | } 62 | } 63 | } 64 | ] 65 | } 66 | ); 67 | } 68 | 69 | function testComplexEqualityExpression() { 70 | let parser = new Parser(); 71 | 72 | assert.deepEqual(parser.parse( 73 | ` 74 | m > n == false; 75 | `), 76 | { 77 | "type": "Program", 78 | "body": [ 79 | { 80 | "type": "ExpressionStatement", 81 | "expression": { 82 | "type": "BinaryExpression", 83 | "operator": "==", 84 | "left": { 85 | "type": "BinaryExpression", 86 | "operator": ">", 87 | "left": { 88 | "type": "Identifier", 89 | "name": "m" 90 | }, 91 | "right": { 92 | "type": "Identifier", 93 | "name": "n" 94 | } 95 | }, 96 | "right": { 97 | "type": "BooleanLiteral", 98 | "value": false 99 | } 100 | } 101 | } 102 | ] 103 | } 104 | ); 105 | 106 | assert.deepEqual(parser.parse( 107 | ` 108 | i < 1 == j < 2; 109 | `), 110 | { 111 | "type": "Program", 112 | "body": [ 113 | { 114 | "type": "ExpressionStatement", 115 | "expression": { 116 | "type": "BinaryExpression", 117 | "operator": "==", 118 | "left": { 119 | "type": "BinaryExpression", 120 | "operator": "<", 121 | "left": { 122 | "type": "Identifier", 123 | "name": "i" 124 | }, 125 | "right": { 126 | "type": "NumericLiteral", 127 | "value": 1 128 | } 129 | }, 130 | "right": { 131 | "type": "BinaryExpression", 132 | "operator": "<", 133 | "left": { 134 | "type": "Identifier", 135 | "name": "j" 136 | }, 137 | "right": { 138 | "type": "NumericLiteral", 139 | "value": 2 140 | } 141 | } 142 | } 143 | } 144 | ] 145 | } 146 | ); 147 | 148 | assert.deepEqual(parser.parse( 149 | ` 150 | p = q * 3 != 6; 151 | `), 152 | { 153 | "type": "Program", 154 | "body": [ 155 | { 156 | "type": "ExpressionStatement", 157 | "expression": { 158 | "type": "AssignmentExpression", 159 | "operator": "=", 160 | "left": { 161 | "type": "Identifier", 162 | "name": "p" 163 | }, 164 | "right": { 165 | "type": "BinaryExpression", 166 | "operator": "!=", 167 | "left": { 168 | "type": "BinaryExpression", 169 | "operator": "*", 170 | "left": { 171 | "type": "Identifier", 172 | "name": "q" 173 | }, 174 | "right": { 175 | "type": "NumericLiteral", 176 | "value": 3 177 | } 178 | }, 179 | "right": { 180 | "type": "NumericLiteral", 181 | "value": 6 182 | } 183 | } 184 | } 185 | } 186 | ] 187 | } 188 | ); 189 | } 190 | 191 | function testEquality() { 192 | testSimpleEqualityExpression(); 193 | testComplexEqualityExpression(); 194 | 195 | console.log('testEquality() passed.'); 196 | } 197 | 198 | export { testEquality }; -------------------------------------------------------------------------------- /test/parser/test-function-call.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testSimpleFunctionCall() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | nop(); 19 | `), 20 | { 21 | "type": "Program", 22 | "body": [ 23 | { 24 | "type": "ExpressionStatement", 25 | expression: { 26 | type: 'CallExpression', 27 | callee: { // callee 即 “被调用者” 28 | type: 'Identifier', 29 | name: 'nop' 30 | }, 31 | arguments: [ 32 | // 33 | ] 34 | } 35 | } 36 | ] 37 | } 38 | ); 39 | 40 | assert.deepEqual(parser.parse( 41 | ` 42 | print(s, 1); 43 | `), 44 | { 45 | "type": "Program", 46 | "body": [ 47 | { 48 | "type": "ExpressionStatement", 49 | expression: { 50 | type: 'CallExpression', 51 | callee: { // callee 即 “被调用者” 52 | type: 'Identifier', 53 | name: 'print' 54 | }, 55 | arguments: [ 56 | { 57 | type: 'Identifier', 58 | name: 's' 59 | }, { 60 | type: 'NumericLiteral', 61 | value: 1 62 | } 63 | ] 64 | } 65 | } 66 | ] 67 | } 68 | ); 69 | } 70 | 71 | function testComplexFunctionCall() { 72 | let parser = new Parser(); 73 | 74 | assert.deepEqual(parser.parse( 75 | ` 76 | console.log(a[1]); 77 | `), 78 | { 79 | "type": "Program", 80 | "body": [ 81 | { 82 | "type": "ExpressionStatement", 83 | expression: { 84 | type: 'CallExpression', 85 | callee: { // callee 即 “被调用者” 86 | type: 'MemberExpression', 87 | computed: false, 88 | object: { 89 | type: 'Identifier', 90 | name: 'console' 91 | }, 92 | property: { 93 | type: 'Identifier', 94 | name: 'log' 95 | } 96 | }, 97 | arguments: [ 98 | { 99 | type: 'MemberExpression', 100 | computed: true, 101 | object: { 102 | type: 'Identifier', 103 | name: 'a' 104 | }, 105 | property: { 106 | type: 'NumericLiteral', 107 | value: 1 108 | } 109 | } 110 | ] 111 | } 112 | } 113 | ] 114 | } 115 | ); 116 | 117 | assert.deepEqual(parser.parse( 118 | ` 119 | user.name.slice(0); 120 | `), 121 | { 122 | "type": "Program", 123 | "body": [ 124 | { 125 | "type": "ExpressionStatement", 126 | "expression": { 127 | "type": "CallExpression", 128 | "callee": { 129 | "type": "MemberExpression", 130 | "computed": false, 131 | "object": { 132 | "type": "MemberExpression", 133 | "computed": false, 134 | "object": { 135 | "type": "Identifier", 136 | "name": "user" 137 | }, 138 | "property": { 139 | "type": "Identifier", 140 | "name": "name" 141 | } 142 | }, 143 | "property": { 144 | "type": "Identifier", 145 | "name": "slice" 146 | } 147 | }, 148 | "arguments": [ 149 | { 150 | "type": "NumericLiteral", 151 | "value": 0 152 | } 153 | ] 154 | } 155 | } 156 | ] 157 | } 158 | ); 159 | 160 | assert.deepEqual(parser.parse( 161 | ` 162 | print(add(1,2)); 163 | `), 164 | { 165 | "type": "Program", 166 | "body": [ 167 | { 168 | "type": "ExpressionStatement", 169 | expression: { 170 | type: 'CallExpression', 171 | callee: { // callee 即 “被调用者” 172 | type: 'Identifier', 173 | name: 'print' 174 | }, 175 | arguments: [ 176 | { 177 | type: 'CallExpression', 178 | callee: { 179 | type: 'Identifier', 180 | name: 'add' 181 | }, 182 | arguments: [ 183 | { 184 | type: 'NumericLiteral', 185 | value: 1 186 | }, 187 | { 188 | type: 'NumericLiteral', 189 | value: 2 190 | } 191 | ] 192 | } 193 | ] 194 | } 195 | } 196 | ] 197 | } 198 | ); 199 | 200 | assert.deepEqual(parser.parse( 201 | ` 202 | inc(1)(2); 203 | `), 204 | { 205 | "type": "Program", 206 | "body": [ 207 | { 208 | "type": "ExpressionStatement", 209 | expression: { 210 | type: 'CallExpression', 211 | callee: { 212 | type: 'CallExpression', 213 | callee: { // callee 即 “被调用者” 214 | type: 'Identifier', 215 | name: 'inc' 216 | }, 217 | arguments: [{ 218 | type: 'NumericLiteral', 219 | value: 1 220 | }] 221 | }, 222 | arguments: [{ 223 | type: 'NumericLiteral', 224 | value: 2 225 | }] 226 | } 227 | } 228 | ] 229 | } 230 | ); 231 | } 232 | 233 | function testFunctionCall() { 234 | testSimpleFunctionCall(); 235 | testComplexFunctionCall(); 236 | 237 | console.log('testFunctionCall() passed.'); 238 | } 239 | 240 | export { testFunctionCall }; -------------------------------------------------------------------------------- /test/parser/test-function-declaration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testFunctionDeclaration() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | function double(x) { 19 | return x*2; 20 | } 21 | `), 22 | { 23 | "type": "Program", 24 | "body": [ 25 | { 26 | "type": "FunctionDeclaration", 27 | "name": { 28 | type: 'Identifier', 29 | name: 'double' 30 | }, 31 | params: [ 32 | { 33 | type: 'Identifier', 34 | name: 'x' 35 | } 36 | ], 37 | body: { 38 | type: 'BlockStatement', 39 | body: [{ 40 | type: 'ReturnStatement', 41 | argument: { 42 | "type": "BinaryExpression", 43 | "operator": "*", 44 | left: { 45 | type: 'Identifier', 46 | name: 'x' 47 | }, 48 | right: { 49 | type: 'NumericLiteral', 50 | value: 2 51 | } 52 | } 53 | }] 54 | } 55 | } 56 | ] 57 | } 58 | ); 59 | 60 | assert.deepEqual(parser.parse( 61 | ` 62 | function add(m,n) { 63 | return; 64 | } 65 | `), 66 | { 67 | "type": "Program", 68 | "body": [ 69 | { 70 | "type": "FunctionDeclaration", 71 | "name": { 72 | type: 'Identifier', 73 | name: 'add' 74 | }, 75 | params: [ 76 | { 77 | type: 'Identifier', 78 | name: 'm' 79 | }, 80 | { 81 | type: 'Identifier', 82 | name: 'n' 83 | } 84 | ], 85 | body: { 86 | type: 'BlockStatement', 87 | body: [{ 88 | type: 'ReturnStatement', 89 | argument: null 90 | }] 91 | } 92 | } 93 | ] 94 | } 95 | ); 96 | 97 | assert.deepEqual(parser.parse( 98 | ` 99 | function empty() { 100 | // 101 | } 102 | `), 103 | { 104 | "type": "Program", 105 | "body": [ 106 | { 107 | "type": "FunctionDeclaration", 108 | "name": { 109 | type: 'Identifier', 110 | name: 'empty' 111 | }, 112 | params: [ 113 | // 114 | ], 115 | body: { 116 | type: 'BlockStatement', 117 | body: [ 118 | // 119 | ] 120 | } 121 | } 122 | ] 123 | } 124 | ); 125 | 126 | console.log('testFunctionDeclaration() passed.'); 127 | } 128 | 129 | export { testFunctionDeclaration }; -------------------------------------------------------------------------------- /test/parser/test-if-statement.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | 10 | import { strict as assert } from 'assert'; 11 | 12 | import { Parser } from '../../src/parser.js'; 13 | 14 | function testSimpleIfStatement() { 15 | let parser = new Parser(); 16 | 17 | assert.deepEqual(parser.parse( 18 | ` 19 | if (x) a=0; 20 | `), 21 | { 22 | "type": "Program", 23 | "body": [ 24 | { 25 | "type": "IfStatement", 26 | "test": { 27 | "type": "Identifier", 28 | "name": "x" 29 | }, 30 | "consequent": { 31 | "type": "ExpressionStatement", 32 | "expression": { 33 | "type": "AssignmentExpression", 34 | "operator": "=", 35 | "left": { 36 | "type": "Identifier", 37 | "name": "a" 38 | }, 39 | "right": { 40 | "type": "NumericLiteral", 41 | "value": 0 42 | } 43 | } 44 | }, 45 | "alternate": null 46 | } 47 | ] 48 | } 49 | ); 50 | 51 | assert.deepEqual(parser.parse( 52 | ` 53 | if (x) 54 | a = 0; 55 | else 56 | a = 1; 57 | `), 58 | { 59 | "type": "Program", 60 | "body": [ 61 | { 62 | "type": "IfStatement", 63 | "test": { 64 | "type": "Identifier", 65 | "name": "x" 66 | }, 67 | "consequent": { 68 | "type": "ExpressionStatement", 69 | "expression": { 70 | "type": "AssignmentExpression", 71 | "operator": "=", 72 | "left": { 73 | "type": "Identifier", 74 | "name": "a" 75 | }, 76 | "right": { 77 | "type": "NumericLiteral", 78 | "value": 0 79 | } 80 | } 81 | }, 82 | "alternate": { 83 | "type": "ExpressionStatement", 84 | "expression": { 85 | "type": "AssignmentExpression", 86 | "operator": "=", 87 | "left": { 88 | "type": "Identifier", 89 | "name": "a" 90 | }, 91 | "right": { 92 | "type": "NumericLiteral", 93 | "value": 1 94 | } 95 | } 96 | } 97 | } 98 | ] 99 | } 100 | ); 101 | } 102 | 103 | function testIfStatementWithBlockStatement() { 104 | let parser = new Parser(); 105 | 106 | assert.deepEqual(parser.parse( 107 | ` 108 | if (x) { 109 | a=0; 110 | } 111 | `), 112 | { 113 | "type": "Program", 114 | "body": [ 115 | { 116 | "type": "IfStatement", 117 | "test": { 118 | type: 'Identifier', 119 | name: 'x' 120 | }, 121 | "consequent": { 122 | type: 'BlockStatement', 123 | body: [ 124 | { 125 | type: 'ExpressionStatement', 126 | expression: { 127 | type: 'AssignmentExpression', 128 | operator: '=', 129 | left: { 130 | type: 'Identifier', 131 | name: 'a' 132 | }, 133 | right: { 134 | type: 'NumericLiteral', 135 | value: 0 136 | } 137 | } 138 | } 139 | ] 140 | }, 141 | "alternate": null 142 | } 143 | ] 144 | } 145 | ); 146 | 147 | assert.deepEqual(parser.parse( 148 | ` 149 | if (x) { 150 | a=0; 151 | }else { 152 | a=1; 153 | } 154 | `), 155 | { 156 | "type": "Program", 157 | "body": [ 158 | { 159 | "type": "IfStatement", 160 | "test": { 161 | type: 'Identifier', 162 | name: 'x' 163 | }, 164 | "consequent": { 165 | type: 'BlockStatement', 166 | body: [ 167 | { 168 | type: 'ExpressionStatement', 169 | expression: { 170 | type: 'AssignmentExpression', 171 | operator: '=', 172 | left: { 173 | type: 'Identifier', 174 | name: 'a' 175 | }, 176 | right: { 177 | type: 'NumericLiteral', 178 | value: 0 179 | } 180 | } 181 | } 182 | ] 183 | }, 184 | "alternate": { 185 | type: 'BlockStatement', 186 | body: [ 187 | { 188 | type: 'ExpressionStatement', 189 | expression: { 190 | type: 'AssignmentExpression', 191 | operator: '=', 192 | left: { 193 | type: 'Identifier', 194 | name: 'a' 195 | }, 196 | right: { 197 | type: 'NumericLiteral', 198 | value: 1 199 | } 200 | } 201 | } 202 | ] 203 | } 204 | } 205 | ] 206 | } 207 | ); 208 | } 209 | 210 | function testCascadingIfStatement() { 211 | let parser = new Parser(); 212 | 213 | assert.deepEqual(parser.parse( 214 | ` 215 | if (x) 216 | if (y) 217 | 2; 218 | else 219 | 8; 220 | `), 221 | { 222 | "type": "Program", 223 | "body": [ 224 | { 225 | "type": "IfStatement", 226 | "test": { 227 | "type": "Identifier", 228 | "name": "x" 229 | }, 230 | "consequent": { 231 | "type": "IfStatement", 232 | "test": { 233 | "type": "Identifier", 234 | "name": "y" 235 | }, 236 | "consequent": { 237 | "type": "ExpressionStatement", 238 | "expression": { 239 | "type": "NumericLiteral", 240 | "value": 2 241 | } 242 | }, 243 | "alternate": { 244 | "type": "ExpressionStatement", 245 | "expression": { 246 | "type": "NumericLiteral", 247 | "value": 8 248 | } 249 | } 250 | }, 251 | "alternate": null 252 | } 253 | ] 254 | } 255 | ); 256 | 257 | assert.deepEqual(parser.parse( 258 | ` 259 | if (p){ 260 | 1; 261 | }else if (q) { 262 | 2; 263 | } 264 | `), 265 | { 266 | "type": "Program", 267 | "body": [ 268 | { 269 | "type": "IfStatement", 270 | "test": { 271 | "type": "Identifier", 272 | "name": "p" 273 | }, 274 | "consequent": { 275 | "type": "BlockStatement", 276 | "body": [ 277 | { 278 | "type": "ExpressionStatement", 279 | "expression": { 280 | "type": "NumericLiteral", 281 | "value": 1 282 | } 283 | } 284 | ] 285 | }, 286 | "alternate": { 287 | "type": "IfStatement", 288 | "test": { 289 | "type": "Identifier", 290 | "name": "q" 291 | }, 292 | "consequent": { 293 | "type": "BlockStatement", 294 | "body": [ 295 | { 296 | "type": "ExpressionStatement", 297 | "expression": { 298 | "type": "NumericLiteral", 299 | "value": 2 300 | } 301 | } 302 | ] 303 | }, 304 | "alternate": null 305 | } 306 | } 307 | ] 308 | } 309 | ); 310 | 311 | assert.deepEqual(parser.parse( 312 | ` 313 | if (m) if (n) {1;} else {2;} else {3;} 314 | `), 315 | { 316 | "type": "Program", 317 | "body": [ 318 | { 319 | "type": "IfStatement", 320 | "test": { 321 | "type": "Identifier", 322 | "name": "m" 323 | }, 324 | "consequent": { 325 | "type": "IfStatement", 326 | "test": { 327 | "type": "Identifier", 328 | "name": "n" 329 | }, 330 | "consequent": { 331 | "type": "BlockStatement", 332 | "body": [ 333 | { 334 | "type": "ExpressionStatement", 335 | "expression": { 336 | "type": "NumericLiteral", 337 | "value": 1 338 | } 339 | } 340 | ] 341 | }, 342 | "alternate": { 343 | "type": "BlockStatement", 344 | "body": [ 345 | { 346 | "type": "ExpressionStatement", 347 | "expression": { 348 | "type": "NumericLiteral", 349 | "value": 2 350 | } 351 | } 352 | ] 353 | } 354 | }, 355 | "alternate": { 356 | "type": "BlockStatement", 357 | "body": [ 358 | { 359 | "type": "ExpressionStatement", 360 | "expression": { 361 | "type": "NumericLiteral", 362 | "value": 3 363 | } 364 | } 365 | ] 366 | } 367 | } 368 | ] 369 | } 370 | ); 371 | } 372 | 373 | function testIfStatement() { 374 | testSimpleIfStatement(); 375 | testIfStatementWithBlockStatement(); 376 | testCascadingIfStatement(); 377 | 378 | console.log('testIfStatement() passed.'); 379 | } 380 | 381 | export { testIfStatement }; -------------------------------------------------------------------------------- /test/parser/test-iteration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testWhileStatement() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | while(x>10) { 19 | x -=1; 20 | } 21 | `), 22 | { 23 | "type": "Program", 24 | "body": [ 25 | { 26 | "type": "WhileStatement", 27 | "test": { 28 | "type": "BinaryExpression", 29 | "operator": ">", 30 | "left": { 31 | "type": "Identifier", 32 | "name": "x" 33 | }, 34 | "right": { 35 | "type": "NumericLiteral", 36 | "value": 10 37 | } 38 | }, 39 | "body": { 40 | "type": "BlockStatement", 41 | "body": [ 42 | { 43 | "type": "ExpressionStatement", 44 | "expression": { 45 | "type": "AssignmentExpression", 46 | "operator": "-=", 47 | "left": { 48 | "type": "Identifier", 49 | "name": "x" 50 | }, 51 | "right": { 52 | "type": "NumericLiteral", 53 | "value": 1 54 | } 55 | } 56 | } 57 | ] 58 | } 59 | } 60 | ] 61 | } 62 | ); 63 | } 64 | 65 | function testDoWhileStatement() { 66 | let parser = new Parser(); 67 | 68 | assert.deepEqual(parser.parse( 69 | ` 70 | do{ 71 | x+=1; 72 | }while(x<10); 73 | `), 74 | { 75 | "type": "Program", 76 | "body": [ 77 | { 78 | "type": "DoWhileStatement", 79 | "test": { 80 | "type": "BinaryExpression", 81 | "operator": "<", 82 | "left": { 83 | "type": "Identifier", 84 | "name": "x" 85 | }, 86 | "right": { 87 | "type": "NumericLiteral", 88 | "value": 10 89 | } 90 | }, 91 | "body": { 92 | "type": "BlockStatement", 93 | "body": [ 94 | { 95 | "type": "ExpressionStatement", 96 | "expression": { 97 | "type": "AssignmentExpression", 98 | "operator": "+=", 99 | "left": { 100 | "type": "Identifier", 101 | "name": "x" 102 | }, 103 | "right": { 104 | "type": "NumericLiteral", 105 | "value": 1 106 | } 107 | } 108 | } 109 | ] 110 | } 111 | } 112 | ] 113 | } 114 | ); 115 | } 116 | 117 | function testForStatement() { 118 | let parser = new Parser(); 119 | 120 | assert.deepEqual(parser.parse( 121 | ` 122 | for(let i =0;i<10;i+=1) { 123 | x+=i; 124 | } 125 | `), 126 | { 127 | "type": "Program", 128 | "body": [ 129 | { 130 | "type": "ForStatement", 131 | "init": { 132 | "type": "VariableStatement", 133 | "declarations": [ 134 | { 135 | "type": "VariableDeclaration", 136 | "id": { 137 | "type": "Identifier", 138 | "name": "i" 139 | }, 140 | "init": { 141 | "type": "NumericLiteral", 142 | "value": 0 143 | } 144 | } 145 | ] 146 | }, 147 | "test": { 148 | "type": "BinaryExpression", 149 | "operator": "<", 150 | "left": { 151 | "type": "Identifier", 152 | "name": "i" 153 | }, 154 | "right": { 155 | "type": "NumericLiteral", 156 | "value": 10 157 | } 158 | }, 159 | "update": { 160 | "type": "AssignmentExpression", 161 | "operator": "+=", 162 | "left": { 163 | "type": "Identifier", 164 | "name": "i" 165 | }, 166 | "right": { 167 | "type": "NumericLiteral", 168 | "value": 1 169 | } 170 | }, 171 | "body": { 172 | "type": "BlockStatement", 173 | "body": [ 174 | { 175 | "type": "ExpressionStatement", 176 | "expression": { 177 | "type": "AssignmentExpression", 178 | "operator": "+=", 179 | "left": { 180 | "type": "Identifier", 181 | "name": "x" 182 | }, 183 | "right": { 184 | "type": "Identifier", 185 | "name": "i" 186 | } 187 | } 188 | } 189 | ] 190 | } 191 | } 192 | ] 193 | } 194 | ); 195 | 196 | assert.deepEqual(parser.parse( 197 | ` 198 | for(i =5;i<10;) { 199 | a; 200 | } 201 | `), 202 | { 203 | "type": "Program", 204 | "body": [ 205 | { 206 | "type": "ForStatement", 207 | "init": { 208 | "type": "AssignmentExpression", 209 | "operator": "=", 210 | "left": { 211 | "type": "Identifier", 212 | "name": "i" 213 | }, 214 | "right": { 215 | "type": "NumericLiteral", 216 | "value": 5 217 | } 218 | }, 219 | "test": { 220 | "type": "BinaryExpression", 221 | "operator": "<", 222 | "left": { 223 | "type": "Identifier", 224 | "name": "i" 225 | }, 226 | "right": { 227 | "type": "NumericLiteral", 228 | "value": 10 229 | } 230 | }, 231 | "update": null, 232 | "body": { 233 | "type": "BlockStatement", 234 | "body": [ 235 | { 236 | "type": "ExpressionStatement", 237 | "expression": { 238 | "type": "Identifier", 239 | "name": "a" 240 | } 241 | } 242 | ] 243 | } 244 | } 245 | ] 246 | } 247 | ); 248 | 249 | assert.deepEqual(parser.parse( 250 | ` 251 | for(;;) { 252 | b; 253 | } 254 | `), 255 | { 256 | "type": "Program", 257 | "body": [ 258 | { 259 | "type": "ForStatement", 260 | "init": null, 261 | "test": null, 262 | "update": null, 263 | "body": { 264 | "type": "BlockStatement", 265 | "body": [ 266 | { 267 | "type": "ExpressionStatement", 268 | "expression": { 269 | "type": "Identifier", 270 | "name": "b" 271 | } 272 | } 273 | ] 274 | } 275 | } 276 | ] 277 | } 278 | ); 279 | } 280 | 281 | function testIteration() { 282 | testWhileStatement(); 283 | testDoWhileStatement(); 284 | testForStatement(); 285 | 286 | console.log('testIteration() passed.'); 287 | } 288 | 289 | export { testIteration }; -------------------------------------------------------------------------------- /test/parser/test-list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testSimpleList() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | [1,2]; 19 | `), 20 | { 21 | "type": "Program", 22 | "body": [ 23 | { 24 | "type": "ExpressionStatement", 25 | "expression": { 26 | "type": "List", 27 | "elements": [ 28 | { 29 | "type": "NumericLiteral", 30 | "value": 1 31 | }, 32 | { 33 | "type": "NumericLiteral", 34 | "value": 2 35 | } 36 | ] 37 | } 38 | } 39 | ] 40 | } 41 | ); 42 | 43 | assert.deepEqual(parser.parse( 44 | ` 45 | [3]; // single element list 46 | `), 47 | { 48 | "type": "Program", 49 | "body": [ 50 | { 51 | "type": "ExpressionStatement", 52 | "expression": { 53 | "type": "List", 54 | "elements": [ 55 | { 56 | "type": "NumericLiteral", 57 | "value": 3 58 | } 59 | ] 60 | } 61 | } 62 | ] 63 | } 64 | ); 65 | 66 | assert.deepEqual(parser.parse( 67 | ` 68 | ["a","b",]; // the last seperator is allowed 69 | `), 70 | { 71 | "type": "Program", 72 | "body": [ 73 | { 74 | "type": "ExpressionStatement", 75 | "expression": { 76 | "type": "List", 77 | "elements": [ 78 | { 79 | "type": "StringLiteral", 80 | "value": "a" 81 | }, 82 | { 83 | "type": "StringLiteral", 84 | "value": "b" 85 | } 86 | ] 87 | } 88 | } 89 | ] 90 | } 91 | ); 92 | 93 | assert.deepEqual(parser.parse( 94 | ` 95 | []; // empty list 96 | `), 97 | { 98 | "type": "Program", 99 | "body": [ 100 | { 101 | "type": "ExpressionStatement", 102 | "expression": { 103 | "type": "List", 104 | "elements": [ 105 | // 106 | ] 107 | } 108 | } 109 | ] 110 | } 111 | ); 112 | 113 | assert.deepEqual(parser.parse( 114 | ` 115 | [4+5, "foo"]; 116 | `), 117 | { 118 | "type": "Program", 119 | "body": [ 120 | { 121 | "type": "ExpressionStatement", 122 | "expression": { 123 | "type": "List", 124 | "elements": [ 125 | { 126 | type: 'BinaryExpression', 127 | operator: '+', 128 | left: { 129 | "type": "NumericLiteral", 130 | "value": 4 131 | }, 132 | right: { 133 | "type": "NumericLiteral", 134 | "value": 5 135 | } 136 | }, 137 | { 138 | "type": "StringLiteral", 139 | "value": "foo" 140 | } 141 | ] 142 | } 143 | } 144 | ] 145 | } 146 | ); 147 | 148 | assert.deepEqual(parser.parse( 149 | ` 150 | [6,[7,8]]; // nested list 151 | `), 152 | { 153 | "type": "Program", 154 | "body": [ 155 | { 156 | "type": "ExpressionStatement", 157 | "expression": { 158 | "type": "List", 159 | "elements": [ 160 | { 161 | "type": "NumericLiteral", 162 | "value": 6 163 | }, 164 | { 165 | "type": "List", 166 | "elements": [ 167 | { 168 | "type": "NumericLiteral", 169 | "value": 7 170 | }, 171 | { 172 | "type": "NumericLiteral", 173 | "value": 8 174 | } 175 | ] 176 | } 177 | ] 178 | } 179 | } 180 | ] 181 | } 182 | ); 183 | } 184 | 185 | function testListWithTuple() { 186 | let parser = new Parser(); 187 | 188 | assert.deepEqual(parser.parse( 189 | ` 190 | [1,(a,b)]; 191 | `), 192 | { 193 | "type": "Program", 194 | "body": [ 195 | { 196 | "type": "ExpressionStatement", 197 | "expression": { 198 | "type": "List", 199 | "elements": [ 200 | { 201 | "type": "NumericLiteral", 202 | "value": 1 203 | }, 204 | { 205 | "type": "Tuple", 206 | "elements": [ 207 | { 208 | "type": "Identifier", 209 | "name": "a" 210 | }, 211 | { 212 | "type": "Identifier", 213 | "name": "b" 214 | } 215 | ] 216 | } 217 | ] 218 | } 219 | } 220 | ] 221 | } 222 | ); 223 | 224 | assert.deepEqual(parser.parse( 225 | ` 226 | (1,[a,b]); 227 | `), 228 | { 229 | "type": "Program", 230 | "body": [ 231 | { 232 | "type": "ExpressionStatement", 233 | "expression": { 234 | "type": "Tuple", 235 | "elements": [ 236 | { 237 | "type": "NumericLiteral", 238 | "value": 1 239 | }, 240 | { 241 | "type": "List", 242 | "elements": [ 243 | { 244 | "type": "Identifier", 245 | "name": "a" 246 | }, 247 | { 248 | "type": "Identifier", 249 | "name": "b" 250 | } 251 | ] 252 | } 253 | ] 254 | } 255 | } 256 | ] 257 | } 258 | ); 259 | } 260 | 261 | function testList() { 262 | testSimpleList(); 263 | testListWithTuple(); 264 | 265 | console.log('testList() passed.'); 266 | } 267 | 268 | export { testList }; -------------------------------------------------------------------------------- /test/parser/test-literal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testNumericLiteral() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse('123;'), { 17 | type: 'Program', 18 | body: [{ 19 | type: 'ExpressionStatement', 20 | expression: 21 | { 22 | type: 'NumericLiteral', 23 | value: 123 24 | } 25 | }] 26 | }); 27 | 28 | assert.deepEqual(parser.parse('2.718;'), { 29 | type: 'Program', 30 | body: [{ 31 | type: 'ExpressionStatement', 32 | expression: 33 | { 34 | type: 'NumericLiteral', 35 | value: 2.718 36 | } 37 | }] 38 | }); 39 | } 40 | 41 | function testStringLiteral() { 42 | let parser = new Parser(); 43 | 44 | assert.deepEqual(parser.parse(`"foo";`), { 45 | type: 'Program', 46 | body: [{ 47 | type: 'ExpressionStatement', 48 | expression: { 49 | type: 'StringLiteral', 50 | value: "foo" 51 | } 52 | }] 53 | }); 54 | 55 | assert.deepEqual(parser.parse(`"";`), { 56 | type: 'Program', 57 | body: [{ 58 | type: 'ExpressionStatement', 59 | expression: { 60 | type: 'StringLiteral', 61 | value: "" 62 | } 63 | }] 64 | }); 65 | 66 | assert.deepEqual(parser.parse('"foo bar";'), { 67 | type: 'Program', 68 | body: [{ 69 | type: 'ExpressionStatement', 70 | expression: { 71 | type: 'StringLiteral', 72 | value: 'foo bar' 73 | } 74 | }] 75 | }); 76 | 77 | assert.deepEqual(parser.parse('"foo\\"bar";'), { 78 | type: 'Program', 79 | body: [{ 80 | type: 'ExpressionStatement', 81 | expression: { 82 | type: 'StringLiteral', 83 | value: 'foo\\"bar' // todo:: escaped char 84 | } 85 | }] 86 | }); 87 | } 88 | 89 | function testLiteral() { 90 | testNumericLiteral(); 91 | testStringLiteral(); 92 | 93 | console.log('testLiteral() passed.'); 94 | } 95 | 96 | export { testLiteral }; -------------------------------------------------------------------------------- /test/parser/test-logical.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testLogicalAnd() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | true && false; 19 | `), 20 | { 21 | "type": "Program", 22 | "body": [ 23 | { 24 | "type": "ExpressionStatement", 25 | "expression": { 26 | "type": "LogicalExpression", 27 | "operator": "&&", 28 | "left": { 29 | "type": "BooleanLiteral", 30 | "value": true 31 | }, 32 | "right": { 33 | "type": "BooleanLiteral", 34 | "value": false 35 | } 36 | } 37 | } 38 | ] 39 | } 40 | ); 41 | 42 | assert.deepEqual(parser.parse( 43 | ` 44 | x>0 && y != 0; 45 | `), 46 | { 47 | "type": "Program", 48 | "body": [ 49 | { 50 | "type": "ExpressionStatement", 51 | "expression": { 52 | "type": "LogicalExpression", 53 | "operator": "&&", 54 | "left": { 55 | "type": "BinaryExpression", 56 | "operator": ">", 57 | "left": { 58 | "type": "Identifier", 59 | "name": "x" 60 | }, 61 | "right": { 62 | "type": "NumericLiteral", 63 | "value": 0 64 | } 65 | }, 66 | "right": { 67 | "type": "BinaryExpression", 68 | "operator": "!=", 69 | "left": { 70 | "type": "Identifier", 71 | "name": "y" 72 | }, 73 | "right": { 74 | "type": "NumericLiteral", 75 | "value": 0 76 | } 77 | } 78 | } 79 | } 80 | ] 81 | } 82 | ); 83 | } 84 | 85 | function testLogicalOr() { 86 | let parser = new Parser(); 87 | 88 | assert.deepEqual(parser.parse( 89 | ` 90 | m > n || p == q; 91 | `), 92 | { 93 | "type": "Program", 94 | "body": [ 95 | { 96 | "type": "ExpressionStatement", 97 | "expression": { 98 | "type": "LogicalExpression", 99 | "operator": "||", 100 | "left": { 101 | "type": "BinaryExpression", 102 | "operator": ">", 103 | "left": { 104 | "type": "Identifier", 105 | "name": "m" 106 | }, 107 | "right": { 108 | "type": "Identifier", 109 | "name": "n" 110 | } 111 | }, 112 | "right": { 113 | "type": "BinaryExpression", 114 | "operator": "==", 115 | "left": { 116 | "type": "Identifier", 117 | "name": "p" 118 | }, 119 | "right": { 120 | "type": "Identifier", 121 | "name": "q" 122 | } 123 | } 124 | } 125 | } 126 | ] 127 | } 128 | ); 129 | 130 | assert.deepEqual(parser.parse( 131 | ` 132 | i && j || x && y; 133 | `), 134 | { 135 | "type": "Program", 136 | "body": [ 137 | { 138 | "type": "ExpressionStatement", 139 | "expression": { 140 | "type": "LogicalExpression", 141 | "operator": "||", 142 | "left": { 143 | "type": "LogicalExpression", 144 | "operator": "&&", 145 | "left": { 146 | "type": "Identifier", 147 | "name": "i" 148 | }, 149 | "right": { 150 | "type": "Identifier", 151 | "name": "j" 152 | } 153 | }, 154 | "right": { 155 | "type": "LogicalExpression", 156 | "operator": "&&", 157 | "left": { 158 | "type": "Identifier", 159 | "name": "x" 160 | }, 161 | "right": { 162 | "type": "Identifier", 163 | "name": "y" 164 | } 165 | } 166 | } 167 | } 168 | ] 169 | } 170 | ); 171 | 172 | assert.deepEqual(parser.parse( 173 | ` 174 | p = x && y || z; 175 | `), 176 | { 177 | "type": "Program", 178 | "body": [ 179 | { 180 | "type": "ExpressionStatement", 181 | "expression": { 182 | "type": "AssignmentExpression", 183 | "operator": "=", 184 | "left": { 185 | "type": "Identifier", 186 | "name": "p" 187 | }, 188 | "right": { 189 | "type": "LogicalExpression", 190 | "operator": "||", 191 | "left": { 192 | "type": "LogicalExpression", 193 | "operator": "&&", 194 | "left": { 195 | "type": "Identifier", 196 | "name": "x" 197 | }, 198 | "right": { 199 | "type": "Identifier", 200 | "name": "y" 201 | } 202 | }, 203 | "right": { 204 | "type": "Identifier", 205 | "name": "z" 206 | } 207 | } 208 | } 209 | } 210 | ] 211 | } 212 | ); 213 | } 214 | 215 | function testLogical() { 216 | testLogicalAnd(); 217 | testLogicalOr(); 218 | 219 | console.log('testLogical() passed.'); 220 | } 221 | 222 | export { testLogical }; -------------------------------------------------------------------------------- /test/parser/test-member.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | 10 | import { strict as assert } from 'assert'; 11 | 12 | import { Parser } from '../../src/parser.js'; 13 | 14 | function testDotMember() { 15 | let parser = new Parser(); 16 | 17 | assert.deepEqual(parser.parse( 18 | ` 19 | a.b; 20 | `), 21 | { 22 | "type": "Program", 23 | "body": [ 24 | { 25 | "type": "ExpressionStatement", 26 | "expression": { 27 | "type": "MemberExpression", 28 | "computed": false, // 点成员 = false, 中括号 = true 29 | "object": { 30 | "type": "Identifier", 31 | "name": "a" 32 | }, 33 | "property": { 34 | "type": "Identifier", 35 | "name": "b" 36 | } 37 | } 38 | } 39 | ] 40 | } 41 | ); 42 | 43 | assert.deepEqual(parser.parse( 44 | ` 45 | x.y = 1; 46 | `), 47 | { 48 | "type": "Program", 49 | "body": [ 50 | { 51 | "type": "ExpressionStatement", 52 | "expression": { 53 | type: 'AssignmentExpression', 54 | operator: '=', 55 | left: { 56 | "type": "MemberExpression", 57 | "computed": false, // 点成员 = false, 中括号 = true 58 | "object": { 59 | "type": "Identifier", 60 | "name": "x" 61 | }, 62 | "property": { 63 | "type": "Identifier", 64 | "name": "y" 65 | } 66 | }, 67 | right: { 68 | type: 'NumericLiteral', 69 | value: 1 70 | } 71 | } 72 | } 73 | ] 74 | } 75 | ); 76 | } 77 | 78 | function testBracketMember() { 79 | let parser = new Parser(); 80 | 81 | assert.deepEqual(parser.parse( 82 | ` 83 | a[1]; 84 | `), 85 | { 86 | "type": "Program", 87 | "body": [ 88 | { 89 | "type": "ExpressionStatement", 90 | "expression": { 91 | "type": "MemberExpression", 92 | "computed": true, // 点成员 = false, 中括号 = true 93 | "object": { 94 | "type": "Identifier", 95 | "name": "a" 96 | }, 97 | "property": { 98 | "type": "NumericLiteral", 99 | value: 1 100 | } 101 | } 102 | } 103 | ] 104 | } 105 | ); 106 | 107 | assert.deepEqual(parser.parse( 108 | ` 109 | x[y*2] = 1; 110 | `), 111 | { 112 | "type": "Program", 113 | "body": [ 114 | { 115 | "type": "ExpressionStatement", 116 | "expression": { 117 | type: 'AssignmentExpression', 118 | operator: '=', 119 | left: { 120 | "type": "MemberExpression", 121 | "computed": true, // 点成员 = false, 中括号 = true 122 | "object": { 123 | "type": "Identifier", 124 | "name": "x" 125 | }, 126 | "property": { 127 | type: 'BinaryExpression', 128 | operator: '*', 129 | left: { 130 | "type": "Identifier", 131 | "name": "y" 132 | }, 133 | right: { 134 | type: 'NumericLiteral', 135 | value: 2 136 | } 137 | } 138 | }, 139 | right: { 140 | type: 'NumericLiteral', 141 | value: 1 142 | } 143 | } 144 | } 145 | ] 146 | } 147 | ); 148 | } 149 | 150 | function testNestedMember() { 151 | let parser = new Parser(); 152 | 153 | assert.deepEqual(parser.parse( 154 | ` 155 | a[b.c]; 156 | `), 157 | { 158 | "type": "Program", 159 | "body": [ 160 | { 161 | "type": "ExpressionStatement", 162 | "expression": { 163 | "type": "MemberExpression", 164 | "computed": true, 165 | "object": { 166 | "type": "Identifier", 167 | "name": "a" 168 | }, 169 | "property": { 170 | "type": "MemberExpression", 171 | "computed": false, 172 | "object": { 173 | "type": "Identifier", 174 | "name": "b" 175 | }, 176 | "property": { 177 | "type": "Identifier", 178 | "name": "c" 179 | } 180 | } 181 | } 182 | } 183 | ] 184 | } 185 | ); 186 | 187 | assert.deepEqual(parser.parse( 188 | ` 189 | x[y[z]]; 190 | `), 191 | { 192 | "type": "Program", 193 | "body": [ 194 | { 195 | "type": "ExpressionStatement", 196 | "expression": { 197 | "type": "MemberExpression", 198 | "computed": true, 199 | "object": { 200 | "type": "Identifier", 201 | "name": "x" 202 | }, 203 | "property": { 204 | "type": "MemberExpression", 205 | "computed": true, 206 | "object": { 207 | "type": "Identifier", 208 | "name": "y" 209 | }, 210 | "property": { 211 | "type": "Identifier", 212 | "name": "z" 213 | } 214 | } 215 | } 216 | } 217 | ] 218 | } 219 | ); 220 | } 221 | 222 | function testComplexObject() { 223 | let parser = new Parser(); 224 | 225 | assert.deepEqual(parser.parse( 226 | ` 227 | (x+y).length; 228 | `), 229 | { 230 | "type": "Program", 231 | "body": [ 232 | { 233 | "type": "ExpressionStatement", 234 | "expression": { 235 | "type": "MemberExpression", 236 | "computed": false, 237 | "object": { 238 | "type": "BinaryExpression", 239 | "operator": "+", 240 | "left": { 241 | "type": "Identifier", 242 | "name": "x" 243 | }, 244 | "right": { 245 | "type": "Identifier", 246 | "name": "y" 247 | } 248 | }, 249 | "property": { 250 | "type": "Identifier", 251 | "name": "length" 252 | } 253 | } 254 | } 255 | ] 256 | } 257 | ); 258 | 259 | assert.deepEqual(parser.parse( 260 | ` 261 | (a+b)[2]; 262 | `), 263 | { 264 | "type": "Program", 265 | "body": [ 266 | { 267 | "type": "ExpressionStatement", 268 | "expression": { 269 | "type": "MemberExpression", 270 | "computed": true, 271 | "object": { 272 | "type": "BinaryExpression", 273 | "operator": "+", 274 | "left": { 275 | "type": "Identifier", 276 | "name": "a" 277 | }, 278 | "right": { 279 | "type": "Identifier", 280 | "name": "b" 281 | } 282 | }, 283 | "property": { 284 | "type": "NumericLiteral", 285 | "value": 2 286 | } 287 | } 288 | } 289 | ] 290 | } 291 | ); 292 | } 293 | 294 | function testChain() { 295 | let parser = new Parser(); 296 | 297 | assert.deepEqual(parser.parse( 298 | ` 299 | a.b.c[1]; 300 | `), 301 | { 302 | "type": "Program", 303 | "body": [ 304 | { 305 | "type": "ExpressionStatement", 306 | "expression": { 307 | "type": "MemberExpression", 308 | "computed": true, // 点成员 = false, 中括号 = true 309 | "object": { 310 | "type": "MemberExpression", 311 | "computed": false, // 点成员 = false, 中括号 = true 312 | "object": { 313 | "type": "MemberExpression", 314 | "computed": false, // 点成员 = false, 中括号 = true 315 | "object": { 316 | "type": "Identifier", 317 | "name": "a" 318 | }, 319 | "property": { 320 | "type": "Identifier", 321 | "name": "b" 322 | } 323 | }, 324 | "property": { 325 | "type": "Identifier", 326 | "name": "c" 327 | } 328 | }, 329 | "property": { 330 | "type": "NumericLiteral", 331 | value: 1 332 | } 333 | } 334 | } 335 | ] 336 | } 337 | ); 338 | 339 | assert.deepEqual(parser.parse( 340 | ` 341 | x.y[0].z; 342 | `), 343 | { 344 | "type": "Program", 345 | "body": [ 346 | { 347 | "type": "ExpressionStatement", 348 | "expression": { 349 | "type": "MemberExpression", 350 | "computed": false, 351 | "object": { 352 | "type": "MemberExpression", 353 | "computed": true, 354 | "object": { 355 | "type": "MemberExpression", 356 | "computed": false, 357 | "object": { 358 | "type": "Identifier", 359 | "name": "x" 360 | }, 361 | "property": { 362 | "type": "Identifier", 363 | "name": "y" 364 | } 365 | }, 366 | "property": { 367 | "type": "NumericLiteral", 368 | "value": 0 369 | } 370 | }, 371 | "property": { 372 | "type": "Identifier", 373 | "name": "z" 374 | } 375 | } 376 | } 377 | ] 378 | } 379 | ); 380 | 381 | assert.deepEqual(parser.parse( 382 | ` 383 | x[1][2]; 384 | `), 385 | { 386 | "type": "Program", 387 | "body": [ 388 | { 389 | "type": "ExpressionStatement", 390 | "expression": { 391 | "type": "MemberExpression", 392 | "computed": true, // 点成员 = false, 中括号 = true 393 | "object": { 394 | "type": "MemberExpression", 395 | "computed": true, // 点成员 = false, 中括号 = true 396 | "object": { 397 | "type": "Identifier", 398 | "name": "x" 399 | }, 400 | "property": { 401 | "type": "NumericLiteral", 402 | "value": 1 403 | } 404 | }, 405 | "property": { 406 | "type": "NumericLiteral", 407 | "value": 2 408 | } 409 | } 410 | } 411 | ] 412 | } 413 | ); 414 | } 415 | 416 | function testMember() { 417 | testDotMember(); 418 | testBracketMember(); 419 | testNestedMember(); 420 | testComplexObject(); 421 | testChain(); 422 | 423 | console.log('testMember() passed.'); 424 | } 425 | 426 | export { testMember }; -------------------------------------------------------------------------------- /test/parser/test-new-call.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | 10 | import { strict as assert } from 'assert'; 11 | 12 | import { Parser } from '../../src/parser.js'; 13 | 14 | function testSimpleNewCall() { 15 | let parser = new Parser(); 16 | 17 | assert.deepEqual(parser.parse( 18 | ` 19 | new Foo(); 20 | `), 21 | { 22 | "type": "Program", 23 | "body": [ 24 | { 25 | "type": "ExpressionStatement", 26 | "expression": { 27 | "type": "NewExpression", 28 | "callee": { 29 | "type": "Identifier", 30 | "name": "Foo" 31 | }, 32 | "arguments": [] 33 | } 34 | } 35 | ] 36 | } 37 | ); 38 | 39 | assert.deepEqual(parser.parse( 40 | ` 41 | new Foo(1); 42 | `), 43 | { 44 | "type": "Program", 45 | "body": [ 46 | { 47 | "type": "ExpressionStatement", 48 | "expression": { 49 | "type": "NewExpression", 50 | "callee": { 51 | "type": "Identifier", 52 | "name": "Foo" 53 | }, 54 | "arguments": [ 55 | { 56 | "type": "NumericLiteral", 57 | "value": 1 58 | } 59 | ] 60 | } 61 | } 62 | ] 63 | } 64 | ); 65 | 66 | assert.deepEqual(parser.parse( 67 | ` 68 | let f = new Foo(1); 69 | `), 70 | { 71 | "type": "Program", 72 | "body": [ 73 | { 74 | "type": "VariableStatement", 75 | "declarations": [ 76 | { 77 | "type": "VariableDeclaration", 78 | "id": { 79 | "type": "Identifier", 80 | "name": "f" 81 | }, 82 | "init": { 83 | "type": "NewExpression", 84 | "callee": { 85 | "type": "Identifier", 86 | "name": "Foo" 87 | }, 88 | "arguments": [ 89 | { 90 | "type": "NumericLiteral", 91 | "value": 1 92 | } 93 | ] 94 | } 95 | } 96 | ] 97 | } 98 | ] 99 | } 100 | ); 101 | } 102 | 103 | function testComplexNewCall() { 104 | let parser = new Parser(); 105 | 106 | assert.deepEqual(parser.parse( 107 | ` 108 | new Foo.bar(1); // -> new (Foo.bar)(1) 109 | `), 110 | { 111 | "type": "Program", 112 | "body": [ 113 | { 114 | "type": "ExpressionStatement", 115 | "expression": { 116 | "type": "NewExpression", 117 | "callee": { 118 | "type": "MemberExpression", 119 | "computed": false, 120 | "object": { 121 | "type": "Identifier", 122 | "name": "Foo" 123 | }, 124 | "property": { 125 | "type": "Identifier", 126 | "name": "bar" 127 | } 128 | }, 129 | "arguments": [ 130 | { 131 | "type": "NumericLiteral", 132 | "value": 1 133 | } 134 | ] 135 | } 136 | } 137 | ] 138 | } 139 | ); 140 | 141 | assert.deepEqual(parser.parse( 142 | ` 143 | new Foo(1).bar(2); // -> (new Foo(1)).bar(2) 144 | `), 145 | { 146 | "type": "Program", 147 | "body": [ 148 | { 149 | "type": "ExpressionStatement", 150 | "expression": { 151 | "type": "CallExpression", 152 | "callee": { 153 | "type": "MemberExpression", 154 | "computed": false, 155 | "object": { 156 | "type": "NewExpression", 157 | "callee": { 158 | "type": "Identifier", 159 | "name": "Foo" 160 | }, 161 | "arguments": [ 162 | { 163 | "type": "NumericLiteral", 164 | "value": 1 165 | } 166 | ] 167 | }, 168 | "property": { 169 | "type": "Identifier", 170 | "name": "bar" 171 | } 172 | }, 173 | "arguments": [ 174 | { 175 | "type": "NumericLiteral", 176 | "value": 2 177 | } 178 | ] 179 | } 180 | } 181 | ] 182 | } 183 | ); 184 | 185 | assert.deepEqual(parser.parse( 186 | ` 187 | new new Foo(1).bar(2); // -> (new (new Foo(1)).bar)(2) 188 | `), 189 | { 190 | "type": "Program", 191 | "body": [ 192 | { 193 | "type": "ExpressionStatement", 194 | "expression": { 195 | "type": "NewExpression", 196 | "callee": { 197 | "type": "MemberExpression", 198 | "computed": false, 199 | "object": { 200 | "type": "NewExpression", 201 | "callee": { 202 | "type": "Identifier", 203 | "name": "Foo" 204 | }, 205 | "arguments": [ 206 | { 207 | "type": "NumericLiteral", 208 | "value": 1 209 | } 210 | ] 211 | }, 212 | "property": { 213 | "type": "Identifier", 214 | "name": "bar" 215 | } 216 | }, 217 | "arguments": [ 218 | { 219 | "type": "NumericLiteral", 220 | "value": 2 221 | } 222 | ] 223 | } 224 | } 225 | ] 226 | } 227 | ); 228 | } 229 | 230 | function testNewCall() { 231 | testSimpleNewCall(); 232 | testComplexNewCall(); 233 | 234 | console.log('testNewCall() passed.'); 235 | } 236 | 237 | export { testNewCall }; -------------------------------------------------------------------------------- /test/parser/test-relational.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testSingleRelationalExpression() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | x>0; 19 | `), 20 | { 21 | "type": "Program", 22 | "body": [ 23 | { 24 | "type": "ExpressionStatement", 25 | "expression": { 26 | "type": "BinaryExpression", 27 | "operator": ">", 28 | "left": { 29 | "type": "Identifier", 30 | "name": 'x' 31 | }, 32 | "right": { 33 | "type": "NumericLiteral", 34 | "value": 0 35 | } 36 | } 37 | } 38 | ] 39 | } 40 | ); 41 | 42 | assert.deepEqual(parser.parse( 43 | ` 44 | y+1>9; 45 | `), 46 | { 47 | "type": "Program", 48 | "body": [ 49 | { 50 | "type": "ExpressionStatement", 51 | "expression": { 52 | "type": "BinaryExpression", 53 | "operator": ">", 54 | "left": { 55 | "type": "BinaryExpression", 56 | "operator": "+", 57 | "left": { 58 | "type": "Identifier", 59 | "name": "y" 60 | }, 61 | "right": { 62 | "type": "NumericLiteral", 63 | "value": 1 64 | } 65 | }, 66 | "right": { 67 | "type": "NumericLiteral", 68 | "value": 9 69 | } 70 | } 71 | } 72 | ] 73 | } 74 | ); 75 | 76 | assert.deepEqual(parser.parse( 77 | ` 78 | z=i>1; 79 | `), 80 | { 81 | "type": "Program", 82 | "body": [ 83 | { 84 | "type": "ExpressionStatement", 85 | "expression": { 86 | "type": "AssignmentExpression", 87 | "operator": "=", 88 | "left": { 89 | "type": "Identifier", 90 | "name": "z" 91 | }, 92 | "right": { 93 | "type": "BinaryExpression", 94 | "operator": ">", 95 | "left": { 96 | "type": "Identifier", 97 | "name": "i" 98 | }, 99 | "right": { 100 | "type": "NumericLiteral", 101 | "value": 1 102 | } 103 | } 104 | } 105 | } 106 | ] 107 | } 108 | ); 109 | } 110 | 111 | function testRelationalExpressionWithinIfStatement() { 112 | let parser = new Parser(); 113 | 114 | assert.deepEqual(parser.parse( 115 | ` 116 | if (x<=0){ 117 | 1; 118 | } 119 | `), 120 | { 121 | "type": "Program", 122 | "body": [ 123 | { 124 | "type": "IfStatement", 125 | "test": { 126 | "type": "BinaryExpression", 127 | "operator": "<=", 128 | "left": { 129 | "type": "Identifier", 130 | "name": "x" 131 | }, 132 | "right": { 133 | "type": "NumericLiteral", 134 | "value": 0 135 | } 136 | }, 137 | "consequent": { 138 | "type": "BlockStatement", 139 | "body": [ 140 | { 141 | "type": "ExpressionStatement", 142 | "expression": { 143 | "type": "NumericLiteral", 144 | "value": 1 145 | } 146 | } 147 | ] 148 | }, 149 | "alternate": null 150 | } 151 | ] 152 | } 153 | ); 154 | } 155 | 156 | function testRelational() { 157 | testSingleRelationalExpression(); 158 | testRelationalExpressionWithinIfStatement(); 159 | 160 | console.log('testRelational() passed.'); 161 | } 162 | 163 | export { testRelational }; -------------------------------------------------------------------------------- /test/parser/test-statement.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testSingleStatement() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | `555;`), { 18 | type: 'Program', 19 | body: [{ 20 | type: 'ExpressionStatement', 21 | expression: { 22 | type: 'NumericLiteral', 23 | value: 555 24 | } 25 | }] 26 | }); 27 | } 28 | 29 | function testMultiStatements() { 30 | let parser = new Parser(); 31 | 32 | assert.deepEqual(parser.parse( 33 | ` 34 | "hello"; 35 | 123; 36 | `), { 37 | type: 'Program', 38 | body: [ 39 | { 40 | type: 'ExpressionStatement', 41 | expression: { 42 | type: 'StringLiteral', 43 | value: "hello" 44 | } 45 | }, 46 | { 47 | type: 'ExpressionStatement', 48 | expression: { 49 | type: 'NumericLiteral', 50 | value: 123 51 | } 52 | }] 53 | }); 54 | } 55 | 56 | function testStatement() { 57 | testSingleStatement(); 58 | testMultiStatements(); 59 | 60 | console.log('testStatement() passed.'); 61 | } 62 | 63 | export { testStatement }; -------------------------------------------------------------------------------- /test/parser/test-tuple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testTuple() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | (1,2); 19 | `), 20 | { 21 | "type": "Program", 22 | "body": [ 23 | { 24 | "type": "ExpressionStatement", 25 | "expression": { 26 | "type": "Tuple", 27 | "elements": [ 28 | { 29 | "type": "NumericLiteral", 30 | "value": 1 31 | }, 32 | { 33 | "type": "NumericLiteral", 34 | "value": 2 35 | } 36 | ] 37 | } 38 | } 39 | ] 40 | } 41 | ); 42 | 43 | assert.deepEqual(parser.parse( 44 | ` 45 | (3,); // single element tuple 46 | `), 47 | { 48 | "type": "Program", 49 | "body": [ 50 | { 51 | "type": "ExpressionStatement", 52 | "expression": { 53 | "type": "Tuple", 54 | "elements": [ 55 | { 56 | "type": "NumericLiteral", 57 | "value": 3 58 | } 59 | ] 60 | } 61 | } 62 | ] 63 | } 64 | ); 65 | 66 | assert.deepEqual(parser.parse( 67 | ` 68 | ("a","b",); // the last seperator is allowed 69 | `), 70 | { 71 | "type": "Program", 72 | "body": [ 73 | { 74 | "type": "ExpressionStatement", 75 | "expression": { 76 | "type": "Tuple", 77 | "elements": [ 78 | { 79 | "type": "StringLiteral", 80 | "value": "a" 81 | }, 82 | { 83 | "type": "StringLiteral", 84 | "value": "b" 85 | } 86 | ] 87 | } 88 | } 89 | ] 90 | } 91 | ); 92 | 93 | assert.deepEqual(parser.parse( 94 | ` 95 | (); // empty tuple 96 | `), 97 | { 98 | "type": "Program", 99 | "body": [ 100 | { 101 | "type": "ExpressionStatement", 102 | "expression": { 103 | "type": "Tuple", 104 | "elements": [ 105 | // 106 | ] 107 | } 108 | } 109 | ] 110 | } 111 | ); 112 | 113 | assert.deepEqual(parser.parse( 114 | ` 115 | (4+5, "foo"); 116 | `), 117 | { 118 | "type": "Program", 119 | "body": [ 120 | { 121 | "type": "ExpressionStatement", 122 | "expression": { 123 | "type": "Tuple", 124 | "elements": [ 125 | { 126 | type: 'BinaryExpression', 127 | operator: '+', 128 | left: { 129 | "type": "NumericLiteral", 130 | "value": 4 131 | }, 132 | right: { 133 | "type": "NumericLiteral", 134 | "value": 5 135 | } 136 | }, 137 | { 138 | "type": "StringLiteral", 139 | "value": "foo" 140 | } 141 | ] 142 | } 143 | } 144 | ] 145 | } 146 | ); 147 | 148 | assert.deepEqual(parser.parse( 149 | ` 150 | (6,(7,8)); // nested tuple 151 | `), 152 | { 153 | "type": "Program", 154 | "body": [ 155 | { 156 | "type": "ExpressionStatement", 157 | "expression": { 158 | "type": "Tuple", 159 | "elements": [ 160 | { 161 | "type": "NumericLiteral", 162 | "value": 6 163 | }, 164 | { 165 | "type": "Tuple", 166 | "elements": [ 167 | { 168 | "type": "NumericLiteral", 169 | "value": 7 170 | }, 171 | { 172 | "type": "NumericLiteral", 173 | "value": 8 174 | } 175 | ] 176 | } 177 | ] 178 | } 179 | } 180 | ] 181 | } 182 | ); 183 | 184 | console.log('testTuple() passed.'); 185 | } 186 | 187 | export { testTuple }; -------------------------------------------------------------------------------- /test/parser/test-unary.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testUnaryNot() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse( 17 | ` 18 | !false; 19 | `), 20 | { 21 | "type": "Program", 22 | "body": [ 23 | { 24 | "type": "ExpressionStatement", 25 | "expression": { 26 | "type": "UnaryExpression", 27 | "operator": "!", 28 | "argument": { 29 | "type": "BooleanLiteral", 30 | "value": false 31 | } 32 | } 33 | } 34 | ] 35 | } 36 | ); 37 | 38 | assert.deepEqual(parser.parse( 39 | ` 40 | !m; 41 | `), 42 | { 43 | "type": "Program", 44 | "body": [ 45 | { 46 | "type": "ExpressionStatement", 47 | "expression": { 48 | "type": "UnaryExpression", 49 | "operator": "!", 50 | "argument": { 51 | "type": "Identifier", 52 | "name": "m" 53 | } 54 | } 55 | } 56 | ] 57 | } 58 | ); 59 | 60 | assert.deepEqual(parser.parse( 61 | ` 62 | !(a>b); 63 | `), 64 | { 65 | "type": "Program", 66 | "body": [ 67 | { 68 | "type": "ExpressionStatement", 69 | "expression": { 70 | "type": "UnaryExpression", 71 | "operator": "!", 72 | "argument": { 73 | "type": "BinaryExpression", 74 | "operator": ">", 75 | "left": { 76 | "type": "Identifier", 77 | "name": "a" 78 | }, 79 | "right": { 80 | "type": "Identifier", 81 | "name": "b" 82 | } 83 | } 84 | } 85 | } 86 | ] 87 | } 88 | ); 89 | } 90 | 91 | function testUnaryNegative() { 92 | let parser = new Parser(); 93 | 94 | assert.deepEqual(parser.parse( 95 | ` 96 | -x; 97 | `), 98 | { 99 | "type": "Program", 100 | "body": [ 101 | { 102 | "type": "ExpressionStatement", 103 | "expression": { 104 | "type": "UnaryExpression", 105 | "operator": "-", 106 | "argument": { 107 | "type": "Identifier", 108 | "name": "x" 109 | } 110 | } 111 | } 112 | ] 113 | } 114 | ); 115 | 116 | assert.deepEqual(parser.parse( 117 | ` 118 | +123; 119 | `), 120 | { 121 | "type": "Program", 122 | "body": [ 123 | { 124 | "type": "ExpressionStatement", 125 | "expression": { 126 | "type": "UnaryExpression", 127 | "operator": "+", 128 | "argument": { 129 | "type": "NumericLiteral", 130 | "value": 123 131 | } 132 | } 133 | } 134 | ] 135 | } 136 | ); 137 | 138 | assert.deepEqual(parser.parse( 139 | ` 140 | -(y+10); 141 | `), 142 | { 143 | "type": "Program", 144 | "body": [ 145 | { 146 | "type": "ExpressionStatement", 147 | "expression": { 148 | "type": "UnaryExpression", 149 | "operator": "-", 150 | "argument": { 151 | "type": "BinaryExpression", 152 | "operator": "+", 153 | "left": { 154 | "type": "Identifier", 155 | "name": "y" 156 | }, 157 | "right": { 158 | "type": "NumericLiteral", 159 | "value": 10 160 | } 161 | } 162 | } 163 | } 164 | ] 165 | } 166 | ); 167 | 168 | assert.deepEqual(parser.parse( 169 | ` 170 | 5+-2; 171 | `), 172 | { 173 | "type": "Program", 174 | "body": [ 175 | { 176 | "type": "ExpressionStatement", 177 | "expression": { 178 | "type": "BinaryExpression", 179 | "operator": "+", 180 | "left": { 181 | "type": "NumericLiteral", 182 | "value": 5 183 | }, 184 | "right": { 185 | "type": "UnaryExpression", 186 | "operator": "-", 187 | "argument": { 188 | "type": "NumericLiteral", 189 | "value": 2 190 | } 191 | } 192 | } 193 | } 194 | ] 195 | } 196 | ); 197 | } 198 | 199 | function testUnaryChain() { 200 | let parser = new Parser(); 201 | 202 | assert.deepEqual(parser.parse( 203 | ` 204 | !-x; 205 | `), 206 | { 207 | "type": "Program", 208 | "body": [ 209 | { 210 | "type": "ExpressionStatement", 211 | "expression": { 212 | "type": "UnaryExpression", 213 | "operator": "!", 214 | "argument": { 215 | "type": "UnaryExpression", 216 | "operator": "-", 217 | "argument": { 218 | "type": "Identifier", 219 | "name": "x" 220 | } 221 | } 222 | } 223 | } 224 | ] 225 | } 226 | ); 227 | 228 | assert.deepEqual(parser.parse( 229 | ` 230 | !!false; 231 | `), 232 | { 233 | "type": "Program", 234 | "body": [ 235 | { 236 | "type": "ExpressionStatement", 237 | "expression": { 238 | "type": "UnaryExpression", 239 | "operator": "!", 240 | "argument": { 241 | "type": "UnaryExpression", 242 | "operator": "!", 243 | "argument": { 244 | "type": "BooleanLiteral", 245 | "value": false 246 | } 247 | } 248 | } 249 | } 250 | ] 251 | } 252 | ); 253 | } 254 | 255 | function testUnary() { 256 | testUnaryNot(); 257 | testUnaryNegative(); 258 | testUnaryChain(); 259 | 260 | console.log('testUnary() passed.'); 261 | } 262 | 263 | export { testUnary }; -------------------------------------------------------------------------------- /test/parser/test-whitespace.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { strict as assert } from 'assert'; 10 | 11 | import { Parser } from '../../src/parser.js'; 12 | 13 | function testWhitespaceChars() { 14 | let parser = new Parser(); 15 | 16 | assert.deepEqual(parser.parse(' 555 ;'), { 17 | type: 'Program', 18 | body: [{ 19 | type: 'ExpressionStatement', 20 | expression: { 21 | type: 'NumericLiteral', 22 | value: 555 23 | } 24 | }] 25 | }); 26 | 27 | assert.deepEqual(parser.parse(' " 555 " ;'), { 28 | type: 'Program', 29 | body: [{ 30 | type: 'ExpressionStatement', 31 | expression: { 32 | type: 'StringLiteral', 33 | value: " 555 " 34 | } 35 | }] 36 | }); 37 | } 38 | 39 | function testNewLine() { 40 | let parser = new Parser(); 41 | 42 | assert.deepEqual(parser.parse( 43 | ` 44 | 555; 45 | `), { 46 | type: 'Program', 47 | body: [{ 48 | type: 'ExpressionStatement', 49 | expression: { 50 | type: 'NumericLiteral', 51 | value: 555 52 | } 53 | }] 54 | }); 55 | } 56 | 57 | function testWhitespace() { 58 | testWhitespaceChars(); 59 | testNewLine(); 60 | 61 | console.log('testWhitespace() passed.'); 62 | } 63 | 64 | export { testWhitespace }; -------------------------------------------------------------------------------- /test/script/01-sum.toy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | let sum; 10 | for (let i = 1; i <= 100; i = i + 1) { 11 | sum = sum + i; 12 | } 13 | print(sum); // 5050 -------------------------------------------------------------------------------- /test/script/02-fib.toy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | // 0、1、1、2、3、5、8、13、21、34、55... 10 | 11 | function fib(n) { 12 | if (n==0) { 13 | 0; 14 | }else if(n==1) { 15 | 1; 16 | }else { 17 | fib(n-1) + fib(n-2); 18 | } 19 | } 20 | 21 | print(fib(9)); // 34 -------------------------------------------------------------------------------- /test/script/03-class.toy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | class Num { 10 | let val; 11 | 12 | constructor(this, x) { 13 | this.val = x; 14 | } 15 | 16 | function add(this, y) { 17 | this.val += y; 18 | } 19 | } 20 | 21 | let a = new Num(1); 22 | let b = new Num(2); 23 | a.add(3); 24 | b.add(5); 25 | print(a.val + b.val); // 11 -------------------------------------------------------------------------------- /test/script/04-line.toy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | class Point { 10 | let x,y; 11 | constructor(this, x, y) { 12 | this.x = x; 13 | this.y = y; 14 | } 15 | } 16 | 17 | class Line { 18 | let p1,p2; 19 | constructor(this, p1, p2) { 20 | this.p1 = p1; 21 | this.p2 = p2; 22 | } 23 | 24 | function length(this) { 25 | sqrt( 26 | pow(this.p1.x - this.p2.x, 27 | 2) 28 | + 29 | pow(this.p1.y - this.p2.y, 30 | 2) 31 | ); 32 | } 33 | } 34 | 35 | let p1 = new Point(2,3); 36 | let p2 = new Point(5,7); 37 | let n = new Line(p1, p2); 38 | print(n.length()); // 5 -------------------------------------------------------------------------------- /test/script/05-list.toy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | class Node { 10 | let val, next; 11 | constructor(this, val, next) { 12 | this.val = val; 13 | this.next = next; 14 | } 15 | } 16 | 17 | class List { 18 | let head; 19 | 20 | function push(this, val) { 21 | this.head = new Node(val, this.head); 22 | } 23 | 24 | function pop(this) { 25 | if (this.head != null) { 26 | let val = this.head.val; 27 | this.head = this.head.next; 28 | val; 29 | } 30 | } 31 | 32 | function length(this) { 33 | let i = 0; 34 | let n = this.head; 35 | while(n != null) { 36 | i+=1; 37 | n=n.next; 38 | } 39 | i; 40 | } 41 | 42 | function to_string(this) { 43 | let s = ""; 44 | let n = this.head; 45 | while(n != null) { 46 | s += n.val + ","; 47 | n=n.next; 48 | } 49 | s; 50 | } 51 | } 52 | 53 | let list = new List(); 54 | 55 | print("push: 3,5,7,9"); 56 | list.push(3); 57 | list.push(5); 58 | list.push(7); 59 | list.push(9); 60 | 61 | print("length: " + list.length()); 62 | print("items: " + list.to_string()); 63 | 64 | print("pop: " + list.pop()); 65 | print("pop: " + list.pop()); 66 | 67 | print("length: " + list.length()); 68 | print("items: " + list.to_string()); 69 | 70 | -------------------------------------------------------------------------------- /test/test-evaluator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { testLiteral } from './evaluator/test-literal.js'; 10 | // import { testWhitespace } from './evaluator/test-whitespace.js'; 11 | import { testComment } from './evaluator/test-comment.js'; 12 | import { testStatement } from './evaluator/test-statement.js'; 13 | import { testBlockStatement } from './evaluator/test-blockstatement.js'; 14 | import { testBinaryExpression } from './evaluator/test-binaryexpression.js'; 15 | // import { testAssignmentExpression } from './evaluator/test-assignment.js'; 16 | import { testDeclarationStatement } from './evaluator/test-declaration.js'; 17 | import { testIfStatement } from './evaluator/test-if-statement.js'; 18 | import { testRelational } from './evaluator/test-relational.js'; 19 | import { testEquality } from './evaluator/test-equality.js'; 20 | import { testLogical } from './evaluator/test-logical.js'; 21 | import { testUnary } from './evaluator/test-unary.js'; 22 | import { testIteration } from './evaluator/test-iteration.js'; 23 | import { testFunctionDeclarationAndFunctionCall } from './evaluator/test-function-declaration-and-function-call.js'; 24 | // import { testMember } from './evaluator/test-member.js'; 25 | // import { testFunctionCall } from './evaluator/test-function-call.js'; 26 | // import { testClassDeclaration } from './evaluator/test-class-declaration.js'; 27 | // import { testNewCall } from './evaluator/test-new-call.js'; 28 | import { testClassDeclarationAndNewCallAndMember } from './evaluator/test-class-declaration-and-new-call-and-member.js'; 29 | // import { testTuple } from './evaluator/test-tuple.js'; 30 | // import { testList } from './evaluator/test-list.js'; 31 | // import { testMap } from './evaluator/test-map.js'; 32 | 33 | function testEvaluator() { 34 | console.log('> testing evaluator...'); 35 | 36 | testLiteral(); 37 | // testWhitespace(); 38 | testComment(); 39 | testStatement(); 40 | testBlockStatement(); 41 | testBinaryExpression(); 42 | // testAssignmentExpression(); 43 | testDeclarationStatement(); 44 | testIfStatement(); 45 | testRelational(); 46 | testEquality(); 47 | testLogical(); 48 | testUnary(); 49 | testIteration(); 50 | // testMember(); 51 | // testFunctionDeclaration(); 52 | // testFunctionCall(); 53 | testFunctionDeclarationAndFunctionCall(); 54 | // testClassDeclaration(); 55 | // testNewCall(); 56 | testClassDeclarationAndNewCallAndMember(); 57 | // testTuple(); 58 | // testList(); 59 | // testMap(); 60 | } 61 | 62 | export { testEvaluator }; -------------------------------------------------------------------------------- /test/test-parser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { testLiteral } from './parser/test-literal.js'; 10 | import { testWhitespace } from './parser/test-whitespace.js'; 11 | import { testComment } from './parser/test-comment.js'; 12 | import { testStatement } from './parser/test-statement.js'; 13 | import { testBlockStatement } from './parser/test-blockstatement.js'; 14 | import { testBinaryExpression } from './parser/test-binaryexpression.js'; 15 | import { testAssignmentExpression } from './parser/test-assignment.js'; 16 | import { testDeclarationStatement } from './parser/test-declaration.js'; 17 | import { testIfStatement } from './parser/test-if-statement.js'; 18 | import { testRelational } from './parser/test-relational.js'; 19 | import { testEquality } from './parser/test-equality.js'; 20 | import { testLogical } from './parser/test-logical.js'; 21 | import { testUnary } from './parser/test-unary.js'; 22 | import { testIteration } from './parser/test-iteration.js'; 23 | import { testFunctionDeclaration } from './parser/test-function-declaration.js'; 24 | import { testMember } from './parser/test-member.js'; 25 | import { testFunctionCall } from './parser/test-function-call.js'; 26 | import { testClassDeclaration } from './parser/test-class-declaration.js'; 27 | import { testNewCall } from './parser/test-new-call.js'; 28 | import { testTuple } from './parser/test-tuple.js'; 29 | import { testList } from './parser/test-list.js'; 30 | import { testMap } from './parser/test-map.js'; 31 | 32 | function testParser() { 33 | console.log('> testing parser...'); 34 | 35 | testLiteral(); 36 | testWhitespace(); 37 | testComment(); 38 | testStatement(); 39 | testBlockStatement(); 40 | testBinaryExpression(); 41 | testAssignmentExpression(); 42 | testDeclarationStatement(); 43 | testIfStatement(); 44 | testRelational(); 45 | testEquality(); 46 | testLogical(); 47 | testUnary(); 48 | testIteration(); 49 | testMember(); 50 | testFunctionDeclaration(); 51 | testFunctionCall(); 52 | testClassDeclaration(); 53 | testNewCall(); 54 | testTuple(); 55 | testList(); 56 | testMap(); 57 | } 58 | 59 | export { testParser }; -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022 Hemashushu , All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | import { testParser } from './test-parser.js'; 10 | import { testEvaluator } from './test-evaluator.js'; 11 | 12 | (() => { 13 | testParser(); 14 | testEvaluator(); 15 | 16 | console.log('> all passed.'); 17 | })(); --------------------------------------------------------------------------------