diff --git a/JSTQL/JSTQL-0.0.1.vsix b/JSTQL/JSTQL-0.0.1.vsix new file mode 100644 index 0000000..965b501 Binary files /dev/null and b/JSTQL/JSTQL-0.0.1.vsix differ diff --git a/JSTQL/src/JSTQL_interface/api.ts b/JSTQL/src/JSTQL_interface/api.ts new file mode 100644 index 0000000..677f725 --- /dev/null +++ b/JSTQL/src/JSTQL_interface/api.ts @@ -0,0 +1,17 @@ +import type { Model } from "../language/generated/ast.js"; + +import { AstNode, EmptyFileSystem, LangiumDocument } from "langium"; +import { parseDocument } from "langium/test"; +import { createJstqlServices } from "../language/jstql-module.js"; +const services = createJstqlServices(EmptyFileSystem).Jstql; + +export async function parseDSLtoAST(modelText: string): Promise { + var doc: LangiumDocument = await parseDocument( + services, + modelText + ); + const db = services.shared.workspace.DocumentBuilder; + await db.build([doc], { validation: true }); + const model = doc.parseResult.value as Model; + return model; +} diff --git a/JSTQL/src/language/jstql-validator.ts b/JSTQL/src/language/jstql-validator.ts index 3f580b9..e1b94cc 100644 --- a/JSTQL/src/language/jstql-validator.ts +++ b/JSTQL/src/language/jstql-validator.ts @@ -9,7 +9,7 @@ export function registerValidationChecks(services: JstqlServices) { const registry = services.validation.ValidationRegistry; const validator = services.validation.JstqlValidator; const checks: ValidationChecks = { - Pair: validator.validateWildcardAplTo, + Pair: validator.validateWildcards, }; registry.register(checks, validator); } @@ -18,28 +18,30 @@ export function registerValidationChecks(services: JstqlServices) { * Implementation of custom validations. */ export class JstqlValidator { - validateWildcardAplTo(pair: Pair, accept: ValidationAcceptor): void { - let validationResultAplTo = validateWildcardAplTo( - collectWildcard(pair.aplTo.apl_to_code.split("")) - ); - if (validationResultAplTo.errors.length != 0) { - accept("error", validationResultAplTo.errors.join("\n"), { - node: pair.aplTo, - property: "apl_to_code", - }); - } + validateWildcards(pair: Pair, accept: ValidationAcceptor): void { + try { + let validationResultAplTo = validateWildcardAplTo( + collectWildcard(pair.aplTo.apl_to_code.split("")) + ); + if (validationResultAplTo.errors.length != 0) { + accept("error", validationResultAplTo.errors.join("\n"), { + node: pair.aplTo, + property: "apl_to_code", + }); + } - let validationResultTraTo = validateWildcardTraTo( - collectWildcard(pair.traTo.transform_to_code.split("")), - validationResultAplTo.env - ); + let validationResultTraTo = validateWildcardTraTo( + collectWildcard(pair.traTo.transform_to_code.split("")), + validationResultAplTo.env + ); - if (validationResultTraTo.length != 0) { - accept("error", validationResultTraTo.join("\n"), { - node: pair.traTo, - property: "transform_to_code", - }); - } + if (validationResultTraTo.length != 0) { + accept("error", validationResultTraTo.join("\n"), { + node: pair.traTo, + property: "transform_to_code", + }); + } + } catch (e) {} } } @@ -99,26 +101,5 @@ function collectWildcard(code: string[]): string[] { i += 1; } } - console.log(wildcards); return wildcards; } -function testValidator() { - let res = validateWildcardAplTo( - collectWildcard( - `() => { - <> - return << returnExpr: Expr >> - }`.split("") - ) - ); - console.log(res); - let res2 = validateWildcardTraTo( - collectWildcard( - `<< blockStatements >> - << returnExpr >>`.split("") - ), - res.env - ); - console.log(res2); -} -testValidator(); diff --git a/dsl_files/test.jstql b/dsl_files/do.jstql similarity index 86% rename from dsl_files/test.jstql rename to dsl_files/do.jstql index 08b00c1..c0ab91a 100644 --- a/dsl_files/test.jstql +++ b/dsl_files/do.jstql @@ -2,13 +2,13 @@ proposal DoExpression{ pair arrowFunction{ applicable to { "() => { - <> + <> return << returnExpr: Expr >> }" } transform to { "do { - << blockStatementsss >> + << blockStatements >> << returnExpr >> }" } diff --git a/dsl_files/pipeline.jstql b/dsl_files/pipeline.jstql new file mode 100644 index 0000000..df9ffc2 --- /dev/null +++ b/dsl_files/pipeline.jstql @@ -0,0 +1,24 @@ +proposal Pipeline{ + pair SingleArgument { + applicable to { + "<>(<>);" + } + + transform to { + "<> |> <>(%);" + } + } + + pair MultiArgument { + applicable to { + "<>( + <>, + <> + );" + } + + transform to { + "<> |> <>(%, <>);" + } + } +} \ No newline at end of file diff --git a/output.js b/output.js new file mode 100644 index 0000000..b63ce39 --- /dev/null +++ b/output.js @@ -0,0 +1,90 @@ +function parse() { + const input = ("input" |> document.getElementById(%)).value; + const data = 32 |> input.slice(%); + const compressedData = data |> decode_base64(%); + const uncompressed = compressedData |> pako.inflate(%); + const json = uncompressed |> JSON.parse(%); + json |> console.log(%); + json |> convertToDesktop(%); +} +function convertToDesktop(json) { + const newValues = { + crb: false, + newClanRaidClassId: 0, + newClanRaidClassLevel: 0, + pendingImmortalSouls: 0, + pendingRaidRubies: 0, + immortalSouls: 0, + lastPurchaseTime: 0, + lastRaidAttemptTimestamp: 0, + lastRaidRewardCheckTimestamp: 0, + shouldShowHZERoster: false, + lastBonusRewardCheckTimestamp: 0 + }; + const mappedValues = { + rubies: json.rubies / 10 |> Math.round(%) + }; + const pcSpecificValues = { + readPatchNumber: "1.0e12", + saveOrigin: "pc" + }; + const hash = "7a990d405d2c6fb93aa8fbb0ec1a3b23"; + const newData = { + ...newValues, + ...json, + ...mappedValues, + ...pcSpecificValues + }; + const compressed = newData |> JSON.stringify(%) |> pako.deflate(%); + const base64 = compressed |> btoa(%); + const finalSaveString = hash + base64; + ("output_output" |> document.getElementById(%)).innerText = finalSaveString; + showOutput(); +} +function showOutput() { + ("outputs" |> document.getElementById(%)).style.visibility = "visible"; +} +function copyOutput() { + const output = "output_output" |> document.getElementById(%); + output.disabled = false; + output.focus(); + output.select(); + "copy" |> document.execCommand(%); + output.disabled = true; + const successElement = "copy_success_msg" |> document.getElementById(%); + successElement.style.visibility = "visible"; + setTimeout(() => successElement.style.visibility = "hidden", 4000); +} +function decode_base64(s) { + let e = {}, + i, + k, + v = [], + r = "", + w = String.fromCharCode; + let n = [[65, 91], [97, 123], [48, 58], [43, 44], [47, 48]]; + for (z in n) { + for (i = n[z][0]; i < n[z][1]; i++) { + i |> w(%) |> v.push(%); + } + } + for (i = 0; i < 64; i++) { + e[v[i]] = i; + } + for (i = 0; i < s.length; i += 72) { + let b = 0, + c, + x, + l = 0, + o = i |> s.substring(%); + for (x = 0; x < o.length; x++) { + c = e[x |> o.charAt(%)]; + b = (b << 6) + c; + l += 6; + while (l >= 8) { + r += (b >>> (l -= 8)) % 256 |> w(%); + } + } + } + return r; +} \ No newline at end of file diff --git a/src/data_structures/tree.ts b/src/data_structures/tree.ts index 283e6e6..b830cca 100644 --- a/src/data_structures/tree.ts +++ b/src/data_structures/tree.ts @@ -24,9 +24,6 @@ export const makeTree = ( 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 @@ -43,12 +40,8 @@ export const makeTree = ( } }, }); - //console.log(first.children); - if (first != null) { return first; - } else { - return undefined; } }; export const showTree = (tree: TreeNode, idents: number = 0) => { diff --git a/src/index.ts b/src/index.ts index 01ee466..ee28f7a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -139,16 +139,16 @@ const main = async () => { //transform(selfHostedTransformExampleMultiStmt, codeFromFile); const jstql_file = - "/home/rolfmg/Coding/Master/didactic-chainsaw/dsl_files/test_hello.jstl"; + "/home/rolfmg/Coding/Master/didactic-chainsaw/dsl_files/pipeline.jstql"; const test_file = Bun.file(jstql_file); const test_JSTQL = await test_file.text(); - console.log(parseJSTQL(test_JSTQL)); - /* - await Bun.write( - "../output.js", - transform(selfHostedTransformExampleMultiStmt, codeFromFile) - ); + let proposals = await parseJSTQL(test_JSTQL); + await Bun.write( + "output.js", + transform(proposals[0].pairs[0], codeFromFile) + ); + /* console.log( transform(selfHostedTransformExampleMultiStmt, selfHostedTestMultiStmt) ); diff --git a/src/langium/langiumRunner.ts b/src/langium/langiumRunner.ts index aeea398..80ab1e5 100644 --- a/src/langium/langiumRunner.ts +++ b/src/langium/langiumRunner.ts @@ -1,5 +1,19 @@ -import { TransformRecipe } from "../transform/transform"; +import { TransformRecipe, Proposal as LocalProp } from "../transform/transform"; +import { parseDSLtoAST } from "../../JSTQL/src/JSTQL_interface/api"; +import { Model, Proposal } from "../../JSTQL/src/language/generated/ast"; -export function parseJSTQL(jstql: string): TransformRecipe { - console.log((await parseDSLtoAST(test_JSTQL)).proposals[0].code); +export async function parseJSTQL(jstql: string): Promise { + let model: Model = await parseDSLtoAST(jstql); + let proposals: LocalProp[] = []; + for (let proposal of model.proposals) { + let pairs: TransformRecipe[] = []; + for (let pair of proposal.pair) { + pairs.push({ + applicableTo: pair.aplTo.apl_to_code, + transformTo: pair.traTo.transform_to_code, + }); + } + proposals.push({ pairs }); + } + return proposals; } diff --git a/src/matcher/matcher.ts b/src/matcher/matcher.ts index 8b347ca..452aefa 100644 --- a/src/matcher/matcher.ts +++ b/src/matcher/matcher.ts @@ -97,9 +97,7 @@ export class Matcher { } // This is broken - multiStatementMatcher(code: TreeNode, aplTo: TreeNode) { - console.log("Currently unsupported"); - } + multiStatementMatcher(code: TreeNode, aplTo: TreeNode) {} match(code: TreeNode, aplTo: TreeNode) {} private checkCodeNode(code_node: t.Node, aplTo: t.Node): boolean { @@ -139,27 +137,4 @@ export class Matcher { return true; } - - private buildPairTree( - code: TreeNode, - aplTo: TreeNode - ): TreeNode { - let temp: TreeNode = new TreeNode(null, { - codeNode: code.element, - aplToNode: aplTo.element, - }); - if (code.children.length >= aplTo.children.length) { - for (let i = 0; i < aplTo.children.length; i++) { - let child = this.buildPairTree( - code.children[i], - aplTo.children[i] - ); - temp.children.push(child); - } - } else { - console.log("ERROR"); - } - - return temp; - } } diff --git a/src/parser/parse.ts b/src/parser/parse.ts index aaebf91..de42ff8 100644 --- a/src/parser/parse.ts +++ b/src/parser/parse.ts @@ -62,7 +62,7 @@ function parseInternalString(dslString: string): { return { identifier, - types: typeString.length > 0 ? typeString.split("|") : [""], + types: typeString ? typeString.split("|") : [""], }; } @@ -73,10 +73,3 @@ export function parse_with_plugins( plugins: [["pipelineOperator", { proposal: "hack", topicToken: "%" }]], }); } - -function testParseInternal() { - parseInternal(` - <>(<< b : Identifier | MemberExpression >>); - `); -} -testParseInternal(); diff --git a/src/transform/transform.ts b/src/transform/transform.ts index 2ebbca0..0c68bf8 100644 --- a/src/transform/transform.ts +++ b/src/transform/transform.ts @@ -1,7 +1,11 @@ import traverse from "@babel/traverse"; import * as t from "@babel/types"; import generate from "@babel/generator"; -import { parseInternal, parse_with_plugins } from "../parser/parse"; +import { + InternalDSLVariable, + parseInternal, + parse_with_plugins, +} from "../parser/parse"; import { TreeNode, makeTree, @@ -11,6 +15,11 @@ import { import { runMatch } from "../matcher/matcher"; import { transformMatch, transformer } from "./transformMatch"; import { preludeBuilder } from "../parser/preludeBuilder"; + +export interface Proposal { + pairs: TransformRecipe[]; +} + export interface TransformRecipe { applicableTo: string; transformTo: string; @@ -21,16 +30,39 @@ export interface SelfHostedRecipe extends TransformRecipe { export function transform(recipe: TransformRecipe, code: string) { if ((recipe).prelude !== undefined) { // We are using the self hosted version - return transformSelfHosted(recipe, code); + return transformSelfHosted( + { + applicableTo: recipe.applicableTo, + transformTo: recipe.transformTo, + }, + preludeBuilder((recipe as SelfHostedRecipe).prelude), + code + ); } else { // We are using JSTQL - return transformJSTQL(recipe, code); + // We have to parse JSTQL to the self hosted version + + let { cleanedJS: applicableTo, prelude } = parseInternal( + recipe.applicableTo + ); + let { cleanedJS: transformTo, prelude: _ } = parseInternal( + recipe.transformTo + ); + + return transformSelfHosted( + { applicableTo, transformTo }, + prelude, + code + ); } } -function transformSelfHosted(recipe: SelfHostedRecipe, code: string) { - let internals = preludeBuilder(recipe.prelude); - +function transformSelfHosted( + recipe: TransformRecipe, + internals: InternalDSLVariable, + code: string +) { + console.log(recipe); let codeAST = parse_with_plugins(code); let codeTree = makeTree(codeAST); let applicabelToAST = parse_with_plugins(recipe.applicableTo); @@ -54,8 +86,7 @@ function transformSelfHosted(recipe: SelfHostedRecipe, code: string) { console.log(generate(match.element.codeNode).code); } console.log(matches.length); - return; - for (let match of matches) { + for (let match of matches.reverse()) { //console.log(transformToTree.element); let output = structuredClone(transformToTree.element); try { @@ -63,56 +94,6 @@ function transformSelfHosted(recipe: SelfHostedRecipe, code: string) { } 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; -} - -function transformJSTQL(recipe: TransformRecipe, code: string) { - let { prelude, cleanedJS } = parseInternal(recipe.applicableTo); - let codeAST = parse_with_plugins(code); - let codeTree = makeTree(codeAST); - let applicabelToAST = parse_with_plugins(cleanedJS); - console.dir(applicabelToAST, { depth: null }); - let applicableToTree = makeTree(applicabelToAST); - let transformTo = parse_with_plugins(recipe.transformTo); - let transformToTree = makeTree(transformTo); - - if ( - codeTree == undefined || - applicableToTree == undefined || - transformToTree == undefined - ) { - throw new Error("This no worky LOL"); - } - - let matches = runMatch(codeTree, applicableToTree, prelude); - - 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"); diff --git a/src/transform/transformMatch.ts b/src/transform/transformMatch.ts index afe2e98..73fb744 100644 --- a/src/transform/transformMatch.ts +++ b/src/transform/transformMatch.ts @@ -9,31 +9,28 @@ import { showTreePaired, } from "../data_structures/tree"; import { InternalDSLVariable } from "../parser/parse"; -import { Match, MatchedTreeNode, PairedNodes } from "../matcher/matcher"; +import { MatchedTreeNode, PairedNodes } from "../matcher/matcher"; import traverse from "@babel/traverse"; export function transformer( - matches: Match, + match: TreeNode, trnTo: TreeNode, output: t.Node, inputCode: t.Node ) { - for (let match of matches.statements) { - transformMatch(match, trnTo, output); - } + transformMatch(match, trnTo, output); if (output.type == "Program") { output = output.body[0]; } - for (let match of matches.statements) - traverse(inputCode, { - enter(path) { - if (path.node === match.element.codeNode) { - path.replaceWith(output); - } - }, - }); + traverse(inputCode, { + enter(path) { + if (path.node === match.element.codeNode) { + path.replaceWith(output); + } + }, + }); } export function transformMatch( @@ -51,7 +48,7 @@ export function transformMatch( traverse(output, { enter(path) { if (path.isIdentifier({ name: trnTo.element.name })) { - //console.log(match.element.codeNode); + console.log(match.element.codeNode); if (match.element.codeNode) { path.replaceWith(match.element.codeNode); }