More robust solver extraction

- Do not hard fail if a single extraction fails
- Do not fail if multiple solutions exist but they are the same
This commit is contained in:
Simon Sawicki
2026-02-08 21:24:57 +01:00
parent e91d03f58a
commit 96c417f90a

View File

@@ -5,10 +5,41 @@ import { extract as extractN } from "./n.ts";
import { setupNodes } from "./setup.ts";
export function preprocessPlayer(data: string): string {
const ast = parse(data);
const body = ast.body;
const program = parse(data);
const plainStatements = modifyPlayer(program);
const solutions = getSolutions(plainStatements);
for (const [name, options] of Object.entries(solutions)) {
plainStatements.push({
type: "ExpressionStatement",
expression: {
type: "AssignmentExpression",
operator: "=",
left: {
type: "MemberExpression",
computed: false,
object: {
type: "Identifier",
name: "_result",
},
property: {
type: "Identifier",
name: name,
},
optional: false,
},
right: multiTry(options),
},
});
}
const block = (() => {
program.body.splice(0, 0, ...setupNodes);
return generate(program);
}
export function modifyPlayer(program: ESTree.Program) {
const body = program.body;
const block: ESTree.BlockStatement = (() => {
switch (body.length) {
case 1: {
const func = body[0];
@@ -40,19 +71,7 @@ export function preprocessPlayer(data: string): string {
throw "unexpected structure";
})();
const found = {
n: [] as ESTree.ArrowFunctionExpression[],
sig: [] as ESTree.ArrowFunctionExpression[],
};
const plainExpressions = block.body.filter((node: ESTree.Node) => {
const n = extractN(node);
if (n) {
found.n.push(n);
}
const sig = extractSig(node);
if (sig) {
found.sig.push(sig);
}
block.body = block.body.filter((node: ESTree.Statement) => {
if (node.type === "ExpressionStatement") {
if (node.expression.type === "AssignmentExpression") {
return true;
@@ -61,43 +80,28 @@ export function preprocessPlayer(data: string): string {
}
return true;
});
block.body = plainExpressions;
for (const [name, options] of Object.entries(found)) {
// TODO: this is cringe fix plz
const unique = new Set(options.map((x) => JSON.stringify(x)));
if (unique.size !== 1) {
const message = `found ${unique.size} ${name} function possibilities`;
throw (
message +
(unique.size ? `: ${options.map((x) => generate(x)).join(", ")}` : "")
);
return block.body;
}
export function getSolutions(
statements: ESTree.Statement[],
): Record<string, ESTree.ArrowFunctionExpression[]> {
const found = {
n: [] as ESTree.ArrowFunctionExpression[],
sig: [] as ESTree.ArrowFunctionExpression[],
};
for (const statement of statements) {
const n = extractN(statement);
if (n) {
found.n.push(n);
}
const sig = extractSig(statement);
if (sig) {
found.sig.push(sig);
}
plainExpressions.push({
type: "ExpressionStatement",
expression: {
type: "AssignmentExpression",
operator: "=",
left: {
type: "MemberExpression",
computed: false,
object: {
type: "Identifier",
name: "_result",
},
property: {
type: "Identifier",
name: name,
},
},
right: options[0],
},
});
}
ast.body.splice(0, 0, ...setupNodes);
return generate(ast);
return found;
}
export function getFromPrepared(code: string): {
@@ -108,3 +112,293 @@ export function getFromPrepared(code: string): {
Function("_result", code)(resultObj);
return resultObj;
}
function multiTry(
generators: ESTree.ArrowFunctionExpression[],
): ESTree.ArrowFunctionExpression {
return {
type: "ArrowFunctionExpression",
params: [
{
type: "Identifier",
name: "_input",
},
],
body: {
type: "BlockStatement",
body: [
{
type: "VariableDeclaration",
kind: "const",
declarations: [
{
type: "VariableDeclarator",
id: {
type: "Identifier",
name: "_results",
},
init: {
type: "NewExpression",
callee: {
type: "Identifier",
name: "Set",
},
arguments: [],
},
},
],
},
{
type: "ForOfStatement",
left: {
type: "VariableDeclaration",
kind: "const",
declarations: [
{
type: "VariableDeclarator",
id: {
type: "Identifier",
name: "_generator",
},
init: null,
},
],
},
right: {
type: "ArrayExpression",
elements: generators,
},
body: {
type: "BlockStatement",
body: [
{
type: "TryStatement",
block: {
type: "BlockStatement",
body: [
{
type: "ExpressionStatement",
expression: {
type: "CallExpression",
callee: {
type: "MemberExpression",
object: {
type: "Identifier",
name: "_results",
},
computed: false,
property: {
type: "Identifier",
name: "add",
},
optional: false,
},
arguments: [
{
type: "CallExpression",
callee: {
type: "Identifier",
name: "_generator",
},
arguments: [
{
type: "Identifier",
name: "_input",
},
],
optional: false,
},
],
optional: false,
},
},
],
},
handler: {
type: "CatchClause",
param: null,
body: {
type: "BlockStatement",
body: [],
},
},
finalizer: null,
},
],
},
await: false,
},
{
type: "IfStatement",
test: {
type: "UnaryExpression",
operator: "!",
argument: {
type: "MemberExpression",
object: {
type: "Identifier",
name: "_results",
},
computed: false,
property: {
type: "Identifier",
name: "size",
},
optional: false,
},
prefix: true,
},
consequent: {
type: "BlockStatement",
body: [
{
type: "ThrowStatement",
argument: {
type: "TemplateLiteral",
expressions: [],
quasis: [
{
type: "TemplateElement",
value: {
cooked: "no solutions",
raw: "no solutions",
},
tail: true,
},
],
},
},
],
},
alternate: null,
},
{
type: "IfStatement",
test: {
type: "BinaryExpression",
left: {
type: "MemberExpression",
object: {
type: "Identifier",
name: "_results",
},
computed: false,
property: {
type: "Identifier",
name: "size",
},
optional: false,
},
right: {
type: "Literal",
value: 1,
},
operator: "!==",
},
consequent: {
type: "BlockStatement",
body: [
{
type: "ThrowStatement",
argument: {
type: "TemplateLiteral",
expressions: [
{
type: "CallExpression",
callee: {
type: "MemberExpression",
object: {
type: "Identifier",
name: "_results",
},
computed: false,
property: {
type: "Identifier",
name: "join",
},
optional: false,
},
arguments: [
{
type: "Literal",
value: ", ",
},
],
optional: false,
},
],
quasis: [
{
type: "TemplateElement",
value: {
cooked: "invalid solutions: ",
raw: "invalid solutions: ",
},
tail: false,
},
{
type: "TemplateElement",
value: {
cooked: "",
raw: "",
},
tail: true,
},
],
},
},
],
},
alternate: null,
},
{
type: "ReturnStatement",
argument: {
type: "MemberExpression",
object: {
type: "CallExpression",
callee: {
type: "MemberExpression",
object: {
type: "CallExpression",
callee: {
type: "MemberExpression",
object: {
type: "Identifier",
name: "_results",
},
computed: false,
property: {
type: "Identifier",
name: "values",
},
optional: false,
},
arguments: [],
optional: false,
},
computed: false,
property: {
type: "Identifier",
name: "next",
},
optional: false,
},
arguments: [],
optional: false,
},
computed: false,
property: {
type: "Identifier",
name: "value",
},
optional: false,
},
},
],
},
async: false,
expression: false,
generator: false,
};
}