From 1718dbe94ef0f19c3d81180dca2c3aa2df59d4fb Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 4 Jul 2023 23:41:29 +0200 Subject: [PATCH] delete eval package --- README.md | 2 +- index.mjs | 1 - packages/core/evaluate.mjs | 2 +- packages/eval/.gitignore | 3 - packages/eval/README.md | 50 --- packages/eval/evaluate.mjs | 12 - packages/eval/index.mjs | 1 - packages/eval/package.json | 50 --- packages/eval/shapeshifter.mjs | 296 ------------------ packages/eval/shift-traverser/index.js | 68 ---- packages/eval/vite.config.js | 19 -- packages/react/vite.config.js | 1 - test/runtime.mjs | 1 - undocumented.json | 11 - .../src/pages/technical-manual/packages.mdx | 2 +- 15 files changed, 3 insertions(+), 516 deletions(-) delete mode 100644 packages/eval/.gitignore delete mode 100644 packages/eval/README.md delete mode 100644 packages/eval/evaluate.mjs delete mode 100644 packages/eval/index.mjs delete mode 100644 packages/eval/package.json delete mode 100644 packages/eval/shapeshifter.mjs delete mode 100644 packages/eval/shift-traverser/index.js delete mode 100644 packages/eval/vite.config.js diff --git a/README.md b/README.md index 42e517d0..ece22323 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ There are multiple npm packages you can use to use strudel, or only parts of it, - [`core`](./packages/core/): tidal pattern engine - [`mini`](./packages/mini): mini notation parser + core binding -- [`eval`](./packages/eval): user code evaluator. syntax sugar + highlighting +- [`transpiler`](./packages/transpiler): user code transpiler - [`tone`](./packages/tone): bindings for Tone.js instruments and effects - [`osc`](./packages/osc): bindings to communicate via OSC - [`midi`](./packages/midi): webmidi bindings diff --git a/index.mjs b/index.mjs index 79729e91..3130b993 100644 --- a/index.mjs +++ b/index.mjs @@ -2,7 +2,6 @@ export * from './packages/core/index.mjs'; export * from './packages/csound/index.mjs'; export * from './packages/embed/index.mjs'; -export * from './packages/eval/index.mjs'; export * from './packages/midi/index.mjs'; export * from './packages/mini/index.mjs'; export * from './packages/osc/index.mjs'; diff --git a/packages/core/evaluate.mjs b/packages/core/evaluate.mjs index 0dada761..9627a2ad 100644 --- a/packages/core/evaluate.mjs +++ b/packages/core/evaluate.mjs @@ -1,6 +1,6 @@ /* evaluate.mjs - -Copyright (C) 2022 Strudel contributors - see +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 . */ diff --git a/packages/eval/.gitignore b/packages/eval/.gitignore deleted file mode 100644 index b8244cfb..00000000 --- a/packages/eval/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -shift-parser -shift-reducer -!shift-traverser \ No newline at end of file diff --git a/packages/eval/README.md b/packages/eval/README.md deleted file mode 100644 index e0984706..00000000 --- a/packages/eval/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# @strudel.cycles/eval - -This package contains the strudel code transformer and evaluator. -It allows creating strudel patterns from input code that is optimized for minimal keystrokes and human readability. - -## Deprecation Note - -This package will not be developed further. Consider using `@strudel.cycles/transpiler` as a replacement. - -## Install - -```sh -npm i @strudel.cycles/eval --save -``` - -## Example - -```js -import { evalScope } from '@strudel.cycles/core'; -import { evaluate } from '@strudel.cycles/eval'; - -evalScope( - import('@strudel.cycles/core'), - // import other strudel packages here -); // add strudel to eval scope - -async function run(code) { - const { pattern } = await evaluate(code); - const events = pattern.firstCycle(); - console.log(events.map((e) => e.show()).join('\n')); -} - -run('sequence([a3, [b3, c4]])'); -``` - -yields: - -```js -(0/1 -> 1/2, 0/1 -> 1/2, a3) -(1/2 -> 3/4, 1/2 -> 3/4, b3) -(3/4 -> 1/1, 3/4 -> 1/1, c4) -``` - -[play with @strudel.cycles/eval on codesandbox](https://codesandbox.io/s/strudel-eval-example-ndz1d8?file=/src/index.js) - -## Dev Notes - -shift-traverser is currently monkey patched because its package.json uses estraverse@^4.2.0, -which does not support the spread operator (Error: Unknown node type SpreadProperty.). -By monkey patched, I mean I copied the source of shift-traverser to a subfolder and installed the dependencies (shift-spec + estraverse@^5.3.0) diff --git a/packages/eval/evaluate.mjs b/packages/eval/evaluate.mjs deleted file mode 100644 index 23132225..00000000 --- a/packages/eval/evaluate.mjs +++ /dev/null @@ -1,12 +0,0 @@ -/* -evaluate.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 { evaluate as _evaluate } from '@strudel.cycles/core'; -import shapeshifter from './shapeshifter.mjs'; - -export const evaluate = async (code) => { - return _evaluate(code, shapeshifter); -}; diff --git a/packages/eval/index.mjs b/packages/eval/index.mjs deleted file mode 100644 index 8b2718dd..00000000 --- a/packages/eval/index.mjs +++ /dev/null @@ -1 +0,0 @@ -export * from './evaluate.mjs'; diff --git a/packages/eval/package.json b/packages/eval/package.json deleted file mode 100644 index 705cfc7e..00000000 --- a/packages/eval/package.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "@strudel.cycles/eval", - "version": "0.8.0", - "description": "Code evaluator for strudel", - "main": "index.mjs", - "publishConfig": { - "main": "dist/index.js", - "module": "dist/index.mjs" - }, - "scripts": { - "build": "vite build", - "test": "vitest run", - "prepublishOnly": "npm run build" - }, - "type": "module", - "directories": { - "test": "test" - }, - "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": "workspace:*", - "estraverse": "^5.3.0", - "shift-ast": "^7.0.0", - "shift-codegen": "^8.1.0", - "shift-parser": "^8.0.0", - "shift-spec": "^2019.0.0", - "shift-traverser": "^1.0.0" - }, - "devDependencies": { - "@strudel.cycles/mini": "workspace:*", - "vite": "^4.3.3", - "vitest": "^0.28.0" - } -} diff --git a/packages/eval/shapeshifter.mjs b/packages/eval/shapeshifter.mjs deleted file mode 100644 index 19907341..00000000 --- a/packages/eval/shapeshifter.mjs +++ /dev/null @@ -1,296 +0,0 @@ -/* -shapeshifter.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 { parseScriptWithLocation } from './shift-parser/index.js'; // npm module does not work in the browser -import traverser from './shift-traverser/index.js'; // npm module does not work in the browser */ -import { parseScriptWithLocation } from 'shift-parser'; -import traverser from './shift-traverser/index.js'; -const { replace } = traverser; -import { - LiteralStringExpression, - IdentifierExpression, - CallExpression, - StaticMemberExpression, - ReturnStatement, - ArrayExpression, - LiteralNumericExpression, -} from 'shift-ast'; -import shiftCodegen from 'shift-codegen'; -const codegen = shiftCodegen.default || shiftCodegen; // parcel module resolution fuckup - -import * as strudel from '@strudel.cycles/core'; - -const { Pattern } = strudel; - -const isNote = (name) => /^[a-gC-G][bs]?[0-9]$/.test(name); - -const addLocations = true; -export const addMiniLocations = true; -export const minifyStrings = true; -export const wrappedAsync = false; // this is now handled by core evaluate by default -export const shouldAddReturn = true; - -export default (_code) => { - const { code, addReturn } = wrapAsync(_code); - const ast = parseScriptWithLocation(disguiseImports(code)); - const artificialNodes = []; - const parents = []; - const shifted = replace(ast.tree, { - enter(node, parent) { - parents.push(parent); - const isSynthetic = parents.some((p) => artificialNodes.includes(p)); - if (isSynthetic) { - return node; - } - - // replace template string `xxx` with mini(`xxx`) - if (minifyStrings && isBackTickString(node)) { - return minifyWithLocation(node, node, ast.locations, artificialNodes); - } - // allows to use top level strings, which are normally directives... but we don't need directives - if (minifyStrings && node.directives?.length === 1 && !node.statements?.length) { - const str = new LiteralStringExpression({ value: node.directives[0].rawValue }); - const wrapped = minifyWithLocation(str, node.directives[0], ast.locations, artificialNodes); - return { ...node, directives: [], statements: [wrapped] }; - } - - // replace double quote string "xxx" with mini('xxx') - if (minifyStrings && isStringWithDoubleQuotes(node, ast.locations, code)) { - return minifyWithLocation(node, node, ast.locations, artificialNodes); - } - - // operator overloading => still not done - const operators = { - '*': 'fast', - '/': 'slow', - '&': 'stack', - '&&': 'append', - }; - if ( - node.type === 'BinaryExpression' && - operators[node.operator] && - ['LiteralNumericExpression', 'LiteralStringExpression', 'IdentifierExpression'].includes(node.right?.type) && - canBeOverloaded(node.left) - ) { - let arg = node.left; - if (node.left.type === 'IdentifierExpression') { - arg = wrapFunction('reify', node.left); - } - return new CallExpression({ - callee: new StaticMemberExpression({ - property: operators[node.operator], - object: wrapFunction('reify', arg), - }), - arguments: [node.right], - }); - } - - const isMarkable = isPatternArg(parents) || hasModifierCall(parent); - // add to location to pure(x) calls - if (node.type === 'CallExpression' && node.callee.name === 'pure') { - const literal = node.arguments[0]; - // const value = literal[{ LiteralNumericExpression: 'value', LiteralStringExpression: 'name' }[literal.type]]; - return reifyWithLocation(literal, node.arguments[0], ast.locations, artificialNodes); - } - // replace pseudo note variables - if (node.type === 'IdentifierExpression') { - if (isNote(node.name)) { - const value = node.name[1] === 's' ? node.name.replace('s', '#') : node.name; - if (addLocations && isMarkable) { - return reifyWithLocation(new LiteralStringExpression({ value }), node, ast.locations, artificialNodes); - } - return new LiteralStringExpression({ value }); - } - if (node.name === 'r') { - return new IdentifierExpression({ name: 'silence' }); - } - } - if ( - addLocations && - ['LiteralStringExpression' /* , 'LiteralNumericExpression' */].includes(node.type) && - isMarkable - ) { - // TODO: to make LiteralNumericExpression work, we need to make sure we're not inside timeCat... - return reifyWithLocation(node, node, ast.locations, artificialNodes); - } - if (addMiniLocations) { - return addMiniNotationLocations(node, ast.locations, artificialNodes); - } - return node; - }, - leave() { - parents.pop(); - }, - }); - // add return to last statement (because it's wrapped in an async function artificially) - if (shouldAddReturn) { - addReturn(shifted); - } - const output = undisguiseImports(codegen(shifted)); - return { output }; -}; - -// renames all import statements to "_mport" as Shift doesn't support dynamic import. -// there shouldn't be any side-effects from this as this change does not affect -// the syntax & will be undone by the equivalent replace in "undisguiseImports". -function disguiseImports(code) { - return code.replaceAll('import', '_mport'); // Must be the same length! -} - -// Rename the renamed import statements back to "import" -function undisguiseImports(code) { - return code.replaceAll('_mport', 'import'); -} - -function wrapAsync(code) { - // wrap code in async to make await work on top level => this will create 1 line offset to locations - // this is why line offset is -1 in getLocationObject calls below - if (wrappedAsync) { - code = `(async () => { -${code} -})()`; - } - const addReturn = (ast) => { - const body = wrappedAsync ? ast.statements[0].expression.callee.body : ast; - body.statements = body.statements - .slice(0, -1) - .concat([new ReturnStatement({ expression: body.statements.slice(-1)[0] })]); - }; - return { - code, - addReturn, - }; -} - -function addMiniNotationLocations(node, locations, artificialNodes) { - const miniFunctions = ['mini', 'm']; - // const isAlreadyWrapped = parent?.type === 'CallExpression' && parent.callee.name === 'withLocationOffset'; - if (node.type === 'CallExpression' && miniFunctions.includes(node.callee.name)) { - // mini('c3') - if (node.arguments.length > 1) { - // TODO: transform mini(...args) to cat(...args.map(mini)) ? - console.warn('multi arg mini locations not supported yet...'); - return node; - } - const str = node.arguments[0]; - return minifyWithLocation(str, str, locations, artificialNodes); - } - if (node.type === 'StaticMemberExpression' && miniFunctions.includes(node.property)) { - // 'c3'.mini or 'c3'.m - return minifyWithLocation(node.object, node, locations, artificialNodes); - } - return node; -} - -function wrapFunction(name, ...args) { - return new CallExpression({ - callee: new IdentifierExpression({ name }), - arguments: args, - }); -} - -function isBackTickString(node) { - return node.type === 'TemplateExpression' && node.elements.length === 1; -} - -function isStringWithDoubleQuotes(node, locations, code) { - if (node.type !== 'LiteralStringExpression') { - return false; - } - const loc = locations.get(node); - const snippet = code.slice(loc.start.offset, loc.end.offset); - return snippet[0] === '"'; // we can trust the end is also ", as the parsing did not fail -} - -// returns true if the given parents belong to a pattern argument node -// this is used to check if a node should receive a location for highlighting -function isPatternArg(parents) { - if (!parents.length) { - return false; - } - const ancestors = parents.slice(0, -1); - const parent = parents[parents.length - 1]; - if (isPatternFactory(parent)) { - return true; - } - if (parent?.type === 'ArrayExpression') { - return isPatternArg(ancestors); - } - return false; -} - -function hasModifierCall(parent) { - // TODO: modifiers are more than composables, for example every is not composable but should be seen as modifier.. - // need all prototypes of Pattern - return parent?.type === 'StaticMemberExpression'; - // && Object.keys(Pattern.prototype.composable).includes(parent.property) -} -const factories = Object.keys(Pattern.prototype.factories).concat(['mini']); - -function isPatternFactory(node) { - return node?.type === 'CallExpression' && factories.includes(node.callee.name); -} - -function canBeOverloaded(node) { - return (node.type === 'IdentifierExpression' && isNote(node.name)) || isPatternFactory(node); - // TODO: support sequence(c3).transpose(3).x.y.z -} - -// turns node in reify(value).withLocation(location), where location is the node's location in the source code -// with this, the reified pattern can pass its location to the event, to know where to highlight when it's active -function reifyWithLocation(literalNode, node, locations, artificialNodes) { - const args = getLocationArguments(node, locations); - const withLocation = new CallExpression({ - callee: new StaticMemberExpression({ - object: wrapFunction('reify', literalNode), - property: 'withLocation', - }), - arguments: args, - }); - artificialNodes.push(withLocation); - return withLocation; -} - -// turns node in reify(value).withLocation(location), where location is the node's location in the source code -// with this, the reified pattern can pass its location to the event, to know where to highlight when it's active -function minifyWithLocation(literalNode, node, locations, artificialNodes) { - const args = getLocationArguments(node, locations); - const wrapped = wrapFunction('mini', literalNode); - if (!addMiniLocations) { - artificialNodes.push(wrapped); - return wrapped; - } - const withLocation = new CallExpression({ - callee: new StaticMemberExpression({ - object: wrapped, - property: 'withMiniLocation', - }), - arguments: args, - }); - artificialNodes.push(withLocation); - return withLocation; -} - -function getLocationArguments(node, locations) { - const loc = locations.get(node); - const lineOffset = wrappedAsync ? -1 : 0; - return [ - new ArrayExpression({ - elements: [ - new LiteralNumericExpression({ value: loc.start.line + lineOffset }), // the minus 1 assumes the code has been wrapped in async iife - new LiteralNumericExpression({ value: loc.start.column }), - new LiteralNumericExpression({ value: loc.start.offset }), - ], - }), - new ArrayExpression({ - elements: [ - new LiteralNumericExpression({ value: loc.end.line + lineOffset }), // the minus 1 assumes the code has been wrapped in async iife - new LiteralNumericExpression({ value: loc.end.column }), - new LiteralNumericExpression({ value: loc.end.offset }), - ], - }), - ]; -} diff --git a/packages/eval/shift-traverser/index.js b/packages/eval/shift-traverser/index.js deleted file mode 100644 index 5ef0977f..00000000 --- a/packages/eval/shift-traverser/index.js +++ /dev/null @@ -1,68 +0,0 @@ -/* -index.js - -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 . -*/ - -/* - 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'; -const Spec = _Spec.default || _Spec; // parcel module resolution fuckup -// 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/packages/eval/vite.config.js b/packages/eval/vite.config.js deleted file mode 100644 index 0fc63a6b..00000000 --- a/packages/eval/vite.config.js +++ /dev/null @@ -1,19 +0,0 @@ -import { defineConfig } from 'vite'; -import { dependencies } from './package.json'; -import { resolve } from 'path'; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [], - build: { - lib: { - entry: resolve(__dirname, 'index.mjs'), - formats: ['es', 'cjs'], - fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]), - }, - rollupOptions: { - external: [...Object.keys(dependencies)], - }, - target: 'esnext', - }, -}); diff --git a/packages/react/vite.config.js b/packages/react/vite.config.js index 9f7ee09e..7a1fb082 100644 --- a/packages/react/vite.config.js +++ b/packages/react/vite.config.js @@ -23,7 +23,6 @@ export default defineConfig({ ...Object.keys(dependencies), // TODO: find out which of below names are obsolete now '@strudel.cycles/tone', - '@strudel.cycles/eval', '@strudel.cycles/transpiler', 'acorn', '@strudel.cycles/core', diff --git a/test/runtime.mjs b/test/runtime.mjs index 94b5f659..777b3d83 100644 --- a/test/runtime.mjs +++ b/test/runtime.mjs @@ -3,7 +3,6 @@ // 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/transpiler'; import { evalScope } from '@strudel.cycles/core'; import * as strudel from '@strudel.cycles/core'; diff --git a/undocumented.json b/undocumented.json index 7735b34d..e8536abd 100644 --- a/undocumented.json +++ b/undocumented.json @@ -156,17 +156,6 @@ ], "/home/felix/projects/strudel/packages/core/gist.js": [], "/home/felix/projects/strudel/packages/core/index.mjs": [], - "/home/felix/projects/strudel/packages/eval/shift-traverser/index.js": [], - "/home/felix/projects/strudel/packages/eval/shapeshifter.mjs": [ - "addMiniLocations", - "minifyStrings", - "wrappedAsync", - "shouldAddReturn" - ], - "/home/felix/projects/strudel/packages/eval/evaluate.mjs": [ - "evaluate" - ], - "/home/felix/projects/strudel/packages/eval/index.mjs": [], "/home/felix/projects/strudel/packages/midi/midi.mjs": [ "WebMidi", "enableWebMidi" diff --git a/website/src/pages/technical-manual/packages.mdx b/website/src/pages/technical-manual/packages.mdx index 4b3a6077..9c36e9a1 100644 --- a/website/src/pages/technical-manual/packages.mdx +++ b/website/src/pages/technical-manual/packages.mdx @@ -52,7 +52,7 @@ These packages provide bindings for different ways to output strudel patterns: ### No Longer Maintained -- [eval](https://github.com/tidalcycles/strudel/tree/main/packages/eval): old code transpiler +- [eval](https://www.npmjs.com/package/@strudel.cycles/eval): old code transpiler - [tone](https://github.com/tidalcycles/strudel/tree/main/packages/tone#strudelcyclestone): bindings for Tone.js instruments and effects - [webdirt](https://github.com/tidalcycles/strudel/tree/main/packages/webdirt): webdirt bindings, replaced by webaudio package