From f696bb68e0fdaa981c0268ddcbb3203ba846cfcd Mon Sep 17 00:00:00 2001 From: Andrew Michael McNutt Date: Fri, 17 Jan 2025 12:48:07 -0700 Subject: [PATCH] get visual explainer mostly running --- apps/lil-buddy/netlify.toml | 6 + apps/lil-buddy/src/app.css | 1 - .../src/lib/SmallStepEvaluator.test.ts | 45 +++++ .../SmallStepEvaluator.test.ts.snap | 107 +++++++++++ .../lil-buddy/src/lib/small-step-evaluator.ts | 175 ++++++++++++++++++ apps/lil-buddy/src/linting/FocusedTest.svelte | 5 + apps/lil-buddy/src/linting/MainTab.svelte | 4 +- .../src/linting/VisualSummarizer.svelte | 27 +++ .../src/linting/VisualSummaryNode.svelte | 124 +++++++++++++ .../linting/summary-nodes/DispatchNode.svelte | 42 +++++ .../linting/summary-nodes/InlineNode.svelte | 55 ++++++ .../summary-nodes/QuantifierNode.svelte | 99 ++++++++++ .../src/lint-language/lint-language.ts | 114 ++++++++---- packages/palette-lint/src/main.ts | 15 +- 14 files changed, 779 insertions(+), 40 deletions(-) create mode 100644 apps/lil-buddy/netlify.toml create mode 100644 apps/lil-buddy/src/lib/SmallStepEvaluator.test.ts create mode 100644 apps/lil-buddy/src/lib/__snapshots__/SmallStepEvaluator.test.ts.snap create mode 100644 apps/lil-buddy/src/lib/small-step-evaluator.ts create mode 100644 apps/lil-buddy/src/linting/VisualSummarizer.svelte create mode 100644 apps/lil-buddy/src/linting/VisualSummaryNode.svelte create mode 100644 apps/lil-buddy/src/linting/summary-nodes/DispatchNode.svelte create mode 100644 apps/lil-buddy/src/linting/summary-nodes/InlineNode.svelte create mode 100644 apps/lil-buddy/src/linting/summary-nodes/QuantifierNode.svelte diff --git a/apps/lil-buddy/netlify.toml b/apps/lil-buddy/netlify.toml new file mode 100644 index 00000000..575289fb --- /dev/null +++ b/apps/lil-buddy/netlify.toml @@ -0,0 +1,6 @@ +[build] +command = "yarn workspace lil-buddy build" +publish = "apps/lil-buddy/dist" + +[dev] +command = "yarn workspace lil-buddy dev" diff --git a/apps/lil-buddy/src/app.css b/apps/lil-buddy/src/app.css index 3edb8425..c36b43e7 100644 --- a/apps/lil-buddy/src/app.css +++ b/apps/lil-buddy/src/app.css @@ -4,7 +4,6 @@ html, body, #app, main { height: 100%; - overflow: hidden; /* font-family: SF Pro Display, SF Pro Text, -apple-system, BlinkMacSystemFont, Roboto, Helvetica Neue, sans-serif !important */ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; } diff --git a/apps/lil-buddy/src/lib/SmallStepEvaluator.test.ts b/apps/lil-buddy/src/lib/SmallStepEvaluator.test.ts new file mode 100644 index 00000000..cd3070bc --- /dev/null +++ b/apps/lil-buddy/src/lib/SmallStepEvaluator.test.ts @@ -0,0 +1,45 @@ +import { expect, test } from "vitest"; +import { makePalFromString } from "color-buddy-palette"; +import { GenerateAST } from "color-buddy-palette-lint"; +import { generateEvaluations } from "./small-step-evaluator"; + +test("SmallStepEvaluator works", () => { + const exampleNode = { + ">": { + left: { deltaE: { left: "a", right: "b" }, algorithm: "2000" }, + right: 10, + }, + }; + const ast = (GenerateAST(exampleNode as any).value as any).children[0] as any; + const pal = makePalFromString(["red", "green"]); + const result = generateEvaluations( + ast, + { a: pal.colors[0], b: pal.colors[1] }, + pal + ); + expect(result).toMatchSnapshot(); +}); + +test("SmallStepEvaluator works with smaller example", () => { + const smallExampleNode = { ">": { left: 11, right: 10 } }; + const ast = (GenerateAST(smallExampleNode as any).value as any) + .children[0] as any; + const result = generateEvaluations( + ast, + {}, + makePalFromString(["red", "green"]) + ); + expect(result).toMatchSnapshot(); +}); + +test.only("SmallStepEvaluator works with small not example", () => { + const smallExampleNode = { not: { ">": { left: 11, right: 10 } } }; + const ast = (GenerateAST(smallExampleNode as any).value as any) + .children[0] as any; + const result = generateEvaluations( + ast, + {}, + makePalFromString(["red", "green"]) + ); + expect(result).toMatchSnapshot(); +}); diff --git a/apps/lil-buddy/src/lib/__snapshots__/SmallStepEvaluator.test.ts.snap b/apps/lil-buddy/src/lib/__snapshots__/SmallStepEvaluator.test.ts.snap new file mode 100644 index 00000000..b36fc932 --- /dev/null +++ b/apps/lil-buddy/src/lib/__snapshots__/SmallStepEvaluator.test.ts.snap @@ -0,0 +1,107 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`SmallStepEvaluator works 1`] = ` +[ + LLExpression { + "nodeType": "expression", + "value": LLPredicate { + "left": LLPairFunction { + "left": LLVariable { + "nodeType": "variable", + "value": "a", + }, + "nodeType": "pairFunction", + "params": { + "algorithm": "2000", + }, + "right": LLVariable { + "nodeType": "variable", + "value": "b", + }, + "type": "deltaE", + }, + "nodeType": "predicate", + "right": LLNumber { + "nodeType": "number", + "value": 10, + }, + "threshold": undefined, + "type": ">", + }, + }, + { + "left": LLNumber { + "nodeType": "number", + "value": 70.2365636120491, + }, + "nodeType": "predicate", + "right": LLNumber { + "nodeType": "number", + "value": 10, + }, + "threshold": undefined, + "type": ">", + }, + LLBool { + "nodeType": "bool", + "value": true, + }, +] +`; + +exports[`SmallStepEvaluator works with small not example 1`] = ` +[ + LLExpression { + "nodeType": "expression", + "value": LLConjunction { + "children": [ + LLBool { + "nodeType": "bool", + "value": true, + }, + ], + "nodeType": "conjunction", + "type": "not", + }, + }, + LLConjunction { + "children": [ + LLBool { + "nodeType": "bool", + "value": true, + }, + ], + "nodeType": "conjunction", + "type": "not", + }, + LLBool { + "nodeType": "bool", + "value": false, + }, +] +`; + +exports[`SmallStepEvaluator works with smaller example 1`] = ` +[ + LLExpression { + "nodeType": "expression", + "value": LLPredicate { + "left": LLNumber { + "nodeType": "number", + "value": 11, + }, + "nodeType": "predicate", + "right": LLNumber { + "nodeType": "number", + "value": 10, + }, + "threshold": undefined, + "type": ">", + }, + }, + LLBool { + "nodeType": "bool", + "value": true, + }, +] +`; diff --git a/apps/lil-buddy/src/lib/small-step-evaluator.ts b/apps/lil-buddy/src/lib/small-step-evaluator.ts new file mode 100644 index 00000000..9a35c382 --- /dev/null +++ b/apps/lil-buddy/src/lib/small-step-evaluator.ts @@ -0,0 +1,175 @@ +import { LLTypes, linter, Environment } from "color-buddy-palette-lint"; +import { Color } from "color-buddy-palette"; +import type { Palette } from "color-buddy-palette"; +type LLNode = InstanceType<(typeof LLTypes)["LLNode"]>; + +export function evaluateNode( + node: any, + inducedVariables: Record, + pal: Palette +) { + const opts = { debugParse: false, debugEval: false, debugCompare: false }; + let newEnv = new Environment(pal, {}, opts, {}); + + newEnv = Object.entries(inducedVariables).reduce((acc, [key, value]) => { + const newVal = new LLTypes.LLValue( + new LLTypes.LLColor(value, value.toHex()) + ); + return acc.set(key, newVal); + }, newEnv); + return node.evaluate(newEnv); +} + +function isValue(node: any) { + switch (node.nodeType) { + case "value": + return true; + case "expression": + case "node": + return isValue(node.value); + case "bool": + case "color": + case "number": + case "variable": + case undefined: + return true; + default: + return false; + } +} +function subTreeIsPureOp(node: any): boolean { + switch (node.nodeType) { + case "pairFunction": + case "predicate": + return isValue(node.left) && isValue(node.right); + case "conjunction": + if (node.type === "not") { + return isValue(node.children[0]); + } + return node.children.every((x: any) => subTreeIsPureOp(x)); + case "array": + return node.children.every((x: any) => subTreeIsPureOp(x)); + case "node": + case "expression": + return subTreeIsPureOp(node.value); + case "bool": + case "color": + case "number": + case "value": + case "variable": + return false; + case "valueFunction": + return isValue(node.input); + // todo + case "quantifier": + case "aggregate": + case "map": + default: + return false; + } +} +function traverseAndMaybeExecute( + node: any, + inducedVariables: Record, + pal: Palette +): { + result: any; + didEval: boolean; +} { + const thisIsPureOp = subTreeIsPureOp(node); + if (thisIsPureOp) { + const result = evaluateNode(node, inducedVariables, pal).result; + const astResult = LLTypes.LLValue.tryToConstruct(result, {} as any); + return { result: astResult, didEval: true }; + } + // let updatedNode = node.deepCopy(); + let updatedNode = Object.assign({}, node); + switch (node.nodeType) { + case "pairFunction": + case "predicate": + const leftTraverse = traverseAndMaybeExecute( + node.left, + inducedVariables, + pal + ); + if (leftTraverse.didEval) { + updatedNode.left = leftTraverse.result; + return { result: updatedNode, didEval: true }; + } + const rightTraverse = traverseAndMaybeExecute( + node.right, + inducedVariables, + pal + ); + if (rightTraverse) { + updatedNode.right = rightTraverse.result; + return { result: updatedNode, didEval: true }; + } + return { result: updatedNode, didEval: false }; + case "conjunction": + + case "array": + const newChildren = []; + let found = false; + for (let idx = 0; idx < node.children.length; idx++) { + if (found) { + newChildren.push(node.children[idx]); + continue; + } + const child = node.children[idx]; + const childResult = traverseAndMaybeExecute( + child, + inducedVariables, + pal + ); + newChildren.push(childResult.result); + found = childResult.didEval; + } + node.children = newChildren; + return { result: node, didEval: found }; + case "node": + case "expression": + return traverseAndMaybeExecute(node.value, inducedVariables, pal); + case "bool": + case "color": + case "number": + case "value": + case "variable": + case undefined: + return { result: node, didEval: false }; + case "valueFunction": + const arg = traverseAndMaybeExecute(node.input, inducedVariables, pal); + if (arg.didEval) { + updatedNode.input = arg.result; + return { result: updatedNode, didEval: true }; + } else { + return { result: updatedNode, didEval: false }; + } + + case "quantifier": + case "aggregate": + case "map": + default: + console.log(node.nodeType, " not implemented yet", node); + throw new Error(`${node.nodeType} not implemented yet`, node); + return { result: node, didEval: false }; + } +} +export function generateEvaluations( + node: LLNode, + inducedVariables: Record, + pal: Palette +): LLNode[] { + const evalLog = [node]; + // let currentNode = node.deepCopy(); + let currentNode = Object.assign({}, node); + while (!subTreeIsPureOp(currentNode)) { + const result = traverseAndMaybeExecute(currentNode, inducedVariables, pal); + evalLog.push(result.result); + currentNode = result.result; + } + const result = evaluateNode(node, inducedVariables, pal).result; + const astResult = LLTypes.LLValue.tryToConstruct(result, {} as any); + evalLog.push(astResult); + return evalLog; +} diff --git a/apps/lil-buddy/src/linting/FocusedTest.svelte b/apps/lil-buddy/src/linting/FocusedTest.svelte index fcc5bb38..8874d8ff 100644 --- a/apps/lil-buddy/src/linting/FocusedTest.svelte +++ b/apps/lil-buddy/src/linting/FocusedTest.svelte @@ -5,6 +5,7 @@ import store from "../stores/store"; export let lint: LintProgram; import { runLint } from "../lib/utils"; + import VisualSummarizer from "./VisualSummarizer.svelte"; $: focusedTest = $store.focusedTest; $: testPal = focusedTest ? focusedTest.type === "passing" @@ -33,8 +34,12 @@ $: blameData = (lintResult.kind === "success" && lintResult.blameData) || []; $: errors = lintRun.errors; $: pairData = blameData as number[][]; + $: console.log(lint); +{#if testPal} + +{/if} {#if currentLintAppliesToCurrentPalette && testPal} {#if lintResult.kind === "success" && lintResult.passes} diff --git a/apps/lil-buddy/src/linting/MainTab.svelte b/apps/lil-buddy/src/linting/MainTab.svelte index faa32ea3..3e46cd1f 100644 --- a/apps/lil-buddy/src/linting/MainTab.svelte +++ b/apps/lil-buddy/src/linting/MainTab.svelte @@ -1,8 +1,8 @@ + + + +{#if error} +
{error}
+{:else} + +{/if} diff --git a/apps/lil-buddy/src/linting/VisualSummaryNode.svelte b/apps/lil-buddy/src/linting/VisualSummaryNode.svelte new file mode 100644 index 00000000..1fd769bc --- /dev/null +++ b/apps/lil-buddy/src/linting/VisualSummaryNode.svelte @@ -0,0 +1,124 @@ + + + + +{#if node.nodeType === "conjunction"} + +{:else if node.nodeType === "quantifier"} +
+
{node.type} - {node.varbs}
+
+ + {#if node.input.value === "colors"} + {#each pal.colors as color} +
+
+ {#each Object.values(inducedVariables) as innerColor} +
+ {/each} +
+
+ {#if !isQuantifierAndChildIsQuantifier} +
Predicate
+ {/if} +
+ +
+
+ {/each} + {:else if Array.isArray(node.input.children) && typeof node.input.children?.at(0)?.constructorString} + {#each makePalFromString(node.input.children.map((x) => x.constructorString)).colors as color} +
+
+ {#each Object.values(inducedVariables) as innerColor} +
+ {/each} +
+
+ {#if isQuantifierAndChildIsQuantifier} +
Predicate
+ {/if} + +
+ {/each} + {/if} +
+
+{:else if node.nodeType === "predicate"} +
+
+
+ +
{node.type}
+ +
+
+{:else if node.nodeType === "number"} +
{node.value}
+{:else if node.nodeType === "variable"} + {#if inducedVariables[node.value]} +
+ {:else} +
{node.value}
+ {/if} +{:else if node.nodeType === "pairFunction"} +
+ {node.type} + {"("} + + {","} + + {")"} +
+{:else if node.type === "color"} +
hi
+{/if} diff --git a/apps/lil-buddy/src/linting/summary-nodes/DispatchNode.svelte b/apps/lil-buddy/src/linting/summary-nodes/DispatchNode.svelte new file mode 100644 index 00000000..1b6903d6 --- /dev/null +++ b/apps/lil-buddy/src/linting/summary-nodes/DispatchNode.svelte @@ -0,0 +1,42 @@ + + +{#if node.nodeType == "conjunction"} + {#each node.children as child} + + {/each} +{:else if node.nodeType === "expression"} + +{:else if predicateNodes.length} + {#each predicateNodes as predicateNode} +
+
+ +
+ {/each} +{:else} + +{/if} diff --git a/apps/lil-buddy/src/linting/summary-nodes/InlineNode.svelte b/apps/lil-buddy/src/linting/summary-nodes/InlineNode.svelte new file mode 100644 index 00000000..a41248a8 --- /dev/null +++ b/apps/lil-buddy/src/linting/summary-nodes/InlineNode.svelte @@ -0,0 +1,55 @@ + + +
+ {#if node.nodeType === "predicate"} +
+
+ +
{node.type}
+ +
+
+ {:else if node.nodeType === "conjunction" && node.type === "not"} +
+ NOT +
+ {:else if node.nodeType === "number"} +
{node.value}
+ {:else if node.nodeType === "variable"} + {#if inducedVariables[node.value]} +
+ {:else} +
{node.value}
+ {/if} + {:else if node.nodeType === "pairFunction"} +
+ {node.type} + {"("} + + {","} + + {")"} +
+ {:else if node.nodeType === "valueFunction"} +
+ {node.type} + {"("} + + {")"} +
+ {:else if node.type === "color"} +
hi
+ {:else if node.nodeType === "bool"} +
{node.value}
+ {/if} +
diff --git a/apps/lil-buddy/src/linting/summary-nodes/QuantifierNode.svelte b/apps/lil-buddy/src/linting/summary-nodes/QuantifierNode.svelte new file mode 100644 index 00000000..e66c052c --- /dev/null +++ b/apps/lil-buddy/src/linting/summary-nodes/QuantifierNode.svelte @@ -0,0 +1,99 @@ + + + + +
+
+
{node.type} - {node.varbs}
+
+ + {#each values as color} +
+
+ {#each Object.values(inducedVariables) as innerColor} +
+ {/each} +
+
+
+
+ +
+
+
+ {/each} +
+
+
→{nodeResult.result}
+
+ diff --git a/packages/palette-lint/src/lint-language/lint-language.ts b/packages/palette-lint/src/lint-language/lint-language.ts index 0727b8d6..259fc884 100644 --- a/packages/palette-lint/src/lint-language/lint-language.ts +++ b/packages/palette-lint/src/lint-language/lint-language.ts @@ -11,10 +11,10 @@ function isColor(x: any): x is Color { } type RawValues = string | number | Color | string[] | number[] | Color[]; -class Environment { +export class Environment { constructor( - private palette: Palette, - private variables: Record, + public palette: Palette, + public variables: Record, public options: OptionsConfig, public colorBlame: Record = {} ) {} @@ -144,6 +144,7 @@ const checkIfValsPresent = (node: Record, keys: string[]) => type ReturnVal = { result: A; env: Environment }; export class LLNode { + nodeType: string = "node"; evaluate(env: Environment): ReturnVal { this.evalCheck(env); throw new Error("Invalid node"); @@ -163,8 +164,9 @@ export class LLNode { } export class LLExpression extends LLNode { + nodeType: string = "expression"; constructor( - private value: LLConjunction | LLPredicate | LLQuantifier | LLBool + public value: LLConjunction | LLPredicate | LLQuantifier | LLBool ) { super(); } @@ -187,9 +189,10 @@ export class LLExpression extends LLNode { const ConjunctionTypes = ["and", "or", "not", "none", "id"] as const; export class LLConjunction extends LLNode { + nodeType: string = "conjunction"; constructor( - private type: (typeof ConjunctionTypes)[number], - private children: LLConjunction[] + public type: (typeof ConjunctionTypes)[number], + public children: LLConjunction[] ) { super(); } @@ -249,7 +252,8 @@ export class LLConjunction extends LLNode { } export class LLValueArray extends LLNode { - constructor(private children: LLValue[]) { + nodeType: string = "array"; + constructor(public children: LLValue[]) { super(); } evaluate(env: Environment): ReturnVal { @@ -269,7 +273,8 @@ export class LLValueArray extends LLNode { } export class LLBool extends LLNode { - constructor(private value: boolean) { + nodeType: string = "bool"; + constructor(public value: boolean) { super(); } evaluate(env: Environment): ReturnVal { @@ -286,7 +291,8 @@ export class LLBool extends LLNode { } export class LLVariable extends LLNode { - constructor(private value: string) { + nodeType: string = "variable"; + constructor(public value: string) { super(); } evaluate(env: Environment): any { @@ -304,9 +310,10 @@ export class LLVariable extends LLNode { } export class LLColor extends LLNode { + nodeType: string = "color"; constructor( - private value: Color, - private constructorString: string + public value: Color, + public constructorString: string ) { super(); } @@ -333,7 +340,8 @@ export class LLColor extends LLNode { } export class LLNumber extends LLNode { - constructor(private value: number) { + nodeType: string = "number"; + constructor(public value: number) { super(); } evaluate(env: Environment): ReturnVal { @@ -351,10 +359,11 @@ export class LLNumber extends LLNode { const LLNumberOpTypes = ["+", "-", "*", "/", "//", "absDiff", "%"] as const; export class LLNumberOp extends LLNode { + nodeType: string = "numberOp"; constructor( - private type: (typeof LLNumberOpTypes)[number], - private left: LLValue, - private right: LLValue + public type: (typeof LLNumberOpTypes)[number], + public left: LLValue, + public right: LLValue ) { super(); } @@ -474,10 +483,11 @@ const getType = (x: any): string => { : typeof x; }; export class LLPredicate extends LLNode { + nodeType: string = "predicate"; constructor( public type: (typeof predicateTypes)[number], - private left: LLValue | LLValueArray | LLMap, - private right: LLValue | LLValueArray | LLMap, + public left: LLValue | LLValueArray | LLMap, + public right: LLValue | LLValueArray | LLMap, public threshold?: number ) { super(); @@ -544,8 +554,9 @@ export class LLPredicate extends LLNode { } export class LLValue extends LLNode { + nodeType: string = "value"; constructor( - private value: + public value: | LLValueFunction | LLPairFunction | LLColor @@ -630,10 +641,11 @@ Object.entries(ColorSpaceDirectory).map(([colorSpace, space]) => { }); export class LLValueFunction extends LLNode { + nodeType: string = "valueFunction"; constructor( - private type: (typeof VFTypes)[number]["primaryKey"], - private input: LLColor | LLVariable, - private params: Record + public type: (typeof VFTypes)[number]["primaryKey"], + public input: LLColor | LLVariable, + public params: Record ) { super(); } @@ -729,11 +741,12 @@ const LLPairFunctionTypes: { }, ]; export class LLPairFunction extends LLNode { + nodeType: string = "pairFunction"; constructor( - private type: (typeof LLPairFunctionTypes)[number]["primaryKey"], - private left: LLColor | LLVariable, - private right: LLColor | LLVariable, - private params: Record + public type: (typeof LLPairFunctionTypes)[number]["primaryKey"], + public left: LLColor | LLVariable, + public right: LLColor | LLVariable, + public params: Record ) { super(); } @@ -785,12 +798,13 @@ let cartesian = (a: any[], b: any[], ...c: any[]): any[] => const QuantifierTypes = ["exist", "all"] as const; const QuantifierTypeErrors = [{ wrong: "exists", right: "exist" }] as const; export class LLQuantifier extends LLNode { + nodeType: string = "quantifier"; constructor( - private type: (typeof QuantifierTypes)[number], - private input: LLValueArray | LLVariable | LLMap, - private predicate: LLPredicate, - private varbs: string[], - private where?: LLPredicate | LLValueFunction + public type: (typeof QuantifierTypes)[number], + public input: LLValueArray | LLVariable | LLMap, + public predicate: LLPredicate, + public varbs: string[], + public where?: LLPredicate | LLValueFunction ) { super(); } @@ -913,9 +927,10 @@ const reduceTypes = [ "extent", ] as const; export class LLAggregate extends LLNode { + nodeType: string = "aggregate"; constructor( - private type: (typeof reduceTypes)[number], - private children: LLValueArray | LLVariable | LLMap + public type: (typeof reduceTypes)[number], + public children: LLValueArray | LLVariable | LLMap ) { super(); } @@ -988,11 +1003,12 @@ const mapTypes = ["map", "filter", "sort", "reverse", "speed"] as const; // example syntax // {map: colors, func: {cvdSim: {type: "protanomaly"}}, varb: "x"} export class LLMap extends LLNode { + nodeType: string = "map"; constructor( - private type: (typeof mapTypes)[number], - private children: LLValueArray | LLVariable | LLMap, - private func: LLValueFunction | LLPairFunction | LLNumberOp, - private varb: string + public type: (typeof mapTypes)[number], + public children: LLValueArray | LLVariable | LLMap, + public func: LLValueFunction | LLPairFunction | LLNumberOp, + public varb: string ) { super(); } @@ -1142,3 +1158,29 @@ export function prettyPrintLL( const ast = parseToAST({ id: [root] }, opts); return ast.toString(); } + +export function GenerateAST( + root: LintProgram, + options: Partial = {} +) { + return parseToAST({ id: [root] }, { ...DEFAULT_OPTIONS, ...options }); +} + +export const LLTypes = { + LLAggregate, + LLBool, + LLColor, + LLConjunction, + LLExpression, + LLMap, + LLNode, + LLNumber, + LLNumberOp, + LLPairFunction, + LLPredicate, + LLQuantifier, + LLValue, + LLValueArray, + LLValueFunction, + LLVariable, +}; diff --git a/packages/palette-lint/src/main.ts b/packages/palette-lint/src/main.ts index 7f89b9a0..41111394 100644 --- a/packages/palette-lint/src/main.ts +++ b/packages/palette-lint/src/main.ts @@ -1,6 +1,19 @@ import linter, { PREBUILT_LINTS } from "./linter"; import type { LintResult, LintProgram } from "./ColorLint"; +import { + GenerateAST, + Environment, + LLTypes, +} from "./lint-language/lint-language"; import { suggestMCFix } from "./linter-tools/monte-carlo-fix"; import { suggestLintFix } from "./linter-tools/lint-fixer"; -export { PREBUILT_LINTS, suggestMCFix, linter, suggestLintFix }; +export { + Environment, + GenerateAST, + LLTypes, + PREBUILT_LINTS, + linter, + suggestLintFix, + suggestMCFix, +}; export type { LintResult, LintProgram };