Skip to content

Commit

Permalink
in the middle of the blame story
Browse files Browse the repository at this point in the history
  • Loading branch information
mcnuttandrew committed Jan 31, 2024
1 parent 3a98254 commit 600e011
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 25 deletions.
23 changes: 23 additions & 0 deletions LintLanguageDocs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Conjunctions:
AND: {and: [EXPR, EXPR, ...]}
OR: {or: [EXPR, EXPR, EXPR]}
NOT: {not: EXPR}

Quantifiers:
FORALL: {all: {value: VARIABLE NAME, where?: PREDICATE, input: VARIABLE NAME | ARRAY}}
EXISTS: {exists: {value: VARIABLE NAME, where?: PREDICATE, input: VARIABLE NAME | ARRAY}}

Comparisons:
equal: {"==": [EXPR, EXPR]}
not equal: {"!=": [EXPR, EXPR]}
less than: {"<": [EXPR, EXPR]}
greater than: {">": [EXPR, EXPR]}

Operations:
sum: {sum: [number, number]} | CAN WE DO VARIABLES FOR THIS?
min: {min: [number, number]} | CAN WE DO VARIABLES FOR THIS?
max: {max: [number, number]} | CAN WE DO VARIABLES FOR THIS?
-- TODO MEAN
toColor: {toColor: variableName, space: 'lab' | 'hsl' | etc, channel: 'a' | 'b' | 'l' | etc}
cvd_sim: {cvd_sim: variableName, type: 'protanomaly' | 'deuteranomaly' | 'tritanopia' | 'grayscale'}
name: {name: variableName}
28 changes: 24 additions & 4 deletions src/lib/LintLanguage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ test("LintLanguage basic eval ", () => {
expect(prettyPrintLL(prog4)).toBe("count(colors) < 10");
});

test("LintLanguage conjunctions", () => {
const prog1 = {
and: [
{ "<": { left: { count: "colors" }, right: 10 } },
{ ">": { left: { count: "colors" }, right: 2 } },
],
};
expect(LLEval(prog1, exampleColors)).toBe(true);
expect(prettyPrintLL(prog1)).toBe("count(colors) < 10 AND count(colors) > 2");

const prog2 = {
or: [
{ "<": { left: { count: "colors" }, right: 2 } },
{ ">": { left: { count: "colors" }, right: 10 } },
],
};
expect(LLEval(prog2, exampleColors)).toBe(false);
expect(prettyPrintLL(prog2)).toBe("count(colors) < 2 OR count(colors) > 10");
});

test("LintLanguage Quantifiers All - Simple", () => {
const simpProg = (colors: string[]) => ({
exist: {
Expand Down Expand Up @@ -276,7 +296,7 @@ test("LintLanguage Name discrimination with a single color", () => {
expect(LLEval(program, [Color.colorFromString("#008137", "lab")])).toBe(true);
});

test("LintLanguage Avoid Extreme Colors", () => {
test.only("LintLanguage Avoid Extreme Colors", () => {
const program = {
all: {
input: "colors",
Expand All @@ -285,7 +305,7 @@ test("LintLanguage Avoid Extreme Colors", () => {
all: {
input: ["#000000", "#ffffff"],
value: "b",
where: { "!=": { left: "a", right: "b" } },
where: { "!=": { left: "index(a)", right: "index(b)" } },
predicate: { "!=": { left: "a", right: "b" } },
},
},
Expand All @@ -294,8 +314,8 @@ test("LintLanguage Avoid Extreme Colors", () => {
expect(prettyPrintLL(program)).toBe(
"all a in (colors), all b in ([#000, #fff]), a != b"
);
expect(LLEval(program, greens)).toBe(false);
expect(LLEval(program, reds)).toBe(true);
expect(LLEval(program, greens)).toBe(true);
expect(LLEval(program, [Color.colorFromString("black", "lab")])).toBe(false);
});

// // YAML VERSION
Expand Down
126 changes: 105 additions & 21 deletions src/lib/lint-language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ class Environment {
constructor(
private colors: Color[],
private variables: Record<string, LLVariable | LLValue | LLValueArray>,
public options: OptionsConfig
public options: OptionsConfig,
public colorBlame: Record<number, boolean> = {}
) {}
set(name: string, value: LLVariable | LLValue | LLValueArray) {
if (name === "colors") {
Expand All @@ -17,7 +18,12 @@ class Environment {
throw new Error("Variable already exists");
}
const newVariables = { ...this.variables, [name]: value };
return new Environment(this.colors, newVariables, this.options);
return new Environment(
this.colors,
newVariables,
this.options,
this.colorBlame
);
}
get(name: string) {
if (name === "colors") {
Expand All @@ -30,8 +36,29 @@ class Environment {
if (!val) throw new Error("Variable not found");
return val;
}
toggleBlame(index: number) {
const newBlame = { ...this.colorBlame, [index]: !this.colorBlame[index] };
console.log("newBlame", newBlame);
return new Environment(this.colors, this.variables, this.options, newBlame);
}
toggleAllBlame() {
const newBlame = this.colors.reduce(
(acc, _, i) => ({ ...acc, [i]: !this.colorBlame[i] }),
{}
);
return new Environment(this.colors, this.variables, this.options, newBlame);
}
mergeBlame(other: Record<number, boolean>) {
const newBlame = { ...this.colorBlame, ...other };
return new Environment(this.colors, this.variables, this.options, newBlame);
}
copy() {
return new Environment(this.colors, { ...this.variables }, this.options);
return new Environment(
this.colors,
{ ...this.variables },
this.options,
this.colorBlame
);
}
}

Expand Down Expand Up @@ -60,7 +87,7 @@ class LLNode {
}
evalCheck(_env: Environment): void {
if (_env.options.debugEval) {
console.log(this.constructor.name, this);
console.log(this.constructor.name);
}
return;
}
Expand Down Expand Up @@ -110,14 +137,36 @@ class LLConjunction extends LLNode {
switch (this.type) {
case "id":
case "and":
let andEnv = env;
const result = children.every((x) => {
const y = x.evaluate(andEnv);
console.log("asd", y.env.colorBlame);
andEnv = andEnv.mergeBlame(y.env.colorBlame);
return y.result;
});
return {
result: children.every((x) => x.evaluate(env).result),
env,
result: result,
env: andEnv,
// result: children.every((x) => {
// const y = x.evaluate(env);
// console.log(y.env);
// return y.result;
// }),
// env,
};
case "or":
return { result: children.some((x) => x.evaluate(env).result), env };
let orEnv = env;
const someResult = children.some((x) => {
const y = x.evaluate(orEnv);
console.log("dsa", y.env.colorBlame);
orEnv = orEnv.mergeBlame(y.env.colorBlame);
return y.result;
});
return { result: someResult, env: orEnv };
// return { result: children.some((x) => x.evaluate(env).result), env };
case "not":
return { result: !children[0].evaluate(env).result, env };
const notResult = children[0].evaluate(env);
return { result: !notResult, env: notResult.env.toggleAllBlame() };
case "none":
return { result: true, env };
}
Expand Down Expand Up @@ -303,9 +352,8 @@ class LLPredicate extends LLNode {
switch (this.type) {
// missing similar
case "similar":
if (!this.similarityThreshold) {
if (!this.similarityThreshold)
throw new Error("Similarity threshold not found");
}
if (isColor) {
const simResult =
(leftEval as Color).symmetricDeltaE(rightEval) <
Expand All @@ -316,6 +364,7 @@ class LLPredicate extends LLNode {
case "==":
return { result: leftEval === rightEval, env };
case "!=":
// console.log(leftEval, rightEval, leftEval !== rightEval);
return { result: leftEval !== rightEval, env };
case ">":
return { result: leftEval > rightEval, env };
Expand Down Expand Up @@ -466,29 +515,62 @@ class LLQuantifier extends LLNode {
this.evalCheck(env);
// weird restoration of type information to make the forward stuff work
const type = tryTypes([LLValueArray, LLValue], env.options);
const idxType = tryTypes([LLValue], env.options);
const where = this.where;
const inputEval = this.input
.evaluate(env)
.result.map((x: any) => type(x))
.filter((x: any) =>
where ? where.evaluate(env.set(this.value, x)).result : true
);
.filter((x: any, index: number) => {
if (!where) {
return true;
}
const newEnv = env
.set(this.value, x)
.set(`index(${this.value})`, idxType(index + 1));
return where.evaluate(newEnv).result;
});

if (!Array.isArray(inputEval)) {
throw new Error("Type error");
}
const predicateEval = this.predicate;
const negate = (func: any) => (x: any) => !func(x);
// TODO can fold all-seq into all if willing to add indexes into the environment tacitly
// note: this will need to adjust the where clause to accommodate for the indices
const evalPred = (x: any) =>
predicateEval.evaluate(env.set(this.value, x)).result;

const evalPred = (x: any, index: number) => {
const newEnv = env
.set(this.value, x)
.set(`index(${this.value})`, idxType(index + 1));
return predicateEval.evaluate(newEnv).result;
};
// switch (this.type) {
// case "exist":
// const resultExist = inputEval.some(evalPred);
// return { result: resultExist, env };
// case "all":
// const resultAll = inputEval.every(evalPred);
// return { result: resultAll, env };
// case "all-seq":
// throw new Error("not implemented");
// }

switch (this.type) {
case "exist":
const resultExist = inputEval.some(evalPred);
return { result: resultExist, env };
const existIndex = inputEval.findIndex(evalPred);
const result = existIndex !== -1;
return { result, env: result ? env : env.toggleBlame(existIndex) };
// return { result: inputEval.some(evalPred), env };
case "all":
const resultAll = inputEval.every(evalPred);
return { result: resultAll, env };
const allIndex = inputEval.findIndex(negate(evalPred));
const pass = allIndex === -1;
const outBlame = pass ? env : env.toggleBlame(allIndex);
console.log({ pass, outBlame: outBlame.colorBlame });
return {
result: pass,
env: outBlame,
};
// return { result: inputEval.every(evalPred), env };
case "all-seq":
throw new Error("not implemented");
}
Expand Down Expand Up @@ -576,13 +658,15 @@ const DEFAULT_OPTIONS = {
stages: false,
};
export function LLEval(root: any, colors: Color[], options = DEFAULT_OPTIONS) {
const env = new Environment(colors, {}, DEFAULT_OPTIONS);
const inputEnv = new Environment(colors, {}, DEFAULT_OPTIONS, {});
const ast = parseToAST({ id: [root] }, DEFAULT_OPTIONS);
if (DEFAULT_OPTIONS.stages) {
console.log(ast.toString());
console.log("EVALUATION EVALUATION EVALUATION EVALUATION");
}
return ast.evaluate(env).result;
const { result, env } = ast.evaluate(inputEnv);
console.log("blame", env.colorBlame);
return result;
}

export function prettyPrintLL(root: any) {
Expand Down

0 comments on commit 600e011

Please sign in to comment.