├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── github-pages.yml │ ├── jest-test.yml │ └── npm-publish.yml ├── .gitignore ├── .husky ├── .gitignore └── pre-commit ├── .prettierrc ├── CHANGELOG.md ├── README.md ├── jest.config.js ├── package.json ├── public ├── ast.json ├── jsx-find.json ├── jsx.bnf ├── jsx.json ├── jsx.md ├── jsx.todo ├── jsx2.json ├── out-find.jsx ├── out.jsx └── visited.json ├── src ├── __tests__ │ ├── __snapshots__ │ │ ├── parser.test.ts.snap │ │ ├── transformer.test.ts.snap │ │ └── traverser.test.ts.snap │ ├── compiler.test.ts │ ├── examples │ │ ├── code-1.txt │ │ └── code.txt │ ├── parser.test.ts │ ├── tokenizer.test.ts │ ├── transformer.test.ts │ ├── traverser.test.ts │ └── utils │ │ ├── index.ts │ │ └── read.ts ├── compiler │ ├── Compiler.ts │ └── index.ts ├── index.ts ├── parser │ ├── Ast.ts │ ├── Consumer.ts │ ├── Factory.ts │ ├── Parser.ts │ ├── Tokenizer.ts │ └── index.ts ├── test.ts ├── transformer │ ├── Factory.ts │ ├── Jsx.ts │ ├── Transformer.ts │ └── index.ts └── traverser │ ├── Traverser.ts │ └── index.ts └── tsconfig.json /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | ## Description 10 | 11 | Describe your problem. 12 | 13 | ## Platform 14 | 15 | Mac or Windows. 16 | 17 | ## Version 18 | 19 | Version of extension, like v1.0.0. 20 | 21 | ## Error Log 22 | 23 | If you get an error message. 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | ## Feature request 10 | 11 | Describe your needs. 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Please provide a brief summary of the changes. If it is related to an issue, please bring the issue number, like `fixes #${number}`. 4 | 5 | ## Type of change 6 | 7 | Please select the relevant option. 8 | 9 | - [ ] Bug fix (non-breaking change which fixes an issue) 10 | - [ ] New feature (non-breaking change which adds functionality) 11 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 12 | - [ ] This change requires a documentation update -------------------------------------------------------------------------------- /.github/workflows/github-pages.yml: -------------------------------------------------------------------------------- 1 | name: Github Pages 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - 'v*.*.*' 9 | 10 | jobs: 11 | Deploy-Pages: 12 | runs-on: ubuntu-18.04 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Install Deps 18 | run: yarn install 19 | 20 | - name: Build Docs 21 | run: yarn docs 22 | 23 | - name: Deploy 24 | uses: peaceiris/actions-gh-pages@v3 25 | with: 26 | github_token: ${{ secrets.GITHUB_TOKEN }} 27 | publish_dir: ./docs 28 | -------------------------------------------------------------------------------- /.github/workflows/jest-test.yml: -------------------------------------------------------------------------------- 1 | name: Jest Tests 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - 'v*.*.*' 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | Jest-Tests: 15 | runs-on: ubuntu-18.04 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v2 19 | 20 | - name: Install Deps 21 | run: yarn install 22 | 23 | - name: Run Tests 24 | run: yarn test 25 | 26 | - name: Upload Coverage 27 | uses: codecov/codecov-action@v2 28 | with: 29 | directory: ./coverage 30 | verbose: true 31 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Npm Publish 2 | on: 3 | workflow_dispatch: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | 8 | jobs: 9 | Npm-Publish: 10 | runs-on: ubuntu-18.04 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | 15 | - name: Install Deps 16 | run: yarn install 17 | 18 | - name: publish with latest tag 19 | uses: JS-DevTools/npm-publish@v1 20 | with: 21 | token: ${{ secrets.NPM_AUTH_TOKEN }} 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .vscode/ 4 | build/ 5 | lib/ 6 | .DS_Store 7 | yarn.lock 8 | coverage -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "arrowParens": "avoid", 6 | "printWidth": 80 7 | } 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [0.1.2](https://github.com/Saber2pr/jsx-ast-parser/compare/v0.1.1...v0.1.2) (2021-10-08) 6 | 7 | 8 | ### Features 9 | 10 | * arrow function args ([ab4b82b](https://github.com/Saber2pr/jsx-ast-parser/commit/ab4b82b8082380c0b150fd90b2bda3328007b5b0)) 11 | * jsx inner expr ([f8f2c10](https://github.com/Saber2pr/jsx-ast-parser/commit/f8f2c104f942b1d9a492aea04c3d97be9294cda1)) 12 | 13 | ### [0.1.1](https://github.com/Saber2pr/jsx-ast-parser/compare/v0.0.9...v0.1.1) (2021-10-07) 14 | 15 | 16 | ### Features 17 | 18 | * tests ([55c998e](https://github.com/Saber2pr/jsx-ast-parser/commit/55c998e295b511efe8d580710420d967503b059f)) 19 | 20 | ### [0.0.9](https://github.com/Saber2pr/jsx-ast-parser/compare/v0.0.8...v0.0.9) (2021-10-06) 21 | 22 | 23 | ### Features 24 | 25 | * statement if else ([2e6b007](https://github.com/Saber2pr/jsx-ast-parser/commit/2e6b007274196e27df756aa0d28c637bcb5c445b)) 26 | * statement return ([c86b4ca](https://github.com/Saber2pr/jsx-ast-parser/commit/c86b4ca3606050935c391ec6d8ac27fbc0fd7253)) 27 | 28 | ### [0.0.8](https://github.com/Saber2pr/jsx-ast-parser/compare/v0.0.7...v0.0.8) (2021-10-05) 29 | 30 | 31 | ### Features 32 | 33 | * bnf ([5654ecf](https://github.com/Saber2pr/jsx-ast-parser/commit/5654ecfe0d984427e609c1942deda397984abb39)) 34 | 35 | ### [0.0.7](https://github.com/Saber2pr/jsx-ast-parser/compare/v0.0.6...v0.0.7) (2021-10-04) 36 | 37 | 38 | ### Features 39 | 40 | * if statement ([356b7d7](https://github.com/Saber2pr/jsx-ast-parser/commit/356b7d7a640f335882a2c9e2581a27e67542342a)) 41 | 42 | ### [0.0.6](https://github.com/Saber2pr/jsx-ast-parser/compare/v0.0.5...v0.0.6) (2021-10-04) 43 | 44 | 45 | ### Features 46 | 47 | * comment ([2bae7ad](https://github.com/Saber2pr/jsx-ast-parser/commit/2bae7ad155b42504af768bcb3728447a371931a7)) 48 | * statement define, assign ([60b2152](https://github.com/Saber2pr/jsx-ast-parser/commit/60b2152b4d469d57b2ab808c634530d29ae9a1e6)) 49 | * tests coverage ci ([332b1c1](https://github.com/Saber2pr/jsx-ast-parser/commit/332b1c1c5dc45d0aab26c22eea9ec2f792093cf8)) 50 | 51 | ### [0.0.5](https://github.com/Saber2pr/jsx-ast-parser/compare/v0.0.4...v0.0.5) (2021-10-03) 52 | 53 | 54 | ### Features 55 | 56 | * jsx props function ([2ea4c25](https://github.com/Saber2pr/jsx-ast-parser/commit/2ea4c25a2d7ebfc375feeca1e1e2a5d962874b01)) 57 | * transformExpression ([3c1274c](https://github.com/Saber2pr/jsx-ast-parser/commit/3c1274c7356b79f5948580e4b14073dc41bf6619)) 58 | 59 | ### [0.0.4](https://github.com/Saber2pr/jsx-ast-parser/compare/v0.0.3...v0.0.4) (2021-10-03) 60 | 61 | 62 | ### Features 63 | 64 | * transform factory ([0dc8204](https://github.com/Saber2pr/jsx-ast-parser/commit/0dc8204856945d68e40fcfd276a8507ef6d0e7da)) 65 | * traverse arrow function & callchain ([96ec78d](https://github.com/Saber2pr/jsx-ast-parser/commit/96ec78ddc6c4559f2175b70c80b72d6d996eecd6)) 66 | 67 | ### [0.0.3](https://github.com/Saber2pr/jsx-ast-parser/compare/v0.0.2...v0.0.3) (2021-10-02) 68 | 69 | 70 | ### Features 71 | 72 | * ArrowFunctionExpr & CallChainExpr ([a86f0f9](https://github.com/Saber2pr/jsx-ast-parser/commit/a86f0f95a67a34517f41c17a6b59fabdd4bf7c0c)) 73 | * compile arrow function & callchain ([34b079e](https://github.com/Saber2pr/jsx-ast-parser/commit/34b079eca08ac4b7f33842c8f36cd88344185af7)) 74 | * jsx props identity ([b710d52](https://github.com/Saber2pr/jsx-ast-parser/commit/b710d5224078f47d76eb9e38424664eee669f5c5)) 75 | * tests arrow function & callchain ([99bf0d4](https://github.com/Saber2pr/jsx-ast-parser/commit/99bf0d4cbf55f855e7c7bd5fdf72aa1d9f10b5b9)) 76 | 77 | ### [0.0.2](https://github.com/Saber2pr/jsx-ast-parser/compare/v0.0.1...v0.0.2) (2021-10-02) 78 | 79 | 80 | ### Features 81 | 82 | * traverser ([6cdea89](https://github.com/Saber2pr/jsx-ast-parser/commit/6cdea89f33ca05a922dedc93167e991b221a8599)) 83 | 84 | ### 0.0.1 (2021-10-02) 85 | 86 | 87 | ### Features 88 | 89 | * ast.type ([466bf53](https://github.com/Saber2pr/jsx-ast-parser/commit/466bf5344134c3644ae111c3e39370326bb5c86d)) 90 | * compiler ([10d3db4](https://github.com/Saber2pr/jsx-ast-parser/commit/10d3db4b1e65692bb7771c4f51a19c29df0528a4)) 91 | * init ([e627ca5](https://github.com/Saber2pr/jsx-ast-parser/commit/e627ca5ea51a229688b13c159ab784661ec80ba0)) 92 | * jsx props number-value, bool-value ([8a3a048](https://github.com/Saber2pr/jsx-ast-parser/commit/8a3a0482e0b325d8c695a7dfbb0f494e14edda20)) 93 | * jsx props object ([8b372b4](https://github.com/Saber2pr/jsx-ast-parser/commit/8b372b45c2c5643a2eedabdeba8dc51ac1644cd4)) 94 | * test ci ([b35a90f](https://github.com/Saber2pr/jsx-ast-parser/commit/b35a90fc9b7f862f4da44f0351d7db889a377608)) 95 | * transform LL1 tests ([3f13f9b](https://github.com/Saber2pr/jsx-ast-parser/commit/3f13f9bbc75fe57681ebbc6911cc9993bbcf3bd5)) 96 | * transformer ([da0d6b7](https://github.com/Saber2pr/jsx-ast-parser/commit/da0d6b754cda345f1770333320fc8602713f0522)) 97 | 98 | 99 | ### Bug Fixes 100 | 101 | * transformPropsExpr ([614d4a3](https://github.com/Saber2pr/jsx-ast-parser/commit/614d4a3935256c758fddf645846f5d90210af679)) 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @saber2pr/jsx-ast-parser 2 | 3 | [![npm](https://img.shields.io/npm/v/@saber2pr/jsx-ast-parser.svg?color=blue)](https://www.npmjs.com/package/@saber2pr/jsx-ast-parser) 4 | [![codecov](https://codecov.io/gh/Saber2pr/jsx-ast-parser/branch/master/graph/badge.svg?token=DI9E88OIZU)](https://codecov.io/gh/Saber2pr/jsx-ast-parser) 5 | 6 | > jsx parser by parser combinators. 7 | 8 | ```bash 9 | yarn add @saber2pr/jsx-ast-parser 10 | ``` 11 | 12 | ### Usage 13 | 14 | ```ts 15 | import { 16 | parser, 17 | transformer, 18 | compiler, 19 | traverser, 20 | } from '@saber2pr/jsx-ast-parser' 21 | 22 | const code = `
world
` 23 | 24 | const ast = parser.parse(code) // parse ast from code string 25 | const jsx = transformer.transform(ast) // transform ast to jsx 26 | 27 | // compile jsx to source code 28 | compiler.compile(jsx) === code // true 29 | 30 | // find jsx node 31 | traverser.findNode(jsx, node => transformer.isTextElement(node)) // [ { tagName: 'text', nodeValue: 'world' } ] 32 | ``` 33 | 34 | #### Help 35 | 36 | 1. [Api Docs](https://saber2pr.top/jsx-ast-parser/) 37 | 2. [Jsx Ast Viewer](https://jsx-ast-viewer.vercel.app/) 38 | 39 | - Profile 40 | - [see parser output ast.json](./public/ast.json) 41 | - [see transformer output jsx.json](./public/jsx.json) 42 | - [see compiler output out.jsx](./public/out.jsx) 43 | 44 | ### Feature 45 | 46 | #### Overview Grammar BNF 47 | 48 | [jsx.bnf](./public/jsx.bnf) 49 | 50 | #### Syntax currently supported 51 | 52 | - [ ] parser 53 | - [ ] jsx 54 | - [x] jsx opened 55 | - [x] jsx self closing 56 | - [ ] jsx props 57 | - [x] jsx props string-value 58 | - [x] jsx props number-value, bool-value 59 | - [x] jsx props object 60 | - [x] jsx props object-array 61 | - [x] jsx props string-array, number-array 62 | - [x] jsx props arrow function 63 | - [x] jsx props arrow function scope statements 64 | - [x] jsx props function 65 | - [x] jsx props function scope statements 66 | - [ ] statement 67 | - [x] call chain 68 | - [x] arrow function 69 | - [x] comment 70 | - [ ] arithmetic 71 | - [x] function 72 | - [x] define 73 | - [x] assign 74 | - [x] if else 75 | - [ ] for 76 | - [ ] while 77 | - [ ] try catch 78 | - [x] return 79 | - [ ] deconstruct 80 | - [ ] as 81 | - [ ] transformer 82 | - [x] transform jsx 83 | - [ ] traverser 84 | - [ ] traverse ast 85 | - [x] traverse jsx 86 | - [ ] compiler 87 | - [ ] compile ast 88 | - [x] compile jsx 89 | 90 | ### Why 91 | 92 | It started as a project for me to learn the principles of compilation, but now I'm confident I can make it better! I will continue to provide analysis tools for JSX code. 93 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.tsx?$': 'ts-jest', 4 | }, 5 | testRegex: '__tests__/.*.test.ts$', 6 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 7 | verbose: true, 8 | collectCoverage: true, 9 | coverageDirectory: '/coverage/', 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@saber2pr/jsx-ast-parser", 3 | "version": "0.1.2", 4 | "description": "jsx parser by parser combinators.", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/Saber2pr/jsx-ast-parser.git" 8 | }, 9 | "author": "saber2pr", 10 | "license": "MIT", 11 | "files": [ 12 | "lib" 13 | ], 14 | "publishConfig": { 15 | "access": "public", 16 | "registry": "https://registry.npmjs.org/" 17 | }, 18 | "main": "./lib/index.js", 19 | "scripts": { 20 | "test": "jest src/__tests__", 21 | "test:dev": "ts-node src/test", 22 | "start": "tsc --watch", 23 | "prepublishOnly": "tsc", 24 | "release": "standard-version", 25 | "docs": "typedoc src/index.ts", 26 | "lint": "prettier --write ./src", 27 | "prepare": "husky install" 28 | }, 29 | "dependencies": { 30 | "typescript-parsec": "^0.3.2" 31 | }, 32 | "devDependencies": { 33 | "@types/jest": "^24.0.12", 34 | "@types/node": "^16.3.3", 35 | "husky": "^7.0.2", 36 | "jest": "^24.8.0", 37 | "lint-staged": "^11.1.2", 38 | "prettier": "^2.4.1", 39 | "standard-version": "^9.3.1", 40 | "ts-jest": "^24.0.2", 41 | "ts-node": "^10.2.1", 42 | "typedoc": "^0.21.5", 43 | "typescript": "^4.3.5" 44 | }, 45 | "lint-staged": { 46 | "*.{js,jsx,ts,tsx}": [ 47 | "yarn lint", 48 | "git add ." 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /public/ast.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Program", 3 | "body": [ 4 | { 5 | "kind": "JsxExpr", 6 | "openingTag": { 7 | "kind": "OpeningTagExpr", 8 | "tagName": { 9 | "kind": "IdentityExpr", 10 | "name": "div" 11 | }, 12 | "props": [ 13 | { 14 | "kind": "PropExpr", 15 | "key": { 16 | "kind": "IdentityExpr", 17 | "name": "width" 18 | }, 19 | "value": { 20 | "kind": "JsxInnerExpr", 21 | "body": { 22 | "kind": "NumberExpr", 23 | "value": 100 24 | } 25 | } 26 | }, 27 | { 28 | "kind": "PropExpr", 29 | "key": { 30 | "kind": "IdentityExpr", 31 | "name": "contentEditable" 32 | }, 33 | "value": { 34 | "kind": "JsxInnerExpr", 35 | "body": { 36 | "kind": "IdentityExpr", 37 | "name": "true" 38 | } 39 | } 40 | }, 41 | { 42 | "kind": "PropExpr", 43 | "key": { 44 | "kind": "IdentityExpr", 45 | "name": "color" 46 | }, 47 | "value": { 48 | "kind": "JsxInnerExpr", 49 | "body": { 50 | "kind": "StringExpr", 51 | "value": "red" 52 | } 53 | } 54 | }, 55 | { 56 | "kind": "PropExpr", 57 | "key": { 58 | "kind": "IdentityExpr", 59 | "name": "arr" 60 | }, 61 | "value": { 62 | "kind": "JsxInnerExpr", 63 | "body": { 64 | "kind": "ArrayExpr", 65 | "items": [ 66 | { 67 | "kind": "NumberExpr", 68 | "value": 1 69 | }, 70 | { 71 | "kind": "NumberExpr", 72 | "value": 2 73 | } 74 | ] 75 | } 76 | } 77 | }, 78 | { 79 | "kind": "PropExpr", 80 | "key": { 81 | "kind": "IdentityExpr", 82 | "name": "style" 83 | }, 84 | "value": { 85 | "kind": "JsxInnerExpr", 86 | "body": { 87 | "kind": "ObjectExpr", 88 | "props": { 89 | "width": { 90 | "kind": "NumberExpr", 91 | "value": 100 92 | }, 93 | "color": { 94 | "kind": "StringExpr", 95 | "value": "red" 96 | }, 97 | "background": { 98 | "kind": "StringExpr", 99 | "value": "blue" 100 | }, 101 | "test": { 102 | "kind": "ObjectExpr", 103 | "props": { 104 | "color": { 105 | "kind": "StringExpr", 106 | "value": "red" 107 | } 108 | } 109 | }, 110 | "child": { 111 | "kind": "JsxExpr", 112 | "openingTag": { 113 | "kind": "OpeningTagExpr", 114 | "tagName": { 115 | "kind": "IdentityExpr", 116 | "name": "span" 117 | }, 118 | "props": [] 119 | }, 120 | "body": [ 121 | { 122 | "kind": "TextExpr", 123 | "value": "233" 124 | } 125 | ], 126 | "closingTag": { 127 | "kind": "ClosingTagExpr", 128 | "tagName": { 129 | "kind": "IdentityExpr", 130 | "name": "span" 131 | } 132 | } 133 | } 134 | } 135 | } 136 | } 137 | }, 138 | { 139 | "kind": "PropExpr", 140 | "key": { 141 | "kind": "IdentityExpr", 142 | "name": "id" 143 | }, 144 | "value": { 145 | "kind": "StringExpr", 146 | "value": "233ccc" 147 | } 148 | }, 149 | { 150 | "kind": "PropExpr", 151 | "key": { 152 | "kind": "IdentityExpr", 153 | "name": "class2Name" 154 | }, 155 | "value": { 156 | "kind": "StringExpr", 157 | "value": "qwq123" 158 | } 159 | }, 160 | { 161 | "kind": "PropExpr", 162 | "key": { 163 | "kind": "IdentityExpr", 164 | "name": "onClick" 165 | }, 166 | "value": { 167 | "kind": "JsxInnerExpr", 168 | "body": { 169 | "kind": "IdentityExpr", 170 | "name": "onClick" 171 | } 172 | } 173 | }, 174 | { 175 | "kind": "PropExpr", 176 | "key": { 177 | "kind": "IdentityExpr", 178 | "name": "onError" 179 | }, 180 | "value": { 181 | "kind": "JsxInnerExpr", 182 | "body": { 183 | "kind": "ArrowFunctionExpr", 184 | "args": [ 185 | { 186 | "kind": "IdentityExpr", 187 | "name": "error" 188 | }, 189 | { 190 | "kind": "IdentityExpr", 191 | "name": "test" 192 | } 193 | ], 194 | "body": { 195 | "kind": "BlockExpr", 196 | "body": [ 197 | { 198 | "kind": "CallChainExpr", 199 | "caller": { 200 | "kind": "IdentityExpr", 201 | "name": "console" 202 | }, 203 | "chain": [ 204 | { 205 | "kind": "IdentityExpr", 206 | "name": "log" 207 | } 208 | ], 209 | "args": [ 210 | { 211 | "kind": "IdentityExpr", 212 | "name": "error" 213 | } 214 | ] 215 | }, 216 | { 217 | "kind": "CallChainExpr", 218 | "caller": { 219 | "kind": "IdentityExpr", 220 | "name": "console" 221 | }, 222 | "chain": [ 223 | { 224 | "kind": "IdentityExpr", 225 | "name": "log" 226 | } 227 | ], 228 | "args": [] 229 | }, 230 | { 231 | "kind": "CallChainExpr", 232 | "caller": { 233 | "kind": "IdentityExpr", 234 | "name": "console" 235 | }, 236 | "chain": [ 237 | { 238 | "kind": "IdentityExpr", 239 | "name": "log" 240 | } 241 | ], 242 | "args": [ 243 | { 244 | "kind": "CallChainExpr", 245 | "caller": { 246 | "kind": "IdentityExpr", 247 | "name": "console" 248 | }, 249 | "chain": [ 250 | { 251 | "kind": "IdentityExpr", 252 | "name": "log" 253 | } 254 | ], 255 | "args": [ 256 | { 257 | "kind": "IdentityExpr", 258 | "name": "test" 259 | } 260 | ] 261 | } 262 | ] 263 | }, 264 | { 265 | "kind": "DefineVariableStatement", 266 | "type": { 267 | "kind": "KeywordExpr", 268 | "name": "const" 269 | }, 270 | "assign": { 271 | "kind": "VariableAssignExpr", 272 | "name": { 273 | "kind": "IdentityExpr", 274 | "name": "a" 275 | }, 276 | "value": { 277 | "kind": "StringExpr", 278 | "value": "a" 279 | } 280 | } 281 | }, 282 | { 283 | "kind": "DefineVariableStatement", 284 | "type": { 285 | "kind": "KeywordExpr", 286 | "name": "let" 287 | }, 288 | "assign": { 289 | "kind": "VariableAssignExpr", 290 | "name": { 291 | "kind": "IdentityExpr", 292 | "name": "b" 293 | }, 294 | "value": { 295 | "kind": "StringExpr", 296 | "value": "b" 297 | } 298 | } 299 | }, 300 | { 301 | "kind": "DefineVariableStatement", 302 | "type": { 303 | "kind": "KeywordExpr", 304 | "name": "const" 305 | }, 306 | "assign": { 307 | "kind": "VariableAssignExpr", 308 | "name": { 309 | "kind": "IdentityExpr", 310 | "name": "c" 311 | }, 312 | "value": { 313 | "kind": "StringExpr", 314 | "value": "c" 315 | } 316 | } 317 | }, 318 | { 319 | "kind": "DefineVariableStatement", 320 | "type": { 321 | "kind": "KeywordExpr", 322 | "name": "let" 323 | }, 324 | "assign": { 325 | "kind": "IdentityExpr", 326 | "name": "d" 327 | } 328 | }, 329 | { 330 | "kind": "VariableAssignExpr", 331 | "name": { 332 | "kind": "IdentityExpr", 333 | "name": "d" 334 | }, 335 | "value": { 336 | "kind": "StringExpr", 337 | "value": "d" 338 | } 339 | }, 340 | { 341 | "kind": "DefineVariableStatement", 342 | "type": { 343 | "kind": "KeywordExpr", 344 | "name": "let" 345 | }, 346 | "assign": { 347 | "kind": "IdentityExpr", 348 | "name": "a" 349 | } 350 | }, 351 | { 352 | "kind": "VariableAssignExpr", 353 | "name": { 354 | "kind": "IdentityExpr", 355 | "name": "a" 356 | }, 357 | "value": { 358 | "kind": "StringExpr", 359 | "value": "a" 360 | } 361 | }, 362 | { 363 | "kind": "IfStatement", 364 | "args": [ 365 | { 366 | "kind": "IdentityExpr", 367 | "name": "a" 368 | } 369 | ], 370 | "body": { 371 | "kind": "BlockExpr", 372 | "body": [ 373 | { 374 | "kind": "CallChainExpr", 375 | "caller": { 376 | "kind": "IdentityExpr", 377 | "name": "console" 378 | }, 379 | "chain": [ 380 | { 381 | "kind": "IdentityExpr", 382 | "name": "log" 383 | } 384 | ], 385 | "args": [ 386 | { 387 | "kind": "IdentityExpr", 388 | "name": "a" 389 | } 390 | ] 391 | } 392 | ] 393 | } 394 | }, 395 | { 396 | "kind": "IfStatement", 397 | "args": [ 398 | { 399 | "kind": "IdentityExpr", 400 | "name": "b" 401 | } 402 | ], 403 | "body": { 404 | "kind": "BlockExpr", 405 | "body": [ 406 | { 407 | "kind": "CallChainExpr", 408 | "caller": { 409 | "kind": "IdentityExpr", 410 | "name": "console" 411 | }, 412 | "chain": [ 413 | { 414 | "kind": "IdentityExpr", 415 | "name": "log" 416 | } 417 | ], 418 | "args": [ 419 | { 420 | "kind": "IdentityExpr", 421 | "name": "b" 422 | } 423 | ] 424 | } 425 | ] 426 | }, 427 | "els": { 428 | "kind": "BlockExpr", 429 | "body": [ 430 | { 431 | "kind": "CallChainExpr", 432 | "caller": { 433 | "kind": "IdentityExpr", 434 | "name": "console" 435 | }, 436 | "chain": [ 437 | { 438 | "kind": "IdentityExpr", 439 | "name": "log" 440 | } 441 | ], 442 | "args": [ 443 | { 444 | "kind": "IdentityExpr", 445 | "name": "c" 446 | } 447 | ] 448 | } 449 | ] 450 | } 451 | }, 452 | { 453 | "kind": "IfStatement", 454 | "args": [ 455 | { 456 | "kind": "IdentityExpr", 457 | "name": "b" 458 | } 459 | ], 460 | "body": { 461 | "kind": "BlockExpr", 462 | "body": [ 463 | { 464 | "kind": "CallChainExpr", 465 | "caller": { 466 | "kind": "IdentityExpr", 467 | "name": "console" 468 | }, 469 | "chain": [ 470 | { 471 | "kind": "IdentityExpr", 472 | "name": "log" 473 | } 474 | ], 475 | "args": [ 476 | { 477 | "kind": "IdentityExpr", 478 | "name": "b" 479 | } 480 | ] 481 | } 482 | ] 483 | }, 484 | "els": { 485 | "kind": "IfStatement", 486 | "args": [ 487 | { 488 | "kind": "IdentityExpr", 489 | "name": "c" 490 | } 491 | ], 492 | "body": { 493 | "kind": "BlockExpr", 494 | "body": [ 495 | { 496 | "kind": "CallChainExpr", 497 | "caller": { 498 | "kind": "IdentityExpr", 499 | "name": "console" 500 | }, 501 | "chain": [ 502 | { 503 | "kind": "IdentityExpr", 504 | "name": "log" 505 | } 506 | ], 507 | "args": [ 508 | { 509 | "kind": "IdentityExpr", 510 | "name": "c" 511 | } 512 | ] 513 | } 514 | ] 515 | } 516 | } 517 | }, 518 | { 519 | "kind": "IfStatement", 520 | "args": [ 521 | { 522 | "kind": "IdentityExpr", 523 | "name": "b" 524 | } 525 | ], 526 | "body": { 527 | "kind": "BlockExpr", 528 | "body": [ 529 | { 530 | "kind": "CallChainExpr", 531 | "caller": { 532 | "kind": "IdentityExpr", 533 | "name": "console" 534 | }, 535 | "chain": [ 536 | { 537 | "kind": "IdentityExpr", 538 | "name": "log" 539 | } 540 | ], 541 | "args": [ 542 | { 543 | "kind": "IdentityExpr", 544 | "name": "b" 545 | } 546 | ] 547 | } 548 | ] 549 | }, 550 | "els": { 551 | "kind": "IfStatement", 552 | "args": [ 553 | { 554 | "kind": "IdentityExpr", 555 | "name": "c" 556 | } 557 | ], 558 | "body": { 559 | "kind": "BlockExpr", 560 | "body": [ 561 | { 562 | "kind": "CallChainExpr", 563 | "caller": { 564 | "kind": "IdentityExpr", 565 | "name": "console" 566 | }, 567 | "chain": [ 568 | { 569 | "kind": "IdentityExpr", 570 | "name": "log" 571 | } 572 | ], 573 | "args": [ 574 | { 575 | "kind": "IdentityExpr", 576 | "name": "c" 577 | } 578 | ] 579 | } 580 | ] 581 | }, 582 | "els": { 583 | "kind": "BlockExpr", 584 | "body": [ 585 | { 586 | "kind": "CallChainExpr", 587 | "caller": { 588 | "kind": "IdentityExpr", 589 | "name": "console" 590 | }, 591 | "chain": [ 592 | { 593 | "kind": "IdentityExpr", 594 | "name": "log" 595 | } 596 | ], 597 | "args": [ 598 | { 599 | "kind": "IdentityExpr", 600 | "name": "d" 601 | } 602 | ] 603 | } 604 | ] 605 | } 606 | } 607 | } 608 | ] 609 | } 610 | } 611 | } 612 | }, 613 | { 614 | "kind": "PropExpr", 615 | "key": { 616 | "kind": "IdentityExpr", 617 | "name": "onSubmit" 618 | }, 619 | "value": { 620 | "kind": "JsxInnerExpr", 621 | "body": { 622 | "kind": "FunctionExpr", 623 | "name": { 624 | "kind": "IdentityExpr", 625 | "name": "test" 626 | }, 627 | "args": [], 628 | "body": { 629 | "kind": "BlockExpr", 630 | "body": [ 631 | { 632 | "kind": "CallChainExpr", 633 | "caller": { 634 | "kind": "IdentityExpr", 635 | "name": "console" 636 | }, 637 | "chain": [ 638 | { 639 | "kind": "IdentityExpr", 640 | "name": "log" 641 | } 642 | ], 643 | "args": [] 644 | }, 645 | { 646 | "kind": "ReturnStatement", 647 | "value": { 648 | "kind": "NumberExpr", 649 | "value": 233 650 | } 651 | } 652 | ] 653 | } 654 | } 655 | } 656 | } 657 | ] 658 | }, 659 | "body": [ 660 | { 661 | "kind": "JsxSelfClosingExpr", 662 | "tagName": { 663 | "kind": "IdentityExpr", 664 | "name": "List" 665 | }, 666 | "props": [ 667 | { 668 | "kind": "PropExpr", 669 | "key": { 670 | "kind": "IdentityExpr", 671 | "name": "list" 672 | }, 673 | "value": { 674 | "kind": "JsxInnerExpr", 675 | "body": { 676 | "kind": "ArrayExpr", 677 | "items": [ 678 | { 679 | "kind": "ObjectExpr", 680 | "props": { 681 | "content": { 682 | "kind": "JsxExpr", 683 | "openingTag": { 684 | "kind": "OpeningTagExpr", 685 | "tagName": { 686 | "kind": "IdentityExpr", 687 | "name": "View" 688 | }, 689 | "props": [ 690 | { 691 | "kind": "PropExpr", 692 | "key": { 693 | "kind": "IdentityExpr", 694 | "name": "color" 695 | }, 696 | "value": { 697 | "kind": "StringExpr", 698 | "value": "red" 699 | } 700 | } 701 | ] 702 | }, 703 | "body": [ 704 | { 705 | "kind": "TextExpr", 706 | "value": "233" 707 | } 708 | ], 709 | "closingTag": { 710 | "kind": "ClosingTagExpr", 711 | "tagName": { 712 | "kind": "IdentityExpr", 713 | "name": "View" 714 | } 715 | } 716 | }, 717 | "logo": { 718 | "kind": "JsxSelfClosingExpr", 719 | "tagName": { 720 | "kind": "IdentityExpr", 721 | "name": "Image" 722 | }, 723 | "props": [ 724 | { 725 | "kind": "PropExpr", 726 | "key": { 727 | "kind": "IdentityExpr", 728 | "name": "mode" 729 | }, 730 | "value": { 731 | "kind": "StringExpr", 732 | "value": "test" 733 | } 734 | } 735 | ] 736 | } 737 | } 738 | }, 739 | { 740 | "kind": "ObjectExpr", 741 | "props": { 742 | "content": { 743 | "kind": "JsxSelfClosingExpr", 744 | "tagName": { 745 | "kind": "IdentityExpr", 746 | "name": "View" 747 | }, 748 | "props": [] 749 | } 750 | } 751 | } 752 | ] 753 | } 754 | } 755 | } 756 | ] 757 | }, 758 | { 759 | "kind": "JsxSelfClosingExpr", 760 | "tagName": { 761 | "kind": "IdentityExpr", 762 | "name": "div" 763 | }, 764 | "props": [] 765 | }, 766 | { 767 | "kind": "JsxSelfClosingExpr", 768 | "tagName": { 769 | "kind": "IdentityExpr", 770 | "name": "div" 771 | }, 772 | "props": [ 773 | { 774 | "kind": "PropExpr", 775 | "key": { 776 | "kind": "IdentityExpr", 777 | "name": "id" 778 | }, 779 | "value": { 780 | "kind": "StringExpr", 781 | "value": "qwq" 782 | } 783 | } 784 | ] 785 | }, 786 | { 787 | "kind": "JsxExpr", 788 | "openingTag": { 789 | "kind": "OpeningTagExpr", 790 | "tagName": { 791 | "kind": "IdentityExpr", 792 | "name": "span" 793 | }, 794 | "props": [] 795 | }, 796 | "body": [ 797 | { 798 | "kind": "TextExpr", 799 | "value": "aaa" 800 | } 801 | ], 802 | "closingTag": { 803 | "kind": "ClosingTagExpr", 804 | "tagName": { 805 | "kind": "IdentityExpr", 806 | "name": "span" 807 | } 808 | } 809 | }, 810 | { 811 | "kind": "JsxExpr", 812 | "openingTag": { 813 | "kind": "OpeningTagExpr", 814 | "tagName": { 815 | "kind": "IdentityExpr", 816 | "name": "span" 817 | }, 818 | "props": [] 819 | }, 820 | "body": [ 821 | { 822 | "kind": "TextExpr", 823 | "value": "1234" 824 | } 825 | ], 826 | "closingTag": { 827 | "kind": "ClosingTagExpr", 828 | "tagName": { 829 | "kind": "IdentityExpr", 830 | "name": "span" 831 | } 832 | } 833 | }, 834 | { 835 | "kind": "JsxExpr", 836 | "openingTag": { 837 | "kind": "OpeningTagExpr", 838 | "tagName": { 839 | "kind": "IdentityExpr", 840 | "name": "span" 841 | }, 842 | "props": [] 843 | }, 844 | "body": [ 845 | { 846 | "kind": "TextExpr", 847 | "value": "1234asd" 848 | } 849 | ], 850 | "closingTag": { 851 | "kind": "ClosingTagExpr", 852 | "tagName": { 853 | "kind": "IdentityExpr", 854 | "name": "span" 855 | } 856 | } 857 | }, 858 | { 859 | "kind": "JsxExpr", 860 | "openingTag": { 861 | "kind": "OpeningTagExpr", 862 | "tagName": { 863 | "kind": "IdentityExpr", 864 | "name": "span" 865 | }, 866 | "props": [] 867 | }, 868 | "body": [ 869 | { 870 | "kind": "TextExpr", 871 | "value": "12aa" 872 | }, 873 | { 874 | "kind": "JsxExpr", 875 | "openingTag": { 876 | "kind": "OpeningTagExpr", 877 | "tagName": { 878 | "kind": "IdentityExpr", 879 | "name": "span" 880 | }, 881 | "props": [] 882 | }, 883 | "body": [ 884 | { 885 | "kind": "TextExpr", 886 | "value": "aaa" 887 | } 888 | ], 889 | "closingTag": { 890 | "kind": "ClosingTagExpr", 891 | "tagName": { 892 | "kind": "IdentityExpr", 893 | "name": "span" 894 | } 895 | } 896 | }, 897 | { 898 | "kind": "TextExpr", 899 | "value": "aa234234aaa" 900 | } 901 | ], 902 | "closingTag": { 903 | "kind": "ClosingTagExpr", 904 | "tagName": { 905 | "kind": "IdentityExpr", 906 | "name": "span" 907 | } 908 | } 909 | } 910 | ], 911 | "closingTag": { 912 | "kind": "ClosingTagExpr", 913 | "tagName": { 914 | "kind": "IdentityExpr", 915 | "name": "div" 916 | } 917 | } 918 | } 919 | ] 920 | } -------------------------------------------------------------------------------- /public/jsx-find.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "tagName": "List", 4 | "props": { 5 | "list": [ 6 | { 7 | "content": { 8 | "tagName": "View", 9 | "props": { 10 | "color": "red" 11 | }, 12 | "children": [ 13 | { 14 | "tagName": "text", 15 | "nodeValue": "233" 16 | } 17 | ] 18 | }, 19 | "logo": { 20 | "tagName": "Image", 21 | "props": { 22 | "mode": "test" 23 | }, 24 | "children": [] 25 | } 26 | }, 27 | { 28 | "content": { 29 | "tagName": "View", 30 | "props": {}, 31 | "children": [] 32 | } 33 | } 34 | ] 35 | }, 36 | "children": [] 37 | } 38 | ] -------------------------------------------------------------------------------- /public/jsx.bnf: -------------------------------------------------------------------------------- 1 | (* Jsx Grammar in BNF - By Saber2pr *) 2 | 3 | JSX ::= ("(")? JSXOPENED | JSXSELFCLOSE (")")? 4 | 5 | PARAMETER ::= "(" (IDENTITY ("," IDENTITY)* (",")?)? ")" 6 | 7 | EXPRESSION ::= JSX 8 | | STRING 9 | | NUMBER 10 | | OBJ 11 | | ARRAY 12 | | ARROWFUNCTION 13 | | CALLCHAIN 14 | | FUNCTION 15 | | BLOCK 16 | 17 | STATEMENT ::= DECLAREVARIABLE 18 | | VARIABLEASSIGN 19 | | CALLCHAIN 20 | | IFSTATEMENT 21 | 22 | NUMBER ::= digit 23 | 24 | TEXT ::= (letter | digit)* 25 | 26 | STRING ::= '"' TEXT '"' 27 | | "'" TEXT "'" 28 | 29 | KEYWORD ::= "var" 30 | | "let" 31 | | "const" 32 | 33 | IDENTITY ::= letter (NUMBER TEXT)? 34 | 35 | OBJ ::= "{" (IDENTITY ":" EXPRESSION ("," IDENTITY ":" EXPRESSION)* (",")?)? "}" 36 | 37 | ARRAY ::= "[" (EXPRESSION ("," EXPRESSION)* (",")?)? "]" 38 | 39 | PROP ::= IDENTITY 40 | | IDENTITY "=" JSXINNEREXPR 41 | | IDENTITY "=" STRING 42 | 43 | OPENTAG ::= "<" IDENTITY ((PROP)*)? ">" 44 | 45 | CLOSETAG ::= "<" "/" IDENTITY ">" 46 | 47 | JSXSELFCLOSE ::= "<" IDENTITY ((PROP)*)? "/" ">" 48 | 49 | JSXINNEREXPR ::= "{" EXPRESSION "}" 50 | 51 | JSXOPENED ::= OPENTAG (((JSX | TEXT)*)|JSXINNEREXPR) CLOSETAG 52 | 53 | ARROWFUNCTION ::= PARAMETER "=" ">" EXPRESSION 54 | 55 | FUNCTION ::= "function" (IDENTITY)? PARAMETER BLOCK 56 | 57 | CALLCHAIN ::= (STRING|OBJ|ARRAY|IDENTITY) ("." IDENTITY (PARAMETER)?)* 58 | | IDENTITY PARAMETER 59 | 60 | VARIABLEASSIGN ::= IDENTITY "=" EXPRESSION 61 | 62 | BLOCK ::= "{" STATEMENT (";" STATEMENT)* (";")? "}" 63 | 64 | DECLAREVARIABLE ::= KEYWORD IDENTITY 65 | | KEYWORD VARIABLEASSIGN 66 | 67 | IFSTATEMENT ::= "if" PARAMETER (BLOCK|STATEMENT) ("else" (BLOCK|STATEMENT))? 68 | 69 | RETURNSTATEMENT ::= "return" EXPRESSION 70 | 71 | PROGRAM ::= (STATEMENT)* -------------------------------------------------------------------------------- /public/jsx.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": [ 3 | { 4 | "tagName": "div", 5 | "props": { 6 | "width": 100, 7 | "contentEditable": { 8 | "name": "true" 9 | }, 10 | "color": "red", 11 | "arr": [ 12 | 1, 13 | 2 14 | ], 15 | "style": { 16 | "width": 100, 17 | "color": "red", 18 | "background": "blue", 19 | "test": { 20 | "color": "red" 21 | }, 22 | "child": { 23 | "tagName": "span", 24 | "props": {}, 25 | "children": [ 26 | { 27 | "tagName": "text", 28 | "nodeValue": "233" 29 | } 30 | ] 31 | } 32 | }, 33 | "id": "233ccc", 34 | "class2Name": "qwq123", 35 | "onClick": { 36 | "name": "onClick" 37 | }, 38 | "onError": { 39 | "args": [ 40 | { 41 | "name": "error" 42 | }, 43 | { 44 | "name": "test" 45 | } 46 | ], 47 | "body": { 48 | "statements": [ 49 | { 50 | "caller": "console", 51 | "chain": [ 52 | "log" 53 | ], 54 | "args": [ 55 | { 56 | "name": "error" 57 | } 58 | ] 59 | }, 60 | { 61 | "caller": "console", 62 | "chain": [ 63 | "log" 64 | ], 65 | "args": [] 66 | }, 67 | { 68 | "caller": "console", 69 | "chain": [ 70 | "log" 71 | ], 72 | "args": [ 73 | { 74 | "caller": "console", 75 | "chain": [ 76 | "log" 77 | ], 78 | "args": [ 79 | { 80 | "name": "test" 81 | } 82 | ] 83 | } 84 | ] 85 | }, 86 | { 87 | "type": "const", 88 | "assign": { 89 | "name": "a", 90 | "value": "a" 91 | } 92 | }, 93 | { 94 | "type": "let", 95 | "assign": { 96 | "name": "b", 97 | "value": "b" 98 | } 99 | }, 100 | { 101 | "type": "const", 102 | "assign": { 103 | "name": "c", 104 | "value": "c" 105 | } 106 | }, 107 | { 108 | "type": "let", 109 | "assign": { 110 | "name": "d" 111 | } 112 | }, 113 | { 114 | "name": "d", 115 | "value": "d" 116 | }, 117 | { 118 | "type": "let", 119 | "assign": { 120 | "name": "a" 121 | } 122 | }, 123 | { 124 | "name": "a", 125 | "value": "a" 126 | }, 127 | { 128 | "args": [ 129 | { 130 | "name": "a" 131 | } 132 | ], 133 | "body": { 134 | "statements": [ 135 | { 136 | "caller": "console", 137 | "chain": [ 138 | "log" 139 | ], 140 | "args": [ 141 | { 142 | "name": "a" 143 | } 144 | ] 145 | } 146 | ] 147 | } 148 | }, 149 | { 150 | "args": [ 151 | { 152 | "name": "b" 153 | } 154 | ], 155 | "body": { 156 | "statements": [ 157 | { 158 | "caller": "console", 159 | "chain": [ 160 | "log" 161 | ], 162 | "args": [ 163 | { 164 | "name": "b" 165 | } 166 | ] 167 | } 168 | ] 169 | }, 170 | "els": { 171 | "statements": [ 172 | { 173 | "caller": "console", 174 | "chain": [ 175 | "log" 176 | ], 177 | "args": [ 178 | { 179 | "name": "c" 180 | } 181 | ] 182 | } 183 | ] 184 | } 185 | }, 186 | { 187 | "args": [ 188 | { 189 | "name": "b" 190 | } 191 | ], 192 | "body": { 193 | "statements": [ 194 | { 195 | "caller": "console", 196 | "chain": [ 197 | "log" 198 | ], 199 | "args": [ 200 | { 201 | "name": "b" 202 | } 203 | ] 204 | } 205 | ] 206 | }, 207 | "els": { 208 | "args": [ 209 | { 210 | "name": "c" 211 | } 212 | ], 213 | "body": { 214 | "statements": [ 215 | { 216 | "caller": "console", 217 | "chain": [ 218 | "log" 219 | ], 220 | "args": [ 221 | { 222 | "name": "c" 223 | } 224 | ] 225 | } 226 | ] 227 | } 228 | } 229 | }, 230 | { 231 | "args": [ 232 | { 233 | "name": "b" 234 | } 235 | ], 236 | "body": { 237 | "statements": [ 238 | { 239 | "caller": "console", 240 | "chain": [ 241 | "log" 242 | ], 243 | "args": [ 244 | { 245 | "name": "b" 246 | } 247 | ] 248 | } 249 | ] 250 | }, 251 | "els": { 252 | "args": [ 253 | { 254 | "name": "c" 255 | } 256 | ], 257 | "body": { 258 | "statements": [ 259 | { 260 | "caller": "console", 261 | "chain": [ 262 | "log" 263 | ], 264 | "args": [ 265 | { 266 | "name": "c" 267 | } 268 | ] 269 | } 270 | ] 271 | }, 272 | "els": { 273 | "statements": [ 274 | { 275 | "caller": "console", 276 | "chain": [ 277 | "log" 278 | ], 279 | "args": [ 280 | { 281 | "name": "d" 282 | } 283 | ] 284 | } 285 | ] 286 | } 287 | } 288 | } 289 | ] 290 | } 291 | }, 292 | "onSubmit": { 293 | "name": "test", 294 | "args": [], 295 | "body": { 296 | "statements": [ 297 | { 298 | "caller": "console", 299 | "chain": [ 300 | "log" 301 | ], 302 | "args": [] 303 | }, 304 | { 305 | "value": 233 306 | } 307 | ] 308 | } 309 | } 310 | }, 311 | "children": [ 312 | { 313 | "tagName": "List", 314 | "props": { 315 | "list": [ 316 | { 317 | "content": { 318 | "tagName": "View", 319 | "props": { 320 | "color": "red" 321 | }, 322 | "children": [ 323 | { 324 | "tagName": "text", 325 | "nodeValue": "233" 326 | } 327 | ] 328 | }, 329 | "logo": { 330 | "tagName": "Image", 331 | "props": { 332 | "mode": "test" 333 | }, 334 | "children": [] 335 | } 336 | }, 337 | { 338 | "content": { 339 | "tagName": "View", 340 | "props": {}, 341 | "children": [] 342 | } 343 | } 344 | ] 345 | }, 346 | "children": [] 347 | }, 348 | { 349 | "tagName": "div", 350 | "props": {}, 351 | "children": [] 352 | }, 353 | { 354 | "tagName": "div", 355 | "props": { 356 | "id": "qwq" 357 | }, 358 | "children": [] 359 | }, 360 | { 361 | "tagName": "span", 362 | "props": {}, 363 | "children": [ 364 | { 365 | "tagName": "text", 366 | "nodeValue": "aaa" 367 | } 368 | ] 369 | }, 370 | { 371 | "tagName": "span", 372 | "props": {}, 373 | "children": [ 374 | { 375 | "tagName": "text", 376 | "nodeValue": "1234" 377 | } 378 | ] 379 | }, 380 | { 381 | "tagName": "span", 382 | "props": {}, 383 | "children": [ 384 | { 385 | "tagName": "text", 386 | "nodeValue": "1234asd" 387 | } 388 | ] 389 | }, 390 | { 391 | "tagName": "span", 392 | "props": {}, 393 | "children": [ 394 | { 395 | "tagName": "text", 396 | "nodeValue": "12aa" 397 | }, 398 | { 399 | "tagName": "span", 400 | "props": {}, 401 | "children": [ 402 | { 403 | "tagName": "text", 404 | "nodeValue": "aaa" 405 | } 406 | ] 407 | }, 408 | { 409 | "tagName": "text", 410 | "nodeValue": "aa234234aaa" 411 | } 412 | ] 413 | } 414 | ] 415 | } 416 | ] 417 | } -------------------------------------------------------------------------------- /public/jsx.md: -------------------------------------------------------------------------------- 1 | - [ ] parser 2 | - [ ] jsx 3 | - [x] jsx opened 4 | - [x] jsx self closing 5 | - [ ] jsx props 6 | - [x] jsx props string-value 7 | - [x] jsx props number-value, bool-value 8 | - [x] jsx props object 9 | - [x] jsx props object-array 10 | - [x] jsx props string-array, number-array 11 | - [x] jsx props arrow function 12 | - [x] jsx props arrow function scope statements 13 | - [x] jsx props function 14 | - [x] jsx props function scope statements 15 | - [ ] statement 16 | - [x] call chain 17 | - [x] arrow function 18 | - [x] comment 19 | - [ ] arithmetic 20 | - [x] function 21 | - [x] define 22 | - [x] assign 23 | - [x] if else 24 | - [ ] for 25 | - [ ] while 26 | - [ ] try catch 27 | - [x] return 28 | - [ ] deconstruct 29 | - [ ] as 30 | - [ ] transformer 31 | - [x] transform jsx 32 | - [ ] traverser 33 | - [ ] traverse ast 34 | - [x] traverse jsx 35 | - [ ] compiler 36 | - [ ] compile ast 37 | - [x] compile jsx 38 | -------------------------------------------------------------------------------- /public/jsx.todo: -------------------------------------------------------------------------------- 1 | { 2 | "todotree": { 3 | "tree": [ 4 | { 5 | "key": 1633490641546, 6 | "children": [ 7 | { 8 | "key": 1633490726905, 9 | "children": [ 10 | { 11 | "key": 1633490649004, 12 | "children": [], 13 | "todo": { 14 | "content": "jsx opened", 15 | "id": 1633490649004, 16 | "level": "default", 17 | "done": true 18 | } 19 | }, 20 | { 21 | "key": 1633490652356, 22 | "children": [], 23 | "todo": { 24 | "content": "jsx self closing", 25 | "id": 1633490652356, 26 | "level": "default", 27 | "done": true 28 | } 29 | }, 30 | { 31 | "key": 1633490768560, 32 | "children": [ 33 | { 34 | "key": 1633490659107, 35 | "children": [], 36 | "todo": { 37 | "content": "jsx props string-value", 38 | "id": 1633490659107, 39 | "level": "default", 40 | "done": true 41 | } 42 | }, 43 | { 44 | "key": 1633490668820, 45 | "children": [], 46 | "todo": { 47 | "content": "jsx props number-value, bool-value", 48 | "id": 1633490668820, 49 | "level": "default", 50 | "done": true 51 | } 52 | }, 53 | { 54 | "key": 1633490678048, 55 | "children": [], 56 | "todo": { 57 | "content": "jsx props object", 58 | "id": 1633490678048, 59 | "level": "default", 60 | "done": true 61 | } 62 | }, 63 | { 64 | "key": 1633490686089, 65 | "children": [], 66 | "todo": { 67 | "content": "jsx props object-array", 68 | "id": 1633490686089, 69 | "level": "default", 70 | "done": true 71 | } 72 | }, 73 | { 74 | "key": 1633490689317, 75 | "children": [], 76 | "todo": { 77 | "content": "jsx props string-array, number-array", 78 | "id": 1633490689317, 79 | "level": "default", 80 | "done": true 81 | } 82 | }, 83 | { 84 | "key": 1633490698464, 85 | "children": [ 86 | { 87 | "key": 1633490705748, 88 | "children": [], 89 | "todo": { 90 | "content": "jsx props arrow function scope statements", 91 | "id": 1633490705748, 92 | "level": "default", 93 | "done": true 94 | } 95 | }, 96 | { 97 | "key": 1633492689630, 98 | "children": [], 99 | "todo": { 100 | "content": "jsx props arrow function single line", 101 | "id": 1633492689630, 102 | "level": "default", 103 | "done": false 104 | } 105 | } 106 | ], 107 | "todo": { 108 | "content": "jsx props arrow function", 109 | "id": 1633490698464, 110 | "level": "default", 111 | "done": false 112 | } 113 | }, 114 | { 115 | "key": 1633490712415, 116 | "children": [ 117 | { 118 | "key": 1633490722733, 119 | "children": [], 120 | "todo": { 121 | "content": "jsx props function scope statements", 122 | "id": 1633490722733, 123 | "level": "default", 124 | "done": true 125 | } 126 | } 127 | ], 128 | "todo": { 129 | "content": "jsx props function", 130 | "id": 1633490712415, 131 | "level": "default", 132 | "done": true 133 | } 134 | } 135 | ], 136 | "todo": { 137 | "content": "jsx props", 138 | "id": 1633490768560, 139 | "level": "default", 140 | "done": false 141 | } 142 | } 143 | ], 144 | "todo": { 145 | "content": "jsx", 146 | "id": 1633490726905, 147 | "level": "default", 148 | "done": false 149 | } 150 | }, 151 | { 152 | "key": 1633490733978, 153 | "children": [ 154 | { 155 | "key": 1633490833940, 156 | "children": [], 157 | "todo": { 158 | "content": "call chain", 159 | "id": 1633490833940, 160 | "level": "default", 161 | "done": true 162 | } 163 | }, 164 | { 165 | "key": 1633490853386, 166 | "children": [], 167 | "todo": { 168 | "content": "arrow function", 169 | "id": 1633490853386, 170 | "level": "default", 171 | "done": true 172 | } 173 | }, 174 | { 175 | "key": 1633490859918, 176 | "children": [], 177 | "todo": { 178 | "content": "comment", 179 | "id": 1633490859918, 180 | "level": "default", 181 | "done": true 182 | } 183 | }, 184 | { 185 | "key": 1633490865848, 186 | "children": [], 187 | "todo": { 188 | "content": "arithmetic", 189 | "id": 1633490865848, 190 | "level": "default", 191 | "done": false 192 | } 193 | }, 194 | { 195 | "key": 1633490877454, 196 | "children": [], 197 | "todo": { 198 | "content": "function", 199 | "id": 1633490877454, 200 | "level": "default", 201 | "done": true 202 | } 203 | }, 204 | { 205 | "key": 1633490884058, 206 | "children": [], 207 | "todo": { 208 | "content": "define", 209 | "id": 1633490884058, 210 | "level": "default", 211 | "done": true 212 | } 213 | }, 214 | { 215 | "key": 1633490891900, 216 | "children": [], 217 | "todo": { 218 | "content": "assign", 219 | "id": 1633490891900, 220 | "level": "default", 221 | "done": true 222 | } 223 | }, 224 | { 225 | "key": 1633490898632, 226 | "children": [], 227 | "todo": { 228 | "content": "if else", 229 | "id": 1633490898632, 230 | "level": "default", 231 | "done": true 232 | } 233 | }, 234 | { 235 | "key": 1633490905063, 236 | "children": [], 237 | "todo": { 238 | "content": "for", 239 | "id": 1633490905063, 240 | "level": "default", 241 | "done": false 242 | } 243 | }, 244 | { 245 | "key": 1633490912031, 246 | "children": [], 247 | "todo": { 248 | "content": "while", 249 | "id": 1633490912031, 250 | "level": "default", 251 | "done": false 252 | } 253 | }, 254 | { 255 | "key": 1633490919267, 256 | "children": [], 257 | "todo": { 258 | "content": "try catch", 259 | "id": 1633490919267, 260 | "level": "default", 261 | "done": false 262 | } 263 | }, 264 | { 265 | "key": 1633490925401, 266 | "children": [], 267 | "todo": { 268 | "content": "return", 269 | "id": 1633490925401, 270 | "level": "default", 271 | "done": true 272 | } 273 | }, 274 | { 275 | "key": 1633490931740, 276 | "children": [], 277 | "todo": { 278 | "content": "deconstruct", 279 | "id": 1633490931740, 280 | "level": "default", 281 | "done": false 282 | } 283 | }, 284 | { 285 | "key": 1633490939572, 286 | "children": [], 287 | "todo": { 288 | "content": "as", 289 | "id": 1633490939572, 290 | "level": "default", 291 | "done": false 292 | } 293 | } 294 | ], 295 | "todo": { 296 | "content": "statement", 297 | "id": 1633490733978, 298 | "level": "default", 299 | "done": false 300 | } 301 | } 302 | ], 303 | "todo": { 304 | "content": "parser", 305 | "id": 1633490641546, 306 | "level": "default", 307 | "done": false 308 | } 309 | }, 310 | { 311 | "key": 1633490972932, 312 | "children": [ 313 | { 314 | "key": 1633490984367, 315 | "children": [], 316 | "todo": { 317 | "content": "transform jsx", 318 | "id": 1633490984367, 319 | "level": "default", 320 | "done": true 321 | } 322 | } 323 | ], 324 | "todo": { 325 | "content": "transformer", 326 | "id": 1633490972932, 327 | "level": "default", 328 | "done": false 329 | } 330 | }, 331 | { 332 | "key": 1633490996382, 333 | "children": [ 334 | { 335 | "key": 1633491001746, 336 | "children": [], 337 | "todo": { 338 | "content": "traverse ast", 339 | "id": 1633491001746, 340 | "level": "default", 341 | "done": false 342 | } 343 | }, 344 | { 345 | "key": 1633491010966, 346 | "children": [], 347 | "todo": { 348 | "content": "traverse jsx", 349 | "id": 1633491010966, 350 | "level": "default", 351 | "done": true 352 | } 353 | } 354 | ], 355 | "todo": { 356 | "content": "traverser", 357 | "id": 1633490996382, 358 | "level": "default", 359 | "done": false 360 | } 361 | }, 362 | { 363 | "key": 1633491015960, 364 | "children": [ 365 | { 366 | "key": 1633491018502, 367 | "children": [], 368 | "todo": { 369 | "content": "compile ast", 370 | "id": 1633491018502, 371 | "level": "default", 372 | "done": false 373 | } 374 | }, 375 | { 376 | "key": 1633491024457, 377 | "children": [], 378 | "todo": { 379 | "content": "compile jsx", 380 | "id": 1633491024457, 381 | "level": "default", 382 | "done": true 383 | } 384 | } 385 | ], 386 | "todo": { 387 | "content": "compiler", 388 | "id": 1633491015960, 389 | "level": "default", 390 | "done": false 391 | } 392 | } 393 | ], 394 | "expandKeys": [ 395 | 1633490768560, 396 | 1633490698464, 397 | 1633490712415, 398 | 1633490733978, 399 | 1633490833940, 400 | 1633490726905, 401 | 1633490972932, 402 | 1633490996382, 403 | 1633491015960, 404 | 1633490641546 405 | ], 406 | "schema": "https://github.com/Saber2pr/vsc-ext-todolist/blob/master/src/api/type.ts#L26" 407 | } 408 | } -------------------------------------------------------------------------------- /public/jsx2.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": [ 3 | { 4 | "tagName": "div", 5 | "props": { 6 | "width": 100, 7 | "contentEditable": { 8 | "name": "true" 9 | }, 10 | "color": "red", 11 | "arr": [ 12 | 1, 13 | 2 14 | ], 15 | "style": { 16 | "width": 100, 17 | "color": "red", 18 | "background": "blue", 19 | "test": { 20 | "color": "red" 21 | }, 22 | "child": { 23 | "tagName": "span", 24 | "props": {}, 25 | "children": [ 26 | { 27 | "tagName": "text", 28 | "nodeValue": "233" 29 | } 30 | ] 31 | } 32 | }, 33 | "id": "233ccc", 34 | "class2Name": "qwq123", 35 | "onClick": { 36 | "name": "onClick" 37 | }, 38 | "onError": { 39 | "args": [ 40 | { 41 | "name": "error" 42 | }, 43 | { 44 | "name": "test" 45 | } 46 | ], 47 | "body": { 48 | "statements": [ 49 | { 50 | "caller": "console", 51 | "chain": [ 52 | "log" 53 | ], 54 | "args": [ 55 | { 56 | "name": "error" 57 | } 58 | ] 59 | }, 60 | { 61 | "caller": "console", 62 | "chain": [ 63 | "log" 64 | ], 65 | "args": [] 66 | }, 67 | { 68 | "caller": "console", 69 | "chain": [ 70 | "log" 71 | ], 72 | "args": [ 73 | { 74 | "caller": "console", 75 | "chain": [ 76 | "log" 77 | ], 78 | "args": [ 79 | { 80 | "name": "test" 81 | } 82 | ] 83 | } 84 | ] 85 | }, 86 | { 87 | "type": "const", 88 | "assign": { 89 | "name": "a", 90 | "value": "a" 91 | } 92 | }, 93 | { 94 | "type": "let", 95 | "assign": { 96 | "name": "b", 97 | "value": "b" 98 | } 99 | }, 100 | { 101 | "type": "const", 102 | "assign": { 103 | "name": "c", 104 | "value": "c" 105 | } 106 | }, 107 | { 108 | "type": "let", 109 | "assign": { 110 | "name": "d" 111 | } 112 | }, 113 | { 114 | "name": "d", 115 | "value": "d" 116 | }, 117 | { 118 | "type": "let", 119 | "assign": { 120 | "name": "a" 121 | } 122 | }, 123 | { 124 | "name": "a", 125 | "value": "a" 126 | }, 127 | { 128 | "args": [ 129 | { 130 | "name": "a" 131 | } 132 | ], 133 | "body": { 134 | "statements": [ 135 | { 136 | "caller": "console", 137 | "chain": [ 138 | "log" 139 | ], 140 | "args": [ 141 | { 142 | "name": "a" 143 | } 144 | ] 145 | } 146 | ] 147 | } 148 | }, 149 | { 150 | "args": [ 151 | { 152 | "name": "b" 153 | } 154 | ], 155 | "body": { 156 | "statements": [ 157 | { 158 | "caller": "console", 159 | "chain": [ 160 | "log" 161 | ], 162 | "args": [ 163 | { 164 | "name": "b" 165 | } 166 | ] 167 | } 168 | ] 169 | }, 170 | "els": { 171 | "statements": [ 172 | { 173 | "caller": "console", 174 | "chain": [ 175 | "log" 176 | ], 177 | "args": [ 178 | { 179 | "name": "c" 180 | } 181 | ] 182 | } 183 | ] 184 | } 185 | }, 186 | { 187 | "args": [ 188 | { 189 | "name": "b" 190 | } 191 | ], 192 | "body": { 193 | "statements": [ 194 | { 195 | "caller": "console", 196 | "chain": [ 197 | "log" 198 | ], 199 | "args": [ 200 | { 201 | "name": "b" 202 | } 203 | ] 204 | } 205 | ] 206 | }, 207 | "els": { 208 | "args": [ 209 | { 210 | "name": "c" 211 | } 212 | ], 213 | "body": { 214 | "statements": [ 215 | { 216 | "caller": "console", 217 | "chain": [ 218 | "log" 219 | ], 220 | "args": [ 221 | { 222 | "name": "c" 223 | } 224 | ] 225 | } 226 | ] 227 | } 228 | } 229 | }, 230 | { 231 | "args": [ 232 | { 233 | "name": "b" 234 | } 235 | ], 236 | "body": { 237 | "statements": [ 238 | { 239 | "caller": "console", 240 | "chain": [ 241 | "log" 242 | ], 243 | "args": [ 244 | { 245 | "name": "b" 246 | } 247 | ] 248 | } 249 | ] 250 | }, 251 | "els": { 252 | "args": [ 253 | { 254 | "name": "c" 255 | } 256 | ], 257 | "body": { 258 | "statements": [ 259 | { 260 | "caller": "console", 261 | "chain": [ 262 | "log" 263 | ], 264 | "args": [ 265 | { 266 | "name": "c" 267 | } 268 | ] 269 | } 270 | ] 271 | }, 272 | "els": { 273 | "statements": [ 274 | { 275 | "caller": "console", 276 | "chain": [ 277 | "log" 278 | ], 279 | "args": [ 280 | { 281 | "name": "d" 282 | } 283 | ] 284 | } 285 | ] 286 | } 287 | } 288 | } 289 | ] 290 | } 291 | }, 292 | "onSubmit": { 293 | "name": "test", 294 | "args": [], 295 | "body": { 296 | "statements": [ 297 | { 298 | "caller": "console", 299 | "chain": [ 300 | "log" 301 | ], 302 | "args": [] 303 | }, 304 | { 305 | "value": 233 306 | } 307 | ] 308 | } 309 | } 310 | }, 311 | "children": [ 312 | { 313 | "tagName": "List", 314 | "props": { 315 | "list": [ 316 | { 317 | "content": { 318 | "tagName": "View", 319 | "props": { 320 | "color": "red" 321 | }, 322 | "children": [ 323 | { 324 | "tagName": "text", 325 | "nodeValue": "233" 326 | } 327 | ] 328 | }, 329 | "logo": { 330 | "tagName": "Image", 331 | "props": { 332 | "mode": "test" 333 | }, 334 | "children": [] 335 | } 336 | }, 337 | { 338 | "content": { 339 | "tagName": "View", 340 | "props": {}, 341 | "children": [] 342 | } 343 | } 344 | ] 345 | }, 346 | "children": [] 347 | }, 348 | { 349 | "tagName": "div", 350 | "props": {}, 351 | "children": [] 352 | }, 353 | { 354 | "tagName": "div", 355 | "props": { 356 | "id": "qwq", 357 | "meta": 233 358 | }, 359 | "children": [] 360 | }, 361 | { 362 | "tagName": "span", 363 | "props": {}, 364 | "children": [ 365 | { 366 | "tagName": "text", 367 | "nodeValue": "aaa" 368 | } 369 | ] 370 | }, 371 | { 372 | "tagName": "span", 373 | "props": {}, 374 | "children": [ 375 | { 376 | "tagName": "text", 377 | "nodeValue": "1234" 378 | } 379 | ] 380 | }, 381 | { 382 | "tagName": "span", 383 | "props": {}, 384 | "children": [ 385 | { 386 | "tagName": "text", 387 | "nodeValue": "1234asd" 388 | } 389 | ] 390 | }, 391 | { 392 | "tagName": "span", 393 | "props": {}, 394 | "children": [ 395 | { 396 | "tagName": "text", 397 | "nodeValue": "12aa" 398 | }, 399 | { 400 | "tagName": "span", 401 | "props": {}, 402 | "children": [ 403 | { 404 | "tagName": "text", 405 | "nodeValue": "aaa" 406 | } 407 | ] 408 | }, 409 | { 410 | "tagName": "text", 411 | "nodeValue": "aa234234aaa" 412 | } 413 | ] 414 | } 415 | ] 416 | } 417 | ] 418 | } -------------------------------------------------------------------------------- /public/out-find.jsx: -------------------------------------------------------------------------------- 1 | [233,logo:},{content:}]}/>] -------------------------------------------------------------------------------- /public/out.jsx: -------------------------------------------------------------------------------- 1 |
233}} id="233ccc" class2Name="qwq123" onClick={onClick} onError={(error,test)=>{console.log(error);console.log();console.log(console.log(test));const a = "a";let b = "b";const c = "c";let d;d = "d";let a;a = "a";if(a){console.log(a)};if(b){console.log(b)}else {console.log(c)};if(b){console.log(b)}else if(c){console.log(c)};if(b){console.log(b)}else if(c){console.log(c)}else {console.log(d)}}} onSubmit={function test(){console.log();return 233}}>233,logo:},{content:}]}/>
aaa12341234asd12aaaaaaa234234aaa
-------------------------------------------------------------------------------- /public/visited.json: -------------------------------------------------------------------------------- 1 | [ 2 | 100, 3 | { 4 | "name": "true" 5 | }, 6 | "red", 7 | 1, 8 | 2, 9 | 100, 10 | "red", 11 | "blue", 12 | "red", 13 | { 14 | "tagName": "text", 15 | "nodeValue": "233" 16 | }, 17 | { 18 | "tagName": "span", 19 | "props": {}, 20 | "children": [ 21 | { 22 | "tagName": "text", 23 | "nodeValue": "233" 24 | } 25 | ] 26 | }, 27 | "233ccc", 28 | "qwq123", 29 | { 30 | "name": "onClick" 31 | }, 32 | { 33 | "args": [ 34 | { 35 | "name": "error" 36 | }, 37 | { 38 | "name": "test" 39 | } 40 | ], 41 | "body": { 42 | "statements": [ 43 | { 44 | "caller": "console", 45 | "chain": [ 46 | "log" 47 | ], 48 | "args": [ 49 | { 50 | "name": "error" 51 | } 52 | ] 53 | }, 54 | { 55 | "caller": "console", 56 | "chain": [ 57 | "log" 58 | ], 59 | "args": [] 60 | }, 61 | { 62 | "caller": "console", 63 | "chain": [ 64 | "log" 65 | ], 66 | "args": [ 67 | { 68 | "caller": "console", 69 | "chain": [ 70 | "log" 71 | ], 72 | "args": [ 73 | { 74 | "name": "test" 75 | } 76 | ] 77 | } 78 | ] 79 | }, 80 | { 81 | "type": "const", 82 | "assign": { 83 | "name": "a", 84 | "value": "a" 85 | } 86 | }, 87 | { 88 | "type": "let", 89 | "assign": { 90 | "name": "b", 91 | "value": "b" 92 | } 93 | }, 94 | { 95 | "type": "const", 96 | "assign": { 97 | "name": "c", 98 | "value": "c" 99 | } 100 | }, 101 | { 102 | "type": "let", 103 | "assign": { 104 | "name": "d" 105 | } 106 | }, 107 | { 108 | "name": "d", 109 | "value": "d" 110 | }, 111 | { 112 | "type": "let", 113 | "assign": { 114 | "name": "a" 115 | } 116 | }, 117 | { 118 | "name": "a", 119 | "value": "a" 120 | }, 121 | { 122 | "args": [ 123 | { 124 | "name": "a" 125 | } 126 | ], 127 | "body": { 128 | "statements": [ 129 | { 130 | "caller": "console", 131 | "chain": [ 132 | "log" 133 | ], 134 | "args": [ 135 | { 136 | "name": "a" 137 | } 138 | ] 139 | } 140 | ] 141 | } 142 | }, 143 | { 144 | "args": [ 145 | { 146 | "name": "b" 147 | } 148 | ], 149 | "body": { 150 | "statements": [ 151 | { 152 | "caller": "console", 153 | "chain": [ 154 | "log" 155 | ], 156 | "args": [ 157 | { 158 | "name": "b" 159 | } 160 | ] 161 | } 162 | ] 163 | }, 164 | "els": { 165 | "statements": [ 166 | { 167 | "caller": "console", 168 | "chain": [ 169 | "log" 170 | ], 171 | "args": [ 172 | { 173 | "name": "c" 174 | } 175 | ] 176 | } 177 | ] 178 | } 179 | }, 180 | { 181 | "args": [ 182 | { 183 | "name": "b" 184 | } 185 | ], 186 | "body": { 187 | "statements": [ 188 | { 189 | "caller": "console", 190 | "chain": [ 191 | "log" 192 | ], 193 | "args": [ 194 | { 195 | "name": "b" 196 | } 197 | ] 198 | } 199 | ] 200 | }, 201 | "els": { 202 | "args": [ 203 | { 204 | "name": "c" 205 | } 206 | ], 207 | "body": { 208 | "statements": [ 209 | { 210 | "caller": "console", 211 | "chain": [ 212 | "log" 213 | ], 214 | "args": [ 215 | { 216 | "name": "c" 217 | } 218 | ] 219 | } 220 | ] 221 | } 222 | } 223 | }, 224 | { 225 | "args": [ 226 | { 227 | "name": "b" 228 | } 229 | ], 230 | "body": { 231 | "statements": [ 232 | { 233 | "caller": "console", 234 | "chain": [ 235 | "log" 236 | ], 237 | "args": [ 238 | { 239 | "name": "b" 240 | } 241 | ] 242 | } 243 | ] 244 | }, 245 | "els": { 246 | "args": [ 247 | { 248 | "name": "c" 249 | } 250 | ], 251 | "body": { 252 | "statements": [ 253 | { 254 | "caller": "console", 255 | "chain": [ 256 | "log" 257 | ], 258 | "args": [ 259 | { 260 | "name": "c" 261 | } 262 | ] 263 | } 264 | ] 265 | }, 266 | "els": { 267 | "statements": [ 268 | { 269 | "caller": "console", 270 | "chain": [ 271 | "log" 272 | ], 273 | "args": [ 274 | { 275 | "name": "d" 276 | } 277 | ] 278 | } 279 | ] 280 | } 281 | } 282 | } 283 | ] 284 | } 285 | }, 286 | { 287 | "name": "test", 288 | "args": [], 289 | "body": { 290 | "statements": [ 291 | { 292 | "caller": "console", 293 | "chain": [ 294 | "log" 295 | ], 296 | "args": [] 297 | }, 298 | { 299 | "value": 233 300 | } 301 | ] 302 | } 303 | }, 304 | "red", 305 | { 306 | "tagName": "text", 307 | "nodeValue": "233" 308 | }, 309 | { 310 | "tagName": "View", 311 | "props": { 312 | "color": "red" 313 | }, 314 | "children": [ 315 | { 316 | "tagName": "text", 317 | "nodeValue": "233" 318 | } 319 | ] 320 | }, 321 | "test", 322 | { 323 | "tagName": "Image", 324 | "props": { 325 | "mode": "test" 326 | }, 327 | "children": [] 328 | }, 329 | { 330 | "tagName": "View", 331 | "props": {}, 332 | "children": [] 333 | }, 334 | { 335 | "tagName": "List", 336 | "props": { 337 | "list": [ 338 | { 339 | "content": { 340 | "tagName": "View", 341 | "props": { 342 | "color": "red" 343 | }, 344 | "children": [ 345 | { 346 | "tagName": "text", 347 | "nodeValue": "233" 348 | } 349 | ] 350 | }, 351 | "logo": { 352 | "tagName": "Image", 353 | "props": { 354 | "mode": "test" 355 | }, 356 | "children": [] 357 | } 358 | }, 359 | { 360 | "content": { 361 | "tagName": "View", 362 | "props": {}, 363 | "children": [] 364 | } 365 | } 366 | ] 367 | }, 368 | "children": [] 369 | }, 370 | { 371 | "tagName": "div", 372 | "props": {}, 373 | "children": [] 374 | }, 375 | "qwq", 376 | { 377 | "tagName": "div", 378 | "props": { 379 | "id": "qwq" 380 | }, 381 | "children": [] 382 | }, 383 | { 384 | "tagName": "text", 385 | "nodeValue": "aaa" 386 | }, 387 | { 388 | "tagName": "span", 389 | "props": {}, 390 | "children": [ 391 | { 392 | "tagName": "text", 393 | "nodeValue": "aaa" 394 | } 395 | ] 396 | }, 397 | { 398 | "tagName": "text", 399 | "nodeValue": "1234" 400 | }, 401 | { 402 | "tagName": "span", 403 | "props": {}, 404 | "children": [ 405 | { 406 | "tagName": "text", 407 | "nodeValue": "1234" 408 | } 409 | ] 410 | }, 411 | { 412 | "tagName": "text", 413 | "nodeValue": "1234asd" 414 | }, 415 | { 416 | "tagName": "span", 417 | "props": {}, 418 | "children": [ 419 | { 420 | "tagName": "text", 421 | "nodeValue": "1234asd" 422 | } 423 | ] 424 | }, 425 | { 426 | "tagName": "text", 427 | "nodeValue": "12aa" 428 | }, 429 | { 430 | "tagName": "text", 431 | "nodeValue": "aaa" 432 | }, 433 | { 434 | "tagName": "span", 435 | "props": {}, 436 | "children": [ 437 | { 438 | "tagName": "text", 439 | "nodeValue": "aaa" 440 | } 441 | ] 442 | }, 443 | { 444 | "tagName": "text", 445 | "nodeValue": "aa234234aaa" 446 | }, 447 | { 448 | "tagName": "span", 449 | "props": {}, 450 | "children": [ 451 | { 452 | "tagName": "text", 453 | "nodeValue": "12aa" 454 | }, 455 | { 456 | "tagName": "span", 457 | "props": {}, 458 | "children": [ 459 | { 460 | "tagName": "text", 461 | "nodeValue": "aaa" 462 | } 463 | ] 464 | }, 465 | { 466 | "tagName": "text", 467 | "nodeValue": "aa234234aaa" 468 | } 469 | ] 470 | }, 471 | { 472 | "tagName": "div", 473 | "props": { 474 | "width": 100, 475 | "contentEditable": { 476 | "name": "true" 477 | }, 478 | "color": "red", 479 | "arr": [ 480 | 1, 481 | 2 482 | ], 483 | "style": { 484 | "width": 100, 485 | "color": "red", 486 | "background": "blue", 487 | "test": { 488 | "color": "red" 489 | }, 490 | "child": { 491 | "tagName": "span", 492 | "props": {}, 493 | "children": [ 494 | { 495 | "tagName": "text", 496 | "nodeValue": "233" 497 | } 498 | ] 499 | } 500 | }, 501 | "id": "233ccc", 502 | "class2Name": "qwq123", 503 | "onClick": { 504 | "name": "onClick" 505 | }, 506 | "onError": { 507 | "args": [ 508 | { 509 | "name": "error" 510 | }, 511 | { 512 | "name": "test" 513 | } 514 | ], 515 | "body": { 516 | "statements": [ 517 | { 518 | "caller": "console", 519 | "chain": [ 520 | "log" 521 | ], 522 | "args": [ 523 | { 524 | "name": "error" 525 | } 526 | ] 527 | }, 528 | { 529 | "caller": "console", 530 | "chain": [ 531 | "log" 532 | ], 533 | "args": [] 534 | }, 535 | { 536 | "caller": "console", 537 | "chain": [ 538 | "log" 539 | ], 540 | "args": [ 541 | { 542 | "caller": "console", 543 | "chain": [ 544 | "log" 545 | ], 546 | "args": [ 547 | { 548 | "name": "test" 549 | } 550 | ] 551 | } 552 | ] 553 | }, 554 | { 555 | "type": "const", 556 | "assign": { 557 | "name": "a", 558 | "value": "a" 559 | } 560 | }, 561 | { 562 | "type": "let", 563 | "assign": { 564 | "name": "b", 565 | "value": "b" 566 | } 567 | }, 568 | { 569 | "type": "const", 570 | "assign": { 571 | "name": "c", 572 | "value": "c" 573 | } 574 | }, 575 | { 576 | "type": "let", 577 | "assign": { 578 | "name": "d" 579 | } 580 | }, 581 | { 582 | "name": "d", 583 | "value": "d" 584 | }, 585 | { 586 | "type": "let", 587 | "assign": { 588 | "name": "a" 589 | } 590 | }, 591 | { 592 | "name": "a", 593 | "value": "a" 594 | }, 595 | { 596 | "args": [ 597 | { 598 | "name": "a" 599 | } 600 | ], 601 | "body": { 602 | "statements": [ 603 | { 604 | "caller": "console", 605 | "chain": [ 606 | "log" 607 | ], 608 | "args": [ 609 | { 610 | "name": "a" 611 | } 612 | ] 613 | } 614 | ] 615 | } 616 | }, 617 | { 618 | "args": [ 619 | { 620 | "name": "b" 621 | } 622 | ], 623 | "body": { 624 | "statements": [ 625 | { 626 | "caller": "console", 627 | "chain": [ 628 | "log" 629 | ], 630 | "args": [ 631 | { 632 | "name": "b" 633 | } 634 | ] 635 | } 636 | ] 637 | }, 638 | "els": { 639 | "statements": [ 640 | { 641 | "caller": "console", 642 | "chain": [ 643 | "log" 644 | ], 645 | "args": [ 646 | { 647 | "name": "c" 648 | } 649 | ] 650 | } 651 | ] 652 | } 653 | }, 654 | { 655 | "args": [ 656 | { 657 | "name": "b" 658 | } 659 | ], 660 | "body": { 661 | "statements": [ 662 | { 663 | "caller": "console", 664 | "chain": [ 665 | "log" 666 | ], 667 | "args": [ 668 | { 669 | "name": "b" 670 | } 671 | ] 672 | } 673 | ] 674 | }, 675 | "els": { 676 | "args": [ 677 | { 678 | "name": "c" 679 | } 680 | ], 681 | "body": { 682 | "statements": [ 683 | { 684 | "caller": "console", 685 | "chain": [ 686 | "log" 687 | ], 688 | "args": [ 689 | { 690 | "name": "c" 691 | } 692 | ] 693 | } 694 | ] 695 | } 696 | } 697 | }, 698 | { 699 | "args": [ 700 | { 701 | "name": "b" 702 | } 703 | ], 704 | "body": { 705 | "statements": [ 706 | { 707 | "caller": "console", 708 | "chain": [ 709 | "log" 710 | ], 711 | "args": [ 712 | { 713 | "name": "b" 714 | } 715 | ] 716 | } 717 | ] 718 | }, 719 | "els": { 720 | "args": [ 721 | { 722 | "name": "c" 723 | } 724 | ], 725 | "body": { 726 | "statements": [ 727 | { 728 | "caller": "console", 729 | "chain": [ 730 | "log" 731 | ], 732 | "args": [ 733 | { 734 | "name": "c" 735 | } 736 | ] 737 | } 738 | ] 739 | }, 740 | "els": { 741 | "statements": [ 742 | { 743 | "caller": "console", 744 | "chain": [ 745 | "log" 746 | ], 747 | "args": [ 748 | { 749 | "name": "d" 750 | } 751 | ] 752 | } 753 | ] 754 | } 755 | } 756 | } 757 | ] 758 | } 759 | }, 760 | "onSubmit": { 761 | "name": "test", 762 | "args": [], 763 | "body": { 764 | "statements": [ 765 | { 766 | "caller": "console", 767 | "chain": [ 768 | "log" 769 | ], 770 | "args": [] 771 | }, 772 | { 773 | "value": 233 774 | } 775 | ] 776 | } 777 | } 778 | }, 779 | "children": [ 780 | { 781 | "tagName": "List", 782 | "props": { 783 | "list": [ 784 | { 785 | "content": { 786 | "tagName": "View", 787 | "props": { 788 | "color": "red" 789 | }, 790 | "children": [ 791 | { 792 | "tagName": "text", 793 | "nodeValue": "233" 794 | } 795 | ] 796 | }, 797 | "logo": { 798 | "tagName": "Image", 799 | "props": { 800 | "mode": "test" 801 | }, 802 | "children": [] 803 | } 804 | }, 805 | { 806 | "content": { 807 | "tagName": "View", 808 | "props": {}, 809 | "children": [] 810 | } 811 | } 812 | ] 813 | }, 814 | "children": [] 815 | }, 816 | { 817 | "tagName": "div", 818 | "props": {}, 819 | "children": [] 820 | }, 821 | { 822 | "tagName": "div", 823 | "props": { 824 | "id": "qwq", 825 | "meta": 233 826 | }, 827 | "children": [] 828 | }, 829 | { 830 | "tagName": "span", 831 | "props": {}, 832 | "children": [ 833 | { 834 | "tagName": "text", 835 | "nodeValue": "aaa" 836 | } 837 | ] 838 | }, 839 | { 840 | "tagName": "span", 841 | "props": {}, 842 | "children": [ 843 | { 844 | "tagName": "text", 845 | "nodeValue": "1234" 846 | } 847 | ] 848 | }, 849 | { 850 | "tagName": "span", 851 | "props": {}, 852 | "children": [ 853 | { 854 | "tagName": "text", 855 | "nodeValue": "1234asd" 856 | } 857 | ] 858 | }, 859 | { 860 | "tagName": "span", 861 | "props": {}, 862 | "children": [ 863 | { 864 | "tagName": "text", 865 | "nodeValue": "12aa" 866 | }, 867 | { 868 | "tagName": "span", 869 | "props": {}, 870 | "children": [ 871 | { 872 | "tagName": "text", 873 | "nodeValue": "aaa" 874 | } 875 | ] 876 | }, 877 | { 878 | "tagName": "text", 879 | "nodeValue": "aa234234aaa" 880 | } 881 | ] 882 | } 883 | ] 884 | } 885 | ] -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/transformer.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Transformer Case 1 1`] = ` 4 | Object { 5 | "body": Array [ 6 | Object { 7 | "children": Array [ 8 | Object { 9 | "children": Array [], 10 | "props": Object { 11 | "list": Array [ 12 | Object { 13 | "content": Object { 14 | "children": Array [ 15 | Object { 16 | "nodeValue": "233", 17 | "tagName": "text", 18 | }, 19 | ], 20 | "props": Object { 21 | "color": "red", 22 | }, 23 | "tagName": "View", 24 | }, 25 | "logo": Object { 26 | "children": Array [], 27 | "props": Object { 28 | "mode": "test", 29 | }, 30 | "tagName": "Image", 31 | }, 32 | }, 33 | Object { 34 | "content": Object { 35 | "children": Array [], 36 | "props": Object {}, 37 | "tagName": "View", 38 | }, 39 | }, 40 | ], 41 | }, 42 | "tagName": "List", 43 | }, 44 | Object { 45 | "children": Array [], 46 | "props": Object {}, 47 | "tagName": "div", 48 | }, 49 | Object { 50 | "children": Array [], 51 | "props": Object { 52 | "id": "qwq", 53 | }, 54 | "tagName": "div", 55 | }, 56 | Object { 57 | "children": Array [ 58 | Object { 59 | "nodeValue": "aaa", 60 | "tagName": "text", 61 | }, 62 | ], 63 | "props": Object {}, 64 | "tagName": "span", 65 | }, 66 | Object { 67 | "children": Array [ 68 | Object { 69 | "nodeValue": "1234", 70 | "tagName": "text", 71 | }, 72 | ], 73 | "props": Object {}, 74 | "tagName": "span", 75 | }, 76 | Object { 77 | "children": Array [ 78 | Object { 79 | "nodeValue": "1234asd", 80 | "tagName": "text", 81 | }, 82 | ], 83 | "props": Object {}, 84 | "tagName": "span", 85 | }, 86 | Object { 87 | "children": Array [ 88 | Object { 89 | "nodeValue": "12aa", 90 | "tagName": "text", 91 | }, 92 | Object { 93 | "children": Array [ 94 | Object { 95 | "nodeValue": "aaa", 96 | "tagName": "text", 97 | }, 98 | ], 99 | "props": Object {}, 100 | "tagName": "span", 101 | }, 102 | Object { 103 | "nodeValue": "aa234234aaa", 104 | "tagName": "text", 105 | }, 106 | ], 107 | "props": Object {}, 108 | "tagName": "span", 109 | }, 110 | ], 111 | "props": Object { 112 | "arr": Array [ 113 | 1, 114 | 2, 115 | ], 116 | "class2Name": "qwq123", 117 | "color": "red", 118 | "contentEditable": Object { 119 | "name": "true", 120 | }, 121 | "id": "233ccc", 122 | "onClick": Object { 123 | "name": "onClick", 124 | }, 125 | "onError": Object { 126 | "args": Array [ 127 | Object { 128 | "name": "error", 129 | }, 130 | Object { 131 | "name": "test", 132 | }, 133 | ], 134 | "body": Object { 135 | "statements": Array [ 136 | Object { 137 | "args": Array [ 138 | Object { 139 | "name": "error", 140 | }, 141 | ], 142 | "caller": "console", 143 | "chain": Array [ 144 | "log", 145 | ], 146 | }, 147 | Object { 148 | "args": Array [], 149 | "caller": "console", 150 | "chain": Array [ 151 | "log", 152 | ], 153 | }, 154 | Object { 155 | "args": Array [ 156 | Object { 157 | "args": Array [ 158 | Object { 159 | "name": "test", 160 | }, 161 | ], 162 | "caller": "console", 163 | "chain": Array [ 164 | "log", 165 | ], 166 | }, 167 | ], 168 | "caller": "console", 169 | "chain": Array [ 170 | "log", 171 | ], 172 | }, 173 | Object { 174 | "assign": Object { 175 | "name": "a", 176 | "value": "a", 177 | }, 178 | "type": "const", 179 | }, 180 | Object { 181 | "assign": Object { 182 | "name": "b", 183 | "value": "b", 184 | }, 185 | "type": "let", 186 | }, 187 | Object { 188 | "assign": Object { 189 | "name": "c", 190 | "value": "c", 191 | }, 192 | "type": "const", 193 | }, 194 | Object { 195 | "assign": Object { 196 | "name": "d", 197 | }, 198 | "type": "let", 199 | }, 200 | Object { 201 | "name": "d", 202 | "value": "d", 203 | }, 204 | Object { 205 | "assign": Object { 206 | "name": "a", 207 | }, 208 | "type": "let", 209 | }, 210 | Object { 211 | "name": "a", 212 | "value": "a", 213 | }, 214 | Object { 215 | "args": Array [ 216 | Object { 217 | "name": "a", 218 | }, 219 | ], 220 | "body": Object { 221 | "statements": Array [ 222 | Object { 223 | "args": Array [ 224 | Object { 225 | "name": "a", 226 | }, 227 | ], 228 | "caller": "console", 229 | "chain": Array [ 230 | "log", 231 | ], 232 | }, 233 | ], 234 | }, 235 | "els": undefined, 236 | }, 237 | Object { 238 | "args": Array [ 239 | Object { 240 | "name": "b", 241 | }, 242 | ], 243 | "body": Object { 244 | "statements": Array [ 245 | Object { 246 | "args": Array [ 247 | Object { 248 | "name": "b", 249 | }, 250 | ], 251 | "caller": "console", 252 | "chain": Array [ 253 | "log", 254 | ], 255 | }, 256 | ], 257 | }, 258 | "els": Object { 259 | "statements": Array [ 260 | Object { 261 | "args": Array [ 262 | Object { 263 | "name": "c", 264 | }, 265 | ], 266 | "caller": "console", 267 | "chain": Array [ 268 | "log", 269 | ], 270 | }, 271 | ], 272 | }, 273 | }, 274 | Object { 275 | "args": Array [ 276 | Object { 277 | "name": "b", 278 | }, 279 | ], 280 | "body": Object { 281 | "statements": Array [ 282 | Object { 283 | "args": Array [ 284 | Object { 285 | "name": "b", 286 | }, 287 | ], 288 | "caller": "console", 289 | "chain": Array [ 290 | "log", 291 | ], 292 | }, 293 | ], 294 | }, 295 | "els": Object { 296 | "args": Array [ 297 | Object { 298 | "name": "c", 299 | }, 300 | ], 301 | "body": Object { 302 | "statements": Array [ 303 | Object { 304 | "args": Array [ 305 | Object { 306 | "name": "c", 307 | }, 308 | ], 309 | "caller": "console", 310 | "chain": Array [ 311 | "log", 312 | ], 313 | }, 314 | ], 315 | }, 316 | "els": undefined, 317 | }, 318 | }, 319 | Object { 320 | "args": Array [ 321 | Object { 322 | "name": "b", 323 | }, 324 | ], 325 | "body": Object { 326 | "statements": Array [ 327 | Object { 328 | "args": Array [ 329 | Object { 330 | "name": "b", 331 | }, 332 | ], 333 | "caller": "console", 334 | "chain": Array [ 335 | "log", 336 | ], 337 | }, 338 | ], 339 | }, 340 | "els": Object { 341 | "args": Array [ 342 | Object { 343 | "name": "c", 344 | }, 345 | ], 346 | "body": Object { 347 | "statements": Array [ 348 | Object { 349 | "args": Array [ 350 | Object { 351 | "name": "c", 352 | }, 353 | ], 354 | "caller": "console", 355 | "chain": Array [ 356 | "log", 357 | ], 358 | }, 359 | ], 360 | }, 361 | "els": Object { 362 | "statements": Array [ 363 | Object { 364 | "args": Array [ 365 | Object { 366 | "name": "d", 367 | }, 368 | ], 369 | "caller": "console", 370 | "chain": Array [ 371 | "log", 372 | ], 373 | }, 374 | ], 375 | }, 376 | }, 377 | }, 378 | ], 379 | }, 380 | }, 381 | "onSubmit": Object { 382 | "args": Array [], 383 | "body": Object { 384 | "statements": Array [ 385 | Object { 386 | "args": Array [], 387 | "caller": "console", 388 | "chain": Array [ 389 | "log", 390 | ], 391 | }, 392 | Object { 393 | "value": 233, 394 | }, 395 | ], 396 | }, 397 | "name": "test", 398 | }, 399 | "style": Object { 400 | "background": "blue", 401 | "child": Object { 402 | "children": Array [ 403 | Object { 404 | "nodeValue": "233", 405 | "tagName": "text", 406 | }, 407 | ], 408 | "props": Object {}, 409 | "tagName": "span", 410 | }, 411 | "color": "red", 412 | "test": Object { 413 | "color": "red", 414 | }, 415 | "width": 100, 416 | }, 417 | "width": 100, 418 | }, 419 | "tagName": "div", 420 | }, 421 | ], 422 | } 423 | `; 424 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/traverser.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Traverser findNode 1`] = ` 4 | Array [ 5 | Object { 6 | "children": Array [], 7 | "props": Object { 8 | "list": Array [ 9 | Object { 10 | "content": Object { 11 | "children": Array [ 12 | Object { 13 | "nodeValue": "233", 14 | "tagName": "text", 15 | }, 16 | ], 17 | "props": Object { 18 | "color": "red", 19 | }, 20 | "tagName": "View", 21 | }, 22 | "logo": Object { 23 | "children": Array [], 24 | "props": Object { 25 | "mode": "test", 26 | }, 27 | "tagName": "Image", 28 | }, 29 | }, 30 | Object { 31 | "content": Object { 32 | "children": Array [], 33 | "props": Object {}, 34 | "tagName": "View", 35 | }, 36 | }, 37 | ], 38 | }, 39 | "tagName": "List", 40 | }, 41 | ] 42 | `; 43 | 44 | exports[`Traverser map 1`] = ` 45 | Object { 46 | "body": Array [ 47 | Object { 48 | "children": Array [ 49 | Object { 50 | "children": Array [], 51 | "props": Object { 52 | "list": Array [ 53 | Object { 54 | "content": Object { 55 | "children": Array [ 56 | Object { 57 | "nodeValue": "233", 58 | "tagName": "text", 59 | }, 60 | ], 61 | "props": Object { 62 | "color": "red", 63 | }, 64 | "tagName": "View", 65 | }, 66 | "logo": Object { 67 | "children": Array [], 68 | "props": Object { 69 | "mode": "test", 70 | }, 71 | "tagName": "Image", 72 | }, 73 | }, 74 | Object { 75 | "content": Object { 76 | "children": Array [], 77 | "props": Object {}, 78 | "tagName": "View", 79 | }, 80 | }, 81 | ], 82 | }, 83 | "tagName": "List", 84 | }, 85 | Object { 86 | "children": Array [], 87 | "props": Object {}, 88 | "tagName": "div", 89 | }, 90 | Object { 91 | "children": Array [], 92 | "props": Object { 93 | "id": "qwq", 94 | "meta": 233, 95 | }, 96 | "tagName": "div", 97 | }, 98 | Object { 99 | "children": Array [ 100 | Object { 101 | "nodeValue": "aaa", 102 | "tagName": "text", 103 | }, 104 | ], 105 | "props": Object {}, 106 | "tagName": "span", 107 | }, 108 | Object { 109 | "children": Array [ 110 | Object { 111 | "nodeValue": "1234", 112 | "tagName": "text", 113 | }, 114 | ], 115 | "props": Object {}, 116 | "tagName": "span", 117 | }, 118 | Object { 119 | "children": Array [ 120 | Object { 121 | "nodeValue": "1234asd", 122 | "tagName": "text", 123 | }, 124 | ], 125 | "props": Object {}, 126 | "tagName": "span", 127 | }, 128 | Object { 129 | "children": Array [ 130 | Object { 131 | "nodeValue": "12aa", 132 | "tagName": "text", 133 | }, 134 | Object { 135 | "children": Array [ 136 | Object { 137 | "nodeValue": "aaa", 138 | "tagName": "text", 139 | }, 140 | ], 141 | "props": Object {}, 142 | "tagName": "span", 143 | }, 144 | Object { 145 | "nodeValue": "aa234234aaa", 146 | "tagName": "text", 147 | }, 148 | ], 149 | "props": Object {}, 150 | "tagName": "span", 151 | }, 152 | ], 153 | "props": Object { 154 | "arr": Array [ 155 | 1, 156 | 2, 157 | ], 158 | "class2Name": "qwq123", 159 | "color": "red", 160 | "contentEditable": Object { 161 | "name": "true", 162 | }, 163 | "id": "233ccc", 164 | "onClick": Object { 165 | "name": "onClick", 166 | }, 167 | "onError": Object { 168 | "args": Array [ 169 | Object { 170 | "name": "error", 171 | }, 172 | Object { 173 | "name": "test", 174 | }, 175 | ], 176 | "body": Object { 177 | "statements": Array [ 178 | Object { 179 | "args": Array [ 180 | Object { 181 | "name": "error", 182 | }, 183 | ], 184 | "caller": "console", 185 | "chain": Array [ 186 | "log", 187 | ], 188 | }, 189 | Object { 190 | "args": Array [], 191 | "caller": "console", 192 | "chain": Array [ 193 | "log", 194 | ], 195 | }, 196 | Object { 197 | "args": Array [ 198 | Object { 199 | "args": Array [ 200 | Object { 201 | "name": "test", 202 | }, 203 | ], 204 | "caller": "console", 205 | "chain": Array [ 206 | "log", 207 | ], 208 | }, 209 | ], 210 | "caller": "console", 211 | "chain": Array [ 212 | "log", 213 | ], 214 | }, 215 | Object { 216 | "assign": Object { 217 | "name": "a", 218 | "value": "a", 219 | }, 220 | "type": "const", 221 | }, 222 | Object { 223 | "assign": Object { 224 | "name": "b", 225 | "value": "b", 226 | }, 227 | "type": "let", 228 | }, 229 | Object { 230 | "assign": Object { 231 | "name": "c", 232 | "value": "c", 233 | }, 234 | "type": "const", 235 | }, 236 | Object { 237 | "assign": Object { 238 | "name": "d", 239 | }, 240 | "type": "let", 241 | }, 242 | Object { 243 | "name": "d", 244 | "value": "d", 245 | }, 246 | Object { 247 | "assign": Object { 248 | "name": "a", 249 | }, 250 | "type": "let", 251 | }, 252 | Object { 253 | "name": "a", 254 | "value": "a", 255 | }, 256 | Object { 257 | "args": Array [ 258 | Object { 259 | "name": "a", 260 | }, 261 | ], 262 | "body": Object { 263 | "statements": Array [ 264 | Object { 265 | "args": Array [ 266 | Object { 267 | "name": "a", 268 | }, 269 | ], 270 | "caller": "console", 271 | "chain": Array [ 272 | "log", 273 | ], 274 | }, 275 | ], 276 | }, 277 | "els": undefined, 278 | }, 279 | Object { 280 | "args": Array [ 281 | Object { 282 | "name": "b", 283 | }, 284 | ], 285 | "body": Object { 286 | "statements": Array [ 287 | Object { 288 | "args": Array [ 289 | Object { 290 | "name": "b", 291 | }, 292 | ], 293 | "caller": "console", 294 | "chain": Array [ 295 | "log", 296 | ], 297 | }, 298 | ], 299 | }, 300 | "els": Object { 301 | "statements": Array [ 302 | Object { 303 | "args": Array [ 304 | Object { 305 | "name": "c", 306 | }, 307 | ], 308 | "caller": "console", 309 | "chain": Array [ 310 | "log", 311 | ], 312 | }, 313 | ], 314 | }, 315 | }, 316 | Object { 317 | "args": Array [ 318 | Object { 319 | "name": "b", 320 | }, 321 | ], 322 | "body": Object { 323 | "statements": Array [ 324 | Object { 325 | "args": Array [ 326 | Object { 327 | "name": "b", 328 | }, 329 | ], 330 | "caller": "console", 331 | "chain": Array [ 332 | "log", 333 | ], 334 | }, 335 | ], 336 | }, 337 | "els": Object { 338 | "args": Array [ 339 | Object { 340 | "name": "c", 341 | }, 342 | ], 343 | "body": Object { 344 | "statements": Array [ 345 | Object { 346 | "args": Array [ 347 | Object { 348 | "name": "c", 349 | }, 350 | ], 351 | "caller": "console", 352 | "chain": Array [ 353 | "log", 354 | ], 355 | }, 356 | ], 357 | }, 358 | "els": undefined, 359 | }, 360 | }, 361 | Object { 362 | "args": Array [ 363 | Object { 364 | "name": "b", 365 | }, 366 | ], 367 | "body": Object { 368 | "statements": Array [ 369 | Object { 370 | "args": Array [ 371 | Object { 372 | "name": "b", 373 | }, 374 | ], 375 | "caller": "console", 376 | "chain": Array [ 377 | "log", 378 | ], 379 | }, 380 | ], 381 | }, 382 | "els": Object { 383 | "args": Array [ 384 | Object { 385 | "name": "c", 386 | }, 387 | ], 388 | "body": Object { 389 | "statements": Array [ 390 | Object { 391 | "args": Array [ 392 | Object { 393 | "name": "c", 394 | }, 395 | ], 396 | "caller": "console", 397 | "chain": Array [ 398 | "log", 399 | ], 400 | }, 401 | ], 402 | }, 403 | "els": Object { 404 | "statements": Array [ 405 | Object { 406 | "args": Array [ 407 | Object { 408 | "name": "d", 409 | }, 410 | ], 411 | "caller": "console", 412 | "chain": Array [ 413 | "log", 414 | ], 415 | }, 416 | ], 417 | }, 418 | }, 419 | }, 420 | ], 421 | }, 422 | }, 423 | "onSubmit": Object { 424 | "args": Array [], 425 | "body": Object { 426 | "statements": Array [ 427 | Object { 428 | "args": Array [], 429 | "caller": "console", 430 | "chain": Array [ 431 | "log", 432 | ], 433 | }, 434 | Object { 435 | "value": 233, 436 | }, 437 | ], 438 | }, 439 | "name": "test", 440 | }, 441 | "style": Object { 442 | "background": "blue", 443 | "child": Object { 444 | "children": Array [ 445 | Object { 446 | "nodeValue": "233", 447 | "tagName": "text", 448 | }, 449 | ], 450 | "props": Object {}, 451 | "tagName": "span", 452 | }, 453 | "color": "red", 454 | "test": Object { 455 | "color": "red", 456 | }, 457 | "width": 100, 458 | }, 459 | "width": 100, 460 | }, 461 | "tagName": "div", 462 | }, 463 | ], 464 | } 465 | `; 466 | -------------------------------------------------------------------------------- /src/__tests__/compiler.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Last Modified by: saber2pr 4 | * @Last Modified time: 2021-10-04 10:25:24 5 | * @Last Modified time: 2021-10-07 11:22:46 6 | */ 7 | import { compiler, parser, transformer } from '../' 8 | import { read } from './utils' 9 | 10 | describe('Compiler', () => { 11 | it('compile', async () => { 12 | const code = await read('code-1.txt') 13 | expect(compiler.compile(transformer.transform(parser.parse(code)))).toBe( 14 | code 15 | ) 16 | }) 17 | 18 | const compile = (code: string) => 19 | compiler.compile(transformer.transform(parser.parse(code))) 20 | const compileExpect = (code: string) => expect(compile(code)).toBe(code) 21 | 22 | it('CallChainExpr', () => { 23 | compileExpect('console.log(error)') 24 | }) 25 | 26 | it('DefineVariableStatement', () => { 27 | compileExpect('const a = "a"') 28 | }) 29 | 30 | it('VariableAssignExpr', () => { 31 | compileExpect('a = "a"') 32 | }) 33 | 34 | it('IfStatement', () => { 35 | compileExpect( 36 | `if(a){console.log(a)}else if(b){console.log(b)}else {console.log(c)}` 37 | ) 38 | }) 39 | 40 | it('ReturnStatement', () => { 41 | compileExpect(`return 233`) 42 | }) 43 | 44 | it('JsxExpr', () => { 45 | compileExpect('
233
') 46 | }) 47 | 48 | it('JsxSelfClosingExpr', () => { 49 | compileExpect('
') 50 | }) 51 | 52 | it('StringExpr', () => { 53 | compileExpect('"saber2pr"') 54 | }) 55 | 56 | it('NumberExpr', () => { 57 | compileExpect('233') 58 | }) 59 | 60 | it('ObjectExpr', () => { 61 | compileExpect('{a:"a"}') 62 | }) 63 | 64 | it('ArrayExpr', () => { 65 | compileExpect('["a","b"]') 66 | }) 67 | 68 | it('ArrowFunctionExpr', () => { 69 | compileExpect('(a)=>{console.log(a)}') 70 | }) 71 | 72 | it('FunctionExpr', () => { 73 | compileExpect('function test(a){console.log(a)}') 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /src/__tests__/examples/code-1.txt: -------------------------------------------------------------------------------- 1 |
233}} id="233ccc" class2Name="qwq123" onClick={onClick} onError={(error,test)=>{console.log(error);console.log();console.log(console.log(test));const a = "a";let b = "b";const c = "c";let d;d = "d";let a;a = "a";if(a){console.log(a)};if(b){console.log(b)}else {console.log(c)};if(b){console.log(b)}else if(c){console.log(c)};if(b){console.log(b)}else if(c){console.log(c)}else {console.log(d)}}} onSubmit={function test(){console.log();return 233}}>233,logo:},{content:}]}/>
aaa12341234asd12aaaaaaa234234aaa
-------------------------------------------------------------------------------- /src/__tests__/examples/code.txt: -------------------------------------------------------------------------------- 1 |
233, 18 | }} 19 | id="233ccc" 20 | class2Name="qwq123" 21 | onClick={onClick} 22 | onError={(error, test,) => { 23 | console.log(error) 24 | console.log() 25 | console.log(console.log(test)) 26 | const a = 'a' 27 | let b = 'b' 28 | const c = 'c' 29 | let d 30 | d = 'd' 31 | let a 32 | a = "a" 33 | if(a){ 34 | console.log(a) 35 | } 36 | 37 | if(b){ 38 | console.log(b) 39 | } else { 40 | console.log(c) 41 | } 42 | 43 | if(b){ 44 | console.log(b) 45 | } else if(c) { 46 | console.log(c) 47 | } 48 | 49 | if(b){ 50 | console.log(b) 51 | } else if(c) { 52 | console.log(c) 53 | } else { 54 | console.log(d) 55 | } 56 | }} 57 | onSubmit={function test() { 58 | console.log() 59 | return 233 60 | }} 61 | > 62 | {/* comment2 */} 63 | 233, 68 | logo: , 69 | }, 70 | { 71 | content: , 72 | }, 73 | ]} 74 | /> 75 |
76 |
77 | aaa 78 | 1234 79 | 1234asd 80 | 81 | 12 aa 82 | aaa 83 | aa234 234aaa 84 | 85 |
-------------------------------------------------------------------------------- /src/__tests__/parser.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:06:08 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-07 11:07:05 6 | */ 7 | import { parser } from '../' 8 | import { read } from './utils' 9 | 10 | describe('Parser', () => { 11 | it('Case 1', async () => { 12 | const code = await read('code.txt') 13 | expect(parser.parse(code)).toMatchSnapshot() 14 | }) 15 | 16 | it('CallChainExpr', () => { 17 | expect( 18 | parser.isCallChainExpr(parser.parse('console.log(error)').body[0]) 19 | ).toBe(true) 20 | }) 21 | 22 | it('DefineVariableStatement', () => { 23 | expect( 24 | parser.isDefineVariableStatement(parser.parse('const a = "a"').body[0]) 25 | ).toBe(true) 26 | }) 27 | 28 | it('VariableAssignExpr', () => { 29 | expect(parser.isVariableAssignExpr(parser.parse('a = "a"').body[0])).toBe( 30 | true 31 | ) 32 | }) 33 | 34 | it('IfStatement', () => { 35 | expect( 36 | parser.isIfStatement( 37 | parser.parse(` 38 | if(a){ 39 | console.log(a) 40 | } else if(b) { 41 | console.log(b) 42 | } else { 43 | console.log(c) 44 | } 45 | `).body[0] 46 | ) 47 | ).toBe(true) 48 | }) 49 | 50 | it('ReturnStatement', () => { 51 | expect(parser.isReturnStatement(parser.parse('return 233').body[0])).toBe( 52 | true 53 | ) 54 | }) 55 | 56 | it('JsxExpr', () => { 57 | expect(parser.isJsxExpr(parser.parse('
233
').body[0])).toBe(true) 58 | }) 59 | 60 | it('JsxSelfClosingExpr', () => { 61 | expect( 62 | parser.isJsxSelfClosingExpr( 63 | parser.parse('
').body[0] 64 | ) 65 | ).toBe(true) 66 | }) 67 | 68 | it('StringExpr', () => { 69 | expect(parser.isStringExpr(parser.parse('"saber2pr"').body[0])).toBe(true) 70 | }) 71 | 72 | it('NumberExpr', () => { 73 | expect(parser.isNumberExpr(parser.parse('233').body[0])).toBe(true) 74 | }) 75 | 76 | it('ObjectExpr', () => { 77 | expect(parser.isObjectExpr(parser.parse('{a:"a"}').body[0])).toBe(true) 78 | }) 79 | 80 | it('ArrayExpr', () => { 81 | expect(parser.isArrayExpr(parser.parse('["a", "b"]').body[0])).toBe(true) 82 | }) 83 | 84 | it('ArrowFunctionExpr', () => { 85 | expect( 86 | parser.isArrowFunctionExpr( 87 | parser.parse('(a) => { console.log(a) }').body[0] 88 | ) 89 | ).toBe(true) 90 | }) 91 | 92 | it('FunctionExpr', () => { 93 | expect( 94 | parser.isFunctionExpr( 95 | parser.parse('function test(a){ console.log(a) }').body[0] 96 | ) 97 | ).toBe(true) 98 | }) 99 | }) 100 | -------------------------------------------------------------------------------- /src/__tests__/tokenizer.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:06:11 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-02 20:14:13 6 | */ 7 | import { parser } from '../' 8 | 9 | const verifyLL1 = ( 10 | words: string[], 11 | kind: any, 12 | rules: [boolean, RegExp, any][] 13 | ) => { 14 | const conflicts: any[][] = [] 15 | for (const word of words) { 16 | const matched = [] 17 | for (const rule of rules) { 18 | if (word.match(rule[1])) { 19 | matched.push(kind[rule[2]]) 20 | } 21 | } 22 | if (matched.length > 1) { 23 | conflicts.push([word, ...matched]) 24 | } 25 | } 26 | if (conflicts.length) { 27 | throw new TypeError( 28 | `[LL1 Conflicts] \n${conflicts 29 | .map(row => `${row[0]}: ${row.slice(1).join(',')}`) 30 | .join('\n')}` 31 | ) 32 | } 33 | } 34 | 35 | describe('Tokenizer', () => { 36 | it('Check LL1', () => { 37 | const words = [ 38 | 'abc', 39 | '1234', 40 | 'ab12', 41 | '12ab', 42 | ' ', 43 | '<', 44 | '=', 45 | '"', 46 | '>', 47 | '/', 48 | '{', 49 | '}', 50 | '[', 51 | ']', 52 | ':', 53 | ',', 54 | "'", 55 | '.', 56 | '(', 57 | ')', 58 | ';', 59 | ] 60 | expect(() => 61 | verifyLL1(words, parser.TokenKind, parser.TokenRules) 62 | ).not.toThrow() 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /src/__tests__/transformer.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:06:14 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-07 11:00:34 6 | */ 7 | import { parser, transformer } from '../' 8 | import { read } from './utils' 9 | 10 | describe('Transformer', () => { 11 | it('Case 1', async () => { 12 | const code = await read('code.txt') 13 | expect(transformer.transform(parser.parse(code))).toMatchSnapshot() 14 | }) 15 | 16 | const transform = (code: string) => 17 | transformer.transform(parser.parse(code).body[0]) 18 | 19 | it('CallChainExpr', () => { 20 | expect(transformer.isCallChain(transform('console.log(error)'))).toBe(true) 21 | }) 22 | 23 | it('DefineVariableStatement', () => { 24 | expect(transformer.isDefineVariable(transform('const a = "a"'))).toBe(true) 25 | }) 26 | 27 | it('VariableAssignExpr', () => { 28 | expect(transformer.isVariableAssign(transform('a = "a"'))).toBe(true) 29 | }) 30 | 31 | it('IfStatement', () => { 32 | expect( 33 | transformer.isIf( 34 | transform(` 35 | if(a){ 36 | console.log(a) 37 | } else if(b) { 38 | console.log(b) 39 | } else { 40 | console.log(c) 41 | } 42 | `) 43 | ) 44 | ).toBe(true) 45 | }) 46 | 47 | it('ReturnStatement', () => { 48 | expect( 49 | transformer.isReturn( 50 | transform(` 51 | return 233 52 | `) 53 | ) 54 | ).toBe(true) 55 | }) 56 | 57 | it('JsxExpr', () => { 58 | expect(transformer.isJsxElement(transform('
233
'))).toBe(true) 59 | }) 60 | 61 | it('JsxSelfClosingExpr', () => { 62 | expect(transformer.isJsxElement(transform('
'))).toBe( 63 | true 64 | ) 65 | }) 66 | 67 | it('StringExpr', () => { 68 | expect(transform('"saber2pr"')).toBe('saber2pr') 69 | }) 70 | 71 | it('NumberExpr', () => { 72 | expect(transform('233')).toBe(233) 73 | }) 74 | 75 | it('ObjectExpr', () => { 76 | expect(transformer.isJsxObject(transform('{a:"a"}'))).toBe(true) 77 | }) 78 | 79 | it('ArrayExpr', () => { 80 | expect(transform('["a", "b"]')).toEqual(['a', 'b']) 81 | }) 82 | 83 | it('ArrowFunctionExpr', () => { 84 | expect( 85 | transformer.isArrowFunction(transform('(a) => { console.log(a) }')) 86 | ).toBe(true) 87 | }) 88 | 89 | it('FunctionExpr', () => { 90 | expect( 91 | transformer.isFunction(transform('function test(a){ console.log(a) }')) 92 | ).toBe(true) 93 | }) 94 | }) 95 | -------------------------------------------------------------------------------- /src/__tests__/traverser.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-10-02 15:31:36 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-04 10:27:50 6 | */ 7 | import { compiler, parser, transformer, traverser } from '../' 8 | import { read } from './utils' 9 | 10 | describe('Traverser', () => { 11 | it('map', async () => { 12 | const code = await read('code.txt') 13 | const ast = parser.parse(code) 14 | const jsx = transformer.transform(ast) 15 | 16 | const jsx2 = traverser.traverse(jsx, node => { 17 | if (transformer.isJsxElement(node)) { 18 | if (node.props && node.props.id === 'qwq') { 19 | return transformer.createJsxElement(node.tagName, { 20 | ...node.props, 21 | meta: 233, 22 | }) 23 | } 24 | } 25 | }) 26 | 27 | expect(jsx2).toMatchSnapshot() 28 | }) 29 | 30 | it('findNode', async () => { 31 | const code = await read('code.txt') 32 | const ast = parser.parse(code) 33 | const jsx = transformer.transform(ast) 34 | 35 | const node = traverser.findNode(jsx, node => { 36 | return transformer.isJsxElement(node) && node.tagName === 'List' 37 | }) 38 | expect(node).toMatchSnapshot() 39 | expect(compiler.compile(node)).toEqual( 40 | `[233,logo:},{content:}]}/>]` 41 | ) 42 | 43 | // get props list source code 44 | const result = node[0] 45 | const contents: string[] = [] 46 | if (transformer.isJsxElement(result)) { 47 | const list = result.props.list 48 | if (Array.isArray(list)) { 49 | list.forEach((item: any) => 50 | contents.push(compiler.compile(item.content)) 51 | ) 52 | } 53 | } 54 | expect(contents).toEqual(['233', '']) 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /src/__tests__/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './read' 2 | -------------------------------------------------------------------------------- /src/__tests__/utils/read.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs/promises' 2 | import path from 'path' 3 | 4 | export const read = async (example: 'code-1.txt' | 'code.txt') => { 5 | const buf = await fs.readFile(path.join(__dirname, `../examples/${example}`)) 6 | return buf.toString() 7 | } 8 | -------------------------------------------------------------------------------- /src/compiler/Compiler.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:05:43 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-08 19:44:02 6 | */ 7 | import * as Jsx from '../transformer/Jsx' 8 | import * as Factory from '../transformer/Factory' 9 | 10 | // basic 11 | export function compileString(element: string) { 12 | return `"${element}"` 13 | } 14 | 15 | export function compileNumber(element: number) { 16 | return `${+element}` 17 | } 18 | 19 | export function compileBoolean(element: boolean) { 20 | return `${element ? 'true' : 'false'}` 21 | } 22 | 23 | export function compileArray(element: Jsx.Type[]) { 24 | return `[${element.map(value => compile(value)).join(',')}]` 25 | } 26 | 27 | export function compileIdentity(element: Jsx.Identity) { 28 | const name = element.name 29 | return name 30 | } 31 | 32 | export function compileJsxObject(element: Jsx.JsxObject | null) { 33 | if (element === null) return 'null' 34 | const entries = Factory.getElementEntries(element) 35 | return `{${entries 36 | .map(([key, value]) => `${key}:${compile(value)}`) 37 | .join(',')}}` 38 | } 39 | 40 | // jsx 41 | export function compileTextElement(element: Jsx.TextElement) { 42 | return `${element.nodeValue}` 43 | } 44 | 45 | export function compileJsxAttributes(element: Jsx.JsxAttributes): string { 46 | const entries = Factory.getElementEntries(element) 47 | if (entries.length === 0) return '' 48 | return ` ${entries 49 | .map(([key, value]) => { 50 | // basic 51 | if (typeof value === 'string') { 52 | return `${key}="${value}"` 53 | } 54 | if (typeof value === 'number') { 55 | return `${key}={${value}}` 56 | } 57 | if (typeof value === 'boolean') { 58 | if (value) { 59 | return `${key}` 60 | } else { 61 | return `${key}={false}` 62 | } 63 | } 64 | // jsx 65 | if (Factory.isTextElement(value)) { 66 | return `${key}="${value.nodeValue}"` 67 | } 68 | // statement 69 | return `${key}={${compile(value)}}` 70 | }) 71 | .join(' ')}` 72 | } 73 | 74 | export function compileJsxElement(element: Jsx.JsxElement) { 75 | const tagName = element.tagName 76 | const props = element.props 77 | const elements = element.children 78 | 79 | // compile jsx element 80 | const attributes = compileJsxAttributes(props) 81 | if (Array.isArray(elements) && elements.length > 0) { 82 | const children = Array.isArray(elements) 83 | ? elements.map(element => compile(element)).join('') 84 | : `{${compile(elements)}}` 85 | return `<${tagName}${attributes}>${children}` 86 | } 87 | 88 | // self closing 89 | return `<${tagName}${attributes}/>` 90 | } 91 | 92 | // expression 93 | export function compileBlock(element: Jsx.Block) { 94 | const body = element.statements ?? [] 95 | return `{${body.map(statement => compile(statement)).join(';')}}` 96 | } 97 | 98 | export function compileParameter(args: Jsx.Parameter = []): string { 99 | return args.map(arg => compile(arg)).join(',') 100 | } 101 | 102 | export function compileArrowFunction(element: Jsx.ArrowFunction) { 103 | const args = element.args ?? [] 104 | const body = element.body ?? [] 105 | return `${ 106 | Factory.isIdentity(args) 107 | ? compileIdentity(args) 108 | : `(${compileParameter(args)})` 109 | }=>${compile(body)}` 110 | } 111 | 112 | export function compileFunction(element: Jsx.Function) { 113 | const name = element.name ?? '' 114 | const args = element.args ?? [] 115 | const body = element.body ?? [] 116 | return `function ${name}(${compileParameter(args)})${compileBlock(body)}` 117 | } 118 | 119 | export function compileCallChain(element: Jsx.CallChain): string { 120 | const caller = element.caller 121 | const chain = element.chain ?? [] 122 | const args = element.args ?? [] 123 | return `${caller}.${chain.join('.')}(${compileParameter(args)})` 124 | } 125 | 126 | export function compileVariableAssign(assign: Jsx.VariableAssign): string { 127 | const { name, value } = assign 128 | return `${name}${value ? ` = ${compile(value)}` : ''}` 129 | } 130 | 131 | // statement 132 | 133 | export function compileDefineVariable(def: Jsx.DefineVariable): string { 134 | const { type, assign } = def 135 | return `${type ? `${type} ` : ''}${ 136 | Factory.isIdentity(assign) 137 | ? compileIdentity(assign) 138 | : compileVariableAssign(assign) 139 | }` 140 | } 141 | 142 | export function compileIf(ifElse: Jsx.If): string { 143 | const { args, body, els } = ifElse 144 | return `if(${compileParameter(args)})${compile(body)}${ 145 | els ? `else ${compile(els)}` : '' 146 | }` 147 | } 148 | 149 | export function compileReturn(ret: Jsx.Return): string { 150 | const { value } = ret 151 | return `return ${compile(value)}` 152 | } 153 | 154 | export function compileProgram(program: Jsx.Program): string { 155 | const { body = [] } = program 156 | return body.map(statement => compile(statement)).join(';') 157 | } 158 | 159 | // compile code 160 | export function compile(element: Jsx.Type): string { 161 | // program 162 | if (Factory.isProgram(element)) { 163 | return compileProgram(element) 164 | } 165 | // text element 166 | if (Factory.isTextElement(element)) { 167 | return compileTextElement(element) 168 | } 169 | // jsx element 170 | if (Factory.isJsxElement(element)) { 171 | return compileJsxElement(element) 172 | } 173 | if (Array.isArray(element)) { 174 | return compileArray(element) 175 | } 176 | if (element === null || Factory.isJsxObject(element)) { 177 | return compileJsxObject(element) 178 | } 179 | // expression 180 | if (Factory.isArrowFunction(element)) { 181 | return compileArrowFunction(element) 182 | } 183 | if (Factory.isFunction(element)) { 184 | return compileFunction(element) 185 | } 186 | if (Factory.isCallChain(element)) { 187 | return compileCallChain(element) 188 | } 189 | if (Factory.isVariableAssign(element)) { 190 | return compileVariableAssign(element) 191 | } 192 | // statement 193 | if (Factory.isDefineVariable(element)) { 194 | return compileDefineVariable(element) 195 | } 196 | if (Factory.isIf(element)) { 197 | return compileIf(element) 198 | } 199 | if (Factory.isReturn(element)) { 200 | return compileReturn(element) 201 | } 202 | if (Factory.isBlock(element)) { 203 | return compileBlock(element) 204 | } 205 | if (Factory.isIdentity(element)) { 206 | return compileIdentity(element) 207 | } 208 | // basic 209 | if (typeof element === 'string') { 210 | return compileString(element) 211 | } 212 | if (typeof element === 'number') { 213 | return compileNumber(element) 214 | } 215 | if (typeof element === 'boolean') { 216 | return compileBoolean(element) 217 | } 218 | return '' 219 | } 220 | -------------------------------------------------------------------------------- /src/compiler/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Compiler' 2 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:07:56 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-02 20:00:40 6 | */ 7 | export * as compiler from './compiler' 8 | export * as parser from './parser' 9 | export * as transformer from './transformer' 10 | export * as traverser from './traverser' 11 | -------------------------------------------------------------------------------- /src/parser/Ast.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:06:21 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-08 19:39:03 6 | */ 7 | export interface Node { 8 | kind: any 9 | [k: string]: any 10 | } 11 | 12 | // Primary 13 | 14 | export interface KeywordExpr extends Node { 15 | kind: 'KeywordExpr' 16 | name: string 17 | } 18 | 19 | export interface IdentityExpr extends Node { 20 | kind: 'IdentityExpr' 21 | name: string 22 | } 23 | 24 | export interface NumberExpr extends Node { 25 | kind: 'NumberExpr' 26 | value: number 27 | } 28 | 29 | export interface StringExpr extends Node { 30 | kind: 'StringExpr' 31 | value: string 32 | } 33 | 34 | export interface ObjectExpr extends Node { 35 | kind: 'ObjectExpr' 36 | props: { 37 | [k: string]: Expression | IdentityExpr 38 | } 39 | } 40 | 41 | export interface ArrayExpr extends Node { 42 | kind: 'ArrayExpr' 43 | items: (Expression | IdentityExpr)[] 44 | } 45 | 46 | export type Expression = 47 | | Jsx 48 | | StringExpr 49 | | NumberExpr 50 | | ObjectExpr 51 | | ArrayExpr 52 | | ArrowFunctionExpr 53 | | CallChainExpr 54 | | FunctionExpr 55 | | VariableAssignExpr 56 | | BlockExpr 57 | 58 | export type Statement = 59 | | CallChainExpr 60 | | DefineVariableStatement 61 | | VariableAssignExpr 62 | | IfStatement 63 | | ReturnStatement 64 | | Jsx 65 | | StringExpr 66 | | NumberExpr 67 | | ObjectExpr 68 | | ArrayExpr 69 | | ArrowFunctionExpr 70 | | FunctionExpr 71 | 72 | export type Parameter = (IdentityExpr | Expression)[] | undefined 73 | 74 | // JSX 75 | 76 | export interface OpeningTagExpr extends Node { 77 | kind: 'OpeningTagExpr' 78 | tagName: IdentityExpr 79 | props: PropExpr[] 80 | } 81 | 82 | export interface ClosingTagExpr extends Node { 83 | kind: 'ClosingTagExpr' 84 | tagName: IdentityExpr 85 | } 86 | 87 | export interface PropExpr extends Node { 88 | kind: 'PropExpr' 89 | key: IdentityExpr 90 | value: JsxInnerExpr | StringExpr 91 | } 92 | 93 | export interface JsxExpr extends Node { 94 | kind: 'JsxExpr' 95 | openingTag: OpeningTagExpr 96 | body: (Jsx | TextExpr)[] | JsxInnerExpr 97 | closingTag: ClosingTagExpr 98 | } 99 | 100 | export interface JsxInnerExpr extends Node { 101 | kind: 'JsxInnerExpr' 102 | body: Expression | IdentityExpr 103 | } 104 | 105 | export interface TextExpr extends Node { 106 | kind: 'TextExpr' 107 | value: string 108 | } 109 | 110 | export interface JsxSelfClosingExpr extends Node { 111 | kind: 'JsxSelfClosingExpr' 112 | tagName: IdentityExpr 113 | props: PropExpr[] 114 | } 115 | 116 | export type Jsx = JsxExpr | JsxSelfClosingExpr 117 | 118 | // Expr 119 | export interface BlockExpr extends Node { 120 | kind: 'BlockExpr' 121 | body: Statement[] 122 | } 123 | 124 | export interface ArrowFunctionExpr extends Node { 125 | kind: 'ArrowFunctionExpr' 126 | args: Parameter | IdentityExpr 127 | body: Expression 128 | } 129 | 130 | export interface FunctionExpr extends Node { 131 | kind: 'FunctionExpr' 132 | name: IdentityExpr | undefined 133 | args: Parameter 134 | body: BlockExpr 135 | } 136 | 137 | export interface CallChainExpr extends Node { 138 | kind: 'CallChainExpr' 139 | caller: IdentityExpr 140 | chain: IdentityExpr[] 141 | args: Parameter 142 | } 143 | 144 | export interface VariableAssignExpr extends Node { 145 | kind: 'VariableAssignExpr' 146 | name: IdentityExpr 147 | value: Expression | undefined 148 | } 149 | 150 | // Statement 151 | 152 | export interface DefineVariableStatement extends Node { 153 | kind: 'DefineVariableStatement' 154 | type: KeywordExpr 155 | assign: VariableAssignExpr | IdentityExpr 156 | } 157 | 158 | export interface IfStatement extends Node { 159 | kind: 'IfStatement' 160 | args: Parameter 161 | body: Statement | BlockExpr 162 | els: Statement | BlockExpr | undefined 163 | } 164 | 165 | export interface ReturnStatement extends Node { 166 | kind: 'ReturnStatement' 167 | value: Expression 168 | } 169 | 170 | // Program 171 | 172 | export interface Program extends Node { 173 | kind: 'Program' 174 | body: Statement[] 175 | } 176 | -------------------------------------------------------------------------------- /src/parser/Consumer.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:06:27 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-08 19:38:57 6 | */ 7 | import * as parsec from 'typescript-parsec' 8 | 9 | import * as Ast from './Ast' 10 | import { TokenKind } from './Tokenizer' 11 | 12 | export type Token = parsec.Token 13 | 14 | // basic 15 | 16 | export function applyNumber(token: Token): Ast.NumberExpr { 17 | return { 18 | kind: 'NumberExpr', 19 | value: +token.text, 20 | } 21 | } 22 | 23 | export function applyString(text: Ast.TextExpr): Ast.StringExpr { 24 | return { 25 | kind: 'StringExpr', 26 | value: text.value, 27 | } 28 | } 29 | 30 | export function applyKeyword(token: Token): Ast.KeywordExpr { 31 | return { 32 | kind: 'KeywordExpr', 33 | name: token.text, 34 | } 35 | } 36 | 37 | export function applyIdentity( 38 | source: [Token, [Ast.NumberExpr, Ast.TextExpr] | undefined] 39 | ): Ast.IdentityExpr { 40 | const [letter, tail] = source 41 | let name = letter.text 42 | if (tail) { 43 | const [digit, text] = tail 44 | if (digit) { 45 | name += digit.value 46 | } 47 | if (text) { 48 | name += text.value 49 | } 50 | } 51 | return { 52 | kind: 'IdentityExpr', 53 | name, 54 | } 55 | } 56 | 57 | // Jsx 58 | 59 | export function applyProp( 60 | source: 61 | | [Ast.IdentityExpr, Ast.PropExpr['value']] 62 | | [Ast.IdentityExpr, undefined] 63 | ): Ast.PropExpr { 64 | const [name, token] = source 65 | let value: Ast.PropExpr['value'] 66 | if (token) { 67 | value = token 68 | } else { 69 | value = { 70 | kind: 'JsxInnerExpr', 71 | body: { 72 | kind: 'IdentityExpr', 73 | name: 'true', 74 | } as Ast.IdentityExpr, 75 | } 76 | } 77 | return { 78 | kind: 'PropExpr', 79 | key: name, 80 | value, 81 | } 82 | } 83 | 84 | export function applyObject( 85 | source: 86 | | [Ast.IdentityExpr, Token, Ast.Expression | Ast.IdentityExpr][] 87 | | undefined = [] 88 | ): Ast.ObjectExpr { 89 | return { 90 | kind: 'ObjectExpr', 91 | props: source.reduce((acc, cur) => ({ ...acc, [cur[0].name]: cur[2] }), {}), 92 | } 93 | } 94 | 95 | export function applyArray( 96 | items: (Ast.Expression | Ast.IdentityExpr)[] | undefined = [] 97 | ): Ast.ArrayExpr { 98 | return { 99 | kind: 'ArrayExpr', 100 | items: items ?? [], 101 | } 102 | } 103 | 104 | export function applyOpeningTag( 105 | source: [Ast.IdentityExpr, Ast.PropExpr[]] 106 | ): Ast.OpeningTagExpr { 107 | const [name, value] = source 108 | return { 109 | kind: 'OpeningTagExpr', 110 | tagName: name, 111 | props: value, 112 | } 113 | } 114 | 115 | export function applyClosingTag(source: Ast.IdentityExpr): Ast.ClosingTagExpr { 116 | return { 117 | kind: 'ClosingTagExpr', 118 | tagName: source, 119 | } 120 | } 121 | 122 | export function applyJsxSelfClosing( 123 | source: [Ast.IdentityExpr, Ast.PropExpr[]] 124 | ): Ast.JsxSelfClosingExpr { 125 | const [name, value] = source 126 | return { 127 | kind: 'JsxSelfClosingExpr', 128 | tagName: name, 129 | props: value, 130 | } 131 | } 132 | 133 | export function applyText(source: Token[]): Ast.TextExpr { 134 | return { 135 | kind: 'TextExpr', 136 | value: source.map(token => token.text).join(''), 137 | } 138 | } 139 | 140 | export function applyJsx( 141 | source: [Ast.OpeningTagExpr, Ast.JsxExpr['body'], Ast.ClosingTagExpr] 142 | ): Ast.JsxExpr { 143 | return { 144 | kind: 'JsxExpr', 145 | openingTag: source[0], 146 | body: source[1], 147 | closingTag: source[2], 148 | } 149 | } 150 | 151 | export function applyJsxInner( 152 | source: Ast.Expression | Ast.IdentityExpr 153 | ): Ast.JsxInnerExpr { 154 | return { 155 | kind: 'JsxInnerExpr', 156 | body: source, 157 | } 158 | } 159 | 160 | // Statement 161 | 162 | export function applyArrowFunction( 163 | source: [Ast.Parameter | Ast.IdentityExpr, Ast.Expression] 164 | ): Ast.ArrowFunctionExpr { 165 | const [args = [], body] = source 166 | return { 167 | kind: 'ArrowFunctionExpr', 168 | args, 169 | body, 170 | } 171 | } 172 | 173 | export function applyFunction( 174 | source: [Ast.IdentityExpr | undefined, Ast.Parameter, Ast.BlockExpr] 175 | ): Ast.FunctionExpr { 176 | const [name, args = [], body] = source 177 | return { 178 | kind: 'FunctionExpr', 179 | name, 180 | args, 181 | body, 182 | } 183 | } 184 | 185 | export function applyCallChain( 186 | source: [Ast.IdentityExpr[], Ast.Parameter] 187 | ): Ast.CallChainExpr { 188 | const [chain, args = []] = source 189 | return { 190 | kind: 'CallChainExpr', 191 | caller: chain[0], 192 | chain: chain.slice(1), 193 | args: args, 194 | } 195 | } 196 | 197 | export function applyVariableAssign( 198 | source: [Ast.IdentityExpr, Ast.Expression | undefined] 199 | ): Ast.VariableAssignExpr { 200 | const [name, value] = source 201 | return { 202 | kind: 'VariableAssignExpr', 203 | name, 204 | value, 205 | } 206 | } 207 | 208 | export function applyBlock(body: Ast.Statement[] = []): Ast.BlockExpr { 209 | return { 210 | kind: 'BlockExpr', 211 | body, 212 | } 213 | } 214 | 215 | // statement 216 | 217 | export function applyDefineVariable( 218 | source: [Ast.KeywordExpr, Ast.VariableAssignExpr | Ast.IdentityExpr] 219 | ): Ast.DefineVariableStatement { 220 | const [type, assign] = source 221 | return { 222 | kind: 'DefineVariableStatement', 223 | type, 224 | assign, 225 | } 226 | } 227 | 228 | export function applyIf( 229 | source: [ 230 | Ast.IfStatement['args'], 231 | Ast.IfStatement['body'], 232 | Ast.IfStatement['els'] 233 | ] 234 | ): Ast.IfStatement { 235 | const [args = [], body, els] = source 236 | return { 237 | kind: 'IfStatement', 238 | args, 239 | body, 240 | els, 241 | } 242 | } 243 | 244 | export function applyReturn(source: Ast.Expression): Ast.ReturnStatement { 245 | return { 246 | kind: 'ReturnStatement', 247 | value: source, 248 | } 249 | } 250 | 251 | export function applyProgram(value: Ast.Program['body']): Ast.Program { 252 | return { 253 | kind: 'Program', 254 | body: value, 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/parser/Factory.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:07:15 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-08 17:10:18 6 | */ 7 | import * as Ast from './Ast' 8 | 9 | export function isIdentityExpr(token: Ast.Node): token is Ast.IdentityExpr { 10 | return (token).kind === 'IdentityExpr' 11 | } 12 | 13 | export function isNumberExpr(token: Ast.Node): token is Ast.NumberExpr { 14 | return (token).kind === 'NumberExpr' 15 | } 16 | 17 | export function isStringExpr(token: Ast.Node): token is Ast.StringExpr { 18 | return (token).kind === 'StringExpr' 19 | } 20 | 21 | export function isObjectExpr(token: Ast.Node): token is Ast.ObjectExpr { 22 | return (token).kind === 'ObjectExpr' 23 | } 24 | 25 | export function isArrayExpr(token: Ast.Node): token is Ast.ArrayExpr { 26 | return (token).kind === 'ArrayExpr' 27 | } 28 | 29 | export function isJsxExpr(token: Ast.Node): token is Ast.JsxExpr { 30 | return (token).kind === 'JsxExpr' 31 | } 32 | 33 | export function isTextExpr(token: Ast.Node): token is Ast.TextExpr { 34 | return (token).kind === 'TextExpr' 35 | } 36 | 37 | export function isJsxSelfClosingExpr( 38 | token: Ast.Node 39 | ): token is Ast.JsxSelfClosingExpr { 40 | return (token).kind === 'JsxSelfClosingExpr' 41 | } 42 | 43 | export function isJsxInnerExpr(token: Ast.Node): token is Ast.JsxInnerExpr { 44 | return (token).kind === 'JsxInnerExpr' 45 | } 46 | 47 | export function isBlockExpr(token: Ast.Node): token is Ast.BlockExpr { 48 | return (token).kind === 'BlockExpr' 49 | } 50 | 51 | export function isCallChainExpr(token: Ast.Node): token is Ast.CallChainExpr { 52 | return (token).kind === 'CallChainExpr' 53 | } 54 | 55 | export function isArrowFunctionExpr( 56 | token: Ast.Node 57 | ): token is Ast.ArrowFunctionExpr { 58 | return (token).kind === 'ArrowFunctionExpr' 59 | } 60 | 61 | export function isFunctionExpr(token: Ast.Node): token is Ast.FunctionExpr { 62 | return (token).kind === 'FunctionExpr' 63 | } 64 | 65 | export function isDefineVariableStatement( 66 | token: Ast.Node 67 | ): token is Ast.DefineVariableStatement { 68 | return (token).kind === 'DefineVariableStatement' 69 | } 70 | 71 | export function isVariableAssignExpr( 72 | token: Ast.Node 73 | ): token is Ast.VariableAssignExpr { 74 | return (token).kind === 'VariableAssignExpr' 75 | } 76 | 77 | export function isIfStatement(token: Ast.Node): token is Ast.IfStatement { 78 | return (token).kind === 'IfStatement' 79 | } 80 | 81 | export function isReturnStatement( 82 | token: Ast.Node 83 | ): token is Ast.ReturnStatement { 84 | return (token).kind === 'ReturnStatement' 85 | } 86 | 87 | export function isProgram(token: Ast.Node): token is Ast.Program { 88 | return (token).kind === 'Program' 89 | } 90 | 91 | export function isExpression(token: Ast.Node): token is Ast.Expression { 92 | switch ((token).kind) { 93 | case 'ArrayExpr': 94 | case 'ArrowFunctionExpr': 95 | case 'BlockExpr': 96 | case 'CallChainExpr': 97 | case 'FunctionExpr': 98 | case 'JsxExpr': 99 | case 'JsxSelfClosingExpr': 100 | case 'NumberExpr': 101 | case 'ObjectExpr': 102 | case 'StringExpr': 103 | case 'VariableAssignExpr': 104 | return true 105 | default: 106 | return false 107 | } 108 | } 109 | 110 | export function isStatement(token: Ast.Node): token is Ast.Statement { 111 | switch ((token).kind) { 112 | case 'CallChainExpr': 113 | case 'DefineVariableStatement': 114 | case 'IfStatement': 115 | case 'ReturnStatement': 116 | case 'VariableAssignExpr': 117 | // expression 118 | case 'ArrayExpr': 119 | case 'ArrowFunctionExpr': 120 | case 'FunctionExpr': 121 | case 'JsxExpr': 122 | case 'JsxSelfClosingExpr': 123 | case 'NumberExpr': 124 | case 'ObjectExpr': 125 | case 'StringExpr': 126 | return true 127 | default: 128 | return false 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/parser/Parser.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:07:35 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-08 19:46:53 6 | */ 7 | import { 8 | alt, 9 | apply, 10 | expectEOF, 11 | expectSingleResult, 12 | kleft, 13 | kmid, 14 | kright, 15 | list_sc, 16 | nil, 17 | opt, 18 | Parser, 19 | rep_sc, 20 | rule, 21 | seq, 22 | str, 23 | tok, 24 | } from 'typescript-parsec' 25 | 26 | import * as Ast from './Ast' 27 | import * as Consumer from './Consumer' 28 | import { tokenizer, TokenKind } from './Tokenizer' 29 | 30 | // Primary 31 | export const KEYWORD = rule() 32 | export const IDENTITY = rule() 33 | export const NUMBER = rule() 34 | export const STRING = rule() 35 | export const OBJ = rule() 36 | export const ARRAY = rule() 37 | 38 | // Jsx 39 | export const PROP = rule() 40 | export const OPENTAG = rule() 41 | export const CLOSETAG = rule() 42 | export const JSXSELFCLOSE = rule() 43 | export const JSXOPENED = rule() 44 | export const TEXT = rule() 45 | export const JSXINNEREXPR = rule() 46 | 47 | // Expression 48 | export const BLOCK = rule() 49 | export const ARROWFUNCTION = rule() 50 | export const FUNCTION = rule() 51 | export const CALLCHAIN = rule() 52 | export const VARIABLEASSIGN = rule() 53 | 54 | // Statement 55 | export const DECLAREVARIABLE = rule() 56 | export const IFSTATEMENT = rule() 57 | export const RETURNSTATEMENT = rule() 58 | 59 | // Program 60 | export const PROGRAM = rule() 61 | 62 | /* 63 | JSX 64 | = JSXOPENED <|> JSXSELFCLOSE 65 | */ 66 | export const JSX: Parser = 67 | kmid(opt(str('(')), alt(JSXOPENED, JSXSELFCLOSE), opt(str(')'))) 68 | 69 | /* 70 | EXPRESSION 71 | = JSX <|> STRING <|> NUMBER <|> IDENTITY <|> OBJ <|> ARRAY | <|> ARROWFUNCTION <|> CALLCHAIN <|> FUNCTION 72 | */ 73 | export const EXPRESSION = alt( 74 | JSX, 75 | STRING, 76 | NUMBER, 77 | OBJ, 78 | ARRAY, 79 | ARROWFUNCTION, 80 | CALLCHAIN, 81 | FUNCTION, 82 | BLOCK 83 | ) 84 | 85 | /* 86 | PARAMETER 87 | = ( commaSep IDENTITY ) 88 | */ 89 | export const PARAMETER: Parser< 90 | TokenKind, 91 | (Ast.IdentityExpr | Ast.Expression)[] | undefined 92 | > = kmid( 93 | str('('), 94 | opt(list_sc(alt(IDENTITY, EXPRESSION), str(','))), 95 | seq(opt(str(',')), str(')')) 96 | ) 97 | 98 | /* 99 | STATEMENT 100 | = DECLAREVARIABLE <|> VARIABLEASSIGN <|> CALLCHAIN <|> IFSTATEMENT <|> RETURNSTATEMENT <|> EXPRESSION 101 | */ 102 | export const STATEMENT = alt( 103 | DECLAREVARIABLE, 104 | VARIABLEASSIGN, 105 | CALLCHAIN, 106 | IFSTATEMENT, 107 | RETURNSTATEMENT, 108 | // expr 109 | JSX, 110 | STRING, 111 | NUMBER, 112 | OBJ, 113 | ARRAY, 114 | ARROWFUNCTION, 115 | FUNCTION 116 | ) 117 | 118 | /* 119 | NUMBER 120 | = digit 121 | */ 122 | NUMBER.setPattern(apply(tok(TokenKind.Digit), Consumer.applyNumber)) 123 | 124 | /* 125 | TEXT 126 | = many $ letter <|> digit 127 | */ 128 | TEXT.setPattern( 129 | apply( 130 | rep_sc(alt(tok(TokenKind.Letter), tok(TokenKind.Digit))), 131 | Consumer.applyText 132 | ) 133 | ) 134 | 135 | /* 136 | STRING 137 | = "TEXT" 138 | = 'TEXT' 139 | */ 140 | STRING.setPattern( 141 | apply( 142 | alt(kmid(str('"'), TEXT, str('"')), kmid(str("'"), TEXT, str("'"))), 143 | Consumer.applyString 144 | ) 145 | ) 146 | 147 | /* 148 | KEYWORD 149 | = var 150 | = let 151 | = const 152 | */ 153 | KEYWORD.setPattern( 154 | apply(alt(str('var'), str('let'), str('const')), Consumer.applyKeyword) 155 | ) 156 | 157 | /* 158 | IDENTITY 159 | = letter : many $ NUMBER : TEXT 160 | */ 161 | IDENTITY.setPattern( 162 | apply( 163 | seq(tok(TokenKind.Letter), opt(seq(NUMBER, TEXT))), 164 | Consumer.applyIdentity 165 | ) 166 | ) 167 | 168 | /* 169 | OBJ 170 | = { commanSep $ IDENTITY : EXPRESSION} 171 | */ 172 | OBJ.setPattern( 173 | apply( 174 | kmid( 175 | str('{'), 176 | opt( 177 | list_sc(seq(IDENTITY, str(':'), alt(EXPRESSION, IDENTITY)), str(',')) 178 | ), 179 | seq(opt(str(',')), str('}')) 180 | ), 181 | Consumer.applyObject 182 | ) 183 | ) 184 | 185 | /* 186 | ARRAY 187 | = [ commaSep EXPRESSION ] 188 | */ 189 | ARRAY.setPattern( 190 | apply( 191 | kmid( 192 | str('['), 193 | opt(list_sc(alt(EXPRESSION, IDENTITY), str(','))), 194 | seq(opt(str(',')), str(']')) 195 | ), 196 | Consumer.applyArray 197 | ) 198 | ) 199 | 200 | /* 201 | PROP 202 | = IDENTITY 203 | = IDENTITY=JSXINNEREXPR 204 | = IDENTITY=STRING 205 | */ 206 | PROP.setPattern( 207 | apply( 208 | alt( 209 | seq(kleft(IDENTITY, str('=')), alt(STRING, JSXINNEREXPR)), 210 | seq(IDENTITY, nil()) 211 | ), 212 | Consumer.applyProp 213 | ) 214 | ) 215 | 216 | /* 217 | OPENTAG 218 | = 219 | */ 220 | OPENTAG.setPattern( 221 | apply( 222 | seq(kright(str('<'), IDENTITY), kleft(rep_sc(PROP), str('>'))), 223 | Consumer.applyOpeningTag 224 | ) 225 | ) 226 | 227 | /* 228 | CLOSETAG 229 | = 230 | */ 231 | CLOSETAG.setPattern( 232 | apply( 233 | kmid(seq(str('<'), str('/')), IDENTITY, str('>')), 234 | Consumer.applyClosingTag 235 | ) 236 | ) 237 | 238 | /* 239 | JSXSELFCLOSE 240 | = 241 | */ 242 | JSXSELFCLOSE.setPattern( 243 | apply( 244 | seq( 245 | kright(str('<'), IDENTITY), 246 | kleft(rep_sc(PROP), seq(str('/'), str('>'))) 247 | ), 248 | Consumer.applyJsxSelfClosing 249 | ) 250 | ) 251 | 252 | JSXINNEREXPR.setPattern( 253 | apply( 254 | kmid(str('{'), alt(EXPRESSION, IDENTITY), str('}')), 255 | Consumer.applyJsxInner 256 | ) 257 | ) 258 | 259 | /* 260 | JSXOPENED 261 | = OPENTAG ((many $ JSX <|> TEXT) <|> JSXINNEREXPR) CLOSETAG 262 | */ 263 | JSXOPENED.setPattern( 264 | apply( 265 | seq(OPENTAG, alt(rep_sc(alt(JSX, TEXT)), JSXINNEREXPR), CLOSETAG), 266 | Consumer.applyJsx 267 | ) 268 | ) 269 | 270 | /* 271 | ARROWFUNCTION 272 | = PARAMETER => BLOCK 273 | */ 274 | ARROWFUNCTION.setPattern( 275 | apply( 276 | seq(alt(PARAMETER, IDENTITY), kright(seq(str('='), str('>')), EXPRESSION)), 277 | Consumer.applyArrowFunction 278 | ) 279 | ) 280 | 281 | /* 282 | FUNCTION 283 | = function PARAMETER BLOCK 284 | = function IDENTITY PARAMETER BLOCK 285 | */ 286 | FUNCTION.setPattern( 287 | apply( 288 | seq(kright(str('function'), opt(IDENTITY)), PARAMETER, BLOCK), 289 | Consumer.applyFunction 290 | ) 291 | ) 292 | 293 | /* 294 | CALLCHAIN 295 | = dotSep IDENTITY PARAMETER 296 | */ 297 | CALLCHAIN.setPattern( 298 | apply(seq(list_sc(IDENTITY, str('.')), PARAMETER), Consumer.applyCallChain) 299 | ) 300 | 301 | /* 302 | VARIABLEASSIGN 303 | = IDENTITY = EXPRESSION 304 | */ 305 | VARIABLEASSIGN.setPattern( 306 | apply( 307 | seq(IDENTITY, kright(str('='), EXPRESSION)), 308 | Consumer.applyVariableAssign 309 | ) 310 | ) 311 | 312 | /* 313 | BLOCK 314 | = { many STATEMENT } 315 | */ 316 | BLOCK.setPattern( 317 | apply( 318 | kmid(str('{'), rep_sc(kleft(STATEMENT, opt(str(';')))), str('}')), 319 | Consumer.applyBlock 320 | ) 321 | ) 322 | 323 | // Statement 324 | 325 | /* 326 | DECLAREVARIABLE 327 | = KEYWORD IDENTITY 328 | = KEYWORD VARIABLEASSIGN 329 | */ 330 | DECLAREVARIABLE.setPattern( 331 | apply( 332 | seq(KEYWORD, alt(VARIABLEASSIGN, IDENTITY)), 333 | Consumer.applyDefineVariable 334 | ) 335 | ) 336 | 337 | /* 338 | IFSTATEMENT 339 | = if PARAMETER BLOCK many $ else (option $ if PARAMETER) BLOCK 340 | */ 341 | IFSTATEMENT.setPattern( 342 | apply( 343 | seq( 344 | kright(str('if'), PARAMETER), 345 | alt(BLOCK, STATEMENT), 346 | opt(kright(str('else'), alt(BLOCK, STATEMENT))) 347 | ), 348 | Consumer.applyIf 349 | ) 350 | ) 351 | 352 | /* 353 | RETURNSTATEMENT 354 | = 355 | */ 356 | RETURNSTATEMENT.setPattern( 357 | apply(kright(str('return'), EXPRESSION), Consumer.applyReturn) 358 | ) 359 | 360 | /* 361 | PROGRAM 362 | = many STATEMENT 363 | */ 364 | PROGRAM.setPattern(apply(rep_sc(STATEMENT), Consumer.applyProgram)) 365 | 366 | // parse ast 367 | export function parse(code: string) { 368 | return expectSingleResult(expectEOF(PROGRAM.parse(tokenizer.parse(code)))) 369 | } 370 | -------------------------------------------------------------------------------- /src/parser/Tokenizer.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:07:39 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-04 10:14:05 6 | */ 7 | import { buildLexer } from 'typescript-parsec' 8 | 9 | export enum TokenKind { 10 | Letter, 11 | Digit, 12 | Space, 13 | Chars, 14 | Comment1, 15 | Comment2, 16 | } 17 | 18 | export const TokenRules: [boolean, RegExp, TokenKind][] = [ 19 | [true, /^[a-zA-Z_$]+/g, TokenKind.Letter], 20 | [true, /^[0-9]+/g, TokenKind.Digit], 21 | [true, /^[<=">/{}\[\]:,'\.();]/g, TokenKind.Chars], 22 | [false, /^\s+/g, TokenKind.Space], 23 | [false, /^[/][/][^\n]*\n/g, TokenKind.Comment1], 24 | [false, /^{?[/]\*([^*]|\*+[^/])*\*+[/]}?/g, TokenKind.Comment2], 25 | ] 26 | 27 | export const tokenizer = buildLexer(TokenRules) 28 | -------------------------------------------------------------------------------- /src/parser/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Ast' 2 | export * from './Consumer' 3 | export * from './Factory' 4 | export * from './Parser' 5 | export * from './Tokenizer' 6 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-10-02 15:31:44 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-08 19:45:38 6 | */ 7 | import { writeFileSync } from 'fs' 8 | 9 | import { compiler, parser, transformer, traverser } from './' 10 | import { read } from './__tests__/utils' 11 | 12 | async function main() { 13 | const code = await read('code.txt') 14 | 15 | const ast = parser.parse(code) 16 | writeFileSync('./public/ast.json', JSON.stringify(ast, null, 2)) 17 | 18 | const jsx = transformer.transform(ast) 19 | writeFileSync('./public/jsx.json', JSON.stringify(jsx, null, 2)) 20 | 21 | const out = compiler.compile(jsx) 22 | writeFileSync('./public/out.jsx', out) 23 | 24 | const visited: transformer.Type = [] 25 | const jsx2 = traverser.traverse(jsx, node => { 26 | console.log('node', node) 27 | visited.push(node) 28 | if (node) { 29 | if (transformer.isJsxElement(node)) { 30 | if (node.props && node.props.id === 'qwq') { 31 | return transformer.createJsxElement(node.tagName, { 32 | ...node.props, 33 | meta: 233, 34 | }) 35 | } 36 | } 37 | } 38 | }) 39 | 40 | writeFileSync('./public/jsx2.json', JSON.stringify(jsx2, null, 2)) 41 | writeFileSync('./public/visited.json', JSON.stringify(visited, null, 2)) 42 | 43 | const node = traverser.findNode( 44 | jsx, 45 | node => 46 | transformer.isJsxElement(node) && node.props && node.tagName === 'List' 47 | ) 48 | writeFileSync('./public/jsx-find.json', JSON.stringify(node, null, 2)) 49 | writeFileSync('./public/out-find.jsx', compiler.compile(node)) 50 | 51 | // get props list source code 52 | if (transformer.isJsxElement(node[0])) { 53 | const result = node[0] 54 | const list = result.props.list 55 | if (Array.isArray(list)) { 56 | console.log(list.map((item: any) => compiler.compile(item.content))) 57 | } 58 | } 59 | } 60 | 61 | main() 62 | -------------------------------------------------------------------------------- /src/transformer/Factory.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:07:42 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-08 19:39:51 6 | */ 7 | import * as Jsx from './Jsx' 8 | 9 | export function isIdentity(element: Jsx.Type): element is Jsx.Identity { 10 | if (!element) return false 11 | const identity = element 12 | return identity.$$typeof === 'identity' 13 | } 14 | 15 | // Jsx 16 | export function isJsxElement(element: Jsx.Type): element is Jsx.JsxElement { 17 | if (!element) return false 18 | const jsxElement = element 19 | return jsxElement.$$typeof === 'jsx' 20 | } 21 | 22 | export function isTextElement(element: Jsx.Type): element is Jsx.TextElement { 23 | if (!element) return false 24 | const textElement = element 25 | return textElement.$$typeof === 'text' 26 | } 27 | 28 | export function isProgram(element: Jsx.Type): element is Jsx.Program { 29 | if (!element) return false 30 | const textElement = element 31 | return textElement.$$typeof === 'program' 32 | } 33 | 34 | export function createNode( 35 | node: T, 36 | kind: T['$$typeof'] = node.$$typeof 37 | ) { 38 | return Object.defineProperty(node, '$$typeof', { 39 | value: kind, 40 | enumerable: false, 41 | }) 42 | } 43 | 44 | export function createIdentity(name: string): Jsx.Identity { 45 | return createNode({ 46 | $$typeof: 'identity', 47 | name, 48 | }) 49 | } 50 | 51 | export function createJsxAttributes( 52 | props: { 53 | [k: string]: Jsx.Type | Jsx.Identity 54 | } = {} 55 | ): Jsx.JsxAttributes { 56 | return createNode({ 57 | $$typeof: 'jsx-attrs', 58 | ...props, 59 | }) 60 | } 61 | 62 | export function createJsxObject( 63 | values: { 64 | [k: string]: Jsx.Type | Jsx.Identity 65 | } = {} 66 | ): Jsx.JsxObject { 67 | return createNode({ 68 | $$typeof: 'jsx-obj', 69 | ...values, 70 | }) 71 | } 72 | 73 | // expression 74 | 75 | export function isArrowFunction( 76 | element: Jsx.Type 77 | ): element is Jsx.ArrowFunction { 78 | if (!element) return false 79 | const func = element 80 | return func.$$typeof === 'arrow-function' 81 | } 82 | 83 | export function isFunction(element: Jsx.Type): element is Jsx.Function { 84 | if (!element) return false 85 | const func = element 86 | return func.$$typeof === 'function' 87 | } 88 | 89 | export function isCallChain(element: Jsx.Type): element is Jsx.CallChain { 90 | if (!element) return false 91 | const call = element 92 | return call.$$typeof === 'call' 93 | } 94 | 95 | export function isVariableAssign( 96 | element: Jsx.Type 97 | ): element is Jsx.VariableAssign { 98 | if (!element) return false 99 | const call = element 100 | return call.$$typeof === 'variable-assign' 101 | } 102 | 103 | // statement 104 | 105 | export function isDefineVariable( 106 | element: Jsx.Type 107 | ): element is Jsx.DefineVariable { 108 | if (!element) return false 109 | const call = element 110 | return call.$$typeof === 'define-variable' 111 | } 112 | 113 | export function isIf(element: Jsx.Type): element is Jsx.If { 114 | if (!element) return false 115 | const call = element 116 | return call.$$typeof === 'if' 117 | } 118 | 119 | export function isReturn(element: Jsx.Type): element is Jsx.Return { 120 | if (!element) return false 121 | const call = element 122 | return call.$$typeof === 'return' 123 | } 124 | 125 | export function isJsxObject(element: Jsx.Type): element is Jsx.JsxObject { 126 | if (!element) return false 127 | const obj = element 128 | return obj.$$typeof === 'jsx-obj' 129 | } 130 | 131 | export function isJsxAttributes( 132 | element: Jsx.Type 133 | ): element is Jsx.JsxAttributes { 134 | if (!element) return false 135 | const obj = element 136 | return obj.$$typeof === 'jsx-attrs' 137 | } 138 | 139 | export function isBlock(element: Jsx.Type): element is Jsx.Block { 140 | if (!element) return false 141 | const obj = element 142 | return obj.$$typeof === 'block' 143 | } 144 | 145 | export function createJsxElement( 146 | tagName: string, 147 | props: Jsx.JsxAttributes = createJsxAttributes(), 148 | children: Jsx.JsxElement['children'] = [] 149 | ): Jsx.JsxElement { 150 | return createNode({ 151 | $$typeof: 'jsx', 152 | tagName, 153 | props, 154 | children, 155 | }) 156 | } 157 | 158 | export function createTextElement(nodeValue: string): Jsx.TextElement { 159 | return createNode({ 160 | $$typeof: 'text', 161 | tagName: 'text', 162 | nodeValue, 163 | }) 164 | } 165 | 166 | export function createBlock(body: Jsx.Type[]): Jsx.Block { 167 | return createNode({ 168 | $$typeof: 'block', 169 | statements: body, 170 | }) 171 | } 172 | 173 | export function createArrowFunction( 174 | args: Jsx.Parameter | Jsx.Identity, 175 | body: Jsx.Type 176 | ): Jsx.ArrowFunction { 177 | return createNode({ 178 | $$typeof: 'arrow-function', 179 | args, 180 | body, 181 | }) 182 | } 183 | 184 | export function createFunction( 185 | name: string | undefined, 186 | args: Jsx.Parameter = [], 187 | body: Jsx.Block 188 | ): Jsx.Function { 189 | return createNode({ 190 | $$typeof: 'function', 191 | name, 192 | args, 193 | body, 194 | }) 195 | } 196 | 197 | export function createCallChain( 198 | caller: string, 199 | chain: string[], 200 | args: Jsx.Parameter = [] 201 | ): Jsx.CallChain { 202 | return createNode({ 203 | $$typeof: 'call', 204 | caller, 205 | chain, 206 | args, 207 | }) 208 | } 209 | 210 | export function createVariableAssign( 211 | name: string, 212 | value: Jsx.Type | undefined 213 | ): Jsx.VariableAssign { 214 | return createNode({ 215 | $$typeof: 'variable-assign', 216 | name: name, 217 | value: value, 218 | }) 219 | } 220 | 221 | export function createDefineVariable( 222 | type: string, 223 | assign: Jsx.VariableAssign | Jsx.Identity 224 | ): Jsx.DefineVariable { 225 | return createNode({ 226 | $$typeof: 'define-variable', 227 | type, 228 | assign, 229 | }) 230 | } 231 | 232 | export function createIf( 233 | args: Jsx.Parameter = [], 234 | body: Jsx.Type, 235 | els: Jsx.Type | undefined 236 | ): Jsx.If { 237 | return createNode({ 238 | $$typeof: 'if', 239 | args, 240 | body, 241 | els: els, 242 | }) 243 | } 244 | 245 | export function createReturn(value: Jsx.Type): Jsx.Return { 246 | return createNode({ 247 | $$typeof: 'return', 248 | value, 249 | }) 250 | } 251 | 252 | export function createProgram(body: Jsx.Type[]): Jsx.Program { 253 | return createNode({ 254 | $$typeof: 'program', 255 | body, 256 | }) 257 | } 258 | 259 | export function getElementEntries(element: Jsx.JsxObject | Jsx.JsxAttributes) { 260 | if (isJsxObject(element) || isJsxAttributes(element)) { 261 | return Object.entries(element).filter(([key]) => key !== '$$typeof') 262 | } 263 | return [] 264 | } 265 | -------------------------------------------------------------------------------- /src/transformer/Jsx.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:07:47 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-08 19:39:57 6 | */ 7 | export type Type = 8 | | JsxNode 9 | | Identity 10 | | string 11 | | number 12 | | boolean 13 | | null 14 | | JsxObject 15 | | JsxAttributes 16 | | Type[] 17 | | ArrowFunction 18 | | CallChain 19 | | Function 20 | | DefineVariable 21 | | VariableAssign 22 | | If 23 | | Block 24 | | Return 25 | | Program 26 | 27 | export type Parameter = (Identity | Type)[] | undefined 28 | 29 | // Jsx 30 | 31 | export interface Node { 32 | $$typeof: string 33 | [k: string]: any 34 | } 35 | 36 | export interface Identity extends Node { 37 | $$typeof: 'identity' 38 | name: string 39 | } 40 | 41 | export interface JsxObject extends Node { 42 | $$typeof: 'jsx-obj' 43 | [k: string]: Type | Identity 44 | } 45 | 46 | export interface JsxAttributes extends Node { 47 | $$typeof: 'jsx-attrs' 48 | [k: string]: Type | Identity 49 | } 50 | 51 | export interface JsxElement extends Node { 52 | $$typeof: 'jsx' 53 | props: JsxAttributes 54 | children: Type[] | Type 55 | } 56 | 57 | export interface TextElement extends Node { 58 | $$typeof: 'text' 59 | tagName: 'text' 60 | nodeValue: string 61 | } 62 | 63 | // Statement 64 | 65 | export interface CallChain extends Node { 66 | $$typeof: 'call' 67 | caller: string 68 | chain: string[] 69 | args: Parameter 70 | } 71 | 72 | export interface Block extends Node { 73 | $$typeof: 'block' 74 | statements: Type[] 75 | } 76 | 77 | export interface ArrowFunction extends Node { 78 | $$typeof: 'arrow-function' 79 | args: Parameter | Identity 80 | body: Type 81 | } 82 | 83 | export interface Function extends Node { 84 | $$typeof: 'function' 85 | name: string | undefined 86 | args: Parameter 87 | body: Block 88 | } 89 | 90 | export interface VariableAssign extends Node { 91 | $$typeof: 'variable-assign' 92 | name: string 93 | value: Type | undefined 94 | } 95 | 96 | export interface DefineVariable extends Node { 97 | $$typeof: 'define-variable' 98 | type: string 99 | assign: VariableAssign | Identity 100 | } 101 | 102 | export interface If extends Node { 103 | $$typeof: 'if' 104 | args: Parameter 105 | body: Type 106 | els: Type | undefined 107 | } 108 | 109 | export interface Return extends Node { 110 | $$typeof: 'return' 111 | value: Type 112 | } 113 | 114 | export interface Program extends Node { 115 | $$typeof: 'program' 116 | body: Type[] 117 | } 118 | 119 | export type JsxNode = JsxElement | TextElement 120 | -------------------------------------------------------------------------------- /src/transformer/Transformer.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-09-12 12:07:49 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-08 19:47:34 6 | */ 7 | import * as Ast from '../parser/Ast' 8 | import * as Factory from '../parser/Factory' 9 | import * as TFactory from './Factory' 10 | import * as Jsx from './Jsx' 11 | 12 | // basic 13 | 14 | export function transformIdentityExpr( 15 | identity: Ast.IdentityExpr 16 | ): Jsx.Identity { 17 | return TFactory.createIdentity(identity.name) 18 | } 19 | 20 | export function transformNumberExpr(number: Ast.NumberExpr): number { 21 | return number.value 22 | } 23 | 24 | export function transformStringExpr(string: Ast.StringExpr): string { 25 | return string.value 26 | } 27 | 28 | export function transformTextExpr(text: Ast.TextExpr): Jsx.TextElement { 29 | return TFactory.createTextElement(text.value) 30 | } 31 | 32 | export function transformObjectExpr(object: Ast.ObjectExpr): Jsx.JsxObject { 33 | const props = object.props 34 | return TFactory.createJsxObject( 35 | Object.fromEntries( 36 | Object.entries(props).map( 37 | ([key, expression]) => [ 38 | key, 39 | Factory.isIdentityExpr(expression) 40 | ? transformIdentityExpr(expression) 41 | : transformExpression(expression), 42 | ], 43 | {} 44 | ) 45 | ) 46 | ) 47 | } 48 | 49 | export function transformArrayExpr( 50 | array: Ast.ArrayExpr 51 | ): (Jsx.Type | Jsx.Identity)[] { 52 | const items = array.items 53 | return items.map(expression => 54 | Factory.isIdentityExpr(expression) 55 | ? transformIdentityExpr(expression) 56 | : transformExpression(expression) 57 | ) 58 | } 59 | 60 | // Jsx 61 | 62 | export function transformPropsExpr(props: Ast.PropExpr[]): Jsx.JsxAttributes { 63 | return TFactory.createJsxAttributes( 64 | Object.fromEntries( 65 | props.map(prop => { 66 | const key = prop.key.name 67 | const expression = prop.value 68 | return [ 69 | key, 70 | Factory.isStringExpr(expression) 71 | ? transformStringExpr(expression) 72 | : transformJsxInner(expression), 73 | ] 74 | }) 75 | ) 76 | ) 77 | } 78 | 79 | export function transformJsxSelfClosingExpr( 80 | jsx: Ast.JsxSelfClosingExpr 81 | ): Jsx.JsxElement { 82 | const tagName = jsx.tagName.name 83 | const props = jsx.props 84 | return TFactory.createJsxElement(tagName, transformPropsExpr(props), []) 85 | } 86 | 87 | export function transformJsxInner( 88 | jsx: Ast.JsxInnerExpr 89 | ): Jsx.Type | Jsx.Identity { 90 | return Factory.isIdentityExpr(jsx.body) 91 | ? transformIdentityExpr(jsx.body) 92 | : transformExpression(jsx.body) 93 | } 94 | 95 | export function transformJsx(jsx: Ast.Jsx): Jsx.JsxElement { 96 | if (Factory.isJsxSelfClosingExpr(jsx)) { 97 | return transformJsxSelfClosingExpr(jsx) 98 | } 99 | const tagName = jsx.openingTag.tagName.name 100 | const props = jsx.openingTag.props 101 | const body = jsx.body 102 | return TFactory.createJsxElement( 103 | tagName, 104 | transformPropsExpr(props), 105 | Array.isArray(body) 106 | ? body.map(node => { 107 | if (Factory.isTextExpr(node)) { 108 | return transformTextExpr(node) 109 | } else { 110 | return transformJsx(node) 111 | } 112 | }) 113 | : transformJsxInner(body) 114 | ) 115 | } 116 | 117 | // Statement 118 | 119 | export function transformArrowFunction( 120 | func: Ast.ArrowFunctionExpr 121 | ): Jsx.ArrowFunction { 122 | const { args, body } = func 123 | return TFactory.createArrowFunction( 124 | Array.isArray(args) 125 | ? transformParameter(args) 126 | : args 127 | ? transformIdentityExpr(args) 128 | : [], 129 | transformExpression(body) 130 | ) 131 | } 132 | 133 | export function transformFunction(func: Ast.FunctionExpr): Jsx.Function { 134 | const { name, args = [], body } = func 135 | return TFactory.createFunction( 136 | name?.name, 137 | transformParameter(args), 138 | transformBlock(body) 139 | ) 140 | } 141 | 142 | export function transformBlock(func: Ast.BlockExpr): Jsx.Block { 143 | const { body } = func 144 | return TFactory.createBlock( 145 | body.map(statement => transformStatement(statement)) 146 | ) 147 | } 148 | 149 | export function transformParameter( 150 | args: Ast.Parameter = [] 151 | ): (Jsx.Type | Jsx.Identity)[] { 152 | return args.map(arg => 153 | Factory.isExpression(arg) 154 | ? transformExpression(arg) 155 | : transformIdentityExpr(arg) 156 | ) 157 | } 158 | 159 | export function transformCallChain(call: Ast.CallChainExpr): Jsx.CallChain { 160 | const { caller, chain, args = [] } = call 161 | return TFactory.createCallChain( 162 | caller.name, 163 | chain.map(item => item.name), 164 | transformParameter(args) 165 | ) 166 | } 167 | 168 | export function transformVariableAssign( 169 | assign: Ast.VariableAssignExpr 170 | ): Jsx.VariableAssign { 171 | const { name, value } = assign 172 | return TFactory.createVariableAssign( 173 | name.name, 174 | value ? transformExpression(value) : undefined 175 | ) 176 | } 177 | 178 | // statement 179 | 180 | export function transformDefineVariable( 181 | def: Ast.DefineVariableStatement 182 | ): Jsx.DefineVariable { 183 | const { type, assign } = def 184 | let value: Jsx.Identity | Jsx.VariableAssign 185 | if (Factory.isVariableAssignExpr(assign)) { 186 | value = transformVariableAssign(assign) 187 | } else { 188 | const result = transformIdentityExpr(assign) 189 | if (typeof result === 'boolean') { 190 | throw new SyntaxError(`transformDefineVariable`) 191 | } else { 192 | value = result 193 | } 194 | } 195 | return TFactory.createDefineVariable(type.name, value) 196 | } 197 | 198 | export function transformIf(ifElse: Ast.IfStatement): Jsx.If { 199 | const { args = [], body, els } = ifElse 200 | return TFactory.createIf( 201 | transformParameter(args), 202 | Factory.isBlockExpr(body) ? transformBlock(body) : transformStatement(body), 203 | els 204 | ? Factory.isBlockExpr(els) 205 | ? transformBlock(els) 206 | : transformStatement(els) 207 | : undefined 208 | ) 209 | } 210 | 211 | export function transformReturn(ret: Ast.ReturnStatement): Jsx.Return { 212 | const { value } = ret 213 | return TFactory.createReturn(transformExpression(value)) 214 | } 215 | 216 | export function transformExpression(expression: Ast.Expression): Jsx.Type { 217 | switch (expression.kind) { 218 | case 'ArrayExpr': 219 | return transformArrayExpr(expression) 220 | case 'JsxExpr': 221 | case 'JsxSelfClosingExpr': 222 | return transformJsx(expression) 223 | case 'NumberExpr': 224 | return transformNumberExpr(expression) 225 | case 'ObjectExpr': 226 | return transformObjectExpr(expression) 227 | case 'StringExpr': 228 | return transformStringExpr(expression) 229 | case 'ArrowFunctionExpr': 230 | return transformArrowFunction(expression) 231 | case 'FunctionExpr': 232 | return transformFunction(expression) 233 | case 'CallChainExpr': 234 | return transformCallChain(expression) 235 | case 'VariableAssignExpr': 236 | return transformVariableAssign(expression) 237 | case 'BlockExpr': 238 | return transformBlock(expression) 239 | default: 240 | return null 241 | } 242 | } 243 | 244 | export function transformStatement(statement: Ast.Statement): Jsx.Type { 245 | switch (statement.kind) { 246 | case 'CallChainExpr': 247 | return transformCallChain(statement) 248 | case 'DefineVariableStatement': 249 | return transformDefineVariable(statement) 250 | case 'VariableAssignExpr': 251 | return transformVariableAssign(statement) 252 | case 'IfStatement': 253 | return transformIf(statement) 254 | case 'ReturnStatement': 255 | return transformReturn(statement) 256 | default: 257 | return null 258 | } 259 | } 260 | 261 | export function transform(program: Ast.Program): Jsx.Type 262 | export function transform(program: Ast.Expression): Jsx.Type 263 | export function transform(program: Ast.Statement): Jsx.Type 264 | export function transform( 265 | program: Ast.Program | Ast.Expression | Ast.Statement 266 | ): Jsx.Type { 267 | if (Factory.isProgram(program)) { 268 | return TFactory.createProgram( 269 | program.body.map(expression => transform(expression)) 270 | ) 271 | } else if (Factory.isExpression(program)) { 272 | return transformExpression(program) 273 | } else { 274 | return transformStatement(program) 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/transformer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Factory' 2 | export * from './Jsx' 3 | export * from './Transformer' 4 | -------------------------------------------------------------------------------- /src/traverser/Traverser.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: saber2pr 3 | * @Date: 2021-10-02 15:31:32 4 | * @Last Modified by: saber2pr 5 | * @Last Modified time: 2021-10-08 19:36:48 6 | */ 7 | import * as Jsx from '../transformer/Jsx' 8 | import * as Factory from '../transformer/Factory' 9 | 10 | // Jsx 11 | export function traverseJsxNode( 12 | node: Jsx.JsxNode, 13 | callback: (node: Jsx.Type) => Jsx.Type | void 14 | ): Jsx.Type { 15 | if (Factory.isTextElement(node)) { 16 | return callback(node) ?? node 17 | } 18 | 19 | // map props 20 | const newProps = traverseJsxAttributes(node.props, callback) 21 | // link new props 22 | node.props = newProps 23 | 24 | // map children 25 | const children = node.children 26 | const newChildren = Array.isArray(children) 27 | ? children.map(node => { 28 | let newNode: Jsx.Type | void 29 | if (Factory.isJsxElement(node)) { 30 | newNode = traverseJsxNode(node, callback) 31 | } 32 | if (newNode) { 33 | return newNode 34 | } 35 | return callback(node) ?? node 36 | }) 37 | : children 38 | 39 | // link new children 40 | const newNode = callback(node) 41 | if (newNode) { 42 | if (Factory.isJsxElement(newNode)) { 43 | newNode.children = newChildren 44 | } 45 | return newNode 46 | } 47 | node.children = newChildren 48 | return node 49 | } 50 | 51 | // statement 52 | 53 | export function traverseArrowFunction( 54 | node: Jsx.ArrowFunction, 55 | callback: (node: Jsx.Type) => Jsx.Type | void 56 | ): Jsx.Type { 57 | // TODO arrow function traverse 58 | return callback(node) ?? node 59 | } 60 | 61 | export function traverseFunction( 62 | node: Jsx.Function, 63 | callback: (node: Jsx.Type) => Jsx.Type | void 64 | ): Jsx.Type { 65 | // TODO function traverse 66 | return callback(node) ?? node 67 | } 68 | 69 | export function traverseCallChain( 70 | call: Jsx.CallChain, 71 | callback: (node: Jsx.Type) => Jsx.Type | void 72 | ): Jsx.Type { 73 | // TODO call chain traverse 74 | return callback(call) ?? call 75 | } 76 | 77 | export function traverseJsxObject( 78 | obj: Jsx.JsxObject, 79 | callback: (node: Jsx.Type) => Jsx.Type | void 80 | ): Jsx.JsxObject { 81 | return Factory.createJsxObject( 82 | Object.fromEntries( 83 | Object.entries(obj).map(([key, value]) => { 84 | return [key, traverse(value, callback)] 85 | }) 86 | ) 87 | ) 88 | } 89 | 90 | export function traverseJsxAttributes( 91 | obj: Jsx.JsxAttributes, 92 | callback: (node: Jsx.Type) => Jsx.Type | void 93 | ): Jsx.JsxAttributes { 94 | return Factory.createJsxAttributes( 95 | Object.fromEntries( 96 | Object.entries(obj).map(([key, value]) => { 97 | return [key, traverse(value, callback)] 98 | }) 99 | ) 100 | ) 101 | } 102 | 103 | export function traverseProgram( 104 | program: Jsx.Program, 105 | callback: (node: Jsx.Type) => Jsx.Type | void 106 | ): Jsx.Program { 107 | const { body = [] } = program 108 | const newBody = body.map(node => traverse(node, callback)) 109 | const newProgram: Jsx.Program = { 110 | ...program, 111 | body: newBody, 112 | } 113 | return newProgram 114 | } 115 | 116 | export function traverse( 117 | node: Jsx.Type, 118 | callback: (node: Jsx.Type) => Jsx.Type | void 119 | ): Jsx.Type { 120 | if (Factory.isProgram(node)) { 121 | return traverseProgram(node, callback) 122 | } 123 | // return leaf node 124 | if ( 125 | node === null || 126 | typeof node === 'string' || 127 | typeof node === 'number' || 128 | typeof node === 'boolean' || 129 | Factory.isTextElement(node) 130 | ) { 131 | return callback(node) ?? node 132 | } 133 | // jsx 134 | if (Factory.isJsxElement(node)) { 135 | return traverseJsxNode(node, callback) 136 | } 137 | // array 138 | if (Array.isArray(node)) { 139 | return node.map(item => traverse(item, callback)) 140 | } 141 | // func 142 | if (Factory.isArrowFunction(node)) { 143 | return traverseArrowFunction(node, callback) 144 | } 145 | // call 146 | if (Factory.isCallChain(node)) { 147 | return traverseCallChain(node, callback) 148 | } 149 | // obj 150 | if (Factory.isJsxObject(node)) { 151 | return traverseJsxObject(node, callback) 152 | } 153 | if (Factory.isJsxAttributes(node)) { 154 | return traverseJsxAttributes(node, callback) 155 | } 156 | return callback(node) ?? node 157 | } 158 | 159 | export function findNode(root: Jsx.Type, filter: (node: Jsx.Type) => boolean) { 160 | const result: Jsx.Type[] = [] 161 | traverse(root, node => { 162 | if (filter(node)) { 163 | result.push(node) 164 | } 165 | }) 166 | return result 167 | } 168 | -------------------------------------------------------------------------------- /src/traverser/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Traverser' 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2019", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "./lib", 7 | "esModuleInterop": true, 8 | "strict": true, 9 | "skipLibCheck": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "noImplicitAny": true, 13 | "moduleResolution": "node", 14 | "downlevelIteration": true, 15 | "experimentalDecorators": true, 16 | "lib": ["ESNext"] 17 | }, 18 | "include": ["src"], 19 | "exclude": ["node_modules", "lib"] 20 | } 21 | --------------------------------------------------------------------------------