Skip to content

Commit

Permalink
get visual explainer mostly running
Browse files Browse the repository at this point in the history
  • Loading branch information
mcnuttandrew committed Jan 17, 2025
1 parent 91380b9 commit f696bb6
Show file tree
Hide file tree
Showing 14 changed files with 779 additions and 40 deletions.
6 changes: 6 additions & 0 deletions apps/lil-buddy/netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[build]
command = "yarn workspace lil-buddy build"
publish = "apps/lil-buddy/dist"

[dev]
command = "yarn workspace lil-buddy dev"
1 change: 0 additions & 1 deletion apps/lil-buddy/src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
45 changes: 45 additions & 0 deletions apps/lil-buddy/src/lib/SmallStepEvaluator.test.ts
Original file line number Diff line number Diff line change
@@ -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();
});
107 changes: 107 additions & 0 deletions apps/lil-buddy/src/lib/__snapshots__/SmallStepEvaluator.test.ts.snap
Original file line number Diff line number Diff line change
@@ -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,
},
]
`;
175 changes: 175 additions & 0 deletions apps/lil-buddy/src/lib/small-step-evaluator.ts
Original file line number Diff line number Diff line change
@@ -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<string, Color>,
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<string, Color>,
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<string, Color>,
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;
}
5 changes: 5 additions & 0 deletions apps/lil-buddy/src/linting/FocusedTest.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -33,8 +34,12 @@
$: blameData = (lintResult.kind === "success" && lintResult.blameData) || [];
$: errors = lintRun.errors;
$: pairData = blameData as number[][];
$: console.log(lint);
</script>

{#if testPal}
<VisualSummarizer lint={lint.program} pal={testPal} />
{/if}
{#if currentLintAppliesToCurrentPalette && testPal}
<PalPreview pal={{ ...testPal }} />
{#if lintResult.kind === "success" && lintResult.passes}
Expand Down
4 changes: 2 additions & 2 deletions apps/lil-buddy/src/linting/MainTab.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script lang="ts">
import { PREBUILT_LINTS, linter } from "color-buddy-palette-lint";
import { PREBUILT_LINTS } from "color-buddy-palette-lint";
import store from "../stores/store";
import { newId, runLint } from "../lib/utils";
import { newId } from "../lib/utils";
import { buttonStyle } from "../lib/styles";
import LintMeta from "./LintMeta.svelte";
Expand Down
Loading

0 comments on commit f696bb6

Please sign in to comment.