Skip to content

Commit

Permalink
fix types for schema generator
Browse files Browse the repository at this point in the history
  • Loading branch information
mcnuttandrew committed Feb 19, 2024
1 parent f34f769 commit 829f520
Show file tree
Hide file tree
Showing 8 changed files with 870 additions and 77 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ First time you start it up you should also run `yarn prep data`

- [ ] Add more blame robustness, may pay to try to reason across all of the operator families (insight: keep a list of the blamable variables in the environment to support tracing)
- [ ] per cols 4 all: color blindness metric should maybe be sensitive to task?
- [ ] Macros story: "not similar", "sequences", "where": { "!=": {"left": "index(a)", "right": "index(b)"} },
- [ ] Macros story: "not similar", "sequences", "where": { "!=": {"left": "index(a)", "right": "index(b)"} }, lab.l should become a macro to reduce the implementation surface
- [ ] More crashy type validation
- [x] Sequential check fix is incorrect for things with equi-ligthness
- [x] Affect rules
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"gen-schema": "npx ts-json-schema-generator --path src/lib/lint-language/lint-type.ts --type LintProgram > public/lint-schema.json --minify"
"gen-schema": "npx ts-json-schema-generator --path src/lib/lint-language/lint-type.ts --type LintProgram > public/lint-schema.json --minify && ts-node-esm scripts/pretty-schema.ts"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0.0",
Expand All @@ -31,6 +31,7 @@
"svelte": "^4.2.3",
"svelte-check": "^3.6.0",
"ts-json-schema-generator": "^1.5.0",
"ts-node": "^10.9.2",
"tslib": "^2.6.2",
"typescript": "^5.2.2",
"vega-typings": "^1.0.1",
Expand Down
714 changes: 713 additions & 1 deletion public/lint-schema.json

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions scripts/pretty-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import fs from "fs/promises";
import { Formatter, FracturedJsonOptions, EolStyle } from "fracturedjsonjs";

const options = new FracturedJsonOptions();
options.MaxTotalLineLength = 120;
options.MaxInlineComplexity = 2;
options.JsonEolStyle = EolStyle.Crlf;

const formatter = new Formatter();
formatter.Options = options;

export function JSONStringify(obj: string) {
return formatter.Reformat(obj);
}

async function main() {
const schema = await fs.readFile("public/lint-schema.json", "utf-8");
const prettySchema = JSONStringify(schema);
await fs.writeFile("public/lint-schema.json", prettySchema);
}

main();
88 changes: 59 additions & 29 deletions src/lib/lint-language/LintLanguage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ const toColors = (colors: string[]): Color[] =>
const exampleColors = toPal(["#d4a8ff", "#7bb9ff", "#008694"]);

test("LintLanguage basic eval - eval with no references ", () => {
const prog1 = { "<": { left: { count: exampleColors.colors }, right: 2 } };
const prog1: LintProgram = {
"<": { left: { count: exampleColors.colors }, right: 2 },
};
expect(prettyPrintLL(prog1)).toBe("count([#d4a8ff, #7bb9ff, #008694]) < 2");
expect(LLEval(prog1, exampleColors).result).toBe(false);
});
Expand Down Expand Up @@ -209,7 +211,7 @@ test("LintLanguage Quantifiers All - tritanopia", () => {
});

test("LintLanguage Boolean values", () => {
const program = { "!=": { left: true, right: false } };
const program: LintProgram = { "!=": { left: true, right: false } };
const options = { debugParse: false };
expect(prettyPrintLL(program, options)).toBe("TRUE != FALSE");
const { result, blame } = LLEval(program, toPal([]), options);
Expand Down Expand Up @@ -292,9 +294,10 @@ test("LintLanguage Arithmetic Ops: =", () => {
expect(prettyPrintLL(program1)).toBe("2 == 2");
expect(LLEval(program1, toPal([])).result).toBe(true);
});
const opProg = (op: string) => ({
"==": { left: { [op]: { left: 2, right: 10 } }, right: 2 },
});
const opProg = (op: "+" | "-" | "/" | "*"): LintProgram =>
({
"==": { left: { [op]: { left: 2, right: 10 } }, right: 2 },
} as LintProgram);
test("LintLanguage Arithmetic Ops: + ", () => {
expect(prettyPrintLL(opProg("+"))).toBe(`2 + 10 == 2`);
expect(LLEval(opProg("+"), toPal([])).result).toBe(false);
Expand All @@ -315,9 +318,10 @@ test("LintLanguage Arithmetic Ops: *", () => {
expect(LLEval(opProg("*"), toPal([])).result).toBe(false);
});

const aggProg = (op: string, right: number) => ({
"==": { left: { [op]: [1, 2, 3, 4] }, right },
});
const aggProg = (op: string, right: number): LintProgram =>
({
"==": { left: { [op]: [1, 2, 3, 4] }, right },
} as LintProgram);
test("LintLanguage Agg Ops: sum", () => {
expect(prettyPrintLL(aggProg("sum", 8))).toBe(`sum([1, 2, 3, 4]) == 8`);
expect(LLEval(aggProg("sum", 8), toPal([])).result).toBe(false);
Expand All @@ -342,6 +346,47 @@ test("LintLanguage Agg Ops: mean", () => {
expect(prettyPrintLL(aggProg("mean", 2.5))).toBe(`mean([1, 2, 3, 4]) == 2.5`);
expect(LLEval(aggProg("mean", 2.5), toPal([])).result).toBe(true);
});
test("LintLanguage Agg Ops: std", () => {
expect(prettyPrintLL(aggProg("std", 10))).toBe(`std([1, 2, 3, 4]) == 10`);
expect(LLEval(aggProg("std", 10), toPal([])).result).toBe(false);
expect(prettyPrintLL(aggProg("std", 1.118033988749895))).toBe(
`std([1, 2, 3, 4]) == 1.118033988749895`
);
expect(LLEval(aggProg("std", 1.118033988749895), toPal([])).result).toBe(
true
);
});

test("LintLanguage Agg Ops: count", () => {
expect(prettyPrintLL(aggProg("count", 10))).toBe(`count([1, 2, 3, 4]) == 10`);
expect(LLEval(aggProg("count", 10), toPal([])).result).toBe(false);
expect(prettyPrintLL(aggProg("count", 4))).toBe(`count([1, 2, 3, 4]) == 4`);
expect(LLEval(aggProg("count", 4), toPal([])).result).toBe(true);
});

test("LintLanguage Agg Ops: extent", () => {
expect(prettyPrintLL(aggProg("extent", 10))).toBe(
`extent([1, 2, 3, 4]) == 10`
);
expect(LLEval(aggProg("extent", 10), toPal([])).result).toBe(false);
expect(prettyPrintLL(aggProg("extent", 3))).toBe(`extent([1, 2, 3, 4]) == 3`);
expect(LLEval(aggProg("extent", 3), toPal([])).result).toBe(true);
});

test("LintLanguage Agg Ops: first", () => {
expect(prettyPrintLL(aggProg("first", 10))).toBe(`first([1, 2, 3, 4]) == 10`);
expect(LLEval(aggProg("first", 10), toPal([])).result).toBe(false);
expect(prettyPrintLL(aggProg("first", 1))).toBe(`first([1, 2, 3, 4]) == 1`);
expect(LLEval(aggProg("first", 1), toPal([])).result).toBe(true);
});

test("LintLanguage Agg Ops: last", () => {
expect(prettyPrintLL(aggProg("last", 10))).toBe(`last([1, 2, 3, 4]) == 10`);
expect(LLEval(aggProg("last", 10), toPal([])).result).toBe(false);
expect(prettyPrintLL(aggProg("last", 4))).toBe(`last([1, 2, 3, 4]) == 4`);
expect(LLEval(aggProg("last", 4), toPal([])).result).toBe(true);
});

test("LintLanguage to color rotate", () => {
const realisticProgram = {
exist: {
Expand Down Expand Up @@ -438,7 +483,7 @@ test("LintLanguage Name discrimination with a single color", () => {

test("LintLanguage Measure Distance", () => {
const colors = toColors(["#000000", "#fff"]);
const program = {
const program: LintProgram = {
"==": {
left: { dist: { left: colors[0], right: colors[1] }, space: "lab" },
right: 100,
Expand All @@ -452,7 +497,7 @@ test("LintLanguage Measure Distance", () => {

test("LintLanguage Measure DeltaE", () => {
const colors = toColors(["#000000", "#fff"]);
const program = {
const program: LintProgram = {
"==": {
left: {
deltaE: { left: colors[0], right: colors[1] },
Expand All @@ -471,7 +516,7 @@ test("LintLanguage Measure DeltaE", () => {

test("LintLanguage Measure Contrast", () => {
const colors = toColors(["#000000", "#fff"]);
const program = {
const program: LintProgram = {
"==": {
left: {
contrast: { left: colors[0], right: colors[1] },
Expand Down Expand Up @@ -703,7 +748,7 @@ test("LintLanguage Sequential Similarity", () => {
});

test("LintLanguage Array Compare", () => {
const program = {
const program: LintProgram = {
"==": {
left: [1, 2, 3],
right: [3, 2, 1],
Expand Down Expand Up @@ -735,7 +780,7 @@ test("LintLanguage Array Compare 2", () => {
});

test("LintLanguage Array Compare -- Sort", () => {
const program = {
const program: LintProgram = {
"==": {
left: [1, 2, 3],
right: {
Expand Down Expand Up @@ -773,7 +818,7 @@ test("LintLanguage Array Compare Color", () => {
});

test("LintLanguage Fair Weighting", () => {
const program = {
const program: LintProgram = {
"<": {
left: { extent: { sort: "colors", varb: "x", func: { "lch.l": "x" } } },
right: 50,
Expand All @@ -787,18 +832,3 @@ test("LintLanguage Fair Weighting", () => {
expect(result.result).toBe(false);
expect(result.blame).toStrictEqual([]);
});

// Text version
// range(colors, lch, c) < threshold_1 AND range(colors, lch, l) < threshold_2

// // YAML VERSION
// all:
// in: colors
// varbs: [a, b]
// where: '!=': [index(a), index(b)]
// predicate:
// and:
// - '<': [range(colors, lch, c), threshold_1]
// - '<': [range(colors, lch, l), threshold_2]

// JSON VERSION
92 changes: 50 additions & 42 deletions src/lib/lint-language/lint-type.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Color as ColorClass } from "../Color";
/**
* Lint Language
* Lint Language is a language for expressing color logic. It is used to define color rules and constraints in the Lint system.
Expand All @@ -20,7 +21,7 @@ export type LintConjunction =
| { and: LintExpression[] }
| { or: LintExpression[] }
| { not: LintExpression };
export type LintQuantifierBase =
type LintQuantifierBase =
| {
varbs: LintVariable[];
/**
Expand Down Expand Up @@ -54,76 +55,81 @@ export type LintQuantifier =
| { exist: LintQuantifierBase };

// Operations
export type LintRef = LintVariable | LintValue | LintValue[] | LintMap;

/**
* A logical comparison expression. It is used to express a comparison between two values. It can be ==, !=, <, > or similar. similar takes two colors and a similarity threshold expressed in dE units.
*/
export type LintComparison =
| { "==": { left: LintRef; right: LintRef } }
| { "!=": { left: LintRef; right: LintRef } }
| { "<": { left: LintRef; right: LintRef } }
| { ">": { left: LintRef; right: LintRef } }
| { absDiff: { left: LintRef; right: LintRef } }
| { similar: { left: LintRef; right: LintRef; threshold: number } };
export type MathOperations = "+" | "-" | "*" | "/";
export type LintMathOps = Record<MathOperations, Number | LintVariable>;
| {
"==": {
left: LintValue | LintArrayValue;
right: LintValue | LintArrayValue;
};
}
| {
"!=": {
left: LintValue | LintArrayValue;
right: LintValue | LintArrayValue;
};
}
| { "<": { left: LintValue; right: LintValue } }
| { ">": { left: LintValue; right: LintValue } }
| { absDiff: { left: LintValue; right: LintValue } }
| { similar: { left: LintValue; right: LintValue; threshold: number } };
export type LintMathOps =
| { "+": { left: LintValue; right: LintValue } }
| { "-": { left: LintValue; right: LintValue } }
| { "*": { left: LintValue; right: LintValue } }
| { "/": { left: LintValue; right: LintValue } };

export type LintPairOps =
| LintPairOpsDist
| LintPairOpsDeltaE
| LintPairOpsContrast;

type PairType = LintColor | LintVariable;
/**
* Compute the distance between two colors using a given color space. The color space can be lab, hsl, or another valid color space.
*/
export type LintPairOpsDist = {
dist: { left: LintRef; right: LintRef };
dist: { left: PairType; right: PairType };
space: "lab" | "hsl";
};

/**
* Compute the deltaE between two colors using a given algorithm. The algorithm can be 2000 or 76.
*/
export type LintPairOpsDeltaE = {
deltaE: { left: LintRef; right: LintRef };
deltaE: { left: PairType; right: PairType };
algorithm: "2000" | "76";
};

/**
* Compute the contrast between two colors using a given algorithm. The algorithm can be APCA, WCAG21, Michelson, Weber, Lstar or DeltaPhi.
*/
export type LintPairOpsContrast = {
contrast: {
left: LintRef;
right: LintRef;
};
algorithm: "APCA" | "WCAG21" | "Michelson" | "Weber" | "Lstar" | "DeltaPhi";
contrast: { left: PairType; right: PairType };
algorithm: "APCA" | "DeltaPhi" | "Lstar" | "Michelson" | "WCAG21" | "Weber";
};

export type MapEval = LintColorFunction | LintPairOps;
type LintArrayValue = LintVariable | LintValue[] | LintMap;
export type LintMap =
| { map: LintVariable | LintValue[]; func: MapEval; varb: string }
| { sort: LintVariable | LintValue[]; func: MapEval; varb: string }
| { reverse: LintVariable | LintValue[] }
| { filter: LintVariable | LintValue[]; func: LintExpression; varb: string }
| { speed: LintVariable | LintValue[] };

export type Aggs =
| "sum"
| "count"
| "mean"
| "max"
| "min"
| "mean"
| "first"
| "last"
| "extent"
| "std";
export type LintAggregate = Record<
Aggs,
LintVariable | (LintVariable | LintValue)[] | LintMap
>;
| { filter: LintArrayValue; func: LintExpression; varb: string }
| { map: LintArrayValue; func: LintValue; varb: string }
| { reverse: LintArrayValue }
| { sort: LintArrayValue; func: LintValue; varb: string }
| { speed: LintArrayValue };

export type LintAggregate =
| { count: LintArrayValue }
| { extent: LintArrayValue }
| { first: LintArrayValue }
| { last: LintArrayValue }
| { max: LintArrayValue }
| { mean: LintArrayValue }
| { mean: LintArrayValue }
| { min: LintArrayValue }
| { std: LintArrayValue }
| { sum: LintArrayValue };

/**
* Simulate color vision deficiency. The type can be protanomaly, deuteranomaly, tritanopia or grayscale.
Expand All @@ -141,7 +147,7 @@ export type LintColorFunction =
space: ColorSpace;
channel: ColorChannel;
}
| Record<string, LintVariable | LintColor>;
| { [cmd: string]: LintVariable | string };

/**
* Converts a Color to a color space component. Has syntax like colorSpace.channel, where colorSpace is a color space and channel is a channel in that color space. Available spaces: hsl, hsv, jzazbz, lab, lch, oklab, oklch, rgb
Expand Down Expand Up @@ -187,6 +193,7 @@ export type LintValue =
| boolean
| LintColor
| LintVariable
| LintComparison
| LintMathOps
| LintPairOps
| LintAggregate
Expand All @@ -195,4 +202,5 @@ export type LintValue =
// raw values
export type LintBoolean = boolean;
export type LintVariable = string;
export type LintColor = string;
// export type LintColor = string | Color | LintVariable;
export type LintColor = string | LintVariable;
7 changes: 4 additions & 3 deletions src/lib/lints/size-discrim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ const lints: CustomLint[] = ["Thin", "Medium", "Wide"].map((key) => {
or: [
{
">": {
left: {
absDiff: { left: { "lab.l": "x" }, right: { "lab.l": "y" } },
},
// left: {
// absDiff: { left: { "lab.l": "x" }, right: { "lab.l": "y" } },
// },
left: { absDiff: { left: 0, right: 1 } },
right: jnd.l,
},
},
Expand Down
Loading

0 comments on commit 829f520

Please sign in to comment.