diff --git a/apps/color-buddy/src/linting/Eval.svelte b/apps/color-buddy/src/linting/Eval.svelte new file mode 100644 index 0000000..65b34a0 --- /dev/null +++ b/apps/color-buddy/src/linting/Eval.svelte @@ -0,0 +1,206 @@ + + +{#if displayMode === "check-customization"} + refreshLints()} /> +{/if} +
+
+ +
+ +
+
+ {#if numIgnored > 0} + + {/if} + +
+ This collection of checks validates whether or not your palette matches a + number of commonly held beliefs about best practices. They wont fit every + situation! So feel free to turn some off. +
+ +
+
+
+
+
+ +
+ {#each groupNames as lintGroup} +
+
+ +
+
+ Logo for {lintGroup} +
+
{lintGroupNames[lintGroup]}
+
+ +
+ {#if (lintGroups[lintGroup] || []).every((x) => !evalConfig[x.lintProgram.name]?.ignore)} + + {/if} + {#if (lintGroups[lintGroup] || []).some((x) => evalConfig[x.lintProgram.name]?.ignore)} + + {/if} +
+
+
+ + {#if !lintGroups[lintGroup].every((x) => x.kind === "ignored" || x.kind === "invalid")} +
+ {#each (lintGroups[lintGroup] || []).sort((a, b) => { + return a.kind === "ignored" ? 1 : -1; + }) as lintResult} + + {/each} +
+ {:else} +
+ All checks in this group are ignored +
+ {/if} + {#if (lintGroups[lintGroup] || []).length === 0 && $lintStore.loadState === "loading"} +
+ Loading +
+ {/if} +
+
+ {/each} +
+
+
+
diff --git a/apps/lil-buddy/netlify.toml b/apps/lil-buddy/netlify.toml index 575289f..fcf6780 100644 --- a/apps/lil-buddy/netlify.toml +++ b/apps/lil-buddy/netlify.toml @@ -2,5 +2,20 @@ command = "yarn workspace lil-buddy build" publish = "apps/lil-buddy/dist" + [dev] -command = "yarn workspace lil-buddy dev" + command = "yarn workspace lil-buddy dev" + targetPort = 5173 + functionsPort = 8081 + envFiles=[".env.development"] + framework = "vite" + +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 + +[[headers]] + for = "/*.json" + [headers.values] + Access-Control-Allow-Origin = "*" \ No newline at end of file diff --git a/apps/lil-buddy/src/components/MonacoEditor.svelte b/apps/lil-buddy/src/components/MonacoEditor.svelte index ede8e6c..774541a 100644 --- a/apps/lil-buddy/src/components/MonacoEditor.svelte +++ b/apps/lil-buddy/src/components/MonacoEditor.svelte @@ -10,6 +10,8 @@ let monaco: typeof Monaco; let editorContainer: HTMLElement; + let isTyping = false; + onMount(async () => { // Import our 'monaco.ts' file here // (onMount() will only be executed in the browser, which is what we want) @@ -24,6 +26,7 @@ const model = monaco.editor.createModel(value, language); editor.setModel(model); editor.onDidChangeModelContent(() => { + // redo this such that there is a debounce onChange(editor.getValue()); }); }); diff --git a/apps/lil-buddy/src/lib/SmallStepEvaluator.test.ts b/apps/lil-buddy/src/lib/SmallStepEvaluator.test.ts index cd3070b..5291f47 100644 --- a/apps/lil-buddy/src/lib/SmallStepEvaluator.test.ts +++ b/apps/lil-buddy/src/lib/SmallStepEvaluator.test.ts @@ -3,6 +3,7 @@ import { makePalFromString } from "color-buddy-palette"; import { GenerateAST } from "color-buddy-palette-lint"; import { generateEvaluations } from "./small-step-evaluator"; +const defaultPal = makePalFromString(["red", "green"]); test("SmallStepEvaluator works", () => { const exampleNode = { ">": { @@ -11,11 +12,10 @@ test("SmallStepEvaluator works", () => { }, }; 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 + { a: defaultPal.colors[0], b: defaultPal.colors[1] }, + defaultPal ); expect(result).toMatchSnapshot(); }); @@ -24,22 +24,21 @@ 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"]) - ); + const result = generateEvaluations(ast, {}, defaultPal); expect(result).toMatchSnapshot(); }); -test.only("SmallStepEvaluator works with small not example", () => { +test("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"]) - ); + const result = generateEvaluations(ast, {}, defaultPal); + expect(result).toMatchSnapshot(); +}); + +test("Agg Test", () => { + const example = { "<": { left: { count: "colors" }, right: 11 } }; + const ast = (GenerateAST(example as any).value as any).children[0] as any; + const result = generateEvaluations(ast, {}, defaultPal); 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 index b36fc93..6565d01 100644 --- a/apps/lil-buddy/src/lib/__snapshots__/SmallStepEvaluator.test.ts.snap +++ b/apps/lil-buddy/src/lib/__snapshots__/SmallStepEvaluator.test.ts.snap @@ -1,5 +1,47 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`Agg Test 1`] = ` +[ + LLExpression { + "nodeType": "expression", + "value": LLPredicate { + "left": LLAggregate { + "children": LLVariable { + "nodeType": "variable", + "value": "colors", + }, + "nodeType": "aggregate", + "type": "count", + }, + "nodeType": "predicate", + "right": LLNumber { + "nodeType": "number", + "value": 11, + }, + "threshold": undefined, + "type": "<", + }, + }, + LLPredicate { + "left": LLNumber { + "nodeType": "number", + "value": 2, + }, + "nodeType": "predicate", + "right": LLNumber { + "nodeType": "number", + "value": 11, + }, + "threshold": undefined, + "type": "<", + }, + LLBool { + "nodeType": "bool", + "value": true, + }, +] +`; + exports[`SmallStepEvaluator works 1`] = ` [ LLExpression { @@ -29,7 +71,7 @@ exports[`SmallStepEvaluator works 1`] = ` "type": ">", }, }, - { + LLPredicate { "left": LLNumber { "nodeType": "number", "value": 70.2365636120491, @@ -55,9 +97,21 @@ exports[`SmallStepEvaluator works with small not example 1`] = ` "nodeType": "expression", "value": LLConjunction { "children": [ - LLBool { - "nodeType": "bool", - "value": true, + LLExpression { + "nodeType": "expression", + "value": LLPredicate { + "left": LLNumber { + "nodeType": "number", + "value": 11, + }, + "nodeType": "predicate", + "right": LLNumber { + "nodeType": "number", + "value": 10, + }, + "threshold": undefined, + "type": ">", + }, }, ], "nodeType": "conjunction", diff --git a/apps/lil-buddy/src/lib/small-step-evaluator.ts b/apps/lil-buddy/src/lib/small-step-evaluator.ts index 9a35c38..6cb6eca 100644 --- a/apps/lil-buddy/src/lib/small-step-evaluator.ts +++ b/apps/lil-buddy/src/lib/small-step-evaluator.ts @@ -9,14 +9,12 @@ export function evaluateNode( 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); + const toVal = (x: Color) => + new LLTypes.LLValue(new LLTypes.LLColor(x, x.toHex())); + const newEnv = Object.entries(inducedVariables).reduce( + (acc, [key, value]) => acc.set(key, toVal(value)), + new Environment(pal, {}, opts, {}) + ); return node.evaluate(newEnv); } @@ -40,6 +38,7 @@ function isValue(node: any) { function subTreeIsPureOp(node: any): boolean { switch (node.nodeType) { case "pairFunction": + case "numberOp": case "predicate": return isValue(node.left) && isValue(node.right); case "conjunction": @@ -47,7 +46,11 @@ function subTreeIsPureOp(node: any): boolean { return isValue(node.children[0]); } return node.children.every((x: any) => subTreeIsPureOp(x)); + case "aggregate": case "array": + if (node.children?.nodeType === "variable") { + return true; + } return node.children.every((x: any) => subTreeIsPureOp(x)); case "node": case "expression": @@ -60,9 +63,7 @@ function subTreeIsPureOp(node: any): boolean { return false; case "valueFunction": return isValue(node.input); - // todo case "quantifier": - case "aggregate": case "map": default: return false; @@ -82,10 +83,10 @@ function traverseAndMaybeExecute( const astResult = LLTypes.LLValue.tryToConstruct(result, {} as any); return { result: astResult, didEval: true }; } - // let updatedNode = node.deepCopy(); - let updatedNode = Object.assign({}, node); + let updatedNode = node.copy(); switch (node.nodeType) { case "pairFunction": + case "numberOp": case "predicate": const leftTraverse = traverseAndMaybeExecute( node.left, @@ -107,16 +108,16 @@ function traverseAndMaybeExecute( } return { result: updatedNode, didEval: false }; case "conjunction": - case "array": + case "aggregate": const newChildren = []; let found = false; - for (let idx = 0; idx < node.children.length; idx++) { + for (let idx = 0; idx < updatedNode.children.length; idx++) { if (found) { - newChildren.push(node.children[idx]); + newChildren.push(updatedNode.children[idx]); continue; } - const child = node.children[idx]; + const child = updatedNode.children[idx]; const childResult = traverseAndMaybeExecute( child, inducedVariables, @@ -125,8 +126,12 @@ function traverseAndMaybeExecute( newChildren.push(childResult.result); found = childResult.didEval; } - node.children = newChildren; - return { result: node, didEval: found }; + // let found = false + // updatedNode.children = updatedNode.children.map(x => { + + // }) + updatedNode.children = newChildren; + return { result: updatedNode, didEval: found }; case "node": case "expression": return traverseAndMaybeExecute(node.value, inducedVariables, pal); @@ -147,7 +152,8 @@ function traverseAndMaybeExecute( } case "quantifier": - case "aggregate": + throw new Error("Quantifiers should not be evaluated here", node); + case "map": default: console.log(node.nodeType, " not implemented yet", node); @@ -160,9 +166,8 @@ export function generateEvaluations( inducedVariables: Record, pal: Palette ): LLNode[] { - const evalLog = [node]; - // let currentNode = node.deepCopy(); - let currentNode = Object.assign({}, node); + const evalLog = [node.copy()]; + let currentNode = node.copy(); while (!subTreeIsPureOp(currentNode)) { const result = traverseAndMaybeExecute(currentNode, inducedVariables, pal); evalLog.push(result.result); diff --git a/apps/lil-buddy/src/linting/FocusedTest.svelte b/apps/lil-buddy/src/linting/FocusedTest.svelte index 8874d8f..a09a2fa 100644 --- a/apps/lil-buddy/src/linting/FocusedTest.svelte +++ b/apps/lil-buddy/src/linting/FocusedTest.svelte @@ -1,6 +1,5 @@ {#if testPal} diff --git a/apps/lil-buddy/src/linting/VisualSummarizer.svelte b/apps/lil-buddy/src/linting/VisualSummarizer.svelte index 5d94132..4c97aae 100644 --- a/apps/lil-buddy/src/linting/VisualSummarizer.svelte +++ b/apps/lil-buddy/src/linting/VisualSummarizer.svelte @@ -1,27 +1,25 @@ - - -{#if error} -
{error}
-{:else} - -{/if} +
+ {#if error} +
{error}
+ {:else} + + {/if} +
diff --git a/apps/lil-buddy/src/linting/summary-nodes/DispatchNode.svelte b/apps/lil-buddy/src/linting/summary-nodes/DispatchNode.svelte index 1b6903d..e2a9fa0 100644 --- a/apps/lil-buddy/src/linting/summary-nodes/DispatchNode.svelte +++ b/apps/lil-buddy/src/linting/summary-nodes/DispatchNode.svelte @@ -1,30 +1,35 @@ -{#if node.nodeType == "conjunction"} +{#if node.nodeType == "conjunction" && !isNotNode} {#each node.children as child} {/each} @@ -37,6 +42,6 @@ {/each} -{:else} +{:else if node.nodeType === "quantifier"} {/if} diff --git a/apps/lil-buddy/src/linting/summary-nodes/InlineNode.svelte b/apps/lil-buddy/src/linting/summary-nodes/InlineNode.svelte index a41248a..ff0b9ca 100644 --- a/apps/lil-buddy/src/linting/summary-nodes/InlineNode.svelte +++ b/apps/lil-buddy/src/linting/summary-nodes/InlineNode.svelte @@ -4,7 +4,6 @@ export let node: any; export let pal: Palette; export let inducedVariables: Record = {}; - $: console.log("asd", node);
@@ -17,8 +16,9 @@
{:else if node.nodeType === "conjunction" && node.type === "not"} -
- NOT +
+ NOT +
{:else if node.nodeType === "number"}
{node.value}
@@ -28,6 +28,13 @@ class="h-5 w-5 rounded-full" style={`background: ${inducedVariables[node.value].toHex()}`} /> + {:else if node.value === "colors"} + {#each pal.colors as color} +
+ {/each} {:else}
{node.value}
{/if} @@ -47,9 +54,35 @@ {")"}
- {:else if node.type === "color"} -
hi
+ {:else if node.nodeType === "color"} + + {#if node.value.channels["L"] === 0 && node.value.channels["a"] === 0 && node.value.channels["b"] === 0} + {node.constructorString} + {:else} +
+ {/if} {:else if node.nodeType === "bool"}
{node.value}
+ {:else if node.nodeType === "aggregate"} +
+ {node.type} + {"("} + {#if node.children?.nodeType === "variable"} + + {:else} + {#each node.children as child} + + {#if child !== node.children[node.children.length - 1]} + {","} + {/if} + {/each} + {/if} + {")"} +
+ {:else if node.nodeType === "expression"} + {/if}
diff --git a/apps/lil-buddy/src/linting/summary-nodes/QuantifierNode.svelte b/apps/lil-buddy/src/linting/summary-nodes/QuantifierNode.svelte index e66c052..543da24 100644 --- a/apps/lil-buddy/src/linting/summary-nodes/QuantifierNode.svelte +++ b/apps/lil-buddy/src/linting/summary-nodes/QuantifierNode.svelte @@ -5,8 +5,7 @@ import DispatchNode from "./DispatchNode.svelte"; import { evaluateNode } from "../../lib/small-step-evaluator"; - type LLNode = InstanceType<(typeof LLTypes)["LLNode"]>; - export let node: LLNode; + export let node: any; export let pal: Palette; export let inducedVariables: Record = {}; @@ -20,22 +19,29 @@ typeof node.input.children?.at(0)?.constructorString ) { return makePalFromString( - node.input.children.map((x) => x.constructorString) + node.input.children.map((x: any) => x.constructorString) ).colors; } return []; } $: nodeResult = evaluateNode(node, inducedVariables, pal); - - + function checkWhere(node: any, color: Color, varb: string): boolean { + if (!node) return true; + const result = evaluateNode( + node, + { ...inducedVariables, [varb]: color }, + pal + ); + return result.result; + } +
-
+
{node.type} - {node.varbs}
- {#each values as color}
@@ -52,14 +58,19 @@
- + {#if checkWhere(node.where, color, node.varbs[0])} + + {:else} +
+ removed by where + {/if}
@@ -67,33 +78,5 @@
→{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 259fc88..40e0acb 100644 --- a/packages/palette-lint/src/lint-language/lint-language.ts +++ b/packages/palette-lint/src/lint-language/lint-language.ts @@ -161,6 +161,10 @@ export class LLNode { toString() { return "Node"; } + copy() { + console.log("asd", this); + return new LLNode(); + } } export class LLExpression extends LLNode { @@ -185,6 +189,9 @@ export class LLExpression extends LLNode { toString(): string { return this.value.toString(); } + copy() { + return new LLExpression(this.value); + } } const ConjunctionTypes = ["and", "or", "not", "none", "id"] as const; @@ -241,6 +248,9 @@ export class LLConjunction extends LLNode { if (childrenTypes.some((x) => x === false)) return false; return new LLConjunction(exprType, childrenTypes); } + copy() { + return new LLConjunction(this.type, this.children); + } toString(): string { if (this.type === "id") return this.children[0].toString(); if (this.type === "none") return ""; @@ -267,6 +277,9 @@ export class LLValueArray extends LLNode { if (childrenTypes.some((x) => x === false)) return false; return new LLValueArray(childrenTypes); } + copy() { + return new LLValueArray(this.children); + } toString(): string { return `[${this.children.map((x) => x.toString()).join(", ")}]`; } @@ -285,6 +298,9 @@ export class LLBool extends LLNode { if (typeof value !== "boolean") return false; return new LLBool(value); } + copy() { + return new LLBool(this.value); + } toString(): string { return this.value ? "TRUE" : "FALSE"; } @@ -304,6 +320,9 @@ export class LLVariable extends LLNode { if (typeof value !== "string") return false; return new LLVariable(value); } + copy() { + return new LLVariable(this.value); + } toString(): string { return this.value; } @@ -333,6 +352,9 @@ export class LLColor extends LLNode { } return false; } + copy() { + return new LLColor(this.value, this.constructorString); + } toString(): string { return this.constructorString; // return this.value.toHex(); @@ -352,6 +374,9 @@ export class LLNumber extends LLNode { if (typeof value !== "number") return false; return new LLNumber(value); } + copy() { + return new LLNumber(this.value); + } toString(): string { return this.value.toString(); } @@ -402,6 +427,9 @@ export class LLNumberOp extends LLNode { } return new LLNumberOp(opType, leftType, rightType); } + copy() { + return new LLNumberOp(this.type, this.left, this.right); + } toString(): string { let left = this.left.toString(); let right = this.right.toString(); @@ -542,6 +570,9 @@ export class LLPredicate extends LLNode { if (!leftType || !rightType) return false; return new LLPredicate(predicateType, leftType, rightType, threshold); } + copy() { + return new LLPredicate(this.type, this.left, this.right, this.threshold); + } toString(): string { let type = "" + this.type; const left = this.left.toString(); @@ -586,6 +617,9 @@ export class LLValue extends LLNode { if (!value) return false; return value; } + copy() { + return new LLValue(this.value); + } toString(): string { return this.value.toString(); } @@ -677,6 +711,9 @@ export class LLValueFunction extends LLNode { const params = getParams(op, node); return new LLValueFunction(op.primaryKey, input, params); } + copy() { + return new LLValueFunction(this.type, this.input, this.params); + } toString(): string { const params = Object.values(this.params).join(", "); const paramString = params.length ? `, ${params}` : ""; @@ -897,6 +934,15 @@ export class LLQuantifier extends LLNode { where && tryTypes([LLPredicate, LLValueFunction], options)(where) ); } + copy() { + return new LLQuantifier( + this.type, + this.input, + this.predicate, + this.varbs, + this.where + ); + } toString(): string { const varbs = this.varbs.join(", "); let target = this.input.toString(); @@ -994,6 +1040,9 @@ export class LLAggregate extends LLNode { } return new LLAggregate(reduceType, childType); } + copy() { + return new LLAggregate(this.type, this.children); + } toString(): string { return `${this.type}(${this.children.toString()})`; } @@ -1104,6 +1153,9 @@ export class LLMap extends LLNode { } return new LLMap(op, childType, func, varb); } + copy() { + return new LLMap(this.type, this.children, this.func, this.varb); + } toString(): string { const type = this.type; const funcStr = this.func.toString();