Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | 2x 2x 2x 2x 2x 2x 1221x 1221x 1221x 1221x 1221x 729x 719x 544x 10x 554x 554x 1221x 404x 1221x 6x 6x 6x 3934x 1938x 1996x 1841x 1841x 1841x 1841x 2713x 1674x 1871x 1137x 761x 753x 913x 5x 913x 1221x 1044x 1044x 1044x 1044x 1044x 1073x 1073x 4x 1x 1x 1x | import * as util from './util';
import {Codegen} from '@jsonjoy.com/codegen/lib/Codegen';
import {createEvaluate} from './createEvaluate';
import {Vars} from './Vars';
import {type ExpressionResult, Literal} from './codegen-steps';
import type {JavaScript} from '@jsonjoy.com/codegen';
import type * as types from './types';
export type JsonExpressionFn = (vars: types.JsonExpressionExecutionContext['vars']) => unknown;
export interface JsonExpressionCodegenOptions extends types.JsonExpressionCodegenContext {
expression: types.Expr;
operators: types.OperatorMap;
}
export class JsonExpressionCodegen {
protected codegen: Codegen<JsonExpressionFn>;
protected evaluate: ReturnType<typeof createEvaluate>;
public constructor(protected options: JsonExpressionCodegenOptions) {
this.codegen = new Codegen<JsonExpressionFn>({
args: ['vars'],
epilogue: '',
});
this.evaluate = createEvaluate({...options});
}
private linkedOperandDeps: Set<string> = new Set();
private linkOperandDeps = (dependency: unknown, name?: string): string => {
if (name) {
if (this.linkedOperandDeps.has(name)) return name;
this.linkedOperandDeps.add(name);
} else {
name = this.codegen.getRegister();
}
this.codegen.linkDependency(dependency, name);
return name;
};
private operatorConst = (js: JavaScript<unknown>): string => {
return this.codegen.addConstant(js);
};
private subExpression = (expr: types.Expr): JsonExpressionFn => {
const codegen = new JsonExpressionCodegen({...this.options, expression: expr});
const fn = codegen.run().compile();
return fn;
};
protected onExpression(expr: types.Expr | unknown): ExpressionResult {
if (expr instanceof Array) {
if (expr.length === 1) return new Literal(expr[0]);
} else return new Literal(expr);
const def = this.options.operators.get(expr[0]);
if (def) {
const [name, , arity, , codegen, impure] = def;
util.assertArity(name, arity, expr);
const operands = expr.slice(1).map((operand) => this.onExpression(operand));
if (!impure) {
const allLiterals = operands.every((expr) => expr instanceof Literal);
if (allLiterals) {
const result = this.evaluate(expr, {vars: new Vars(undefined)});
return new Literal(result);
}
}
const ctx: types.OperatorCodegenCtx<types.Expression> = {
expr,
operands,
createPattern: this.options.createPattern,
operand: (operand: types.Expression) => this.onExpression(operand),
link: this.linkOperandDeps,
const: this.operatorConst,
subExpression: this.subExpression,
var: (value: string) => this.codegen.var(value),
};
return codegen(ctx);
}
return new Literal(false);
}
public run(): this {
const expr = this.onExpression(this.options.expression);
this.codegen.js(`return ${expr};`);
return this;
}
public generate() {
return this.codegen.generate();
}
public compileRaw(): JsonExpressionFn {
return this.codegen.compile();
}
public compile(): JsonExpressionFn {
const fn = this.compileRaw();
return (vars) => {
try {
return fn(vars);
} catch (err) {
if (err instanceof Error) throw err;
const error = new Error('Expression evaluation error.');
(<any>error).value = err;
throw error;
}
};
}
}
|