This commit is contained in:
Rolf Martin Glomsrud 2024-05-12 20:06:37 +02:00
parent 8ddffdf3d7
commit 2f3c35462b
13 changed files with 232 additions and 167 deletions

BIN
JSTQL/JSTQL-0.0.1.vsix Normal file

Binary file not shown.

View file

@ -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<Model> {
var doc: LangiumDocument<AstNode> = 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;
}

View file

@ -9,7 +9,7 @@ export function registerValidationChecks(services: JstqlServices) {
const registry = services.validation.ValidationRegistry;
const validator = services.validation.JstqlValidator;
const checks: ValidationChecks<JstqlAstType> = {
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(
`() => {
<<blockStatements: anyStatementList>>
return << returnExpr: Expr >>
}`.split("")
)
);
console.log(res);
let res2 = validateWildcardTraTo(
collectWildcard(
`<< blockStatements >>
<< returnExpr >>`.split("")
),
res.env
);
console.log(res2);
}
testValidator();

View file

@ -2,13 +2,13 @@ proposal DoExpression{
pair arrowFunction{
applicable to {
"() => {
<<blockStatements: anyStatementList>>
<<blockStatements: anyStatementList | hello >>
return << returnExpr: Expr >>
}"
}
transform to {
"do {
<< blockStatementsss >>
<< blockStatements >>
<< returnExpr >>
}"
}

24
dsl_files/pipeline.jstql Normal file
View file

@ -0,0 +1,24 @@
proposal Pipeline{
pair SingleArgument {
applicable to {
"<<someFunctionIdent:Identifier | MemberExpression>>(<<someFunctionParam: Expression | Identifier>>);"
}
transform to {
"<<someFunctionParam>> |> <<someFunctionIdent>>(%);"
}
}
pair MultiArgument {
applicable to {
"<<someFunctionIdent:Identifier>>(
<<firstFunctionParam : Expression | Identifier>>,
<<restOfFunctionParams: anyRest>>
);"
}
transform to {
"<<firstFunctionParam>> |> <<someFunctionIdent>>(%, <<restOfFunctionParams>>);"
}
}
}

90
output.js Normal file
View file

@ -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;
}

View file

@ -24,9 +24,6 @@ export const makeTree = (
let first: TreeNode<t.Node> | null = null;
traverse(ast, {
enter(path: any) {
//console.log(path.node);
//console.log("Entered: ", path.node.type);
let node: TreeNode<t.Node> = new TreeNode<t.Node>(
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<t.Node>, idents: number = 0) => {

View file

@ -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)
);

View file

@ -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<LocalProp[]> {
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;
}

View file

@ -97,9 +97,7 @@ export class Matcher {
}
// This is broken
multiStatementMatcher(code: TreeNode<t.Node>, aplTo: TreeNode<t.Node>) {
console.log("Currently unsupported");
}
multiStatementMatcher(code: TreeNode<t.Node>, aplTo: TreeNode<t.Node>) {}
match(code: TreeNode<t.Node>, aplTo: TreeNode<t.Node>) {}
private checkCodeNode(code_node: t.Node, aplTo: t.Node): boolean {
@ -139,27 +137,4 @@ export class Matcher {
return true;
}
private buildPairTree(
code: TreeNode<t.Node>,
aplTo: TreeNode<t.Node>
): TreeNode<PairedNodes> {
let temp: TreeNode<PairedNodes> = 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;
}
}

View file

@ -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(`
<<a:Identifier>>(<< b : Identifier | MemberExpression >>);
`);
}
testParseInternal();

View file

@ -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 ((<SelfHostedRecipe>recipe).prelude !== undefined) {
// We are using the self hosted version
return transformSelfHosted(<SelfHostedRecipe>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");

View file

@ -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<PairedNodes>,
trnTo: TreeNode<t.Node>,
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);
}