diff --git a/.vscode/settings.json b/.vscode/settings.json index b396049..c62565e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,11 @@ { - // The path to the `bun` executable. - "bun.runtime": "/home/rolfmg/.bun/bin/bun", + // The path to the `bun` executable. + "bun.runtime": "/home/rolfmg/.bun/bin/bun", - // If support for Bun should be added to the default "JavaScript Debug Terminal". - "bun.debugTerminal.enabled": true, - - // If the debugger should stop on the first line of the program. - "bun.debugTerminal.stopOnEntry": true, -} \ No newline at end of file + // If support for Bun should be added to the default "JavaScript Debug Terminal". + "bun.debugTerminal.enabled": true, + + // If the debugger should stop on the first line of the program. + "bun.debugTerminal.stopOnEntry": true, + "cSpell.words": ["babelparser"] +} diff --git a/babel b/babel index 380d186..2f9c48d 160000 --- a/babel +++ b/babel @@ -1 +1 @@ -Subproject commit 380d186b77d32734c595652a40cbda5e4e109c5c +Subproject commit 2f9c48d4eda2d69908fc53ea285f47ed2c540f7e diff --git a/bun.lockb b/bun.lockb index 4d1deda..5aaca4d 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/demo-site/index.html b/demo-site/index.html new file mode 100644 index 0000000..ed0b63f --- /dev/null +++ b/demo-site/index.html @@ -0,0 +1,19 @@ + + + +Page Title + + + + + + +

Demo site

+

+ + + \ No newline at end of file diff --git a/didactic-chainsaw-dsl/src/language/js-transform-lang copy.langium b/didactic-chainsaw-dsl/src/language/js-transform-lang copy.langium new file mode 100644 index 0000000..dcf393c --- /dev/null +++ b/didactic-chainsaw-dsl/src/language/js-transform-lang copy.langium @@ -0,0 +1,18 @@ +grammar JsTransformLang + + +terminal ID: /[_a-zA-Z][\w_]*/; +terminal STRING: /"(\\.|[^"\\])*"|'(\\.|[^'\\])*'/; +entry Model: + proposals+=Proposal*; + + +Proposal: + "proposal" proposalName=ID "{" + "applicable" "to" "{" + code=STRING + "}" + "replace" "with" "{" + code=STRING + "}" + "}"; diff --git a/didactic-chainsaw-dsl/src/language/js-transform-lang-validator.ts b/didactic-chainsaw-dsl/src/language/js-transform-lang-validator.ts index 44e7cc4..9464b33 100644 --- a/didactic-chainsaw-dsl/src/language/js-transform-lang-validator.ts +++ b/didactic-chainsaw-dsl/src/language/js-transform-lang-validator.ts @@ -1,5 +1,5 @@ -import type { ValidationAcceptor, ValidationChecks } from "langium"; -import type { JsTransformLangAstType, Proposal } from "./generated/ast.js"; +import type { ValidationChecks } from "langium"; +import type { JsTransformLangAstType } from "./generated/ast.js"; import type { JsTransformLangServices } from "./js-transform-lang-module.js"; /** @@ -9,7 +9,7 @@ export function registerValidationChecks(services: JsTransformLangServices) { const registry = services.validation.ValidationRegistry; const validator = services.validation.JsTransformLangValidator; const checks: ValidationChecks = { - Proposal: validator.checkPersonStartsWithCapital, + //Person: validator.checkPersonStartsWithCapital, }; registry.register(checks, validator); } @@ -18,6 +18,7 @@ export function registerValidationChecks(services: JsTransformLangServices) { * Implementation of custom validations. */ export class JsTransformLangValidator { + /* checkPersonStartsWithCapital( proposal: Proposal, accept: ValidationAcceptor @@ -31,4 +32,5 @@ export class JsTransformLangValidator { } } } + */ } diff --git a/didactic-chainsaw-dsl/src/language/js-transform-lang.langium b/didactic-chainsaw-dsl/src/language/js-transform-lang.langium index 71cd962..5fd636f 100644 --- a/didactic-chainsaw-dsl/src/language/js-transform-lang.langium +++ b/didactic-chainsaw-dsl/src/language/js-transform-lang.langium @@ -1,18 +1,32 @@ grammar JsTransformLang - -terminal PROPOSALNAME: /[_a-zA-Z]+/; -terminal STRING: /"(\\.|[^"\\])*"|'(\\.|[^'\\])*'/; entry Model: - (proposals+=Proposal); - + (proposals+=Proposal)*; Proposal: - "proposal" "(" proposalName=PROPOSALNAME ")" "{" + 'proposal' name=ID "{" "applicable" "to" "{" - code=STRING + code=RICH_TEXT "}" "replace" "with" "{" - code=STRING + "}" "}"; + +hidden terminal WS: /\s+/; +terminal ID: /[_a-zA-Z][\w_]*/; + +terminal fragment IN_RICH_STRING: + "''" !('«'|"'") + | "'" !('«'|"'"); +//| !('«'|"'"); +terminal STRING: /"(\\.|[^"\\])*"|'(\\.|[^'\\])*'/; +hidden terminal ML_COMMENT: /\/\*[\s\S]*?\*\//; +hidden terminal SL_COMMENT: /\/\/[^\n\r]*/; +terminal RICH_TEXT: "'''" IN_RICH_STRING* ("'''"| ("'" "'"?)? ); +terminal RICH_TEXT_START: "'''" IN_RICH_STRING* ("'" "'"?)? '«'; +terminal RICH_TEXT_END: '»' IN_RICH_STRING* ("'''"| ("'" "'"?)? ); +terminal RICH_TEXT_INBETWEEN: '»' IN_RICH_STRING* ("'" "'"?)? '«'; +terminal COMMENT_RICH_TEXT_INBETWEEN: "««" !('\n'|'\r')* ('\r'? '\n' IN_RICH_STRING* ("'" "'"?)? '«')?; +terminal COMMENT_RICH_TEXT_END: "««" !('\n'|'\r')* (('\r'? '\n' IN_RICH_STRING* ("'''"| ("'" "'"?)? )) ); + diff --git a/dsl_files/test.jstl b/dsl_files/test.jstl index 3d20c5e..b55cb1d 100644 --- a/dsl_files/test.jstl +++ b/dsl_files/test.jstl @@ -1,4 +1,7 @@ -proposal (async) { + + + +proposal async { applicable to { let _$_a_$_ = await _$_expr_$_(); console.log(_$_a_$_); diff --git a/dsl_files/test_hello.jstl b/dsl_files/test_hello.jstl new file mode 100644 index 0000000..ed3b330 --- /dev/null +++ b/dsl_files/test_hello.jstl @@ -0,0 +1,19 @@ +proposal p1 { + applicable to { + "let a = 0;" + } + replace with { + "SOMETHING" + } +} + + +proposal p2 { + applicable to { + "let a = 0;" + } + replace with { + "SOMETHING" + } +} + diff --git a/output.js b/output.js new file mode 100644 index 0000000..3f2d1e0 --- /dev/null +++ b/output.js @@ -0,0 +1,120 @@ +require("@risingstack/trace"); +const Discord = require("discord.js"); +const ytdl = require("ytdl-core"); +const bot = new Discord.Client(); +const fs = new require("fs"); +const path = require("path"); +const probe = require("pmx").probe(); +const jsonfile = require("jsonfile"); +const commandCooldown = require("./helpers/commandCooldown.js"); +let cleverbot = require("cleverbot.io"), + clever = new cleverbot("jp6wu9XZbYdoICmo", "54jV1VcMNxGQyc2cdKUFUpjkPVo3bTr2"); +const log = require("./helpers/log.js"); +bot.on("ready", () => { + bot.user.setGame(".help"); + (function loop(i) { + setTimeout(function () { + bot.guilds.size |> guilds.set(%); + if (true) { + loop(i); + } + }, 1000); + })(10); + log(`Ready to serve ${bot.users.size} users, in ${bot.channels.size} channels of ${bot.guilds.size} servers.`); +}); +fs.readFile("config.json", (err, data) => { + if (err) { + log("Config file does not exist, creating one."); + let obj = { + discordToken: "TOKEN", + discordBotsToken: "TOKEN" + }; + jsonfile.spaces = 4; + jsonfile.writeFile("config.json", obj, err => { + err |> console.log(%); + }); + process.exit(1); + } else { + config = require("./config.json"); + config.discordToken |> bot.login(%); + } +}); +global.skips = {}; +global.queue = { + test: "test" +}; +global.dispatchers = new Map(); +global.connections = new Map(); +global.voices = new Map(); +global.streams = new Map(); +let config = "ERR"; +global.allstreams = 0; +global.counter = probe.counter({ + name: "Streams" +}); +let guilds = probe.metric({ + name: "Guilds" +}); +let userCooldown = new Map(); +bot.commands = new Discord.Collection(); +bot.aliases = new Discord.Collection(); +fs.readdir("./commands/", (err, files) => { + if (err) err |> console.error(%); + log(`Loading a total of ${files.length} commands.`); + files.forEach(f => { + let props = require(`./commands/${f}`); + log(`Loading Command: ${props.info.name}. :ok_hand:`); + bot.commands.set(props.info.name, props); + /* + props.conf.aliases.forEach(alias => { + bot.aliases.set(alias, props.info.name); + }); + */ + }); +}); + +bot.on("message", msg => { + const prefix = "."; + let id = bot.user.id; + let clevername = new RegExp(`^<@!?${id}>`); + if (msg.content.startsWith(prefix)) {} else if (clevername.test(msg.content)) {} else return; + if (msg.author.bot) return; + if (msg.guild) { + if (!queue[msg.guild.id]) { + queue[msg.guild.id] = []; + } + } else return; + let command = msg.content.split(" ")[0].slice(prefix.length); + let params = msg.content.split(" ").slice(1); + //let perms = bot.elevation(msg); + let cmd; + if (!userCooldown.get(msg.author.id)) { + userCooldown.set(msg.author.id, 0); + } + if (bot.commands.has(command)) { + if (!commandCooldown(userCooldown.get(msg.author.id))) { + userCooldown.set(msg.author.id, Date.now()); + cmd = bot.commands.get(command); + } else msg.channel.sendMessage("You're sending commands too quickly!"); + } else if (bot.aliases.has(command)) { + cmd = bot.commands.get(bot.aliases.get(command)); + } + if (cmd) { + cmd.run(bot, msg, params); + } + if (clevername.test(msg.content)) { + msg.author.username + " (" + msg.author.id + ") issued command: " + msg.content |> console.log(%); + let string = msg.content; + string = msg.content.split(" "); + string.shift(); + " " |> string.join(%); + msg.author.username |> clever.setNick(%); + clever.create(function (err, session) { + if (err) log(err); + clever.ask(string, function (err, response) { + if (err) log(err); + msg.channel.sendMessage(response).then(msg => log(`Sent message: ${msg.content}`)).catch(console.error); + }); + }); + } +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 298c048..1b84bbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "didactic-chainsaw", "dependencies": { + "@babel/core": "^7.23.7", "@babel/generator": "^7.23.0", "@babel/parser": "^7.23.0", "@babel/traverse": "^7.23.0", @@ -16,30 +17,88 @@ "ts-node": "^10.9.1" }, "devDependencies": { + "@babel/plugin-proposal-pipeline-operator": "^7.23.3", "@swc/cli": "^0.1.62", "@types/node": "^20.5.9", "bun-types": "latest", "typescript": "^5.2.2" } }, - "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dependencies": { - "@babel/highlight": "^7.22.13", + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dependencies": { + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", "dependencies": { - "@babel/types": "^7.23.0", + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dependencies": { + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -57,6 +116,42 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, "node_modules/@babel/helper-environment-visitor": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", @@ -88,6 +183,55 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", @@ -100,9 +244,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "engines": { "node": ">=6.9.0" } @@ -115,10 +259,31 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.7.tgz", + "integrity": "sha512-6AMnjCoC8wjqBzDHkuqpa7jAKwvMo4dC+lr/TFBz+ucfulO1XMpDnwWPGBNwClOKZ8h6xn5N81W/R5OrcKtCbQ==", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -129,9 +294,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "bin": { "parser": "bin/babel-parser.js" }, @@ -139,6 +304,37 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-proposal-pipeline-operator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-pipeline-operator/-/plugin-proposal-pipeline-operator-7.23.3.tgz", + "integrity": "sha512-8TDc1vEx+YRaGiF8J8w/XcADaBuqc0RnokaMRrHdX7Vx74WhmxPU8wtM/OHSXvgw45P9tlHS/l0YDpNXwLghmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-pipeline-operator": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-pipeline-operator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-pipeline-operator/-/plugin-syntax-pipeline-operator-7.23.3.tgz", + "integrity": "sha512-xypNE8ptJ5buVtgAAOZzN3gIV6McZfMA27GMhy70a8auQIxbLW9g/uKsaoWqUHdPJgpsXYjVD+5oDyS6pRvraA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -153,19 +349,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", - "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -173,11 +369,11 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, @@ -957,6 +1153,37 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/bun": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/bun/-/bun-1.0.4.tgz", @@ -1031,6 +1258,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001574", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001574.tgz", + "integrity": "sha512-BtYEK4r/iHt/txm81KBudCUcTy7t+s9emrIaHqjYurQ10x71zJ5VQ9x1dYPcz/b+pKSp4y/v1xSI67A+LzpNyg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -1098,6 +1344,11 @@ "node": ">= 0.6" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -1174,6 +1425,11 @@ "node": ">=0.3.1" } }, + "node_modules/electron-to-chromium": { + "version": "1.4.623", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.623.tgz", + "integrity": "sha512-lKoz10iCYlP1WtRYdh5MvocQPWVRoI7ysp6qf18bmeBgR8abE6+I2CsfyNKztRDZvhdWc+krKT6wS7Neg8sw3A==" + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1183,6 +1439,14 @@ "once": "^1.4.0" } }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", @@ -1348,6 +1612,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -1540,6 +1812,17 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/keyv": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", @@ -1633,6 +1916,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + }, "node_modules/normalize-url": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", @@ -1733,6 +2021,11 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -2202,6 +2495,35 @@ "node": ">=14.17" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 297c2e5..612c499 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,27 @@ { - "name": "didactic-chainsaw", - "module": "index.ts", - "type": "module", - "scripts": { - "run": "bun run src/index.ts", - "watch": "bun --watch src/index.ts" - }, - "devDependencies": { - "@swc/cli": "^0.1.62", - "@types/node": "^20.5.9", - "bun-types": "latest", - "typescript": "^5.2.2" - }, - "dependencies": { - "@babel/generator": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/traverse": "^7.23.0", - "@types/babel-traverse": "^6.25.10", - "babel": "^6.23.0", - "bun": "^1.0.4", - "ts-node": "^10.9.1" - } + "name": "didactic-chainsaw", + "module": "index.ts", + "type": "module", + "scripts": { + "": "bun run src/index.ts", + "watch": "bun --watch src/index.ts" + }, + "devDependencies": { + "@babel/plugin-proposal-pipeline-operator": "^7.23.3", + "@swc/cli": "^0.1.62", + "@types/node": "^20.5.9", + "bun-types": "latest", + "typescript": "^5.2.2" + }, + "dependencies": { + "@babel/core": "^7.23.7", + "@babel/generator": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/traverse": "^7.23.0", + "@types/babel-traverse": "^6.25.10", + "@types/babel__traverse": "^7.20.5", + "babel": "^6.23.0", + "bun": "^1.0.4", + "ts-node": "^10.9.1" + } } diff --git a/src/babel.config.json b/src/babel.config.json new file mode 100644 index 0000000..e69de29 diff --git a/src/data_structures/tree.ts b/src/data_structures/tree.ts new file mode 100644 index 0000000..283e6e6 --- /dev/null +++ b/src/data_structures/tree.ts @@ -0,0 +1,72 @@ +import * as babelparser from "@babel/parser"; + +import traverse from "@babel/traverse"; +import * as t from "@babel/types"; +import { PairedNodes } from "../matcher/matcher"; + +export class TreeNode { + public parent: TreeNode | null; + public element: T; + public children: TreeNode[] = []; + + constructor(parent: TreeNode | null, element: T) { + this.parent = parent; + this.element = element; + if (this.parent) this.parent.children.push(this); + } +} + +export const makeTree = ( + ast: babelparser.ParseResult +): TreeNode | undefined => { + let last: TreeNode | null = null; + + let first: TreeNode | null = null; + traverse(ast, { + enter(path: any) { + //console.log(path.node); + //console.log("Entered: ", path.node.type); + + let node: TreeNode = new TreeNode( + last, + path.node as t.Node + ); + + if (last == null) { + first = node; + } + last = node; + }, + exit(path: any) { + if (last && last?.element?.type != "Program") { + last = last.parent; + } + }, + }); + //console.log(first.children); + + if (first != null) { + return first; + } else { + return undefined; + } +}; +export const showTree = (tree: TreeNode, idents: number = 0) => { + console.log(" ".repeat(idents) + tree.element?.type); + tree.children.forEach((child) => { + showTree(child, idents + 1); + }); +}; +export const showTreePaired = ( + tree: TreeNode, + idents: number = 0 +) => { + console.log( + " ".repeat(idents), + tree.element.aplToNode.type, + tree.element.codeNode.type + ); + tree.children.forEach((child) => { + showTreePaired(child, idents + 1); + }); +}; diff --git a/src/index.ts b/src/index.ts index 677e837..f4f2bc3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,36 +1,101 @@ -import * as babelparser from "../babel/packages/babel-parser"; -import { parseApplicableTo } from "./parser/parse"; +//import * as babelparser from "../babel/packages/babel-parser"; +import * as babelparser from "@babel/parser"; +//import core from "../babel/packages/babel-core"; +import { parse_with_plugins } from "./parser/parse"; import { TransformRecipe, transform } from "./transform/transform"; /* proposal await_to_promise { applicable to { let a = await b(); - <> + <> } transform to { b().then((a) => { - console.log(a); + <> }) } } */ -const transformExample:TransformRecipe = { - applicableTo: `let a = await b();<>`, - consumeBlock: true, - identifiers: ["b", "a", "rest"], - transformTo: "b().then((a) => {<>})" +/* +// Status quo +var minLoc = Object.keys( grunt.config( "uglify.all.files" ) )[ 0 ]; + +// With pipes +var minLoc = grunt.config('uglify.all.files') |> Object.keys(%)[0]; + + +proposal pipeline_simple{ + applicable to { + var minLoc = Object.keys( grunt.config( "uglify.all.files" ) )[ 0 ]; + } + + transform to { + var minLoc = grunt.config('uglify.all.files') |> Object.keys(%)[0]; + } } -const code = "let a = await b(); console.log(a);" -const main = (): void => { - transform(transformExample, code); +*/ - +/* - +an example of what this will hit: +Unary function calls test(1); + +proposal pipeline_simple{ + applicable to { + <>(<>); + console.log(<>) + } + + transform to { + b |> a(%); + } +} + +*/ + +const transformExample: TransformRecipe = { + applicableTo: `<>(<>);`, + transformTo: "b |> a(%)", +}; +const code = + "a(something);a(1+1);something(some_other_thing + 1 + 10 + 100); console.log(a)"; + +// Expected outcome: 3 correct matches +const secondTransformExample: TransformRecipe = { + applicableTo: `<>.<>(<>);`, + transformTo: "c |> a.b(%);", +}; +const code2 = `console.log(a);something.sometingOther(b(c));some.thing(1+1);a(b)`; + +// Expected outcome: 1 correct match +const thirdTransformExample: TransformRecipe = { + applicableTo: `myFunction(<>)`, + transformTo: `a |> myFunction(%)`, +}; +const code3 = `myFunction(a);otherFunction(a); myFunction.otherfunction(a)`; + +// Expected outcome: 3 correct matches +const simpleTransformExample: TransformRecipe = { + applicableTo: `<>.<>(<>)`, + transformTo: `something |> a.b(%)`, +}; + +const test: TransformRecipe = { + applicableTo: "let <> = 0;", + transformTo: "if (true) {console.log(<>)};", +}; +const path = "../test.js"; +const file = Bun.file(path); +const codeFromFile = await file.text(); +const main = async () => { + await Bun.write( + "../output.js", + transform(simpleTransformExample, codeFromFile) + ); }; main(); diff --git a/src/matcher/matcher.ts b/src/matcher/matcher.ts new file mode 100644 index 0000000..f3e9925 --- /dev/null +++ b/src/matcher/matcher.ts @@ -0,0 +1,156 @@ +import * as t from "@babel/types"; + +import * as babelparser from "@babel/parser"; +import { TreeNode, makeTree, showTree } from "../data_structures/tree"; +import { InternalDSLVariable } from "../parser/parse"; + +const keys_to_ignore = ["loc", "start", "end", "type"]; + +export interface MatchedTreeNode { + aplToNode: TreeNode; + codeNode: TreeNode; +} + +export interface PairedNodes { + aplToNode: t.Node; + codeNode: t.Node; +} + +export function runMatch( + code: TreeNode, + applicableTo: TreeNode, + internals: Map +): TreeNode[] { + let matches: TreeNode[] = []; + + function checkDSLInternals(code_node: t.Node, aplTo: t.Node): boolean { + if (aplTo.type == "Identifier" && internals.has(aplTo.name)) { + let dsl_types = internals.get(aplTo.name)?.type ?? []; + + for (const dsl_type of dsl_types) { + if (dsl_type == "Expression") { + if ( + code_node.type.includes("Expression") || + code_node.type == "StringLiteral" + ) { + return true; + } + } else if (dsl_type == "") { + if ( + code_node.type == "Identifier" && + aplTo.type == "Identifier" + ) { + return true; + } + } else if (dsl_type == "Identifier") { + if (code_node.type == "Identifier") { + return true; + } + } + } + } + return false; + } + + function match( + code: TreeNode, + applicableTo: TreeNode + ): boolean { + if (code.element.type == "Program") { + code.children.forEach((code_child) => { + match(code_child, applicableTo); + }); + } + + // This is a bit wierd, as we currently do not support having ApplicableTo be multiple statements + if (applicableTo.element.type == "Program") { + match(code, applicableTo.children[0]); + } + + let node_matches = checkCodeNode(code.element, applicableTo.element); + + //If element matches DSL internals, we return right away and ignore the contents + if (checkDSLInternals(code.element, applicableTo.element)) { + return true; + } + + if (node_matches) { + if (applicableTo.children.length != code.children.length) { + return false; + } + for (let i = 0; i < applicableTo.children.length; i++) { + //Verify we can actually do a match + if (!match(code.children[i], applicableTo.children[i])) { + return false; + } + } + return true; + } else { + for (let code_child of code.children) { + //Avoid matching on single identifier + if (code_child.element.type == "Identifier") { + continue; + } + if (match(code_child, applicableTo)) { + matches.push(code_child); + } + } + return false; + } + } + match(code, applicableTo); + console.log(matches.length); + + return matches + .map((match) => pairMatch(match, applicableTo)) + .filter((match) => match != null); +} + +function pairMatch( + match: TreeNode, + aplTo: TreeNode +): TreeNode | null { + if (aplTo.element.type == "Program") { + return pairMatch(match, aplTo.children[0]); + } + try { + let node: TreeNode = new TreeNode(null, { + codeNode: match.element, + aplToNode: aplTo.element, + }); + + for (let i = 0; i < aplTo.children.length; i++) { + let child = pairMatch(match.children[i], aplTo.children[i]); + child.parent = node; + node.children.push(child); + } + + return node; + } catch (exception) { + return null; + } +} + +function checkCodeNode(code_node: t.Node, aplTo: t.Node): boolean { + if (code_node.type != aplTo.type) { + return false; + } + + //If not an internal DSL variable, gotta verify that the identifier is the same + if (code_node.type === "Identifier" && aplTo.type === "Identifier") { + if (code_node.name != aplTo.name) { + return false; + } + } + for (let key of Object.keys(aplTo)) { + if (key in keys_to_ignore) { + continue; + } + + if (!Object.keys(code_node).includes(key)) { + return false; + } + } + + return true; +} diff --git a/src/parser/internal_to_js_map.ts b/src/parser/internal_to_js_map.ts new file mode 100644 index 0000000..84853c8 --- /dev/null +++ b/src/parser/internal_to_js_map.ts @@ -0,0 +1,11 @@ + +import * as t from "@babel/types"; + + +export interface InternalJsMap{ + CallExpression: +} + + + + diff --git a/src/parser/parse.ts b/src/parser/parse.ts index 4672a77..1ba7491 100644 --- a/src/parser/parse.ts +++ b/src/parser/parse.ts @@ -1,50 +1,78 @@ +import * as babelparser from "@babel/parser"; -// This needs to support multiple commands in future +import * as t from "@babel/types"; -export interface Command{ - commandName: string, - commandIdentifier: string, +export interface InternalDSLVariable { + type: string[]; + dsl_name: string; } -export interface ApplicableToResult{ - commands: Command[], - applicableTo: string, +export interface InternalParseResult { + internals: Map; + cleanedJS: string; } -export function parseApplicableTo(applicableTo:string) :ApplicableToResult { +export function parseInternal(applicableTo: string): InternalParseResult { + let lastChar: null | string = null; + let inDslParseMode = false; - let applicableToIter = applicableTo[Symbol.iterator](); + let inDslParseString = ""; - let applicableToOut = ""; + let internalParseResult: InternalParseResult = { + internals: new Map(), + cleanedJS: "", + }; - let commands:Command[] = []; - let curCommandName = ""; - let curCommandIdentifier = ""; - - let nextIter; - while(!(nextIter = applicableToIter.next()).done){ - if (nextIter.value === "<" && applicableToIter.next().value === "<") { - let commandChar; - let commandName = ""; - - while((commandChar = applicableToIter.next()).value != ":"){ - commandName += commandChar.value; + for (let char of applicableTo) { + if (inDslParseMode) { + if (char == ">" && lastChar == ">") { + //remove first closing > + inDslParseString = inDslParseString.slice(0, -1); + let { identifier, type, replaceWith } = + parseInternalString(inDslParseString); + internalParseResult.cleanedJS += replaceWith; + internalParseResult.internals.set("___" + identifier, { + type: type, + dsl_name: identifier, + }); + inDslParseString = ""; + inDslParseMode = false; + continue; } - let commandIdentifier = ""; - - while((commandChar = applicableToIter.next()).value != ">"){ - commandIdentifier += commandChar.value; + inDslParseString += char; + } else { + if (char == "<" && lastChar == "<") { + //Remove previous < + internalParseResult.cleanedJS = + internalParseResult.cleanedJS.slice(0, -1); + inDslParseMode = true; + continue; } - let _ = applicableToIter.next(); - - commands.push({commandIdentifier, commandName}); - }else{ - applicableToOut += nextIter.value; + internalParseResult.cleanedJS += char; } + + lastChar = char; } - return {applicableTo:applicableToOut, commands}; - + return internalParseResult; +} + +function parseInternalString(dslString: string) { + let splitted = dslString.split(":"); + + return { + identifier: splitted[0], + type: splitted.length > 1 ? splitted[1].split("|") : [""], + replaceWith: "___" + splitted[0], + }; +} + +export function parse_with_plugins( + code: string +): babelparser.ParseResult { + return babelparser.parse(code, { + plugins: [["pipelineOperator", { proposal: "hack", topicToken: "%" }]], + }); } diff --git a/src/placeholders.js b/src/placeholders.js deleted file mode 100644 index 139597f..0000000 --- a/src/placeholders.js +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/structurizer.js b/src/structurizer.js deleted file mode 100644 index b28b04f..0000000 --- a/src/structurizer.js +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/test/test_transform.test.ts b/src/test/test_transform.test.ts new file mode 100644 index 0000000..42cb5d7 --- /dev/null +++ b/src/test/test_transform.test.ts @@ -0,0 +1,39 @@ +import { expect, test } from "bun:test"; +import { TransformRecipe, transform } from "../transform/transform"; + +const transformExample: TransformRecipe = { + applicableTo: `<>(<>);`, + transformTo: "b |> a(%)", +}; +const code = + "a(something);a(1+1);something(some_other_thing + 1 + 10 + 100); console.log(a)"; + +test("Test code: " + code + " on " + transformExample.applicableTo, () => { + expect(transform(transformExample, code).length).toBe( + "something |> a(%);1 + 1 |> a(%);some_other_thing + 1 + 10 + 100 |> something(%);console.log(a);" + ); +}); +// Expected outcome: 3 correct matches +const secondTransformExample: TransformRecipe = { + applicableTo: `<>.<>(<>);`, + transformTo: "c |> a.b(%);", +}; +const code2 = `console.log(a);something.sometingOther(b(c));some.thing(1+1); a(b)`; +test( + "Test code: " + code2 + " on " + secondTransformExample.applicableTo, + () => { + expect(transform(secondTransformExample, code2).length).toBe(3); + } +); +// Expected outcome: 1 correct match +const thirdTransformExample: TransformRecipe = { + applicableTo: `myFunction(<>)`, + transformTo: `a |> myFunction(%)`, +}; +const code3 = `myFunction(a);otherFunction(a); myFunction.otherfunction(a)`; +test( + "Test code: " + code3 + " on " + thirdTransformExample.applicableTo, + () => { + expect(transform(thirdTransformExample, code3)).toBe(``); + } +); diff --git a/src/transform/transform.ts b/src/transform/transform.ts index 6d4e4cf..7a09027 100644 --- a/src/transform/transform.ts +++ b/src/transform/transform.ts @@ -1,50 +1,60 @@ - -import * as babelparser from "@babel/parser"; import traverse from "@babel/traverse"; import * as t from "@babel/types"; - -import { parseApplicableTo } from "../parser/parse"; -export interface TransformRecipe{ - applicableTo: string, - identifiers: string[], - consumeBlock: boolean, - transformTo: string, +import generate from "@babel/generator"; +import { parseInternal, parse_with_plugins } from "../parser/parse"; +import { + TreeNode, + makeTree, + showTree, + showTreePaired, +} from "../data_structures/tree"; +import { runMatch } from "../matcher/matcher"; +import { transformMatch, transformer } from "./transformMatch"; +export interface TransformRecipe { + applicableTo: string; + transformTo: string; } -export function transform(recipe: TransformRecipe, code:string){ - - let {commands, applicableTo} = parseApplicableTo(recipe.applicableTo); - - - //console.log(applicableTo); +export function transform(recipe: TransformRecipe, code: string) { + let { internals, cleanedJS } = parseInternal(recipe.applicableTo); + let codeAST = parse_with_plugins(code); + let codeTree = makeTree(codeAST); + let applicabelToAST = parse_with_plugins(cleanedJS); + let applicableToTree = makeTree(applicabelToAST); + let transformTo = parse_with_plugins(recipe.transformTo); + let transformToTree = makeTree(transformTo); - let applicableToAST = babelparser.parse(applicableTo, {allowAwaitOutsideFunction: true}); - console.log(applicableToAST); + console.log(); - let codeAST = babelparser.parse(code, {allowAwaitOutsideFunction: true}); - console.log(codeAST); + if ( + codeTree == undefined || + applicableToTree == undefined || + transformToTree == undefined + ) { + throw new Error("This no worky LOL"); + } + let matches = runMatch(codeTree, applicableToTree, internals); - - - traverse(applicableToAST, { - enter(path:any) { - traverse(codeAST, { - enter(codePath:any){ - if (codePath.node.type === "Program"){ - return; - } - if (codePath.node.type === path.node.type){ - console.log("We found a match with"); - console.log(codePath.node); - - } - } - }) + for (let match of matches) { + //console.log(transformToTree.element); + let output = structuredClone(transformToTree.element); + try { + transformer(match, transformToTree, output, codeAST); + } catch (error) { + console.log("We failed to transform an element!"); } - }) + //let result = generate(transformToTreeClone.element); + //console.log(output); + console.log(generate(match.element.codeNode).code, "is turned into:"); + console.log(generate(output, { topicToken: "%" }).code); + //console.log(generate(codeAST, { topicToken: "%" }).code); + console.log("\n"); + } + + console.log("Final generated code: \n"); + + let output = generate(codeAST, { topicToken: "%" }).code; + //showTree(transformToTree); + + return output; } - - - - - diff --git a/src/transform/transformMatch.ts b/src/transform/transformMatch.ts new file mode 100644 index 0000000..3d8b3d8 --- /dev/null +++ b/src/transform/transformMatch.ts @@ -0,0 +1,94 @@ +import * as t from "@babel/types"; + +import * as babelparser from "@babel/parser"; + +import { + TreeNode, + makeTree, + showTree, + showTreePaired, +} from "../data_structures/tree"; +import { InternalDSLVariable } from "../parser/parse"; +import { MatchedTreeNode, PairedNodes } from "../matcher/matcher"; +import traverse from "@babel/traverse"; + +export function transformer( + match: TreeNode, + trnTo: TreeNode, + output: t.Node, + inputCode: t.Node +) { + transformMatch(match, trnTo, output); + + if (output.type == "Program") { + output = output.body[0]; + } + traverse(inputCode, { + enter(path) { + if (path.node === match.element.codeNode) { + console.log("We did stuffs?"); + path.replaceWith(output); + } + }, + }); +} + +export function transformMatch( + match: TreeNode, + trnTo: TreeNode, + output: t.Node +) { + if (trnTo.element.type == "Program") { + return transformMatch(match, trnTo.children[0], output); + } + + let isMatch = matchNode(match.element.aplToNode, trnTo.element); + if (isMatch) { + if (trnTo.element.type == "Identifier") { + traverse(output, { + enter(path) { + if (path.isIdentifier({ name: trnTo.element.name })) { + //console.log(match.element.codeNode); + if (match.element.codeNode) { + path.replaceWith(match.element.codeNode); + } + } + }, + }); + } + } else { + for (let match_child of match.children) { + transformMatch(match_child, trnTo, output); + } + for (let trnTo_child of trnTo.children) { + transformMatch(match, trnTo_child, output); + } + } +} + +function matchNode(aplTo: t.Node, trnTo: t.Node): boolean { + //console.log(trnTo); + + if (trnTo.type == "Identifier" && aplTo.type == "Identifier") { + let aplToName = washName(aplTo.name); + let trnToName = trnTo.name; + if (aplToName == trnToName) { + return true; + } + } else if (trnTo.type == "Identifier" && aplTo.type == "Identifier") { + let aplToName = washName(aplTo.name); + let trnToName = trnTo.name; + + if (aplToName == trnToName) { + return true; + } + } + return false; +} + +function washName(name: string): string { + if (name.startsWith("___")) { + return name.slice(3); + } + return name; +} diff --git a/test.js b/test.js new file mode 100644 index 0000000..aa92a51 --- /dev/null +++ b/test.js @@ -0,0 +1,160 @@ +require("@risingstack/trace"); +const Discord = require("discord.js"); +const ytdl = require("ytdl-core"); +const bot = new Discord.Client(); +const fs = new require("fs"); +const path = require("path"); +const probe = require("pmx").probe(); +const jsonfile = require("jsonfile"); +const commandCooldown = require("./helpers/commandCooldown.js"); +let cleverbot = require("cleverbot.io"), + clever = new cleverbot( + "jp6wu9XZbYdoICmo", + "54jV1VcMNxGQyc2cdKUFUpjkPVo3bTr2" + ); + +const log = require("./helpers/log.js"); + +bot.on("ready", () => { + bot.user.setGame(".help"); + + (function loop(i) { + setTimeout(function () { + guilds.set(bot.guilds.size); + if (true) { + loop(i); + } + }, 1000); + })(10); + + log( + `Ready to serve ${bot.users.size} users, in ${bot.channels.size} channels of ${bot.guilds.size} servers.` + ); +}); + +fs.readFile("config.json", (err, data) => { + if (err) { + log("Config file does not exist, creating one."); + + let obj = { + discordToken: "TOKEN", + discordBotsToken: "TOKEN", + }; + + jsonfile.spaces = 4; + jsonfile.writeFile("config.json", obj, (err) => { + console.log(err); + }); + process.exit(1); + } else { + config = require("./config.json"); + + bot.login(config.discordToken); + } +}); + +global.skips = {}; +global.queue = { + test: "test", +}; + +global.dispatchers = new Map(); +global.connections = new Map(); +global.voices = new Map(); +global.streams = new Map(); + +let config = "ERR"; + +global.allstreams = 0; +global.counter = probe.counter({ + name: "Streams", +}); +let guilds = probe.metric({ + name: "Guilds", +}); + +let userCooldown = new Map(); + +bot.commands = new Discord.Collection(); +bot.aliases = new Discord.Collection(); +fs.readdir("./commands/", (err, files) => { + if (err) console.error(err); + log(`Loading a total of ${files.length} commands.`); + files.forEach((f) => { + let props = require(`./commands/${f}`); + log(`Loading Command: ${props.info.name}. :ok_hand:`); + bot.commands.set(props.info.name, props); + /* + props.conf.aliases.forEach(alias => { + bot.aliases.set(alias, props.info.name); + }); + */ + }); +}); + +bot.on("message", (msg) => { + const prefix = "."; + + let id = bot.user.id; + let clevername = new RegExp(`^<@!?${id}>`); + + if (msg.content.startsWith(prefix)) { + } else if (clevername.test(msg.content)) { + } else return; + if (msg.author.bot) return; + + if (msg.guild) { + if (!queue[msg.guild.id]) { + queue[msg.guild.id] = []; + } + } else return; + + let command = msg.content.split(" ")[0].slice(prefix.length); + let params = msg.content.split(" ").slice(1); + //let perms = bot.elevation(msg); + let cmd; + + if (!userCooldown.get(msg.author.id)) { + userCooldown.set(msg.author.id, 0); + } + + if (bot.commands.has(command)) { + if (!commandCooldown(userCooldown.get(msg.author.id))) { + userCooldown.set(msg.author.id, Date.now()); + cmd = bot.commands.get(command); + } else msg.channel.sendMessage("You're sending commands too quickly!"); + } else if (bot.aliases.has(command)) { + cmd = bot.commands.get(bot.aliases.get(command)); + } + if (cmd) { + cmd.run(bot, msg, params); + } + + if (clevername.test(msg.content)) { + console.log( + msg.author.username + + " (" + + msg.author.id + + ") issued command: " + + msg.content + ); + + let string = msg.content; + string = msg.content.split(" "); + string.shift(); + string.join(" "); + + clever.setNick(msg.author.username); + + clever.create(function (err, session) { + if (err) log(err); + clever.ask(string, function (err, response) { + if (err) log(err); + msg.channel + .sendMessage(response) + .then((msg) => log(`Sent message: ${msg.content}`)) + .catch(console.error); + }); + }); + } +}); diff --git a/tsconfig.json b/tsconfig.json index 3c12a82..19d03b7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,12 +3,13 @@ "rootDir": "src", "outDir": "dist", "strict": true, - "target": "es6", + "target": "es2017"17", "module": "commonjs", "sourceMap": true, "esModuleInterop": true, "moduleResolution": "node", "types": ["bun-types"] }, - "include": ["src", "babel/packages/babel-parser"] + "include": ["src"], + "exclude": ["babel"] }