diff --git a/package-lock.json b/package-lock.json index 8d396eab..d1a8c11e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2133,6 +2133,10 @@ "resolved": "packages/tone", "link": true }, + "node_modules/@strudel.cycles/transpiler": { + "resolved": "packages/transpiler", + "link": true + }, "node_modules/@strudel.cycles/webaudio": { "resolved": "packages/webaudio", "link": true @@ -4207,10 +4211,7 @@ "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "optional": true, - "peer": true + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, "node_modules/defaults": { "version": "1.0.4", @@ -4948,9 +4949,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "optional": true, - "peer": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -4972,9 +4970,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "optional": true, - "peer": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -4991,6 +4986,11 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.1.tgz", + "integrity": "sha512-woY0RUD87WzMBUiZLx8NsYr23N5BKsOMZHhu2hoNRVh6NXGfoiT1KOL8G3UHlJAnEDGmfa5ubNA/AacfG+Kb0g==" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -5109,10 +5109,7 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "optional": true, - "peer": true + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "node_modules/fastq": { "version": "1.13.0", @@ -7221,9 +7218,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "optional": true, - "peer": true, "dependencies": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" @@ -8782,9 +8776,6 @@ "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "optional": true, - "peer": true, "dependencies": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.6", @@ -9525,9 +9516,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">= 0.8.0" } @@ -10764,7 +10752,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -11576,9 +11564,6 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "optional": true, - "peer": true, "dependencies": { "prelude-ls": "~1.1.2" }, @@ -12151,9 +12136,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">=0.10.0" } @@ -12486,7 +12468,7 @@ }, "packages/eval": { "name": "@strudel.cycles/eval", - "version": "0.3.0", + "version": "0.3.1", "license": "AGPL-3.0-or-later", "dependencies": { "@strudel.cycles/core": "^0.3.1", @@ -12513,10 +12495,10 @@ }, "packages/midi": { "name": "@strudel.cycles/midi", - "version": "0.3.0", + "version": "0.3.1", "license": "AGPL-3.0-or-later", "dependencies": { - "@strudel.cycles/tone": "^0.3.0", + "@strudel.cycles/tone": "^0.3.1", "tone": "^14.7.77", "webmidi": "^3.0.21" } @@ -12537,12 +12519,12 @@ }, "packages/mini": { "name": "@strudel.cycles/mini", - "version": "0.3.0", + "version": "0.3.1", "license": "AGPL-3.0-or-later", "dependencies": { "@strudel.cycles/core": "^0.3.1", - "@strudel.cycles/eval": "^0.3.0", - "@strudel.cycles/tone": "^0.3.0" + "@strudel.cycles/eval": "^0.3.1", + "@strudel.cycles/tone": "^0.3.1" }, "devDependencies": { "peggy": "^2.0.1" @@ -12558,13 +12540,13 @@ }, "packages/react": { "name": "@strudel.cycles/react", - "version": "0.3.0", + "version": "0.3.1", "license": "AGPL-3.0-or-later", "dependencies": { "@codemirror/lang-javascript": "^6.1.1", "@strudel.cycles/core": "^0.3.1", - "@strudel.cycles/eval": "^0.3.0", - "@strudel.cycles/tone": "^0.3.0", + "@strudel.cycles/eval": "^0.3.1", + "@strudel.cycles/tone": "^0.3.1", "@uiw/codemirror-themes": "^4.12.4", "@uiw/react-codemirror": "^4.12.4", "react-hook-inview": "^4.5.0" @@ -12639,11 +12621,11 @@ }, "packages/soundfonts": { "name": "@strudel.cycles/soundfonts", - "version": "0.3.0", + "version": "0.3.1", "license": "AGPL-3.0-or-later", "dependencies": { "@strudel.cycles/core": "^0.3.1", - "@strudel.cycles/webaudio": "^0.3.0", + "@strudel.cycles/webaudio": "^0.3.1", "sfumato": "^0.1.2", "soundfont2": "^0.4.0" }, @@ -12671,7 +12653,7 @@ }, "packages/tonal": { "name": "@strudel.cycles/tonal", - "version": "0.3.0", + "version": "0.3.1", "license": "AGPL-3.0-or-later", "dependencies": { "@strudel.cycles/core": "^0.3.1", @@ -12695,7 +12677,7 @@ }, "packages/tone": { "name": "@strudel.cycles/tone", - "version": "0.3.0", + "version": "0.3.1", "license": "AGPL-3.0-or-later", "dependencies": { "@strudel.cycles/core": "^0.3.1", @@ -12703,9 +12685,31 @@ "tone": "^14.7.77" } }, + "packages/transpiler": { + "name": "@strudel.cycles/transpiler", + "version": "0.3.1", + "license": "AGPL-3.0-or-later", + "dependencies": { + "@strudel.cycles/core": "^0.3.1", + "acorn": "^8.8.1", + "escodegen": "^2.0.0", + "estree-walker": "^3.0.1" + } + }, + "packages/transpiler/node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "packages/webaudio": { "name": "@strudel.cycles/webaudio", - "version": "0.3.0", + "version": "0.3.1", "license": "AGPL-3.0-or-later", "dependencies": { "@strudel.cycles/core": "^0.3.1" @@ -12713,7 +12717,7 @@ }, "packages/webdirt": { "name": "@strudel.cycles/webdirt", - "version": "0.3.0", + "version": "0.3.1", "license": "AGPL-3.0-or-later", "dependencies": { "@strudel.cycles/core": "^0.3.1", @@ -12722,7 +12726,7 @@ }, "packages/xen": { "name": "@strudel.cycles/xen", - "version": "0.3.0", + "version": "0.3.1", "license": "AGPL-3.0-or-later", "dependencies": { "@strudel.cycles/core": "^0.3.1" @@ -14423,7 +14427,7 @@ "@strudel.cycles/midi": { "version": "file:packages/midi", "requires": { - "@strudel.cycles/tone": "^0.3.0", + "@strudel.cycles/tone": "^0.3.1", "tone": "^14.7.77", "webmidi": "^3.0.21" }, @@ -14443,8 +14447,8 @@ "version": "file:packages/mini", "requires": { "@strudel.cycles/core": "^0.3.1", - "@strudel.cycles/eval": "^0.3.0", - "@strudel.cycles/tone": "^0.3.0", + "@strudel.cycles/eval": "^0.3.1", + "@strudel.cycles/tone": "^0.3.1", "peggy": "^2.0.1" } }, @@ -14459,8 +14463,8 @@ "requires": { "@codemirror/lang-javascript": "^6.1.1", "@strudel.cycles/core": "^0.3.1", - "@strudel.cycles/eval": "^0.3.0", - "@strudel.cycles/tone": "^0.3.0", + "@strudel.cycles/eval": "^0.3.1", + "@strudel.cycles/tone": "^0.3.1", "@types/react": "^17.0.2", "@types/react-dom": "^17.0.2", "@uiw/codemirror-themes": "^4.12.4", @@ -14516,7 +14520,7 @@ "version": "file:packages/soundfonts", "requires": { "@strudel.cycles/core": "^0.3.1", - "@strudel.cycles/webaudio": "^0.3.0", + "@strudel.cycles/webaudio": "^0.3.1", "node-fetch": "^3.2.6", "sfumato": "^0.1.2", "soundfont2": "^0.4.0" @@ -14562,6 +14566,22 @@ "tone": "^14.7.77" } }, + "@strudel.cycles/transpiler": { + "version": "file:packages/transpiler", + "requires": { + "@strudel.cycles/core": "^0.3.1", + "acorn": "^8.8.1", + "escodegen": "^2.0.0", + "estree-walker": "^3.0.1" + }, + "dependencies": { + "acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + } + } + }, "@strudel.cycles/webaudio": { "version": "file:packages/webaudio", "requires": { @@ -16271,10 +16291,7 @@ "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "optional": true, - "peer": true + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, "defaults": { "version": "1.0.4", @@ -16749,9 +16766,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "optional": true, - "peer": true, "requires": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -16763,16 +16777,18 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "optional": true, - "peer": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "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": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.1.tgz", + "integrity": "sha512-woY0RUD87WzMBUiZLx8NsYr23N5BKsOMZHhu2hoNRVh6NXGfoiT1KOL8G3UHlJAnEDGmfa5ubNA/AacfG+Kb0g==" + }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -16869,10 +16885,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "optional": true, - "peer": true + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "fastq": { "version": "1.13.0", @@ -18473,9 +18486,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "optional": true, - "peer": true, "requires": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" @@ -19696,9 +19706,6 @@ "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "optional": true, - "peer": true, "requires": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.6", @@ -20227,10 +20234,7 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "optional": true, - "peer": true + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==" }, "process-nextick-args": { "version": "2.0.1", @@ -20339,8 +20343,8 @@ "requires": { "@codemirror/lang-javascript": "^6.1.1", "@strudel.cycles/core": "^0.3.1", - "@strudel.cycles/eval": "^0.3.0", - "@strudel.cycles/tone": "^0.3.0", + "@strudel.cycles/eval": "^0.3.1", + "@strudel.cycles/tone": "^0.3.1", "@types/react": "^17.0.2", "@types/react-dom": "^17.0.2", "@uiw/codemirror-themes": "^4.12.4", @@ -21237,7 +21241,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "devOptional": true }, "source-map-generator": { "version": "0.8.0", @@ -21893,9 +21897,6 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "optional": true, - "peer": true, "requires": { "prelude-ls": "~1.1.2" } @@ -22294,10 +22295,7 @@ "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "optional": true, - "peer": true + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, "wordwrap": { "version": "1.0.0", diff --git a/packages/core/examples/vite-vanilla-repl/main.js b/packages/core/examples/vite-vanilla-repl/main.js index d0497e79..1ddffe55 100644 --- a/packages/core/examples/vite-vanilla-repl/main.js +++ b/packages/core/examples/vite-vanilla-repl/main.js @@ -1,6 +1,6 @@ import { controls, repl, evalScope } from '@strudel.cycles/core'; import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio'; -import shapeshifter from '@strudel.cycles/eval/shapeshifter.mjs'; +import { transpiler } from '@strudel.cycles/transpiler'; import tune from './tune.mjs'; const ctx = getAudioContext(); @@ -18,7 +18,7 @@ evalScope( const { evaluate } = repl({ defaultOutput: webaudioOutput, getTime: () => ctx.currentTime, - transpiler: shapeshifter, + transpiler, }); document.getElementById('start').addEventListener('click', () => { ctx.resume(); diff --git a/packages/transpiler/index.mjs b/packages/transpiler/index.mjs new file mode 100644 index 00000000..4832a423 --- /dev/null +++ b/packages/transpiler/index.mjs @@ -0,0 +1,5 @@ +import { evaluate as _evaluate } from '@strudel.cycles/core'; +import { transpiler } from './transpiler.mjs'; +export * from './transpiler.mjs'; + +export const evaluate = (code) => _evaluate(code, transpiler); diff --git a/packages/transpiler/package.json b/packages/transpiler/package.json new file mode 100644 index 00000000..df4968b9 --- /dev/null +++ b/packages/transpiler/package.json @@ -0,0 +1,32 @@ +{ + "name": "@strudel.cycles/transpiler", + "version": "0.3.1", + "description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.", + "main": "index.mjs", + "scripts": { + "test": "vitest run" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/tidalcycles/strudel.git" + }, + "keywords": [ + "tidalcycles", + "strudel", + "pattern", + "livecoding", + "algorave" + ], + "author": "Felix Roos ", + "license": "AGPL-3.0-or-later", + "bugs": { + "url": "https://github.com/tidalcycles/strudel/issues" + }, + "homepage": "https://github.com/tidalcycles/strudel#readme", + "dependencies": { + "@strudel.cycles/core": "^0.3.1", + "acorn": "^8.8.1", + "escodegen": "^2.0.0", + "estree-walker": "^3.0.1" + } +} diff --git a/packages/transpiler/test/transpiler.test.mjs b/packages/transpiler/test/transpiler.test.mjs new file mode 100644 index 00000000..bfb77062 --- /dev/null +++ b/packages/transpiler/test/transpiler.test.mjs @@ -0,0 +1,39 @@ +/* +transpiler.test.mjs - +Copyright (C) 2022 Strudel contributors - see +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ + +import { describe, it, expect } from 'vitest'; +import { transpiler } from '../transpiler.mjs'; + +describe('transpiler', () => { + it('wraps double quote string with mini and adds location', () => { + expect(transpiler('"c3"', { wrapAsync: false, addReturn: false })).toEqual("mini('c3').withMiniLocation(0, 4);"); + expect(transpiler('stack("c3","bd sd")', { wrapAsync: false, addReturn: false })).toEqual( + "stack(mini('c3').withMiniLocation(6, 10), mini('bd sd').withMiniLocation(11, 18));", + ); + }); + it('wraps backtick string with mini and adds location', () => { + expect(transpiler('`c3`', { wrapAsync: false, addReturn: false })).toEqual("mini('c3').withMiniLocation(0, 4);"); + }); + it('replaces note variables with note strings', () => { + expect(transpiler('seq(c3, d3)', { wrapAsync: false, addReturn: false })).toEqual("seq('c3', 'd3');"); + }); + it('keeps tagged template literal as is', () => { + expect(transpiler('xxx`c3`', { wrapAsync: false, addReturn: false })).toEqual('xxx`c3`;'); + }); + it('supports top level await', () => { + expect(transpiler("await samples('xxx');", { wrapAsync: false, addReturn: false })).toEqual( + "await samples('xxx');", + ); + }); + /* it('parses dynamic imports', () => { + expect( + transpiler("const { default: foo } = await import('https://bar.com/foo.js');", { + wrapAsync: false, + addReturn: false, + }), + ).toEqual("const {default: foo} = await import('https://bar.com/foo.js');"); + }); */ +}); diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs new file mode 100644 index 00000000..e663ef5b --- /dev/null +++ b/packages/transpiler/transpiler.mjs @@ -0,0 +1,100 @@ +import escodegen from 'escodegen'; +import { parse } from 'acorn'; +import { walk } from 'estree-walker'; +import { isNote } from '@strudel.cycles/core'; + +export function transpiler(input, options = {}) { + const { wrapAsync = true, addReturn = true } = options; + + let ast = parse(input, { + ecmaVersion: 2022, + allowAwaitOutsideFunction: true, + }); + + walk(ast, { + enter(node, parent, prop, index) { + if (isBackTickString(node, parent)) { + const { quasis, start, end } = node; + const { raw } = quasis[0].value; + this.skip(); + return this.replace(miniWithLocation(raw, start, end)); + } + if (isStringWithDoubleQuotes(node)) { + const { value, start, end } = node; + this.skip(); + return this.replace(miniWithLocation(value, start, end)); + } + if (node.type === 'Identifier' && isNote(node.name)) { + this.skip(); + return this.replace({ type: 'Literal', value: node.name }); + } + // TODO: + }, + leave(node, parent, prop, index) {}, + }); + + const { body } = ast; + if (!body?.[body.length - 1]?.expression) { + throw new Error('unexpected ast format without body expression'); + } + + // add return to last statement + if (addReturn) { + const { expression } = body[body.length - 1]; + body[body.length - 1] = { + type: 'ReturnStatement', + argument: expression, + }; + } + const output = escodegen.generate(ast); + if (wrapAsync) { + return `(async ()=>{${output}})()`; + } + return output; +} + +function isStringWithDoubleQuotes(node, locations, code) { + const { raw, type } = node; + if (type !== 'Literal') { + return false; + } + return raw[0] === '"'; +} + +function isBackTickString(node, parent) { + return node.type === 'TemplateLiteral' && parent.type !== 'TaggedTemplateExpression'; +} + +function miniWithLocation(value, start, end) { + // with location + return { + type: 'CallExpression', + callee: { + type: 'MemberExpression', + object: { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'mini', + }, + arguments: [{ type: 'Literal', value }], + optional: false, + }, + property: { + type: 'Identifier', + name: 'withMiniLocation', + }, + }, + arguments: [ + { + type: 'Literal', + value: start, + }, + { + type: 'Literal', + value: end, + }, + ], + optional: false, + }; +} diff --git a/repl/src/runtime.mjs b/repl/src/runtime.mjs index 1da561f9..8945f1a8 100644 --- a/repl/src/runtime.mjs +++ b/repl/src/runtime.mjs @@ -3,7 +3,8 @@ // it might require mocking more stuff when tunes added that use other functions // import * as tunes from './tunes.mjs'; -import { evaluate } from '@strudel.cycles/eval'; +// import { evaluate } from '@strudel.cycles/eval'; +import { evaluate } from '@strudel.cycles/transpiler'; import { evalScope } from '@strudel.cycles/core'; import * as strudel from '@strudel.cycles/core'; import * as webaudio from '@strudel.cycles/webaudio';