├── .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 | [](https://www.npmjs.com/package/@saber2pr/jsx-ast-parser)
4 | [](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}${tagName}>`
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 |
--------------------------------------------------------------------------------