From c9995f9e5723a95e744664aebf700a0f12801bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eiri=CC=81kur=20Torfason?= Date: Wed, 16 Feb 2022 22:59:59 +0000 Subject: [PATCH] Provide JavaScript port of TokenStreamRewriter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: EirĂ­kur Fannar Torfason --- .gitignore | 6 + runtime/JavaScript/.c8rc.json | 10 + runtime/JavaScript/.eslintrc.yml | 5 +- runtime/JavaScript/package-lock.json | 538 +++++++++++++ runtime/JavaScript/package.json | 5 +- runtime/JavaScript/spec/IntervalSetSpec.js | 2 +- runtime/JavaScript/spec/rewriter/Makefile | 16 + .../spec/rewriter/TokenStreamRewriterSpec.js | 715 ++++++++++++++++++ runtime/JavaScript/spec/rewriter/abc.g4 | 4 + runtime/JavaScript/spec/rewriter/calc.g4 | 8 + .../spec/rewriter/generatedCode/abc.js | 37 + .../spec/rewriter/generatedCode/calc.js | 50 ++ .../src/antlr4/BufferedTokenStream.js | 12 +- .../src/antlr4/TokenStreamRewriter.d.ts | 38 + .../src/antlr4/TokenStreamRewriter.js | 442 +++++++++++ runtime/JavaScript/src/antlr4/index.d.ts | 1 + runtime/JavaScript/src/antlr4/index.node.js | 3 +- runtime/JavaScript/src/antlr4/index.web.js | 3 +- 18 files changed, 1885 insertions(+), 10 deletions(-) create mode 100644 runtime/JavaScript/.c8rc.json create mode 100644 runtime/JavaScript/spec/rewriter/Makefile create mode 100644 runtime/JavaScript/spec/rewriter/TokenStreamRewriterSpec.js create mode 100644 runtime/JavaScript/spec/rewriter/abc.g4 create mode 100644 runtime/JavaScript/spec/rewriter/calc.g4 create mode 100644 runtime/JavaScript/spec/rewriter/generatedCode/abc.js create mode 100644 runtime/JavaScript/spec/rewriter/generatedCode/calc.js create mode 100644 runtime/JavaScript/src/antlr4/TokenStreamRewriter.d.ts create mode 100644 runtime/JavaScript/src/antlr4/TokenStreamRewriter.js diff --git a/.gitignore b/.gitignore index d8a659d539..a9a2852411 100644 --- a/.gitignore +++ b/.gitignore @@ -83,6 +83,9 @@ nbactions*.xml /gen4/ /tool/playground/ tmp/ +**/generatedCode/*.interp +**/generatedCode/*.tokens +**/generatedCode/*.bak # Configurable build files bilder.py @@ -107,6 +110,9 @@ runtime/PHP # Swift binaries .build/ +# Code coverage reports +coverage/ + # Cpp generated build files runtime/Cpp/CMakeCache.txt runtime/Cpp/CMakeFiles/ diff --git a/runtime/JavaScript/.c8rc.json b/runtime/JavaScript/.c8rc.json new file mode 100644 index 0000000000..5e7f11451e --- /dev/null +++ b/runtime/JavaScript/.c8rc.json @@ -0,0 +1,10 @@ +{ + "all": true, + "include": [ + "src/antlr4/**/*.js" + ], + "reporter": [ + "text", + "lcov" + ] +} diff --git a/runtime/JavaScript/.eslintrc.yml b/runtime/JavaScript/.eslintrc.yml index c8f66d671a..b125825eca 100644 --- a/runtime/JavaScript/.eslintrc.yml +++ b/runtime/JavaScript/.eslintrc.yml @@ -14,7 +14,8 @@ parser: "@babel/eslint-parser" parserOptions: sourceType: module project: ['./tsconfig.json'] + ecmaVersion: 2022 rules: - no-unused-vars: ["error", {vars: "all", args: "none"}] - no-prototype-builtins: [ "off" ] + no-unused-vars: ["error", { vars: "all", args: "none" }] + no-prototype-builtins: ["off"] no-fallthrough: ["error", { "commentPattern": "no-break" }] diff --git a/runtime/JavaScript/package-lock.json b/runtime/JavaScript/package-lock.json index be1aedd965..ec8f67401b 100644 --- a/runtime/JavaScript/package-lock.json +++ b/runtime/JavaScript/package-lock.json @@ -14,6 +14,7 @@ "@babel/preset-env": "^7.19.4", "@types/node": "^18.7.23", "babel-loader": "^8.2.5", + "c8": "^7.12.0", "compression-webpack-plugin": "^10.0.0", "eslint": "^8.23.1", "eslint-webpack-plugin": "^3.2.0", @@ -1655,6 +1656,12 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -1735,6 +1742,15 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -1876,6 +1892,12 @@ "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", "dev": true }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -2332,6 +2354,32 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/c8": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.12.0.tgz", + "integrity": "sha512-CtgQrHOkyxr5koX1wEUmN/5cfDa2ckbHRA4Gy5LAL0zaCFtVWJS5++n+w4/sr2GWGerBxgTjpKeDclk/Qk6W/A==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^2.0.0", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-reports": "^3.1.4", + "rimraf": "^3.0.2", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2380,6 +2428,17 @@ "node": ">=6.0" } }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -2589,6 +2648,12 @@ "integrity": "sha512-Uk7C+7aPBryUR1Fwvk9VmipBcN9fVsqBO57jV2ZjTm+IZ6BMNqu7EDVEg2HxCNufk6QcWlFsBkhQyQroB2VWKw==", "dev": true }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", @@ -3155,6 +3220,19 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3176,6 +3254,15 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -3256,6 +3343,12 @@ "node": ">=4" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -3355,6 +3448,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -3412,6 +3514,63 @@ "node": ">=0.10.0" } }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jasmine": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.5.0.tgz", @@ -4072,6 +4231,15 @@ "jsesc": "bin/jsesc" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -4265,6 +4433,12 @@ "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -4284,6 +4458,20 @@ "source-map": "^0.6.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -4449,6 +4637,20 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -4588,6 +4790,20 @@ "punycode": "^2.1.0" } }, + "node_modules/v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", @@ -4780,18 +4996,104 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -5935,6 +6237,12 @@ "to-fast-properties": "^2.0.0" } }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -5992,6 +6300,12 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, "@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -6114,6 +6428,12 @@ "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", "dev": true }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -6487,6 +6807,26 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "c8": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.12.0.tgz", + "integrity": "sha512-CtgQrHOkyxr5koX1wEUmN/5cfDa2ckbHRA4Gy5LAL0zaCFtVWJS5++n+w4/sr2GWGerBxgTjpKeDclk/Qk6W/A==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^2.0.0", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-reports": "^3.1.4", + "rimraf": "^3.0.2", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -6516,6 +6856,17 @@ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -6679,6 +7030,12 @@ "integrity": "sha512-Uk7C+7aPBryUR1Fwvk9VmipBcN9fVsqBO57jV2ZjTm+IZ6BMNqu7EDVEg2HxCNufk6QcWlFsBkhQyQroB2VWKw==", "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", @@ -7092,6 +7449,16 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -7110,6 +7477,12 @@ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -7172,6 +7545,12 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -7241,6 +7620,12 @@ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -7283,6 +7668,50 @@ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, "jasmine": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.5.0.tgz", @@ -7778,6 +8207,12 @@ } } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -7898,6 +8333,12 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7914,6 +8355,17 @@ "source-map": "^0.6.0" } }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -8014,6 +8466,17 @@ } } }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -8103,6 +8566,17 @@ "punycode": "^2.1.0" } }, + "v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + } + }, "watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", @@ -8229,18 +8703,82 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/runtime/JavaScript/package.json b/runtime/JavaScript/package.json index b9e6e6d0a5..70b426f652 100644 --- a/runtime/JavaScript/package.json +++ b/runtime/JavaScript/package.json @@ -25,6 +25,7 @@ "@babel/preset-env": "^7.19.4", "@types/node": "^18.7.23", "babel-loader": "^8.2.5", + "c8": "^7.12.0", "compression-webpack-plugin": "^10.0.0", "eslint": "^8.23.1", "eslint-webpack-plugin": "^3.2.0", @@ -40,7 +41,9 @@ }, "scripts": { "build": "webpack", - "test": "jasmine" + "test": "jasmine", + "coverage": "c8 jasmine", + "lint": "eslint src/antlr4/" }, "engines": { "node": ">=16" diff --git a/runtime/JavaScript/spec/IntervalSetSpec.js b/runtime/JavaScript/spec/IntervalSetSpec.js index 78baa6f77e..b40511e543 100644 --- a/runtime/JavaScript/spec/IntervalSetSpec.js +++ b/runtime/JavaScript/spec/IntervalSetSpec.js @@ -1,4 +1,4 @@ -import antlr4 from "../src/antlr4/index.js"; +import antlr4 from "../src/antlr4/index.node.js"; const IntervalSet = antlr4.IntervalSet; describe('IntervalSet', () => { diff --git a/runtime/JavaScript/spec/rewriter/Makefile b/runtime/JavaScript/spec/rewriter/Makefile new file mode 100644 index 0000000000..79e7cd5db9 --- /dev/null +++ b/runtime/JavaScript/spec/rewriter/Makefile @@ -0,0 +1,16 @@ +ANTLR_VERSION = 4.12.0 +ANTLR_JAR = .antlr/antlr-$(ANTLR_VERSION)-complete.jar + +.antlr: + mkdir .antlr + +$(ANTLR_JAR): .antlr + curl https://www.antlr.org/download/antlr-$(ANTLR_VERSION)-complete.jar -o $(ANTLR_JAR) + +abc: abc.g4 $(ANTLR_JAR) + java -jar $(ANTLR_JAR) -Dlanguage=JavaScript -no-listener abc.g4 -o generatedCode/ + sed -i.bak "s/import antlr4 from 'antlr4'/import antlr4 from '..\/..\/..\/src\/antlr4\/index.node.js'/" generatedCode/abc.js + +calc: calc.g4 $(ANTLR_JAR) + java -jar $(ANTLR_JAR) -Dlanguage=JavaScript -no-listener calc.g4 -o generatedCode/ + sed -i.bak "s/import antlr4 from 'antlr4'/import antlr4 from '..\/..\/..\/src\/antlr4\/index.node.js'/" generatedCode/calc.js diff --git a/runtime/JavaScript/spec/rewriter/TokenStreamRewriterSpec.js b/runtime/JavaScript/spec/rewriter/TokenStreamRewriterSpec.js new file mode 100644 index 0000000000..a58bc26ef0 --- /dev/null +++ b/runtime/JavaScript/spec/rewriter/TokenStreamRewriterSpec.js @@ -0,0 +1,715 @@ +import antlr4 from "../../src/antlr4/index.node.js"; +import abc from "./generatedCode/abc.js"; +import calc from "./generatedCode/calc.js"; + +/** + * + * @param {antlr4.Lexer} lexerClass + * @param {string} input + */ +function getRewriter(lexerClass, input) { + const chars = new antlr4.InputStream(input); + const lexer = new lexerClass(chars); + const tokens = new antlr4.CommonTokenStream(lexer); + tokens.fill(); + return new antlr4.TokenStreamRewriter(tokens); +} + +describe("TokenStreamRewriter", () => { + it("inserts '0' before index 0", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(0, "0"); + + // Assert + expect(rewriter.getText()).toEqual("0abc"); + }); + + it("inserts 'x' after last index", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertAfter(2, "x"); + + // Assert + expect(rewriter.getText()).toEqual("abcx"); + }); + + it("inserts 'x' after the 'b' token", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + const bToken = rewriter.tokens.get(1); + + // Act + rewriter.insertAfter(bToken, "x"); + + // Assert + expect(rewriter.getText()).toEqual("abxc"); + }); + + it("inserts 'x' at the end if the index is out of bounds", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertAfter(100, "x"); + + // Assert + expect(rewriter.getText()).toEqual("abcx"); + }); + + it("inserts 'x' before the 'b' token", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + const bToken = rewriter.tokens.get(1); + + // Act + rewriter.insertBefore(bToken, "x"); + + // Assert + expect(rewriter.getText()).toEqual("axbc"); + }); + + it("inserts 'x' before and after middle index", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(1, "x"); + rewriter.insertAfter(1, "x"); + + // Assert + expect(rewriter.getText()).toEqual("axbxc"); + }); + + it("replaces the first token with an 'x'", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.replaceSingle(0, "x"); + + // Assert + expect(rewriter.getText()).toEqual("xbc"); + }); + + it("replaces the last token with an 'x'", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.replaceSingle(2, "x"); + + // Assert + expect(rewriter.getText()).toEqual("abx"); + }); + + it("replaces the middle token with an 'x'", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.replaceSingle(1, "x"); + + // Assert + expect(rewriter.getText()).toEqual("axc"); + }); + + it("calls getText() with different start/stop arguments (1 of 2)", () => { + // Arrange + const rewriter = getRewriter(calc, "x = 3 * 0;"); + + // Act + rewriter.replace(4, 8, "0"); // replace 3 * 0 with 0 + + // Assert + expect(rewriter.getTokenStream().getText()).toEqual("x = 3 * 0;"); + expect(rewriter.getText()).toEqual("x = 0;"); + expect(rewriter.getText(new antlr4.Interval(0, 9))).toEqual("x = 0;"); + expect(rewriter.getText(new antlr4.Interval(4, 8))).toEqual("0"); + }); + + it("calls getText() with different start/stop arguments (2 of 2)", () => { + // Arrange + const rewriter = getRewriter(calc, "x = 3 * 0 + 2 * 0;"); + + // Act/Assert + expect(rewriter.getTokenStream().getText()).toEqual("x = 3 * 0 + 2 * 0;"); + + rewriter.replace(4, 8, "0"); // replace 3 * 0 with 0 + + expect(rewriter.getText()).toEqual("x = 0 + 2 * 0;"); + expect(rewriter.getText(new antlr4.Interval(0, 17))).toEqual("x = 0 + 2 * 0;"); + expect(rewriter.getText(new antlr4.Interval(4, 8))).toEqual("0"); + expect(rewriter.getText(new antlr4.Interval(0, 8))).toEqual("x = 0"); + expect(rewriter.getText(new antlr4.Interval(12, 16))).toEqual("2 * 0"); + + rewriter.insertAfter(17, "// comment"); + + expect(rewriter.getText(new antlr4.Interval(12, 18))).toEqual("2 * 0;// comment"); + expect(rewriter.getText(new antlr4.Interval(0, 8))).toEqual("x = 0"); + }); + + it("replaces the middle index, twice", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.replaceSingle(1, "x"); + rewriter.replaceSingle(1, "y"); + + // Assert + expect(rewriter.getText()).toEqual("ayc"); + }); + + it("inserts '_' at the beginning and then replaces the middle token, twice", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(0, "_"); + rewriter.replaceSingle(1, "x"); + rewriter.replaceSingle(1, "y"); + + // Assert + expect(rewriter.getText()).toEqual("_ayc"); + }); + + it("replaces, then deletes the middle index", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.replaceSingle(1, "x"); + rewriter.delete(1); + + // Assert + expect(rewriter.getText()).toEqual("ac"); + }); + + it("throws an error when inserting into a replaced segment", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.replace(0, 2, "x"); + rewriter.insertBefore(1, "0"); + + // Assert + expect(() => rewriter.getText()).toThrowError( + "insert op ,1:1]:\"0\"> within boundaries of previous ,1:0]..[@2,2:2='c',<3>,1:2]:\"x\">" + ); + }); + + it("throws an error when inserting into a deleted segment", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.delete(0, 2); + rewriter.insertBefore(1, "0"); + + // Assert + expect(() => rewriter.getText()).toThrowError( + "insert op ,1:1]:\"0\"> within boundaries of previous ,1:0]..[@2,2:2='c',<3>,1:2]>" + ); + }); + + it("inserts '0' before the first token and then replaces it with an 'x'", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(0, "0"); + rewriter.replaceSingle(0, "x"); + + // Assert + expect(rewriter.getText()).toEqual("0xbc"); + }); + + it("inserts texts in reverse order when multiple inserts occur at the same index", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(1, "x"); + rewriter.insertBefore(1, "y"); + + // Assert + expect(rewriter.getText()).toEqual("ayxbc"); + }); + + it("inserts 'y' and 'x' before the first index and then replaces it with 'z'", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(0, "x"); + rewriter.insertBefore(0, "y"); + rewriter.replaceSingle(0, "z"); + + // Assert + expect(rewriter.getText()).toEqual("yxzbc"); + }); + + it("replaces the last index with an 'x' and then inserts 'y' before it", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.replaceSingle(2, "x"); + rewriter.insertBefore(2, "y"); + + // Assert + expect(rewriter.getText()).toEqual("abyx"); + }); + + it("replaces thte last index with an 'x' and then inserts 'y' after it", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.replaceSingle(2, "x"); + rewriter.insertAfter(2, "y"); + + // Assert + expect(rewriter.getText()).toEqual("abxy"); + }); + + it("replaces a range with an 'x' and then inserts 'y' before the left edge of the range", () => { + // Arrange + const rewriter = getRewriter(abc, "abcccba"); + + // Act + rewriter.replace(2, 4, "x"); + rewriter.insertBefore(2, "y"); + + // Assert + expect(rewriter.getText()).toEqual("abyxba"); + }); + + it("throws an error if an attempt is made to insert a token before the right edge of a replaced range", () => { + // Arrange + const rewriter = getRewriter(abc, "abcccba"); + + // Act + rewriter.replace(2, 4, "x"); + rewriter.insertBefore(4, "y"); + + // Assert + expect(() => rewriter.getText()).toThrowError( + "insert op ,1:4]:\"y\"> within boundaries of previous ,1:2]..[@4,4:4='c',<3>,1:4]:\"x\">" + ); + }); + + it("replaces a range with an 'x' then inserts 'y' after the right edge of the range", () => { + // Arrange + const rewriter = getRewriter(abc, "abcccba"); + + // Act + rewriter.replace(2, 4, "x"); + rewriter.insertAfter(4, "y"); + + // Assert + expect(rewriter.getText()).toEqual("abxyba"); + }); + + it("replaces a token range", () => { + // Arrange + const rewriter = getRewriter(abc, "abcba"); + const bToken = rewriter.tokens.get(1); + const dToken = rewriter.tokens.get(3); + + // Act + rewriter.replace(bToken, dToken, "x"); + + // Assert + expect(rewriter.getText()).toEqual("axa"); + }); + + it("throws an error when replace is given an invalid range", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + const badRanges = [ + [1, 0], // from > to + [-1, 1], // from is negative + [1, -1], // to is negative + [-2, -1], // both are negative + [1, 4] // to is out of bounds + ]; + + // Act/Assert + for (const [from, to] of badRanges) { + expect(() => rewriter.replace(from, to, "x")).toThrow(); + } + }); + + it("replaces all tokens with an 'x'", () => { + // Arrange + const rewriter = getRewriter(abc, "abcccba"); + + // Act + rewriter.replace(0, 6, "x"); + + // Assert + expect(rewriter.getText()).toEqual("x"); + }); + + it("replaces the middle 'ccc' with 'xyz'", () => { + // Arrange + const rewriter = getRewriter(abc, "abcccba"); + + // Act + rewriter.replace(2, 4, "xyz"); + + // Assert + expect(rewriter.getText(new antlr4.Interval(0, 6))).toEqual("abxyzba"); + }); + + it("throws an error if second replace operation overlaps the first one on the right", () => { + // Arrange + const rewriter = getRewriter(abc, "abcccba"); + + // Act + rewriter.replace(2, 4, "xyz"); + rewriter.replace(3, 5, "foo"); + + // Assert + expect(() => rewriter.getText()).toThrowError( + "replace op boundaries of ,1:3]..[@5,5:5='b',<2>,1:5]:\"foo\"> overlap with previous ,1:2]..[@4,4:4='c',<3>,1:4]:\"xyz\">" + ); + }); + + it("throws an error if second replace operation overlaps the first one on the left", () => { + // Arrange + const chars = new antlr4.InputStream("abcccba"); + const lexer = new abc(chars); + const tokens = new antlr4.CommonTokenStream(lexer); + tokens.fill(); + const rewriter = new antlr4.TokenStreamRewriter(tokens); + + // Act + rewriter.replace(2, 4, "xyz"); + rewriter.replace(1, 3, "foo"); + + // Assert + expect(() => rewriter.getText()).toThrowError( + "replace op boundaries of ,1:1]..[@3,3:3='c',<3>,1:3]:\"foo\"> overlap with previous ,1:2]..[@4,4:4='c',<3>,1:4]:\"xyz\">" + ); + }); + + it("ignores first replace operation when the second one overlaps it on both sides (superset)", () => { + // Arrange + const rewriter = getRewriter(abc, "abcba"); + + // Act + rewriter.replace(2, 2, "xyz"); + rewriter.replace(0, 3, "foo"); + + // Assert + expect(rewriter.getText()).toEqual("fooa"); + }); + + it("inserts 'x' and 'y' before the first token", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(0, "x"); + rewriter.insertBefore(0, "y"); + + // Assert + expect(rewriter.getText()).toEqual("yxabc"); + }); + + it("performs 3 inserts at 2 locations", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(1, "x"); + rewriter.insertBefore(0, "y"); + rewriter.insertBefore(1, "z"); + + // Assert + expect(rewriter.getText()).toEqual("yazxbc"); + }); + + it("replaces 'abc' with 'foo' and then inserts 'z' before it", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.replace(0, 2, "foo"); + rewriter.insertBefore(0, "z"); + + // Assert + expect(rewriter.getText()).toEqual("zfoo"); + }); + + it("deletes 'abc' and then inserts 'z' before it", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.delete(0, 2); + rewriter.insertBefore(0, "z"); + + // Assert + expect(rewriter.getText()).toEqual("z"); + }); + + + it("makes 3 inserts at 3 locations", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(1, "x"); + rewriter.insertBefore(2, "y"); + rewriter.insertBefore(0, "z"); + + // Assert + expect(rewriter.getText()).toEqual("zaxbyc"); + }); + + it("throws an error if second replace operation affects a subset of a previous one", () => { + // Arrange + const rewriter = getRewriter(abc, "abcc"); + + // Act + rewriter.replace(0, 3, "bar"); + rewriter.replace(1, 2, "foo"); + + // Assert + expect(() => rewriter.getText()).toThrowError( + "replace op boundaries of ,1:1]..[@2,2:2='c',<3>,1:2]:\"foo\"> overlap with previous ,1:0]..[@3,3:3='c',<3>,1:3]:\"bar\">" + ); + }); + + it("ignores the first replace operation when the secone one extends it to the left", () => { + // Arrange + const rewriter = getRewriter(abc, "abcc"); + + // Act + rewriter.replace(1, 2, "foo"); + rewriter.replace(0, 2, "bar"); + + // Assert + expect(rewriter.getText()).toEqual("barc"); + }); + + it("ignores the first replace operation when the secone one extends it to the right", () => { + // Arrange + const rewriter = getRewriter(abc, "abcc"); + + // Act + rewriter.replace(1, 2, "foo"); + rewriter.replace(1, 3, "bar"); + + // Assert + expect(rewriter.getText()).toEqual("abar"); + }); + + it("only applies one replace operation when identical ones are given", () => { + // Arrange + const rewriter = getRewriter(abc, "abcc"); + + // Act + rewriter.replace(1, 2, "foo"); + rewriter.replace(1, 2, "foo"); + + // Assert + expect(rewriter.getText()).toEqual("afooc"); + }); + + it("drops the insert operation when it is covered by a subsequent replace operation", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(2, "foo"); + rewriter.replace(1, 2, "foo"); + + // Assert + expect(rewriter.getText()).toEqual("afoo"); + }); + + it("performs the insert operation when disjoint from the replace operation (1 of 2)", () => { + // Arrange + const rewriter = getRewriter(abc, "abcc"); + + // Act + rewriter.insertBefore(1, "x"); + rewriter.replace(2, 3, "foo"); + + // Assert + expect(rewriter.getText()).toEqual("axbfoo"); + }); + + it("performs the insert operation when disjoint from the replace operation (2 of 2)", () => { + // Arrange + const rewriter = getRewriter(abc, "abcc"); + + // Act + rewriter.replace(2, 3, "foo"); + rewriter.insertBefore(1, "x"); + + // Assert + expect(rewriter.getText()).toEqual("axbfoo"); + }); + + it("inserts 'y' before the last token, then deletes it", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(2, "y"); + rewriter.delete(2); + + // Assert + expect(rewriter.getText()).toEqual("aby"); + }); + + it("deletes the 'a' token", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + const aToken = rewriter.tokens.get(0); + + // Act + rewriter.delete(aToken); + + // Assert + expect(rewriter.getText()).toEqual("bc"); + }); + + // Test for https://github.com/antlr/antlr4/issues/550 + it("distinguishes between insertAfter and insertBefore to preserve order (1 of 2)", () => { + // Arrange + const rewriter = getRewriter(abc, "aa"); + + // Act + rewriter.insertBefore(0, ""); + rewriter.insertAfter(0, ""); + rewriter.insertBefore(1, ""); + rewriter.insertAfter(1, ""); + + // Assert + expect(rewriter.getText()).toEqual("aa"); + }); + + it("distinguishes between insertAfter and insertBefore to preserve order (2 of 2)", () => { + // Arrange + const rewriter = getRewriter(abc, "aa"); + + // Act + rewriter.insertBefore(0, "

"); + rewriter.insertBefore(0, ""); + rewriter.insertAfter(0, "

"); + rewriter.insertAfter(0, ""); + rewriter.insertBefore(1, ""); + rewriter.insertAfter(1, ""); + + // Assert + expect(rewriter.getText()).toEqual("

a

a"); + }); + + it("preserves the order of contiguous inserts", () => { + // Arrange + const rewriter = getRewriter(abc, "ab"); + + // Act + rewriter.insertBefore(0, "

"); + rewriter.insertBefore(0, ""); + rewriter.insertBefore(0, "

"); + rewriter.insertAfter(0, "

"); + rewriter.insertAfter(0, "
"); + rewriter.insertAfter(0, "
"); + rewriter.insertBefore(1, "!"); + + // Assert + expect(rewriter.getText()).toEqual("

a

!b"); + }); + + it("accepts different types as text", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(0, false); + rewriter.insertBefore(0, 0); + rewriter.insertBefore(0, {}); + rewriter.insertBefore(0, []); + rewriter.insertBefore(0, ""); + rewriter.insertBefore(0, null); + + // Assert + expect(rewriter.getText()).toEqual("[object Object]0falseabc"); + }); + + it("returns the original input if no rewrites have occurred", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + const result = rewriter.getText(); + + // Assert + expect(result).toEqual("abc"); + }); + + it("segments operations by program name", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + rewriter.insertBefore(0, "b", "P1"); + rewriter.insertAfter(0, "c", "P2"); + rewriter.replaceSingle(2, "b", "P2"); + + // Assert + expect(rewriter.getText("P1")).toEqual("babc"); + expect(rewriter.getText("P2")).toEqual("acbb"); + }); + + it("doesn't make a fuss if getText is supplied with an interval that exceeds the token range", () => { + // Arrange + const rewriter = getRewriter(abc, "abc"); + + // Act + const unmodified = rewriter.getText(new antlr4.Interval(-1, 3)); + rewriter.insertAfter(2, "a"); + const modified = rewriter.getText(new antlr4.Interval(0, 200)); + + // Assert + expect(unmodified).toEqual("abc"); + expect(modified).toEqual("abca"); + }); + + it("ignores inserts that occur within a removed range", () => { + // Arrange + const rewriter = getRewriter(abc, "abcba"); + + // Act + rewriter.insertAfter(2, "c"); + rewriter.delete(2, 3); + + // Assert + expect(rewriter.getText()).toEqual("aba"); + }); + + it("handles overlapping delete ranges", () => { + // Arrange + const rewriter = getRewriter(abc, "abcba"); + + // Act + rewriter.delete(1, 3); + rewriter.delete(2, 4); + + // Assert + expect(rewriter.getText()).toEqual("a"); + }); +}); diff --git a/runtime/JavaScript/spec/rewriter/abc.g4 b/runtime/JavaScript/spec/rewriter/abc.g4 new file mode 100644 index 0000000000..c7dc9d1fcf --- /dev/null +++ b/runtime/JavaScript/spec/rewriter/abc.g4 @@ -0,0 +1,4 @@ +lexer grammar abc; +A: 'a'; +B: 'b'; +C: 'c'; \ No newline at end of file diff --git a/runtime/JavaScript/spec/rewriter/calc.g4 b/runtime/JavaScript/spec/rewriter/calc.g4 new file mode 100644 index 0000000000..d651c022fe --- /dev/null +++ b/runtime/JavaScript/spec/rewriter/calc.g4 @@ -0,0 +1,8 @@ +lexer grammar calc; +ID: 'a' ..'z'+; +INT: '0' ..'9'+; +SEMI: ';'; +PLUS: '+'; +MUL: '*'; +ASSIGN: '='; +WS: ' '+; diff --git a/runtime/JavaScript/spec/rewriter/generatedCode/abc.js b/runtime/JavaScript/spec/rewriter/generatedCode/abc.js new file mode 100644 index 0000000000..e8714aa4c7 --- /dev/null +++ b/runtime/JavaScript/spec/rewriter/generatedCode/abc.js @@ -0,0 +1,37 @@ +// Generated from abc.g4 by ANTLR 4.12.0 +// jshint ignore: start +import antlr4 from '../../../src/antlr4/index.node.js'; + + +const serializedATN = [4,0,3,13,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,1,0,1,0,1,1, +1,1,1,2,1,2,0,0,3,1,1,3,2,5,3,1,0,0,12,0,1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0, +0,1,7,1,0,0,0,3,9,1,0,0,0,5,11,1,0,0,0,7,8,5,97,0,0,8,2,1,0,0,0,9,10,5,98, +0,0,10,4,1,0,0,0,11,12,5,99,0,0,12,6,1,0,0,0,1,0,0]; + + +const atn = new antlr4.atn.ATNDeserializer().deserialize(serializedATN); + +const decisionsToDFA = atn.decisionToState.map( (ds, index) => new antlr4.dfa.DFA(ds, index) ); + +export default class abc extends antlr4.Lexer { + + static grammarFileName = "abc.g4"; + static channelNames = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" ]; + static modeNames = [ "DEFAULT_MODE" ]; + static literalNames = [ null, "'a'", "'b'", "'c'" ]; + static symbolicNames = [ null, "A", "B", "C" ]; + static ruleNames = [ "A", "B", "C" ]; + + constructor(input) { + super(input) + this._interp = new antlr4.atn.LexerATNSimulator(this, atn, decisionsToDFA, new antlr4.atn.PredictionContextCache()); + } +} + +abc.EOF = antlr4.Token.EOF; +abc.A = 1; +abc.B = 2; +abc.C = 3; + + + diff --git a/runtime/JavaScript/spec/rewriter/generatedCode/calc.js b/runtime/JavaScript/spec/rewriter/generatedCode/calc.js new file mode 100644 index 0000000000..1bbdd05060 --- /dev/null +++ b/runtime/JavaScript/spec/rewriter/generatedCode/calc.js @@ -0,0 +1,50 @@ +// Generated from calc.g4 by ANTLR 4.12.0 +// jshint ignore: start +import antlr4 from '../../../src/antlr4/index.node.js'; + + +const serializedATN = [4,0,7,38,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4, +7,4,2,5,7,5,2,6,7,6,1,0,4,0,17,8,0,11,0,12,0,18,1,1,4,1,22,8,1,11,1,12,1, +23,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,6,4,6,35,8,6,11,6,12,6,36,0,0,7,1,1, +3,2,5,3,7,4,9,5,11,6,13,7,1,0,0,40,0,1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0, +7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,1,16,1,0,0,0,3,21,1,0,0, +0,5,25,1,0,0,0,7,27,1,0,0,0,9,29,1,0,0,0,11,31,1,0,0,0,13,34,1,0,0,0,15, +17,2,97,122,0,16,15,1,0,0,0,17,18,1,0,0,0,18,16,1,0,0,0,18,19,1,0,0,0,19, +2,1,0,0,0,20,22,2,48,57,0,21,20,1,0,0,0,22,23,1,0,0,0,23,21,1,0,0,0,23,24, +1,0,0,0,24,4,1,0,0,0,25,26,5,59,0,0,26,6,1,0,0,0,27,28,5,43,0,0,28,8,1,0, +0,0,29,30,5,42,0,0,30,10,1,0,0,0,31,32,5,61,0,0,32,12,1,0,0,0,33,35,5,32, +0,0,34,33,1,0,0,0,35,36,1,0,0,0,36,34,1,0,0,0,36,37,1,0,0,0,37,14,1,0,0, +0,4,0,18,23,36,0]; + + +const atn = new antlr4.atn.ATNDeserializer().deserialize(serializedATN); + +const decisionsToDFA = atn.decisionToState.map( (ds, index) => new antlr4.dfa.DFA(ds, index) ); + +export default class calc extends antlr4.Lexer { + + static grammarFileName = "calc.g4"; + static channelNames = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" ]; + static modeNames = [ "DEFAULT_MODE" ]; + static literalNames = [ null, null, null, "';'", "'+'", "'*'", "'='" ]; + static symbolicNames = [ null, "ID", "INT", "SEMI", "PLUS", "MUL", "ASSIGN", + "WS" ]; + static ruleNames = [ "ID", "INT", "SEMI", "PLUS", "MUL", "ASSIGN", "WS" ]; + + constructor(input) { + super(input) + this._interp = new antlr4.atn.LexerATNSimulator(this, atn, decisionsToDFA, new antlr4.atn.PredictionContextCache()); + } +} + +calc.EOF = antlr4.Token.EOF; +calc.ID = 1; +calc.INT = 2; +calc.SEMI = 3; +calc.PLUS = 4; +calc.MUL = 5; +calc.ASSIGN = 6; +calc.WS = 7; + + + diff --git a/runtime/JavaScript/src/antlr4/BufferedTokenStream.js b/runtime/JavaScript/src/antlr4/BufferedTokenStream.js index 08a093c454..a0d8c2be8a 100644 --- a/runtime/JavaScript/src/antlr4/BufferedTokenStream.js +++ b/runtime/JavaScript/src/antlr4/BufferedTokenStream.js @@ -83,6 +83,10 @@ export default class BufferedTokenStream extends TokenStream { this.index = this.adjustSeekIndex(index); } + get size() { + return this.tokens.length; + } + get(index) { this.lazyInit(); return this.tokens[index]; @@ -148,7 +152,7 @@ export default class BufferedTokenStream extends TokenStream { return n; } -// Get all tokens from start..stop inclusively/// + // Get all tokens from start..stop inclusively/// getTokens(start, stop, types) { if (types === undefined) { types = null; @@ -230,7 +234,7 @@ export default class BufferedTokenStream extends TokenStream { this.index = this.adjustSeekIndex(0); } -// Reset this token stream by setting its token source./// + // Reset this token stream by setting its token source./// setTokenSource(tokenSource) { this.tokenSource = tokenSource; this.tokens = []; @@ -278,7 +282,7 @@ export default class BufferedTokenStream extends TokenStream { * EOF. If channel is -1, find any non default channel token. */ getHiddenTokensToRight(tokenIndex, - channel) { + channel) { if (channel === undefined) { channel = -1; } @@ -299,7 +303,7 @@ export default class BufferedTokenStream extends TokenStream { * If channel is -1, find any non default channel token. */ getHiddenTokensToLeft(tokenIndex, - channel) { + channel) { if (channel === undefined) { channel = -1; } diff --git a/runtime/JavaScript/src/antlr4/TokenStreamRewriter.d.ts b/runtime/JavaScript/src/antlr4/TokenStreamRewriter.d.ts new file mode 100644 index 0000000000..180365bba3 --- /dev/null +++ b/runtime/JavaScript/src/antlr4/TokenStreamRewriter.d.ts @@ -0,0 +1,38 @@ +import { CommonTokenStream } from "./CommonTokenStream"; +import { Token } from "./Token"; +import { Interval } from "./misc/Interval"; + +type Rewrites = Array; +type Text = unknown; + +export declare class TokenStreamRewriter { + static DEFAULT_PROGRAM_NAME: string; + constructor(tokens: CommonTokenStream); + getTokenStream(): CommonTokenStream; + insertAfter(token: Token, text: Text, programName?: string): void; + insertAfter(index: number, text: Text, programName?: string): void; + insertBefore(token: Token, text: Text, programName?: string): void; + insertBefore(index: number, text: Text, programName?: string): void; + replaceSingle(token: Token, text: Text, programName?: string): void; + replaceSingle(index: number, text: Text, programName?: string): void; + replace(from: Token | number, to: Token | number, text: Text, programName?: string): void; + delete(from: number | Token, to: number | Token, programName?: string): void; + getProgram(name: string): Rewrites; + initializeProgram(name: string): Rewrites; + getText(): string; + getText(program: string): string; + getText(interval: Interval, programName?: string): string; + reduceToSingleOperationPerIndex(rewrites: Rewrites): Map; + catOpText(a: Text, b: Text): string; + getKindOfOps(rewrites: Rewrites, kind: any, before: number): RewriteOperation[]; +} + + +declare class RewriteOperation { + constructor(tokens: CommonTokenStream, index: number, instructionIndex: number, text: Text); + tokens: CommonTokenStream; + instructionIndex: number; + index: number; + text: Text; + toString(): string; +} diff --git a/runtime/JavaScript/src/antlr4/TokenStreamRewriter.js b/runtime/JavaScript/src/antlr4/TokenStreamRewriter.js new file mode 100644 index 0000000000..e45125866d --- /dev/null +++ b/runtime/JavaScript/src/antlr4/TokenStreamRewriter.js @@ -0,0 +1,442 @@ +import Token from "./Token.js"; +import Interval from "./misc/Interval.js"; + +/** + * @typedef {import("./CommonTokenStream").default} CommonTokenStream + * @typedef {Array} Rewrites + * @typedef {unknown} Text + */ + +export default class TokenStreamRewriter { + // eslint-disable-next-line no-undef + static DEFAULT_PROGRAM_NAME = "default"; + + /** + * @param {CommonTokenStream} tokens The token stream to modify + */ + constructor(tokens) { + this.tokens = tokens; + /** @type {Map} */ + this.programs = new Map(); + } + + /** + * @returns {CommonTokenStream} + */ + getTokenStream() { + return this.tokens; + } + + /** + * Insert the supplied text after the specified token (or token index) + * @param {Token | number} tokenOrIndex + * @param {Text} text + * @param {string} [programName] + */ + insertAfter(tokenOrIndex, text, programName = TokenStreamRewriter.DEFAULT_PROGRAM_NAME) { + /** @type {number} */ + let index; + if (typeof tokenOrIndex === "number") { + index = tokenOrIndex; + } else { + index = tokenOrIndex.tokenIndex; + } + + // to insert after, just insert before next index (even if past end) + let rewrites = this.getProgram(programName); + let op = new InsertAfterOp(this.tokens, index, rewrites.length, text); + rewrites.push(op); + } + + /** + * Insert the supplied text before the specified token (or token index) + * @param {Token | number} tokenOrIndex + * @param {Text} text + * @param {string} [programName] + */ + insertBefore(tokenOrIndex, text, programName = TokenStreamRewriter.DEFAULT_PROGRAM_NAME) { + /** @type {number} */ + let index; + if (typeof tokenOrIndex === "number") { + index = tokenOrIndex; + } else { + index = tokenOrIndex.tokenIndex; + } + + const rewrites = this.getProgram(programName); + const op = new InsertBeforeOp(this.tokens, index, rewrites.length, text); + rewrites.push(op); + } + + /** + * Replace the specified token with the supplied text + * @param {Token | number} tokenOrIndex + * @param {Text} text + * @param {string} [programName] + */ + replaceSingle(tokenOrIndex, text, programName = TokenStreamRewriter.DEFAULT_PROGRAM_NAME) { + this.replace(tokenOrIndex, tokenOrIndex, text, programName); + } + + /** + * Replace the specified range of tokens with the supplied text + * @param {Token | number} from + * @param {Token | number} to + * @param {Text} text + * @param {string} [programName] + */ + replace(from, to, text, programName = TokenStreamRewriter.DEFAULT_PROGRAM_NAME) { + if (typeof from !== "number") { + from = from.tokenIndex; + } + if (typeof to !== "number") { + to = to.tokenIndex; + } + if (from > to || from < 0 || to < 0 || to >= this.tokens.size) { + throw new RangeError(`replace: range invalid: ${from}..${to}(size=${this.tokens.size})`); + } + let rewrites = this.getProgram(programName); + let op = new ReplaceOp(this.tokens, from, to, rewrites.length, text); + rewrites.push(op); + } + + /** + * Delete the specified range of tokens + * @param {number | Token} from + * @param {number | Token} to + * @param {string} [programName] + */ + delete(from, to, programName = TokenStreamRewriter.DEFAULT_PROGRAM_NAME) { + if (typeof to === "undefined") { + to = from; + } + this.replace(from, to, null, programName); + } + + /** + * @param {string} name + * @returns {Rewrites} + */ + getProgram(name) { + let is = this.programs.get(name); + if (is == null) { + is = this.initializeProgram(name); + } + return is; + } + + /** + * @param {string} name + * @returns {Rewrites} + */ + initializeProgram(name) { + const is = []; + this.programs.set(name, is); + return is; + } + + /** + * Return the text from the original tokens altered per the instructions given to this rewriter + * @param {Interval | string} [intervalOrProgram] + * @param {string} [programName] + * @returns {string} + */ + getText(intervalOrProgram, programName = TokenStreamRewriter.DEFAULT_PROGRAM_NAME) { + let interval; + if (intervalOrProgram instanceof Interval) { + interval = intervalOrProgram; + } else { + interval = new Interval(0, this.tokens.size - 1); + } + + if (typeof intervalOrProgram === "string") { + programName = intervalOrProgram; + } + + const rewrites = this.programs.get(programName); + let start = interval.start; + let stop = interval.stop; + + // ensure start/end are in range + if (stop > this.tokens.size - 1) { + stop = this.tokens.size - 1; + } + if (start < 0) { + start = 0; + } + + if (rewrites == null || rewrites.length === 0) { + return this.tokens.getText(new Interval(start, stop)); // no instructions to execute + } + + let buf = []; + + // First, optimize instruction stream + let indexToOp = this.reduceToSingleOperationPerIndex(rewrites); + + // Walk buffer, executing instructions and emitting tokens + let i = start; + while (i <= stop && i < this.tokens.size) { + let op = indexToOp.get(i); + indexToOp.delete(i); // remove so any left have index size-1 + let t = this.tokens.get(i); + if (op == null) { + // no operation at that index, just dump token + if (t.type !== Token.EOF) { + buf.push(String(t.text)); + } + i++; // move to next token + } + else { + i = op.execute(buf); // execute operation and skip + } + } + + // include stuff after end if it's last index in buffer + // So, if they did an insertAfter(lastValidIndex, "foo"), include + // foo if end==lastValidIndex. + if (stop === this.tokens.size - 1) { + // Scan any remaining operations after last token + // should be included (they will be inserts). + for (const op of indexToOp.values()) { + if (op.index >= this.tokens.size - 1) { + buf.push(op.text.toString()); + } + } + } + + return buf.join(""); + } + + /** + * @param {Rewrites} rewrites + * @returns {Map} a map from token index to operation + */ + reduceToSingleOperationPerIndex(rewrites) { + // WALK REPLACES + for (let i = 0; i < rewrites.length; i++) { + let op = rewrites[i]; + if (op == null) { + continue; + } + if (!(op instanceof ReplaceOp)) { + continue; + } + let rop = op; + // Wipe prior inserts within range + let inserts = this.getKindOfOps(rewrites, InsertBeforeOp, i); + for (let iop of inserts) { + if (iop.index === rop.index) { + // E.g., insert before 2, delete 2..2; update replace + // text to include insert before, kill insert + rewrites[iop.instructionIndex] = undefined; + rop.text = iop.text.toString() + (rop.text != null ? rop.text.toString() : ""); + } + else if (iop.index > rop.index && iop.index <= rop.lastIndex) { + // delete insert as it's a no-op. + rewrites[iop.instructionIndex] = undefined; + } + } + // Drop any prior replaces contained within + let prevReplaces = this.getKindOfOps(rewrites, ReplaceOp, i); + for (let prevRop of prevReplaces) { + if (prevRop.index >= rop.index && prevRop.lastIndex <= rop.lastIndex) { + // delete replace as it's a no-op. + rewrites[prevRop.instructionIndex] = undefined; + continue; + } + // throw exception unless disjoint or identical + let disjoint = + prevRop.lastIndex < rop.index || prevRop.index > rop.lastIndex; + // Delete special case of replace (text==null): + // D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right) + if (prevRop.text == null && rop.text == null && !disjoint) { + rewrites[prevRop.instructionIndex] = undefined; // kill first delete + rop.index = Math.min(prevRop.index, rop.index); + rop.lastIndex = Math.max(prevRop.lastIndex, rop.lastIndex); + } + else if (!disjoint) { + throw new Error(`replace op boundaries of ${rop} overlap with previous ${prevRop}`); + } + } + } + + // WALK INSERTS + for (let i = 0; i < rewrites.length; i++) { + let op = rewrites[i]; + if (op == null) { + continue; + } + if (!(op instanceof InsertBeforeOp)) { + continue; + } + let iop = op; + // combine current insert with prior if any at same index + let prevInserts = this.getKindOfOps(rewrites, InsertBeforeOp, i); + for (let prevIop of prevInserts) { + if (prevIop.index === iop.index) { + if (prevIop instanceof InsertAfterOp) { + iop.text = this.catOpText(prevIop.text, iop.text); + rewrites[prevIop.instructionIndex] = undefined; + } + else if (prevIop instanceof InsertBeforeOp) { // combine objects + // convert to strings...we're in process of toString'ing + // whole token buffer so no lazy eval issue with any templates + iop.text = this.catOpText(iop.text, prevIop.text); + // delete redundant prior insert + rewrites[prevIop.instructionIndex] = undefined; + } + } + } + // look for replaces where iop.index is in range; error + let prevReplaces = this.getKindOfOps(rewrites, ReplaceOp, i); + for (let rop of prevReplaces) { + if (iop.index === rop.index) { + rop.text = this.catOpText(iop.text, rop.text); + rewrites[i] = undefined; // delete current insert + continue; + } + if (iop.index >= rop.index && iop.index <= rop.lastIndex) { + throw new Error(`insert op ${iop} within boundaries of previous ${rop}`); + } + } + } + + /** @type {Map} */ + let m = new Map(); + for (let op of rewrites) { + if (op == null) { + // ignore deleted ops + continue; + } + if (m.get(op.index) != null) { + throw new Error("should only be one op per index"); + } + m.set(op.index, op); + } + return m; + } + + /** + * @param {Text} a + * @param {Text} b + * @returns {string} + */ + catOpText(a, b) { + let x = ""; + let y = ""; + if (a != null) { + x = a.toString(); + } + if (b != null) { + y = b.toString(); + } + return x + y; + } + + /** + * Get all operations before an index of a particular kind + * @param {Rewrites} rewrites + * @param {any} kind + * @param {number} before + */ + getKindOfOps(rewrites, kind, before) { + return rewrites.slice(0, before).filter(op => op && op instanceof kind); + } +} + +class RewriteOperation { + /** + * @param {CommonTokenStream} tokens + * @param {number} index + * @param {number} instructionIndex + * @param {Text} text + */ + constructor(tokens, index, instructionIndex, text) { + this.tokens = tokens; + this.instructionIndex = instructionIndex; + this.index = index; + this.text = text === undefined ? "" : text; + } + + toString() { + let opName = this.constructor.name; + const $index = opName.indexOf("$"); + opName = opName.substring($index + 1, opName.length); + return "<" + opName + "@" + this.tokens.get(this.index) + + ":\"" + this.text + "\">"; + } +} + +class InsertBeforeOp extends RewriteOperation { + /** + * @param {CommonTokenStream} tokens + * @param {number} index + * @param {number} instructionIndex + * @param {Text} text + */ + constructor(tokens, index, instructionIndex, text) { + super(tokens, index, instructionIndex, text); + } + + /** + * @param {string[]} buf + * @returns {number} the index of the next token to operate on + */ + execute(buf) { + if (this.text) { + buf.push(this.text.toString()); + } + + if (this.tokens.get(this.index).type !== Token.EOF) { + buf.push(String(this.tokens.get(this.index).text)); + } + return this.index + 1; + } +} + +class InsertAfterOp extends InsertBeforeOp { + /** + * @param {CommonTokenStream} tokens + * @param {number} index + * @param {number} instructionIndex + * @param {Text} text + */ + constructor(tokens, index, instructionIndex, text) { + super(tokens, index + 1, instructionIndex, text); // insert after is insert before index+1 + } +} + +class ReplaceOp extends RewriteOperation { + /** + * @param {CommonTokenStream} tokens + * @param {number} from + * @param {number} to + * @param {number} instructionIndex + * @param {Text} text + */ + constructor(tokens, from, to, instructionIndex, text) { + super(tokens, from, instructionIndex, text); + this.lastIndex = to; + } + + /** + * @param {string[]} buf + * @returns {number} the index of the next token to operate on + */ + execute(buf) { + if (this.text) { + buf.push(this.text.toString()); + } + return this.lastIndex + 1; + } + + toString() { + if (this.text == null) { + return ""; + } + return ""; + } +} diff --git a/runtime/JavaScript/src/antlr4/index.d.ts b/runtime/JavaScript/src/antlr4/index.d.ts index 8a29f1c85c..3c6c9bcf8b 100644 --- a/runtime/JavaScript/src/antlr4/index.d.ts +++ b/runtime/JavaScript/src/antlr4/index.d.ts @@ -18,3 +18,4 @@ export * from './tree'; export * from './state'; export * from './error'; export * from './utils'; +export * from './TokenStreamRewriter'; diff --git a/runtime/JavaScript/src/antlr4/index.node.js b/runtime/JavaScript/src/antlr4/index.node.js index b61df15cd1..8566e178bd 100644 --- a/runtime/JavaScript/src/antlr4/index.node.js +++ b/runtime/JavaScript/src/antlr4/index.node.js @@ -44,10 +44,11 @@ import DiagnosticErrorListener from "./error/DiagnosticErrorListener.js" import RuleNode from "./tree/RuleNode.js" import TerminalNode from "./tree/TerminalNode.js" import arrayToString from "./utils/arrayToString.js" +import TokenStreamRewriter from './TokenStreamRewriter.js'; export default { atn, dfa, context, misc, tree, error, Token, CommonToken, CharStreams, CharStream, InputStream, FileStream, CommonTokenStream, Lexer, Parser, - ParserRuleContext, Interval, IntervalSet, LL1Analyzer, Utils + ParserRuleContext, Interval, IntervalSet, LL1Analyzer, Utils, TokenStreamRewriter } export { diff --git a/runtime/JavaScript/src/antlr4/index.web.js b/runtime/JavaScript/src/antlr4/index.web.js index 96a30e9d4d..c7906e0e16 100644 --- a/runtime/JavaScript/src/antlr4/index.web.js +++ b/runtime/JavaScript/src/antlr4/index.web.js @@ -43,10 +43,11 @@ import DiagnosticErrorListener from "./error/DiagnosticErrorListener.js" import RuleNode from "./tree/RuleNode.js" import TerminalNode from "./tree/TerminalNode.js" import arrayToString from "./utils/arrayToString.js" +import TokenStreamRewriter from './TokenStreamRewriter.js'; export default { atn, dfa, context, misc, tree, error, Token, CommonToken, CharStreams, CharStream, InputStream, CommonTokenStream, Lexer, Parser, - ParserRuleContext, Interval, IntervalSet, LL1Analyzer, Utils + ParserRuleContext, Interval, IntervalSet, LL1Analyzer, Utils, TokenStreamRewriter } export {