diff --git a/.gitignore b/.gitignore index fe54b513..47be69cb 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,5 @@ cabal.project.local~ .HTF/ .ghc.environment.* node_modules/ -.DS_Store \ No newline at end of file +.DS_Store +repl-parcel \ No newline at end of file diff --git a/repl/package-lock.json b/repl/package-lock.json index d9166583..74909c21 100644 --- a/repl/package-lock.json +++ b/repl/package-lock.json @@ -7,9 +7,16 @@ "dependencies": { "@tonaljs/tonal": "^4.6.5", "codemirror": "^5.65.1", + "estraverse": "^5.3.0", "react": "^17.0.2", "react-codemirror2": "^7.2.1", "react-dom": "^17.0.2", + "shift-ast": "^6.1.0", + "shift-codegen": "^7.0.3", + "shift-parser": "^7.0.3", + "shift-reducer": "6.0.0", + "shift-spec": "^2018.0.2", + "shift-traverser": "^1.0.0", "tone": "^14.7.77" }, "devDependencies": { @@ -3757,12 +3764,28 @@ "fsevents": "~2.1.2" } }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", "dev": true }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -5578,6 +5601,11 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/multimap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multimap/-/multimap-1.1.0.tgz", + "integrity": "sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw==" + }, "node_modules/nanocolors": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.2.13.tgz", @@ -7248,6 +7276,87 @@ "node": ">=8" } }, + "node_modules/shift-ast": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.1.0.tgz", + "integrity": "sha512-Vj4XUIJIFPIh6VcBGJ1hjH/kM88XGer94Pr7Rvxa+idEylDsrwtLw268HoxGo5xReL6T3DdRl/9/Pr1XihZ/8Q==" + }, + "node_modules/shift-codegen": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/shift-codegen/-/shift-codegen-7.0.3.tgz", + "integrity": "sha512-dfCVVdBF0qZ6pkajQ3bjxRdNEltyxEITVe7tBJkQt2eCI3znUkSxq0VSe/tTWq1LKHeAS4HuOiqYEuHMFkSq9w==", + "dependencies": { + "esutils": "^2.0.2", + "object-assign": "^4.1.0", + "shift-reducer": "6.0.0" + } + }, + "node_modules/shift-parser": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/shift-parser/-/shift-parser-7.0.3.tgz", + "integrity": "sha512-uYX2ORyZfKZrUc4iKKkO9KOhzUSxCrSBk7QK6ZmShId+BOo1gh1IwecVy97ynyOTpmhPWUttjC8BzsnQl65Zew==", + "dependencies": { + "multimap": "^1.0.2", + "shift-ast": "6.0.0", + "shift-reducer": "6.0.0", + "shift-regexp-acceptor": "2.0.3" + } + }, + "node_modules/shift-parser/node_modules/shift-ast": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz", + "integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw==" + }, + "node_modules/shift-reducer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/shift-reducer/-/shift-reducer-6.0.0.tgz", + "integrity": "sha512-2rJraRP8drIOjvaE/sALa+0tGJmMVUzlmS3wIJerJbaYuCjpFAiF0WjkTOFVtz1144Nm/ECmqeG+7yRhuMVsMg==", + "dependencies": { + "shift-ast": "6.0.0" + } + }, + "node_modules/shift-reducer/node_modules/shift-ast": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz", + "integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw==" + }, + "node_modules/shift-regexp-acceptor": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/shift-regexp-acceptor/-/shift-regexp-acceptor-2.0.3.tgz", + "integrity": "sha512-sxL7e5JNUFxm+gutFRXktX2D6KVgDAHNuDsk5XHB9Z+N5yXooZG6pdZ1GEbo3Jz6lF7ETYLBC4WAjIFm2RKTmA==", + "dependencies": { + "unicode-match-property-ecmascript": "1.0.4", + "unicode-match-property-value-ecmascript": "1.0.2", + "unicode-property-aliases-ecmascript": "1.0.4" + } + }, + "node_modules/shift-spec": { + "version": "2018.0.2", + "resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2018.0.2.tgz", + "integrity": "sha512-5CP/cKDEim4rZ6ViCSipTLY2U7HJr8q/kpDuCBmebFqbx/0DeozWO+9ienHmYjgGLDfHrqj+LBAN67FRK2vE6w==" + }, + "node_modules/shift-traverser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shift-traverser/-/shift-traverser-1.0.0.tgz", + "integrity": "sha512-DMY3512wJbdC+IC+nhLH3/Stgr2BbxbNcg7qyZ6+e5qNnNs8TBQJWdMsRgHlX1JXwF4C0ONKS8VUxsPT0Tf7aw==", + "dependencies": { + "estraverse": "4.2.0", + "shift-spec": "2018.0.0" + } + }, + "node_modules/shift-traverser/node_modules/estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shift-traverser/node_modules/shift-spec": { + "version": "2018.0.0", + "resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2018.0.0.tgz", + "integrity": "sha512-/aiPOkj7dbe+CV2VZhIMTHQToZmgniofpRG7Yr7x2/0sO6CSVC++py1Wzf+s+rWSTDHKcLvziVAxjRRV4i4EoQ==" + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -8228,6 +8337,42 @@ "through": "^2.3.8" } }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz", + "integrity": "sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz", + "integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg==", + "engines": { + "node": ">=4" + } + }, "node_modules/unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", @@ -11624,12 +11769,22 @@ } } }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, "estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", "dev": true }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -13032,6 +13187,11 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "multimap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multimap/-/multimap-1.1.0.tgz", + "integrity": "sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw==" + }, "nanocolors": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.2.13.tgz", @@ -14258,6 +14418,90 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shift-ast": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.1.0.tgz", + "integrity": "sha512-Vj4XUIJIFPIh6VcBGJ1hjH/kM88XGer94Pr7Rvxa+idEylDsrwtLw268HoxGo5xReL6T3DdRl/9/Pr1XihZ/8Q==" + }, + "shift-codegen": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/shift-codegen/-/shift-codegen-7.0.3.tgz", + "integrity": "sha512-dfCVVdBF0qZ6pkajQ3bjxRdNEltyxEITVe7tBJkQt2eCI3znUkSxq0VSe/tTWq1LKHeAS4HuOiqYEuHMFkSq9w==", + "requires": { + "esutils": "^2.0.2", + "object-assign": "^4.1.0", + "shift-reducer": "6.0.0" + } + }, + "shift-parser": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/shift-parser/-/shift-parser-7.0.3.tgz", + "integrity": "sha512-uYX2ORyZfKZrUc4iKKkO9KOhzUSxCrSBk7QK6ZmShId+BOo1gh1IwecVy97ynyOTpmhPWUttjC8BzsnQl65Zew==", + "requires": { + "multimap": "^1.0.2", + "shift-ast": "6.0.0", + "shift-reducer": "6.0.0", + "shift-regexp-acceptor": "2.0.3" + }, + "dependencies": { + "shift-ast": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz", + "integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw==" + } + } + }, + "shift-reducer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/shift-reducer/-/shift-reducer-6.0.0.tgz", + "integrity": "sha512-2rJraRP8drIOjvaE/sALa+0tGJmMVUzlmS3wIJerJbaYuCjpFAiF0WjkTOFVtz1144Nm/ECmqeG+7yRhuMVsMg==", + "requires": { + "shift-ast": "6.0.0" + }, + "dependencies": { + "shift-ast": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz", + "integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw==" + } + } + }, + "shift-regexp-acceptor": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/shift-regexp-acceptor/-/shift-regexp-acceptor-2.0.3.tgz", + "integrity": "sha512-sxL7e5JNUFxm+gutFRXktX2D6KVgDAHNuDsk5XHB9Z+N5yXooZG6pdZ1GEbo3Jz6lF7ETYLBC4WAjIFm2RKTmA==", + "requires": { + "unicode-match-property-ecmascript": "1.0.4", + "unicode-match-property-value-ecmascript": "1.0.2", + "unicode-property-aliases-ecmascript": "1.0.4" + } + }, + "shift-spec": { + "version": "2018.0.2", + "resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2018.0.2.tgz", + "integrity": "sha512-5CP/cKDEim4rZ6ViCSipTLY2U7HJr8q/kpDuCBmebFqbx/0DeozWO+9ienHmYjgGLDfHrqj+LBAN67FRK2vE6w==" + }, + "shift-traverser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shift-traverser/-/shift-traverser-1.0.0.tgz", + "integrity": "sha512-DMY3512wJbdC+IC+nhLH3/Stgr2BbxbNcg7qyZ6+e5qNnNs8TBQJWdMsRgHlX1JXwF4C0ONKS8VUxsPT0Tf7aw==", + "requires": { + "estraverse": "4.2.0", + "shift-spec": "2018.0.0" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + }, + "shift-spec": { + "version": "2018.0.0", + "resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2018.0.0.tgz", + "integrity": "sha512-/aiPOkj7dbe+CV2VZhIMTHQToZmgniofpRG7Yr7x2/0sO6CSVC++py1Wzf+s+rWSTDHKcLvziVAxjRRV4i4EoQ==" + } + } + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -15001,6 +15245,30 @@ "through": "^2.3.8" } }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==" + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz", + "integrity": "sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ==" + }, + "unicode-property-aliases-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz", + "integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg==" + }, "unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", diff --git a/repl/package.json b/repl/package.json index 2e87f86c..29dc4b58 100644 --- a/repl/package.json +++ b/repl/package.json @@ -10,9 +10,13 @@ "dependencies": { "@tonaljs/tonal": "^4.6.5", "codemirror": "^5.65.1", + "estraverse": "^5.3.0", "react": "^17.0.2", "react-codemirror2": "^7.2.1", "react-dom": "^17.0.2", + "shift-ast": "^6.1.0", + "shift-codegen": "^7.0.3", + "shift-spec": "^2018.0.2", "tone": "^14.7.77" }, "devDependencies": { diff --git a/repl/src/App.tsx b/repl/src/App.tsx index 45eaa9ee..ad54944b 100644 --- a/repl/src/App.tsx +++ b/repl/src/App.tsx @@ -8,8 +8,9 @@ import * as tunes from './tunes'; import * as parser from './parse'; import CodeMirror from './CodeMirror'; import hot from '../public/hot'; +import { isNote } from 'tone'; -const { tetris, tetrisRev } = tunes; +const { tetris, tetrisRev, shapeShifted } = tunes; const { parse } = parser; const getHotCode = async () => { @@ -30,17 +31,18 @@ defaultSynth.set({ function App() { const [mode, setMode] = useState('javascript'); - const [code, setCode] = useState(tetrisRev); + const [code, setCode] = useState(shapeShifted); const [log, setLog] = useState(''); const logBox = useRef(); const [error, setError] = useState(); const [pattern, setPattern] = useState(); const [activePattern, setActivePattern] = useState(); const [isHot, setIsHot] = useState(false); // set to true to enable live coding in hot.js, using dev server + const pushLog = (message: string) => setLog((log) => log + `${log ? '\n\n' : ''}${message}`); // logs events of cycle const logCycle = (_events: any, cycle: any) => { if (_events.length) { - setLog((log) => log + `${log ? '\n\n' : ''}# cycle ${cycle}\n` + _events.map((e: any) => e.show()).join('\n')); + pushLog(`# cycle ${cycle}\n` + _events.map((e: any) => e.show()).join('\n')); } }; // cycle hook to control scheduling @@ -48,6 +50,9 @@ function App() { onEvent: useCallback((time, event) => { try { if (typeof event.value === 'string') { + if (!isNote(event.value)) { + throw new Error('not a note: ' + event.value); + } defaultSynth.triggerAttackRelease(event.value, event.duration, time); /* console.warn('no instrument chosen', event); throw new Error(`no instrument chosen for ${JSON.stringify(event)}`); */ @@ -55,11 +60,10 @@ function App() { const { onTrigger } = event.value; onTrigger(time, event); } - setError(undefined); } catch (err: any) { console.warn(err); err.message = 'unplayable event: ' + err?.message; - setError(err); + pushLog(err.message); // not with setError, because then we would have to setError(undefined) on next playable event } }, []), onQuery: useCallback( diff --git a/repl/src/parse.ts b/repl/src/parse.ts index 9669f7a9..9f1f0309 100644 --- a/repl/src/parse.ts +++ b/repl/src/parse.ts @@ -3,6 +3,7 @@ import * as strudel from '../../strudel.mjs'; import { Scale, Note, Interval } from '@tonaljs/tonal'; import './tone'; import * as toneStuff from './tone'; +import shapeshifter from './shapeshifter'; // even if some functions are not used, we need them to be available in eval const { pure, stack, slowcat, fastcat, cat, sequence, polymeter, pm, polyrhythm, pr, /* reify, */ silence, Fraction } = @@ -114,6 +115,7 @@ export const parse: any = (code: string) => { } catch (err) { // code is not haskell like mode = 'javascript'; + code = shapeshifter(code); _pattern = eval(code); if (_pattern?.constructor?.name !== 'Pattern') { const message = `got "${typeof _pattern}" instead of pattern`; diff --git a/repl/src/shapeshifter.js b/repl/src/shapeshifter.js new file mode 100644 index 00000000..b9d56a78 --- /dev/null +++ b/repl/src/shapeshifter.js @@ -0,0 +1,26 @@ +import { parseScript } from './shift-parser/index.js'; // npm module does not work in the browser +import traverser from './shift-traverser'; // npm module does not work in the browser +const { replace } = traverser; +import { LiteralStringExpression, IdentifierExpression } from 'shift-ast'; +import codegen from 'shift-codegen'; + +const isNote = (name) => /^[a-gC-G][bs]?[0-9]$/.test(name); + +export default (code) => { + const ast = parseScript(code); + const shifted = replace(ast, { + enter(node, parent) { + // replace identifiers that are a note with a note string + if (node.type === 'IdentifierExpression') { + if (isNote(node.name)) { + const value = node.name[1] === 's' ? node.name.replace('s', '#') : node.name; + return new LiteralStringExpression({ value }); + } + if (node.name === 'r') { + return new IdentifierExpression({ name: 'silence' }); + } + } + }, + }); + return codegen(shifted); +}; diff --git a/repl/src/shift-parser/early-error-state.js b/repl/src/shift-parser/early-error-state.js new file mode 100644 index 00000000..38533747 --- /dev/null +++ b/repl/src/shift-parser/early-error-state.js @@ -0,0 +1,409 @@ +/** + * Copyright 2014 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import MultiMap from 'multimap'; + +function addEach(thisMap, ...otherMaps) { + otherMaps.forEach(otherMap => { + otherMap.forEachEntry((v, k) => { + thisMap.set.apply(thisMap, [k].concat(v)); + }); + }); + return thisMap; +} + +let identity; // initialised below EarlyErrorState + +export class EarlyErrorState { + + constructor() { + this.errors = []; + // errors that are only errors in strict mode code + this.strictErrors = []; + + // Label values used in LabeledStatement nodes; cleared at function boundaries + this.usedLabelNames = []; + + // BreakStatement nodes; cleared at iteration; switch; and function boundaries + this.freeBreakStatements = []; + // ContinueStatement nodes; cleared at + this.freeContinueStatements = []; + + // labeled BreakStatement nodes; cleared at LabeledStatement with same Label and function boundaries + this.freeLabeledBreakStatements = []; + // labeled ContinueStatement nodes; cleared at labeled iteration statement with same Label and function boundaries + this.freeLabeledContinueStatements = []; + + // NewTargetExpression nodes; cleared at function (besides arrow expression) boundaries + this.newTargetExpressions = []; + + // BindingIdentifier nodes; cleared at containing declaration node + this.boundNames = new MultiMap; + // BindingIdentifiers that were found to be in a lexical binding position + this.lexicallyDeclaredNames = new MultiMap; + // BindingIdentifiers that were the name of a FunctionDeclaration + this.functionDeclarationNames = new MultiMap; + // BindingIdentifiers that were found to be in a variable binding position + this.varDeclaredNames = new MultiMap; + // BindingIdentifiers that were found to be in a variable binding position + this.forOfVarDeclaredNames = []; + + // Names that this module exports + this.exportedNames = new MultiMap; + // Locally declared names that are referenced in export declarations + this.exportedBindings = new MultiMap; + + // CallExpressions with Super callee + this.superCallExpressions = []; + // SuperCall expressions in the context of a Method named "constructor" + this.superCallExpressionsInConstructorMethod = []; + // MemberExpressions with Super object + this.superPropertyExpressions = []; + + // YieldExpression and YieldGeneratorExpression nodes; cleared at function boundaries + this.yieldExpressions = []; + // AwaitExpression nodes; cleared at function boundaries + this.awaitExpressions = []; + } + + + addFreeBreakStatement(s) { + this.freeBreakStatements.push(s); + return this; + } + + addFreeLabeledBreakStatement(s) { + this.freeLabeledBreakStatements.push(s); + return this; + } + + clearFreeBreakStatements() { + this.freeBreakStatements = []; + return this; + } + + addFreeContinueStatement(s) { + this.freeContinueStatements.push(s); + return this; + } + + addFreeLabeledContinueStatement(s) { + this.freeLabeledContinueStatements.push(s); + return this; + } + + clearFreeContinueStatements() { + this.freeContinueStatements = []; + return this; + } + + enforceFreeBreakStatementErrors(createError) { + [].push.apply(this.errors, this.freeBreakStatements.map(createError)); + this.freeBreakStatements = []; + return this; + } + + enforceFreeLabeledBreakStatementErrors(createError) { + [].push.apply(this.errors, this.freeLabeledBreakStatements.map(createError)); + this.freeLabeledBreakStatements = []; + return this; + } + + enforceFreeContinueStatementErrors(createError) { + [].push.apply(this.errors, this.freeContinueStatements.map(createError)); + this.freeContinueStatements = []; + return this; + } + + enforceFreeLabeledContinueStatementErrors(createError) { + [].push.apply(this.errors, this.freeLabeledContinueStatements.map(createError)); + this.freeLabeledContinueStatements = []; + return this; + } + + + observeIterationLabel(label) { + this.usedLabelNames.push(label); + this.freeLabeledBreakStatements = this.freeLabeledBreakStatements.filter(s => s.label !== label); + this.freeLabeledContinueStatements = this.freeLabeledContinueStatements.filter(s => s.label !== label); + return this; + } + + observeNonIterationLabel(label) { + this.usedLabelNames.push(label); + this.freeLabeledBreakStatements = this.freeLabeledBreakStatements.filter(s => s.label !== label); + return this; + } + + clearUsedLabelNames() { + this.usedLabelNames = []; + return this; + } + + + observeSuperCallExpression(node) { + this.superCallExpressions.push(node); + return this; + } + + observeConstructorMethod() { + this.superCallExpressionsInConstructorMethod = this.superCallExpressions; + this.superCallExpressions = []; + return this; + } + + clearSuperCallExpressionsInConstructorMethod() { + this.superCallExpressionsInConstructorMethod = []; + return this; + } + + enforceSuperCallExpressions(createError) { + [].push.apply(this.errors, this.superCallExpressions.map(createError)); + [].push.apply(this.errors, this.superCallExpressionsInConstructorMethod.map(createError)); + this.superCallExpressions = []; + this.superCallExpressionsInConstructorMethod = []; + return this; + } + + enforceSuperCallExpressionsInConstructorMethod(createError) { + [].push.apply(this.errors, this.superCallExpressionsInConstructorMethod.map(createError)); + this.superCallExpressionsInConstructorMethod = []; + return this; + } + + + observeSuperPropertyExpression(node) { + this.superPropertyExpressions.push(node); + return this; + } + + clearSuperPropertyExpressions() { + this.superPropertyExpressions = []; + return this; + } + + enforceSuperPropertyExpressions(createError) { + [].push.apply(this.errors, this.superPropertyExpressions.map(createError)); + this.superPropertyExpressions = []; + return this; + } + + + observeNewTargetExpression(node) { + this.newTargetExpressions.push(node); + return this; + } + + clearNewTargetExpressions() { + this.newTargetExpressions = []; + return this; + } + + + bindName(name, node) { + this.boundNames.set(name, node); + return this; + } + + clearBoundNames() { + this.boundNames = new MultiMap; + return this; + } + + observeLexicalDeclaration() { + addEach(this.lexicallyDeclaredNames, this.boundNames); + this.boundNames = new MultiMap; + return this; + } + + observeLexicalBoundary() { + this.previousLexicallyDeclaredNames = this.lexicallyDeclaredNames; + this.lexicallyDeclaredNames = new MultiMap; + this.functionDeclarationNames = new MultiMap; + return this; + } + + enforceDuplicateLexicallyDeclaredNames(createError) { + this.lexicallyDeclaredNames.forEachEntry(nodes => { + if (nodes.length > 1) { + nodes.slice(1).forEach(dupeNode => { + this.addError(createError(dupeNode)); + }); + } + }); + return this; + } + + enforceConflictingLexicallyDeclaredNames(otherNames, createError) { + this.lexicallyDeclaredNames.forEachEntry((nodes, bindingName) => { + if (otherNames.has(bindingName)) { + nodes.forEach(conflictingNode => { + this.addError(createError(conflictingNode)); + }); + } + }); + return this; + } + + observeFunctionDeclaration() { + this.observeVarBoundary(); + addEach(this.functionDeclarationNames, this.boundNames); + this.boundNames = new MultiMap; + return this; + } + + functionDeclarationNamesAreLexical() { + addEach(this.lexicallyDeclaredNames, this.functionDeclarationNames); + this.functionDeclarationNames = new MultiMap; + return this; + } + + observeVarDeclaration() { + addEach(this.varDeclaredNames, this.boundNames); + this.boundNames = new MultiMap; + return this; + } + + recordForOfVars() { + this.varDeclaredNames.forEach(bindingIdentifier => { + this.forOfVarDeclaredNames.push(bindingIdentifier); + }); + return this; + } + + observeVarBoundary() { + this.lexicallyDeclaredNames = new MultiMap; + this.functionDeclarationNames = new MultiMap; + this.varDeclaredNames = new MultiMap; + this.forOfVarDeclaredNames = []; + return this; + } + + + exportName(name, node) { + this.exportedNames.set(name, node); + return this; + } + + exportDeclaredNames() { + addEach(this.exportedNames, this.lexicallyDeclaredNames, this.varDeclaredNames); + addEach(this.exportedBindings, this.lexicallyDeclaredNames, this.varDeclaredNames); + return this; + } + + exportBinding(name, node) { + this.exportedBindings.set(name, node); + return this; + } + + clearExportedBindings() { + this.exportedBindings = new MultiMap; + return this; + } + + + observeYieldExpression(node) { + this.yieldExpressions.push(node); + return this; + } + + clearYieldExpressions() { + this.yieldExpressions = []; + return this; + } + + observeAwaitExpression(node) { + this.awaitExpressions.push(node); + return this; + } + + clearAwaitExpressions() { + this.awaitExpressions = []; + return this; + } + + + addError(e) { + this.errors.push(e); + return this; + } + + addStrictError(e) { + this.strictErrors.push(e); + return this; + } + + enforceStrictErrors() { + [].push.apply(this.errors, this.strictErrors); + this.strictErrors = []; + return this; + } + + + // MONOID IMPLEMENTATION + + static empty() { + return identity; + } + + concat(s) { + if (this === identity) return s; + if (s === identity) return this; + [].push.apply(this.errors, s.errors); + [].push.apply(this.strictErrors, s.strictErrors); + [].push.apply(this.usedLabelNames, s.usedLabelNames); + [].push.apply(this.freeBreakStatements, s.freeBreakStatements); + [].push.apply(this.freeContinueStatements, s.freeContinueStatements); + [].push.apply(this.freeLabeledBreakStatements, s.freeLabeledBreakStatements); + [].push.apply(this.freeLabeledContinueStatements, s.freeLabeledContinueStatements); + [].push.apply(this.newTargetExpressions, s.newTargetExpressions); + addEach(this.boundNames, s.boundNames); + addEach(this.lexicallyDeclaredNames, s.lexicallyDeclaredNames); + addEach(this.functionDeclarationNames, s.functionDeclarationNames); + addEach(this.varDeclaredNames, s.varDeclaredNames); + [].push.apply(this.forOfVarDeclaredNames, s.forOfVarDeclaredNames); + addEach(this.exportedNames, s.exportedNames); + addEach(this.exportedBindings, s.exportedBindings); + [].push.apply(this.superCallExpressions, s.superCallExpressions); + [].push.apply(this.superCallExpressionsInConstructorMethod, s.superCallExpressionsInConstructorMethod); + [].push.apply(this.superPropertyExpressions, s.superPropertyExpressions); + [].push.apply(this.yieldExpressions, s.yieldExpressions); + [].push.apply(this.awaitExpressions, s.awaitExpressions); + return this; + } + +} + +identity = new EarlyErrorState; +Object.getOwnPropertyNames(EarlyErrorState.prototype).forEach(methodName => { + if (methodName === 'constructor') return; + Object.defineProperty(identity, methodName, { + value() { + return EarlyErrorState.prototype[methodName].apply(new EarlyErrorState, arguments); + }, + enumerable: false, + writable: true, + configurable: true, + }); +}); + +export class EarlyError extends Error { + constructor(node, message) { + super(message); + this.node = node; + this.message = message; + } +} diff --git a/repl/src/shift-parser/early-errors.js b/repl/src/shift-parser/early-errors.js new file mode 100644 index 00000000..02bc56e1 --- /dev/null +++ b/repl/src/shift-parser/early-errors.js @@ -0,0 +1,772 @@ +/** + * Copyright 2014 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import reduce, { MonoidalReducer } from '../shift-reducer'; +import { isStrictModeReservedWord } from './utils'; +import { ErrorMessages } from './errors'; + +import { EarlyErrorState, EarlyError } from './early-error-state'; + +function isStrictFunctionBody({ directives }) { + return directives.some(directive => directive.rawValue === 'use strict'); +} + +function isLabelledFunction(node) { + return node.type === 'LabeledStatement' && + (node.body.type === 'FunctionDeclaration' || isLabelledFunction(node.body)); +} + +function isIterationStatement(node) { + switch (node.type) { + case 'LabeledStatement': + return isIterationStatement(node.body); + case 'DoWhileStatement': + case 'ForInStatement': + case 'ForOfStatement': + case 'ForStatement': + case 'WhileStatement': + return true; + } + return false; +} + +function isSpecialMethod(methodDefinition) { + if (methodDefinition.name.type !== 'StaticPropertyName' || methodDefinition.name.value !== 'constructor') { + return false; + } + switch (methodDefinition.type) { + case 'Getter': + case 'Setter': + return true; + case 'Method': + return methodDefinition.isGenerator || methodDefinition.isAsync; + } + /* istanbul ignore next */ + throw new Error('not reached'); +} + + +function enforceDuplicateConstructorMethods(node, s) { + let ctors = node.elements.filter(e => + !e.isStatic && + e.method.type === 'Method' && + !e.method.isGenerator && + e.method.name.type === 'StaticPropertyName' && + e.method.name.value === 'constructor' + ); + if (ctors.length > 1) { + ctors.slice(1).forEach(ctor => { + s = s.addError(new EarlyError(ctor, 'Duplicate constructor method in class')); + }); + } + return s; +} + +const SUPERCALL_ERROR = node => new EarlyError(node, ErrorMessages.ILLEGAL_SUPER_CALL); +const SUPERPROPERTY_ERROR = node => new EarlyError(node, 'Member access on super must be in a method'); +const DUPLICATE_BINDING = node => new EarlyError(node, `Duplicate binding ${JSON.stringify(node.name)}`); +const FREE_CONTINUE = node => new EarlyError(node, 'Continue statement must be nested within an iteration statement'); +const UNBOUND_CONTINUE = node => new EarlyError(node, `Continue statement must be nested within an iteration statement with label ${JSON.stringify(node.label)}`); +const FREE_BREAK = node => new EarlyError(node, 'Break statement must be nested within an iteration statement or a switch statement'); +const UNBOUND_BREAK = node => new EarlyError(node, `Break statement must be nested within a statement with label ${JSON.stringify(node.label)}`); + +export class EarlyErrorChecker extends MonoidalReducer { + constructor() { + super(EarlyErrorState); + } + + reduceAssignmentExpression() { + return super.reduceAssignmentExpression(...arguments).clearBoundNames(); + } + + reduceAssignmentTargetIdentifier(node) { + let s = this.identity; + if (node.name === 'eval' || node.name === 'arguments' || isStrictModeReservedWord(node.name)) { + s = s.addStrictError(new EarlyError(node, `The identifier ${JSON.stringify(node.name)} must not be in binding position in strict mode`)); + } + return s; + } + + reduceArrowExpression(node, { params, body }) { + let isSimpleParameterList = node.params.rest == null && node.params.items.every(i => i.type === 'BindingIdentifier'); + params = params.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + if (node.body.type === 'FunctionBody') { + body = body.enforceConflictingLexicallyDeclaredNames(params.lexicallyDeclaredNames, DUPLICATE_BINDING); + if (isStrictFunctionBody(node.body)) { + params = params.enforceStrictErrors(); + body = body.enforceStrictErrors(); + } + } + params.yieldExpressions.forEach(n => { + params = params.addError(new EarlyError(n, 'Arrow parameters must not contain yield expressions')); + }); + params.awaitExpressions.forEach(n => { + params = params.addError(new EarlyError(n, 'Arrow parameters must not contain await expressions')); + }); + let s = super.reduceArrowExpression(node, { params, body }); + if (!isSimpleParameterList && node.body.type === 'FunctionBody' && isStrictFunctionBody(node.body)) { + s = s.addError(new EarlyError(node, 'Functions with non-simple parameter lists may not contain a "use strict" directive')); + } + s = s.clearYieldExpressions(); + s = s.clearAwaitExpressions(); + s = s.observeVarBoundary(); + return s; + } + + reduceAwaitExpression(node, { expression }) { + return expression.observeAwaitExpression(node); + } + + reduceBindingIdentifier(node) { + let s = this.identity; + if (node.name === 'eval' || node.name === 'arguments' || isStrictModeReservedWord(node.name)) { + s = s.addStrictError(new EarlyError(node, `The identifier ${JSON.stringify(node.name)} must not be in binding position in strict mode`)); + } + s = s.bindName(node.name, node); + return s; + } + + reduceBlock() { + let s = super.reduceBlock(...arguments); + s = s.functionDeclarationNamesAreLexical(); + s = s.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + s = s.enforceConflictingLexicallyDeclaredNames(s.varDeclaredNames, DUPLICATE_BINDING); + s = s.observeLexicalBoundary(); + return s; + } + + reduceBreakStatement(node) { + let s = super.reduceBreakStatement(...arguments); + s = node.label == null + ? s.addFreeBreakStatement(node) + : s.addFreeLabeledBreakStatement(node); + return s; + } + + reduceCallExpression(node) { + let s = super.reduceCallExpression(...arguments); + if (node.callee.type === 'Super') { + s = s.observeSuperCallExpression(node); + } + return s; + } + + reduceCatchClause(node, { binding, body }) { + binding = binding.observeLexicalDeclaration(); + binding = binding.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + binding = binding.enforceConflictingLexicallyDeclaredNames(body.previousLexicallyDeclaredNames, DUPLICATE_BINDING); + binding.lexicallyDeclaredNames.forEachEntry((nodes, bindingName) => { + if (body.varDeclaredNames.has(bindingName)) { + body.varDeclaredNames.get(bindingName).forEach(conflictingNode => { + if (body.forOfVarDeclaredNames.indexOf(conflictingNode) >= 0) { + binding = binding.addError(DUPLICATE_BINDING(conflictingNode)); + } + }); + } + }); + let s = super.reduceCatchClause(node, { binding, body }); + s = s.observeLexicalBoundary(); + return s; + } + + reduceClassDeclaration(node, { name, super: _super, elements }) { + let s = name.enforceStrictErrors(); + let sElements = this.append(...elements); + sElements = sElements.enforceStrictErrors(); + if (node.super != null) { + _super = _super.enforceStrictErrors(); + s = this.append(s, _super); + sElements = sElements.clearSuperCallExpressionsInConstructorMethod(); + } + s = this.append(s, sElements); + s = enforceDuplicateConstructorMethods(node, s); + s = s.observeLexicalDeclaration(); + return s; + } + + reduceClassElement(node) { + let s = super.reduceClassElement(...arguments); + if (!node.isStatic && isSpecialMethod(node.method)) { + s = s.addError(new EarlyError(node, ErrorMessages.ILLEGAL_CONSTRUCTORS)); + } + if (node.isStatic && node.method.name.type === 'StaticPropertyName' && node.method.name.value === 'prototype') { + s = s.addError(new EarlyError(node, 'Static class methods cannot be named "prototype"')); + } + return s; + } + + reduceClassExpression(node, { name, super: _super, elements }) { + let s = node.name == null ? this.identity : name.enforceStrictErrors(); + let sElements = this.append(...elements); + sElements = sElements.enforceStrictErrors(); + if (node.super != null) { + _super = _super.enforceStrictErrors(); + s = this.append(s, _super); + sElements = sElements.clearSuperCallExpressionsInConstructorMethod(); + } + s = this.append(s, sElements); + s = enforceDuplicateConstructorMethods(node, s); + s = s.clearBoundNames(); + return s; + } + + reduceCompoundAssignmentExpression() { + return super.reduceCompoundAssignmentExpression(...arguments).clearBoundNames(); + } + + reduceComputedMemberExpression(node) { + let s = super.reduceComputedMemberExpression(...arguments); + if (node.object.type === 'Super') { + s = s.observeSuperPropertyExpression(node); + } + return s; + } + + reduceContinueStatement(node) { + let s = super.reduceContinueStatement(...arguments); + s = node.label == null + ? s.addFreeContinueStatement(node) + : s.addFreeLabeledContinueStatement(node); + return s; + } + + reduceDoWhileStatement(node) { + let s = super.reduceDoWhileStatement(...arguments); + if (isLabelledFunction(node.body)) { + s = s.addError(new EarlyError(node.body, 'The body of a do-while statement must not be a labeled function declaration')); + } + s = s.clearFreeContinueStatements(); + s = s.clearFreeBreakStatements(); + return s; + } + + reduceExport() { + let s = super.reduceExport(...arguments); + s = s.functionDeclarationNamesAreLexical(); + s = s.exportDeclaredNames(); + return s; + } + + reduceExportFrom() { + let s = super.reduceExportFrom(...arguments); + s = s.clearExportedBindings(); + return s; + } + + reduceExportFromSpecifier(node) { + let s = super.reduceExportFromSpecifier(...arguments); + s = s.exportName(node.exportedName || node.name, node); + s = s.exportBinding(node.name, node); + return s; + } + + reduceExportLocalSpecifier(node) { + let s = super.reduceExportLocalSpecifier(...arguments); + s = s.exportName(node.exportedName || node.name.name, node); + s = s.exportBinding(node.name.name, node); + return s; + } + + reduceExportDefault(node) { + let s = super.reduceExportDefault(...arguments); + s = s.functionDeclarationNamesAreLexical(); + s = s.exportName('default', node); + return s; + } + + reduceFormalParameters() { + let s = super.reduceFormalParameters(...arguments); + s = s.observeLexicalDeclaration(); + return s; + } + + reduceForStatement(node, { init, test, update, body }) { + if (init != null) { + init = init.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + init = init.enforceConflictingLexicallyDeclaredNames(body.varDeclaredNames, DUPLICATE_BINDING); + } + let s = super.reduceForStatement(node, { init, test, update, body }); + if (node.init != null && node.init.type === 'VariableDeclaration' && node.init.kind === 'const') { + node.init.declarators.forEach(declarator => { + if (declarator.init == null) { + s = s.addError(new EarlyError(declarator, 'Constant lexical declarations must have an initialiser')); + } + }); + } + if (isLabelledFunction(node.body)) { + s = s.addError(new EarlyError(node.body, 'The body of a for statement must not be a labeled function declaration')); + } + s = s.clearFreeContinueStatements(); + s = s.clearFreeBreakStatements(); + s = s.observeLexicalBoundary(); + return s; + } + + reduceForInStatement(node, { left, right, body }) { + left = left.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + left = left.enforceConflictingLexicallyDeclaredNames(body.varDeclaredNames, DUPLICATE_BINDING); + let s = super.reduceForInStatement(node, { left, right, body }); + if (isLabelledFunction(node.body)) { + s = s.addError(new EarlyError(node.body, 'The body of a for-in statement must not be a labeled function declaration')); + } + s = s.clearFreeContinueStatements(); + s = s.clearFreeBreakStatements(); + s = s.observeLexicalBoundary(); + return s; + } + + reduceForOfStatement(node, { left, right, body }) { + left = left.recordForOfVars(); + left = left.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + left = left.enforceConflictingLexicallyDeclaredNames(body.varDeclaredNames, DUPLICATE_BINDING); + let s = super.reduceForOfStatement(node, { left, right, body }); + if (isLabelledFunction(node.body)) { + s = s.addError(new EarlyError(node.body, 'The body of a for-of statement must not be a labeled function declaration')); + } + s = s.clearFreeContinueStatements(); + s = s.clearFreeBreakStatements(); + s = s.observeLexicalBoundary(); + return s; + } + + reduceForAwaitStatement(node, { left, right, body }) { + left = left.recordForOfVars(); + left = left.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + left = left.enforceConflictingLexicallyDeclaredNames(body.varDeclaredNames, DUPLICATE_BINDING); + let s = super.reduceForOfStatement(node, { left, right, body }); + if (isLabelledFunction(node.body)) { + s = s.addError(new EarlyError(node.body, 'The body of a for-await statement must not be a labeled function declaration')); + } + s = s.clearFreeContinueStatements(); + s = s.clearFreeBreakStatements(); + s = s.observeLexicalBoundary(); + return s; + } + + reduceFunctionBody(node) { + let s = super.reduceFunctionBody(...arguments); + s = s.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + s = s.enforceConflictingLexicallyDeclaredNames(s.varDeclaredNames, DUPLICATE_BINDING); + s = s.enforceFreeContinueStatementErrors(FREE_CONTINUE); + s = s.enforceFreeLabeledContinueStatementErrors(UNBOUND_CONTINUE); + s = s.enforceFreeBreakStatementErrors(FREE_BREAK); + s = s.enforceFreeLabeledBreakStatementErrors(UNBOUND_BREAK); + s = s.clearUsedLabelNames(); + s = s.clearYieldExpressions(); + s = s.clearAwaitExpressions(); + if (isStrictFunctionBody(node)) { + s = s.enforceStrictErrors(); + } + return s; + } + + reduceFunctionDeclaration(node, { name, params, body }) { + let isSimpleParameterList = node.params.rest == null && node.params.items.every(i => i.type === 'BindingIdentifier'); + let addError = !isSimpleParameterList || node.isGenerator ? 'addError' : 'addStrictError'; + params.lexicallyDeclaredNames.forEachEntry(nodes => { + if (nodes.length > 1) { + nodes.slice(1).forEach(dupeNode => { + params = params[addError](DUPLICATE_BINDING(dupeNode)); + }); + } + }); + body = body.enforceConflictingLexicallyDeclaredNames(params.lexicallyDeclaredNames, DUPLICATE_BINDING); + body = body.enforceSuperCallExpressions(SUPERCALL_ERROR); + body = body.enforceSuperPropertyExpressions(SUPERPROPERTY_ERROR); + params = params.enforceSuperCallExpressions(SUPERCALL_ERROR); + params = params.enforceSuperPropertyExpressions(SUPERPROPERTY_ERROR); + if (node.isGenerator) { + params.yieldExpressions.forEach(n => { + params = params.addError(new EarlyError(n, 'Generator parameters must not contain yield expressions')); + }); + } + if (node.isAsync) { + params.awaitExpressions.forEach(n => { + params = params.addError(new EarlyError(n, 'Async function parameters must not contain await expressions')); + }); + } + params = params.clearNewTargetExpressions(); + body = body.clearNewTargetExpressions(); + if (isStrictFunctionBody(node.body)) { + params = params.enforceStrictErrors(); + body = body.enforceStrictErrors(); + } + let s = super.reduceFunctionDeclaration(node, { name, params, body }); + if (!isSimpleParameterList && isStrictFunctionBody(node.body)) { + s = s.addError(new EarlyError(node, 'Functions with non-simple parameter lists may not contain a "use strict" directive')); + } + s = s.clearYieldExpressions(); + s = s.clearAwaitExpressions(); + s = s.observeFunctionDeclaration(); + return s; + } + + reduceFunctionExpression(node, { name, params, body }) { + let isSimpleParameterList = node.params.rest == null && node.params.items.every(i => i.type === 'BindingIdentifier'); + let addError = !isSimpleParameterList || node.isGenerator ? 'addError' : 'addStrictError'; + params.lexicallyDeclaredNames.forEachEntry((nodes, bindingName) => { + if (nodes.length > 1) { + nodes.slice(1).forEach(dupeNode => { + params = params[addError](new EarlyError(dupeNode, `Duplicate binding ${JSON.stringify(bindingName)}`)); + }); + } + }); + body = body.enforceConflictingLexicallyDeclaredNames(params.lexicallyDeclaredNames, DUPLICATE_BINDING); + body = body.enforceSuperCallExpressions(SUPERCALL_ERROR); + body = body.enforceSuperPropertyExpressions(SUPERPROPERTY_ERROR); + params = params.enforceSuperCallExpressions(SUPERCALL_ERROR); + params = params.enforceSuperPropertyExpressions(SUPERPROPERTY_ERROR); + if (node.isGenerator) { + params.yieldExpressions.forEach(n => { + params = params.addError(new EarlyError(n, 'Generator parameters must not contain yield expressions')); + }); + } + if (node.isAsync) { + params.awaitExpressions.forEach(n => { + params = params.addError(new EarlyError(n, 'Async function parameters must not contain await expressions')); + }); + } + params = params.clearNewTargetExpressions(); + body = body.clearNewTargetExpressions(); + if (isStrictFunctionBody(node.body)) { + params = params.enforceStrictErrors(); + body = body.enforceStrictErrors(); + } + let s = super.reduceFunctionExpression(node, { name, params, body }); + if (!isSimpleParameterList && isStrictFunctionBody(node.body)) { + s = s.addError(new EarlyError(node, 'Functions with non-simple parameter lists may not contain a "use strict" directive')); + } + s = s.clearBoundNames(); + s = s.clearYieldExpressions(); + s = s.clearAwaitExpressions(); + s = s.observeVarBoundary(); + return s; + } + + reduceGetter(node, { name, body }) { + body = body.enforceSuperCallExpressions(SUPERCALL_ERROR); + body = body.clearSuperPropertyExpressions(); + body = body.clearNewTargetExpressions(); + if (isStrictFunctionBody(node.body)) { + body = body.enforceStrictErrors(); + } + let s = super.reduceGetter(node, { name, body }); + s = s.observeVarBoundary(); + return s; + } + + reduceIdentifierExpression(node) { + let s = this.identity; + if (isStrictModeReservedWord(node.name)) { + s = s.addStrictError(new EarlyError(node, `The identifier ${JSON.stringify(node.name)} must not be in expression position in strict mode`)); + } + return s; + } + + reduceIfStatement(node, { test, consequent, alternate }) { + if (isLabelledFunction(node.consequent)) { + consequent = consequent.addError(new EarlyError(node.consequent, 'The consequent of an if statement must not be a labeled function declaration')); + } + if (node.alternate != null && isLabelledFunction(node.alternate)) { + alternate = alternate.addError(new EarlyError(node.alternate, 'The alternate of an if statement must not be a labeled function declaration')); + } + if (node.consequent.type === 'FunctionDeclaration') { + consequent = consequent.addStrictError(new EarlyError(node.consequent, 'FunctionDeclarations in IfStatements are disallowed in strict mode')); + consequent = consequent.observeLexicalBoundary(); + } + if (node.alternate != null && node.alternate.type === 'FunctionDeclaration') { + alternate = alternate.addStrictError(new EarlyError(node.alternate, 'FunctionDeclarations in IfStatements are disallowed in strict mode')); + alternate = alternate.observeLexicalBoundary(); + } + return super.reduceIfStatement(node, { test, consequent, alternate }); + } + + reduceImport() { + let s = super.reduceImport(...arguments); + s = s.observeLexicalDeclaration(); + return s; + } + + reduceImportNamespace() { + let s = super.reduceImportNamespace(...arguments); + s = s.observeLexicalDeclaration(); + return s; + } + + reduceLabeledStatement(node) { + let s = super.reduceLabeledStatement(...arguments); + if (node.label === 'yield' || isStrictModeReservedWord(node.label)) { + s = s.addStrictError(new EarlyError(node, `The identifier ${JSON.stringify(node.label)} must not be in label position in strict mode`)); + } + if (s.usedLabelNames.indexOf(node.label) >= 0) { + s = s.addError(new EarlyError(node, `Label ${JSON.stringify(node.label)} has already been declared`)); + } + if (node.body.type === 'FunctionDeclaration') { + s = s.addStrictError(new EarlyError(node, 'Labeled FunctionDeclarations are disallowed in strict mode')); + } + s = isIterationStatement(node.body) + ? s.observeIterationLabel(node.label) + : s.observeNonIterationLabel(node.label); + return s; + } + + reduceLiteralRegExpExpression() { + let s = this.identity; + // NOTE: the RegExp pattern acceptor is disabled until we have more confidence in its correctness (more tests) + // if (!PatternAcceptor.test(node.pattern, node.flags.indexOf("u") >= 0)) { + // s = s.addError(new EarlyError(node, "Invalid regular expression pattern")); + // } + return s; + } + + reduceMethod(node, { name, params, body }) { + let isSimpleParameterList = node.params.rest == null && node.params.items.every(i => i.type === 'BindingIdentifier'); + params = params.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + body = body.enforceConflictingLexicallyDeclaredNames(params.lexicallyDeclaredNames, DUPLICATE_BINDING); + if (node.name.type === 'StaticPropertyName' && node.name.value === 'constructor') { + body = body.observeConstructorMethod(); + params = params.observeConstructorMethod(); + } else { + body = body.enforceSuperCallExpressions(SUPERCALL_ERROR); + params = params.enforceSuperCallExpressions(SUPERCALL_ERROR); + } + if (node.isGenerator) { + params.yieldExpressions.forEach(n => { + params = params.addError(new EarlyError(n, 'Generator parameters must not contain yield expressions')); + }); + } + if (node.isAsync) { + params.awaitExpressions.forEach(n => { + params = params.addError(new EarlyError(n, 'Async function parameters must not contain await expressions')); + }); + } + body = body.clearSuperPropertyExpressions(); + params = params.clearSuperPropertyExpressions(); + params = params.clearNewTargetExpressions(); + body = body.clearNewTargetExpressions(); + if (isStrictFunctionBody(node.body)) { + params = params.enforceStrictErrors(); + body = body.enforceStrictErrors(); + } + let s = super.reduceMethod(node, { name, params, body }); + if (!isSimpleParameterList && isStrictFunctionBody(node.body)) { + s = s.addError(new EarlyError(node, 'Functions with non-simple parameter lists may not contain a "use strict" directive')); + } + s = s.clearYieldExpressions(); + s = s.clearAwaitExpressions(); + s = s.observeVarBoundary(); + return s; + } + + reduceModule() { + let s = super.reduceModule(...arguments); + s = s.functionDeclarationNamesAreLexical(); + s = s.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + s = s.enforceConflictingLexicallyDeclaredNames(s.varDeclaredNames, DUPLICATE_BINDING); + s.exportedNames.forEachEntry((nodes, bindingName) => { + if (nodes.length > 1) { + nodes.slice(1).forEach(dupeNode => { + s = s.addError(new EarlyError(dupeNode, `Duplicate export ${JSON.stringify(bindingName)}`)); + }); + } + }); + s.exportedBindings.forEachEntry((nodes, bindingName) => { + if (!s.lexicallyDeclaredNames.has(bindingName) && !s.varDeclaredNames.has(bindingName)) { + nodes.forEach(undeclaredNode => { + s = s.addError(new EarlyError(undeclaredNode, `Exported binding ${JSON.stringify(bindingName)} is not declared`)); + }); + } + }); + s.newTargetExpressions.forEach(node => { + s = s.addError(new EarlyError(node, 'new.target must be within function (but not arrow expression) code')); + }); + s = s.enforceFreeContinueStatementErrors(FREE_CONTINUE); + s = s.enforceFreeLabeledContinueStatementErrors(UNBOUND_CONTINUE); + s = s.enforceFreeBreakStatementErrors(FREE_BREAK); + s = s.enforceFreeLabeledBreakStatementErrors(UNBOUND_BREAK); + s = s.enforceSuperCallExpressions(SUPERCALL_ERROR); + s = s.enforceSuperPropertyExpressions(SUPERPROPERTY_ERROR); + s = s.enforceStrictErrors(); + return s; + } + + reduceNewTargetExpression(node) { + return this.identity.observeNewTargetExpression(node); + } + + reduceObjectExpression(node) { + let s = super.reduceObjectExpression(...arguments); + s = s.enforceSuperCallExpressionsInConstructorMethod(SUPERCALL_ERROR); + let protos = node.properties.filter(p => p.type === 'DataProperty' && p.name.type === 'StaticPropertyName' && p.name.value === '__proto__'); + protos.slice(1).forEach(n => { + s = s.addError(new EarlyError(n, 'Duplicate __proto__ property in object literal not allowed')); + }); + return s; + } + + reduceUpdateExpression() { + let s = super.reduceUpdateExpression(...arguments); + s = s.clearBoundNames(); + return s; + } + + reduceUnaryExpression(node) { + let s = super.reduceUnaryExpression(...arguments); + if (node.operator === 'delete' && node.operand.type === 'IdentifierExpression') { + s = s.addStrictError(new EarlyError(node, 'Identifier expressions must not be deleted in strict mode')); + } + return s; + } + + reduceScript(node) { + let s = super.reduceScript(...arguments); + s = s.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + s = s.enforceConflictingLexicallyDeclaredNames(s.varDeclaredNames, DUPLICATE_BINDING); + s.newTargetExpressions.forEach(n => { + s = s.addError(new EarlyError(n, 'new.target must be within function (but not arrow expression) code')); + }); + s = s.enforceFreeContinueStatementErrors(FREE_CONTINUE); + s = s.enforceFreeLabeledContinueStatementErrors(UNBOUND_CONTINUE); + s = s.enforceFreeBreakStatementErrors(FREE_BREAK); + s = s.enforceFreeLabeledBreakStatementErrors(UNBOUND_BREAK); + s = s.enforceSuperCallExpressions(SUPERCALL_ERROR); + s = s.enforceSuperPropertyExpressions(SUPERPROPERTY_ERROR); + if (isStrictFunctionBody(node)) { + s = s.enforceStrictErrors(); + } + return s; + } + + reduceSetter(node, { name, param, body }) { + let isSimpleParameterList = node.param.type === 'BindingIdentifier'; + param = param.observeLexicalDeclaration(); + param = param.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + body = body.enforceConflictingLexicallyDeclaredNames(param.lexicallyDeclaredNames, DUPLICATE_BINDING); + param = param.enforceSuperCallExpressions(SUPERCALL_ERROR); + body = body.enforceSuperCallExpressions(SUPERCALL_ERROR); + param = param.clearSuperPropertyExpressions(); + body = body.clearSuperPropertyExpressions(); + param = param.clearNewTargetExpressions(); + body = body.clearNewTargetExpressions(); + if (isStrictFunctionBody(node.body)) { + param = param.enforceStrictErrors(); + body = body.enforceStrictErrors(); + } + let s = super.reduceSetter(node, { name, param, body }); + if (!isSimpleParameterList && isStrictFunctionBody(node.body)) { + s = s.addError(new EarlyError(node, 'Functions with non-simple parameter lists may not contain a "use strict" directive')); + } + s = s.observeVarBoundary(); + return s; + } + + reduceStaticMemberExpression(node) { + let s = super.reduceStaticMemberExpression(...arguments); + if (node.object.type === 'Super') { + s = s.observeSuperPropertyExpression(node); + } + return s; + } + + reduceSwitchStatement(node, { discriminant, cases }) { + let sCases = this.append(...cases); + sCases = sCases.functionDeclarationNamesAreLexical(); + sCases = sCases.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + sCases = sCases.enforceConflictingLexicallyDeclaredNames(sCases.varDeclaredNames, DUPLICATE_BINDING); + sCases = sCases.observeLexicalBoundary(); + let s = this.append(discriminant, sCases); + s = s.clearFreeBreakStatements(); + return s; + } + + reduceSwitchStatementWithDefault(node, { discriminant, preDefaultCases, defaultCase, postDefaultCases }) { + let sCases = this.append(defaultCase, ...preDefaultCases, ...postDefaultCases); + sCases = sCases.functionDeclarationNamesAreLexical(); + sCases = sCases.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); + sCases = sCases.enforceConflictingLexicallyDeclaredNames(sCases.varDeclaredNames, DUPLICATE_BINDING); + sCases = sCases.observeLexicalBoundary(); + let s = this.append(discriminant, sCases); + s = s.clearFreeBreakStatements(); + return s; + } + + reduceVariableDeclaration(node) { + let s = super.reduceVariableDeclaration(...arguments); + switch (node.kind) { + case 'const': + case 'let': { + s = s.observeLexicalDeclaration(); + if (s.lexicallyDeclaredNames.has('let')) { + s.lexicallyDeclaredNames.get('let').forEach(n => { + s = s.addError(new EarlyError(n, 'Lexical declarations must not have a binding named "let"')); + }); + } + break; + } + case 'var': + s = s.observeVarDeclaration(); + break; + } + return s; + } + + reduceVariableDeclarationStatement(node) { + let s = super.reduceVariableDeclarationStatement(...arguments); + if (node.declaration.kind === 'const') { + node.declaration.declarators.forEach(declarator => { + if (declarator.init == null) { + s = s.addError(new EarlyError(declarator, 'Constant lexical declarations must have an initialiser')); + } + }); + } + return s; + } + + reduceWhileStatement(node) { + let s = super.reduceWhileStatement(...arguments); + if (isLabelledFunction(node.body)) { + s = s.addError(new EarlyError(node.body, 'The body of a while statement must not be a labeled function declaration')); + } + s = s.clearFreeContinueStatements().clearFreeBreakStatements(); + return s; + } + + reduceWithStatement(node) { + let s = super.reduceWithStatement(...arguments); + if (isLabelledFunction(node.body)) { + s = s.addError(new EarlyError(node.body, 'The body of a with statement must not be a labeled function declaration')); + } + s = s.addStrictError(new EarlyError(node, 'Strict mode code must not include a with statement')); + return s; + } + + reduceYieldExpression(node) { + let s = super.reduceYieldExpression(...arguments); + s = s.observeYieldExpression(node); + return s; + } + + reduceYieldGeneratorExpression(node) { + let s = super.reduceYieldGeneratorExpression(...arguments); + s = s.observeYieldExpression(node); + return s; + } + + + static check(node) { + return reduce(new EarlyErrorChecker, node).errors; + } +} diff --git a/repl/src/shift-parser/errors.js b/repl/src/shift-parser/errors.js new file mode 100644 index 00000000..11d84b01 --- /dev/null +++ b/repl/src/shift-parser/errors.js @@ -0,0 +1,119 @@ +/** + * Copyright 2014 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const ErrorMessages = { + UNEXPECTED_TOKEN(id) { + return `Unexpected token ${JSON.stringify(id)}`; + }, + UNEXPECTED_ILLEGAL_TOKEN(id) { + return `Unexpected ${JSON.stringify(id)}`; + }, + UNEXPECTED_ESCAPED_KEYWORD: 'Unexpected escaped keyword', + UNEXPECTED_NUMBER: 'Unexpected number', + UNEXPECTED_STRING: 'Unexpected string', + UNEXPECTED_IDENTIFIER: 'Unexpected identifier', + UNEXPECTED_RESERVED_WORD: 'Unexpected reserved word', + UNEXPECTED_TEMPLATE: 'Unexpected template', + UNEXPECTED_EOS: 'Unexpected end of input', + UNEXPECTED_LINE_TERMINATOR: 'Unexpected line terminator', + UNEXPECTED_COMMA_AFTER_REST: 'Unexpected comma after rest', + UNEXPECTED_REST_PARAMETERS_INITIALIZATION: 'Rest parameter may not have a default initializer', + NEWLINE_AFTER_THROW: 'Illegal newline after throw', + UNTERMINATED_REGEXP: 'Invalid regular expression: missing /', + INVALID_LAST_REST_PARAMETER: 'Rest parameter must be last formal parameter', + INVALID_REST_PARAMETERS_INITIALIZATION: 'Rest parameter may not have a default initializer', + INVALID_REGEXP_FLAGS: 'Invalid regular expression flags', + INVALID_REGEX: 'Invalid regular expression', + INVALID_LHS_IN_ASSIGNMENT: 'Invalid left-hand side in assignment', + INVALID_LHS_IN_BINDING: 'Invalid left-hand side in binding', // todo collapse messages? + INVALID_LHS_IN_FOR_IN: 'Invalid left-hand side in for-in', + INVALID_LHS_IN_FOR_OF: 'Invalid left-hand side in for-of', + INVALID_LHS_IN_FOR_AWAIT: 'Invalid left-hand side in for-await', + INVALID_UPDATE_OPERAND: 'Increment/decrement target must be an identifier or member expression', + INVALID_EXPONENTIATION_LHS: 'Unary expressions as the left operand of an exponentation expression ' + + 'must be disambiguated with parentheses', + MULTIPLE_DEFAULTS_IN_SWITCH: 'More than one default clause in switch statement', + NO_CATCH_OR_FINALLY: 'Missing catch or finally after try', + ILLEGAL_RETURN: 'Illegal return statement', + ILLEGAL_ARROW_FUNCTION_PARAMS: 'Illegal arrow function parameter list', + INVALID_ASYNC_PARAMS: 'Async function parameters must not contain await expressions', + INVALID_VAR_INIT_FOR_IN: 'Invalid variable declaration in for-in statement', + INVALID_VAR_INIT_FOR_OF: 'Invalid variable declaration in for-of statement', + INVALID_VAR_INIT_FOR_AWAIT: 'Invalid variable declaration in for-await statement', + UNINITIALIZED_BINDINGPATTERN_IN_FOR_INIT: 'Binding pattern appears without initializer in for statement init', + ILLEGAL_PROPERTY: 'Illegal property initializer', + INVALID_ID_BINDING_STRICT_MODE(id) { + return `The identifier ${JSON.stringify(id)} must not be in binding position in strict mode`; + }, + INVALID_ID_IN_LABEL_STRICT_MODE(id) { + return `The identifier ${JSON.stringify(id)} must not be in label position in strict mode`; + }, + INVALID_ID_IN_EXPRESSION_STRICT_MODE(id) { + return `The identifier ${JSON.stringify(id)} must not be in expression position in strict mode`; + }, + INVALID_CALL_TO_SUPER: 'Calls to super must be in the "constructor" method of a class expression ' + + 'or class declaration that has a superclass', + INVALID_DELETE_STRICT_MODE: 'Identifier expressions must not be deleted in strict mode', + DUPLICATE_BINDING(id) { + return `Duplicate binding ${JSON.stringify(id)}`; + }, + ILLEGAL_ID_IN_LEXICAL_DECLARATION(id) { + return `Lexical declarations must not have a binding named ${JSON.stringify(id)}`; + }, + UNITIALIZED_CONST: 'Constant lexical declarations must have an initialiser', + ILLEGAL_LABEL_IN_BODY(stmt) { + return `The body of a ${stmt} statement must not be a labeled function declaration`; + }, + ILLEGEAL_LABEL_IN_IF: 'The consequent of an if statement must not be a labeled function declaration', + ILLEGAL_LABEL_IN_ELSE: 'The alternate of an if statement must not be a labeled function declaration', + ILLEGAL_CONTINUE_WITHOUT_ITERATION_WITH_ID(id) { + return `Continue statement must be nested within an iteration statement with label ${JSON.stringify(id)}`; + }, + ILLEGAL_CONTINUE_WITHOUT_ITERATION: 'Continue statement must be nested within an iteration statement', + ILLEGAL_BREAK_WITHOUT_ITERATION_OR_SWITCH: + 'Break statement must be nested within an iteration statement or a switch statement', + ILLEGAL_WITH_STRICT_MODE: 'Strict mode code must not include a with statement', + ILLEGAL_ACCESS_SUPER_MEMBER: 'Member access on super must be in a method', + ILLEGAL_SUPER_CALL: 'Calls to super must be in the "constructor" method of a class expression or class declaration that has a superclass', + DUPLICATE_LABEL_DECLARATION(label) { + return `Label ${JSON.stringify(label)} has already been declared`; + }, + ILLEGAL_BREAK_WITHIN_LABEL(label) { + return `Break statement must be nested within a statement with label ${JSON.stringify(label)}`; + }, + ILLEGAL_YIELD_EXPRESSIONS(paramType) { + return `${paramType} parameters must not contain yield expressions`; + }, + ILLEGAL_YIELD_IDENTIFIER: '"yield" may not be used as an identifier in this context', + ILLEGAL_AWAIT_IDENTIFIER: '"await" may not be used as an identifier in this context', + DUPLICATE_CONSTRUCTOR: 'Duplicate constructor method in class', + ILLEGAL_CONSTRUCTORS: 'Constructors cannot be async, generators, getters or setters', + ILLEGAL_STATIC_CLASS_NAME: 'Static class methods cannot be named "prototype"', + NEW_TARGET_ERROR: 'new.target must be within function (but not arrow expression) code', + DUPLICATE_EXPORT(id) { + return `Duplicate export ${JSON.stringify(id)}`; + }, + UNDECLARED_BINDING(id) { + return `Exported binding ${JSON.stringify(id)} is not declared`; + }, + DUPLICATE_PROPTO_PROP: 'Duplicate __proto__ property in object literal not allowed', + ILLEGAL_LABEL_FUNC_DECLARATION: 'Labeled FunctionDeclarations are disallowed in strict mode', + ILLEGAL_FUNC_DECL_IF: 'FunctionDeclarations in IfStatements are disallowed in strict mode', + ILLEGAL_USE_STRICT: 'Functions with non-simple parameter lists may not contain a "use strict" directive', + ILLEGAL_EXPORTED_NAME: 'Names of variables used in an export specifier from the current module must be identifiers', + NO_OCTALS_IN_TEMPLATES: 'Template literals may not contain octal escape sequences', + NO_AWAIT_IN_ASYNC_PARAMS: 'Async arrow parameters may not contain "await"', +}; diff --git a/repl/src/shift-parser/index.js b/repl/src/shift-parser/index.js new file mode 100644 index 00000000..a372a939 --- /dev/null +++ b/repl/src/shift-parser/index.js @@ -0,0 +1,149 @@ +/** + * Copyright 2016 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GenericParser } from './parser'; +import { JsError } from './tokenizer'; +import { EarlyErrorChecker } from './early-errors'; +import { isLineTerminator } from './utils'; + +class ParserWithLocation extends GenericParser { + constructor(source) { + super(source); + this.locations = new WeakMap; + this.comments = []; + } + + startNode() { + return this.getLocation(); + } + + finishNode(node, start) { + if (node.type === 'Script' || node.type === 'Module') { + this.locations.set(node, { + start: { line: 1, column: 0, offset: 0 }, + end: this.getLocation(), + }); + return node; + } + if (node.type === 'TemplateExpression') { + // Adjust TemplateElements to not include surrounding backticks or braces + for (let i = 0; i < node.elements.length; i += 2) { + const endAdjustment = i < node.elements.length - 1 ? 2 : 1; // discard '${' or '`' respectively + const element = node.elements[i]; + const location = this.locations.get(element); + this.locations.set(element, { + start: { line: location.start.line, column: location.start.column + 1, offset: location.start.offset + 1 }, // discard '}' or '`' + end: { line: location.end.line, column: location.end.column - endAdjustment, offset: location.end.offset - endAdjustment }, + }); + } + } + this.locations.set(node, { + start, + end: this.getLastTokenEndLocation(), + }); + return node; + } + + copyNode(src, dest) { + this.locations.set(dest, this.locations.get(src)); // todo check undefined + return dest; + } + + skipSingleLineComment(offset) { + // We're actually extending the *tokenizer*, here. + const start = { + line: this.line + 1, + column: this.index - this.lineStart, + offset: this.index, + }; + const c = this.source[this.index]; + const type = c === '/' ? 'SingleLine' : c === '<' ? 'HTMLOpen' : 'HTMLClose'; + + super.skipSingleLineComment(offset); + + const end = { + line: this.line + 1, + column: this.index - this.lineStart, + offset: this.index, + }; + const trailingLineTerminatorCharacters = this.source[this.index - 2] === '\r' ? 2 : isLineTerminator(this.source.charCodeAt(this.index - 1)) ? 1 : 0; + const text = this.source.substring(start.offset + offset, end.offset - trailingLineTerminatorCharacters); + + this.comments.push({ text, type, start, end }); + } + + skipMultiLineComment() { + const start = { + line: this.line + 1, + column: this.index - this.lineStart, + offset: this.index, + }; + const type = 'MultiLine'; + + const retval = super.skipMultiLineComment(); + + const end = { + line: this.line + 1, + column: this.index - this.lineStart, + offset: this.index, + }; + const text = this.source.substring(start.offset + 2, end.offset - 2); + + this.comments.push({ text, type, start, end }); + + return retval; + } +} + +function generateInterface(parsingFunctionName) { + return function parse(code, { earlyErrors = true } = {}) { + let parser = new GenericParser(code); + let tree = parser[parsingFunctionName](); + if (earlyErrors) { + let errors = EarlyErrorChecker.check(tree); + // for now, just throw the first error; we will handle multiple errors later + if (errors.length > 0) { + throw new JsError(0, 1, 0, errors[0].message); + } + } + return tree; + }; +} + +function generateInterfaceWithLocation(parsingFunctionName) { + return function parse(code, { earlyErrors = true } = {}) { + let parser = new ParserWithLocation(code); + let tree = parser[parsingFunctionName](); + if (earlyErrors) { + let errors = EarlyErrorChecker.check(tree); + // for now, just throw the first error; we will handle multiple errors later + if (errors.length > 0) { + let { node, message } = errors[0]; + let { offset, line, column } = parser.locations.get(node).start; + throw new JsError(offset, line, column, message); + } + } + return { tree, locations: parser.locations, comments: parser.comments }; + }; +} + +export const parseModule = generateInterface('parseModule'); +export const parseScript = generateInterface('parseScript'); +export const parseModuleWithLocation = generateInterfaceWithLocation('parseModule'); +export const parseScriptWithLocation = generateInterfaceWithLocation('parseScript'); +export default parseScript; +export { EarlyErrorChecker, GenericParser, ParserWithLocation }; +export { default as Tokenizer, TokenClass, TokenType } from './tokenizer'; diff --git a/repl/src/shift-parser/parser.js b/repl/src/shift-parser/parser.js new file mode 100644 index 00000000..71da79c9 --- /dev/null +++ b/repl/src/shift-parser/parser.js @@ -0,0 +1,2641 @@ +/** + * Copyright 2014 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ErrorMessages } from './errors'; + +import acceptRegex from 'shift-regexp-acceptor'; + +import Tokenizer, { TokenClass, TokenType } from './tokenizer'; + +import * as AST from 'shift-ast'; + +// Empty parameter list for ArrowExpression +const ARROW_EXPRESSION_PARAMS = 'CoverParenthesizedExpressionAndArrowParameterList'; +const EXPORT_UNKNOWN_SPECIFIER = 'ExportNameOfUnknownType'; + +const Precedence = { + Sequence: 0, + Yield: 1, + Assignment: 1, + Conditional: 2, + ArrowFunction: 2, + LogicalOR: 3, + LogicalAND: 4, + BitwiseOR: 5, + BitwiseXOR: 6, + BitwiseAND: 7, + Equality: 8, + Relational: 9, + BitwiseSHIFT: 10, + Additive: 11, + Multiplicative: 12, + Unary: 13, + Postfix: 14, + Call: 15, + New: 16, + TaggedTemplate: 17, + Member: 18, + Primary: 19, +}; + +const BinaryPrecedence = { + '||': Precedence.LogicalOR, + '&&': Precedence.LogicalAND, + '|': Precedence.BitwiseOR, + '^': Precedence.BitwiseXOR, + '&': Precedence.BitwiseAND, + '==': Precedence.Equality, + '!=': Precedence.Equality, + '===': Precedence.Equality, + '!==': Precedence.Equality, + '<': Precedence.Relational, + '>': Precedence.Relational, + '<=': Precedence.Relational, + '>=': Precedence.Relational, + 'in': Precedence.Relational, + 'instanceof': Precedence.Relational, + '<<': Precedence.BitwiseSHIFT, + '>>': Precedence.BitwiseSHIFT, + '>>>': Precedence.BitwiseSHIFT, + '+': Precedence.Additive, + '-': Precedence.Additive, + '*': Precedence.Multiplicative, + '%': Precedence.Multiplicative, + '/': Precedence.Multiplicative, +}; + +function isValidSimpleAssignmentTarget(node) { + if (node == null) return false; + switch (node.type) { + case 'IdentifierExpression': + case 'ComputedMemberExpression': + case 'StaticMemberExpression': + return true; + } + return false; +} + +function isPrefixOperator(token) { + switch (token.type) { + case TokenType.INC: + case TokenType.DEC: + case TokenType.ADD: + case TokenType.SUB: + case TokenType.BIT_NOT: + case TokenType.NOT: + case TokenType.DELETE: + case TokenType.VOID: + case TokenType.TYPEOF: + return true; + } + return false; +} + +function isUpdateOperator(token) { + return token.type === TokenType.INC || token.type === TokenType.DEC; +} + +export class GenericParser extends Tokenizer { + constructor(source) { + super(source); + this.allowIn = true; + this.inFunctionBody = false; + this.inParameter = false; + this.allowYieldExpression = false; + this.allowAwaitExpression = false; + this.firstAwaitLocation = null; // for forbidding `await` in async arrow params. + this.module = false; + this.moduleIsTheGoalSymbol = false; + this.strict = false; + + // Cover grammar + this.isBindingElement = true; + this.isAssignmentTarget = true; + this.firstExprError = null; + } + + match(subType) { + return this.lookahead.type === subType; + } + + matchIdentifier() { + switch (this.lookahead.type) { + case TokenType.IDENTIFIER: + case TokenType.LET: + case TokenType.YIELD: + case TokenType.ASYNC: + return true; + case TokenType.AWAIT: + if (!this.moduleIsTheGoalSymbol) { + if (this.firstAwaitLocation === null) { + this.firstAwaitLocation = this.getLocation(); + } + return true; + } + return false; + case TokenType.ESCAPED_KEYWORD: + if (this.lookahead.value === 'await' && !this.moduleIsTheGoalSymbol) { + if (this.firstAwaitLocation === null) { + this.firstAwaitLocation = this.getLocation(); + } + return true; + } + return this.lookahead.value === 'let' + || this.lookahead.value === 'yield' + || this.lookahead.value === 'async'; + } + return false; + } + + eat(tokenType) { + if (this.lookahead.type === tokenType) { + return this.lex(); + } + return null; + } + + expect(tokenType) { + if (this.lookahead.type === tokenType) { + return this.lex(); + } + throw this.createUnexpected(this.lookahead); + } + + matchContextualKeyword(keyword) { + return this.lookahead.type === TokenType.IDENTIFIER && !this.lookahead.escaped && this.lookahead.value === keyword; + } + + expectContextualKeyword(keyword) { + if (this.lookahead.type === TokenType.IDENTIFIER && !this.lookahead.escaped && this.lookahead.value === keyword) { + return this.lex(); + } + throw this.createUnexpected(this.lookahead); + } + + eatContextualKeyword(keyword) { + if (this.lookahead.type === TokenType.IDENTIFIER && !this.lookahead.escaped && this.lookahead.value === keyword) { + return this.lex(); + } + return null; + } + + consumeSemicolon() { + if (this.eat(TokenType.SEMICOLON)) return; + if (this.hasLineTerminatorBeforeNext) return; + if (!this.eof() && !this.match(TokenType.RBRACE)) { + throw this.createUnexpected(this.lookahead); + } + } + + // this is a no-op, reserved for future use + startNode(node) { + return node; + } + + copyNode(src, dest) { + return dest; + } + + finishNode(node /* , startState */) { + return node; + } + + parseModule() { + this.moduleIsTheGoalSymbol = this.module = this.strict = true; + this.lookahead = this.advance(); + + let startState = this.startNode(); + let { directives, statements } = this.parseBody(); + if (!this.match(TokenType.EOS)) { + throw this.createUnexpected(this.lookahead); + } + return this.finishNode(new AST.Module({ directives, items: statements }), startState); + } + + parseScript() { + this.lookahead = this.advance(); + + let startState = this.startNode(); + let { directives, statements } = this.parseBody(); + if (!this.match(TokenType.EOS)) { + throw this.createUnexpected(this.lookahead); + } + return this.finishNode(new AST.Script({ directives, statements }), startState); + } + + parseFunctionBody() { + let oldInFunctionBody = this.inFunctionBody; + let oldModule = this.module; + let oldStrict = this.strict; + this.inFunctionBody = true; + this.module = false; + + let startState = this.startNode(); + this.expect(TokenType.LBRACE); + let body = new AST.FunctionBody(this.parseBody()); + this.expect(TokenType.RBRACE); + body = this.finishNode(body, startState); + + this.inFunctionBody = oldInFunctionBody; + this.module = oldModule; + this.strict = oldStrict; + + return body; + } + + parseBody() { + let directives = [], statements = [], parsingDirectives = true, directiveOctal = null; + + while (true) { + if (this.eof() || this.match(TokenType.RBRACE)) break; + let token = this.lookahead; + let text = token.slice.text; + let isStringLiteral = token.type === TokenType.STRING; + let isModule = this.module; + let directiveLocation = this.getLocation(); + let directiveStartState = this.startNode(); + let stmt = isModule ? this.parseModuleItem() : this.parseStatementListItem(); + if (parsingDirectives) { + if (isStringLiteral && stmt.type === 'ExpressionStatement' && stmt.expression.type === 'LiteralStringExpression') { + if (!directiveOctal && token.octal) { + directiveOctal = this.createErrorWithLocation(directiveLocation, 'Unexpected legacy octal escape sequence: \\' + token.octal); + } + let rawValue = text.slice(1, -1); + if (rawValue === 'use strict') { + this.strict = true; + } + directives.push(this.finishNode(new AST.Directive({ rawValue }), directiveStartState)); + } else { + parsingDirectives = false; + if (directiveOctal && this.strict) { + throw directiveOctal; + } + statements.push(stmt); + } + } else { + statements.push(stmt); + } + } + if (directiveOctal && this.strict) { + throw directiveOctal; + } + + return { directives, statements }; + } + + parseImportSpecifier() { + let startState = this.startNode(), name; + if (this.matchIdentifier()) { + name = this.parseIdentifier(); + if (!this.eatContextualKeyword('as')) { + return this.finishNode(new AST.ImportSpecifier({ + name: null, + binding: this.finishNode(new AST.BindingIdentifier({ name }), startState), + }), startState); + } + } else if (this.lookahead.type.klass.isIdentifierName) { + name = this.parseIdentifierName(); + this.expectContextualKeyword('as'); + } + + return this.finishNode(new AST.ImportSpecifier({ name, binding: this.parseBindingIdentifier() }), startState); + } + + parseNameSpaceBinding() { + this.expect(TokenType.MUL); + this.expectContextualKeyword('as'); + return this.parseBindingIdentifier(); + } + + parseNamedImports() { + let result = []; + this.expect(TokenType.LBRACE); + while (!this.eat(TokenType.RBRACE)) { + result.push(this.parseImportSpecifier()); + if (!this.eat(TokenType.COMMA)) { + this.expect(TokenType.RBRACE); + break; + } + } + return result; + } + + parseFromClause() { + this.expectContextualKeyword('from'); + let value = this.expect(TokenType.STRING).str; + return value; + } + + parseImportDeclaration() { + let startState = this.startNode(), defaultBinding = null, moduleSpecifier; + this.expect(TokenType.IMPORT); + if (this.match(TokenType.STRING)) { + moduleSpecifier = this.lex().str; + this.consumeSemicolon(); + return this.finishNode(new AST.Import({ defaultBinding: null, namedImports: [], moduleSpecifier }), startState); + } + if (this.matchIdentifier()) { + defaultBinding = this.parseBindingIdentifier(); + if (!this.eat(TokenType.COMMA)) { + let decl = new AST.Import({ defaultBinding, namedImports: [], moduleSpecifier: this.parseFromClause() }); + this.consumeSemicolon(); + return this.finishNode(decl, startState); + } + } + if (this.match(TokenType.MUL)) { + let decl = new AST.ImportNamespace({ + defaultBinding, + namespaceBinding: this.parseNameSpaceBinding(), + moduleSpecifier: this.parseFromClause(), + }); + this.consumeSemicolon(); + return this.finishNode(decl, startState); + } else if (this.match(TokenType.LBRACE)) { + let decl = new AST.Import({ + defaultBinding, + namedImports: this.parseNamedImports(), + moduleSpecifier: this.parseFromClause(), + }); + this.consumeSemicolon(); + return this.finishNode(decl, startState); + } + throw this.createUnexpected(this.lookahead); + } + + parseExportSpecifier() { + let startState = this.startNode(); + let name = this.finishNode({ type: EXPORT_UNKNOWN_SPECIFIER, isIdentifier: this.matchIdentifier(), value: this.parseIdentifierName() }, startState); + if (this.eatContextualKeyword('as')) { + let exportedName = this.parseIdentifierName(); + return this.finishNode({ name, exportedName }, startState); + } + return this.finishNode({ name, exportedName: null }, startState); + } + + parseExportClause() { + this.expect(TokenType.LBRACE); + let result = []; + while (!this.eat(TokenType.RBRACE)) { + result.push(this.parseExportSpecifier()); + if (!this.eat(TokenType.COMMA)) { + this.expect(TokenType.RBRACE); + break; + } + } + return result; + } + + parseExportDeclaration() { + let startState = this.startNode(), decl; + this.expect(TokenType.EXPORT); + switch (this.lookahead.type) { + case TokenType.MUL: + this.lex(); + // export * FromClause ; + decl = new AST.ExportAllFrom({ moduleSpecifier: this.parseFromClause() }); + this.consumeSemicolon(); + break; + case TokenType.LBRACE: { + // export ExportClause FromClause ; + // export ExportClause ; + let namedExports = this.parseExportClause(); + let moduleSpecifier = null; + if (this.matchContextualKeyword('from')) { + moduleSpecifier = this.parseFromClause(); + decl = new AST.ExportFrom({ namedExports: namedExports.map(e => this.copyNode(e, new AST.ExportFromSpecifier({ name: e.name.value, exportedName: e.exportedName }))), moduleSpecifier }); + } else { + namedExports.forEach(({ name }) => { + if (!name.isIdentifier) { + throw this.createError(ErrorMessages.ILLEGAL_EXPORTED_NAME); + } + }); + decl = new AST.ExportLocals({ namedExports: namedExports.map(e => this.copyNode(e, new AST.ExportLocalSpecifier({ name: this.copyNode(e.name, new AST.IdentifierExpression({ name: e.name.value })), exportedName: e.exportedName }))) }); + } + this.consumeSemicolon(); + break; + } + case TokenType.CLASS: + // export ClassDeclaration + decl = new AST.Export({ declaration: this.parseClass({ isExpr: false, inDefault: false }) }); + break; + case TokenType.FUNCTION: + // export HoistableDeclaration + decl = new AST.Export({ declaration: this.parseFunction({ isExpr: false, inDefault: false, allowGenerator: true, isAsync: false }) }); + break; + case TokenType.ASYNC: { + let preAsyncStartState = this.startNode(); + this.lex(); + decl = new AST.Export({ declaration: this.parseFunction({ isExpr: false, inDefault: false, allowGenerator: true, isAsync: true, startState: preAsyncStartState }) }); + break; + } + case TokenType.DEFAULT: + this.lex(); + switch (this.lookahead.type) { + case TokenType.FUNCTION: + // export default HoistableDeclaration[Default] + decl = new AST.ExportDefault({ + body: this.parseFunction({ isExpr: false, inDefault: true, allowGenerator: true, isAsync: false }), + }); + break; + case TokenType.CLASS: + // export default ClassDeclaration[Default] + decl = new AST.ExportDefault({ body: this.parseClass({ isExpr: false, inDefault: true }) }); + break; + case TokenType.ASYNC: { + let preAsyncStartState = this.startNode(); + let lexerState = this.saveLexerState(); + this.lex(); + if (!this.hasLineTerminatorBeforeNext && this.match(TokenType.FUNCTION)) { + decl = new AST.ExportDefault({ + body: this.parseFunction({ isExpr: false, inDefault: true, allowGenerator: false, isAsync: true, startState: preAsyncStartState }), + }); + break; + } + this.restoreLexerState(lexerState); + } + // else fall through + default: + // export default [lookahead ∉ {function, async [no LineTerminatorHere] function, class}] AssignmentExpression[In] ; + decl = new AST.ExportDefault({ body: this.parseAssignmentExpression() }); + this.consumeSemicolon(); + break; + } + break; + case TokenType.VAR: + case TokenType.LET: + case TokenType.CONST: + // export LexicalDeclaration + decl = new AST.Export({ declaration: this.parseVariableDeclaration(true) }); + this.consumeSemicolon(); + break; + default: + throw this.createUnexpected(this.lookahead); + } + return this.finishNode(decl, startState); + } + + parseModuleItem() { + switch (this.lookahead.type) { + case TokenType.IMPORT: + return this.parseImportDeclaration(); + case TokenType.EXPORT: + return this.parseExportDeclaration(); + default: + return this.parseStatementListItem(); + } + } + + lookaheadLexicalDeclaration() { + if (this.match(TokenType.LET) || this.match(TokenType.CONST)) { + let lexerState = this.saveLexerState(); + this.lex(); + if ( + this.matchIdentifier() || + this.match(TokenType.LBRACE) || + this.match(TokenType.LBRACK) + ) { + this.restoreLexerState(lexerState); + return true; + } + this.restoreLexerState(lexerState); + } + return false; + } + + parseStatementListItem() { + if (this.eof()) throw this.createUnexpected(this.lookahead); + + switch (this.lookahead.type) { + case TokenType.FUNCTION: + return this.parseFunction({ isExpr: false, inDefault: false, allowGenerator: true, isAsync: false }); + case TokenType.CLASS: + return this.parseClass({ isExpr: false, inDefault: false }); + case TokenType.ASYNC: { + let preAsyncStartState = this.getLocation(); + let lexerState = this.saveLexerState(); + this.lex(); + if (!this.hasLineTerminatorBeforeNext && this.match(TokenType.FUNCTION)) { + return this.parseFunction({ isExpr: false, inDefault: false, allowGenerator: true, isAsync: true, startState: preAsyncStartState }); + } + this.restoreLexerState(lexerState); + return this.parseStatement(); + } + default: + if (this.lookaheadLexicalDeclaration()) { + let startState = this.startNode(); + return this.finishNode(this.parseVariableDeclarationStatement(), startState); + } + return this.parseStatement(); + } + } + + parseStatement() { + let startState = this.startNode(); + let stmt = this.isolateCoverGrammar(this.parseStatementHelper); + return this.finishNode(stmt, startState); + } + + parseStatementHelper() { + if (this.eof()) { + throw this.createUnexpected(this.lookahead); + } + + switch (this.lookahead.type) { + case TokenType.SEMICOLON: + return this.parseEmptyStatement(); + case TokenType.LBRACE: + return this.parseBlockStatement(); + case TokenType.LPAREN: + return this.parseExpressionStatement(); + case TokenType.BREAK: + return this.parseBreakStatement(); + case TokenType.CONTINUE: + return this.parseContinueStatement(); + case TokenType.DEBUGGER: + return this.parseDebuggerStatement(); + case TokenType.DO: + return this.parseDoWhileStatement(); + case TokenType.FOR: + return this.parseForStatement(); + case TokenType.IF: + return this.parseIfStatement(); + case TokenType.RETURN: + return this.parseReturnStatement(); + case TokenType.SWITCH: + return this.parseSwitchStatement(); + case TokenType.THROW: + return this.parseThrowStatement(); + case TokenType.TRY: + return this.parseTryStatement(); + case TokenType.VAR: + return this.parseVariableDeclarationStatement(); + case TokenType.WHILE: + return this.parseWhileStatement(); + case TokenType.WITH: + return this.parseWithStatement(); + case TokenType.FUNCTION: + case TokenType.CLASS: + throw this.createUnexpected(this.lookahead); + + default: { + let lexerState = this.saveLexerState(); + if (this.eat(TokenType.LET)) { + if (this.match(TokenType.LBRACK)) { + this.restoreLexerState(lexerState); + throw this.createUnexpected(this.lookahead); + } + this.restoreLexerState(lexerState); + } else if (this.eat(TokenType.ASYNC)) { + if (!this.hasLineTerminatorBeforeNext && this.match(TokenType.FUNCTION)) { + throw this.createUnexpected(this.lookahead); + } + this.restoreLexerState(lexerState); + } + let expr = this.parseExpression(); + // 12.12 Labelled Statements; + if (expr.type === 'IdentifierExpression' && this.eat(TokenType.COLON)) { + let labeledBody = this.match(TokenType.FUNCTION) + ? this.parseFunction({ isExpr: false, inDefault: false, allowGenerator: false, isAsync: false }) + : this.parseStatement(); + return new AST.LabeledStatement({ label: expr.name, body: labeledBody }); + } + this.consumeSemicolon(); + return new AST.ExpressionStatement({ expression: expr }); + } + } + } + + parseEmptyStatement() { + this.lex(); + return new AST.EmptyStatement; + } + + parseBlockStatement() { + return new AST.BlockStatement({ block: this.parseBlock() }); + } + + parseExpressionStatement() { + let expr = this.parseExpression(); + this.consumeSemicolon(); + return new AST.ExpressionStatement({ expression: expr }); + } + + parseBreakStatement() { + this.lex(); + + // Catch the very common case first: immediately a semicolon (U+003B). + if (this.eat(TokenType.SEMICOLON) || this.hasLineTerminatorBeforeNext) { + return new AST.BreakStatement({ label: null }); + } + + let label = null; + if (this.matchIdentifier()) { + label = this.parseIdentifier(); + } + + this.consumeSemicolon(); + + return new AST.BreakStatement({ label }); + } + + parseContinueStatement() { + this.lex(); + + // Catch the very common case first: immediately a semicolon (U+003B). + if (this.eat(TokenType.SEMICOLON) || this.hasLineTerminatorBeforeNext) { + return new AST.ContinueStatement({ label: null }); + } + + let label = null; + if (this.matchIdentifier()) { + label = this.parseIdentifier(); + } + + this.consumeSemicolon(); + + return new AST.ContinueStatement({ label }); + } + + + parseDebuggerStatement() { + this.lex(); + this.consumeSemicolon(); + return new AST.DebuggerStatement; + } + + parseDoWhileStatement() { + this.lex(); + let body = this.parseStatement(); + this.expect(TokenType.WHILE); + this.expect(TokenType.LPAREN); + let test = this.parseExpression(); + this.expect(TokenType.RPAREN); + this.eat(TokenType.SEMICOLON); + return new AST.DoWhileStatement({ body, test }); + } + + parseForStatement() { + this.lex(); + let isAwait = this.allowAwaitExpression && this.eat(TokenType.AWAIT); + this.expect(TokenType.LPAREN); + let test = null; + let right = null; + if (isAwait && this.match(TokenType.SEMICOLON)) { + throw this.createUnexpected(this.lookahead); + } + if (this.eat(TokenType.SEMICOLON)) { + if (!this.match(TokenType.SEMICOLON)) { + test = this.parseExpression(); + } + this.expect(TokenType.SEMICOLON); + if (!this.match(TokenType.RPAREN)) { + right = this.parseExpression(); + } + return new AST.ForStatement({ init: null, test, update: right, body: this.getIteratorStatementEpilogue() }); + } + let startsWithLet = this.match(TokenType.LET); + let isForDecl = this.lookaheadLexicalDeclaration(); + let leftStartState = this.startNode(); + if (this.match(TokenType.VAR) || isForDecl) { + let previousAllowIn = this.allowIn; + this.allowIn = false; + let init = this.parseVariableDeclaration(false); + this.allowIn = previousAllowIn; + + if (init.declarators.length === 1 && (this.match(TokenType.IN) || this.matchContextualKeyword('of'))) { + let ctor; + let decl = init.declarators[0]; + + if (this.match(TokenType.IN)) { + if (isAwait) { + throw this.createUnexpected(this.lookahead); + } + if (decl.init !== null && (this.strict || init.kind !== 'var' || decl.binding.type !== 'BindingIdentifier')) { + throw this.createError(ErrorMessages.INVALID_VAR_INIT_FOR_IN); + } + ctor = AST.ForInStatement; + this.lex(); + right = this.parseExpression(); + } else { + if (decl.init !== null) { + throw this.createError(isAwait ? ErrorMessages.INVALID_VAR_INIT_FOR_AWAIT : ErrorMessages.INVALID_VAR_INIT_FOR_OF); + } + if (isAwait) { + ctor = AST.ForAwaitStatement; + } else { + ctor = AST.ForOfStatement; + } + this.lex(); + right = this.parseAssignmentExpression(); + } + + let body = this.getIteratorStatementEpilogue(); + + return new ctor({ left: init, right, body }); + } else if (isAwait) { + throw this.createUnexpected(this.lookahead); + } + this.expect(TokenType.SEMICOLON); + if (init.declarators.some(decl => decl.binding.type !== 'BindingIdentifier' && decl.init === null)) { + throw this.createError(ErrorMessages.UNINITIALIZED_BINDINGPATTERN_IN_FOR_INIT); + } + if (!this.match(TokenType.SEMICOLON)) { + test = this.parseExpression(); + } + this.expect(TokenType.SEMICOLON); + if (!this.match(TokenType.RPAREN)) { + right = this.parseExpression(); + } + return new AST.ForStatement({ init, test, update: right, body: this.getIteratorStatementEpilogue() }); + + } + let previousAllowIn = this.allowIn; + this.allowIn = false; + let expr = this.inheritCoverGrammar(this.parseAssignmentExpressionOrTarget); + this.allowIn = previousAllowIn; + + if (this.isAssignmentTarget && expr.type !== 'AssignmentExpression' && (this.match(TokenType.IN) || this.matchContextualKeyword('of'))) { + if (expr.type === 'ObjectAssignmentTarget' || expr.type === 'ArrayAssignmentTarget') { + this.firstExprError = null; + } + if (startsWithLet && this.matchContextualKeyword('of')) { + throw this.createError(isAwait ? ErrorMessages.INVALID_LHS_IN_FOR_AWAIT : ErrorMessages.INVALID_LHS_IN_FOR_OF); + } + let ctor; + if (this.match(TokenType.IN)) { + if (isAwait) { + throw this.createUnexpected(this.lookahead); + } + ctor = AST.ForInStatement; + this.lex(); + right = this.parseExpression(); + } else { + if (isAwait) { + ctor = AST.ForAwaitStatement; + } else { + ctor = AST.ForOfStatement; + } + this.lex(); + right = this.parseAssignmentExpression(); + } + + return new ctor({ left: this.transformDestructuring(expr), right, body: this.getIteratorStatementEpilogue() }); + } else if (isAwait) { + throw this.createError(ErrorMessages.INVALID_LHS_IN_FOR_AWAIT); + } + if (this.firstExprError) { + throw this.firstExprError; + } + while (this.eat(TokenType.COMMA)) { + let rhs = this.parseAssignmentExpression(); + expr = this.finishNode(new AST.BinaryExpression({ left: expr, operator: ',', right: rhs }), leftStartState); + } + if (this.match(TokenType.IN)) { + throw this.createError(ErrorMessages.INVALID_LHS_IN_FOR_IN); + } + if (this.matchContextualKeyword('of')) { + throw this.createError(ErrorMessages.INVALID_LHS_IN_FOR_OF); + } + this.expect(TokenType.SEMICOLON); + if (!this.match(TokenType.SEMICOLON)) { + test = this.parseExpression(); + } + this.expect(TokenType.SEMICOLON); + if (!this.match(TokenType.RPAREN)) { + right = this.parseExpression(); + } + return new AST.ForStatement({ init: expr, test, update: right, body: this.getIteratorStatementEpilogue() }); + } + + getIteratorStatementEpilogue() { + this.expect(TokenType.RPAREN); + let body = this.parseStatement(); + return body; + } + + parseIfStatementChild() { + return this.match(TokenType.FUNCTION) + ? this.parseFunction({ isExpr: false, inDefault: false, allowGenerator: false, isAsync: false }) + : this.parseStatement(); + } + + parseIfStatement() { + this.lex(); + this.expect(TokenType.LPAREN); + let test = this.parseExpression(); + this.expect(TokenType.RPAREN); + let consequent = this.parseIfStatementChild(); + let alternate = null; + if (this.eat(TokenType.ELSE)) { + alternate = this.parseIfStatementChild(); + } + return new AST.IfStatement({ test, consequent, alternate }); + } + + parseReturnStatement() { + if (!this.inFunctionBody) { + throw this.createError(ErrorMessages.ILLEGAL_RETURN); + } + + this.lex(); + + // Catch the very common case first: immediately a semicolon (U+003B). + if (this.eat(TokenType.SEMICOLON) || this.hasLineTerminatorBeforeNext) { + return new AST.ReturnStatement({ expression: null }); + } + + let expression = null; + if (!this.match(TokenType.RBRACE) && !this.eof()) { + expression = this.parseExpression(); + } + + this.consumeSemicolon(); + return new AST.ReturnStatement({ expression }); + } + + parseSwitchStatement() { + this.lex(); + this.expect(TokenType.LPAREN); + let discriminant = this.parseExpression(); + this.expect(TokenType.RPAREN); + this.expect(TokenType.LBRACE); + + if (this.eat(TokenType.RBRACE)) { + return new AST.SwitchStatement({ discriminant, cases: [] }); + } + + let cases = this.parseSwitchCases(); + if (this.match(TokenType.DEFAULT)) { + let defaultCase = this.parseSwitchDefault(); + let postDefaultCases = this.parseSwitchCases(); + if (this.match(TokenType.DEFAULT)) { + throw this.createError(ErrorMessages.MULTIPLE_DEFAULTS_IN_SWITCH); + } + this.expect(TokenType.RBRACE); + return new AST.SwitchStatementWithDefault({ + discriminant, + preDefaultCases: cases, + defaultCase, + postDefaultCases, + }); + } + this.expect(TokenType.RBRACE); + return new AST.SwitchStatement({ discriminant, cases }); + } + + parseSwitchCases() { + let result = []; + while (!(this.eof() || this.match(TokenType.RBRACE) || this.match(TokenType.DEFAULT))) { + result.push(this.parseSwitchCase()); + } + return result; + } + + parseSwitchCase() { + let startState = this.startNode(); + this.expect(TokenType.CASE); + return this.finishNode(new AST.SwitchCase({ + test: this.parseExpression(), + consequent: this.parseSwitchCaseBody(), + }), startState); + } + + parseSwitchDefault() { + let startState = this.startNode(); + this.expect(TokenType.DEFAULT); + return this.finishNode(new AST.SwitchDefault({ consequent: this.parseSwitchCaseBody() }), startState); + } + + parseSwitchCaseBody() { + this.expect(TokenType.COLON); + return this.parseStatementListInSwitchCaseBody(); + } + + parseStatementListInSwitchCaseBody() { + let result = []; + while (!(this.eof() || this.match(TokenType.RBRACE) || this.match(TokenType.DEFAULT) || this.match(TokenType.CASE))) { + result.push(this.parseStatementListItem()); + } + return result; + } + + parseThrowStatement() { + let token = this.lex(); + if (this.hasLineTerminatorBeforeNext) { + throw this.createErrorWithLocation(token, ErrorMessages.NEWLINE_AFTER_THROW); + } + let expression = this.parseExpression(); + this.consumeSemicolon(); + return new AST.ThrowStatement({ expression }); + } + + parseTryStatement() { + this.lex(); + let body = this.parseBlock(); + + if (this.match(TokenType.CATCH)) { + let catchClause = this.parseCatchClause(); + if (this.eat(TokenType.FINALLY)) { + let finalizer = this.parseBlock(); + return new AST.TryFinallyStatement({ body, catchClause, finalizer }); + } + return new AST.TryCatchStatement({ body, catchClause }); + } + + if (this.eat(TokenType.FINALLY)) { + let finalizer = this.parseBlock(); + return new AST.TryFinallyStatement({ body, catchClause: null, finalizer }); + } + throw this.createError(ErrorMessages.NO_CATCH_OR_FINALLY); + } + + parseVariableDeclarationStatement() { + let declaration = this.parseVariableDeclaration(true); + this.consumeSemicolon(); + return new AST.VariableDeclarationStatement({ declaration }); + } + + parseWhileStatement() { + this.lex(); + this.expect(TokenType.LPAREN); + let test = this.parseExpression(); + let body = this.getIteratorStatementEpilogue(); + return new AST.WhileStatement({ test, body }); + } + + parseWithStatement() { + this.lex(); + this.expect(TokenType.LPAREN); + let object = this.parseExpression(); + this.expect(TokenType.RPAREN); + let body = this.parseStatement(); + return new AST.WithStatement({ object, body }); + } + + parseCatchClause() { + let startState = this.startNode(); + + this.lex(); + this.expect(TokenType.LPAREN); + if (this.match(TokenType.RPAREN) || this.match(TokenType.LPAREN)) { + throw this.createUnexpected(this.lookahead); + } + let binding = this.parseBindingTarget(); + this.expect(TokenType.RPAREN); + let body = this.parseBlock(); + + return this.finishNode(new AST.CatchClause({ binding, body }), startState); + } + + parseBlock() { + let startState = this.startNode(); + this.expect(TokenType.LBRACE); + let body = []; + while (!this.match(TokenType.RBRACE)) { + body.push(this.parseStatementListItem()); + } + this.expect(TokenType.RBRACE); + return this.finishNode(new AST.Block({ statements: body }), startState); + } + + parseVariableDeclaration(bindingPatternsMustHaveInit) { + let startState = this.startNode(); + let token = this.lex(); + + // preceded by this.match(TokenSubType.VAR) || this.match(TokenSubType.LET); + let kind = token.type === TokenType.VAR ? 'var' : token.type === TokenType.CONST ? 'const' : 'let'; + let declarators = this.parseVariableDeclaratorList(bindingPatternsMustHaveInit); + return this.finishNode(new AST.VariableDeclaration({ kind, declarators }), startState); + } + + parseVariableDeclaratorList(bindingPatternsMustHaveInit) { + let result = []; + do { + result.push(this.parseVariableDeclarator(bindingPatternsMustHaveInit)); + } while (this.eat(TokenType.COMMA)); + return result; + } + + parseVariableDeclarator(bindingPatternsMustHaveInit) { + let startState = this.startNode(); + + if (this.match(TokenType.LPAREN)) { + throw this.createUnexpected(this.lookahead); + } + + let previousAllowIn = this.allowIn; + this.allowIn = true; + let binding = this.parseBindingTarget(); + this.allowIn = previousAllowIn; + + if (bindingPatternsMustHaveInit && binding.type !== 'BindingIdentifier' && !this.match(TokenType.ASSIGN)) { + this.expect(TokenType.ASSIGN); + } + + let init = null; + if (this.eat(TokenType.ASSIGN)) { + init = this.parseAssignmentExpression(); + } + + return this.finishNode(new AST.VariableDeclarator({ binding, init }), startState); + } + + isolateCoverGrammar(parser) { + let oldIsBindingElement = this.isBindingElement, + oldIsAssignmentTarget = this.isAssignmentTarget, + oldFirstExprError = this.firstExprError, + result; + this.isBindingElement = this.isAssignmentTarget = true; + this.firstExprError = null; + result = parser.call(this); + if (this.firstExprError !== null) { + throw this.firstExprError; + } + this.isBindingElement = oldIsBindingElement; + this.isAssignmentTarget = oldIsAssignmentTarget; + this.firstExprError = oldFirstExprError; + return result; + } + + inheritCoverGrammar(parser) { + let oldIsBindingElement = this.isBindingElement, + oldIsAssignmentTarget = this.isAssignmentTarget, + oldFirstExprError = this.firstExprError, + result; + this.isBindingElement = this.isAssignmentTarget = true; + this.firstExprError = null; + result = parser.call(this); + this.isBindingElement = this.isBindingElement && oldIsBindingElement; + this.isAssignmentTarget = this.isAssignmentTarget && oldIsAssignmentTarget; + this.firstExprError = oldFirstExprError || this.firstExprError; + return result; + } + + parseExpression() { + let startState = this.startNode(); + + let left = this.parseAssignmentExpression(); + if (this.match(TokenType.COMMA)) { + while (!this.eof()) { + if (!this.match(TokenType.COMMA)) break; + this.lex(); + let right = this.parseAssignmentExpression(); + left = this.finishNode(new AST.BinaryExpression({ left, operator: ',', right }), startState); + } + } + return left; + } + + finishArrowParams(head) { + let { params = null, rest = null } = head; + if (head.type !== ARROW_EXPRESSION_PARAMS) { + if (head.type === 'IdentifierExpression') { + params = [this.targetToBinding(this.transformDestructuring(head))]; + } else { + throw this.createUnexpected(this.lookahead); + } + } + return this.copyNode(head, new AST.FormalParameters({ items: params, rest })); + } + + parseArrowExpressionTail(params, isAsync, startState) { + this.expect(TokenType.ARROW); + let previousYield = this.allowYieldExpression; + let previousAwait = this.allowAwaitExpression; + let previousAwaitLocation = this.firstAwaitLocation; + this.allowYieldExpression = false; + this.allowAwaitExpression = isAsync; + this.firstAwaitLocation = null; + let body; + if (this.match(TokenType.LBRACE)) { + let previousAllowIn = this.allowIn; + this.allowIn = true; + body = this.parseFunctionBody(); + this.allowIn = previousAllowIn; + } else { + body = this.parseAssignmentExpression(); + } + this.allowYieldExpression = previousYield; + this.allowAwaitExpression = previousAwait; + this.firstAwaitLocation = previousAwaitLocation; + return this.finishNode(new AST.ArrowExpression({ isAsync, params, body }), startState); + } + + parseAssignmentExpression() { + return this.isolateCoverGrammar(this.parseAssignmentExpressionOrTarget); + } + + parseAssignmentExpressionOrTarget() { + let startState = this.startNode(); + if (this.allowYieldExpression && this.match(TokenType.YIELD)) { + this.isBindingElement = this.isAssignmentTarget = false; + return this.parseYieldExpression(); + } + let expr = this.parseConditionalExpression(); + if (!this.hasLineTerminatorBeforeNext && this.match(TokenType.ARROW)) { + this.isBindingElement = this.isAssignmentTarget = false; + this.firstExprError = null; + let isAsync = expr.type === ARROW_EXPRESSION_PARAMS && expr.isAsync; + return this.parseArrowExpressionTail(this.finishArrowParams(expr), isAsync, startState); + } + let isAssignmentOperator = false; + let operator = this.lookahead; + switch (operator.type) { + case TokenType.ASSIGN_BIT_OR: + case TokenType.ASSIGN_BIT_XOR: + case TokenType.ASSIGN_BIT_AND: + case TokenType.ASSIGN_SHL: + case TokenType.ASSIGN_SHR: + case TokenType.ASSIGN_SHR_UNSIGNED: + case TokenType.ASSIGN_ADD: + case TokenType.ASSIGN_SUB: + case TokenType.ASSIGN_MUL: + case TokenType.ASSIGN_DIV: + case TokenType.ASSIGN_MOD: + case TokenType.ASSIGN_EXP: + isAssignmentOperator = true; + break; + } + if (isAssignmentOperator) { + if (!this.isAssignmentTarget || !isValidSimpleAssignmentTarget(expr)) { + throw this.createError(ErrorMessages.INVALID_LHS_IN_ASSIGNMENT); + } + expr = this.transformDestructuring(expr); + } else if (operator.type === TokenType.ASSIGN) { + if (!this.isAssignmentTarget) { + throw this.createError(ErrorMessages.INVALID_LHS_IN_ASSIGNMENT); + } + expr = this.transformDestructuring(expr); + } else { + return expr; + } + this.lex(); + let rhs = this.parseAssignmentExpression(); + + this.firstExprError = null; + let node; + if (operator.type === TokenType.ASSIGN) { + node = new AST.AssignmentExpression({ binding: expr, expression: rhs }); + } else { + node = new AST.CompoundAssignmentExpression({ binding: expr, operator: operator.type.name, expression: rhs }); + this.isBindingElement = this.isAssignmentTarget = false; + } + return this.finishNode(node, startState); + } + + targetToBinding(node) { + if (node === null) { + return null; + } + + switch (node.type) { + case 'AssignmentTargetIdentifier': + return this.copyNode(node, new AST.BindingIdentifier({ name: node.name })); + case 'ArrayAssignmentTarget': + return this.copyNode(node, new AST.ArrayBinding({ elements: node.elements.map(e => this.targetToBinding(e)), rest: this.targetToBinding(node.rest) })); + case 'ObjectAssignmentTarget': + return this.copyNode(node, new AST.ObjectBinding({ properties: node.properties.map(p => this.targetToBinding(p)), rest: this.targetToBinding(node.rest) })); + case 'AssignmentTargetPropertyIdentifier': + return this.copyNode(node, new AST.BindingPropertyIdentifier({ binding: this.targetToBinding(node.binding), init: node.init })); + case 'AssignmentTargetPropertyProperty': + return this.copyNode(node, new AST.BindingPropertyProperty({ name: node.name, binding: this.targetToBinding(node.binding) })); + case 'AssignmentTargetWithDefault': + return this.copyNode(node, new AST.BindingWithDefault({ binding: this.targetToBinding(node.binding), init: node.init })); + } + + // istanbul ignore next + throw new Error('Not reached'); + } + + transformDestructuring(node) { + switch (node.type) { + + case 'DataProperty': + return this.copyNode(node, new AST.AssignmentTargetPropertyProperty({ + name: node.name, + binding: this.transformDestructuringWithDefault(node.expression), + })); + case 'ShorthandProperty': + return this.copyNode(node, new AST.AssignmentTargetPropertyIdentifier({ + binding: this.copyNode(node, new AST.AssignmentTargetIdentifier({ name: node.name.name })), + init: null, + })); + + case 'ObjectExpression': { + let last = node.properties.length > 0 ? node.properties[node.properties.length - 1] : void 0; + if (last != null && last.type === 'SpreadProperty') { + return this.copyNode(node, new AST.ObjectAssignmentTarget({ + properties: node.properties.slice(0, -1).map(e => e && this.transformDestructuringWithDefault(e)), + rest: this.transformDestructuring(last.expression), + })); + } + + return this.copyNode(node, new AST.ObjectAssignmentTarget({ + properties: node.properties.map(e => e && this.transformDestructuringWithDefault(e)), + rest: null, + })); + } + case 'ArrayExpression': { + let last = node.elements[node.elements.length - 1]; + if (last != null && last.type === 'SpreadElement') { + return this.copyNode(node, new AST.ArrayAssignmentTarget({ + elements: node.elements.slice(0, -1).map(e => e && this.transformDestructuringWithDefault(e)), + rest: this.copyNode(last.expression, this.transformDestructuring(last.expression)), + })); + } + return this.copyNode(node, new AST.ArrayAssignmentTarget({ + elements: node.elements.map(e => e && this.transformDestructuringWithDefault(e)), + rest: null, + })); + } + case 'IdentifierExpression': + return this.copyNode(node, new AST.AssignmentTargetIdentifier({ name: node.name })); + + case 'StaticPropertyName': + return this.copyNode(node, new AST.AssignmentTargetIdentifier({ name: node.value })); + + case 'ComputedMemberExpression': + return this.copyNode(node, new AST.ComputedMemberAssignmentTarget({ object: node.object, expression: node.expression })); + case 'StaticMemberExpression': + return this.copyNode(node, new AST.StaticMemberAssignmentTarget({ object: node.object, property: node.property })); + + case 'ArrayAssignmentTarget': + case 'ObjectAssignmentTarget': + case 'ComputedMemberAssignmentTarget': + case 'StaticMemberAssignmentTarget': + case 'AssignmentTargetIdentifier': + case 'AssignmentTargetPropertyIdentifier': + case 'AssignmentTargetPropertyProperty': + case 'AssignmentTargetWithDefault': + return node; + } + // istanbul ignore next + throw new Error('Not reached'); + } + + transformDestructuringWithDefault(node) { + switch (node.type) { + case 'AssignmentExpression': + return this.copyNode(node, new AST.AssignmentTargetWithDefault({ + binding: this.transformDestructuring(node.binding), + init: node.expression, + })); + } + return this.transformDestructuring(node); + } + + lookaheadAssignmentExpression() { + if (this.matchIdentifier()) { + return true; + } + switch (this.lookahead.type) { + case TokenType.ADD: + case TokenType.ASSIGN_DIV: + case TokenType.BIT_NOT: + case TokenType.CLASS: + case TokenType.DEC: + case TokenType.DELETE: + case TokenType.DIV: + case TokenType.FALSE: + case TokenType.FUNCTION: + case TokenType.INC: + case TokenType.LBRACE: + case TokenType.LBRACK: + case TokenType.LPAREN: + case TokenType.NEW: + case TokenType.NOT: + case TokenType.NULL: + case TokenType.NUMBER: + case TokenType.STRING: + case TokenType.SUB: + case TokenType.SUPER: + case TokenType.THIS: + case TokenType.TRUE: + case TokenType.TYPEOF: + case TokenType.VOID: + case TokenType.TEMPLATE: + return true; + } + return false; + } + + parseYieldExpression() { + let startState = this.startNode(); + + this.lex(); + if (this.hasLineTerminatorBeforeNext) { + return this.finishNode(new AST.YieldExpression({ expression: null }), startState); + } + let isGenerator = !!this.eat(TokenType.MUL); + let expr = null; + if (isGenerator || this.lookaheadAssignmentExpression()) { + expr = this.parseAssignmentExpression(); + } + let ctor = isGenerator ? AST.YieldGeneratorExpression : AST.YieldExpression; + return this.finishNode(new ctor({ expression: expr }), startState); + } + + parseConditionalExpression() { + let startState = this.startNode(); + let test = this.parseBinaryExpression(); + if (this.firstExprError) return test; + if (this.eat(TokenType.CONDITIONAL)) { + this.isBindingElement = this.isAssignmentTarget = false; + let previousAllowIn = this.allowIn; + this.allowIn = true; + let consequent = this.isolateCoverGrammar(this.parseAssignmentExpression); + this.allowIn = previousAllowIn; + this.expect(TokenType.COLON); + let alternate = this.isolateCoverGrammar(this.parseAssignmentExpression); + return this.finishNode(new AST.ConditionalExpression({ test, consequent, alternate }), startState); + } + return test; + } + + isBinaryOperator(type) { + switch (type) { + case TokenType.OR: + case TokenType.AND: + case TokenType.BIT_OR: + case TokenType.BIT_XOR: + case TokenType.BIT_AND: + case TokenType.EQ: + case TokenType.NE: + case TokenType.EQ_STRICT: + case TokenType.NE_STRICT: + case TokenType.LT: + case TokenType.GT: + case TokenType.LTE: + case TokenType.GTE: + case TokenType.INSTANCEOF: + case TokenType.SHL: + case TokenType.SHR: + case TokenType.SHR_UNSIGNED: + case TokenType.ADD: + case TokenType.SUB: + case TokenType.MUL: + case TokenType.DIV: + case TokenType.MOD: + return true; + case TokenType.IN: + return this.allowIn; + default: + return false; + } + } + + parseBinaryExpression() { + let startState = this.startNode(); + let left = this.parseExponentiationExpression(); + if (this.firstExprError) { + return left; + } + + let operator = this.lookahead.type; + if (!this.isBinaryOperator(operator)) return left; + + this.isBindingElement = this.isAssignmentTarget = false; + + this.lex(); + let stack = []; + stack.push({ startState, left, operator, precedence: BinaryPrecedence[operator.name] }); + startState = this.startNode(); + let right = this.isolateCoverGrammar(this.parseExponentiationExpression); + operator = this.lookahead.type; + while (this.isBinaryOperator(operator)) { + let precedence = BinaryPrecedence[operator.name]; + // Reduce: make a binary expression from the three topmost entries. + while (stack.length && precedence <= stack[stack.length - 1].precedence) { + let stackItem = stack[stack.length - 1]; + let stackOperator = stackItem.operator; + left = stackItem.left; + stack.pop(); + startState = stackItem.startState; + right = this.finishNode(new AST.BinaryExpression({ left, operator: stackOperator.name, right }), startState); + } + + this.lex(); + stack.push({ startState, left: right, operator, precedence }); + + startState = this.startNode(); + right = this.isolateCoverGrammar(this.parseExponentiationExpression); + operator = this.lookahead.type; + } + + // Final reduce to clean-up the stack. + return stack.reduceRight((expr, stackItem) => + this.finishNode(new AST.BinaryExpression({ + left: stackItem.left, + operator: stackItem.operator.name, + right: expr, + }), stackItem.startState), + right); + } + + parseExponentiationExpression() { + let startState = this.startNode(); + + let leftIsParenthesized = this.lookahead.type === TokenType.LPAREN; + let left = this.parseUnaryExpression(); + if (this.lookahead.type !== TokenType.EXP) { + return left; + } + if (left.type === 'UnaryExpression' && !leftIsParenthesized) { + throw this.createError(ErrorMessages.INVALID_EXPONENTIATION_LHS); + } + this.lex(); + + this.isBindingElement = this.isAssignmentTarget = false; + + let right = this.isolateCoverGrammar(this.parseExponentiationExpression); + return this.finishNode(new AST.BinaryExpression({ left, operator: '**', right }), startState); + } + + parseUnaryExpression() { + if (this.lookahead.type.klass !== TokenClass.Punctuator && this.lookahead.type.klass !== TokenClass.Keyword) { + return this.parseUpdateExpression(); + } + + let startState = this.startNode(); + if (this.allowAwaitExpression && this.eat(TokenType.AWAIT)) { + this.isBindingElement = this.isAssignmentTarget = false; + let expression = this.isolateCoverGrammar(this.parseUnaryExpression); + return this.finishNode(new AST.AwaitExpression({ expression }), startState); + } + + let operator = this.lookahead; + if (!isPrefixOperator(operator)) { + return this.parseUpdateExpression(); + } + + this.lex(); + this.isBindingElement = this.isAssignmentTarget = false; + + let node; + if (isUpdateOperator(operator)) { + let operandStartLocation = this.getLocation(); + let operand = this.isolateCoverGrammar(this.parseUnaryExpression); + if (!isValidSimpleAssignmentTarget(operand)) { + throw this.createErrorWithLocation(operandStartLocation, ErrorMessages.INVALID_UPDATE_OPERAND); + } + operand = this.transformDestructuring(operand); + node = new AST.UpdateExpression({ isPrefix: true, operator: operator.value, operand }); + } else { + let operand = this.isolateCoverGrammar(this.parseUnaryExpression); + node = new AST.UnaryExpression({ operator: operator.value, operand }); + } + + return this.finishNode(node, startState); + } + + parseUpdateExpression() { + let startLocation = this.getLocation(); + let startState = this.startNode(); + + let operand = this.parseLeftHandSideExpression({ allowCall: true }); + if (this.firstExprError || this.hasLineTerminatorBeforeNext) return operand; + + let operator = this.lookahead; + if (!isUpdateOperator(operator)) return operand; + this.lex(); + this.isBindingElement = this.isAssignmentTarget = false; + if (!isValidSimpleAssignmentTarget(operand)) { + throw this.createErrorWithLocation(startLocation, ErrorMessages.INVALID_UPDATE_OPERAND); + } + operand = this.transformDestructuring(operand); + + return this.finishNode(new AST.UpdateExpression({ isPrefix: false, operator: operator.value, operand }), startState); + } + + parseLeftHandSideExpression({ allowCall }) { + let startState = this.startNode(); + let previousAllowIn = this.allowIn; + this.allowIn = true; + + let expr, token = this.lookahead; + + if (this.eat(TokenType.SUPER)) { + this.isBindingElement = false; + this.isAssignmentTarget = false; + expr = this.finishNode(new AST.Super, startState); + if (this.match(TokenType.LPAREN)) { + if (allowCall) { + expr = this.finishNode(new AST.CallExpression({ + callee: expr, + arguments: this.parseArgumentList().args, + }), startState); + } else { + throw this.createUnexpected(token); + } + } else if (this.match(TokenType.LBRACK)) { + expr = this.finishNode(new AST.ComputedMemberExpression({ + object: expr, + expression: this.parseComputedMember(), + }), startState); + this.isAssignmentTarget = true; + } else if (this.match(TokenType.PERIOD)) { + expr = this.finishNode(new AST.StaticMemberExpression({ + object: expr, + property: this.parseStaticMember(), + }), startState); + this.isAssignmentTarget = true; + } else { + throw this.createUnexpected(token); + } + } else if (this.match(TokenType.NEW)) { + this.isBindingElement = this.isAssignmentTarget = false; + expr = this.parseNewExpression(); + } else if (this.match(TokenType.ASYNC)) { + expr = this.parsePrimaryExpression(); + // there's only three things this could be: an identifier, an async arrow, or an async function expression. + if (expr.type === 'IdentifierExpression' && allowCall && !this.hasLineTerminatorBeforeNext) { + if (this.matchIdentifier()) { + // `async [no lineterminator here] identifier` must be an async arrow + let afterAsyncStartState = this.startNode(); + let previousAwait = this.allowAwaitExpression; + this.allowAwaitExpression = true; + let param = this.parseBindingIdentifier(); + this.allowAwaitExpression = previousAwait; + this.ensureArrow(); + return this.finishNode({ + type: ARROW_EXPRESSION_PARAMS, + params: [param], + rest: null, + isAsync: true, + }, afterAsyncStartState); + } + if (this.match(TokenType.LPAREN)) { + // the maximally obnoxious case: `async (` + let afterAsyncStartState = this.startNode(); + let previousAwaitLocation = this.firstAwaitLocation; + this.firstAwaitLocation = null; + let { args, locationFollowingFirstSpread } = this.parseArgumentList(); + if (this.isBindingElement && !this.hasLineTerminatorBeforeNext && this.match(TokenType.ARROW)) { + if (locationFollowingFirstSpread !== null) { + throw this.createErrorWithLocation(locationFollowingFirstSpread, ErrorMessages.UNEXPECTED_TOKEN(',')); + } + if (this.firstAwaitLocation !== null) { + throw this.createErrorWithLocation(this.firstAwaitLocation, ErrorMessages.NO_AWAIT_IN_ASYNC_PARAMS); + } + let rest = null; + if (args.length > 0 && args[args.length - 1].type === 'SpreadElement') { + rest = this.targetToBinding(this.transformDestructuringWithDefault(args[args.length - 1].expression)); + if (rest.init != null) { + throw this.createError(ErrorMessages.UNEXPECTED_REST_PARAMETERS_INITIALIZATION); + } + args = args.slice(0, -1); + } + let params = args.map(arg => this.targetToBinding(this.transformDestructuringWithDefault(arg))); + return this.finishNode({ + type: ARROW_EXPRESSION_PARAMS, + params, + rest, + isAsync: true, + }, afterAsyncStartState); + } + this.firstAwaitLocation = previousAwaitLocation || this.firstAwaitLocation; + // otherwise we've just taken the first iteration of the loop below + this.isBindingElement = this.isAssignmentTarget = false; + expr = this.finishNode(new AST.CallExpression({ + callee: expr, + arguments: args, + }), startState); + } + } + } else { + expr = this.parsePrimaryExpression(); + if (this.firstExprError) { + return expr; + } + } + + while (true) { + if (allowCall && this.match(TokenType.LPAREN)) { + this.isBindingElement = this.isAssignmentTarget = false; + expr = this.finishNode(new AST.CallExpression({ + callee: expr, + arguments: this.parseArgumentList().args, + }), startState); + } else if (this.match(TokenType.LBRACK)) { + this.isBindingElement = false; + this.isAssignmentTarget = true; + expr = this.finishNode(new AST.ComputedMemberExpression({ + object: expr, + expression: this.parseComputedMember(), + }), startState); + } else if (this.match(TokenType.PERIOD)) { + this.isBindingElement = false; + this.isAssignmentTarget = true; + expr = this.finishNode(new AST.StaticMemberExpression({ + object: expr, + property: this.parseStaticMember(), + }), startState); + } else if (this.match(TokenType.TEMPLATE)) { + this.isBindingElement = this.isAssignmentTarget = false; + expr = this.finishNode(new AST.TemplateExpression({ + tag: expr, + elements: this.parseTemplateElements(), + }), startState); + } else { + break; + } + } + + this.allowIn = previousAllowIn; + + return expr; + } + + parseTemplateElements() { + let startState = this.startNode(); + let token = this.lookahead; + if (token.tail) { + this.lex(); + return [this.finishNode(new AST.TemplateElement({ rawValue: token.slice.text.slice(1, -1) }), startState)]; + } + let result = [ + this.finishNode(new AST.TemplateElement({ rawValue: this.lex().slice.text.slice(1, -2) }), startState), + ]; + while (true) { + result.push(this.parseExpression()); + if (!this.match(TokenType.RBRACE)) { + throw this.createILLEGAL(); + } + this.index = this.startIndex; + this.line = this.startLine; + this.lineStart = this.startLineStart; + this.lookahead = this.scanTemplateElement(); + startState = this.startNode(); + token = this.lex(); + if (token.tail) { + result.push(this.finishNode(new AST.TemplateElement({ rawValue: token.slice.text.slice(1, -1) }), startState)); + return result; + } + result.push(this.finishNode(new AST.TemplateElement({ rawValue: token.slice.text.slice(1, -2) }), startState)); + } + } + + parseStaticMember() { + this.lex(); + if (this.lookahead.type.klass.isIdentifierName) { + return this.lex().value; + } + throw this.createUnexpected(this.lookahead); + } + + parseComputedMember() { + this.lex(); + let expr = this.parseExpression(); + this.expect(TokenType.RBRACK); + return expr; + } + + parseNewExpression() { + let startState = this.startNode(); + this.lex(); + if (this.eat(TokenType.PERIOD)) { + this.expectContextualKeyword('target'); + return this.finishNode(new AST.NewTargetExpression, startState); + } + let callee = this.isolateCoverGrammar(() => this.parseLeftHandSideExpression({ allowCall: false })); + return this.finishNode(new AST.NewExpression({ + callee, + arguments: this.match(TokenType.LPAREN) ? this.parseArgumentList().args : [], + }), startState); + } + + parseRegexFlags(flags) { + let global = false, + ignoreCase = false, + multiLine = false, + unicode = false, + sticky = false, + dotAll = false; + for (let i = 0; i < flags.length; ++i) { + let f = flags[i]; + switch (f) { + case 'g': + if (global) { + throw this.createError('Duplicate regular expression flag \'g\''); + } + global = true; + break; + case 'i': + if (ignoreCase) { + throw this.createError('Duplicate regular expression flag \'i\''); + } + ignoreCase = true; + break; + case 'm': + if (multiLine) { + throw this.createError('Duplicate regular expression flag \'m\''); + } + multiLine = true; + break; + case 'u': + if (unicode) { + throw this.createError('Duplicate regular expression flag \'u\''); + } + unicode = true; + break; + case 'y': + if (sticky) { + throw this.createError('Duplicate regular expression flag \'y\''); + } + sticky = true; + break; + case 's': + if (dotAll) { + throw this.createError('Duplicate regular expression flag \'s\''); + } + dotAll = true; + break; + default: + throw this.createError(`Invalid regular expression flag '${f}'`); + } + } + return { global, ignoreCase, multiLine, unicode, sticky, dotAll }; + } + + parsePrimaryExpression() { + if (this.match(TokenType.LPAREN)) { + return this.parseGroupExpression(); + } + + let startState = this.startNode(); + + if (this.eat(TokenType.ASYNC)) { + if (!this.hasLineTerminatorBeforeNext && this.match(TokenType.FUNCTION)) { + this.isBindingElement = this.isAssignmentTarget = false; + return this.finishNode(this.parseFunction({ isExpr: true, inDefault: false, allowGenerator: true, isAsync: true }), startState); + } + return this.finishNode(new AST.IdentifierExpression({ name: 'async' }), startState); + } + + if (this.matchIdentifier()) { + return this.finishNode(new AST.IdentifierExpression({ name: this.parseIdentifier() }), startState); + } + switch (this.lookahead.type) { + case TokenType.STRING: + this.isBindingElement = this.isAssignmentTarget = false; + return this.parseStringLiteral(); + case TokenType.NUMBER: + this.isBindingElement = this.isAssignmentTarget = false; + return this.parseNumericLiteral(); + case TokenType.THIS: + this.lex(); + this.isBindingElement = this.isAssignmentTarget = false; + return this.finishNode(new AST.ThisExpression, startState); + case TokenType.FUNCTION: + this.isBindingElement = this.isAssignmentTarget = false; + return this.finishNode(this.parseFunction({ isExpr: true, inDefault: false, allowGenerator: true, isAsync: false }), startState); + case TokenType.TRUE: + this.lex(); + this.isBindingElement = this.isAssignmentTarget = false; + return this.finishNode(new AST.LiteralBooleanExpression({ value: true }), startState); + case TokenType.FALSE: + this.lex(); + this.isBindingElement = this.isAssignmentTarget = false; + return this.finishNode(new AST.LiteralBooleanExpression({ value: false }), startState); + case TokenType.NULL: + this.lex(); + this.isBindingElement = this.isAssignmentTarget = false; + return this.finishNode(new AST.LiteralNullExpression, startState); + case TokenType.LBRACK: + return this.parseArrayExpression(); + case TokenType.LBRACE: + return this.parseObjectExpression(); + case TokenType.TEMPLATE: + this.isBindingElement = this.isAssignmentTarget = false; + return this.finishNode(new AST.TemplateExpression({ tag: null, elements: this.parseTemplateElements() }), startState); + case TokenType.DIV: + case TokenType.ASSIGN_DIV: { + this.isBindingElement = this.isAssignmentTarget = false; + this.lookahead = this.scanRegExp(this.match(TokenType.DIV) ? '/' : '/='); + let token = this.lex(); + let lastSlash = token.value.lastIndexOf('/'); + let pattern = token.value.slice(1, lastSlash); + let flags = token.value.slice(lastSlash + 1); + let ctorArgs = this.parseRegexFlags(flags); + if (!acceptRegex(pattern, ctorArgs)) { + throw this.createError(ErrorMessages.INVALID_REGEX); + } + ctorArgs.pattern = pattern; + return this.finishNode(new AST.LiteralRegExpExpression(ctorArgs), startState); + } + case TokenType.CLASS: + this.isBindingElement = this.isAssignmentTarget = false; + return this.parseClass({ isExpr: true, inDefault: false }); + default: + throw this.createUnexpected(this.lookahead); + } + } + + parseNumericLiteral() { + let startLocation = this.getLocation(); + let startState = this.startNode(); + let token = this.lex(); + if (token.octal && this.strict) { + if (token.noctal) { + throw this.createErrorWithLocation(startLocation, 'Unexpected noctal integer literal'); + } else { + throw this.createErrorWithLocation(startLocation, 'Unexpected legacy octal integer literal'); + } + } + let node = token.value === 1 / 0 + ? new AST.LiteralInfinityExpression + : new AST.LiteralNumericExpression({ value: token.value }); + return this.finishNode(node, startState); + } + + parseStringLiteral() { + let startLocation = this.getLocation(); + let startState = this.startNode(); + let token = this.lex(); + if (token.octal != null && this.strict) { + throw this.createErrorWithLocation(startLocation, 'Unexpected legacy octal escape sequence: \\' + token.octal); + } + return this.finishNode(new AST.LiteralStringExpression({ value: token.str }), startState); + } + + parseIdentifierName() { + if (this.lookahead.type.klass.isIdentifierName) { + return this.lex().value; + } + throw this.createUnexpected(this.lookahead); + } + + parseBindingIdentifier() { + let startState = this.startNode(); + return this.finishNode(new AST.BindingIdentifier({ name: this.parseIdentifier() }), startState); + } + + parseIdentifier() { + if (this.lookahead.value === 'yield' && this.allowYieldExpression) { + throw this.createError(ErrorMessages.ILLEGAL_YIELD_IDENTIFIER); + } + if (this.lookahead.value === 'await' && this.allowAwaitExpression) { + throw this.createError(ErrorMessages.ILLEGAL_AWAIT_IDENTIFIER); + } + if (this.matchIdentifier()) { + return this.lex().value; + } + throw this.createUnexpected(this.lookahead); + } + + parseArgumentList() { + this.lex(); + let args = this.parseArguments(); + this.expect(TokenType.RPAREN); + return args; + } + + parseArguments() { + let args = []; + let locationFollowingFirstSpread = null; + while (!this.match(TokenType.RPAREN)) { + let arg; + let startState = this.startNode(); + if (this.eat(TokenType.ELLIPSIS)) { + arg = this.finishNode(new AST.SpreadElement({ expression: this.inheritCoverGrammar(this.parseAssignmentExpressionOrTarget) }), startState); + if (locationFollowingFirstSpread === null) { + args.push(arg); + if (this.match(TokenType.RPAREN)) { + break; + } + locationFollowingFirstSpread = this.getLocation(); + this.expect(TokenType.COMMA); + continue; + } + } else { + arg = this.inheritCoverGrammar(this.parseAssignmentExpressionOrTarget); + } + args.push(arg); + if (this.match(TokenType.RPAREN)) { + break; + } + this.expect(TokenType.COMMA); + } + return { args, locationFollowingFirstSpread }; + } + + // 11.2 Left-Hand-Side Expressions; + + ensureArrow() { + if (this.hasLineTerminatorBeforeNext) { + throw this.createError(ErrorMessages.UNEXPECTED_LINE_TERMINATOR); + } + if (!this.match(TokenType.ARROW)) { + this.expect(TokenType.ARROW); + } + } + + parseGroupExpression() { + // At this point, we need to parse 3 things: + // 1. Group expression + // 2. Assignment target of assignment expression + // 3. Parameter list of arrow function + let rest = null; + let preParenStartState = this.startNode(); + let start = this.expect(TokenType.LPAREN); + let postParenStartState = this.startNode(); + if (this.match(TokenType.RPAREN)) { + this.lex(); + let paramsNode = this.finishNode({ + type: ARROW_EXPRESSION_PARAMS, + params: [], + rest: null, + isAsync: false, + }, preParenStartState); + this.ensureArrow(); + this.isBindingElement = this.isAssignmentTarget = false; + return paramsNode; + } else if (this.eat(TokenType.ELLIPSIS)) { + rest = this.parseBindingTarget(); + if (this.match(TokenType.ASSIGN)) { + throw this.createError(ErrorMessages.INVALID_REST_PARAMETERS_INITIALIZATION); + } + if (this.match(TokenType.COMMA)) { + throw this.createError(ErrorMessages.INVALID_LAST_REST_PARAMETER); + } + this.expect(TokenType.RPAREN); + let paramsNode = this.finishNode({ + type: ARROW_EXPRESSION_PARAMS, + params: [], + rest, + isAsync: false, + }, preParenStartState); + this.ensureArrow(); + this.isBindingElement = this.isAssignmentTarget = false; + return paramsNode; + } + let group = this.inheritCoverGrammar(this.parseAssignmentExpressionOrTarget); + + let params = this.isBindingElement ? [this.targetToBinding(this.transformDestructuringWithDefault(group))] : null; + + while (this.eat(TokenType.COMMA)) { + if (this.match(TokenType.RPAREN)) { + if (!this.isBindingElement) { + throw this.createUnexpected(this.lookahead); + } + this.firstExprError = this.firstExprError || this.createUnexpected(this.lookahead); + group = null; + break; + } + this.isAssignmentTarget = false; + if (this.match(TokenType.ELLIPSIS)) { + if (!this.isBindingElement) { + throw this.createUnexpected(this.lookahead); + } + this.lex(); + rest = this.parseBindingTarget(); + if (this.match(TokenType.ASSIGN)) { + throw this.createError(ErrorMessages.INVALID_REST_PARAMETERS_INITIALIZATION); + } + if (this.match(TokenType.COMMA)) { + throw this.createError(ErrorMessages.INVALID_LAST_REST_PARAMETER); + } + break; + } + + if (group) { + // Can be either binding element or assignment target. + let expr = this.inheritCoverGrammar(this.parseAssignmentExpressionOrTarget); + if (this.isBindingElement) { + params.push(this.targetToBinding(this.transformDestructuringWithDefault(expr))); + } else { + params = null; + } + + if (this.firstExprError) { + group = null; + } else { + group = this.finishNode(new AST.BinaryExpression({ + left: group, + operator: ',', + right: expr, + }), postParenStartState); + } + } else { + // Can be only binding elements. + let binding = this.parseBindingElement(); + params.push(binding); + } + } + this.expect(TokenType.RPAREN); + + if (!this.hasLineTerminatorBeforeNext && this.match(TokenType.ARROW)) { + if (!this.isBindingElement) { + throw this.createErrorWithLocation(start, ErrorMessages.ILLEGAL_ARROW_FUNCTION_PARAMS); + } + + this.isBindingElement = false; + return this.finishNode({ + type: ARROW_EXPRESSION_PARAMS, + params, + rest, + isAsync: false, + }, preParenStartState); + } + // Ensure assignment pattern: + if (rest) { + this.ensureArrow(); + } + this.isBindingElement = false; + if (!isValidSimpleAssignmentTarget(group)) { + this.isAssignmentTarget = false; + } + return group; + } + + parseArrayExpression() { + let startLocation = this.getLocation(); + let startState = this.startNode(); + + this.lex(); + + let exprs = []; + let rest = null; + + while (true) { + if (this.match(TokenType.RBRACK)) { + break; + } + if (this.eat(TokenType.COMMA)) { + exprs.push(null); + } else { + let elementStartState = this.startNode(); + let expr; + if (this.eat(TokenType.ELLIPSIS)) { + // Spread/Rest element + expr = this.inheritCoverGrammar(this.parseAssignmentExpressionOrTarget); + if (!this.isAssignmentTarget && this.firstExprError) { + throw this.firstExprError; + } + if (expr.type === 'ArrayAssignmentTarget' || expr.type === 'ObjectAssignmentTarget') { + rest = expr; + break; + } + if (expr.type !== 'ArrayExpression' && expr.type !== 'ObjectExpression' && !isValidSimpleAssignmentTarget(expr)) { + this.isBindingElement = this.isAssignmentTarget = false; + } + expr = this.finishNode(new AST.SpreadElement({ expression: expr }), elementStartState); + if (!this.match(TokenType.RBRACK)) { + this.isBindingElement = this.isAssignmentTarget = false; + } + } else { + expr = this.inheritCoverGrammar(this.parseAssignmentExpressionOrTarget); + if (!this.isAssignmentTarget && this.firstExprError) { + throw this.firstExprError; + } + } + exprs.push(expr); + + if (!this.match(TokenType.RBRACK)) { + this.expect(TokenType.COMMA); + } + } + } + + if (rest && this.match(TokenType.COMMA)) { + throw this.createErrorWithLocation(startLocation, ErrorMessages.UNEXPECTED_COMMA_AFTER_REST); + } + + this.expect(TokenType.RBRACK); + + if (rest) { + // No need to check isAssignmentTarget: the only way to have something we know is a rest element is if we have ...Object/ArrayAssignmentTarget, which implies we have a firstExprError; as such, if isAssignmentTarget were false, we'd've thrown above before setting rest. + return this.finishNode(new AST.ArrayAssignmentTarget({ + elements: exprs.map(e => e && this.transformDestructuringWithDefault(e)), + rest, + }), startState); + } else if (this.firstExprError) { + let last = exprs[exprs.length - 1]; + if (last != null && last.type === 'SpreadElement') { + return this.finishNode(new AST.ArrayAssignmentTarget({ + elements: exprs.slice(0, -1).map(e => e && this.transformDestructuringWithDefault(e)), + rest: this.transformDestructuring(last.expression), + }), startState); + } + return this.finishNode(new AST.ArrayAssignmentTarget({ + elements: exprs.map(e => e && this.transformDestructuringWithDefault(e)), + rest: null, + }), startState); + + } + return this.finishNode(new AST.ArrayExpression({ elements: exprs }), startState); + } + + parseObjectExpression() { + let startState = this.startNode(); + this.lex(); + let properties = []; + while (!this.match(TokenType.RBRACE)) { + let isSpreadProperty = false; + if (this.match(TokenType.ELLIPSIS)) { + isSpreadProperty = true; + let spreadPropertyOrAssignmentTarget = this.parseSpreadPropertyDefinition(); + properties.push(spreadPropertyOrAssignmentTarget); + } else { + let property = this.inheritCoverGrammar(this.parsePropertyDefinition); + properties.push(property); + } + if (!this.match(TokenType.RBRACE)) { + this.expect(TokenType.COMMA); + if (isSpreadProperty) { + this.isBindingElement = this.isAssignmentTarget = false; + } + } + } + this.expect(TokenType.RBRACE); + if (this.firstExprError) { + if (!this.isAssignmentTarget) { + throw this.createError(ErrorMessages.INVALID_LHS_IN_BINDING); + } + let last = properties[properties.length - 1]; + if (last != null && last.type === 'SpreadProperty') { + return this.finishNode(new AST.ObjectAssignmentTarget({ + properties: properties.slice(0, -1).map(p => this.transformDestructuringWithDefault(p)), + rest: this.transformDestructuring(last.expression), + }), startState); + } + return this.finishNode(new AST.ObjectAssignmentTarget({ properties: properties.map(p => this.transformDestructuringWithDefault(p)), rest: null }), startState); + } + return this.finishNode(new AST.ObjectExpression({ properties }), startState); + } + + parseSpreadPropertyDefinition() { + let startState = this.startNode(); + this.expect(TokenType.ELLIPSIS); + let expression = this.parseAssignmentExpression(); + if (!isValidSimpleAssignmentTarget(expression)) { + this.isBindingElement = this.isAssignmentTarget = false; + } else if (expression.type !== 'IdentifierExpression') { + this.isBindingElement = false; + } + return this.finishNode(new AST.SpreadProperty({ expression }), startState); + } + + parsePropertyDefinition() { + let startLocation = this.getLocation(); + let startState = this.startNode(); + let token = this.lookahead; + + let { methodOrKey, kind } = this.parseMethodDefinition(); + switch (kind) { + case 'method': + this.isBindingElement = this.isAssignmentTarget = false; + return methodOrKey; + case 'identifier': + if (token.value === 'await' && this.firstAwaitLocation == null) { + this.firstAwaitLocation = this.getLocation(); + } + if (this.eat(TokenType.ASSIGN)) { + if (this.allowYieldExpression && token.value === 'yield') { + throw this.createError(ErrorMessages.ILLEGAL_YIELD_IDENTIFIER); + } + if (this.allowAwaitExpression && token.value === 'await') { + throw this.createError(ErrorMessages.ILLEGAL_AWAIT_IDENTIFIER); + } + // CoverInitializedName + let init = this.isolateCoverGrammar(this.parseAssignmentExpression); + this.firstExprError = this.createErrorWithLocation(startLocation, ErrorMessages.ILLEGAL_PROPERTY); + return this.finishNode(new AST.AssignmentTargetPropertyIdentifier({ + binding: this.transformDestructuring(methodOrKey), + init, + }), startState); + } else if (!this.match(TokenType.COLON)) { + if (this.allowYieldExpression && token.value === 'yield') { + throw this.createError(ErrorMessages.ILLEGAL_YIELD_IDENTIFIER); + } + if (this.allowAwaitExpression && token.value === 'await') { + throw this.createError(ErrorMessages.ILLEGAL_AWAIT_IDENTIFIER); + } + if (token.type === TokenType.IDENTIFIER || token.value === 'let' || token.value === 'yield' || token.value === 'async' || token.value === 'await') { + return this.finishNode(new AST.ShorthandProperty({ name: this.finishNode(new AST.IdentifierExpression({ name: methodOrKey.value }), startState) }), startState); + } + throw this.createUnexpected(token); + } + } + + // property + this.expect(TokenType.COLON); + + let expr = this.inheritCoverGrammar(this.parseAssignmentExpressionOrTarget); + if (this.firstExprError) { + return this.finishNode(new AST.AssignmentTargetPropertyProperty({ name: methodOrKey, binding: expr }), startState); + } + return this.finishNode(new AST.DataProperty({ name: methodOrKey, expression: expr }), startState); + } + + parsePropertyName() { + // PropertyName[Yield,GeneratorParameter]: + let token = this.lookahead; + let startState = this.startNode(); + + if (this.eof()) { + throw this.createUnexpected(token); + } + + switch (token.type) { + case TokenType.STRING: + return { + name: this.finishNode(new AST.StaticPropertyName({ + value: this.parseStringLiteral().value, + }), startState), + binding: null, + }; + case TokenType.NUMBER: { + let numLiteral = this.parseNumericLiteral(); + return { + name: this.finishNode(new AST.StaticPropertyName({ + value: `${numLiteral.type === 'LiteralInfinityExpression' ? 1 / 0 : numLiteral.value}`, + }), startState), + binding: null, + }; + } + case TokenType.LBRACK: { + this.lex(); + let expr = this.parseAssignmentExpression(); + this.expect(TokenType.RBRACK); + return { name: this.finishNode(new AST.ComputedPropertyName({ expression: expr }), startState), binding: null }; + } + } + + let name = this.parseIdentifierName(); + return { + name: this.finishNode(new AST.StaticPropertyName({ value: name }), startState), + binding: this.finishNode(new AST.BindingIdentifier({ name }), startState), + }; + } + + /** + * Test if lookahead can be the beginning of a `PropertyName`. + * @returns {boolean} + */ + lookaheadPropertyName() { + switch (this.lookahead.type) { + case TokenType.NUMBER: + case TokenType.STRING: + case TokenType.LBRACK: + return true; + default: + return this.lookahead.type.klass.isIdentifierName; + } + } + + // eslint-disable-next-line valid-jsdoc + /** + * Try to parse a method definition. + * + * If it turns out to be one of: + * * `IdentifierReference` + * * `CoverInitializedName` (`IdentifierReference "=" AssignmentExpression`) + * * `PropertyName : AssignmentExpression` + * The parser will stop at the end of the leading `Identifier` or `PropertyName` and return it. + * + * @returns {{methodOrKey: (Method|PropertyName), kind: string}} + */ + parseMethodDefinition() { + let token = this.lookahead; + let startState = this.startNode(); + + let preAsyncTokenState = this.saveLexerState(); + + let isAsync = !!this.eat(TokenType.ASYNC); + if (isAsync && this.hasLineTerminatorBeforeNext) { + isAsync = false; + this.restoreLexerState(preAsyncTokenState); + } + + let isGenerator = !!this.eat(TokenType.MUL); + if (isAsync && !this.lookaheadPropertyName()) { + isAsync = false; + isGenerator = false; + this.restoreLexerState(preAsyncTokenState); + } + + let { name } = this.parsePropertyName(); + + if (!isGenerator && !isAsync) { + if (token.type === TokenType.IDENTIFIER && token.value.length === 3) { + // Property Assignment: Getter and Setter. + if (token.value === 'get' && this.lookaheadPropertyName() && !token.escaped) { + ({ name } = this.parsePropertyName()); + this.expect(TokenType.LPAREN); + this.expect(TokenType.RPAREN); + let previousYield = this.allowYieldExpression; + let previousAwait = this.allowAwaitExpression; + let previousAwaitLocation = this.firstAwaitLocation; + this.allowYieldExpression = false; + this.allowAwaitExpression = false; + this.firstAwaitLocation = null; + let body = this.parseFunctionBody(); + this.allowYieldExpression = previousYield; + this.allowAwaitExpression = previousAwait; + this.firstAwaitLocation = previousAwaitLocation; + return { + methodOrKey: this.finishNode(new AST.Getter({ name, body }), startState), + kind: 'method', + }; + } else if (token.value === 'set' && this.lookaheadPropertyName() && !token.escaped) { + ({ name } = this.parsePropertyName()); + this.expect(TokenType.LPAREN); + let previousYield = this.allowYieldExpression; + let previousAwait = this.allowAwaitExpression; + let previousAwaitLocation = this.firstAwaitLocation; + this.allowYieldExpression = false; + this.allowAwaitExpression = false; + this.firstAwaitLocation = null; + let param = this.parseBindingElement(); + this.expect(TokenType.RPAREN); + let body = this.parseFunctionBody(); + this.allowYieldExpression = previousYield; + this.allowAwaitExpression = previousAwait; + this.firstAwaitLocation = previousAwaitLocation; + return { + methodOrKey: this.finishNode(new AST.Setter({ name, param, body }), startState), + kind: 'method', + }; + } + } + } + if (isAsync) { + let previousYield = this.allowYieldExpression; + let previousAwait = this.allowAwaitExpression; + this.allowYieldExpression = isGenerator; + this.allowAwaitExpression = true; + let params = this.parseParams(); + this.allowYieldExpression = isGenerator; + this.allowAwaitExpression = true; + let body = this.parseFunctionBody(); + this.allowYieldExpression = previousYield; + this.allowAwaitExpression = previousAwait; + return { + methodOrKey: this.finishNode(new AST.Method({ isAsync, isGenerator, name, params, body }), startState), + kind: 'method', + }; + } + + if (this.match(TokenType.LPAREN)) { + let previousYield = this.allowYieldExpression; + let previousAwait = this.allowAwaitExpression; + let previousAwaitLocation = this.firstAwaitLocation; + this.allowYieldExpression = isGenerator; + this.allowAwaitExpression = false; + this.firstAwaitLocation = null; + let params = this.parseParams(); + let body = this.parseFunctionBody(); + this.allowYieldExpression = previousYield; + this.allowAwaitExpression = previousAwait; + this.firstAwaitLocation = previousAwaitLocation; + + return { + methodOrKey: this.finishNode(new AST.Method({ isAsync, isGenerator, name, params, body }), startState), + kind: 'method', + }; + } + + if (isGenerator && this.match(TokenType.COLON)) { + throw this.createUnexpected(this.lookahead); + } + + return { + methodOrKey: name, + kind: token.type.klass.isIdentifierName ? 'identifier' : 'property', + escaped: token.escaped, + }; + } + + parseClass({ isExpr, inDefault }) { + let startState = this.startNode(); + + this.lex(); + let name = null; + let heritage = null; + + if (this.matchIdentifier()) { + name = this.parseBindingIdentifier(); + } else if (!isExpr) { + if (inDefault) { + name = new AST.BindingIdentifier({ name: '*default*' }); + } else { + throw this.createUnexpected(this.lookahead); + } + } + + if (this.eat(TokenType.EXTENDS)) { + heritage = this.isolateCoverGrammar(() => this.parseLeftHandSideExpression({ allowCall: true })); + } + + this.expect(TokenType.LBRACE); + let elements = []; + while (!this.eat(TokenType.RBRACE)) { + if (this.eat(TokenType.SEMICOLON)) { + continue; + } + let isStatic = false; + let classElementStart = this.startNode(); + let { methodOrKey, kind, escaped } = this.parseMethodDefinition(); + if (kind === 'identifier' && methodOrKey.value === 'static' && !escaped) { + isStatic = true; + ({ methodOrKey, kind } = this.parseMethodDefinition()); + } + if (kind === 'method') { + elements.push(this.finishNode(new AST.ClassElement({ isStatic, method: methodOrKey }), classElementStart)); + } else { + throw this.createError('Only methods are allowed in classes'); + } + } + return this.finishNode(new (isExpr ? AST.ClassExpression : AST.ClassDeclaration)({ name, super: heritage, elements }), startState); + } + + parseFunction({ isExpr, inDefault, allowGenerator, isAsync, startState = this.startNode() }) { + this.lex(); + let name = null; + let isGenerator = allowGenerator && !!this.eat(TokenType.MUL); + + let previousYield = this.allowYieldExpression; + let previousAwait = this.allowAwaitExpression; + let previousAwaitLocation = this.firstAwaitLocation; + + if (isExpr) { + this.allowYieldExpression = isGenerator; + this.allowAwaitExpression = isAsync; + } + + if (!this.match(TokenType.LPAREN)) { + name = this.parseBindingIdentifier(); + } else if (!isExpr) { + if (inDefault) { + name = new AST.BindingIdentifier({ name: '*default*' }); + } else { + throw this.createUnexpected(this.lookahead); + } + } + this.allowYieldExpression = isGenerator; + this.allowAwaitExpression = isAsync; + this.firstAwaitLocation = null; + let params = this.parseParams(); + let body = this.parseFunctionBody(); + this.allowYieldExpression = previousYield; + this.allowAwaitExpression = previousAwait; + this.firstAwaitLocation = previousAwaitLocation; + + return this.finishNode(new (isExpr ? AST.FunctionExpression : AST.FunctionDeclaration)({ isAsync, isGenerator, name, params, body }), startState); + } + + parseArrayBinding() { + let startState = this.startNode(); + + this.expect(TokenType.LBRACK); + + let elements = [], rest = null; + + while (true) { + if (this.match(TokenType.RBRACK)) { + break; + } + let el; + + if (this.eat(TokenType.COMMA)) { + el = null; + } else { + if (this.eat(TokenType.ELLIPSIS)) { + rest = this.parseBindingTarget(); + break; + } else { + el = this.parseBindingElement(); + } + if (!this.match(TokenType.RBRACK)) { + this.expect(TokenType.COMMA); + } + } + elements.push(el); + } + + this.expect(TokenType.RBRACK); + + return this.finishNode(new AST.ArrayBinding({ elements, rest }), startState); + } + + parseBindingProperty() { + let startState = this.startNode(); + let isIdentifier = this.matchIdentifier(); + let token = this.lookahead; + let { name, binding } = this.parsePropertyName(); + if (isIdentifier && name.type === 'StaticPropertyName') { + if (!this.match(TokenType.COLON)) { + if (this.allowYieldExpression && token.value === 'yield') { + throw this.createError(ErrorMessages.ILLEGAL_YIELD_IDENTIFIER); + } + if (this.allowAwaitExpression && token.value === 'await') { + throw this.createError(ErrorMessages.ILLEGAL_AWAIT_IDENTIFIER); + } + let defaultValue = null; + if (this.eat(TokenType.ASSIGN)) { + defaultValue = this.parseAssignmentExpression(); + } + return this.finishNode(new AST.BindingPropertyIdentifier({ + binding, + init: defaultValue, + }), startState); + } + } + this.expect(TokenType.COLON); + binding = this.parseBindingElement(); + return this.finishNode(new AST.BindingPropertyProperty({ name, binding }), startState); + } + + parseObjectBinding() { + let startState = this.startNode(); + this.expect(TokenType.LBRACE); + + let properties = []; + let rest = null; + while (!this.match(TokenType.RBRACE)) { + if (this.eat(TokenType.ELLIPSIS)) { + rest = this.parseBindingIdentifier(); + break; + } + properties.push(this.parseBindingProperty()); + if (!this.match(TokenType.RBRACE)) { + this.expect(TokenType.COMMA); + } + } + + this.expect(TokenType.RBRACE); + + return this.finishNode(new AST.ObjectBinding({ properties, rest }), startState); + } + + parseBindingTarget() { + if (this.matchIdentifier()) { + return this.parseBindingIdentifier(); + } + switch (this.lookahead.type) { + case TokenType.LBRACK: + return this.parseArrayBinding(); + case TokenType.LBRACE: + return this.parseObjectBinding(); + } + throw this.createUnexpected(this.lookahead); + } + + parseBindingElement() { + let startState = this.startNode(); + let binding = this.parseBindingTarget(); + if (this.eat(TokenType.ASSIGN)) { + let init = this.parseAssignmentExpression(); + binding = this.finishNode(new AST.BindingWithDefault({ binding, init }), startState); + } + return binding; + } + + parseParam() { + let previousInParameter = this.inParameter; + this.inParameter = true; + let param = this.parseBindingElement(); + this.inParameter = previousInParameter; + return param; + } + + parseParams() { + let startState = this.startNode(); + this.expect(TokenType.LPAREN); + + let items = [], rest = null; + while (!this.match(TokenType.RPAREN)) { + if (this.eat(TokenType.ELLIPSIS)) { + rest = this.parseBindingTarget(); + if (this.lookahead.type === TokenType.ASSIGN) { + throw this.createError(ErrorMessages.UNEXPECTED_REST_PARAMETERS_INITIALIZATION); + } + if (this.match(TokenType.COMMA)) { + throw this.createError(ErrorMessages.UNEXPECTED_COMMA_AFTER_REST); + } + break; + } + items.push(this.parseParam()); + if (this.match(TokenType.RPAREN)) break; + this.expect(TokenType.COMMA); + } + + this.expect(TokenType.RPAREN); + + return this.finishNode(new AST.FormalParameters({ items, rest }), startState); + } +} diff --git a/repl/src/shift-parser/tokenizer.js b/repl/src/shift-parser/tokenizer.js new file mode 100644 index 00000000..3bbeb609 --- /dev/null +++ b/repl/src/shift-parser/tokenizer.js @@ -0,0 +1,1505 @@ +/** + * Copyright 2014 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import { getHexValue, isLineTerminator, isWhiteSpace, isIdentifierStart, isIdentifierPart, isDecimalDigit } from './utils'; +import { ErrorMessages } from './errors'; + +export const TokenClass = { + Eof: { name: '' }, + Ident: { name: 'Identifier', isIdentifierName: true }, + Keyword: { name: 'Keyword', isIdentifierName: true }, + NumericLiteral: { name: 'Numeric' }, + TemplateElement: { name: 'Template' }, + Punctuator: { name: 'Punctuator' }, + StringLiteral: { name: 'String' }, + RegularExpression: { name: 'RegularExpression' }, + Illegal: { name: 'Illegal' }, +}; + +export const TokenType = { + EOS: { klass: TokenClass.Eof, name: 'EOS' }, + LPAREN: { klass: TokenClass.Punctuator, name: '(' }, + RPAREN: { klass: TokenClass.Punctuator, name: ')' }, + LBRACK: { klass: TokenClass.Punctuator, name: '[' }, + RBRACK: { klass: TokenClass.Punctuator, name: ']' }, + LBRACE: { klass: TokenClass.Punctuator, name: '{' }, + RBRACE: { klass: TokenClass.Punctuator, name: '}' }, + COLON: { klass: TokenClass.Punctuator, name: ':' }, + SEMICOLON: { klass: TokenClass.Punctuator, name: ';' }, + PERIOD: { klass: TokenClass.Punctuator, name: '.' }, + ELLIPSIS: { klass: TokenClass.Punctuator, name: '...' }, + ARROW: { klass: TokenClass.Punctuator, name: '=>' }, + CONDITIONAL: { klass: TokenClass.Punctuator, name: '?' }, + INC: { klass: TokenClass.Punctuator, name: '++' }, + DEC: { klass: TokenClass.Punctuator, name: '--' }, + ASSIGN: { klass: TokenClass.Punctuator, name: '=' }, + ASSIGN_BIT_OR: { klass: TokenClass.Punctuator, name: '|=' }, + ASSIGN_BIT_XOR: { klass: TokenClass.Punctuator, name: '^=' }, + ASSIGN_BIT_AND: { klass: TokenClass.Punctuator, name: '&=' }, + ASSIGN_SHL: { klass: TokenClass.Punctuator, name: '<<=' }, + ASSIGN_SHR: { klass: TokenClass.Punctuator, name: '>>=' }, + ASSIGN_SHR_UNSIGNED: { klass: TokenClass.Punctuator, name: '>>>=' }, + ASSIGN_ADD: { klass: TokenClass.Punctuator, name: '+=' }, + ASSIGN_SUB: { klass: TokenClass.Punctuator, name: '-=' }, + ASSIGN_MUL: { klass: TokenClass.Punctuator, name: '*=' }, + ASSIGN_DIV: { klass: TokenClass.Punctuator, name: '/=' }, + ASSIGN_MOD: { klass: TokenClass.Punctuator, name: '%=' }, + ASSIGN_EXP: { klass: TokenClass.Punctuator, name: '**=' }, + COMMA: { klass: TokenClass.Punctuator, name: ',' }, + OR: { klass: TokenClass.Punctuator, name: '||' }, + AND: { klass: TokenClass.Punctuator, name: '&&' }, + BIT_OR: { klass: TokenClass.Punctuator, name: '|' }, + BIT_XOR: { klass: TokenClass.Punctuator, name: '^' }, + BIT_AND: { klass: TokenClass.Punctuator, name: '&' }, + SHL: { klass: TokenClass.Punctuator, name: '<<' }, + SHR: { klass: TokenClass.Punctuator, name: '>>' }, + SHR_UNSIGNED: { klass: TokenClass.Punctuator, name: '>>>' }, + ADD: { klass: TokenClass.Punctuator, name: '+' }, + SUB: { klass: TokenClass.Punctuator, name: '-' }, + MUL: { klass: TokenClass.Punctuator, name: '*' }, + DIV: { klass: TokenClass.Punctuator, name: '/' }, + MOD: { klass: TokenClass.Punctuator, name: '%' }, + EXP: { klass: TokenClass.Punctuator, name: '**' }, + EQ: { klass: TokenClass.Punctuator, name: '==' }, + NE: { klass: TokenClass.Punctuator, name: '!=' }, + EQ_STRICT: { klass: TokenClass.Punctuator, name: '===' }, + NE_STRICT: { klass: TokenClass.Punctuator, name: '!==' }, + LT: { klass: TokenClass.Punctuator, name: '<' }, + GT: { klass: TokenClass.Punctuator, name: '>' }, + LTE: { klass: TokenClass.Punctuator, name: '<=' }, + GTE: { klass: TokenClass.Punctuator, name: '>=' }, + INSTANCEOF: { klass: TokenClass.Keyword, name: 'instanceof' }, + IN: { klass: TokenClass.Keyword, name: 'in' }, + NOT: { klass: TokenClass.Punctuator, name: '!' }, + BIT_NOT: { klass: TokenClass.Punctuator, name: '~' }, + ASYNC: { klass: TokenClass.Keyword, name: 'async' }, + AWAIT: { klass: TokenClass.Keyword, name: 'await' }, + ENUM: { klass: TokenClass.Keyword, name: 'enum' }, + DELETE: { klass: TokenClass.Keyword, name: 'delete' }, + TYPEOF: { klass: TokenClass.Keyword, name: 'typeof' }, + VOID: { klass: TokenClass.Keyword, name: 'void' }, + BREAK: { klass: TokenClass.Keyword, name: 'break' }, + CASE: { klass: TokenClass.Keyword, name: 'case' }, + CATCH: { klass: TokenClass.Keyword, name: 'catch' }, + CLASS: { klass: TokenClass.Keyword, name: 'class' }, + CONTINUE: { klass: TokenClass.Keyword, name: 'continue' }, + DEBUGGER: { klass: TokenClass.Keyword, name: 'debugger' }, + DEFAULT: { klass: TokenClass.Keyword, name: 'default' }, + DO: { klass: TokenClass.Keyword, name: 'do' }, + ELSE: { klass: TokenClass.Keyword, name: 'else' }, + EXPORT: { klass: TokenClass.Keyword, name: 'export' }, + EXTENDS: { klass: TokenClass.Keyword, name: 'extends' }, + FINALLY: { klass: TokenClass.Keyword, name: 'finally' }, + FOR: { klass: TokenClass.Keyword, name: 'for' }, + FUNCTION: { klass: TokenClass.Keyword, name: 'function' }, + IF: { klass: TokenClass.Keyword, name: 'if' }, + IMPORT: { klass: TokenClass.Keyword, name: 'import' }, + LET: { klass: TokenClass.Keyword, name: 'let' }, + NEW: { klass: TokenClass.Keyword, name: 'new' }, + RETURN: { klass: TokenClass.Keyword, name: 'return' }, + SUPER: { klass: TokenClass.Keyword, name: 'super' }, + SWITCH: { klass: TokenClass.Keyword, name: 'switch' }, + THIS: { klass: TokenClass.Keyword, name: 'this' }, + THROW: { klass: TokenClass.Keyword, name: 'throw' }, + TRY: { klass: TokenClass.Keyword, name: 'try' }, + VAR: { klass: TokenClass.Keyword, name: 'var' }, + WHILE: { klass: TokenClass.Keyword, name: 'while' }, + WITH: { klass: TokenClass.Keyword, name: 'with' }, + NULL: { klass: TokenClass.Keyword, name: 'null' }, + TRUE: { klass: TokenClass.Keyword, name: 'true' }, + FALSE: { klass: TokenClass.Keyword, name: 'false' }, + YIELD: { klass: TokenClass.Keyword, name: 'yield' }, + NUMBER: { klass: TokenClass.NumericLiteral, name: '' }, + STRING: { klass: TokenClass.StringLiteral, name: '' }, + REGEXP: { klass: TokenClass.RegularExpression, name: '' }, + IDENTIFIER: { klass: TokenClass.Ident, name: '' }, + CONST: { klass: TokenClass.Keyword, name: 'const' }, + TEMPLATE: { klass: TokenClass.TemplateElement, name: '' }, + ESCAPED_KEYWORD: { klass: TokenClass.Keyword, name: '' }, + ILLEGAL: { klass: TokenClass.Illegal, name: '' }, +}; + +const TT = TokenType; +const I = TT.ILLEGAL; +const F = false; +const T = true; + +const ONE_CHAR_PUNCTUATOR = [ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, TT.NOT, I, I, I, + TT.MOD, TT.BIT_AND, I, TT.LPAREN, TT.RPAREN, TT.MUL, TT.ADD, TT.COMMA, TT.SUB, TT.PERIOD, TT.DIV, I, I, I, I, I, I, I, + I, I, I, TT.COLON, TT.SEMICOLON, TT.LT, TT.ASSIGN, TT.GT, TT.CONDITIONAL, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, + I, I, I, I, I, I, I, I, I, I, I, I, TT.LBRACK, I, TT.RBRACK, TT.BIT_XOR, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, + I, I, I, I, I, I, I, I, I, I, I, I, I, TT.LBRACE, TT.BIT_OR, TT.RBRACE, TT.BIT_NOT, +]; + +const PUNCTUATOR_START = [ + F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, T, T, + F, T, T, T, T, T, T, F, T, F, F, F, F, F, F, F, F, F, F, T, T, T, T, T, T, F, F, F, F, F, F, F, F, F, F, F, F, F, F, + F, F, F, F, F, F, F, F, F, F, F, F, F, T, F, T, T, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, + F, F, F, F, F, F, T, T, T, T, F, +]; + +export class JsError extends Error { + constructor(index, line, column, msg) { + super(msg); + this.index = index; + // Safari defines these properties as non-writable and non-configurable on Error objects + try { + this.line = line; + this.column = column; + } catch (e) {} + // define these as well so Safari still has access to this info + this.parseErrorLine = line; + this.parseErrorColumn = column; + this.description = msg; + this.message = `[${line}:${column}]: ${msg}`; + } +} + +function fromCodePoint(cp) { + if (cp <= 0xFFFF) return String.fromCharCode(cp); + let cu1 = String.fromCharCode(Math.floor((cp - 0x10000) / 0x400) + 0xD800); + let cu2 = String.fromCharCode((cp - 0x10000) % 0x400 + 0xDC00); + return cu1 + cu2; +} + +function decodeUtf16(lead, trail) { + return (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000; +} + +export default class Tokenizer { + constructor(source) { + this.source = source; + this.index = 0; + this.line = 0; + this.lineStart = 0; + this.startIndex = 0; + this.startLine = 0; + this.startLineStart = 0; + this.lastIndex = 0; + this.lastLine = 0; + this.lastLineStart = 0; + this.hasLineTerminatorBeforeNext = false; + this.tokenIndex = 0; + } + + saveLexerState() { + return { + source: this.source, + index: this.index, + line: this.line, + lineStart: this.lineStart, + startIndex: this.startIndex, + startLine: this.startLine, + startLineStart: this.startLineStart, + lastIndex: this.lastIndex, + lastLine: this.lastLine, + lastLineStart: this.lastLineStart, + lookahead: this.lookahead, + hasLineTerminatorBeforeNext: this.hasLineTerminatorBeforeNext, + tokenIndex: this.tokenIndex, + }; + } + + restoreLexerState(state) { + this.source = state.source; + this.index = state.index; + this.line = state.line; + this.lineStart = state.lineStart; + this.startIndex = state.startIndex; + this.startLine = state.startLine; + this.startLineStart = state.startLineStart; + this.lastIndex = state.lastIndex; + this.lastLine = state.lastLine; + this.lastLineStart = state.lastLineStart; + this.lookahead = state.lookahead; + this.hasLineTerminatorBeforeNext = state.hasLineTerminatorBeforeNext; + this.tokenIndex = state.tokenIndex; + } + + createILLEGAL() { + this.startIndex = this.index; + this.startLine = this.line; + this.startLineStart = this.lineStart; + return this.index < this.source.length + ? this.createError(ErrorMessages.UNEXPECTED_ILLEGAL_TOKEN, this.source.charAt(this.index)) + : this.createError(ErrorMessages.UNEXPECTED_EOS); + } + + createUnexpected(token) { + switch (token.type.klass) { + case TokenClass.Eof: + return this.createError(ErrorMessages.UNEXPECTED_EOS); + case TokenClass.Ident: + return this.createError(ErrorMessages.UNEXPECTED_IDENTIFIER); + case TokenClass.Keyword: + if (token.type === TokenType.ESCAPED_KEYWORD) { + return this.createError(ErrorMessages.UNEXPECTED_ESCAPED_KEYWORD); + } + return this.createError(ErrorMessages.UNEXPECTED_TOKEN, token.slice.text); + case TokenClass.NumericLiteral: + return this.createError(ErrorMessages.UNEXPECTED_NUMBER); + case TokenClass.TemplateElement: + return this.createError(ErrorMessages.UNEXPECTED_TEMPLATE); + case TokenClass.Punctuator: + return this.createError(ErrorMessages.UNEXPECTED_TOKEN, token.type.name); + case TokenClass.StringLiteral: + return this.createError(ErrorMessages.UNEXPECTED_STRING); + // the other token classes are RegularExpression and Illegal, but they cannot reach here + } + // istanbul ignore next + throw new Error('Unreachable: unexpected token of class ' + token.type.klass); + } + + createError(message, ...params) { + let msg; + if (typeof message === 'function') { + msg = message(...params); + } else { + msg = message; + } + return new JsError(this.startIndex, this.startLine + 1, this.startIndex - this.startLineStart + 1, msg); + } + + createErrorWithLocation(location, message) { + /* istanbul ignore next */ + let msg = message.replace(/\{(\d+)\}/g, (_, n) => JSON.stringify(arguments[+n + 2])); + if (location.slice && location.slice.startLocation) { + location = location.slice.startLocation; + } + return new JsError(location.offset, location.line, location.column + 1, msg); + } + + static cse2(id, ch1, ch2) { + return id.charAt(1) === ch1 && id.charAt(2) === ch2; + } + + static cse3(id, ch1, ch2, ch3) { + return id.charAt(1) === ch1 && id.charAt(2) === ch2 && id.charAt(3) === ch3; + } + + static cse4(id, ch1, ch2, ch3, ch4) { + return id.charAt(1) === ch1 && id.charAt(2) === ch2 && id.charAt(3) === ch3 && id.charAt(4) === ch4; + } + + static cse5(id, ch1, ch2, ch3, ch4, ch5) { + return id.charAt(1) === ch1 && id.charAt(2) === ch2 && id.charAt(3) === ch3 && id.charAt(4) === ch4 && id.charAt(5) === ch5; + } + + static cse6(id, ch1, ch2, ch3, ch4, ch5, ch6) { + return id.charAt(1) === ch1 && id.charAt(2) === ch2 && id.charAt(3) === ch3 && id.charAt(4) === ch4 && id.charAt(5) === ch5 && id.charAt(6) === ch6; + } + + static cse7(id, ch1, ch2, ch3, ch4, ch5, ch6, ch7) { + return id.charAt(1) === ch1 && id.charAt(2) === ch2 && id.charAt(3) === ch3 && id.charAt(4) === ch4 && id.charAt(5) === ch5 && id.charAt(6) === ch6 && id.charAt(7) === ch7; + } + + getKeyword(id) { + if (id.length === 1 || id.length > 10) { + return TokenType.IDENTIFIER; + } + + /* istanbul ignore next */ + switch (id.length) { + case 2: + switch (id.charAt(0)) { + case 'i': + switch (id.charAt(1)) { + case 'f': + return TokenType.IF; + case 'n': + return TokenType.IN; + default: + break; + } + break; + case 'd': + if (id.charAt(1) === 'o') { + return TokenType.DO; + } + break; + } + break; + case 3: + switch (id.charAt(0)) { + case 'v': + if (Tokenizer.cse2(id, 'a', 'r')) { + return TokenType.VAR; + } + break; + case 'f': + if (Tokenizer.cse2(id, 'o', 'r')) { + return TokenType.FOR; + } + break; + case 'n': + if (Tokenizer.cse2(id, 'e', 'w')) { + return TokenType.NEW; + } + break; + case 't': + if (Tokenizer.cse2(id, 'r', 'y')) { + return TokenType.TRY; + } + break; + case 'l': + if (Tokenizer.cse2(id, 'e', 't')) { + return TokenType.LET; + } + break; + } + break; + case 4: + switch (id.charAt(0)) { + case 't': + if (Tokenizer.cse3(id, 'h', 'i', 's')) { + return TokenType.THIS; + } else if (Tokenizer.cse3(id, 'r', 'u', 'e')) { + return TokenType.TRUE; + } + break; + case 'n': + if (Tokenizer.cse3(id, 'u', 'l', 'l')) { + return TokenType.NULL; + } + break; + case 'e': + if (Tokenizer.cse3(id, 'l', 's', 'e')) { + return TokenType.ELSE; + } else if (Tokenizer.cse3(id, 'n', 'u', 'm')) { + return TokenType.ENUM; + } + break; + case 'c': + if (Tokenizer.cse3(id, 'a', 's', 'e')) { + return TokenType.CASE; + } + break; + case 'v': + if (Tokenizer.cse3(id, 'o', 'i', 'd')) { + return TokenType.VOID; + } + break; + case 'w': + if (Tokenizer.cse3(id, 'i', 't', 'h')) { + return TokenType.WITH; + } + break; + } + break; + case 5: + switch (id.charAt(0)) { + case 'a': + if (Tokenizer.cse4(id, 's', 'y', 'n', 'c')) { + return TokenType.ASYNC; + } + if (Tokenizer.cse4(id, 'w', 'a', 'i', 't')) { + return TokenType.AWAIT; + } + break; + case 'w': + if (Tokenizer.cse4(id, 'h', 'i', 'l', 'e')) { + return TokenType.WHILE; + } + break; + case 'b': + if (Tokenizer.cse4(id, 'r', 'e', 'a', 'k')) { + return TokenType.BREAK; + } + break; + case 'f': + if (Tokenizer.cse4(id, 'a', 'l', 's', 'e')) { + return TokenType.FALSE; + } + break; + case 'c': + if (Tokenizer.cse4(id, 'a', 't', 'c', 'h')) { + return TokenType.CATCH; + } else if (Tokenizer.cse4(id, 'o', 'n', 's', 't')) { + return TokenType.CONST; + } else if (Tokenizer.cse4(id, 'l', 'a', 's', 's')) { + return TokenType.CLASS; + } + break; + case 't': + if (Tokenizer.cse4(id, 'h', 'r', 'o', 'w')) { + return TokenType.THROW; + } + break; + case 'y': + if (Tokenizer.cse4(id, 'i', 'e', 'l', 'd')) { + return TokenType.YIELD; + } + break; + case 's': + if (Tokenizer.cse4(id, 'u', 'p', 'e', 'r')) { + return TokenType.SUPER; + } + break; + } + break; + case 6: + switch (id.charAt(0)) { + case 'r': + if (Tokenizer.cse5(id, 'e', 't', 'u', 'r', 'n')) { + return TokenType.RETURN; + } + break; + case 't': + if (Tokenizer.cse5(id, 'y', 'p', 'e', 'o', 'f')) { + return TokenType.TYPEOF; + } + break; + case 'd': + if (Tokenizer.cse5(id, 'e', 'l', 'e', 't', 'e')) { + return TokenType.DELETE; + } + break; + case 's': + if (Tokenizer.cse5(id, 'w', 'i', 't', 'c', 'h')) { + return TokenType.SWITCH; + } + break; + case 'e': + if (Tokenizer.cse5(id, 'x', 'p', 'o', 'r', 't')) { + return TokenType.EXPORT; + } + break; + case 'i': + if (Tokenizer.cse5(id, 'm', 'p', 'o', 'r', 't')) { + return TokenType.IMPORT; + } + break; + } + break; + case 7: + switch (id.charAt(0)) { + case 'd': + if (Tokenizer.cse6(id, 'e', 'f', 'a', 'u', 'l', 't')) { + return TokenType.DEFAULT; + } + break; + case 'f': + if (Tokenizer.cse6(id, 'i', 'n', 'a', 'l', 'l', 'y')) { + return TokenType.FINALLY; + } + break; + case 'e': + if (Tokenizer.cse6(id, 'x', 't', 'e', 'n', 'd', 's')) { + return TokenType.EXTENDS; + } + break; + } + break; + case 8: + switch (id.charAt(0)) { + case 'f': + if (Tokenizer.cse7(id, 'u', 'n', 'c', 't', 'i', 'o', 'n')) { + return TokenType.FUNCTION; + } + break; + case 'c': + if (Tokenizer.cse7(id, 'o', 'n', 't', 'i', 'n', 'u', 'e')) { + return TokenType.CONTINUE; + } + break; + case 'd': + if (Tokenizer.cse7(id, 'e', 'b', 'u', 'g', 'g', 'e', 'r')) { + return TokenType.DEBUGGER; + } + break; + } + break; + case 10: + if (id === 'instanceof') { + return TokenType.INSTANCEOF; + } + break; + } + return TokenType.IDENTIFIER; + } + + skipSingleLineComment(offset) { + this.index += offset; + while (this.index < this.source.length) { + /** + * @type {Number} + */ + let chCode = this.source.charCodeAt(this.index); + this.index++; + if (isLineTerminator(chCode)) { + this.hasLineTerminatorBeforeNext = true; + if (chCode === 0xD /* "\r" */ && this.source.charCodeAt(this.index) === 0xA /* "\n" */) { + this.index++; + } + this.lineStart = this.index; + this.line++; + return; + } + } + } + + skipMultiLineComment() { + this.index += 2; + const length = this.source.length; + let isLineStart = false; + while (this.index < length) { + let chCode = this.source.charCodeAt(this.index); + if (chCode < 0x80) { + switch (chCode) { + case 42: // "*" + // Block comment ends with "*/". + if (this.source.charAt(this.index + 1) === '/') { + this.index = this.index + 2; + return isLineStart; + } + this.index++; + break; + case 10: // "\n" + isLineStart = true; + this.hasLineTerminatorBeforeNext = true; + this.index++; + this.lineStart = this.index; + this.line++; + break; + case 13: // "\r": + isLineStart = true; + this.hasLineTerminatorBeforeNext = true; + if (this.source.charAt(this.index + 1) === '\n') { + this.index++; + } + this.index++; + this.lineStart = this.index; + this.line++; + break; + default: + this.index++; + } + } else if (chCode === 0x2028 || chCode === 0x2029) { + isLineStart = true; + this.hasLineTerminatorBeforeNext = true; + this.index++; + this.lineStart = this.index; + this.line++; + } else { + this.index++; + } + } + throw this.createILLEGAL(); + } + + + skipComment() { + this.hasLineTerminatorBeforeNext = false; + + let isLineStart = this.index === 0; + const length = this.source.length; + + while (this.index < length) { + let chCode = this.source.charCodeAt(this.index); + if (isWhiteSpace(chCode)) { + this.index++; + } else if (isLineTerminator(chCode)) { + this.hasLineTerminatorBeforeNext = true; + this.index++; + if (chCode === 13 /* "\r" */ && this.source.charAt(this.index) === '\n') { + this.index++; + } + this.lineStart = this.index; + this.line++; + isLineStart = true; + } else if (chCode === 47 /* "/" */) { + if (this.index + 1 >= length) { + break; + } + chCode = this.source.charCodeAt(this.index + 1); + if (chCode === 47 /* "/" */) { + this.skipSingleLineComment(2); + isLineStart = true; + } else if (chCode === 42 /* "*" */) { + isLineStart = this.skipMultiLineComment() || isLineStart; + } else { + break; + } + } else if (!this.moduleIsTheGoalSymbol && isLineStart && chCode === 45 /* "-" */) { + if (this.index + 2 >= length) { + break; + } + // U+003E is ">" + if (this.source.charAt(this.index + 1) === '-' && this.source.charAt(this.index + 2) === '>') { + // "-->" is a single-line comment + this.skipSingleLineComment(3); + } else { + break; + } + } else if (!this.moduleIsTheGoalSymbol && chCode === 60 /* "<" */) { + if (this.source.slice(this.index + 1, this.index + 4) === '!--') { + this.skipSingleLineComment(4); + isLineStart = true; + } else { + break; + } + } else { + break; + } + } + } + + scanHexEscape2() { + if (this.index + 2 > this.source.length) { + return -1; + } + let r1 = getHexValue(this.source.charAt(this.index)); + if (r1 === -1) { + return -1; + } + let r2 = getHexValue(this.source.charAt(this.index + 1)); + if (r2 === -1) { + return -1; + } + this.index += 2; + return r1 << 4 | r2; + } + + scanUnicode() { + if (this.source.charAt(this.index) === '{') { + // \u{HexDigits} + let i = this.index + 1; + let hexDigits = 0, ch; + while (i < this.source.length) { + ch = this.source.charAt(i); + let hex = getHexValue(ch); + if (hex === -1) { + break; + } + hexDigits = hexDigits << 4 | hex; + if (hexDigits > 0x10FFFF) { + throw this.createILLEGAL(); + } + i++; + } + if (ch !== '}') { + throw this.createILLEGAL(); + } + if (i === this.index + 1) { + ++this.index; // This is so that the error is 'Unexpected "}"' instead of 'Unexpected "{"'. + throw this.createILLEGAL(); + } + this.index = i + 1; + return hexDigits; + } + // \uHex4Digits + if (this.index + 4 > this.source.length) { + return -1; + } + let r1 = getHexValue(this.source.charAt(this.index)); + if (r1 === -1) { + return -1; + } + let r2 = getHexValue(this.source.charAt(this.index + 1)); + if (r2 === -1) { + return -1; + } + let r3 = getHexValue(this.source.charAt(this.index + 2)); + if (r3 === -1) { + return -1; + } + let r4 = getHexValue(this.source.charAt(this.index + 3)); + if (r4 === -1) { + return -1; + } + this.index += 4; + return r1 << 12 | r2 << 8 | r3 << 4 | r4; + } + + getEscapedIdentifier() { + let id = ''; + let check = isIdentifierStart; + + while (this.index < this.source.length) { + let ch = this.source.charAt(this.index); + let code = ch.charCodeAt(0); + let start = this.index; + ++this.index; + if (ch === '\\') { + if (this.index >= this.source.length) { + throw this.createILLEGAL(); + } + if (this.source.charAt(this.index) !== 'u') { + throw this.createILLEGAL(); + } + ++this.index; + code = this.scanUnicode(); + if (code < 0) { + throw this.createILLEGAL(); + } + ch = fromCodePoint(code); + } else if (code >= 0xD800 && code <= 0xDBFF) { + if (this.index >= this.source.length) { + throw this.createILLEGAL(); + } + let lowSurrogateCode = this.source.charCodeAt(this.index); + ++this.index; + if (!(lowSurrogateCode >= 0xDC00 && lowSurrogateCode <= 0xDFFF)) { + throw this.createILLEGAL(); + } + code = decodeUtf16(code, lowSurrogateCode); + ch = fromCodePoint(code); + } + if (!check(code)) { + if (id.length < 1) { + throw this.createILLEGAL(); + } + this.index = start; + return id; + } + check = isIdentifierPart; + id += ch; + } + return id; + } + + getIdentifier() { + let start = this.index; + let l = this.source.length; + let i = this.index; + let check = isIdentifierStart; + while (i < l) { + let ch = this.source.charAt(i); + let code = ch.charCodeAt(0); + if (ch === '\\' || code >= 0xD800 && code <= 0xDBFF) { + // Go back and try the hard one. + this.index = start; + return this.getEscapedIdentifier(); + } + if (!check(code)) { + this.index = i; + return this.source.slice(start, i); + } + ++i; + check = isIdentifierPart; + } + this.index = i; + return this.source.slice(start, i); + } + + scanIdentifier() { + let startLocation = this.getLocation(); + let start = this.index; + + // Backslash (U+005C) starts an escaped character. + let id = this.source.charAt(this.index) === '\\' ? this.getEscapedIdentifier() : this.getIdentifier(); + + let slice = this.getSlice(start, startLocation); + slice.text = id; + let hasEscape = this.index - start !== id.length; + + let type = this.getKeyword(id); + if (hasEscape && type !== TokenType.IDENTIFIER) { + type = TokenType.ESCAPED_KEYWORD; + } + return { type, value: id, slice, escaped: hasEscape }; + } + + getLocation() { + return { + line: this.startLine + 1, + column: this.startIndex - this.startLineStart, + offset: this.startIndex, + }; + } + + getLastTokenEndLocation() { + return { + line: this.lastLine + 1, + column: this.lastIndex - this.lastLineStart, + offset: this.lastIndex, + }; + } + + getSlice(start, startLocation) { + return { text: this.source.slice(start, this.index), start, startLocation, end: this.index }; + } + + scanPunctuatorHelper() { + let ch1 = this.source.charAt(this.index); + + switch (ch1) { + // Check for most common single-character punctuators. + case '.': { + let ch2 = this.source.charAt(this.index + 1); + if (ch2 !== '.') return TokenType.PERIOD; + let ch3 = this.source.charAt(this.index + 2); + if (ch3 !== '.') return TokenType.PERIOD; + return TokenType.ELLIPSIS; + } + case '(': + return TokenType.LPAREN; + case ')': + case ';': + case ',': + return ONE_CHAR_PUNCTUATOR[ch1.charCodeAt(0)]; + case '{': + return TokenType.LBRACE; + case '}': + case '[': + case ']': + case ':': + case '?': + case '~': + return ONE_CHAR_PUNCTUATOR[ch1.charCodeAt(0)]; + default: + // "=" (U+003D) marks an assignment or comparison operator. + if (this.index + 1 < this.source.length && this.source.charAt(this.index + 1) === '=') { + switch (ch1) { + case '=': + if (this.index + 2 < this.source.length && this.source.charAt(this.index + 2) === '=') { + return TokenType.EQ_STRICT; + } + return TokenType.EQ; + case '!': + if (this.index + 2 < this.source.length && this.source.charAt(this.index + 2) === '=') { + return TokenType.NE_STRICT; + } + return TokenType.NE; + case '|': + return TokenType.ASSIGN_BIT_OR; + case '+': + return TokenType.ASSIGN_ADD; + case '-': + return TokenType.ASSIGN_SUB; + case '*': + return TokenType.ASSIGN_MUL; + case '<': + return TokenType.LTE; + case '>': + return TokenType.GTE; + case '/': + return TokenType.ASSIGN_DIV; + case '%': + return TokenType.ASSIGN_MOD; + case '^': + return TokenType.ASSIGN_BIT_XOR; + case '&': + return TokenType.ASSIGN_BIT_AND; + // istanbul ignore next + default: + break; // failed + } + } + } + + if (this.index + 1 < this.source.length) { + let ch2 = this.source.charAt(this.index + 1); + if (ch1 === ch2) { + if (this.index + 2 < this.source.length) { + let ch3 = this.source.charAt(this.index + 2); + if (ch1 === '>' && ch3 === '>') { + // 4-character punctuator: >>>= + if (this.index + 3 < this.source.length && this.source.charAt(this.index + 3) === '=') { + return TokenType.ASSIGN_SHR_UNSIGNED; + } + return TokenType.SHR_UNSIGNED; + } + + if (ch1 === '<' && ch3 === '=') { + return TokenType.ASSIGN_SHL; + } + + if (ch1 === '>' && ch3 === '=') { + return TokenType.ASSIGN_SHR; + } + + if (ch1 === '*' && ch3 === '=') { + return TokenType.ASSIGN_EXP; + } + } + // Other 2-character punctuators: ++ -- << >> && || + switch (ch1) { + case '*': + return TokenType.EXP; + case '+': + return TokenType.INC; + case '-': + return TokenType.DEC; + case '<': + return TokenType.SHL; + case '>': + return TokenType.SHR; + case '&': + return TokenType.AND; + case '|': + return TokenType.OR; + // istanbul ignore next + default: + break; // failed + } + } else if (ch1 === '=' && ch2 === '>') { + return TokenType.ARROW; + } + } + + return ONE_CHAR_PUNCTUATOR[ch1.charCodeAt(0)]; + } + + // 7.7 Punctuators + scanPunctuator() { + let startLocation = this.getLocation(); + let start = this.index; + let subType = this.scanPunctuatorHelper(); + this.index += subType.name.length; + return { type: subType, value: subType.name, slice: this.getSlice(start, startLocation) }; + } + + scanHexLiteral(start, startLocation) { + let i = this.index; + while (i < this.source.length) { + let ch = this.source.charAt(i); + let hex = getHexValue(ch); + if (hex === -1) { + break; + } + i++; + } + + if (this.index === i) { + throw this.createILLEGAL(); + } + + if (i < this.source.length && isIdentifierStart(this.source.charCodeAt(i))) { + throw this.createILLEGAL(); + } + + this.index = i; + + let slice = this.getSlice(start, startLocation); + return { type: TokenType.NUMBER, value: parseInt(slice.text.substr(2), 16), slice }; + } + + scanBinaryLiteral(start, startLocation) { + let offset = this.index - start; + + while (this.index < this.source.length) { + let ch = this.source.charAt(this.index); + if (ch !== '0' && ch !== '1') { + break; + } + this.index++; + } + + if (this.index - start <= offset) { + throw this.createILLEGAL(); + } + + if (this.index < this.source.length && (isIdentifierStart(this.source.charCodeAt(this.index)) + || isDecimalDigit(this.source.charCodeAt(this.index)))) { + throw this.createILLEGAL(); + } + + return { + type: TokenType.NUMBER, + value: parseInt(this.getSlice(start, startLocation).text.substr(offset), 2), + slice: this.getSlice(start, startLocation), + octal: false, + noctal: false, + }; + } + + scanOctalLiteral(start, startLocation) { + while (this.index < this.source.length) { + let ch = this.source.charAt(this.index); + if (ch >= '0' && ch <= '7') { + this.index++; + } else if (isIdentifierPart(ch.charCodeAt(0))) { + throw this.createILLEGAL(); + } else { + break; + } + } + + if (this.index - start === 2) { + throw this.createILLEGAL(); + } + + return { + type: TokenType.NUMBER, + value: parseInt(this.getSlice(start, startLocation).text.substr(2), 8), + slice: this.getSlice(start, startLocation), + octal: false, + noctal: false, + }; + } + + scanLegacyOctalLiteral(start, startLocation) { + let isOctal = true; + + while (this.index < this.source.length) { + let ch = this.source.charAt(this.index); + if (ch >= '0' && ch <= '7') { + this.index++; + } else if (ch === '8' || ch === '9') { + isOctal = false; + this.index++; + } else if (isIdentifierPart(ch.charCodeAt(0))) { + throw this.createILLEGAL(); + } else { + break; + } + } + + let slice = this.getSlice(start, startLocation); + if (!isOctal) { + this.eatDecimalLiteralSuffix(); + return { + type: TokenType.NUMBER, + slice, + value: +slice.text, + octal: true, + noctal: !isOctal, + }; + } + + return { + type: TokenType.NUMBER, + slice, + value: parseInt(slice.text.substr(1), 8), + octal: true, + noctal: !isOctal, + }; + } + + scanNumericLiteral() { + let ch = this.source.charAt(this.index); + // assert(ch === "." || "0" <= ch && ch <= "9") + let startLocation = this.getLocation(); + let start = this.index; + + if (ch === '0') { + this.index++; + if (this.index < this.source.length) { + ch = this.source.charAt(this.index); + if (ch === 'x' || ch === 'X') { + this.index++; + return this.scanHexLiteral(start, startLocation); + } else if (ch === 'b' || ch === 'B') { + this.index++; + return this.scanBinaryLiteral(start, startLocation); + } else if (ch === 'o' || ch === 'O') { + this.index++; + return this.scanOctalLiteral(start, startLocation); + } else if (ch >= '0' && ch <= '9') { + return this.scanLegacyOctalLiteral(start, startLocation); + } + } else { + let slice = this.getSlice(start, startLocation); + return { + type: TokenType.NUMBER, + value: +slice.text, + slice, + octal: false, + noctal: false, + }; + } + } else if (ch !== '.') { + // Must be "1".."9" + ch = this.source.charAt(this.index); + while (ch >= '0' && ch <= '9') { + this.index++; + if (this.index === this.source.length) { + let slice = this.getSlice(start, startLocation); + return { + type: TokenType.NUMBER, + value: +slice.text, + slice, + octal: false, + noctal: false, + }; + } + ch = this.source.charAt(this.index); + } + } + + this.eatDecimalLiteralSuffix(); + + if (this.index !== this.source.length && isIdentifierStart(this.source.charCodeAt(this.index))) { + throw this.createILLEGAL(); + } + + let slice = this.getSlice(start, startLocation); + return { + type: TokenType.NUMBER, + value: +slice.text, + slice, + octal: false, + noctal: false, + }; + } + + eatDecimalLiteralSuffix() { + let ch = this.source.charAt(this.index); + if (ch === '.') { + this.index++; + if (this.index === this.source.length) { + return; + } + + ch = this.source.charAt(this.index); + while (ch >= '0' && ch <= '9') { + this.index++; + if (this.index === this.source.length) { + return; + } + ch = this.source.charAt(this.index); + } + } + + // EOF not reached here + if (ch === 'e' || ch === 'E') { + this.index++; + if (this.index === this.source.length) { + throw this.createILLEGAL(); + } + + ch = this.source.charAt(this.index); + if (ch === '+' || ch === '-') { + this.index++; + if (this.index === this.source.length) { + throw this.createILLEGAL(); + } + ch = this.source.charAt(this.index); + } + + if (ch >= '0' && ch <= '9') { + while (ch >= '0' && ch <= '9') { + this.index++; + if (this.index === this.source.length) { + break; + } + ch = this.source.charAt(this.index); + } + } else { + throw this.createILLEGAL(); + } + } + } + + scanStringEscape(str, octal) { + this.index++; + if (this.index === this.source.length) { + throw this.createILLEGAL(); + } + let ch = this.source.charAt(this.index); + if (isLineTerminator(ch.charCodeAt(0))) { + this.index++; + if (ch === '\r' && this.source.charAt(this.index) === '\n') { + this.index++; + } + this.lineStart = this.index; + this.line++; + } else { + switch (ch) { + case 'n': + str += '\n'; + this.index++; + break; + case 'r': + str += '\r'; + this.index++; + break; + case 't': + str += '\t'; + this.index++; + break; + case 'u': + case 'x': { + let unescaped; + this.index++; + if (this.index >= this.source.length) { + throw this.createILLEGAL(); + } + unescaped = ch === 'u' ? this.scanUnicode() : this.scanHexEscape2(); + if (unescaped < 0) { + throw this.createILLEGAL(); + } + str += fromCodePoint(unescaped); + break; + } + case 'b': + str += '\b'; + this.index++; + break; + case 'f': + str += '\f'; + this.index++; + break; + case 'v': + str += '\u000B'; + this.index++; + break; + default: + if (ch >= '0' && ch <= '7') { + let octalStart = this.index; + let octLen = 1; + // 3 digits are only allowed when string starts + // with 0, 1, 2, 3 + if (ch >= '0' && ch <= '3') { + octLen = 0; + } + let code = 0; + while (octLen < 3 && ch >= '0' && ch <= '7') { + this.index++; + if (octLen > 0 || ch !== '0') { + octal = this.source.slice(octalStart, this.index); + } + code *= 8; + code += ch - '0'; + octLen++; + if (this.index === this.source.length) { + throw this.createILLEGAL(); + } + ch = this.source.charAt(this.index); + } + if (code === 0 && octLen === 1 && (ch === '8' || ch === '9')) { + octal = this.source.slice(octalStart, this.index + 1); + } + str += String.fromCharCode(code); + } else if (ch === '8' || ch === '9') { + throw this.createILLEGAL(); + } else { + str += ch; + this.index++; + } + } + } + return [str, octal]; + } + // 7.8.4 String Literals + scanStringLiteral() { + let str = ''; + + let quote = this.source.charAt(this.index); + // assert((quote === "\"" || quote === """), "String literal must starts with a quote") + + let startLocation = this.getLocation(); + let start = this.index; + this.index++; + + let octal = null; + while (this.index < this.source.length) { + let ch = this.source.charAt(this.index); + if (ch === quote) { + this.index++; + return { type: TokenType.STRING, slice: this.getSlice(start, startLocation), str, octal }; + } else if (ch === '\\') { + [str, octal] = this.scanStringEscape(str, octal); + } else if (isLineTerminator(ch.charCodeAt(0))) { + throw this.createILLEGAL(); + } else { + str += ch; + this.index++; + } + } + + throw this.createILLEGAL(); + } + + scanTemplateElement() { + let startLocation = this.getLocation(); + let start = this.index; + this.index++; + while (this.index < this.source.length) { + let ch = this.source.charCodeAt(this.index); + switch (ch) { + case 0x60: { // ` + this.index++; + return { type: TokenType.TEMPLATE, tail: true, slice: this.getSlice(start, startLocation) }; + } + case 0x24: { // $ + if (this.source.charCodeAt(this.index + 1) === 0x7B) { // { + this.index += 2; + return { type: TokenType.TEMPLATE, tail: false, slice: this.getSlice(start, startLocation) }; + } + this.index++; + break; + } + case 0x5C: { // \\ + let octal = this.scanStringEscape('', null)[1]; + if (octal != null) { + throw this.createError(ErrorMessages.NO_OCTALS_IN_TEMPLATES); + } + break; + } + case 0x0D: { // \r + this.line++; + this.index++; + if (this.index < this.source.length && this.source.charAt(this.index) === '\n') { + this.index++; + } + this.lineStart = this.index; + break; + } + case 0x0A: // \r + case 0x2028: + case 0x2029: { + this.line++; + this.index++; + this.lineStart = this.index; + break; + } + default: + this.index++; + } + } + + throw this.createILLEGAL(); + } + + scanRegExp(str) { + let startLocation = this.getLocation(); + let start = this.index; + + let terminated = false; + let classMarker = false; + while (this.index < this.source.length) { + let ch = this.source.charAt(this.index); + if (ch === '\\') { + str += ch; + this.index++; + ch = this.source.charAt(this.index); + // ECMA-262 7.8.5 + if (isLineTerminator(ch.charCodeAt(0))) { + throw this.createError(ErrorMessages.UNTERMINATED_REGEXP); + } + str += ch; + this.index++; + } else if (isLineTerminator(ch.charCodeAt(0))) { + throw this.createError(ErrorMessages.UNTERMINATED_REGEXP); + } else { + if (classMarker) { + if (ch === ']') { + classMarker = false; + } + } else if (ch === '/') { + terminated = true; + str += ch; + this.index++; + break; + } else if (ch === '[') { + classMarker = true; + } + str += ch; + this.index++; + } + } + + if (!terminated) { + throw this.createError(ErrorMessages.UNTERMINATED_REGEXP); + } + + while (this.index < this.source.length) { + let ch = this.source.charAt(this.index); + if (ch === '\\') { + throw this.createError(ErrorMessages.INVALID_REGEXP_FLAGS); + } + if (!isIdentifierPart(ch.charCodeAt(0))) { + break; + } + this.index++; + str += ch; + } + return { type: TokenType.REGEXP, value: str, slice: this.getSlice(start, startLocation) }; + } + + advance() { + let startLocation = this.getLocation(); + + this.lastIndex = this.index; + this.lastLine = this.line; + this.lastLineStart = this.lineStart; + + this.skipComment(); + + this.startIndex = this.index; + this.startLine = this.line; + this.startLineStart = this.lineStart; + + if (this.lastIndex === 0) { + this.lastIndex = this.index; + this.lastLine = this.line; + this.lastLineStart = this.lineStart; + } + + if (this.index >= this.source.length) { + return { type: TokenType.EOS, slice: this.getSlice(this.index, startLocation) }; + } + + let charCode = this.source.charCodeAt(this.index); + + if (charCode < 0x80) { + if (PUNCTUATOR_START[charCode]) { + return this.scanPunctuator(); + } + + if (isIdentifierStart(charCode) || charCode === 0x5C /* backslash (\) */) { + return this.scanIdentifier(); + } + + // Dot (.) U+002E can also start a floating-point number, hence the need + // to check the next character. + if (charCode === 0x2E) { + if (this.index + 1 < this.source.length && isDecimalDigit(this.source.charCodeAt(this.index + 1))) { + return this.scanNumericLiteral(); + } + return this.scanPunctuator(); + } + + // String literal starts with single quote (U+0027) or double quote (U+0022). + if (charCode === 0x27 || charCode === 0x22) { + return this.scanStringLiteral(); + } + + // Template literal starts with back quote (U+0060) + if (charCode === 0x60) { + return this.scanTemplateElement(); + } + + if (charCode /* "0" */ >= 0x30 && charCode <= 0x39 /* "9" */) { + return this.scanNumericLiteral(); + } + + // Slash (/) U+002F can also start a regex. + throw this.createILLEGAL(); + } else { + if (isIdentifierStart(charCode) || charCode >= 0xD800 && charCode <= 0xDBFF) { + return this.scanIdentifier(); + } + + throw this.createILLEGAL(); + } + } + + eof() { + return this.lookahead.type === TokenType.EOS; + } + + lex() { + let prevToken = this.lookahead; + this.lookahead = this.advance(); + this.tokenIndex++; + return prevToken; + } +} diff --git a/repl/src/shift-parser/unicode.js b/repl/src/shift-parser/unicode.js new file mode 100644 index 00000000..27906deb --- /dev/null +++ b/repl/src/shift-parser/unicode.js @@ -0,0 +1,11 @@ +// Generated by scripts/generate-unicode-data.js + +export const whitespaceArray = [5760, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8239, 8287, 12288, 65279]; +export const whitespaceBool = [false, false, false, false, false, false, false, false, false, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]; + +export const idStartLargeRegex = /^[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]$/; +export const idStartBool = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false]; + +export const idContinueLargeRegex = /^[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]$/; +export const idContinueBool = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false]; + diff --git a/repl/src/shift-parser/utils.js b/repl/src/shift-parser/utils.js new file mode 100644 index 00000000..e87b9662 --- /dev/null +++ b/repl/src/shift-parser/utils.js @@ -0,0 +1,105 @@ +/** + * Copyright 2017 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { whitespaceArray, whitespaceBool, idStartLargeRegex, idStartBool, idContinueLargeRegex, idContinueBool } from './unicode'; + + +const strictReservedWords = [ + 'null', + 'true', + 'false', + + 'implements', + 'interface', + 'package', + 'private', + 'protected', + 'public', + 'static', + 'let', + + 'if', + 'in', + 'do', + 'var', + 'for', + 'new', + 'try', + 'this', + 'else', + 'case', + 'void', + 'with', + 'enum', + 'while', + 'break', + 'catch', + 'throw', + 'const', + 'yield', + 'class', + 'super', + 'return', + 'typeof', + 'delete', + 'switch', + 'export', + 'import', + 'default', + 'finally', + 'extends', + 'function', + 'continue', + 'debugger', + 'instanceof', +]; + +export function isStrictModeReservedWord(id) { + return strictReservedWords.indexOf(id) !== -1; +} + +export function isWhiteSpace(ch) { + return ch < 128 ? whitespaceBool[ch] : ch === 0xA0 || ch > 0x167F && whitespaceArray.indexOf(ch) !== -1; +} + +export function isLineTerminator(ch) { + return ch === 0x0A || ch === 0x0D || ch === 0x2028 || ch === 0x2029; +} + +export function isIdentifierStart(ch) { + return ch < 128 ? idStartBool[ch] : idStartLargeRegex.test(String.fromCodePoint(ch)); +} + +export function isIdentifierPart(ch) { + return ch < 128 ? idContinueBool[ch] : idContinueLargeRegex.test(String.fromCodePoint(ch)); +} + +export function isDecimalDigit(ch) { + return ch >= 48 && ch <= 57; +} + +export function getHexValue(rune) { + if (rune >= '0' && rune <= '9') { + return rune.charCodeAt(0) - 48; + } + if (rune >= 'a' && rune <= 'f') { + return rune.charCodeAt(0) - 87; + } + if (rune >= 'A' && rune <= 'F') { + return rune.charCodeAt(0) - 55; + } + return -1; +} diff --git a/repl/src/shift-reducer/adapt.js b/repl/src/shift-reducer/adapt.js new file mode 100644 index 00000000..f3ec0c40 --- /dev/null +++ b/repl/src/shift-reducer/adapt.js @@ -0,0 +1,418 @@ +// Generated by generate-adapt.js +/** + * Copyright 2018 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Shift from 'shift-ast'; + +export default (fn, reducer) => ({ + __proto__: reducer, + + reduceArrayAssignmentTarget(node, data) { + return fn(super.reduceArrayAssignmentTarget(node, data), node); + }, + + reduceArrayBinding(node, data) { + return fn(super.reduceArrayBinding(node, data), node); + }, + + reduceArrayExpression(node, data) { + return fn(super.reduceArrayExpression(node, data), node); + }, + + reduceArrowExpression(node, data) { + return fn(super.reduceArrowExpression(node, data), node); + }, + + reduceAssignmentExpression(node, data) { + return fn(super.reduceAssignmentExpression(node, data), node); + }, + + reduceAssignmentTargetIdentifier(node, data) { + return fn(super.reduceAssignmentTargetIdentifier(node, data), node); + }, + + reduceAssignmentTargetPropertyIdentifier(node, data) { + return fn(super.reduceAssignmentTargetPropertyIdentifier(node, data), node); + }, + + reduceAssignmentTargetPropertyProperty(node, data) { + return fn(super.reduceAssignmentTargetPropertyProperty(node, data), node); + }, + + reduceAssignmentTargetWithDefault(node, data) { + return fn(super.reduceAssignmentTargetWithDefault(node, data), node); + }, + + reduceAwaitExpression(node, data) { + return fn(super.reduceAwaitExpression(node, data), node); + }, + + reduceBinaryExpression(node, data) { + return fn(super.reduceBinaryExpression(node, data), node); + }, + + reduceBindingIdentifier(node, data) { + return fn(super.reduceBindingIdentifier(node, data), node); + }, + + reduceBindingPropertyIdentifier(node, data) { + return fn(super.reduceBindingPropertyIdentifier(node, data), node); + }, + + reduceBindingPropertyProperty(node, data) { + return fn(super.reduceBindingPropertyProperty(node, data), node); + }, + + reduceBindingWithDefault(node, data) { + return fn(super.reduceBindingWithDefault(node, data), node); + }, + + reduceBlock(node, data) { + return fn(super.reduceBlock(node, data), node); + }, + + reduceBlockStatement(node, data) { + return fn(super.reduceBlockStatement(node, data), node); + }, + + reduceBreakStatement(node, data) { + return fn(super.reduceBreakStatement(node, data), node); + }, + + reduceCallExpression(node, data) { + return fn(super.reduceCallExpression(node, data), node); + }, + + reduceCatchClause(node, data) { + return fn(super.reduceCatchClause(node, data), node); + }, + + reduceClassDeclaration(node, data) { + return fn(super.reduceClassDeclaration(node, data), node); + }, + + reduceClassElement(node, data) { + return fn(super.reduceClassElement(node, data), node); + }, + + reduceClassExpression(node, data) { + return fn(super.reduceClassExpression(node, data), node); + }, + + reduceCompoundAssignmentExpression(node, data) { + return fn(super.reduceCompoundAssignmentExpression(node, data), node); + }, + + reduceComputedMemberAssignmentTarget(node, data) { + return fn(super.reduceComputedMemberAssignmentTarget(node, data), node); + }, + + reduceComputedMemberExpression(node, data) { + return fn(super.reduceComputedMemberExpression(node, data), node); + }, + + reduceComputedPropertyName(node, data) { + return fn(super.reduceComputedPropertyName(node, data), node); + }, + + reduceConditionalExpression(node, data) { + return fn(super.reduceConditionalExpression(node, data), node); + }, + + reduceContinueStatement(node, data) { + return fn(super.reduceContinueStatement(node, data), node); + }, + + reduceDataProperty(node, data) { + return fn(super.reduceDataProperty(node, data), node); + }, + + reduceDebuggerStatement(node, data) { + return fn(super.reduceDebuggerStatement(node, data), node); + }, + + reduceDirective(node, data) { + return fn(super.reduceDirective(node, data), node); + }, + + reduceDoWhileStatement(node, data) { + return fn(super.reduceDoWhileStatement(node, data), node); + }, + + reduceEmptyStatement(node, data) { + return fn(super.reduceEmptyStatement(node, data), node); + }, + + reduceExport(node, data) { + return fn(super.reduceExport(node, data), node); + }, + + reduceExportAllFrom(node, data) { + return fn(super.reduceExportAllFrom(node, data), node); + }, + + reduceExportDefault(node, data) { + return fn(super.reduceExportDefault(node, data), node); + }, + + reduceExportFrom(node, data) { + return fn(super.reduceExportFrom(node, data), node); + }, + + reduceExportFromSpecifier(node, data) { + return fn(super.reduceExportFromSpecifier(node, data), node); + }, + + reduceExportLocalSpecifier(node, data) { + return fn(super.reduceExportLocalSpecifier(node, data), node); + }, + + reduceExportLocals(node, data) { + return fn(super.reduceExportLocals(node, data), node); + }, + + reduceExpressionStatement(node, data) { + return fn(super.reduceExpressionStatement(node, data), node); + }, + + reduceForAwaitStatement(node, data) { + return fn(super.reduceForAwaitStatement(node, data), node); + }, + + reduceForInStatement(node, data) { + return fn(super.reduceForInStatement(node, data), node); + }, + + reduceForOfStatement(node, data) { + return fn(super.reduceForOfStatement(node, data), node); + }, + + reduceForStatement(node, data) { + return fn(super.reduceForStatement(node, data), node); + }, + + reduceFormalParameters(node, data) { + return fn(super.reduceFormalParameters(node, data), node); + }, + + reduceFunctionBody(node, data) { + return fn(super.reduceFunctionBody(node, data), node); + }, + + reduceFunctionDeclaration(node, data) { + return fn(super.reduceFunctionDeclaration(node, data), node); + }, + + reduceFunctionExpression(node, data) { + return fn(super.reduceFunctionExpression(node, data), node); + }, + + reduceGetter(node, data) { + return fn(super.reduceGetter(node, data), node); + }, + + reduceIdentifierExpression(node, data) { + return fn(super.reduceIdentifierExpression(node, data), node); + }, + + reduceIfStatement(node, data) { + return fn(super.reduceIfStatement(node, data), node); + }, + + reduceImport(node, data) { + return fn(super.reduceImport(node, data), node); + }, + + reduceImportNamespace(node, data) { + return fn(super.reduceImportNamespace(node, data), node); + }, + + reduceImportSpecifier(node, data) { + return fn(super.reduceImportSpecifier(node, data), node); + }, + + reduceLabeledStatement(node, data) { + return fn(super.reduceLabeledStatement(node, data), node); + }, + + reduceLiteralBooleanExpression(node, data) { + return fn(super.reduceLiteralBooleanExpression(node, data), node); + }, + + reduceLiteralInfinityExpression(node, data) { + return fn(super.reduceLiteralInfinityExpression(node, data), node); + }, + + reduceLiteralNullExpression(node, data) { + return fn(super.reduceLiteralNullExpression(node, data), node); + }, + + reduceLiteralNumericExpression(node, data) { + return fn(super.reduceLiteralNumericExpression(node, data), node); + }, + + reduceLiteralRegExpExpression(node, data) { + return fn(super.reduceLiteralRegExpExpression(node, data), node); + }, + + reduceLiteralStringExpression(node, data) { + return fn(super.reduceLiteralStringExpression(node, data), node); + }, + + reduceMethod(node, data) { + return fn(super.reduceMethod(node, data), node); + }, + + reduceModule(node, data) { + return fn(super.reduceModule(node, data), node); + }, + + reduceNewExpression(node, data) { + return fn(super.reduceNewExpression(node, data), node); + }, + + reduceNewTargetExpression(node, data) { + return fn(super.reduceNewTargetExpression(node, data), node); + }, + + reduceObjectAssignmentTarget(node, data) { + return fn(super.reduceObjectAssignmentTarget(node, data), node); + }, + + reduceObjectBinding(node, data) { + return fn(super.reduceObjectBinding(node, data), node); + }, + + reduceObjectExpression(node, data) { + return fn(super.reduceObjectExpression(node, data), node); + }, + + reduceReturnStatement(node, data) { + return fn(super.reduceReturnStatement(node, data), node); + }, + + reduceScript(node, data) { + return fn(super.reduceScript(node, data), node); + }, + + reduceSetter(node, data) { + return fn(super.reduceSetter(node, data), node); + }, + + reduceShorthandProperty(node, data) { + return fn(super.reduceShorthandProperty(node, data), node); + }, + + reduceSpreadElement(node, data) { + return fn(super.reduceSpreadElement(node, data), node); + }, + + reduceSpreadProperty(node, data) { + return fn(super.reduceSpreadProperty(node, data), node); + }, + + reduceStaticMemberAssignmentTarget(node, data) { + return fn(super.reduceStaticMemberAssignmentTarget(node, data), node); + }, + + reduceStaticMemberExpression(node, data) { + return fn(super.reduceStaticMemberExpression(node, data), node); + }, + + reduceStaticPropertyName(node, data) { + return fn(super.reduceStaticPropertyName(node, data), node); + }, + + reduceSuper(node, data) { + return fn(super.reduceSuper(node, data), node); + }, + + reduceSwitchCase(node, data) { + return fn(super.reduceSwitchCase(node, data), node); + }, + + reduceSwitchDefault(node, data) { + return fn(super.reduceSwitchDefault(node, data), node); + }, + + reduceSwitchStatement(node, data) { + return fn(super.reduceSwitchStatement(node, data), node); + }, + + reduceSwitchStatementWithDefault(node, data) { + return fn(super.reduceSwitchStatementWithDefault(node, data), node); + }, + + reduceTemplateElement(node, data) { + return fn(super.reduceTemplateElement(node, data), node); + }, + + reduceTemplateExpression(node, data) { + return fn(super.reduceTemplateExpression(node, data), node); + }, + + reduceThisExpression(node, data) { + return fn(super.reduceThisExpression(node, data), node); + }, + + reduceThrowStatement(node, data) { + return fn(super.reduceThrowStatement(node, data), node); + }, + + reduceTryCatchStatement(node, data) { + return fn(super.reduceTryCatchStatement(node, data), node); + }, + + reduceTryFinallyStatement(node, data) { + return fn(super.reduceTryFinallyStatement(node, data), node); + }, + + reduceUnaryExpression(node, data) { + return fn(super.reduceUnaryExpression(node, data), node); + }, + + reduceUpdateExpression(node, data) { + return fn(super.reduceUpdateExpression(node, data), node); + }, + + reduceVariableDeclaration(node, data) { + return fn(super.reduceVariableDeclaration(node, data), node); + }, + + reduceVariableDeclarationStatement(node, data) { + return fn(super.reduceVariableDeclarationStatement(node, data), node); + }, + + reduceVariableDeclarator(node, data) { + return fn(super.reduceVariableDeclarator(node, data), node); + }, + + reduceWhileStatement(node, data) { + return fn(super.reduceWhileStatement(node, data), node); + }, + + reduceWithStatement(node, data) { + return fn(super.reduceWithStatement(node, data), node); + }, + + reduceYieldExpression(node, data) { + return fn(super.reduceYieldExpression(node, data), node); + }, + + reduceYieldGeneratorExpression(node, data) { + return fn(super.reduceYieldGeneratorExpression(node, data), node); + }, +}); diff --git a/repl/src/shift-reducer/clone-reducer.js b/repl/src/shift-reducer/clone-reducer.js new file mode 100644 index 00000000..4cdcc334 --- /dev/null +++ b/repl/src/shift-reducer/clone-reducer.js @@ -0,0 +1,416 @@ +// Generated by generate-clone-reducer.js +/** + * Copyright 2018 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Shift from 'shift-ast'; + +export default class CloneReducer { + reduceArrayAssignmentTarget(node, { elements, rest }) { + return new Shift.ArrayAssignmentTarget({ elements, rest }); + } + + reduceArrayBinding(node, { elements, rest }) { + return new Shift.ArrayBinding({ elements, rest }); + } + + reduceArrayExpression(node, { elements }) { + return new Shift.ArrayExpression({ elements }); + } + + reduceArrowExpression(node, { params, body }) { + return new Shift.ArrowExpression({ isAsync: node.isAsync, params, body }); + } + + reduceAssignmentExpression(node, { binding, expression }) { + return new Shift.AssignmentExpression({ binding, expression }); + } + + reduceAssignmentTargetIdentifier(node) { + return new Shift.AssignmentTargetIdentifier({ name: node.name }); + } + + reduceAssignmentTargetPropertyIdentifier(node, { binding, init }) { + return new Shift.AssignmentTargetPropertyIdentifier({ binding, init }); + } + + reduceAssignmentTargetPropertyProperty(node, { name, binding }) { + return new Shift.AssignmentTargetPropertyProperty({ name, binding }); + } + + reduceAssignmentTargetWithDefault(node, { binding, init }) { + return new Shift.AssignmentTargetWithDefault({ binding, init }); + } + + reduceAwaitExpression(node, { expression }) { + return new Shift.AwaitExpression({ expression }); + } + + reduceBinaryExpression(node, { left, right }) { + return new Shift.BinaryExpression({ left, operator: node.operator, right }); + } + + reduceBindingIdentifier(node) { + return new Shift.BindingIdentifier({ name: node.name }); + } + + reduceBindingPropertyIdentifier(node, { binding, init }) { + return new Shift.BindingPropertyIdentifier({ binding, init }); + } + + reduceBindingPropertyProperty(node, { name, binding }) { + return new Shift.BindingPropertyProperty({ name, binding }); + } + + reduceBindingWithDefault(node, { binding, init }) { + return new Shift.BindingWithDefault({ binding, init }); + } + + reduceBlock(node, { statements }) { + return new Shift.Block({ statements }); + } + + reduceBlockStatement(node, { block }) { + return new Shift.BlockStatement({ block }); + } + + reduceBreakStatement(node) { + return new Shift.BreakStatement({ label: node.label }); + } + + reduceCallExpression(node, { callee, arguments: _arguments }) { + return new Shift.CallExpression({ callee, arguments: _arguments }); + } + + reduceCatchClause(node, { binding, body }) { + return new Shift.CatchClause({ binding, body }); + } + + reduceClassDeclaration(node, { name, super: _super, elements }) { + return new Shift.ClassDeclaration({ name, super: _super, elements }); + } + + reduceClassElement(node, { method }) { + return new Shift.ClassElement({ isStatic: node.isStatic, method }); + } + + reduceClassExpression(node, { name, super: _super, elements }) { + return new Shift.ClassExpression({ name, super: _super, elements }); + } + + reduceCompoundAssignmentExpression(node, { binding, expression }) { + return new Shift.CompoundAssignmentExpression({ binding, operator: node.operator, expression }); + } + + reduceComputedMemberAssignmentTarget(node, { object, expression }) { + return new Shift.ComputedMemberAssignmentTarget({ object, expression }); + } + + reduceComputedMemberExpression(node, { object, expression }) { + return new Shift.ComputedMemberExpression({ object, expression }); + } + + reduceComputedPropertyName(node, { expression }) { + return new Shift.ComputedPropertyName({ expression }); + } + + reduceConditionalExpression(node, { test, consequent, alternate }) { + return new Shift.ConditionalExpression({ test, consequent, alternate }); + } + + reduceContinueStatement(node) { + return new Shift.ContinueStatement({ label: node.label }); + } + + reduceDataProperty(node, { name, expression }) { + return new Shift.DataProperty({ name, expression }); + } + + reduceDebuggerStatement(node) { + return new Shift.DebuggerStatement; + } + + reduceDirective(node) { + return new Shift.Directive({ rawValue: node.rawValue }); + } + + reduceDoWhileStatement(node, { body, test }) { + return new Shift.DoWhileStatement({ body, test }); + } + + reduceEmptyStatement(node) { + return new Shift.EmptyStatement; + } + + reduceExport(node, { declaration }) { + return new Shift.Export({ declaration }); + } + + reduceExportAllFrom(node) { + return new Shift.ExportAllFrom({ moduleSpecifier: node.moduleSpecifier }); + } + + reduceExportDefault(node, { body }) { + return new Shift.ExportDefault({ body }); + } + + reduceExportFrom(node, { namedExports }) { + return new Shift.ExportFrom({ namedExports, moduleSpecifier: node.moduleSpecifier }); + } + + reduceExportFromSpecifier(node) { + return new Shift.ExportFromSpecifier({ name: node.name, exportedName: node.exportedName }); + } + + reduceExportLocalSpecifier(node, { name }) { + return new Shift.ExportLocalSpecifier({ name, exportedName: node.exportedName }); + } + + reduceExportLocals(node, { namedExports }) { + return new Shift.ExportLocals({ namedExports }); + } + + reduceExpressionStatement(node, { expression }) { + return new Shift.ExpressionStatement({ expression }); + } + + reduceForAwaitStatement(node, { left, right, body }) { + return new Shift.ForAwaitStatement({ left, right, body }); + } + + reduceForInStatement(node, { left, right, body }) { + return new Shift.ForInStatement({ left, right, body }); + } + + reduceForOfStatement(node, { left, right, body }) { + return new Shift.ForOfStatement({ left, right, body }); + } + + reduceForStatement(node, { init, test, update, body }) { + return new Shift.ForStatement({ init, test, update, body }); + } + + reduceFormalParameters(node, { items, rest }) { + return new Shift.FormalParameters({ items, rest }); + } + + reduceFunctionBody(node, { directives, statements }) { + return new Shift.FunctionBody({ directives, statements }); + } + + reduceFunctionDeclaration(node, { name, params, body }) { + return new Shift.FunctionDeclaration({ isAsync: node.isAsync, isGenerator: node.isGenerator, name, params, body }); + } + + reduceFunctionExpression(node, { name, params, body }) { + return new Shift.FunctionExpression({ isAsync: node.isAsync, isGenerator: node.isGenerator, name, params, body }); + } + + reduceGetter(node, { name, body }) { + return new Shift.Getter({ name, body }); + } + + reduceIdentifierExpression(node) { + return new Shift.IdentifierExpression({ name: node.name }); + } + + reduceIfStatement(node, { test, consequent, alternate }) { + return new Shift.IfStatement({ test, consequent, alternate }); + } + + reduceImport(node, { defaultBinding, namedImports }) { + return new Shift.Import({ defaultBinding, namedImports, moduleSpecifier: node.moduleSpecifier }); + } + + reduceImportNamespace(node, { defaultBinding, namespaceBinding }) { + return new Shift.ImportNamespace({ defaultBinding, namespaceBinding, moduleSpecifier: node.moduleSpecifier }); + } + + reduceImportSpecifier(node, { binding }) { + return new Shift.ImportSpecifier({ name: node.name, binding }); + } + + reduceLabeledStatement(node, { body }) { + return new Shift.LabeledStatement({ label: node.label, body }); + } + + reduceLiteralBooleanExpression(node) { + return new Shift.LiteralBooleanExpression({ value: node.value }); + } + + reduceLiteralInfinityExpression(node) { + return new Shift.LiteralInfinityExpression; + } + + reduceLiteralNullExpression(node) { + return new Shift.LiteralNullExpression; + } + + reduceLiteralNumericExpression(node) { + return new Shift.LiteralNumericExpression({ value: node.value }); + } + + reduceLiteralRegExpExpression(node) { + return new Shift.LiteralRegExpExpression({ pattern: node.pattern, global: node.global, ignoreCase: node.ignoreCase, multiLine: node.multiLine, dotAll: node.dotAll, unicode: node.unicode, sticky: node.sticky }); + } + + reduceLiteralStringExpression(node) { + return new Shift.LiteralStringExpression({ value: node.value }); + } + + reduceMethod(node, { name, params, body }) { + return new Shift.Method({ isAsync: node.isAsync, isGenerator: node.isGenerator, name, params, body }); + } + + reduceModule(node, { directives, items }) { + return new Shift.Module({ directives, items }); + } + + reduceNewExpression(node, { callee, arguments: _arguments }) { + return new Shift.NewExpression({ callee, arguments: _arguments }); + } + + reduceNewTargetExpression(node) { + return new Shift.NewTargetExpression; + } + + reduceObjectAssignmentTarget(node, { properties, rest }) { + return new Shift.ObjectAssignmentTarget({ properties, rest }); + } + + reduceObjectBinding(node, { properties, rest }) { + return new Shift.ObjectBinding({ properties, rest }); + } + + reduceObjectExpression(node, { properties }) { + return new Shift.ObjectExpression({ properties }); + } + + reduceReturnStatement(node, { expression }) { + return new Shift.ReturnStatement({ expression }); + } + + reduceScript(node, { directives, statements }) { + return new Shift.Script({ directives, statements }); + } + + reduceSetter(node, { name, param, body }) { + return new Shift.Setter({ name, param, body }); + } + + reduceShorthandProperty(node, { name }) { + return new Shift.ShorthandProperty({ name }); + } + + reduceSpreadElement(node, { expression }) { + return new Shift.SpreadElement({ expression }); + } + + reduceSpreadProperty(node, { expression }) { + return new Shift.SpreadProperty({ expression }); + } + + reduceStaticMemberAssignmentTarget(node, { object }) { + return new Shift.StaticMemberAssignmentTarget({ object, property: node.property }); + } + + reduceStaticMemberExpression(node, { object }) { + return new Shift.StaticMemberExpression({ object, property: node.property }); + } + + reduceStaticPropertyName(node) { + return new Shift.StaticPropertyName({ value: node.value }); + } + + reduceSuper(node) { + return new Shift.Super; + } + + reduceSwitchCase(node, { test, consequent }) { + return new Shift.SwitchCase({ test, consequent }); + } + + reduceSwitchDefault(node, { consequent }) { + return new Shift.SwitchDefault({ consequent }); + } + + reduceSwitchStatement(node, { discriminant, cases }) { + return new Shift.SwitchStatement({ discriminant, cases }); + } + + reduceSwitchStatementWithDefault(node, { discriminant, preDefaultCases, defaultCase, postDefaultCases }) { + return new Shift.SwitchStatementWithDefault({ discriminant, preDefaultCases, defaultCase, postDefaultCases }); + } + + reduceTemplateElement(node) { + return new Shift.TemplateElement({ rawValue: node.rawValue }); + } + + reduceTemplateExpression(node, { tag, elements }) { + return new Shift.TemplateExpression({ tag, elements }); + } + + reduceThisExpression(node) { + return new Shift.ThisExpression; + } + + reduceThrowStatement(node, { expression }) { + return new Shift.ThrowStatement({ expression }); + } + + reduceTryCatchStatement(node, { body, catchClause }) { + return new Shift.TryCatchStatement({ body, catchClause }); + } + + reduceTryFinallyStatement(node, { body, catchClause, finalizer }) { + return new Shift.TryFinallyStatement({ body, catchClause, finalizer }); + } + + reduceUnaryExpression(node, { operand }) { + return new Shift.UnaryExpression({ operator: node.operator, operand }); + } + + reduceUpdateExpression(node, { operand }) { + return new Shift.UpdateExpression({ isPrefix: node.isPrefix, operator: node.operator, operand }); + } + + reduceVariableDeclaration(node, { declarators }) { + return new Shift.VariableDeclaration({ kind: node.kind, declarators }); + } + + reduceVariableDeclarationStatement(node, { declaration }) { + return new Shift.VariableDeclarationStatement({ declaration }); + } + + reduceVariableDeclarator(node, { binding, init }) { + return new Shift.VariableDeclarator({ binding, init }); + } + + reduceWhileStatement(node, { test, body }) { + return new Shift.WhileStatement({ test, body }); + } + + reduceWithStatement(node, { object, body }) { + return new Shift.WithStatement({ object, body }); + } + + reduceYieldExpression(node, { expression }) { + return new Shift.YieldExpression({ expression }); + } + + reduceYieldGeneratorExpression(node, { expression }) { + return new Shift.YieldGeneratorExpression({ expression }); + } +} diff --git a/repl/src/shift-reducer/director.js b/repl/src/shift-reducer/director.js new file mode 100644 index 00000000..c9e4b038 --- /dev/null +++ b/repl/src/shift-reducer/director.js @@ -0,0 +1,418 @@ +// Generated by generate-director.js +/** + * Copyright 2018 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const director = { + ArrayAssignmentTarget(reducer, node) { + return reducer.reduceArrayAssignmentTarget(node, { elements: node.elements.map(v => v && this[v.type](reducer, v)), rest: node.rest && this[node.rest.type](reducer, node.rest) }); + }, + + ArrayBinding(reducer, node) { + return reducer.reduceArrayBinding(node, { elements: node.elements.map(v => v && this[v.type](reducer, v)), rest: node.rest && this[node.rest.type](reducer, node.rest) }); + }, + + ArrayExpression(reducer, node) { + return reducer.reduceArrayExpression(node, { elements: node.elements.map(v => v && this[v.type](reducer, v)) }); + }, + + ArrowExpression(reducer, node) { + return reducer.reduceArrowExpression(node, { params: this.FormalParameters(reducer, node.params), body: this[node.body.type](reducer, node.body) }); + }, + + AssignmentExpression(reducer, node) { + return reducer.reduceAssignmentExpression(node, { binding: this[node.binding.type](reducer, node.binding), expression: this[node.expression.type](reducer, node.expression) }); + }, + + AssignmentTargetIdentifier(reducer, node) { + return reducer.reduceAssignmentTargetIdentifier(node); + }, + + AssignmentTargetPropertyIdentifier(reducer, node) { + return reducer.reduceAssignmentTargetPropertyIdentifier(node, { binding: this.AssignmentTargetIdentifier(reducer, node.binding), init: node.init && this[node.init.type](reducer, node.init) }); + }, + + AssignmentTargetPropertyProperty(reducer, node) { + return reducer.reduceAssignmentTargetPropertyProperty(node, { name: this[node.name.type](reducer, node.name), binding: this[node.binding.type](reducer, node.binding) }); + }, + + AssignmentTargetWithDefault(reducer, node) { + return reducer.reduceAssignmentTargetWithDefault(node, { binding: this[node.binding.type](reducer, node.binding), init: this[node.init.type](reducer, node.init) }); + }, + + AwaitExpression(reducer, node) { + return reducer.reduceAwaitExpression(node, { expression: this[node.expression.type](reducer, node.expression) }); + }, + + BinaryExpression(reducer, node) { + return reducer.reduceBinaryExpression(node, { left: this[node.left.type](reducer, node.left), right: this[node.right.type](reducer, node.right) }); + }, + + BindingIdentifier(reducer, node) { + return reducer.reduceBindingIdentifier(node); + }, + + BindingPropertyIdentifier(reducer, node) { + return reducer.reduceBindingPropertyIdentifier(node, { binding: this.BindingIdentifier(reducer, node.binding), init: node.init && this[node.init.type](reducer, node.init) }); + }, + + BindingPropertyProperty(reducer, node) { + return reducer.reduceBindingPropertyProperty(node, { name: this[node.name.type](reducer, node.name), binding: this[node.binding.type](reducer, node.binding) }); + }, + + BindingWithDefault(reducer, node) { + return reducer.reduceBindingWithDefault(node, { binding: this[node.binding.type](reducer, node.binding), init: this[node.init.type](reducer, node.init) }); + }, + + Block(reducer, node) { + return reducer.reduceBlock(node, { statements: node.statements.map(v => this[v.type](reducer, v)) }); + }, + + BlockStatement(reducer, node) { + return reducer.reduceBlockStatement(node, { block: this.Block(reducer, node.block) }); + }, + + BreakStatement(reducer, node) { + return reducer.reduceBreakStatement(node); + }, + + CallExpression(reducer, node) { + return reducer.reduceCallExpression(node, { callee: this[node.callee.type](reducer, node.callee), arguments: node.arguments.map(v => this[v.type](reducer, v)) }); + }, + + CatchClause(reducer, node) { + return reducer.reduceCatchClause(node, { binding: this[node.binding.type](reducer, node.binding), body: this.Block(reducer, node.body) }); + }, + + ClassDeclaration(reducer, node) { + return reducer.reduceClassDeclaration(node, { name: this.BindingIdentifier(reducer, node.name), super: node.super && this[node.super.type](reducer, node.super), elements: node.elements.map(v => this.ClassElement(reducer, v)) }); + }, + + ClassElement(reducer, node) { + return reducer.reduceClassElement(node, { method: this[node.method.type](reducer, node.method) }); + }, + + ClassExpression(reducer, node) { + return reducer.reduceClassExpression(node, { name: node.name && this.BindingIdentifier(reducer, node.name), super: node.super && this[node.super.type](reducer, node.super), elements: node.elements.map(v => this.ClassElement(reducer, v)) }); + }, + + CompoundAssignmentExpression(reducer, node) { + return reducer.reduceCompoundAssignmentExpression(node, { binding: this[node.binding.type](reducer, node.binding), expression: this[node.expression.type](reducer, node.expression) }); + }, + + ComputedMemberAssignmentTarget(reducer, node) { + return reducer.reduceComputedMemberAssignmentTarget(node, { object: this[node.object.type](reducer, node.object), expression: this[node.expression.type](reducer, node.expression) }); + }, + + ComputedMemberExpression(reducer, node) { + return reducer.reduceComputedMemberExpression(node, { object: this[node.object.type](reducer, node.object), expression: this[node.expression.type](reducer, node.expression) }); + }, + + ComputedPropertyName(reducer, node) { + return reducer.reduceComputedPropertyName(node, { expression: this[node.expression.type](reducer, node.expression) }); + }, + + ConditionalExpression(reducer, node) { + return reducer.reduceConditionalExpression(node, { test: this[node.test.type](reducer, node.test), consequent: this[node.consequent.type](reducer, node.consequent), alternate: this[node.alternate.type](reducer, node.alternate) }); + }, + + ContinueStatement(reducer, node) { + return reducer.reduceContinueStatement(node); + }, + + DataProperty(reducer, node) { + return reducer.reduceDataProperty(node, { name: this[node.name.type](reducer, node.name), expression: this[node.expression.type](reducer, node.expression) }); + }, + + DebuggerStatement(reducer, node) { + return reducer.reduceDebuggerStatement(node); + }, + + Directive(reducer, node) { + return reducer.reduceDirective(node); + }, + + DoWhileStatement(reducer, node) { + return reducer.reduceDoWhileStatement(node, { body: this[node.body.type](reducer, node.body), test: this[node.test.type](reducer, node.test) }); + }, + + EmptyStatement(reducer, node) { + return reducer.reduceEmptyStatement(node); + }, + + Export(reducer, node) { + return reducer.reduceExport(node, { declaration: this[node.declaration.type](reducer, node.declaration) }); + }, + + ExportAllFrom(reducer, node) { + return reducer.reduceExportAllFrom(node); + }, + + ExportDefault(reducer, node) { + return reducer.reduceExportDefault(node, { body: this[node.body.type](reducer, node.body) }); + }, + + ExportFrom(reducer, node) { + return reducer.reduceExportFrom(node, { namedExports: node.namedExports.map(v => this.ExportFromSpecifier(reducer, v)) }); + }, + + ExportFromSpecifier(reducer, node) { + return reducer.reduceExportFromSpecifier(node); + }, + + ExportLocalSpecifier(reducer, node) { + return reducer.reduceExportLocalSpecifier(node, { name: this.IdentifierExpression(reducer, node.name) }); + }, + + ExportLocals(reducer, node) { + return reducer.reduceExportLocals(node, { namedExports: node.namedExports.map(v => this.ExportLocalSpecifier(reducer, v)) }); + }, + + ExpressionStatement(reducer, node) { + return reducer.reduceExpressionStatement(node, { expression: this[node.expression.type](reducer, node.expression) }); + }, + + ForAwaitStatement(reducer, node) { + return reducer.reduceForAwaitStatement(node, { left: this[node.left.type](reducer, node.left), right: this[node.right.type](reducer, node.right), body: this[node.body.type](reducer, node.body) }); + }, + + ForInStatement(reducer, node) { + return reducer.reduceForInStatement(node, { left: this[node.left.type](reducer, node.left), right: this[node.right.type](reducer, node.right), body: this[node.body.type](reducer, node.body) }); + }, + + ForOfStatement(reducer, node) { + return reducer.reduceForOfStatement(node, { left: this[node.left.type](reducer, node.left), right: this[node.right.type](reducer, node.right), body: this[node.body.type](reducer, node.body) }); + }, + + ForStatement(reducer, node) { + return reducer.reduceForStatement(node, { init: node.init && this[node.init.type](reducer, node.init), test: node.test && this[node.test.type](reducer, node.test), update: node.update && this[node.update.type](reducer, node.update), body: this[node.body.type](reducer, node.body) }); + }, + + FormalParameters(reducer, node) { + return reducer.reduceFormalParameters(node, { items: node.items.map(v => this[v.type](reducer, v)), rest: node.rest && this[node.rest.type](reducer, node.rest) }); + }, + + FunctionBody(reducer, node) { + return reducer.reduceFunctionBody(node, { directives: node.directives.map(v => this.Directive(reducer, v)), statements: node.statements.map(v => this[v.type](reducer, v)) }); + }, + + FunctionDeclaration(reducer, node) { + return reducer.reduceFunctionDeclaration(node, { name: this.BindingIdentifier(reducer, node.name), params: this.FormalParameters(reducer, node.params), body: this.FunctionBody(reducer, node.body) }); + }, + + FunctionExpression(reducer, node) { + return reducer.reduceFunctionExpression(node, { name: node.name && this.BindingIdentifier(reducer, node.name), params: this.FormalParameters(reducer, node.params), body: this.FunctionBody(reducer, node.body) }); + }, + + Getter(reducer, node) { + return reducer.reduceGetter(node, { name: this[node.name.type](reducer, node.name), body: this.FunctionBody(reducer, node.body) }); + }, + + IdentifierExpression(reducer, node) { + return reducer.reduceIdentifierExpression(node); + }, + + IfStatement(reducer, node) { + return reducer.reduceIfStatement(node, { test: this[node.test.type](reducer, node.test), consequent: this[node.consequent.type](reducer, node.consequent), alternate: node.alternate && this[node.alternate.type](reducer, node.alternate) }); + }, + + Import(reducer, node) { + return reducer.reduceImport(node, { defaultBinding: node.defaultBinding && this.BindingIdentifier(reducer, node.defaultBinding), namedImports: node.namedImports.map(v => this.ImportSpecifier(reducer, v)) }); + }, + + ImportNamespace(reducer, node) { + return reducer.reduceImportNamespace(node, { defaultBinding: node.defaultBinding && this.BindingIdentifier(reducer, node.defaultBinding), namespaceBinding: this.BindingIdentifier(reducer, node.namespaceBinding) }); + }, + + ImportSpecifier(reducer, node) { + return reducer.reduceImportSpecifier(node, { binding: this.BindingIdentifier(reducer, node.binding) }); + }, + + LabeledStatement(reducer, node) { + return reducer.reduceLabeledStatement(node, { body: this[node.body.type](reducer, node.body) }); + }, + + LiteralBooleanExpression(reducer, node) { + return reducer.reduceLiteralBooleanExpression(node); + }, + + LiteralInfinityExpression(reducer, node) { + return reducer.reduceLiteralInfinityExpression(node); + }, + + LiteralNullExpression(reducer, node) { + return reducer.reduceLiteralNullExpression(node); + }, + + LiteralNumericExpression(reducer, node) { + return reducer.reduceLiteralNumericExpression(node); + }, + + LiteralRegExpExpression(reducer, node) { + return reducer.reduceLiteralRegExpExpression(node); + }, + + LiteralStringExpression(reducer, node) { + return reducer.reduceLiteralStringExpression(node); + }, + + Method(reducer, node) { + return reducer.reduceMethod(node, { name: this[node.name.type](reducer, node.name), params: this.FormalParameters(reducer, node.params), body: this.FunctionBody(reducer, node.body) }); + }, + + Module(reducer, node) { + return reducer.reduceModule(node, { directives: node.directives.map(v => this.Directive(reducer, v)), items: node.items.map(v => this[v.type](reducer, v)) }); + }, + + NewExpression(reducer, node) { + return reducer.reduceNewExpression(node, { callee: this[node.callee.type](reducer, node.callee), arguments: node.arguments.map(v => this[v.type](reducer, v)) }); + }, + + NewTargetExpression(reducer, node) { + return reducer.reduceNewTargetExpression(node); + }, + + ObjectAssignmentTarget(reducer, node) { + return reducer.reduceObjectAssignmentTarget(node, { properties: node.properties.map(v => this[v.type](reducer, v)), rest: node.rest && this[node.rest.type](reducer, node.rest) }); + }, + + ObjectBinding(reducer, node) { + return reducer.reduceObjectBinding(node, { properties: node.properties.map(v => this[v.type](reducer, v)), rest: node.rest && this[node.rest.type](reducer, node.rest) }); + }, + + ObjectExpression(reducer, node) { + return reducer.reduceObjectExpression(node, { properties: node.properties.map(v => this[v.type](reducer, v)) }); + }, + + ReturnStatement(reducer, node) { + return reducer.reduceReturnStatement(node, { expression: node.expression && this[node.expression.type](reducer, node.expression) }); + }, + + Script(reducer, node) { + return reducer.reduceScript(node, { directives: node.directives.map(v => this.Directive(reducer, v)), statements: node.statements.map(v => this[v.type](reducer, v)) }); + }, + + Setter(reducer, node) { + return reducer.reduceSetter(node, { name: this[node.name.type](reducer, node.name), param: this[node.param.type](reducer, node.param), body: this.FunctionBody(reducer, node.body) }); + }, + + ShorthandProperty(reducer, node) { + return reducer.reduceShorthandProperty(node, { name: this.IdentifierExpression(reducer, node.name) }); + }, + + SpreadElement(reducer, node) { + return reducer.reduceSpreadElement(node, { expression: this[node.expression.type](reducer, node.expression) }); + }, + + SpreadProperty(reducer, node) { + return reducer.reduceSpreadProperty(node, { expression: this[node.expression.type](reducer, node.expression) }); + }, + + StaticMemberAssignmentTarget(reducer, node) { + return reducer.reduceStaticMemberAssignmentTarget(node, { object: this[node.object.type](reducer, node.object) }); + }, + + StaticMemberExpression(reducer, node) { + return reducer.reduceStaticMemberExpression(node, { object: this[node.object.type](reducer, node.object) }); + }, + + StaticPropertyName(reducer, node) { + return reducer.reduceStaticPropertyName(node); + }, + + Super(reducer, node) { + return reducer.reduceSuper(node); + }, + + SwitchCase(reducer, node) { + return reducer.reduceSwitchCase(node, { test: this[node.test.type](reducer, node.test), consequent: node.consequent.map(v => this[v.type](reducer, v)) }); + }, + + SwitchDefault(reducer, node) { + return reducer.reduceSwitchDefault(node, { consequent: node.consequent.map(v => this[v.type](reducer, v)) }); + }, + + SwitchStatement(reducer, node) { + return reducer.reduceSwitchStatement(node, { discriminant: this[node.discriminant.type](reducer, node.discriminant), cases: node.cases.map(v => this.SwitchCase(reducer, v)) }); + }, + + SwitchStatementWithDefault(reducer, node) { + return reducer.reduceSwitchStatementWithDefault(node, { discriminant: this[node.discriminant.type](reducer, node.discriminant), preDefaultCases: node.preDefaultCases.map(v => this.SwitchCase(reducer, v)), defaultCase: this.SwitchDefault(reducer, node.defaultCase), postDefaultCases: node.postDefaultCases.map(v => this.SwitchCase(reducer, v)) }); + }, + + TemplateElement(reducer, node) { + return reducer.reduceTemplateElement(node); + }, + + TemplateExpression(reducer, node) { + return reducer.reduceTemplateExpression(node, { tag: node.tag && this[node.tag.type](reducer, node.tag), elements: node.elements.map(v => this[v.type](reducer, v)) }); + }, + + ThisExpression(reducer, node) { + return reducer.reduceThisExpression(node); + }, + + ThrowStatement(reducer, node) { + return reducer.reduceThrowStatement(node, { expression: this[node.expression.type](reducer, node.expression) }); + }, + + TryCatchStatement(reducer, node) { + return reducer.reduceTryCatchStatement(node, { body: this.Block(reducer, node.body), catchClause: this.CatchClause(reducer, node.catchClause) }); + }, + + TryFinallyStatement(reducer, node) { + return reducer.reduceTryFinallyStatement(node, { body: this.Block(reducer, node.body), catchClause: node.catchClause && this.CatchClause(reducer, node.catchClause), finalizer: this.Block(reducer, node.finalizer) }); + }, + + UnaryExpression(reducer, node) { + return reducer.reduceUnaryExpression(node, { operand: this[node.operand.type](reducer, node.operand) }); + }, + + UpdateExpression(reducer, node) { + return reducer.reduceUpdateExpression(node, { operand: this[node.operand.type](reducer, node.operand) }); + }, + + VariableDeclaration(reducer, node) { + return reducer.reduceVariableDeclaration(node, { declarators: node.declarators.map(v => this.VariableDeclarator(reducer, v)) }); + }, + + VariableDeclarationStatement(reducer, node) { + return reducer.reduceVariableDeclarationStatement(node, { declaration: this.VariableDeclaration(reducer, node.declaration) }); + }, + + VariableDeclarator(reducer, node) { + return reducer.reduceVariableDeclarator(node, { binding: this[node.binding.type](reducer, node.binding), init: node.init && this[node.init.type](reducer, node.init) }); + }, + + WhileStatement(reducer, node) { + return reducer.reduceWhileStatement(node, { test: this[node.test.type](reducer, node.test), body: this[node.body.type](reducer, node.body) }); + }, + + WithStatement(reducer, node) { + return reducer.reduceWithStatement(node, { object: this[node.object.type](reducer, node.object), body: this[node.body.type](reducer, node.body) }); + }, + + YieldExpression(reducer, node) { + return reducer.reduceYieldExpression(node, { expression: node.expression && this[node.expression.type](reducer, node.expression) }); + }, + + YieldGeneratorExpression(reducer, node) { + return reducer.reduceYieldGeneratorExpression(node, { expression: this[node.expression.type](reducer, node.expression) }); + }, +}; + +export function reduce(reducer, node) { + return director[node.type](reducer, node); +} diff --git a/repl/src/shift-reducer/index.js b/repl/src/shift-reducer/index.js new file mode 100644 index 00000000..2a97b980 --- /dev/null +++ b/repl/src/shift-reducer/index.js @@ -0,0 +1,27 @@ +/* + * Copyright 2018 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { reduce, reduce as default } from './director.js'; +export { thunkedReduce } from './thunked-director.js'; +export { default as thunkify } from './thunkify.js'; +export { default as thunkifyClass } from './thunkify-class.js'; +export { default as memoize } from './memoize.js'; +export { default as CloneReducer } from './clone-reducer.js'; +export { default as LazyCloneReducer } from './lazy-clone-reducer.js'; +export { default as MonoidalReducer } from './monoidal-reducer.js'; +export { default as ThunkedMonoidalReducer } from './thunked-monoidal-reducer.js'; +export { default as adapt } from './adapt.js'; +export { PlusReducer, ThunkedPlusReducer, ConcatReducer, ThunkedConcatReducer, AndReducer, ThunkedAndReducer, OrReducer, ThunkedOrReducer } from './reducers.js'; diff --git a/repl/src/shift-reducer/lazy-clone-reducer.js b/repl/src/shift-reducer/lazy-clone-reducer.js new file mode 100644 index 00000000..68df0d2e --- /dev/null +++ b/repl/src/shift-reducer/lazy-clone-reducer.js @@ -0,0 +1,650 @@ +// Generated by generate-lazy-clone-reducer.js +/** + * Copyright 2018 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Shift from 'shift-ast'; + +export default class LazyCloneReducer { + reduceArrayAssignmentTarget(node, { elements, rest }) { + if ((node.elements.length === elements.length && node.elements.every((v, i) => v === elements[i])) && node.rest === rest) { + return node; + } + return new Shift.ArrayAssignmentTarget({ elements, rest }); + } + + reduceArrayBinding(node, { elements, rest }) { + if ((node.elements.length === elements.length && node.elements.every((v, i) => v === elements[i])) && node.rest === rest) { + return node; + } + return new Shift.ArrayBinding({ elements, rest }); + } + + reduceArrayExpression(node, { elements }) { + if ((node.elements.length === elements.length && node.elements.every((v, i) => v === elements[i]))) { + return node; + } + return new Shift.ArrayExpression({ elements }); + } + + reduceArrowExpression(node, { params, body }) { + if (node.params === params && node.body === body) { + return node; + } + return new Shift.ArrowExpression({ isAsync: node.isAsync, params, body }); + } + + reduceAssignmentExpression(node, { binding, expression }) { + if (node.binding === binding && node.expression === expression) { + return node; + } + return new Shift.AssignmentExpression({ binding, expression }); + } + + reduceAssignmentTargetIdentifier(node) { + return node; + } + + reduceAssignmentTargetPropertyIdentifier(node, { binding, init }) { + if (node.binding === binding && node.init === init) { + return node; + } + return new Shift.AssignmentTargetPropertyIdentifier({ binding, init }); + } + + reduceAssignmentTargetPropertyProperty(node, { name, binding }) { + if (node.name === name && node.binding === binding) { + return node; + } + return new Shift.AssignmentTargetPropertyProperty({ name, binding }); + } + + reduceAssignmentTargetWithDefault(node, { binding, init }) { + if (node.binding === binding && node.init === init) { + return node; + } + return new Shift.AssignmentTargetWithDefault({ binding, init }); + } + + reduceAwaitExpression(node, { expression }) { + if (node.expression === expression) { + return node; + } + return new Shift.AwaitExpression({ expression }); + } + + reduceBinaryExpression(node, { left, right }) { + if (node.left === left && node.right === right) { + return node; + } + return new Shift.BinaryExpression({ left, operator: node.operator, right }); + } + + reduceBindingIdentifier(node) { + return node; + } + + reduceBindingPropertyIdentifier(node, { binding, init }) { + if (node.binding === binding && node.init === init) { + return node; + } + return new Shift.BindingPropertyIdentifier({ binding, init }); + } + + reduceBindingPropertyProperty(node, { name, binding }) { + if (node.name === name && node.binding === binding) { + return node; + } + return new Shift.BindingPropertyProperty({ name, binding }); + } + + reduceBindingWithDefault(node, { binding, init }) { + if (node.binding === binding && node.init === init) { + return node; + } + return new Shift.BindingWithDefault({ binding, init }); + } + + reduceBlock(node, { statements }) { + if ((node.statements.length === statements.length && node.statements.every((v, i) => v === statements[i]))) { + return node; + } + return new Shift.Block({ statements }); + } + + reduceBlockStatement(node, { block }) { + if (node.block === block) { + return node; + } + return new Shift.BlockStatement({ block }); + } + + reduceBreakStatement(node) { + return node; + } + + reduceCallExpression(node, { callee, arguments: _arguments }) { + if (node.callee === callee && (node.arguments.length === _arguments.length && node.arguments.every((v, i) => v === _arguments[i]))) { + return node; + } + return new Shift.CallExpression({ callee, arguments: _arguments }); + } + + reduceCatchClause(node, { binding, body }) { + if (node.binding === binding && node.body === body) { + return node; + } + return new Shift.CatchClause({ binding, body }); + } + + reduceClassDeclaration(node, { name, super: _super, elements }) { + if (node.name === name && node.super === _super && (node.elements.length === elements.length && node.elements.every((v, i) => v === elements[i]))) { + return node; + } + return new Shift.ClassDeclaration({ name, super: _super, elements }); + } + + reduceClassElement(node, { method }) { + if (node.method === method) { + return node; + } + return new Shift.ClassElement({ isStatic: node.isStatic, method }); + } + + reduceClassExpression(node, { name, super: _super, elements }) { + if (node.name === name && node.super === _super && (node.elements.length === elements.length && node.elements.every((v, i) => v === elements[i]))) { + return node; + } + return new Shift.ClassExpression({ name, super: _super, elements }); + } + + reduceCompoundAssignmentExpression(node, { binding, expression }) { + if (node.binding === binding && node.expression === expression) { + return node; + } + return new Shift.CompoundAssignmentExpression({ binding, operator: node.operator, expression }); + } + + reduceComputedMemberAssignmentTarget(node, { object, expression }) { + if (node.object === object && node.expression === expression) { + return node; + } + return new Shift.ComputedMemberAssignmentTarget({ object, expression }); + } + + reduceComputedMemberExpression(node, { object, expression }) { + if (node.object === object && node.expression === expression) { + return node; + } + return new Shift.ComputedMemberExpression({ object, expression }); + } + + reduceComputedPropertyName(node, { expression }) { + if (node.expression === expression) { + return node; + } + return new Shift.ComputedPropertyName({ expression }); + } + + reduceConditionalExpression(node, { test, consequent, alternate }) { + if (node.test === test && node.consequent === consequent && node.alternate === alternate) { + return node; + } + return new Shift.ConditionalExpression({ test, consequent, alternate }); + } + + reduceContinueStatement(node) { + return node; + } + + reduceDataProperty(node, { name, expression }) { + if (node.name === name && node.expression === expression) { + return node; + } + return new Shift.DataProperty({ name, expression }); + } + + reduceDebuggerStatement(node) { + return node; + } + + reduceDirective(node) { + return node; + } + + reduceDoWhileStatement(node, { body, test }) { + if (node.body === body && node.test === test) { + return node; + } + return new Shift.DoWhileStatement({ body, test }); + } + + reduceEmptyStatement(node) { + return node; + } + + reduceExport(node, { declaration }) { + if (node.declaration === declaration) { + return node; + } + return new Shift.Export({ declaration }); + } + + reduceExportAllFrom(node) { + return node; + } + + reduceExportDefault(node, { body }) { + if (node.body === body) { + return node; + } + return new Shift.ExportDefault({ body }); + } + + reduceExportFrom(node, { namedExports }) { + if ((node.namedExports.length === namedExports.length && node.namedExports.every((v, i) => v === namedExports[i]))) { + return node; + } + return new Shift.ExportFrom({ namedExports, moduleSpecifier: node.moduleSpecifier }); + } + + reduceExportFromSpecifier(node) { + return node; + } + + reduceExportLocalSpecifier(node, { name }) { + if (node.name === name) { + return node; + } + return new Shift.ExportLocalSpecifier({ name, exportedName: node.exportedName }); + } + + reduceExportLocals(node, { namedExports }) { + if ((node.namedExports.length === namedExports.length && node.namedExports.every((v, i) => v === namedExports[i]))) { + return node; + } + return new Shift.ExportLocals({ namedExports }); + } + + reduceExpressionStatement(node, { expression }) { + if (node.expression === expression) { + return node; + } + return new Shift.ExpressionStatement({ expression }); + } + + reduceForAwaitStatement(node, { left, right, body }) { + if (node.left === left && node.right === right && node.body === body) { + return node; + } + return new Shift.ForAwaitStatement({ left, right, body }); + } + + reduceForInStatement(node, { left, right, body }) { + if (node.left === left && node.right === right && node.body === body) { + return node; + } + return new Shift.ForInStatement({ left, right, body }); + } + + reduceForOfStatement(node, { left, right, body }) { + if (node.left === left && node.right === right && node.body === body) { + return node; + } + return new Shift.ForOfStatement({ left, right, body }); + } + + reduceForStatement(node, { init, test, update, body }) { + if (node.init === init && node.test === test && node.update === update && node.body === body) { + return node; + } + return new Shift.ForStatement({ init, test, update, body }); + } + + reduceFormalParameters(node, { items, rest }) { + if ((node.items.length === items.length && node.items.every((v, i) => v === items[i])) && node.rest === rest) { + return node; + } + return new Shift.FormalParameters({ items, rest }); + } + + reduceFunctionBody(node, { directives, statements }) { + if ((node.directives.length === directives.length && node.directives.every((v, i) => v === directives[i])) && (node.statements.length === statements.length && node.statements.every((v, i) => v === statements[i]))) { + return node; + } + return new Shift.FunctionBody({ directives, statements }); + } + + reduceFunctionDeclaration(node, { name, params, body }) { + if (node.name === name && node.params === params && node.body === body) { + return node; + } + return new Shift.FunctionDeclaration({ isAsync: node.isAsync, isGenerator: node.isGenerator, name, params, body }); + } + + reduceFunctionExpression(node, { name, params, body }) { + if (node.name === name && node.params === params && node.body === body) { + return node; + } + return new Shift.FunctionExpression({ isAsync: node.isAsync, isGenerator: node.isGenerator, name, params, body }); + } + + reduceGetter(node, { name, body }) { + if (node.name === name && node.body === body) { + return node; + } + return new Shift.Getter({ name, body }); + } + + reduceIdentifierExpression(node) { + return node; + } + + reduceIfStatement(node, { test, consequent, alternate }) { + if (node.test === test && node.consequent === consequent && node.alternate === alternate) { + return node; + } + return new Shift.IfStatement({ test, consequent, alternate }); + } + + reduceImport(node, { defaultBinding, namedImports }) { + if (node.defaultBinding === defaultBinding && (node.namedImports.length === namedImports.length && node.namedImports.every((v, i) => v === namedImports[i]))) { + return node; + } + return new Shift.Import({ defaultBinding, namedImports, moduleSpecifier: node.moduleSpecifier }); + } + + reduceImportNamespace(node, { defaultBinding, namespaceBinding }) { + if (node.defaultBinding === defaultBinding && node.namespaceBinding === namespaceBinding) { + return node; + } + return new Shift.ImportNamespace({ defaultBinding, namespaceBinding, moduleSpecifier: node.moduleSpecifier }); + } + + reduceImportSpecifier(node, { binding }) { + if (node.binding === binding) { + return node; + } + return new Shift.ImportSpecifier({ name: node.name, binding }); + } + + reduceLabeledStatement(node, { body }) { + if (node.body === body) { + return node; + } + return new Shift.LabeledStatement({ label: node.label, body }); + } + + reduceLiteralBooleanExpression(node) { + return node; + } + + reduceLiteralInfinityExpression(node) { + return node; + } + + reduceLiteralNullExpression(node) { + return node; + } + + reduceLiteralNumericExpression(node) { + return node; + } + + reduceLiteralRegExpExpression(node) { + return node; + } + + reduceLiteralStringExpression(node) { + return node; + } + + reduceMethod(node, { name, params, body }) { + if (node.name === name && node.params === params && node.body === body) { + return node; + } + return new Shift.Method({ isAsync: node.isAsync, isGenerator: node.isGenerator, name, params, body }); + } + + reduceModule(node, { directives, items }) { + if ((node.directives.length === directives.length && node.directives.every((v, i) => v === directives[i])) && (node.items.length === items.length && node.items.every((v, i) => v === items[i]))) { + return node; + } + return new Shift.Module({ directives, items }); + } + + reduceNewExpression(node, { callee, arguments: _arguments }) { + if (node.callee === callee && (node.arguments.length === _arguments.length && node.arguments.every((v, i) => v === _arguments[i]))) { + return node; + } + return new Shift.NewExpression({ callee, arguments: _arguments }); + } + + reduceNewTargetExpression(node) { + return node; + } + + reduceObjectAssignmentTarget(node, { properties, rest }) { + if ((node.properties.length === properties.length && node.properties.every((v, i) => v === properties[i])) && node.rest === rest) { + return node; + } + return new Shift.ObjectAssignmentTarget({ properties, rest }); + } + + reduceObjectBinding(node, { properties, rest }) { + if ((node.properties.length === properties.length && node.properties.every((v, i) => v === properties[i])) && node.rest === rest) { + return node; + } + return new Shift.ObjectBinding({ properties, rest }); + } + + reduceObjectExpression(node, { properties }) { + if ((node.properties.length === properties.length && node.properties.every((v, i) => v === properties[i]))) { + return node; + } + return new Shift.ObjectExpression({ properties }); + } + + reduceReturnStatement(node, { expression }) { + if (node.expression === expression) { + return node; + } + return new Shift.ReturnStatement({ expression }); + } + + reduceScript(node, { directives, statements }) { + if ((node.directives.length === directives.length && node.directives.every((v, i) => v === directives[i])) && (node.statements.length === statements.length && node.statements.every((v, i) => v === statements[i]))) { + return node; + } + return new Shift.Script({ directives, statements }); + } + + reduceSetter(node, { name, param, body }) { + if (node.name === name && node.param === param && node.body === body) { + return node; + } + return new Shift.Setter({ name, param, body }); + } + + reduceShorthandProperty(node, { name }) { + if (node.name === name) { + return node; + } + return new Shift.ShorthandProperty({ name }); + } + + reduceSpreadElement(node, { expression }) { + if (node.expression === expression) { + return node; + } + return new Shift.SpreadElement({ expression }); + } + + reduceSpreadProperty(node, { expression }) { + if (node.expression === expression) { + return node; + } + return new Shift.SpreadProperty({ expression }); + } + + reduceStaticMemberAssignmentTarget(node, { object }) { + if (node.object === object) { + return node; + } + return new Shift.StaticMemberAssignmentTarget({ object, property: node.property }); + } + + reduceStaticMemberExpression(node, { object }) { + if (node.object === object) { + return node; + } + return new Shift.StaticMemberExpression({ object, property: node.property }); + } + + reduceStaticPropertyName(node) { + return node; + } + + reduceSuper(node) { + return node; + } + + reduceSwitchCase(node, { test, consequent }) { + if (node.test === test && (node.consequent.length === consequent.length && node.consequent.every((v, i) => v === consequent[i]))) { + return node; + } + return new Shift.SwitchCase({ test, consequent }); + } + + reduceSwitchDefault(node, { consequent }) { + if ((node.consequent.length === consequent.length && node.consequent.every((v, i) => v === consequent[i]))) { + return node; + } + return new Shift.SwitchDefault({ consequent }); + } + + reduceSwitchStatement(node, { discriminant, cases }) { + if (node.discriminant === discriminant && (node.cases.length === cases.length && node.cases.every((v, i) => v === cases[i]))) { + return node; + } + return new Shift.SwitchStatement({ discriminant, cases }); + } + + reduceSwitchStatementWithDefault(node, { discriminant, preDefaultCases, defaultCase, postDefaultCases }) { + if (node.discriminant === discriminant && (node.preDefaultCases.length === preDefaultCases.length && node.preDefaultCases.every((v, i) => v === preDefaultCases[i])) && node.defaultCase === defaultCase && (node.postDefaultCases.length === postDefaultCases.length && node.postDefaultCases.every((v, i) => v === postDefaultCases[i]))) { + return node; + } + return new Shift.SwitchStatementWithDefault({ discriminant, preDefaultCases, defaultCase, postDefaultCases }); + } + + reduceTemplateElement(node) { + return node; + } + + reduceTemplateExpression(node, { tag, elements }) { + if (node.tag === tag && (node.elements.length === elements.length && node.elements.every((v, i) => v === elements[i]))) { + return node; + } + return new Shift.TemplateExpression({ tag, elements }); + } + + reduceThisExpression(node) { + return node; + } + + reduceThrowStatement(node, { expression }) { + if (node.expression === expression) { + return node; + } + return new Shift.ThrowStatement({ expression }); + } + + reduceTryCatchStatement(node, { body, catchClause }) { + if (node.body === body && node.catchClause === catchClause) { + return node; + } + return new Shift.TryCatchStatement({ body, catchClause }); + } + + reduceTryFinallyStatement(node, { body, catchClause, finalizer }) { + if (node.body === body && node.catchClause === catchClause && node.finalizer === finalizer) { + return node; + } + return new Shift.TryFinallyStatement({ body, catchClause, finalizer }); + } + + reduceUnaryExpression(node, { operand }) { + if (node.operand === operand) { + return node; + } + return new Shift.UnaryExpression({ operator: node.operator, operand }); + } + + reduceUpdateExpression(node, { operand }) { + if (node.operand === operand) { + return node; + } + return new Shift.UpdateExpression({ isPrefix: node.isPrefix, operator: node.operator, operand }); + } + + reduceVariableDeclaration(node, { declarators }) { + if ((node.declarators.length === declarators.length && node.declarators.every((v, i) => v === declarators[i]))) { + return node; + } + return new Shift.VariableDeclaration({ kind: node.kind, declarators }); + } + + reduceVariableDeclarationStatement(node, { declaration }) { + if (node.declaration === declaration) { + return node; + } + return new Shift.VariableDeclarationStatement({ declaration }); + } + + reduceVariableDeclarator(node, { binding, init }) { + if (node.binding === binding && node.init === init) { + return node; + } + return new Shift.VariableDeclarator({ binding, init }); + } + + reduceWhileStatement(node, { test, body }) { + if (node.test === test && node.body === body) { + return node; + } + return new Shift.WhileStatement({ test, body }); + } + + reduceWithStatement(node, { object, body }) { + if (node.object === object && node.body === body) { + return node; + } + return new Shift.WithStatement({ object, body }); + } + + reduceYieldExpression(node, { expression }) { + if (node.expression === expression) { + return node; + } + return new Shift.YieldExpression({ expression }); + } + + reduceYieldGeneratorExpression(node, { expression }) { + if (node.expression === expression) { + return node; + } + return new Shift.YieldGeneratorExpression({ expression }); + } +} diff --git a/repl/src/shift-reducer/memoize.js b/repl/src/shift-reducer/memoize.js new file mode 100644 index 00000000..2f3887e6 --- /dev/null +++ b/repl/src/shift-reducer/memoize.js @@ -0,0 +1,914 @@ +// Generated by generate-memoize.js +/** + * Copyright 2018 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Shift from 'shift-ast'; + +export default function memoize(reducer) { + const cache = new WeakMap; + return { + reduceArrayAssignmentTarget(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceArrayAssignmentTarget(node, arg); + cache.set(node, res); + return res; + }, + + reduceArrayBinding(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceArrayBinding(node, arg); + cache.set(node, res); + return res; + }, + + reduceArrayExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceArrayExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceArrowExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceArrowExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceAssignmentExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceAssignmentExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceAssignmentTargetIdentifier(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceAssignmentTargetIdentifier(node); + cache.set(node, res); + return res; + }, + + reduceAssignmentTargetPropertyIdentifier(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceAssignmentTargetPropertyIdentifier(node, arg); + cache.set(node, res); + return res; + }, + + reduceAssignmentTargetPropertyProperty(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceAssignmentTargetPropertyProperty(node, arg); + cache.set(node, res); + return res; + }, + + reduceAssignmentTargetWithDefault(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceAssignmentTargetWithDefault(node, arg); + cache.set(node, res); + return res; + }, + + reduceAwaitExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceAwaitExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceBinaryExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceBinaryExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceBindingIdentifier(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceBindingIdentifier(node); + cache.set(node, res); + return res; + }, + + reduceBindingPropertyIdentifier(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceBindingPropertyIdentifier(node, arg); + cache.set(node, res); + return res; + }, + + reduceBindingPropertyProperty(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceBindingPropertyProperty(node, arg); + cache.set(node, res); + return res; + }, + + reduceBindingWithDefault(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceBindingWithDefault(node, arg); + cache.set(node, res); + return res; + }, + + reduceBlock(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceBlock(node, arg); + cache.set(node, res); + return res; + }, + + reduceBlockStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceBlockStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceBreakStatement(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceBreakStatement(node); + cache.set(node, res); + return res; + }, + + reduceCallExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceCallExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceCatchClause(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceCatchClause(node, arg); + cache.set(node, res); + return res; + }, + + reduceClassDeclaration(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceClassDeclaration(node, arg); + cache.set(node, res); + return res; + }, + + reduceClassElement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceClassElement(node, arg); + cache.set(node, res); + return res; + }, + + reduceClassExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceClassExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceCompoundAssignmentExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceCompoundAssignmentExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceComputedMemberAssignmentTarget(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceComputedMemberAssignmentTarget(node, arg); + cache.set(node, res); + return res; + }, + + reduceComputedMemberExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceComputedMemberExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceComputedPropertyName(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceComputedPropertyName(node, arg); + cache.set(node, res); + return res; + }, + + reduceConditionalExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceConditionalExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceContinueStatement(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceContinueStatement(node); + cache.set(node, res); + return res; + }, + + reduceDataProperty(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceDataProperty(node, arg); + cache.set(node, res); + return res; + }, + + reduceDebuggerStatement(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceDebuggerStatement(node); + cache.set(node, res); + return res; + }, + + reduceDirective(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceDirective(node); + cache.set(node, res); + return res; + }, + + reduceDoWhileStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceDoWhileStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceEmptyStatement(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceEmptyStatement(node); + cache.set(node, res); + return res; + }, + + reduceExport(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceExport(node, arg); + cache.set(node, res); + return res; + }, + + reduceExportAllFrom(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceExportAllFrom(node); + cache.set(node, res); + return res; + }, + + reduceExportDefault(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceExportDefault(node, arg); + cache.set(node, res); + return res; + }, + + reduceExportFrom(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceExportFrom(node, arg); + cache.set(node, res); + return res; + }, + + reduceExportFromSpecifier(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceExportFromSpecifier(node); + cache.set(node, res); + return res; + }, + + reduceExportLocalSpecifier(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceExportLocalSpecifier(node, arg); + cache.set(node, res); + return res; + }, + + reduceExportLocals(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceExportLocals(node, arg); + cache.set(node, res); + return res; + }, + + reduceExpressionStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceExpressionStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceForAwaitStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceForAwaitStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceForInStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceForInStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceForOfStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceForOfStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceForStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceForStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceFormalParameters(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceFormalParameters(node, arg); + cache.set(node, res); + return res; + }, + + reduceFunctionBody(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceFunctionBody(node, arg); + cache.set(node, res); + return res; + }, + + reduceFunctionDeclaration(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceFunctionDeclaration(node, arg); + cache.set(node, res); + return res; + }, + + reduceFunctionExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceFunctionExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceGetter(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceGetter(node, arg); + cache.set(node, res); + return res; + }, + + reduceIdentifierExpression(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceIdentifierExpression(node); + cache.set(node, res); + return res; + }, + + reduceIfStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceIfStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceImport(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceImport(node, arg); + cache.set(node, res); + return res; + }, + + reduceImportNamespace(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceImportNamespace(node, arg); + cache.set(node, res); + return res; + }, + + reduceImportSpecifier(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceImportSpecifier(node, arg); + cache.set(node, res); + return res; + }, + + reduceLabeledStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceLabeledStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceLiteralBooleanExpression(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceLiteralBooleanExpression(node); + cache.set(node, res); + return res; + }, + + reduceLiteralInfinityExpression(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceLiteralInfinityExpression(node); + cache.set(node, res); + return res; + }, + + reduceLiteralNullExpression(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceLiteralNullExpression(node); + cache.set(node, res); + return res; + }, + + reduceLiteralNumericExpression(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceLiteralNumericExpression(node); + cache.set(node, res); + return res; + }, + + reduceLiteralRegExpExpression(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceLiteralRegExpExpression(node); + cache.set(node, res); + return res; + }, + + reduceLiteralStringExpression(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceLiteralStringExpression(node); + cache.set(node, res); + return res; + }, + + reduceMethod(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceMethod(node, arg); + cache.set(node, res); + return res; + }, + + reduceModule(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceModule(node, arg); + cache.set(node, res); + return res; + }, + + reduceNewExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceNewExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceNewTargetExpression(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceNewTargetExpression(node); + cache.set(node, res); + return res; + }, + + reduceObjectAssignmentTarget(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceObjectAssignmentTarget(node, arg); + cache.set(node, res); + return res; + }, + + reduceObjectBinding(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceObjectBinding(node, arg); + cache.set(node, res); + return res; + }, + + reduceObjectExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceObjectExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceReturnStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceReturnStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceScript(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceScript(node, arg); + cache.set(node, res); + return res; + }, + + reduceSetter(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceSetter(node, arg); + cache.set(node, res); + return res; + }, + + reduceShorthandProperty(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceShorthandProperty(node, arg); + cache.set(node, res); + return res; + }, + + reduceSpreadElement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceSpreadElement(node, arg); + cache.set(node, res); + return res; + }, + + reduceSpreadProperty(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceSpreadProperty(node, arg); + cache.set(node, res); + return res; + }, + + reduceStaticMemberAssignmentTarget(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceStaticMemberAssignmentTarget(node, arg); + cache.set(node, res); + return res; + }, + + reduceStaticMemberExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceStaticMemberExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceStaticPropertyName(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceStaticPropertyName(node); + cache.set(node, res); + return res; + }, + + reduceSuper(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceSuper(node); + cache.set(node, res); + return res; + }, + + reduceSwitchCase(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceSwitchCase(node, arg); + cache.set(node, res); + return res; + }, + + reduceSwitchDefault(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceSwitchDefault(node, arg); + cache.set(node, res); + return res; + }, + + reduceSwitchStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceSwitchStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceSwitchStatementWithDefault(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceSwitchStatementWithDefault(node, arg); + cache.set(node, res); + return res; + }, + + reduceTemplateElement(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceTemplateElement(node); + cache.set(node, res); + return res; + }, + + reduceTemplateExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceTemplateExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceThisExpression(node) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceThisExpression(node); + cache.set(node, res); + return res; + }, + + reduceThrowStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceThrowStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceTryCatchStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceTryCatchStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceTryFinallyStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceTryFinallyStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceUnaryExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceUnaryExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceUpdateExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceUpdateExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceVariableDeclaration(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceVariableDeclaration(node, arg); + cache.set(node, res); + return res; + }, + + reduceVariableDeclarationStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceVariableDeclarationStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceVariableDeclarator(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceVariableDeclarator(node, arg); + cache.set(node, res); + return res; + }, + + reduceWhileStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceWhileStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceWithStatement(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceWithStatement(node, arg); + cache.set(node, res); + return res; + }, + + reduceYieldExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceYieldExpression(node, arg); + cache.set(node, res); + return res; + }, + + reduceYieldGeneratorExpression(node, arg) { + if (cache.has(node)) { + return cache.get(node); + } + const res = reducer.reduceYieldGeneratorExpression(node, arg); + cache.set(node, res); + return res; + }, + }; +} diff --git a/repl/src/shift-reducer/monoidal-reducer.js b/repl/src/shift-reducer/monoidal-reducer.js new file mode 100644 index 00000000..29369a80 --- /dev/null +++ b/repl/src/shift-reducer/monoidal-reducer.js @@ -0,0 +1,430 @@ +// Generated by generate-monoidal-reducer.js +/** + * Copyright 2018 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Shift from 'shift-ast'; + +export default class MonoidalReducer { + constructor(monoid) { + let identity = monoid.empty(); + this.identity = identity; + let concat; + if (monoid.prototype && typeof monoid.prototype.concat === 'function') { + concat = Function.prototype.call.bind(monoid.prototype.concat); + } else if (typeof monoid.concat === 'function') { + concat = monoid.concat; + } else { + throw new TypeError('Monoid must provide a `concat` method'); + } + this.append = (...args) => args.reduce(concat, identity); + } + + reduceArrayAssignmentTarget(node, { elements, rest }) { + return this.append(...elements.filter(n => n != null), rest == null ? this.identity : rest); + } + + reduceArrayBinding(node, { elements, rest }) { + return this.append(...elements.filter(n => n != null), rest == null ? this.identity : rest); + } + + reduceArrayExpression(node, { elements }) { + return this.append(...elements.filter(n => n != null)); + } + + reduceArrowExpression(node, { params, body }) { + return this.append(params, body); + } + + reduceAssignmentExpression(node, { binding, expression }) { + return this.append(binding, expression); + } + + reduceAssignmentTargetIdentifier(node) { + return this.identity; + } + + reduceAssignmentTargetPropertyIdentifier(node, { binding, init }) { + return this.append(binding, init == null ? this.identity : init); + } + + reduceAssignmentTargetPropertyProperty(node, { name, binding }) { + return this.append(name, binding); + } + + reduceAssignmentTargetWithDefault(node, { binding, init }) { + return this.append(binding, init); + } + + reduceAwaitExpression(node, { expression }) { + return expression; + } + + reduceBinaryExpression(node, { left, right }) { + return this.append(left, right); + } + + reduceBindingIdentifier(node) { + return this.identity; + } + + reduceBindingPropertyIdentifier(node, { binding, init }) { + return this.append(binding, init == null ? this.identity : init); + } + + reduceBindingPropertyProperty(node, { name, binding }) { + return this.append(name, binding); + } + + reduceBindingWithDefault(node, { binding, init }) { + return this.append(binding, init); + } + + reduceBlock(node, { statements }) { + return this.append(...statements); + } + + reduceBlockStatement(node, { block }) { + return block; + } + + reduceBreakStatement(node) { + return this.identity; + } + + reduceCallExpression(node, { callee, arguments: _arguments }) { + return this.append(callee, ..._arguments); + } + + reduceCatchClause(node, { binding, body }) { + return this.append(binding, body); + } + + reduceClassDeclaration(node, { name, super: _super, elements }) { + return this.append(name, _super == null ? this.identity : _super, ...elements); + } + + reduceClassElement(node, { method }) { + return method; + } + + reduceClassExpression(node, { name, super: _super, elements }) { + return this.append(name == null ? this.identity : name, _super == null ? this.identity : _super, ...elements); + } + + reduceCompoundAssignmentExpression(node, { binding, expression }) { + return this.append(binding, expression); + } + + reduceComputedMemberAssignmentTarget(node, { object, expression }) { + return this.append(object, expression); + } + + reduceComputedMemberExpression(node, { object, expression }) { + return this.append(object, expression); + } + + reduceComputedPropertyName(node, { expression }) { + return expression; + } + + reduceConditionalExpression(node, { test, consequent, alternate }) { + return this.append(test, consequent, alternate); + } + + reduceContinueStatement(node) { + return this.identity; + } + + reduceDataProperty(node, { name, expression }) { + return this.append(name, expression); + } + + reduceDebuggerStatement(node) { + return this.identity; + } + + reduceDirective(node) { + return this.identity; + } + + reduceDoWhileStatement(node, { body, test }) { + return this.append(body, test); + } + + reduceEmptyStatement(node) { + return this.identity; + } + + reduceExport(node, { declaration }) { + return declaration; + } + + reduceExportAllFrom(node) { + return this.identity; + } + + reduceExportDefault(node, { body }) { + return body; + } + + reduceExportFrom(node, { namedExports }) { + return this.append(...namedExports); + } + + reduceExportFromSpecifier(node) { + return this.identity; + } + + reduceExportLocalSpecifier(node, { name }) { + return name; + } + + reduceExportLocals(node, { namedExports }) { + return this.append(...namedExports); + } + + reduceExpressionStatement(node, { expression }) { + return expression; + } + + reduceForAwaitStatement(node, { left, right, body }) { + return this.append(left, right, body); + } + + reduceForInStatement(node, { left, right, body }) { + return this.append(left, right, body); + } + + reduceForOfStatement(node, { left, right, body }) { + return this.append(left, right, body); + } + + reduceForStatement(node, { init, test, update, body }) { + return this.append(init == null ? this.identity : init, test == null ? this.identity : test, update == null ? this.identity : update, body); + } + + reduceFormalParameters(node, { items, rest }) { + return this.append(...items, rest == null ? this.identity : rest); + } + + reduceFunctionBody(node, { directives, statements }) { + return this.append(...directives, ...statements); + } + + reduceFunctionDeclaration(node, { name, params, body }) { + return this.append(name, params, body); + } + + reduceFunctionExpression(node, { name, params, body }) { + return this.append(name == null ? this.identity : name, params, body); + } + + reduceGetter(node, { name, body }) { + return this.append(name, body); + } + + reduceIdentifierExpression(node) { + return this.identity; + } + + reduceIfStatement(node, { test, consequent, alternate }) { + return this.append(test, consequent, alternate == null ? this.identity : alternate); + } + + reduceImport(node, { defaultBinding, namedImports }) { + return this.append(defaultBinding == null ? this.identity : defaultBinding, ...namedImports); + } + + reduceImportNamespace(node, { defaultBinding, namespaceBinding }) { + return this.append(defaultBinding == null ? this.identity : defaultBinding, namespaceBinding); + } + + reduceImportSpecifier(node, { binding }) { + return binding; + } + + reduceLabeledStatement(node, { body }) { + return body; + } + + reduceLiteralBooleanExpression(node) { + return this.identity; + } + + reduceLiteralInfinityExpression(node) { + return this.identity; + } + + reduceLiteralNullExpression(node) { + return this.identity; + } + + reduceLiteralNumericExpression(node) { + return this.identity; + } + + reduceLiteralRegExpExpression(node) { + return this.identity; + } + + reduceLiteralStringExpression(node) { + return this.identity; + } + + reduceMethod(node, { name, params, body }) { + return this.append(name, params, body); + } + + reduceModule(node, { directives, items }) { + return this.append(...directives, ...items); + } + + reduceNewExpression(node, { callee, arguments: _arguments }) { + return this.append(callee, ..._arguments); + } + + reduceNewTargetExpression(node) { + return this.identity; + } + + reduceObjectAssignmentTarget(node, { properties, rest }) { + return this.append(...properties, rest == null ? this.identity : rest); + } + + reduceObjectBinding(node, { properties, rest }) { + return this.append(...properties, rest == null ? this.identity : rest); + } + + reduceObjectExpression(node, { properties }) { + return this.append(...properties); + } + + reduceReturnStatement(node, { expression }) { + return expression == null ? this.identity : expression; + } + + reduceScript(node, { directives, statements }) { + return this.append(...directives, ...statements); + } + + reduceSetter(node, { name, param, body }) { + return this.append(name, param, body); + } + + reduceShorthandProperty(node, { name }) { + return name; + } + + reduceSpreadElement(node, { expression }) { + return expression; + } + + reduceSpreadProperty(node, { expression }) { + return expression; + } + + reduceStaticMemberAssignmentTarget(node, { object }) { + return object; + } + + reduceStaticMemberExpression(node, { object }) { + return object; + } + + reduceStaticPropertyName(node) { + return this.identity; + } + + reduceSuper(node) { + return this.identity; + } + + reduceSwitchCase(node, { test, consequent }) { + return this.append(test, ...consequent); + } + + reduceSwitchDefault(node, { consequent }) { + return this.append(...consequent); + } + + reduceSwitchStatement(node, { discriminant, cases }) { + return this.append(discriminant, ...cases); + } + + reduceSwitchStatementWithDefault(node, { discriminant, preDefaultCases, defaultCase, postDefaultCases }) { + return this.append(discriminant, ...preDefaultCases, defaultCase, ...postDefaultCases); + } + + reduceTemplateElement(node) { + return this.identity; + } + + reduceTemplateExpression(node, { tag, elements }) { + return this.append(tag == null ? this.identity : tag, ...elements); + } + + reduceThisExpression(node) { + return this.identity; + } + + reduceThrowStatement(node, { expression }) { + return expression; + } + + reduceTryCatchStatement(node, { body, catchClause }) { + return this.append(body, catchClause); + } + + reduceTryFinallyStatement(node, { body, catchClause, finalizer }) { + return this.append(body, catchClause == null ? this.identity : catchClause, finalizer); + } + + reduceUnaryExpression(node, { operand }) { + return operand; + } + + reduceUpdateExpression(node, { operand }) { + return operand; + } + + reduceVariableDeclaration(node, { declarators }) { + return this.append(...declarators); + } + + reduceVariableDeclarationStatement(node, { declaration }) { + return declaration; + } + + reduceVariableDeclarator(node, { binding, init }) { + return this.append(binding, init == null ? this.identity : init); + } + + reduceWhileStatement(node, { test, body }) { + return this.append(test, body); + } + + reduceWithStatement(node, { object, body }) { + return this.append(object, body); + } + + reduceYieldExpression(node, { expression }) { + return expression == null ? this.identity : expression; + } + + reduceYieldGeneratorExpression(node, { expression }) { + return expression; + } +} diff --git a/repl/src/shift-reducer/reducers.js b/repl/src/shift-reducer/reducers.js new file mode 100644 index 00000000..ed7a43cb --- /dev/null +++ b/repl/src/shift-reducer/reducers.js @@ -0,0 +1,91 @@ +/* + * Copyright 2018 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +import MonoidalReducer from './monoidal-reducer.js'; +import ThunkedMonoidalReducer from './thunked-monoidal-reducer.js'; + +const PlusMonoid = { + empty: () => 0, + concat: (a, b) => a + b, +}; + +const ConcatMonoid = { + empty: () => [], + concat: (a, b) => a.concat(b), +}; + +const AndMonoid = { + empty: () => true, + concat: (a, b) => a && b, + concatThunk: (a, b) => a && b(), +}; + +const OrMonoid = { + empty: () => false, + concat: (a, b) => a || b, + concatThunk: (a, b) => a || b(), +}; + + +export class PlusReducer extends MonoidalReducer { + constructor() { + super(PlusMonoid); + } +} + +export class ThunkedPlusReducer extends ThunkedMonoidalReducer { + constructor() { + super(PlusMonoid); + } +} + +export class ConcatReducer extends MonoidalReducer { + constructor() { + super(ConcatMonoid); + } +} + +export class ThunkedConcatReducer extends ThunkedMonoidalReducer { + constructor() { + super(ConcatMonoid); + } +} + +export class AndReducer extends MonoidalReducer { + constructor() { + super(AndMonoid); + } +} + +export class ThunkedAndReducer extends ThunkedMonoidalReducer { + constructor() { + super(AndMonoid); + } +} + +export class OrReducer extends MonoidalReducer { + constructor() { + super(OrMonoid); + } +} + +export class ThunkedOrReducer extends ThunkedMonoidalReducer { + constructor() { + super(OrMonoid); + } +} diff --git a/repl/src/shift-reducer/thunked-director.js b/repl/src/shift-reducer/thunked-director.js new file mode 100644 index 00000000..01fd3d3f --- /dev/null +++ b/repl/src/shift-reducer/thunked-director.js @@ -0,0 +1,418 @@ +// Generated by generate-director.js +/** + * Copyright 2018 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const director = { + ArrayAssignmentTarget(reducer, node) { + return reducer.reduceArrayAssignmentTarget(node, { elements: node.elements.map(v => v && (() => this[v.type](reducer, v))), rest: node.rest && (() => this[node.rest.type](reducer, node.rest)) }); + }, + + ArrayBinding(reducer, node) { + return reducer.reduceArrayBinding(node, { elements: node.elements.map(v => v && (() => this[v.type](reducer, v))), rest: node.rest && (() => this[node.rest.type](reducer, node.rest)) }); + }, + + ArrayExpression(reducer, node) { + return reducer.reduceArrayExpression(node, { elements: node.elements.map(v => v && (() => this[v.type](reducer, v))) }); + }, + + ArrowExpression(reducer, node) { + return reducer.reduceArrowExpression(node, { params: (() => this.FormalParameters(reducer, node.params)), body: (() => this[node.body.type](reducer, node.body)) }); + }, + + AssignmentExpression(reducer, node) { + return reducer.reduceAssignmentExpression(node, { binding: (() => this[node.binding.type](reducer, node.binding)), expression: (() => this[node.expression.type](reducer, node.expression)) }); + }, + + AssignmentTargetIdentifier(reducer, node) { + return reducer.reduceAssignmentTargetIdentifier(node); + }, + + AssignmentTargetPropertyIdentifier(reducer, node) { + return reducer.reduceAssignmentTargetPropertyIdentifier(node, { binding: (() => this.AssignmentTargetIdentifier(reducer, node.binding)), init: node.init && (() => this[node.init.type](reducer, node.init)) }); + }, + + AssignmentTargetPropertyProperty(reducer, node) { + return reducer.reduceAssignmentTargetPropertyProperty(node, { name: (() => this[node.name.type](reducer, node.name)), binding: (() => this[node.binding.type](reducer, node.binding)) }); + }, + + AssignmentTargetWithDefault(reducer, node) { + return reducer.reduceAssignmentTargetWithDefault(node, { binding: (() => this[node.binding.type](reducer, node.binding)), init: (() => this[node.init.type](reducer, node.init)) }); + }, + + AwaitExpression(reducer, node) { + return reducer.reduceAwaitExpression(node, { expression: (() => this[node.expression.type](reducer, node.expression)) }); + }, + + BinaryExpression(reducer, node) { + return reducer.reduceBinaryExpression(node, { left: (() => this[node.left.type](reducer, node.left)), right: (() => this[node.right.type](reducer, node.right)) }); + }, + + BindingIdentifier(reducer, node) { + return reducer.reduceBindingIdentifier(node); + }, + + BindingPropertyIdentifier(reducer, node) { + return reducer.reduceBindingPropertyIdentifier(node, { binding: (() => this.BindingIdentifier(reducer, node.binding)), init: node.init && (() => this[node.init.type](reducer, node.init)) }); + }, + + BindingPropertyProperty(reducer, node) { + return reducer.reduceBindingPropertyProperty(node, { name: (() => this[node.name.type](reducer, node.name)), binding: (() => this[node.binding.type](reducer, node.binding)) }); + }, + + BindingWithDefault(reducer, node) { + return reducer.reduceBindingWithDefault(node, { binding: (() => this[node.binding.type](reducer, node.binding)), init: (() => this[node.init.type](reducer, node.init)) }); + }, + + Block(reducer, node) { + return reducer.reduceBlock(node, { statements: node.statements.map(v => (() => this[v.type](reducer, v))) }); + }, + + BlockStatement(reducer, node) { + return reducer.reduceBlockStatement(node, { block: (() => this.Block(reducer, node.block)) }); + }, + + BreakStatement(reducer, node) { + return reducer.reduceBreakStatement(node); + }, + + CallExpression(reducer, node) { + return reducer.reduceCallExpression(node, { callee: (() => this[node.callee.type](reducer, node.callee)), arguments: node.arguments.map(v => (() => this[v.type](reducer, v))) }); + }, + + CatchClause(reducer, node) { + return reducer.reduceCatchClause(node, { binding: (() => this[node.binding.type](reducer, node.binding)), body: (() => this.Block(reducer, node.body)) }); + }, + + ClassDeclaration(reducer, node) { + return reducer.reduceClassDeclaration(node, { name: (() => this.BindingIdentifier(reducer, node.name)), super: node.super && (() => this[node.super.type](reducer, node.super)), elements: node.elements.map(v => (() => this.ClassElement(reducer, v))) }); + }, + + ClassElement(reducer, node) { + return reducer.reduceClassElement(node, { method: (() => this[node.method.type](reducer, node.method)) }); + }, + + ClassExpression(reducer, node) { + return reducer.reduceClassExpression(node, { name: node.name && (() => this.BindingIdentifier(reducer, node.name)), super: node.super && (() => this[node.super.type](reducer, node.super)), elements: node.elements.map(v => (() => this.ClassElement(reducer, v))) }); + }, + + CompoundAssignmentExpression(reducer, node) { + return reducer.reduceCompoundAssignmentExpression(node, { binding: (() => this[node.binding.type](reducer, node.binding)), expression: (() => this[node.expression.type](reducer, node.expression)) }); + }, + + ComputedMemberAssignmentTarget(reducer, node) { + return reducer.reduceComputedMemberAssignmentTarget(node, { object: (() => this[node.object.type](reducer, node.object)), expression: (() => this[node.expression.type](reducer, node.expression)) }); + }, + + ComputedMemberExpression(reducer, node) { + return reducer.reduceComputedMemberExpression(node, { object: (() => this[node.object.type](reducer, node.object)), expression: (() => this[node.expression.type](reducer, node.expression)) }); + }, + + ComputedPropertyName(reducer, node) { + return reducer.reduceComputedPropertyName(node, { expression: (() => this[node.expression.type](reducer, node.expression)) }); + }, + + ConditionalExpression(reducer, node) { + return reducer.reduceConditionalExpression(node, { test: (() => this[node.test.type](reducer, node.test)), consequent: (() => this[node.consequent.type](reducer, node.consequent)), alternate: (() => this[node.alternate.type](reducer, node.alternate)) }); + }, + + ContinueStatement(reducer, node) { + return reducer.reduceContinueStatement(node); + }, + + DataProperty(reducer, node) { + return reducer.reduceDataProperty(node, { name: (() => this[node.name.type](reducer, node.name)), expression: (() => this[node.expression.type](reducer, node.expression)) }); + }, + + DebuggerStatement(reducer, node) { + return reducer.reduceDebuggerStatement(node); + }, + + Directive(reducer, node) { + return reducer.reduceDirective(node); + }, + + DoWhileStatement(reducer, node) { + return reducer.reduceDoWhileStatement(node, { body: (() => this[node.body.type](reducer, node.body)), test: (() => this[node.test.type](reducer, node.test)) }); + }, + + EmptyStatement(reducer, node) { + return reducer.reduceEmptyStatement(node); + }, + + Export(reducer, node) { + return reducer.reduceExport(node, { declaration: (() => this[node.declaration.type](reducer, node.declaration)) }); + }, + + ExportAllFrom(reducer, node) { + return reducer.reduceExportAllFrom(node); + }, + + ExportDefault(reducer, node) { + return reducer.reduceExportDefault(node, { body: (() => this[node.body.type](reducer, node.body)) }); + }, + + ExportFrom(reducer, node) { + return reducer.reduceExportFrom(node, { namedExports: node.namedExports.map(v => (() => this.ExportFromSpecifier(reducer, v))) }); + }, + + ExportFromSpecifier(reducer, node) { + return reducer.reduceExportFromSpecifier(node); + }, + + ExportLocalSpecifier(reducer, node) { + return reducer.reduceExportLocalSpecifier(node, { name: (() => this.IdentifierExpression(reducer, node.name)) }); + }, + + ExportLocals(reducer, node) { + return reducer.reduceExportLocals(node, { namedExports: node.namedExports.map(v => (() => this.ExportLocalSpecifier(reducer, v))) }); + }, + + ExpressionStatement(reducer, node) { + return reducer.reduceExpressionStatement(node, { expression: (() => this[node.expression.type](reducer, node.expression)) }); + }, + + ForAwaitStatement(reducer, node) { + return reducer.reduceForAwaitStatement(node, { left: (() => this[node.left.type](reducer, node.left)), right: (() => this[node.right.type](reducer, node.right)), body: (() => this[node.body.type](reducer, node.body)) }); + }, + + ForInStatement(reducer, node) { + return reducer.reduceForInStatement(node, { left: (() => this[node.left.type](reducer, node.left)), right: (() => this[node.right.type](reducer, node.right)), body: (() => this[node.body.type](reducer, node.body)) }); + }, + + ForOfStatement(reducer, node) { + return reducer.reduceForOfStatement(node, { left: (() => this[node.left.type](reducer, node.left)), right: (() => this[node.right.type](reducer, node.right)), body: (() => this[node.body.type](reducer, node.body)) }); + }, + + ForStatement(reducer, node) { + return reducer.reduceForStatement(node, { init: node.init && (() => this[node.init.type](reducer, node.init)), test: node.test && (() => this[node.test.type](reducer, node.test)), update: node.update && (() => this[node.update.type](reducer, node.update)), body: (() => this[node.body.type](reducer, node.body)) }); + }, + + FormalParameters(reducer, node) { + return reducer.reduceFormalParameters(node, { items: node.items.map(v => (() => this[v.type](reducer, v))), rest: node.rest && (() => this[node.rest.type](reducer, node.rest)) }); + }, + + FunctionBody(reducer, node) { + return reducer.reduceFunctionBody(node, { directives: node.directives.map(v => (() => this.Directive(reducer, v))), statements: node.statements.map(v => (() => this[v.type](reducer, v))) }); + }, + + FunctionDeclaration(reducer, node) { + return reducer.reduceFunctionDeclaration(node, { name: (() => this.BindingIdentifier(reducer, node.name)), params: (() => this.FormalParameters(reducer, node.params)), body: (() => this.FunctionBody(reducer, node.body)) }); + }, + + FunctionExpression(reducer, node) { + return reducer.reduceFunctionExpression(node, { name: node.name && (() => this.BindingIdentifier(reducer, node.name)), params: (() => this.FormalParameters(reducer, node.params)), body: (() => this.FunctionBody(reducer, node.body)) }); + }, + + Getter(reducer, node) { + return reducer.reduceGetter(node, { name: (() => this[node.name.type](reducer, node.name)), body: (() => this.FunctionBody(reducer, node.body)) }); + }, + + IdentifierExpression(reducer, node) { + return reducer.reduceIdentifierExpression(node); + }, + + IfStatement(reducer, node) { + return reducer.reduceIfStatement(node, { test: (() => this[node.test.type](reducer, node.test)), consequent: (() => this[node.consequent.type](reducer, node.consequent)), alternate: node.alternate && (() => this[node.alternate.type](reducer, node.alternate)) }); + }, + + Import(reducer, node) { + return reducer.reduceImport(node, { defaultBinding: node.defaultBinding && (() => this.BindingIdentifier(reducer, node.defaultBinding)), namedImports: node.namedImports.map(v => (() => this.ImportSpecifier(reducer, v))) }); + }, + + ImportNamespace(reducer, node) { + return reducer.reduceImportNamespace(node, { defaultBinding: node.defaultBinding && (() => this.BindingIdentifier(reducer, node.defaultBinding)), namespaceBinding: (() => this.BindingIdentifier(reducer, node.namespaceBinding)) }); + }, + + ImportSpecifier(reducer, node) { + return reducer.reduceImportSpecifier(node, { binding: (() => this.BindingIdentifier(reducer, node.binding)) }); + }, + + LabeledStatement(reducer, node) { + return reducer.reduceLabeledStatement(node, { body: (() => this[node.body.type](reducer, node.body)) }); + }, + + LiteralBooleanExpression(reducer, node) { + return reducer.reduceLiteralBooleanExpression(node); + }, + + LiteralInfinityExpression(reducer, node) { + return reducer.reduceLiteralInfinityExpression(node); + }, + + LiteralNullExpression(reducer, node) { + return reducer.reduceLiteralNullExpression(node); + }, + + LiteralNumericExpression(reducer, node) { + return reducer.reduceLiteralNumericExpression(node); + }, + + LiteralRegExpExpression(reducer, node) { + return reducer.reduceLiteralRegExpExpression(node); + }, + + LiteralStringExpression(reducer, node) { + return reducer.reduceLiteralStringExpression(node); + }, + + Method(reducer, node) { + return reducer.reduceMethod(node, { name: (() => this[node.name.type](reducer, node.name)), params: (() => this.FormalParameters(reducer, node.params)), body: (() => this.FunctionBody(reducer, node.body)) }); + }, + + Module(reducer, node) { + return reducer.reduceModule(node, { directives: node.directives.map(v => (() => this.Directive(reducer, v))), items: node.items.map(v => (() => this[v.type](reducer, v))) }); + }, + + NewExpression(reducer, node) { + return reducer.reduceNewExpression(node, { callee: (() => this[node.callee.type](reducer, node.callee)), arguments: node.arguments.map(v => (() => this[v.type](reducer, v))) }); + }, + + NewTargetExpression(reducer, node) { + return reducer.reduceNewTargetExpression(node); + }, + + ObjectAssignmentTarget(reducer, node) { + return reducer.reduceObjectAssignmentTarget(node, { properties: node.properties.map(v => (() => this[v.type](reducer, v))), rest: node.rest && (() => this[node.rest.type](reducer, node.rest)) }); + }, + + ObjectBinding(reducer, node) { + return reducer.reduceObjectBinding(node, { properties: node.properties.map(v => (() => this[v.type](reducer, v))), rest: node.rest && (() => this[node.rest.type](reducer, node.rest)) }); + }, + + ObjectExpression(reducer, node) { + return reducer.reduceObjectExpression(node, { properties: node.properties.map(v => (() => this[v.type](reducer, v))) }); + }, + + ReturnStatement(reducer, node) { + return reducer.reduceReturnStatement(node, { expression: node.expression && (() => this[node.expression.type](reducer, node.expression)) }); + }, + + Script(reducer, node) { + return reducer.reduceScript(node, { directives: node.directives.map(v => (() => this.Directive(reducer, v))), statements: node.statements.map(v => (() => this[v.type](reducer, v))) }); + }, + + Setter(reducer, node) { + return reducer.reduceSetter(node, { name: (() => this[node.name.type](reducer, node.name)), param: (() => this[node.param.type](reducer, node.param)), body: (() => this.FunctionBody(reducer, node.body)) }); + }, + + ShorthandProperty(reducer, node) { + return reducer.reduceShorthandProperty(node, { name: (() => this.IdentifierExpression(reducer, node.name)) }); + }, + + SpreadElement(reducer, node) { + return reducer.reduceSpreadElement(node, { expression: (() => this[node.expression.type](reducer, node.expression)) }); + }, + + SpreadProperty(reducer, node) { + return reducer.reduceSpreadProperty(node, { expression: (() => this[node.expression.type](reducer, node.expression)) }); + }, + + StaticMemberAssignmentTarget(reducer, node) { + return reducer.reduceStaticMemberAssignmentTarget(node, { object: (() => this[node.object.type](reducer, node.object)) }); + }, + + StaticMemberExpression(reducer, node) { + return reducer.reduceStaticMemberExpression(node, { object: (() => this[node.object.type](reducer, node.object)) }); + }, + + StaticPropertyName(reducer, node) { + return reducer.reduceStaticPropertyName(node); + }, + + Super(reducer, node) { + return reducer.reduceSuper(node); + }, + + SwitchCase(reducer, node) { + return reducer.reduceSwitchCase(node, { test: (() => this[node.test.type](reducer, node.test)), consequent: node.consequent.map(v => (() => this[v.type](reducer, v))) }); + }, + + SwitchDefault(reducer, node) { + return reducer.reduceSwitchDefault(node, { consequent: node.consequent.map(v => (() => this[v.type](reducer, v))) }); + }, + + SwitchStatement(reducer, node) { + return reducer.reduceSwitchStatement(node, { discriminant: (() => this[node.discriminant.type](reducer, node.discriminant)), cases: node.cases.map(v => (() => this.SwitchCase(reducer, v))) }); + }, + + SwitchStatementWithDefault(reducer, node) { + return reducer.reduceSwitchStatementWithDefault(node, { discriminant: (() => this[node.discriminant.type](reducer, node.discriminant)), preDefaultCases: node.preDefaultCases.map(v => (() => this.SwitchCase(reducer, v))), defaultCase: (() => this.SwitchDefault(reducer, node.defaultCase)), postDefaultCases: node.postDefaultCases.map(v => (() => this.SwitchCase(reducer, v))) }); + }, + + TemplateElement(reducer, node) { + return reducer.reduceTemplateElement(node); + }, + + TemplateExpression(reducer, node) { + return reducer.reduceTemplateExpression(node, { tag: node.tag && (() => this[node.tag.type](reducer, node.tag)), elements: node.elements.map(v => (() => this[v.type](reducer, v))) }); + }, + + ThisExpression(reducer, node) { + return reducer.reduceThisExpression(node); + }, + + ThrowStatement(reducer, node) { + return reducer.reduceThrowStatement(node, { expression: (() => this[node.expression.type](reducer, node.expression)) }); + }, + + TryCatchStatement(reducer, node) { + return reducer.reduceTryCatchStatement(node, { body: (() => this.Block(reducer, node.body)), catchClause: (() => this.CatchClause(reducer, node.catchClause)) }); + }, + + TryFinallyStatement(reducer, node) { + return reducer.reduceTryFinallyStatement(node, { body: (() => this.Block(reducer, node.body)), catchClause: node.catchClause && (() => this.CatchClause(reducer, node.catchClause)), finalizer: (() => this.Block(reducer, node.finalizer)) }); + }, + + UnaryExpression(reducer, node) { + return reducer.reduceUnaryExpression(node, { operand: (() => this[node.operand.type](reducer, node.operand)) }); + }, + + UpdateExpression(reducer, node) { + return reducer.reduceUpdateExpression(node, { operand: (() => this[node.operand.type](reducer, node.operand)) }); + }, + + VariableDeclaration(reducer, node) { + return reducer.reduceVariableDeclaration(node, { declarators: node.declarators.map(v => (() => this.VariableDeclarator(reducer, v))) }); + }, + + VariableDeclarationStatement(reducer, node) { + return reducer.reduceVariableDeclarationStatement(node, { declaration: (() => this.VariableDeclaration(reducer, node.declaration)) }); + }, + + VariableDeclarator(reducer, node) { + return reducer.reduceVariableDeclarator(node, { binding: (() => this[node.binding.type](reducer, node.binding)), init: node.init && (() => this[node.init.type](reducer, node.init)) }); + }, + + WhileStatement(reducer, node) { + return reducer.reduceWhileStatement(node, { test: (() => this[node.test.type](reducer, node.test)), body: (() => this[node.body.type](reducer, node.body)) }); + }, + + WithStatement(reducer, node) { + return reducer.reduceWithStatement(node, { object: (() => this[node.object.type](reducer, node.object)), body: (() => this[node.body.type](reducer, node.body)) }); + }, + + YieldExpression(reducer, node) { + return reducer.reduceYieldExpression(node, { expression: node.expression && (() => this[node.expression.type](reducer, node.expression)) }); + }, + + YieldGeneratorExpression(reducer, node) { + return reducer.reduceYieldGeneratorExpression(node, { expression: (() => this[node.expression.type](reducer, node.expression)) }); + }, +}; + +export function thunkedReduce(reducer, node) { + return director[node.type](reducer, node); +} diff --git a/repl/src/shift-reducer/thunked-monoidal-reducer.js b/repl/src/shift-reducer/thunked-monoidal-reducer.js new file mode 100644 index 00000000..c1ba6dea --- /dev/null +++ b/repl/src/shift-reducer/thunked-monoidal-reducer.js @@ -0,0 +1,444 @@ +// Generated by generate-monoidal-reducer.js +/** + * Copyright 2018 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Shift from 'shift-ast'; + +export default class MonoidalReducer { + constructor(monoid) { + let identity = monoid.empty(); + this.identity = identity; + + let concatThunk; + if (monoid.prototype && typeof monoid.prototype.concatThunk === 'function') { + concatThunk = Function.prototype.call.bind(monoid.prototype.concatThunk); + } else if (typeof monoid.concatThunk === 'function') { + concatThunk = monoid.concatThunk; + } else { + let concat; + if (monoid.prototype && typeof monoid.prototype.concat === 'function') { + concat = Function.prototype.call.bind(monoid.prototype.concat); + } else if (typeof monoid.concat === 'function') { + concat = monoid.concat; + } else { + throw new TypeError('Monoid must provide a `concatThunk` or `concat` method'); + } + if (typeof monoid.isAbsorbing === 'function') { + let isAbsorbing = monoid.isAbsorbing; + concatThunk = (a, b) => isAbsorbing(a) ? a : concat(a, b()); + } else { + concatThunk = (a, b) => concat(a, b()); + } + } + this.append = (...args) => args.reduce(concatThunk, identity); + } + + reduceArrayAssignmentTarget(node, { elements, rest }) { + return this.append(...elements.filter(n => n != null), rest == null ? () => this.identity : rest); + } + + reduceArrayBinding(node, { elements, rest }) { + return this.append(...elements.filter(n => n != null), rest == null ? () => this.identity : rest); + } + + reduceArrayExpression(node, { elements }) { + return this.append(...elements.filter(n => n != null)); + } + + reduceArrowExpression(node, { params, body }) { + return this.append(params, body); + } + + reduceAssignmentExpression(node, { binding, expression }) { + return this.append(binding, expression); + } + + reduceAssignmentTargetIdentifier(node) { + return this.identity; + } + + reduceAssignmentTargetPropertyIdentifier(node, { binding, init }) { + return this.append(binding, init == null ? () => this.identity : init); + } + + reduceAssignmentTargetPropertyProperty(node, { name, binding }) { + return this.append(name, binding); + } + + reduceAssignmentTargetWithDefault(node, { binding, init }) { + return this.append(binding, init); + } + + reduceAwaitExpression(node, { expression }) { + return expression(); + } + + reduceBinaryExpression(node, { left, right }) { + return this.append(left, right); + } + + reduceBindingIdentifier(node) { + return this.identity; + } + + reduceBindingPropertyIdentifier(node, { binding, init }) { + return this.append(binding, init == null ? () => this.identity : init); + } + + reduceBindingPropertyProperty(node, { name, binding }) { + return this.append(name, binding); + } + + reduceBindingWithDefault(node, { binding, init }) { + return this.append(binding, init); + } + + reduceBlock(node, { statements }) { + return this.append(...statements); + } + + reduceBlockStatement(node, { block }) { + return block(); + } + + reduceBreakStatement(node) { + return this.identity; + } + + reduceCallExpression(node, { callee, arguments: _arguments }) { + return this.append(callee, ..._arguments); + } + + reduceCatchClause(node, { binding, body }) { + return this.append(binding, body); + } + + reduceClassDeclaration(node, { name, super: _super, elements }) { + return this.append(name, _super == null ? () => this.identity : _super, ...elements); + } + + reduceClassElement(node, { method }) { + return method(); + } + + reduceClassExpression(node, { name, super: _super, elements }) { + return this.append(name == null ? () => this.identity : name, _super == null ? () => this.identity : _super, ...elements); + } + + reduceCompoundAssignmentExpression(node, { binding, expression }) { + return this.append(binding, expression); + } + + reduceComputedMemberAssignmentTarget(node, { object, expression }) { + return this.append(object, expression); + } + + reduceComputedMemberExpression(node, { object, expression }) { + return this.append(object, expression); + } + + reduceComputedPropertyName(node, { expression }) { + return expression(); + } + + reduceConditionalExpression(node, { test, consequent, alternate }) { + return this.append(test, consequent, alternate); + } + + reduceContinueStatement(node) { + return this.identity; + } + + reduceDataProperty(node, { name, expression }) { + return this.append(name, expression); + } + + reduceDebuggerStatement(node) { + return this.identity; + } + + reduceDirective(node) { + return this.identity; + } + + reduceDoWhileStatement(node, { body, test }) { + return this.append(body, test); + } + + reduceEmptyStatement(node) { + return this.identity; + } + + reduceExport(node, { declaration }) { + return declaration(); + } + + reduceExportAllFrom(node) { + return this.identity; + } + + reduceExportDefault(node, { body }) { + return body(); + } + + reduceExportFrom(node, { namedExports }) { + return this.append(...namedExports); + } + + reduceExportFromSpecifier(node) { + return this.identity; + } + + reduceExportLocalSpecifier(node, { name }) { + return name(); + } + + reduceExportLocals(node, { namedExports }) { + return this.append(...namedExports); + } + + reduceExpressionStatement(node, { expression }) { + return expression(); + } + + reduceForAwaitStatement(node, { left, right, body }) { + return this.append(left, right, body); + } + + reduceForInStatement(node, { left, right, body }) { + return this.append(left, right, body); + } + + reduceForOfStatement(node, { left, right, body }) { + return this.append(left, right, body); + } + + reduceForStatement(node, { init, test, update, body }) { + return this.append(init == null ? () => this.identity : init, test == null ? () => this.identity : test, update == null ? () => this.identity : update, body); + } + + reduceFormalParameters(node, { items, rest }) { + return this.append(...items, rest == null ? () => this.identity : rest); + } + + reduceFunctionBody(node, { directives, statements }) { + return this.append(...directives, ...statements); + } + + reduceFunctionDeclaration(node, { name, params, body }) { + return this.append(name, params, body); + } + + reduceFunctionExpression(node, { name, params, body }) { + return this.append(name == null ? () => this.identity : name, params, body); + } + + reduceGetter(node, { name, body }) { + return this.append(name, body); + } + + reduceIdentifierExpression(node) { + return this.identity; + } + + reduceIfStatement(node, { test, consequent, alternate }) { + return this.append(test, consequent, alternate == null ? () => this.identity : alternate); + } + + reduceImport(node, { defaultBinding, namedImports }) { + return this.append(defaultBinding == null ? () => this.identity : defaultBinding, ...namedImports); + } + + reduceImportNamespace(node, { defaultBinding, namespaceBinding }) { + return this.append(defaultBinding == null ? () => this.identity : defaultBinding, namespaceBinding); + } + + reduceImportSpecifier(node, { binding }) { + return binding(); + } + + reduceLabeledStatement(node, { body }) { + return body(); + } + + reduceLiteralBooleanExpression(node) { + return this.identity; + } + + reduceLiteralInfinityExpression(node) { + return this.identity; + } + + reduceLiteralNullExpression(node) { + return this.identity; + } + + reduceLiteralNumericExpression(node) { + return this.identity; + } + + reduceLiteralRegExpExpression(node) { + return this.identity; + } + + reduceLiteralStringExpression(node) { + return this.identity; + } + + reduceMethod(node, { name, params, body }) { + return this.append(name, params, body); + } + + reduceModule(node, { directives, items }) { + return this.append(...directives, ...items); + } + + reduceNewExpression(node, { callee, arguments: _arguments }) { + return this.append(callee, ..._arguments); + } + + reduceNewTargetExpression(node) { + return this.identity; + } + + reduceObjectAssignmentTarget(node, { properties, rest }) { + return this.append(...properties, rest == null ? () => this.identity : rest); + } + + reduceObjectBinding(node, { properties, rest }) { + return this.append(...properties, rest == null ? () => this.identity : rest); + } + + reduceObjectExpression(node, { properties }) { + return this.append(...properties); + } + + reduceReturnStatement(node, { expression }) { + return expression == null ? this.identity : expression(); + } + + reduceScript(node, { directives, statements }) { + return this.append(...directives, ...statements); + } + + reduceSetter(node, { name, param, body }) { + return this.append(name, param, body); + } + + reduceShorthandProperty(node, { name }) { + return name(); + } + + reduceSpreadElement(node, { expression }) { + return expression(); + } + + reduceSpreadProperty(node, { expression }) { + return expression(); + } + + reduceStaticMemberAssignmentTarget(node, { object }) { + return object(); + } + + reduceStaticMemberExpression(node, { object }) { + return object(); + } + + reduceStaticPropertyName(node) { + return this.identity; + } + + reduceSuper(node) { + return this.identity; + } + + reduceSwitchCase(node, { test, consequent }) { + return this.append(test, ...consequent); + } + + reduceSwitchDefault(node, { consequent }) { + return this.append(...consequent); + } + + reduceSwitchStatement(node, { discriminant, cases }) { + return this.append(discriminant, ...cases); + } + + reduceSwitchStatementWithDefault(node, { discriminant, preDefaultCases, defaultCase, postDefaultCases }) { + return this.append(discriminant, ...preDefaultCases, defaultCase, ...postDefaultCases); + } + + reduceTemplateElement(node) { + return this.identity; + } + + reduceTemplateExpression(node, { tag, elements }) { + return this.append(tag == null ? () => this.identity : tag, ...elements); + } + + reduceThisExpression(node) { + return this.identity; + } + + reduceThrowStatement(node, { expression }) { + return expression(); + } + + reduceTryCatchStatement(node, { body, catchClause }) { + return this.append(body, catchClause); + } + + reduceTryFinallyStatement(node, { body, catchClause, finalizer }) { + return this.append(body, catchClause == null ? () => this.identity : catchClause, finalizer); + } + + reduceUnaryExpression(node, { operand }) { + return operand(); + } + + reduceUpdateExpression(node, { operand }) { + return operand(); + } + + reduceVariableDeclaration(node, { declarators }) { + return this.append(...declarators); + } + + reduceVariableDeclarationStatement(node, { declaration }) { + return declaration(); + } + + reduceVariableDeclarator(node, { binding, init }) { + return this.append(binding, init == null ? () => this.identity : init); + } + + reduceWhileStatement(node, { test, body }) { + return this.append(test, body); + } + + reduceWithStatement(node, { object, body }) { + return this.append(object, body); + } + + reduceYieldExpression(node, { expression }) { + return expression == null ? this.identity : expression(); + } + + reduceYieldGeneratorExpression(node, { expression }) { + return expression(); + } +} diff --git a/repl/src/shift-reducer/thunkify-class.js b/repl/src/shift-reducer/thunkify-class.js new file mode 100644 index 00000000..4d091f21 --- /dev/null +++ b/repl/src/shift-reducer/thunkify-class.js @@ -0,0 +1,416 @@ +// Generated by generate-thunkify.js +/** + * Copyright 2018 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default function thunkifyClass(reducerClass) { + return class extends reducerClass { + reduceArrayAssignmentTarget(node, { elements, rest }) { + return super.reduceArrayAssignmentTarget(node, { elements: elements.map(n => n == null ? null : n()), rest: rest == null ? null : rest() }); + } + + reduceArrayBinding(node, { elements, rest }) { + return super.reduceArrayBinding(node, { elements: elements.map(n => n == null ? null : n()), rest: rest == null ? null : rest() }); + } + + reduceArrayExpression(node, { elements }) { + return super.reduceArrayExpression(node, { elements: elements.map(n => n == null ? null : n()) }); + } + + reduceArrowExpression(node, { params, body }) { + return super.reduceArrowExpression(node, { params: params(), body: body() }); + } + + reduceAssignmentExpression(node, { binding, expression }) { + return super.reduceAssignmentExpression(node, { binding: binding(), expression: expression() }); + } + + reduceAssignmentTargetIdentifier(node) { + return super.reduceAssignmentTargetIdentifier(node); + } + + reduceAssignmentTargetPropertyIdentifier(node, { binding, init }) { + return super.reduceAssignmentTargetPropertyIdentifier(node, { binding: binding(), init: init == null ? null : init() }); + } + + reduceAssignmentTargetPropertyProperty(node, { name, binding }) { + return super.reduceAssignmentTargetPropertyProperty(node, { name: name(), binding: binding() }); + } + + reduceAssignmentTargetWithDefault(node, { binding, init }) { + return super.reduceAssignmentTargetWithDefault(node, { binding: binding(), init: init() }); + } + + reduceAwaitExpression(node, { expression }) { + return super.reduceAwaitExpression(node, { expression: expression() }); + } + + reduceBinaryExpression(node, { left, right }) { + return super.reduceBinaryExpression(node, { left: left(), right: right() }); + } + + reduceBindingIdentifier(node) { + return super.reduceBindingIdentifier(node); + } + + reduceBindingPropertyIdentifier(node, { binding, init }) { + return super.reduceBindingPropertyIdentifier(node, { binding: binding(), init: init == null ? null : init() }); + } + + reduceBindingPropertyProperty(node, { name, binding }) { + return super.reduceBindingPropertyProperty(node, { name: name(), binding: binding() }); + } + + reduceBindingWithDefault(node, { binding, init }) { + return super.reduceBindingWithDefault(node, { binding: binding(), init: init() }); + } + + reduceBlock(node, { statements }) { + return super.reduceBlock(node, { statements: statements.map(n => n()) }); + } + + reduceBlockStatement(node, { block }) { + return super.reduceBlockStatement(node, { block: block() }); + } + + reduceBreakStatement(node) { + return super.reduceBreakStatement(node); + } + + reduceCallExpression(node, { callee, arguments: _arguments }) { + return super.reduceCallExpression(node, { callee: callee(), arguments: _arguments.map(n => n()) }); + } + + reduceCatchClause(node, { binding, body }) { + return super.reduceCatchClause(node, { binding: binding(), body: body() }); + } + + reduceClassDeclaration(node, { name, super: _super, elements }) { + return super.reduceClassDeclaration(node, { name: name(), super: _super == null ? null : _super(), elements: elements.map(n => n()) }); + } + + reduceClassElement(node, { method }) { + return super.reduceClassElement(node, { method: method() }); + } + + reduceClassExpression(node, { name, super: _super, elements }) { + return super.reduceClassExpression(node, { name: name == null ? null : name(), super: _super == null ? null : _super(), elements: elements.map(n => n()) }); + } + + reduceCompoundAssignmentExpression(node, { binding, expression }) { + return super.reduceCompoundAssignmentExpression(node, { binding: binding(), expression: expression() }); + } + + reduceComputedMemberAssignmentTarget(node, { object, expression }) { + return super.reduceComputedMemberAssignmentTarget(node, { object: object(), expression: expression() }); + } + + reduceComputedMemberExpression(node, { object, expression }) { + return super.reduceComputedMemberExpression(node, { object: object(), expression: expression() }); + } + + reduceComputedPropertyName(node, { expression }) { + return super.reduceComputedPropertyName(node, { expression: expression() }); + } + + reduceConditionalExpression(node, { test, consequent, alternate }) { + return super.reduceConditionalExpression(node, { test: test(), consequent: consequent(), alternate: alternate() }); + } + + reduceContinueStatement(node) { + return super.reduceContinueStatement(node); + } + + reduceDataProperty(node, { name, expression }) { + return super.reduceDataProperty(node, { name: name(), expression: expression() }); + } + + reduceDebuggerStatement(node) { + return super.reduceDebuggerStatement(node); + } + + reduceDirective(node) { + return super.reduceDirective(node); + } + + reduceDoWhileStatement(node, { body, test }) { + return super.reduceDoWhileStatement(node, { body: body(), test: test() }); + } + + reduceEmptyStatement(node) { + return super.reduceEmptyStatement(node); + } + + reduceExport(node, { declaration }) { + return super.reduceExport(node, { declaration: declaration() }); + } + + reduceExportAllFrom(node) { + return super.reduceExportAllFrom(node); + } + + reduceExportDefault(node, { body }) { + return super.reduceExportDefault(node, { body: body() }); + } + + reduceExportFrom(node, { namedExports }) { + return super.reduceExportFrom(node, { namedExports: namedExports.map(n => n()) }); + } + + reduceExportFromSpecifier(node) { + return super.reduceExportFromSpecifier(node); + } + + reduceExportLocalSpecifier(node, { name }) { + return super.reduceExportLocalSpecifier(node, { name: name() }); + } + + reduceExportLocals(node, { namedExports }) { + return super.reduceExportLocals(node, { namedExports: namedExports.map(n => n()) }); + } + + reduceExpressionStatement(node, { expression }) { + return super.reduceExpressionStatement(node, { expression: expression() }); + } + + reduceForAwaitStatement(node, { left, right, body }) { + return super.reduceForAwaitStatement(node, { left: left(), right: right(), body: body() }); + } + + reduceForInStatement(node, { left, right, body }) { + return super.reduceForInStatement(node, { left: left(), right: right(), body: body() }); + } + + reduceForOfStatement(node, { left, right, body }) { + return super.reduceForOfStatement(node, { left: left(), right: right(), body: body() }); + } + + reduceForStatement(node, { init, test, update, body }) { + return super.reduceForStatement(node, { init: init == null ? null : init(), test: test == null ? null : test(), update: update == null ? null : update(), body: body() }); + } + + reduceFormalParameters(node, { items, rest }) { + return super.reduceFormalParameters(node, { items: items.map(n => n()), rest: rest == null ? null : rest() }); + } + + reduceFunctionBody(node, { directives, statements }) { + return super.reduceFunctionBody(node, { directives: directives.map(n => n()), statements: statements.map(n => n()) }); + } + + reduceFunctionDeclaration(node, { name, params, body }) { + return super.reduceFunctionDeclaration(node, { name: name(), params: params(), body: body() }); + } + + reduceFunctionExpression(node, { name, params, body }) { + return super.reduceFunctionExpression(node, { name: name == null ? null : name(), params: params(), body: body() }); + } + + reduceGetter(node, { name, body }) { + return super.reduceGetter(node, { name: name(), body: body() }); + } + + reduceIdentifierExpression(node) { + return super.reduceIdentifierExpression(node); + } + + reduceIfStatement(node, { test, consequent, alternate }) { + return super.reduceIfStatement(node, { test: test(), consequent: consequent(), alternate: alternate == null ? null : alternate() }); + } + + reduceImport(node, { defaultBinding, namedImports }) { + return super.reduceImport(node, { defaultBinding: defaultBinding == null ? null : defaultBinding(), namedImports: namedImports.map(n => n()) }); + } + + reduceImportNamespace(node, { defaultBinding, namespaceBinding }) { + return super.reduceImportNamespace(node, { defaultBinding: defaultBinding == null ? null : defaultBinding(), namespaceBinding: namespaceBinding() }); + } + + reduceImportSpecifier(node, { binding }) { + return super.reduceImportSpecifier(node, { binding: binding() }); + } + + reduceLabeledStatement(node, { body }) { + return super.reduceLabeledStatement(node, { body: body() }); + } + + reduceLiteralBooleanExpression(node) { + return super.reduceLiteralBooleanExpression(node); + } + + reduceLiteralInfinityExpression(node) { + return super.reduceLiteralInfinityExpression(node); + } + + reduceLiteralNullExpression(node) { + return super.reduceLiteralNullExpression(node); + } + + reduceLiteralNumericExpression(node) { + return super.reduceLiteralNumericExpression(node); + } + + reduceLiteralRegExpExpression(node) { + return super.reduceLiteralRegExpExpression(node); + } + + reduceLiteralStringExpression(node) { + return super.reduceLiteralStringExpression(node); + } + + reduceMethod(node, { name, params, body }) { + return super.reduceMethod(node, { name: name(), params: params(), body: body() }); + } + + reduceModule(node, { directives, items }) { + return super.reduceModule(node, { directives: directives.map(n => n()), items: items.map(n => n()) }); + } + + reduceNewExpression(node, { callee, arguments: _arguments }) { + return super.reduceNewExpression(node, { callee: callee(), arguments: _arguments.map(n => n()) }); + } + + reduceNewTargetExpression(node) { + return super.reduceNewTargetExpression(node); + } + + reduceObjectAssignmentTarget(node, { properties, rest }) { + return super.reduceObjectAssignmentTarget(node, { properties: properties.map(n => n()), rest: rest == null ? null : rest() }); + } + + reduceObjectBinding(node, { properties, rest }) { + return super.reduceObjectBinding(node, { properties: properties.map(n => n()), rest: rest == null ? null : rest() }); + } + + reduceObjectExpression(node, { properties }) { + return super.reduceObjectExpression(node, { properties: properties.map(n => n()) }); + } + + reduceReturnStatement(node, { expression }) { + return super.reduceReturnStatement(node, { expression: expression == null ? null : expression() }); + } + + reduceScript(node, { directives, statements }) { + return super.reduceScript(node, { directives: directives.map(n => n()), statements: statements.map(n => n()) }); + } + + reduceSetter(node, { name, param, body }) { + return super.reduceSetter(node, { name: name(), param: param(), body: body() }); + } + + reduceShorthandProperty(node, { name }) { + return super.reduceShorthandProperty(node, { name: name() }); + } + + reduceSpreadElement(node, { expression }) { + return super.reduceSpreadElement(node, { expression: expression() }); + } + + reduceSpreadProperty(node, { expression }) { + return super.reduceSpreadProperty(node, { expression: expression() }); + } + + reduceStaticMemberAssignmentTarget(node, { object }) { + return super.reduceStaticMemberAssignmentTarget(node, { object: object() }); + } + + reduceStaticMemberExpression(node, { object }) { + return super.reduceStaticMemberExpression(node, { object: object() }); + } + + reduceStaticPropertyName(node) { + return super.reduceStaticPropertyName(node); + } + + reduceSuper(node) { + return super.reduceSuper(node); + } + + reduceSwitchCase(node, { test, consequent }) { + return super.reduceSwitchCase(node, { test: test(), consequent: consequent.map(n => n()) }); + } + + reduceSwitchDefault(node, { consequent }) { + return super.reduceSwitchDefault(node, { consequent: consequent.map(n => n()) }); + } + + reduceSwitchStatement(node, { discriminant, cases }) { + return super.reduceSwitchStatement(node, { discriminant: discriminant(), cases: cases.map(n => n()) }); + } + + reduceSwitchStatementWithDefault(node, { discriminant, preDefaultCases, defaultCase, postDefaultCases }) { + return super.reduceSwitchStatementWithDefault(node, { discriminant: discriminant(), preDefaultCases: preDefaultCases.map(n => n()), defaultCase: defaultCase(), postDefaultCases: postDefaultCases.map(n => n()) }); + } + + reduceTemplateElement(node) { + return super.reduceTemplateElement(node); + } + + reduceTemplateExpression(node, { tag, elements }) { + return super.reduceTemplateExpression(node, { tag: tag == null ? null : tag(), elements: elements.map(n => n()) }); + } + + reduceThisExpression(node) { + return super.reduceThisExpression(node); + } + + reduceThrowStatement(node, { expression }) { + return super.reduceThrowStatement(node, { expression: expression() }); + } + + reduceTryCatchStatement(node, { body, catchClause }) { + return super.reduceTryCatchStatement(node, { body: body(), catchClause: catchClause() }); + } + + reduceTryFinallyStatement(node, { body, catchClause, finalizer }) { + return super.reduceTryFinallyStatement(node, { body: body(), catchClause: catchClause == null ? null : catchClause(), finalizer: finalizer() }); + } + + reduceUnaryExpression(node, { operand }) { + return super.reduceUnaryExpression(node, { operand: operand() }); + } + + reduceUpdateExpression(node, { operand }) { + return super.reduceUpdateExpression(node, { operand: operand() }); + } + + reduceVariableDeclaration(node, { declarators }) { + return super.reduceVariableDeclaration(node, { declarators: declarators.map(n => n()) }); + } + + reduceVariableDeclarationStatement(node, { declaration }) { + return super.reduceVariableDeclarationStatement(node, { declaration: declaration() }); + } + + reduceVariableDeclarator(node, { binding, init }) { + return super.reduceVariableDeclarator(node, { binding: binding(), init: init == null ? null : init() }); + } + + reduceWhileStatement(node, { test, body }) { + return super.reduceWhileStatement(node, { test: test(), body: body() }); + } + + reduceWithStatement(node, { object, body }) { + return super.reduceWithStatement(node, { object: object(), body: body() }); + } + + reduceYieldExpression(node, { expression }) { + return super.reduceYieldExpression(node, { expression: expression == null ? null : expression() }); + } + + reduceYieldGeneratorExpression(node, { expression }) { + return super.reduceYieldGeneratorExpression(node, { expression: expression() }); + } + }; +} diff --git a/repl/src/shift-reducer/thunkify.js b/repl/src/shift-reducer/thunkify.js new file mode 100644 index 00000000..80620d64 --- /dev/null +++ b/repl/src/shift-reducer/thunkify.js @@ -0,0 +1,416 @@ +// Generated by generate-thunkify.js +/** + * Copyright 2018 Shape Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default function thunkify(reducer) { + return { + reduceArrayAssignmentTarget(node, { elements, rest }) { + return reducer.reduceArrayAssignmentTarget(node, { elements: elements.map(n => n == null ? null : n()), rest: rest == null ? null : rest() }); + }, + + reduceArrayBinding(node, { elements, rest }) { + return reducer.reduceArrayBinding(node, { elements: elements.map(n => n == null ? null : n()), rest: rest == null ? null : rest() }); + }, + + reduceArrayExpression(node, { elements }) { + return reducer.reduceArrayExpression(node, { elements: elements.map(n => n == null ? null : n()) }); + }, + + reduceArrowExpression(node, { params, body }) { + return reducer.reduceArrowExpression(node, { params: params(), body: body() }); + }, + + reduceAssignmentExpression(node, { binding, expression }) { + return reducer.reduceAssignmentExpression(node, { binding: binding(), expression: expression() }); + }, + + reduceAssignmentTargetIdentifier(node) { + return reducer.reduceAssignmentTargetIdentifier(node); + }, + + reduceAssignmentTargetPropertyIdentifier(node, { binding, init }) { + return reducer.reduceAssignmentTargetPropertyIdentifier(node, { binding: binding(), init: init == null ? null : init() }); + }, + + reduceAssignmentTargetPropertyProperty(node, { name, binding }) { + return reducer.reduceAssignmentTargetPropertyProperty(node, { name: name(), binding: binding() }); + }, + + reduceAssignmentTargetWithDefault(node, { binding, init }) { + return reducer.reduceAssignmentTargetWithDefault(node, { binding: binding(), init: init() }); + }, + + reduceAwaitExpression(node, { expression }) { + return reducer.reduceAwaitExpression(node, { expression: expression() }); + }, + + reduceBinaryExpression(node, { left, right }) { + return reducer.reduceBinaryExpression(node, { left: left(), right: right() }); + }, + + reduceBindingIdentifier(node) { + return reducer.reduceBindingIdentifier(node); + }, + + reduceBindingPropertyIdentifier(node, { binding, init }) { + return reducer.reduceBindingPropertyIdentifier(node, { binding: binding(), init: init == null ? null : init() }); + }, + + reduceBindingPropertyProperty(node, { name, binding }) { + return reducer.reduceBindingPropertyProperty(node, { name: name(), binding: binding() }); + }, + + reduceBindingWithDefault(node, { binding, init }) { + return reducer.reduceBindingWithDefault(node, { binding: binding(), init: init() }); + }, + + reduceBlock(node, { statements }) { + return reducer.reduceBlock(node, { statements: statements.map(n => n()) }); + }, + + reduceBlockStatement(node, { block }) { + return reducer.reduceBlockStatement(node, { block: block() }); + }, + + reduceBreakStatement(node) { + return reducer.reduceBreakStatement(node); + }, + + reduceCallExpression(node, { callee, arguments: _arguments }) { + return reducer.reduceCallExpression(node, { callee: callee(), arguments: _arguments.map(n => n()) }); + }, + + reduceCatchClause(node, { binding, body }) { + return reducer.reduceCatchClause(node, { binding: binding(), body: body() }); + }, + + reduceClassDeclaration(node, { name, super: _super, elements }) { + return reducer.reduceClassDeclaration(node, { name: name(), super: _super == null ? null : _super(), elements: elements.map(n => n()) }); + }, + + reduceClassElement(node, { method }) { + return reducer.reduceClassElement(node, { method: method() }); + }, + + reduceClassExpression(node, { name, super: _super, elements }) { + return reducer.reduceClassExpression(node, { name: name == null ? null : name(), super: _super == null ? null : _super(), elements: elements.map(n => n()) }); + }, + + reduceCompoundAssignmentExpression(node, { binding, expression }) { + return reducer.reduceCompoundAssignmentExpression(node, { binding: binding(), expression: expression() }); + }, + + reduceComputedMemberAssignmentTarget(node, { object, expression }) { + return reducer.reduceComputedMemberAssignmentTarget(node, { object: object(), expression: expression() }); + }, + + reduceComputedMemberExpression(node, { object, expression }) { + return reducer.reduceComputedMemberExpression(node, { object: object(), expression: expression() }); + }, + + reduceComputedPropertyName(node, { expression }) { + return reducer.reduceComputedPropertyName(node, { expression: expression() }); + }, + + reduceConditionalExpression(node, { test, consequent, alternate }) { + return reducer.reduceConditionalExpression(node, { test: test(), consequent: consequent(), alternate: alternate() }); + }, + + reduceContinueStatement(node) { + return reducer.reduceContinueStatement(node); + }, + + reduceDataProperty(node, { name, expression }) { + return reducer.reduceDataProperty(node, { name: name(), expression: expression() }); + }, + + reduceDebuggerStatement(node) { + return reducer.reduceDebuggerStatement(node); + }, + + reduceDirective(node) { + return reducer.reduceDirective(node); + }, + + reduceDoWhileStatement(node, { body, test }) { + return reducer.reduceDoWhileStatement(node, { body: body(), test: test() }); + }, + + reduceEmptyStatement(node) { + return reducer.reduceEmptyStatement(node); + }, + + reduceExport(node, { declaration }) { + return reducer.reduceExport(node, { declaration: declaration() }); + }, + + reduceExportAllFrom(node) { + return reducer.reduceExportAllFrom(node); + }, + + reduceExportDefault(node, { body }) { + return reducer.reduceExportDefault(node, { body: body() }); + }, + + reduceExportFrom(node, { namedExports }) { + return reducer.reduceExportFrom(node, { namedExports: namedExports.map(n => n()) }); + }, + + reduceExportFromSpecifier(node) { + return reducer.reduceExportFromSpecifier(node); + }, + + reduceExportLocalSpecifier(node, { name }) { + return reducer.reduceExportLocalSpecifier(node, { name: name() }); + }, + + reduceExportLocals(node, { namedExports }) { + return reducer.reduceExportLocals(node, { namedExports: namedExports.map(n => n()) }); + }, + + reduceExpressionStatement(node, { expression }) { + return reducer.reduceExpressionStatement(node, { expression: expression() }); + }, + + reduceForAwaitStatement(node, { left, right, body }) { + return reducer.reduceForAwaitStatement(node, { left: left(), right: right(), body: body() }); + }, + + reduceForInStatement(node, { left, right, body }) { + return reducer.reduceForInStatement(node, { left: left(), right: right(), body: body() }); + }, + + reduceForOfStatement(node, { left, right, body }) { + return reducer.reduceForOfStatement(node, { left: left(), right: right(), body: body() }); + }, + + reduceForStatement(node, { init, test, update, body }) { + return reducer.reduceForStatement(node, { init: init == null ? null : init(), test: test == null ? null : test(), update: update == null ? null : update(), body: body() }); + }, + + reduceFormalParameters(node, { items, rest }) { + return reducer.reduceFormalParameters(node, { items: items.map(n => n()), rest: rest == null ? null : rest() }); + }, + + reduceFunctionBody(node, { directives, statements }) { + return reducer.reduceFunctionBody(node, { directives: directives.map(n => n()), statements: statements.map(n => n()) }); + }, + + reduceFunctionDeclaration(node, { name, params, body }) { + return reducer.reduceFunctionDeclaration(node, { name: name(), params: params(), body: body() }); + }, + + reduceFunctionExpression(node, { name, params, body }) { + return reducer.reduceFunctionExpression(node, { name: name == null ? null : name(), params: params(), body: body() }); + }, + + reduceGetter(node, { name, body }) { + return reducer.reduceGetter(node, { name: name(), body: body() }); + }, + + reduceIdentifierExpression(node) { + return reducer.reduceIdentifierExpression(node); + }, + + reduceIfStatement(node, { test, consequent, alternate }) { + return reducer.reduceIfStatement(node, { test: test(), consequent: consequent(), alternate: alternate == null ? null : alternate() }); + }, + + reduceImport(node, { defaultBinding, namedImports }) { + return reducer.reduceImport(node, { defaultBinding: defaultBinding == null ? null : defaultBinding(), namedImports: namedImports.map(n => n()) }); + }, + + reduceImportNamespace(node, { defaultBinding, namespaceBinding }) { + return reducer.reduceImportNamespace(node, { defaultBinding: defaultBinding == null ? null : defaultBinding(), namespaceBinding: namespaceBinding() }); + }, + + reduceImportSpecifier(node, { binding }) { + return reducer.reduceImportSpecifier(node, { binding: binding() }); + }, + + reduceLabeledStatement(node, { body }) { + return reducer.reduceLabeledStatement(node, { body: body() }); + }, + + reduceLiteralBooleanExpression(node) { + return reducer.reduceLiteralBooleanExpression(node); + }, + + reduceLiteralInfinityExpression(node) { + return reducer.reduceLiteralInfinityExpression(node); + }, + + reduceLiteralNullExpression(node) { + return reducer.reduceLiteralNullExpression(node); + }, + + reduceLiteralNumericExpression(node) { + return reducer.reduceLiteralNumericExpression(node); + }, + + reduceLiteralRegExpExpression(node) { + return reducer.reduceLiteralRegExpExpression(node); + }, + + reduceLiteralStringExpression(node) { + return reducer.reduceLiteralStringExpression(node); + }, + + reduceMethod(node, { name, params, body }) { + return reducer.reduceMethod(node, { name: name(), params: params(), body: body() }); + }, + + reduceModule(node, { directives, items }) { + return reducer.reduceModule(node, { directives: directives.map(n => n()), items: items.map(n => n()) }); + }, + + reduceNewExpression(node, { callee, arguments: _arguments }) { + return reducer.reduceNewExpression(node, { callee: callee(), arguments: _arguments.map(n => n()) }); + }, + + reduceNewTargetExpression(node) { + return reducer.reduceNewTargetExpression(node); + }, + + reduceObjectAssignmentTarget(node, { properties, rest }) { + return reducer.reduceObjectAssignmentTarget(node, { properties: properties.map(n => n()), rest: rest == null ? null : rest() }); + }, + + reduceObjectBinding(node, { properties, rest }) { + return reducer.reduceObjectBinding(node, { properties: properties.map(n => n()), rest: rest == null ? null : rest() }); + }, + + reduceObjectExpression(node, { properties }) { + return reducer.reduceObjectExpression(node, { properties: properties.map(n => n()) }); + }, + + reduceReturnStatement(node, { expression }) { + return reducer.reduceReturnStatement(node, { expression: expression == null ? null : expression() }); + }, + + reduceScript(node, { directives, statements }) { + return reducer.reduceScript(node, { directives: directives.map(n => n()), statements: statements.map(n => n()) }); + }, + + reduceSetter(node, { name, param, body }) { + return reducer.reduceSetter(node, { name: name(), param: param(), body: body() }); + }, + + reduceShorthandProperty(node, { name }) { + return reducer.reduceShorthandProperty(node, { name: name() }); + }, + + reduceSpreadElement(node, { expression }) { + return reducer.reduceSpreadElement(node, { expression: expression() }); + }, + + reduceSpreadProperty(node, { expression }) { + return reducer.reduceSpreadProperty(node, { expression: expression() }); + }, + + reduceStaticMemberAssignmentTarget(node, { object }) { + return reducer.reduceStaticMemberAssignmentTarget(node, { object: object() }); + }, + + reduceStaticMemberExpression(node, { object }) { + return reducer.reduceStaticMemberExpression(node, { object: object() }); + }, + + reduceStaticPropertyName(node) { + return reducer.reduceStaticPropertyName(node); + }, + + reduceSuper(node) { + return reducer.reduceSuper(node); + }, + + reduceSwitchCase(node, { test, consequent }) { + return reducer.reduceSwitchCase(node, { test: test(), consequent: consequent.map(n => n()) }); + }, + + reduceSwitchDefault(node, { consequent }) { + return reducer.reduceSwitchDefault(node, { consequent: consequent.map(n => n()) }); + }, + + reduceSwitchStatement(node, { discriminant, cases }) { + return reducer.reduceSwitchStatement(node, { discriminant: discriminant(), cases: cases.map(n => n()) }); + }, + + reduceSwitchStatementWithDefault(node, { discriminant, preDefaultCases, defaultCase, postDefaultCases }) { + return reducer.reduceSwitchStatementWithDefault(node, { discriminant: discriminant(), preDefaultCases: preDefaultCases.map(n => n()), defaultCase: defaultCase(), postDefaultCases: postDefaultCases.map(n => n()) }); + }, + + reduceTemplateElement(node) { + return reducer.reduceTemplateElement(node); + }, + + reduceTemplateExpression(node, { tag, elements }) { + return reducer.reduceTemplateExpression(node, { tag: tag == null ? null : tag(), elements: elements.map(n => n()) }); + }, + + reduceThisExpression(node) { + return reducer.reduceThisExpression(node); + }, + + reduceThrowStatement(node, { expression }) { + return reducer.reduceThrowStatement(node, { expression: expression() }); + }, + + reduceTryCatchStatement(node, { body, catchClause }) { + return reducer.reduceTryCatchStatement(node, { body: body(), catchClause: catchClause() }); + }, + + reduceTryFinallyStatement(node, { body, catchClause, finalizer }) { + return reducer.reduceTryFinallyStatement(node, { body: body(), catchClause: catchClause == null ? null : catchClause(), finalizer: finalizer() }); + }, + + reduceUnaryExpression(node, { operand }) { + return reducer.reduceUnaryExpression(node, { operand: operand() }); + }, + + reduceUpdateExpression(node, { operand }) { + return reducer.reduceUpdateExpression(node, { operand: operand() }); + }, + + reduceVariableDeclaration(node, { declarators }) { + return reducer.reduceVariableDeclaration(node, { declarators: declarators.map(n => n()) }); + }, + + reduceVariableDeclarationStatement(node, { declaration }) { + return reducer.reduceVariableDeclarationStatement(node, { declaration: declaration() }); + }, + + reduceVariableDeclarator(node, { binding, init }) { + return reducer.reduceVariableDeclarator(node, { binding: binding(), init: init == null ? null : init() }); + }, + + reduceWhileStatement(node, { test, body }) { + return reducer.reduceWhileStatement(node, { test: test(), body: body() }); + }, + + reduceWithStatement(node, { object, body }) { + return reducer.reduceWithStatement(node, { object: object(), body: body() }); + }, + + reduceYieldExpression(node, { expression }) { + return reducer.reduceYieldExpression(node, { expression: expression == null ? null : expression() }); + }, + + reduceYieldGeneratorExpression(node, { expression }) { + return reducer.reduceYieldGeneratorExpression(node, { expression: expression() }); + }, + }; +} diff --git a/repl/src/shift-traverser/index.js b/repl/src/shift-traverser/index.js new file mode 100644 index 00000000..858bc4d5 --- /dev/null +++ b/repl/src/shift-traverser/index.js @@ -0,0 +1,61 @@ +/* + Copyright (C) 2014 Yusuke Suzuki + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import Spec from 'shift-spec'; +// import { version } from '../package.json' + +// Loading uncached estraverse for changing estraverse.Syntax. +import _estraverse from 'estraverse'; + +const estraverse = _estraverse.cloneEnvironment(); + +// Adjust estraverse members. + +Object.keys(estraverse.Syntax) + .filter((key) => key !== 'Property') + .forEach((key) => { + delete estraverse.Syntax[key]; + delete estraverse.VisitorKeys[key]; + }); + +Object.assign( + estraverse.Syntax, + Object.keys(Spec).reduce((result, key) => { + result[key] = key; + return result; + }, {}) +); + +Object.assign( + estraverse.VisitorKeys, + Object.keys(Spec).reduce((result, key) => { + result[key] = Spec[key].fields.map((field) => field.name); + return result; + }, {}) +); + +// estraverse.version = version; +export default estraverse; + +/* vim: set sw=4 ts=4 et tw=80 : */ diff --git a/repl/src/tone.ts b/repl/src/tone.ts index b0f78699..8e14d4ef 100644 --- a/repl/src/tone.ts +++ b/repl/src/tone.ts @@ -1,14 +1,18 @@ import { Pattern as _Pattern } from '../../strudel.mjs'; -import { AutoFilter, Destination, Filter, Gain, Transport, Synth } from 'tone'; +import { AutoFilter, Destination, Filter, Gain, isNote, Synth } from 'tone'; const Pattern = _Pattern as any; const getTrigger = (getChain: any, value: any) => (time: number, event: any) => { - const chain = getChain(time); // make sure this returns a node that is connected toDestination + const chain = getChain(); // make sure this returns a node that is connected toDestination // time + if (!isNote(value)) { + throw new Error('not a note: ' + value); + } chain.triggerAttackRelease(value, event.duration, time); - Transport.scheduleOnce(() => { + setTimeout(() => { + // setTimeout is a little bit better compared to Transport.scheduleOnce chain.dispose(); // mark for garbage collection - }, '+' + event.duration * 2); + }, event.duration * 2000); }; Pattern.prototype._synth = function (type: any = 'triangle') { @@ -54,8 +58,8 @@ Pattern.prototype.chain = function (...effectGetters: any) { throw new Error('cannot chain: need instrument first (like synth)'); } const chain = (value.chain || []).concat(effectGetters); - const getChain = (time: number) => { - const effects = chain.map((getEffect: any) => getEffect(time)); + const getChain = () => { + const effects = chain.map((getEffect: any) => getEffect()); return value.getInstrument().chain(...effects, Destination); }; const onTrigger = getTrigger(getChain, value.value); diff --git a/repl/src/tunes.ts b/repl/src/tunes.ts index 4f299fb9..0b98b351 100644 --- a/repl/src/tunes.ts +++ b/repl/src/tunes.ts @@ -1,3 +1,26 @@ +export const shapeShifted = `stack( + sequence( + e5, [b4, c5], d5, [c5, b4], + a4, [a4, c5], e5, [d5, c5], + b4, [r, c5], d5, e5, + c5, a4, a4, r, + [r, d5], [r, f5], a5, [g5, f5], + e5, [r, c5], e5, [d5, c5], + b4, [b4, c5], d5, e5, + c5, a4, a4, r, + ).rev(), + sequence( + e2, e3, e2, e3, e2, e3, e2, e3, + a2, a3, a2, a3, a2, a3, a2, a3, + gs2, gs3, gs2, gs3, e2, e3, e2, e3, + a2, a3, a2, a3, a2, a3, b1, c2, + d2, d3, d2, d3, d2, d3, d2, d3, + c2, c3, c2, c3, c2, c3, c2, c3, + b1, b2, b1, b2, e2, e3, e2, e3, + a1, a2, a1, a2, a1, a2, a1, a2, + ).rev() +).slow(16).rev()`; + export const tetrisWithFunctions = `stack(sequence( 'e5', sequence('b4', 'c5'), 'd5', sequence('c5', 'b4'), 'a4', sequence('a4', 'c5'), 'e5', sequence('d5', 'c5'),