├── .gitignore ├── LICENSE ├── README.md ├── ast.ts ├── codegen.spec.ts ├── codegen.ts ├── compiler.spec.ts ├── compiler.ts ├── package.json ├── parser.spec.ts ├── parser.ts ├── pnpm-lock.yaml ├── tokenizer.spec.ts ├── tokenizer.ts ├── transformer.spec.ts ├── transformer.ts ├── traverser.spec.ts └── traverser.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 阿崔cxr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # the-tutorial-super-tiny-compiler 2 | 3 | the tutorial super tiny compiler 4 | 5 | 基于 [the-super-tiny-compiler](https://github.com/jamiebuilds/the-super-tiny-compiler) 实现 6 | 7 | ## 前端为什么要了解编译原理 8 | 9 | 当你做到高级前端的时候是经常需要和插件打交道的 10 | 而很多插件的实现原理都用到了 AST 11 | 比如 eslint、webpack、rollup 等等 12 | 13 | 拿一个大家熟悉的例子来讲 就是 vue 的template 14 | template 是如何编译成 render 函数的 15 | 这其中就需要用到编译原理的知识 16 | 17 | 当然 对于前端来讲也不需要用到多深层次的编译原理知识 18 | 通过这个库来了解一下是非常好的选择 让你对编译原理有一个认知 19 | 20 | 很多科班出身的同学可能在学校的时候也没有好好的学习编译原理 21 | 为什么 因为老师照本宣科 只给你讲理论的东西 22 | 而理论最前期个人认为是非常无聊的 都没有自己实现过 怎么能知道有什么样子的问题呢 23 | 24 | 所以一个最好的学习方式就是先实现一个非常简单的 然后从简入深 25 | 看得见摸得到的 demo 才能激发出兴趣来 26 | 27 | ## 为什么有这个库 28 | 29 | 这个库的代码是配套视频的 30 | 对于编程教学的核心来讲 实现的过程才是最有价值的 31 | 基于此呢 我决定录制视频来实现 the-super-tiny-compiler 32 | 33 | [视频链接]() 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ast.ts: -------------------------------------------------------------------------------- 1 | export enum NodeTypes { 2 | NumberLiteral = "NumberLiteral", 3 | Program = "Program", 4 | StringLiteral = "StringLiteral", 5 | CallExpression = "CallExpression", 6 | } 7 | 8 | export type ChildNode = 9 | | NumberLiteralNode 10 | | CallExpressionNode 11 | | StringLiteralNode; 12 | 13 | export interface Node { 14 | type: NodeTypes; 15 | } 16 | 17 | export interface NumberLiteralNode extends Node { 18 | type: NodeTypes.NumberLiteral; 19 | value: string; 20 | } 21 | 22 | export interface StringLiteralNode extends Node { 23 | value: string; 24 | type: NodeTypes.StringLiteral; 25 | } 26 | 27 | export interface CallExpressionNode extends Node { 28 | name: string; 29 | params: ChildNode[]; 30 | type: NodeTypes.CallExpression; 31 | context?: ChildNode[]; 32 | } 33 | 34 | export interface RootNode extends Node { 35 | body: ChildNode[]; 36 | type: NodeTypes.Program; 37 | context?: ChildNode[]; 38 | } 39 | 40 | export function createStringLiteralNode(value): StringLiteralNode { 41 | return { 42 | type: NodeTypes.StringLiteral, 43 | value, 44 | }; 45 | } 46 | 47 | export function createRootNode(): RootNode { 48 | return { 49 | type: NodeTypes.Program, 50 | body: [], 51 | }; 52 | } 53 | 54 | export function createNumberLiteralNode(value: string): NumberLiteralNode { 55 | return { 56 | type: NodeTypes.NumberLiteral, 57 | value, 58 | }; 59 | } 60 | 61 | export function createCallExpression(name): CallExpressionNode { 62 | return { 63 | type: NodeTypes.CallExpression, 64 | name, 65 | params: [], 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /codegen.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from "vitest"; 2 | import { NodeTypes } from "./ast"; 3 | import { codegen } from "./codegen"; 4 | test("codegen", () => { 5 | const ast = { 6 | type: NodeTypes.Program, 7 | body: [ 8 | { 9 | type: "ExpressionStatement", 10 | expression: { 11 | type: "CallExpression", 12 | callee: { 13 | type: "Identifier", 14 | name: "add", 15 | }, 16 | arguments: [ 17 | { 18 | type: "NumberLiteral", 19 | value: "2", 20 | }, 21 | { 22 | type: "CallExpression", 23 | callee: { 24 | type: "Identifier", 25 | name: "subtract", 26 | }, 27 | arguments: [ 28 | { 29 | type: "NumberLiteral", 30 | value: "4", 31 | }, 32 | { 33 | type: "NumberLiteral", 34 | value: "2", 35 | }, 36 | ], 37 | }, 38 | ], 39 | }, 40 | }, 41 | ], 42 | }; 43 | 44 | expect(codegen(ast)).toMatchInlineSnapshot('"add(2, subtract(4, 2));"'); 45 | }); 46 | 47 | test("two ExpressionStatement", () => { 48 | const ast = { 49 | type: NodeTypes.Program, 50 | body: [ 51 | { 52 | type: "ExpressionStatement", 53 | expression: { 54 | type: "CallExpression", 55 | callee: { 56 | type: "Identifier", 57 | name: "add", 58 | }, 59 | arguments: [ 60 | { 61 | type: "NumberLiteral", 62 | value: "2", 63 | }, 64 | { 65 | type: "CallExpression", 66 | callee: { 67 | type: "Identifier", 68 | name: "subtract", 69 | }, 70 | arguments: [ 71 | { 72 | type: "NumberLiteral", 73 | value: "4", 74 | }, 75 | { 76 | type: "NumberLiteral", 77 | value: "2", 78 | }, 79 | ], 80 | }, 81 | ], 82 | }, 83 | }, 84 | { 85 | type: "ExpressionStatement", 86 | expression: { 87 | type: "CallExpression", 88 | callee: { 89 | type: "Identifier", 90 | name: "add", 91 | }, 92 | arguments: [ 93 | { 94 | type: "NumberLiteral", 95 | value: "2", 96 | }, 97 | { 98 | type: "CallExpression", 99 | callee: { 100 | type: "Identifier", 101 | name: "subtract", 102 | }, 103 | arguments: [ 104 | { 105 | type: "NumberLiteral", 106 | value: "4", 107 | }, 108 | { 109 | type: "NumberLiteral", 110 | value: "2", 111 | }, 112 | ], 113 | }, 114 | ], 115 | }, 116 | }, 117 | ], 118 | }; 119 | 120 | expect(codegen(ast)).toMatchInlineSnapshot( 121 | '"add(2, subtract(4, 2));add(2, subtract(4, 2));"' 122 | ); 123 | }); 124 | -------------------------------------------------------------------------------- /codegen.ts: -------------------------------------------------------------------------------- 1 | export function codegen(node) { 2 | switch (node.type) { 3 | case "Program": 4 | return node.body.map(codegen).join(""); 5 | case "ExpressionStatement": 6 | return codegen(node.expression) + ";"; 7 | case "NumberLiteral": 8 | return node.value; 9 | case "CallExpression": 10 | return ( 11 | node.callee.name + "(" + node.arguments.map(codegen).join(", ") + ")" 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /compiler.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from "vitest"; 2 | import { compiler } from "./compiler"; 3 | test("compiler", () => { 4 | const code = `(add 2 (subtract 4 2))`; 5 | 6 | expect(compiler(code)).toBe("add(2, subtract(4, 2));"); 7 | }); 8 | -------------------------------------------------------------------------------- /compiler.ts: -------------------------------------------------------------------------------- 1 | import { tokenizer } from "./tokenizer"; 2 | import { parser } from "./parser"; 3 | import { transformer } from "./transformer"; 4 | import { codegen } from "./codegen"; 5 | export function compiler(code: string) { 6 | const tokens = tokenizer(code); 7 | const ast = parser(tokens); 8 | const transformedAst = transformer(ast); 9 | return codegen(transformedAst); 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "vitest": "^0.22.1" 4 | }, 5 | "name": "the-tutorial-super-tiny-compiler", 6 | "description": "the tutorial super tiny compiler", 7 | "version": "1.0.0", 8 | "main": "index.js", 9 | "scripts": { 10 | "test": "vitest --run" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/cuixiaorui/the-tutorial-super-tiny-compiler.git" 15 | }, 16 | "keywords": [], 17 | "author": "", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/cuixiaorui/the-tutorial-super-tiny-compiler/issues" 21 | }, 22 | "homepage": "https://github.com/cuixiaorui/the-tutorial-super-tiny-compiler#readme" 23 | } -------------------------------------------------------------------------------- /parser.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { NodeTypes } from "./ast"; 3 | import { parser } from "./parser"; 4 | import { TokenTypes } from "./tokenizer"; 5 | 6 | describe("parser", () => { 7 | it("parser tokens to ast", () => { 8 | const tokens = [ 9 | { type: TokenTypes.Paren, value: "(" }, 10 | { type: TokenTypes.Name, value: "add" }, 11 | { type: TokenTypes.Number, value: "2" }, 12 | { type: TokenTypes.Paren, value: "(" }, 13 | { type: TokenTypes.Name, value: "subtract" }, 14 | { type: TokenTypes.Number, value: "4" }, 15 | { type: TokenTypes.Number, value: "2" }, 16 | { type: TokenTypes.Paren, value: ")" }, 17 | { type: TokenTypes.Paren, value: ")" }, 18 | ]; 19 | const ast = { 20 | type: NodeTypes.Program, 21 | body: [ 22 | { 23 | type: NodeTypes.CallExpression, 24 | name: "add", 25 | params: [ 26 | { 27 | type: NodeTypes.NumberLiteral, 28 | value: "2", 29 | }, 30 | { 31 | type: NodeTypes.CallExpression, 32 | name: "subtract", 33 | params: [ 34 | { 35 | type: NodeTypes.NumberLiteral, 36 | value: "4", 37 | }, 38 | { 39 | type: NodeTypes.NumberLiteral, 40 | value: "2", 41 | }, 42 | ], 43 | }, 44 | ], 45 | }, 46 | ], 47 | }; 48 | expect(parser(tokens)).toEqual(ast); 49 | }); 50 | 51 | it("number", () => { 52 | const tokens = [{ type: TokenTypes.Number, value: "2" }]; 53 | 54 | const ast = { 55 | type: NodeTypes.Program, 56 | body: [ 57 | { 58 | type: NodeTypes.NumberLiteral, 59 | value: "2", 60 | }, 61 | ], 62 | }; 63 | expect(parser(tokens)).toEqual(ast); 64 | }); 65 | 66 | it("name", () => { 67 | const tokens = [{ type: TokenTypes.String, value: "hello" }]; 68 | 69 | const ast = { 70 | type: NodeTypes.Program, 71 | body: [ 72 | { 73 | type: NodeTypes.StringLiteral, 74 | value: "hello", 75 | }, 76 | ], 77 | }; 78 | expect(parser(tokens)).toEqual(ast); 79 | }); 80 | 81 | it("call expression (add 1 1)", () => { 82 | const tokens = [ 83 | { type: TokenTypes.Paren, value: "(" }, 84 | { type: TokenTypes.Name, value: "add" }, 85 | { type: TokenTypes.Number, value: "1" }, 86 | { type: TokenTypes.Number, value: "1" }, 87 | { type: TokenTypes.Paren, value: ")" }, 88 | ]; 89 | const ast = { 90 | type: NodeTypes.Program, 91 | body: [ 92 | { 93 | type: NodeTypes.CallExpression, 94 | name: "add", 95 | params: [ 96 | { 97 | type: NodeTypes.NumberLiteral, 98 | value: "1", 99 | }, 100 | { 101 | type: NodeTypes.NumberLiteral, 102 | value: "1", 103 | }, 104 | ], 105 | }, 106 | ], 107 | }; 108 | 109 | expect(parser(tokens)).toEqual(ast); 110 | }); 111 | }); 112 | -------------------------------------------------------------------------------- /parser.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createRootNode, 3 | createStringLiteralNode, 4 | createNumberLiteralNode, 5 | createCallExpression, 6 | } from "./ast"; 7 | import { Token, TokenTypes } from "./tokenizer"; 8 | 9 | export function parser(tokens: Token[]) { 10 | const root = createRootNode(); 11 | 12 | let current = 0; 13 | 14 | function walk() { 15 | let token = tokens[current]; 16 | 17 | if (token.type === TokenTypes.Number) { 18 | current++; 19 | 20 | return createNumberLiteralNode(token.value); 21 | } 22 | 23 | if (token.type === TokenTypes.String) { 24 | current++; 25 | 26 | return createStringLiteralNode(token.value); 27 | } 28 | 29 | if (token.type === TokenTypes.Paren && token.value === "(") { 30 | token = tokens[++current]; 31 | 32 | let node = createCallExpression(token.value); 33 | 34 | // 上一个 token 已经使用完了 所以我们还需要在移动下位置 35 | token = tokens[++current]; 36 | // params 37 | while ( 38 | // token.type !== TokenType.paren || 39 | // (token.type === TokenType.paren && token.value !== ")") 40 | !(token.type === TokenTypes.Paren && token.value === ")") 41 | ) { 42 | node.params.push(walk()); 43 | token = tokens[current]; 44 | } 45 | 46 | // 跳过 ) 47 | current++; 48 | 49 | return node; 50 | } 51 | 52 | throw new Error(`识别不了的 token: ${token}`); 53 | } 54 | 55 | while (current < tokens.length) { 56 | root.body.push(walk()); 57 | } 58 | 59 | return root; 60 | } 61 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.3 2 | 3 | specifiers: 4 | vitest: ^0.22.1 5 | 6 | devDependencies: 7 | vitest: registry.npmmirror.com/vitest/0.22.1 8 | 9 | packages: 10 | 11 | registry.npmmirror.com/@esbuild/linux-loong64/0.14.54: 12 | resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz} 13 | name: '@esbuild/linux-loong64' 14 | version: 0.14.54 15 | engines: {node: '>=12'} 16 | cpu: [loong64] 17 | os: [linux] 18 | requiresBuild: true 19 | dev: true 20 | optional: true 21 | 22 | registry.npmmirror.com/@types/chai-subset/1.3.3: 23 | resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/chai-subset/-/chai-subset-1.3.3.tgz} 24 | name: '@types/chai-subset' 25 | version: 1.3.3 26 | dependencies: 27 | '@types/chai': registry.npmmirror.com/@types/chai/4.3.3 28 | dev: true 29 | 30 | registry.npmmirror.com/@types/chai/4.3.3: 31 | resolution: {integrity: sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/chai/-/chai-4.3.3.tgz} 32 | name: '@types/chai' 33 | version: 4.3.3 34 | dev: true 35 | 36 | registry.npmmirror.com/@types/node/18.7.6: 37 | resolution: {integrity: sha512-EdxgKRXgYsNITy5mjjXjVE/CS8YENSdhiagGrLqjG0pvA2owgJ6i4l7wy/PFZGC0B1/H20lWKN7ONVDNYDZm7A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/node/-/node-18.7.6.tgz} 38 | name: '@types/node' 39 | version: 18.7.6 40 | dev: true 41 | 42 | registry.npmmirror.com/assertion-error/1.1.0: 43 | resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/assertion-error/-/assertion-error-1.1.0.tgz} 44 | name: assertion-error 45 | version: 1.1.0 46 | dev: true 47 | 48 | registry.npmmirror.com/chai/4.3.6: 49 | resolution: {integrity: sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/chai/-/chai-4.3.6.tgz} 50 | name: chai 51 | version: 4.3.6 52 | engines: {node: '>=4'} 53 | dependencies: 54 | assertion-error: registry.npmmirror.com/assertion-error/1.1.0 55 | check-error: registry.npmmirror.com/check-error/1.0.2 56 | deep-eql: registry.npmmirror.com/deep-eql/3.0.1 57 | get-func-name: registry.npmmirror.com/get-func-name/2.0.0 58 | loupe: registry.npmmirror.com/loupe/2.3.4 59 | pathval: registry.npmmirror.com/pathval/1.1.1 60 | type-detect: registry.npmmirror.com/type-detect/4.0.8 61 | dev: true 62 | 63 | registry.npmmirror.com/check-error/1.0.2: 64 | resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/check-error/-/check-error-1.0.2.tgz} 65 | name: check-error 66 | version: 1.0.2 67 | dev: true 68 | 69 | registry.npmmirror.com/debug/4.3.4: 70 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz} 71 | name: debug 72 | version: 4.3.4 73 | engines: {node: '>=6.0'} 74 | peerDependencies: 75 | supports-color: '*' 76 | peerDependenciesMeta: 77 | supports-color: 78 | optional: true 79 | dependencies: 80 | ms: registry.npmmirror.com/ms/2.1.2 81 | dev: true 82 | 83 | registry.npmmirror.com/deep-eql/3.0.1: 84 | resolution: {integrity: sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/deep-eql/-/deep-eql-3.0.1.tgz} 85 | name: deep-eql 86 | version: 3.0.1 87 | engines: {node: '>=0.12'} 88 | dependencies: 89 | type-detect: registry.npmmirror.com/type-detect/4.0.8 90 | dev: true 91 | 92 | registry.npmmirror.com/esbuild-android-64/0.14.54: 93 | resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz} 94 | name: esbuild-android-64 95 | version: 0.14.54 96 | engines: {node: '>=12'} 97 | cpu: [x64] 98 | os: [android] 99 | requiresBuild: true 100 | dev: true 101 | optional: true 102 | 103 | registry.npmmirror.com/esbuild-android-arm64/0.14.54: 104 | resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz} 105 | name: esbuild-android-arm64 106 | version: 0.14.54 107 | engines: {node: '>=12'} 108 | cpu: [arm64] 109 | os: [android] 110 | requiresBuild: true 111 | dev: true 112 | optional: true 113 | 114 | registry.npmmirror.com/esbuild-darwin-64/0.14.54: 115 | resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz} 116 | name: esbuild-darwin-64 117 | version: 0.14.54 118 | engines: {node: '>=12'} 119 | cpu: [x64] 120 | os: [darwin] 121 | requiresBuild: true 122 | dev: true 123 | optional: true 124 | 125 | registry.npmmirror.com/esbuild-darwin-arm64/0.14.54: 126 | resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz} 127 | name: esbuild-darwin-arm64 128 | version: 0.14.54 129 | engines: {node: '>=12'} 130 | cpu: [arm64] 131 | os: [darwin] 132 | requiresBuild: true 133 | dev: true 134 | optional: true 135 | 136 | registry.npmmirror.com/esbuild-freebsd-64/0.14.54: 137 | resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz} 138 | name: esbuild-freebsd-64 139 | version: 0.14.54 140 | engines: {node: '>=12'} 141 | cpu: [x64] 142 | os: [freebsd] 143 | requiresBuild: true 144 | dev: true 145 | optional: true 146 | 147 | registry.npmmirror.com/esbuild-freebsd-arm64/0.14.54: 148 | resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz} 149 | name: esbuild-freebsd-arm64 150 | version: 0.14.54 151 | engines: {node: '>=12'} 152 | cpu: [arm64] 153 | os: [freebsd] 154 | requiresBuild: true 155 | dev: true 156 | optional: true 157 | 158 | registry.npmmirror.com/esbuild-linux-32/0.14.54: 159 | resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz} 160 | name: esbuild-linux-32 161 | version: 0.14.54 162 | engines: {node: '>=12'} 163 | cpu: [ia32] 164 | os: [linux] 165 | requiresBuild: true 166 | dev: true 167 | optional: true 168 | 169 | registry.npmmirror.com/esbuild-linux-64/0.14.54: 170 | resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz} 171 | name: esbuild-linux-64 172 | version: 0.14.54 173 | engines: {node: '>=12'} 174 | cpu: [x64] 175 | os: [linux] 176 | requiresBuild: true 177 | dev: true 178 | optional: true 179 | 180 | registry.npmmirror.com/esbuild-linux-arm/0.14.54: 181 | resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz} 182 | name: esbuild-linux-arm 183 | version: 0.14.54 184 | engines: {node: '>=12'} 185 | cpu: [arm] 186 | os: [linux] 187 | requiresBuild: true 188 | dev: true 189 | optional: true 190 | 191 | registry.npmmirror.com/esbuild-linux-arm64/0.14.54: 192 | resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz} 193 | name: esbuild-linux-arm64 194 | version: 0.14.54 195 | engines: {node: '>=12'} 196 | cpu: [arm64] 197 | os: [linux] 198 | requiresBuild: true 199 | dev: true 200 | optional: true 201 | 202 | registry.npmmirror.com/esbuild-linux-mips64le/0.14.54: 203 | resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz} 204 | name: esbuild-linux-mips64le 205 | version: 0.14.54 206 | engines: {node: '>=12'} 207 | cpu: [mips64el] 208 | os: [linux] 209 | requiresBuild: true 210 | dev: true 211 | optional: true 212 | 213 | registry.npmmirror.com/esbuild-linux-ppc64le/0.14.54: 214 | resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz} 215 | name: esbuild-linux-ppc64le 216 | version: 0.14.54 217 | engines: {node: '>=12'} 218 | cpu: [ppc64] 219 | os: [linux] 220 | requiresBuild: true 221 | dev: true 222 | optional: true 223 | 224 | registry.npmmirror.com/esbuild-linux-riscv64/0.14.54: 225 | resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz} 226 | name: esbuild-linux-riscv64 227 | version: 0.14.54 228 | engines: {node: '>=12'} 229 | cpu: [riscv64] 230 | os: [linux] 231 | requiresBuild: true 232 | dev: true 233 | optional: true 234 | 235 | registry.npmmirror.com/esbuild-linux-s390x/0.14.54: 236 | resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz} 237 | name: esbuild-linux-s390x 238 | version: 0.14.54 239 | engines: {node: '>=12'} 240 | cpu: [s390x] 241 | os: [linux] 242 | requiresBuild: true 243 | dev: true 244 | optional: true 245 | 246 | registry.npmmirror.com/esbuild-netbsd-64/0.14.54: 247 | resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz} 248 | name: esbuild-netbsd-64 249 | version: 0.14.54 250 | engines: {node: '>=12'} 251 | cpu: [x64] 252 | os: [netbsd] 253 | requiresBuild: true 254 | dev: true 255 | optional: true 256 | 257 | registry.npmmirror.com/esbuild-openbsd-64/0.14.54: 258 | resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz} 259 | name: esbuild-openbsd-64 260 | version: 0.14.54 261 | engines: {node: '>=12'} 262 | cpu: [x64] 263 | os: [openbsd] 264 | requiresBuild: true 265 | dev: true 266 | optional: true 267 | 268 | registry.npmmirror.com/esbuild-sunos-64/0.14.54: 269 | resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz} 270 | name: esbuild-sunos-64 271 | version: 0.14.54 272 | engines: {node: '>=12'} 273 | cpu: [x64] 274 | os: [sunos] 275 | requiresBuild: true 276 | dev: true 277 | optional: true 278 | 279 | registry.npmmirror.com/esbuild-windows-32/0.14.54: 280 | resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz} 281 | name: esbuild-windows-32 282 | version: 0.14.54 283 | engines: {node: '>=12'} 284 | cpu: [ia32] 285 | os: [win32] 286 | requiresBuild: true 287 | dev: true 288 | optional: true 289 | 290 | registry.npmmirror.com/esbuild-windows-64/0.14.54: 291 | resolution: {integrity: sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz} 292 | name: esbuild-windows-64 293 | version: 0.14.54 294 | engines: {node: '>=12'} 295 | cpu: [x64] 296 | os: [win32] 297 | requiresBuild: true 298 | dev: true 299 | optional: true 300 | 301 | registry.npmmirror.com/esbuild-windows-arm64/0.14.54: 302 | resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz} 303 | name: esbuild-windows-arm64 304 | version: 0.14.54 305 | engines: {node: '>=12'} 306 | cpu: [arm64] 307 | os: [win32] 308 | requiresBuild: true 309 | dev: true 310 | optional: true 311 | 312 | registry.npmmirror.com/esbuild/0.14.54: 313 | resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild/-/esbuild-0.14.54.tgz} 314 | name: esbuild 315 | version: 0.14.54 316 | engines: {node: '>=12'} 317 | hasBin: true 318 | requiresBuild: true 319 | optionalDependencies: 320 | '@esbuild/linux-loong64': registry.npmmirror.com/@esbuild/linux-loong64/0.14.54 321 | esbuild-android-64: registry.npmmirror.com/esbuild-android-64/0.14.54 322 | esbuild-android-arm64: registry.npmmirror.com/esbuild-android-arm64/0.14.54 323 | esbuild-darwin-64: registry.npmmirror.com/esbuild-darwin-64/0.14.54 324 | esbuild-darwin-arm64: registry.npmmirror.com/esbuild-darwin-arm64/0.14.54 325 | esbuild-freebsd-64: registry.npmmirror.com/esbuild-freebsd-64/0.14.54 326 | esbuild-freebsd-arm64: registry.npmmirror.com/esbuild-freebsd-arm64/0.14.54 327 | esbuild-linux-32: registry.npmmirror.com/esbuild-linux-32/0.14.54 328 | esbuild-linux-64: registry.npmmirror.com/esbuild-linux-64/0.14.54 329 | esbuild-linux-arm: registry.npmmirror.com/esbuild-linux-arm/0.14.54 330 | esbuild-linux-arm64: registry.npmmirror.com/esbuild-linux-arm64/0.14.54 331 | esbuild-linux-mips64le: registry.npmmirror.com/esbuild-linux-mips64le/0.14.54 332 | esbuild-linux-ppc64le: registry.npmmirror.com/esbuild-linux-ppc64le/0.14.54 333 | esbuild-linux-riscv64: registry.npmmirror.com/esbuild-linux-riscv64/0.14.54 334 | esbuild-linux-s390x: registry.npmmirror.com/esbuild-linux-s390x/0.14.54 335 | esbuild-netbsd-64: registry.npmmirror.com/esbuild-netbsd-64/0.14.54 336 | esbuild-openbsd-64: registry.npmmirror.com/esbuild-openbsd-64/0.14.54 337 | esbuild-sunos-64: registry.npmmirror.com/esbuild-sunos-64/0.14.54 338 | esbuild-windows-32: registry.npmmirror.com/esbuild-windows-32/0.14.54 339 | esbuild-windows-64: registry.npmmirror.com/esbuild-windows-64/0.14.54 340 | esbuild-windows-arm64: registry.npmmirror.com/esbuild-windows-arm64/0.14.54 341 | dev: true 342 | 343 | registry.npmmirror.com/fsevents/2.3.2: 344 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz} 345 | name: fsevents 346 | version: 2.3.2 347 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 348 | os: [darwin] 349 | requiresBuild: true 350 | dev: true 351 | optional: true 352 | 353 | registry.npmmirror.com/function-bind/1.1.1: 354 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz} 355 | name: function-bind 356 | version: 1.1.1 357 | dev: true 358 | 359 | registry.npmmirror.com/get-func-name/2.0.0: 360 | resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/get-func-name/-/get-func-name-2.0.0.tgz} 361 | name: get-func-name 362 | version: 2.0.0 363 | dev: true 364 | 365 | registry.npmmirror.com/has/1.0.3: 366 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/has/-/has-1.0.3.tgz} 367 | name: has 368 | version: 1.0.3 369 | engines: {node: '>= 0.4.0'} 370 | dependencies: 371 | function-bind: registry.npmmirror.com/function-bind/1.1.1 372 | dev: true 373 | 374 | registry.npmmirror.com/is-core-module/2.10.0: 375 | resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/is-core-module/-/is-core-module-2.10.0.tgz} 376 | name: is-core-module 377 | version: 2.10.0 378 | dependencies: 379 | has: registry.npmmirror.com/has/1.0.3 380 | dev: true 381 | 382 | registry.npmmirror.com/local-pkg/0.4.2: 383 | resolution: {integrity: sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/local-pkg/-/local-pkg-0.4.2.tgz} 384 | name: local-pkg 385 | version: 0.4.2 386 | engines: {node: '>=14'} 387 | dev: true 388 | 389 | registry.npmmirror.com/loupe/2.3.4: 390 | resolution: {integrity: sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/loupe/-/loupe-2.3.4.tgz} 391 | name: loupe 392 | version: 2.3.4 393 | dependencies: 394 | get-func-name: registry.npmmirror.com/get-func-name/2.0.0 395 | dev: true 396 | 397 | registry.npmmirror.com/ms/2.1.2: 398 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz} 399 | name: ms 400 | version: 2.1.2 401 | dev: true 402 | 403 | registry.npmmirror.com/nanoid/3.3.4: 404 | resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz} 405 | name: nanoid 406 | version: 3.3.4 407 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 408 | hasBin: true 409 | dev: true 410 | 411 | registry.npmmirror.com/path-parse/1.0.7: 412 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz} 413 | name: path-parse 414 | version: 1.0.7 415 | dev: true 416 | 417 | registry.npmmirror.com/pathval/1.1.1: 418 | resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/pathval/-/pathval-1.1.1.tgz} 419 | name: pathval 420 | version: 1.1.1 421 | dev: true 422 | 423 | registry.npmmirror.com/picocolors/1.0.0: 424 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz} 425 | name: picocolors 426 | version: 1.0.0 427 | dev: true 428 | 429 | registry.npmmirror.com/postcss/8.4.16: 430 | resolution: {integrity: sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/postcss/-/postcss-8.4.16.tgz} 431 | name: postcss 432 | version: 8.4.16 433 | engines: {node: ^10 || ^12 || >=14} 434 | dependencies: 435 | nanoid: registry.npmmirror.com/nanoid/3.3.4 436 | picocolors: registry.npmmirror.com/picocolors/1.0.0 437 | source-map-js: registry.npmmirror.com/source-map-js/1.0.2 438 | dev: true 439 | 440 | registry.npmmirror.com/resolve/1.22.1: 441 | resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz} 442 | name: resolve 443 | version: 1.22.1 444 | hasBin: true 445 | dependencies: 446 | is-core-module: registry.npmmirror.com/is-core-module/2.10.0 447 | path-parse: registry.npmmirror.com/path-parse/1.0.7 448 | supports-preserve-symlinks-flag: registry.npmmirror.com/supports-preserve-symlinks-flag/1.0.0 449 | dev: true 450 | 451 | registry.npmmirror.com/rollup/2.77.3: 452 | resolution: {integrity: sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/rollup/-/rollup-2.77.3.tgz} 453 | name: rollup 454 | version: 2.77.3 455 | engines: {node: '>=10.0.0'} 456 | hasBin: true 457 | optionalDependencies: 458 | fsevents: registry.npmmirror.com/fsevents/2.3.2 459 | dev: true 460 | 461 | registry.npmmirror.com/source-map-js/1.0.2: 462 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz} 463 | name: source-map-js 464 | version: 1.0.2 465 | engines: {node: '>=0.10.0'} 466 | dev: true 467 | 468 | registry.npmmirror.com/supports-preserve-symlinks-flag/1.0.0: 469 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz} 470 | name: supports-preserve-symlinks-flag 471 | version: 1.0.0 472 | engines: {node: '>= 0.4'} 473 | dev: true 474 | 475 | registry.npmmirror.com/tinypool/0.2.4: 476 | resolution: {integrity: sha512-Vs3rhkUH6Qq1t5bqtb816oT+HeJTXfwt2cbPH17sWHIYKTotQIFPk3tf2fgqRrVyMDVOc1EnPgzIxfIulXVzwQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tinypool/-/tinypool-0.2.4.tgz} 477 | name: tinypool 478 | version: 0.2.4 479 | engines: {node: '>=14.0.0'} 480 | dev: true 481 | 482 | registry.npmmirror.com/tinyspy/1.0.2: 483 | resolution: {integrity: sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tinyspy/-/tinyspy-1.0.2.tgz} 484 | name: tinyspy 485 | version: 1.0.2 486 | engines: {node: '>=14.0.0'} 487 | dev: true 488 | 489 | registry.npmmirror.com/type-detect/4.0.8: 490 | resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/type-detect/-/type-detect-4.0.8.tgz} 491 | name: type-detect 492 | version: 4.0.8 493 | engines: {node: '>=4'} 494 | dev: true 495 | 496 | registry.npmmirror.com/vite/3.0.8: 497 | resolution: {integrity: sha512-AOZ4eN7mrkJiOLuw8IA7piS4IdOQyQCA81GxGsAQvAZzMRi9ZwGB3TOaYsj4uLAWK46T5L4AfQ6InNGlxX30IQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vite/-/vite-3.0.8.tgz} 498 | name: vite 499 | version: 3.0.8 500 | engines: {node: ^14.18.0 || >=16.0.0} 501 | hasBin: true 502 | peerDependencies: 503 | less: '*' 504 | sass: '*' 505 | stylus: '*' 506 | terser: ^5.4.0 507 | peerDependenciesMeta: 508 | less: 509 | optional: true 510 | sass: 511 | optional: true 512 | stylus: 513 | optional: true 514 | terser: 515 | optional: true 516 | dependencies: 517 | esbuild: registry.npmmirror.com/esbuild/0.14.54 518 | postcss: registry.npmmirror.com/postcss/8.4.16 519 | resolve: registry.npmmirror.com/resolve/1.22.1 520 | rollup: registry.npmmirror.com/rollup/2.77.3 521 | optionalDependencies: 522 | fsevents: registry.npmmirror.com/fsevents/2.3.2 523 | dev: true 524 | 525 | registry.npmmirror.com/vitest/0.22.1: 526 | resolution: {integrity: sha512-+x28YTnSLth4KbXg7MCzoDAzPJlJex7YgiZbUh6YLp0/4PqVZ7q7/zyfdL0OaPtKTpNiQFPpMC8Y2MSzk8F7dw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vitest/-/vitest-0.22.1.tgz} 527 | name: vitest 528 | version: 0.22.1 529 | engines: {node: '>=v14.16.0'} 530 | hasBin: true 531 | peerDependencies: 532 | '@edge-runtime/vm': '*' 533 | '@vitest/browser': '*' 534 | '@vitest/ui': '*' 535 | happy-dom: '*' 536 | jsdom: '*' 537 | peerDependenciesMeta: 538 | '@edge-runtime/vm': 539 | optional: true 540 | '@vitest/browser': 541 | optional: true 542 | '@vitest/ui': 543 | optional: true 544 | happy-dom: 545 | optional: true 546 | jsdom: 547 | optional: true 548 | dependencies: 549 | '@types/chai': registry.npmmirror.com/@types/chai/4.3.3 550 | '@types/chai-subset': registry.npmmirror.com/@types/chai-subset/1.3.3 551 | '@types/node': registry.npmmirror.com/@types/node/18.7.6 552 | chai: registry.npmmirror.com/chai/4.3.6 553 | debug: registry.npmmirror.com/debug/4.3.4 554 | local-pkg: registry.npmmirror.com/local-pkg/0.4.2 555 | tinypool: registry.npmmirror.com/tinypool/0.2.4 556 | tinyspy: registry.npmmirror.com/tinyspy/1.0.2 557 | vite: registry.npmmirror.com/vite/3.0.8 558 | transitivePeerDependencies: 559 | - less 560 | - sass 561 | - stylus 562 | - supports-color 563 | - terser 564 | dev: true 565 | -------------------------------------------------------------------------------- /tokenizer.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from "vitest"; 2 | import { tokenizer, TokenTypes } from "./tokenizer"; 3 | test("tokenizer", () => { 4 | const code = `(add 2 (subtract 4 2))`; 5 | const tokens = [ 6 | { type:TokenTypes.Paren, value: "(" }, 7 | { type:TokenTypes.Name, value: "add" }, 8 | { type:TokenTypes.Number, value: "2" }, 9 | { type:TokenTypes.Paren, value: "(" }, 10 | { type:TokenTypes.Name, value: "subtract" }, 11 | { type:TokenTypes.Number, value: "4" }, 12 | { type:TokenTypes.Number, value: "2" }, 13 | { type:TokenTypes.Paren, value: ")" }, 14 | { type:TokenTypes.Paren, value: ")" }, 15 | ]; 16 | 17 | expect(tokenizer(code)).toEqual(tokens); 18 | }); 19 | 20 | test("left paren", () => { 21 | const code = `(`; 22 | const tokens = [{ type: TokenTypes.Paren, value: "(" }]; 23 | expect(tokenizer(code)).toEqual(tokens); 24 | }); 25 | 26 | test("right paren", () => { 27 | const code = `)`; 28 | const tokens = [{ type: TokenTypes.Paren, value: ")" }]; 29 | expect(tokenizer(code)).toEqual(tokens); 30 | }); 31 | 32 | test("add", () => { 33 | const code = `add`; 34 | const tokens = [{ type: TokenTypes.Name, value: "add" }]; 35 | expect(tokenizer(code)).toEqual(tokens); 36 | }); 37 | 38 | test("number", () => { 39 | const code = `22`; 40 | const tokens = [{ type: TokenTypes.Number, value: "22" }]; 41 | expect(tokenizer(code)).toEqual(tokens); 42 | }); 43 | 44 | 45 | 46 | test("(add 1 2)", () => { 47 | const code = `(add 1 2)`; 48 | const tokens = [ 49 | { type: TokenTypes.Paren, value: "(" }, 50 | { type: TokenTypes.Name, value: "add" }, 51 | { type: TokenTypes.Number, value: "1" }, 52 | { type: TokenTypes.Number, value: "2" }, 53 | { type: TokenTypes.Paren, value: ")" }, 54 | ]; 55 | 56 | expect(tokenizer(code)).toEqual(tokens); 57 | }); -------------------------------------------------------------------------------- /tokenizer.ts: -------------------------------------------------------------------------------- 1 | export enum TokenTypes { 2 | Paren, 3 | Name, 4 | Number, 5 | String 6 | } 7 | 8 | export interface Token { 9 | type: TokenTypes; 10 | value: string; 11 | } 12 | 13 | export function tokenizer(code: string) { 14 | const tokens: Token[] = []; 15 | let current = 0; 16 | 17 | while (current < code.length) { 18 | let char = code[current]; 19 | 20 | const WHITESPACE = /\s/; 21 | if (WHITESPACE.test(char)) { 22 | current++; 23 | continue; 24 | } 25 | 26 | if (char === "(") { 27 | tokens.push({ 28 | type: TokenTypes.Paren, 29 | value: char, 30 | }); 31 | current++; 32 | continue; 33 | } 34 | 35 | if (char === ")") { 36 | tokens.push({ 37 | type: TokenTypes.Paren, 38 | value: char, 39 | }); 40 | current++; 41 | continue; 42 | } 43 | 44 | const LETTERS = /[a-z]/i; 45 | if (LETTERS.test(char)) { 46 | let value = ""; 47 | while (LETTERS.test(char) && current < code.length) { 48 | value += char; 49 | char = code[++current]; 50 | } 51 | 52 | tokens.push({ 53 | type: TokenTypes.Name, 54 | value, 55 | }); 56 | } 57 | 58 | const NUMBERS = /[0-9]/; 59 | 60 | if (NUMBERS.test(char)) { 61 | let value = ""; 62 | while (NUMBERS.test(char) && current < code.length) { 63 | value += char; 64 | char = code[++current]; 65 | } 66 | 67 | tokens.push({ 68 | type: TokenTypes.Number, 69 | value, 70 | }); 71 | } 72 | } 73 | 74 | return tokens; 75 | } 76 | -------------------------------------------------------------------------------- /transformer.spec.ts: -------------------------------------------------------------------------------- 1 | import { RootNode, NodeTypes } from "./ast"; 2 | import { test, expect } from "vitest"; 3 | import { transformer } from "./transformer"; 4 | test("transformer", () => { 5 | const originalAST: RootNode = { 6 | type: NodeTypes.Program, 7 | body: [ 8 | { 9 | type: NodeTypes.CallExpression, 10 | name: "add", 11 | params: [ 12 | { 13 | type: NodeTypes.NumberLiteral, 14 | value: "2", 15 | }, 16 | { 17 | type: NodeTypes.CallExpression, 18 | name: "subtract", 19 | params: [ 20 | { 21 | type: NodeTypes.NumberLiteral, 22 | value: "4", 23 | }, 24 | { 25 | type: NodeTypes.NumberLiteral, 26 | value: "2", 27 | }, 28 | ], 29 | }, 30 | ], 31 | }, 32 | ], 33 | }; 34 | 35 | const transformedAST = { 36 | type: NodeTypes.Program, 37 | body: [ 38 | { 39 | type: "ExpressionStatement", 40 | expression: { 41 | type: "CallExpression", 42 | callee: { 43 | type: "Identifier", 44 | name: "add", 45 | }, 46 | arguments: [ 47 | { 48 | type: "NumberLiteral", 49 | value: "2", 50 | }, 51 | { 52 | type: "CallExpression", 53 | callee: { 54 | type: "Identifier", 55 | name: "subtract", 56 | }, 57 | arguments: [ 58 | { 59 | type: "NumberLiteral", 60 | value: "4", 61 | }, 62 | { 63 | type: "NumberLiteral", 64 | value: "2", 65 | }, 66 | ], 67 | }, 68 | ], 69 | }, 70 | }, 71 | ], 72 | }; 73 | 74 | expect(transformer(originalAST)).toEqual(transformedAST); 75 | }); 76 | -------------------------------------------------------------------------------- /transformer.ts: -------------------------------------------------------------------------------- 1 | import { NodeTypes, RootNode } from "./ast"; 2 | import { traverser } from "./traverser"; 3 | export function transformer(ast: RootNode) { 4 | const newAst = { 5 | type: NodeTypes.Program, 6 | body: [], 7 | }; 8 | 9 | ast.context = newAst.body; 10 | 11 | traverser(ast, { 12 | CallExpression: { 13 | enter(node, parent) { 14 | if (node.type === NodeTypes.CallExpression) { 15 | let expression: any = { 16 | type: "CallExpression", 17 | callee: { 18 | type: "Identifier", 19 | name: node.name, 20 | }, 21 | arguments: [], 22 | }; 23 | 24 | node.context = expression.arguments; 25 | 26 | if (parent?.type !== NodeTypes.CallExpression) { 27 | expression = { 28 | type: "ExpressionStatement", 29 | expression, 30 | }; 31 | } 32 | 33 | parent?.context?.push(expression); 34 | } 35 | }, 36 | }, 37 | 38 | NumberLiteral: { 39 | enter(node, parent) { 40 | if (node.type === NodeTypes.NumberLiteral) { 41 | const numberNode: any = { 42 | type: "NumberLiteral", 43 | value: node.value, 44 | }; 45 | 46 | parent?.context?.push(numberNode); 47 | } 48 | }, 49 | }, 50 | }); 51 | 52 | return newAst; 53 | } 54 | -------------------------------------------------------------------------------- /traverser.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { NodeTypes, RootNode } from "./ast"; 3 | import { traverser, Visitor } from "./traverser"; 4 | // 遍历树 5 | test("traverser", () => { 6 | const ast: RootNode = { 7 | type: NodeTypes.Program, 8 | body: [ 9 | { 10 | type: NodeTypes.CallExpression, 11 | name: "add", 12 | params: [ 13 | { 14 | type: NodeTypes.NumberLiteral, 15 | value: "2", 16 | }, 17 | { 18 | type: NodeTypes.CallExpression, 19 | name: "subtract", 20 | params: [ 21 | { 22 | type: NodeTypes.NumberLiteral, 23 | value: "4", 24 | }, 25 | { 26 | type: NodeTypes.NumberLiteral, 27 | value: "2", 28 | }, 29 | ], 30 | }, 31 | ], 32 | }, 33 | ], 34 | }; 35 | 36 | const callCounts: Array[] = []; 37 | const visitor: Visitor = { 38 | Program: { 39 | enter(node, parent) { 40 | callCounts.push(["program-enter", node.type, ""]); 41 | }, 42 | exit(node, parent) { 43 | callCounts.push(["program-exit", node.type, ""]); 44 | }, 45 | }, 46 | 47 | CallExpression: { 48 | enter(node, parent) { 49 | callCounts.push(["callExpression-enter", node.type, parent!.type]); 50 | }, 51 | exit(node, parent) { 52 | callCounts.push(["callExpression-exit", node.type, parent!.type]); 53 | }, 54 | }, 55 | 56 | NumberLiteral: { 57 | enter(node, parent) { 58 | callCounts.push(["numberLiteral-enter", node.type, parent!.type]); 59 | }, 60 | exit(node, parent) { 61 | callCounts.push(["numberLiteral-exit", node.type, parent!.type]); 62 | }, 63 | }, 64 | }; 65 | 66 | traverser(ast, visitor); 67 | 68 | expect(callCounts).toEqual([ 69 | ["program-enter", NodeTypes.Program, ""], 70 | ["callExpression-enter", NodeTypes.CallExpression, NodeTypes.Program], 71 | ["numberLiteral-enter", NodeTypes.NumberLiteral, NodeTypes.CallExpression], 72 | ["numberLiteral-exit", NodeTypes.NumberLiteral, NodeTypes.CallExpression], 73 | [ 74 | "callExpression-enter", 75 | NodeTypes.CallExpression, 76 | NodeTypes.CallExpression, 77 | ], 78 | ["numberLiteral-enter", NodeTypes.NumberLiteral, NodeTypes.CallExpression], 79 | ["numberLiteral-exit", NodeTypes.NumberLiteral, NodeTypes.CallExpression], 80 | ["numberLiteral-enter", NodeTypes.NumberLiteral, NodeTypes.CallExpression], 81 | ["numberLiteral-exit", NodeTypes.NumberLiteral, NodeTypes.CallExpression], 82 | ["callExpression-exit", NodeTypes.CallExpression, NodeTypes.CallExpression], 83 | ["callExpression-exit", NodeTypes.CallExpression, NodeTypes.Program], 84 | ["program-exit", NodeTypes.Program, ""], 85 | ]); 86 | }); 87 | -------------------------------------------------------------------------------- /traverser.ts: -------------------------------------------------------------------------------- 1 | import { ChildNode, NodeTypes, RootNode, CallExpressionNode } from "./ast"; 2 | 3 | type ParentNode = RootNode | CallExpressionNode | undefined; 4 | type MethodFn = (node: RootNode | ChildNode, parent: ParentNode) => void; 5 | interface VisitorOption { 6 | enter: MethodFn; 7 | exit?: MethodFn; 8 | } 9 | export interface Visitor { 10 | Program?: VisitorOption; 11 | NumberLiteral?: VisitorOption; 12 | CallExpression?: VisitorOption; 13 | StringLiteral?: VisitorOption; 14 | } 15 | 16 | export function traverser(rootNode: RootNode, visitor: Visitor) { 17 | // 遍历树 深度优先搜索 18 | function traverArray(array: ChildNode[], parent: ParentNode) { 19 | array.forEach((node) => { 20 | traverNode(node, parent); 21 | }); 22 | } 23 | 24 | function traverNode(node: RootNode | ChildNode, parent?: ParentNode) { 25 | // enter 26 | const methods = visitor[node.type]; 27 | if (methods) { 28 | methods.enter(node, parent); 29 | } 30 | 31 | switch (node.type) { 32 | case NodeTypes.Program: 33 | traverArray(node.body, node); 34 | break; 35 | case NodeTypes.CallExpression: 36 | traverArray(node.params, node); 37 | break; 38 | case NodeTypes.NumberLiteral: 39 | break; 40 | default: 41 | break; 42 | } 43 | 44 | // exit 45 | if (methods && methods.exit) { 46 | methods.exit(node, parent); 47 | } 48 | } 49 | traverNode(rootNode); 50 | } 51 | --------------------------------------------------------------------------------