resolver rework

This commit is contained in:
Emmaline Autumn 2024-08-21 15:19:41 -06:00
parent 3417fdd3d7
commit 9c9edd9e90
2 changed files with 144 additions and 79 deletions

View File

@ -8,7 +8,15 @@ export function Resolver({ resolver }: { resolver: string }) {
const [res] = useState(new TTCQueryResolver(parser));
const [content, setContent] = useState<ReactNode>("");
useEffect(() => {
setContent(res.resolve(resolver));
let resolved = res.resolve(resolver);
setContent(
typeof resolved?.display === "function" ? (
<resolved.display />
) : (
resolved?.display
)
);
}, [resolver, res]);
return <span>{content}</span>;
}
@ -31,8 +39,9 @@ export function OnDemandResolver({
for (const idx of stackIdxs) {
let thing = res.current.getFromStack(idx);
if (Array.isArray(thing)) thing = thing.at(0);
if (typeof thing === "function") thing = thing();
content = content.replaceAll(idx, thing as string);
if (typeof thing.display === "function")
content = content.replaceAll(idx, thing.display() as string);
else content = content.replaceAll(idx, thing.display as string);
}
setContent(content);
}, [res, template]);

View File

@ -1,14 +1,20 @@
/* eslint-disable react/display-name */
import { ReactNode } from "react";
import { Dice } from "../dice";
import { sum } from "../utils/sum";
import { DiceChart } from "./DiceChart";
import { TTCQueryParser } from "./TTCQueryParser";
interface StackItem {
value: any;
display: ReactNode | (() => ReactNode);
}
export class TTCQueryResolver {
private parser: TTCQueryParser | null;
private context: QueryableObject | null = null;
private stack: any[] = [];
private stack: StackItem[] = [];
constructor(parser?: TTCQueryParser) {
this.parser = parser || null;
@ -22,25 +28,35 @@ export class TTCQueryResolver {
this.context = obj;
}
public resolve(resolver: string, onDemand?: boolean) {
public resolve(resolver: string, onDemand?: boolean): StackItem | undefined {
try {
const resList = resolver.split(",");
for (const res of resList) {
this.stack.push(this.parseResolver(res));
}
const last = this.stack.at(-1);
if (typeof last === "function" && !onDemand) return last();
if (typeof last?.display === "function" && !onDemand) {
last.display = last.display();
}
return last;
} catch (e) {
return e?.toString();
return { value: e?.toString(), display: e?.toString() };
}
}
private parseResolver(resolver: string) {
private parseResolver(resolver: string): StackItem {
if (this.isArithmetic(resolver)) return this.solveArithmetic(resolver);
if (this.isQuery(resolver)) return this.runQuery(resolver);
return resolver;
if (Dice.isDice(resolver)) {
const value = new Dice(resolver);
const dice: StackItem = {
value,
display: () => value.toString(),
};
return dice;
}
return { value: resolver, display: resolver };
}
private isQuery(resolver: string) {
return (
@ -49,81 +65,82 @@ export class TTCQueryResolver {
/^\$\d\./.test(resolver)
);
}
private runQuery(query: string) {
private runQuery(query: string): StackItem {
if (!this.parser) throw "Parser not defined in query resolver";
if (query.startsWith("$")) {
const [_, idx, q] = query.match(/^(\$\d+)\.(.*)/) || [];
if (!_) throw "Detected stack query but did not match the regex";
const stackItem = this.getFromStack(idx);
if (
typeof stackItem === "string" &&
Dice.isDice(
stackItem.replace(/\$\d+/g, (e) =>
this.getFromStack<number>(e).toString()
)
)
) {
return this.handleDice(
stackItem.replace(/\$\d+/g, (e) =>
this.getFromStack<number>(e).toString()
),
q
);
if (stackItem.value instanceof Dice) {
return {
value: query,
display: this.handleDice(stackItem.value, q),
};
}
return this.parser.search(q, stackItem as QueryableObject);
const [res] = this.parser.search(q, stackItem.value as QueryableObject);
debugger;
if (Dice.isDice(res)) {
const value = new Dice(res);
return {
value,
display: () => value.toString(),
};
}
return {
value: res,
display() {
return (
this.value.render ?? this.value ?? "Error resolving query: " + query
);
},
};
}
// if (query.startsWith("?") || query.startsWith("_"))
return query.startsWith("_") && this.context
const res =
query.startsWith("_") && this.context
? this.parser.search(query.replace("_", "^"), this.context).at(0)
: this.parser.search(query.replace(/^[?_].?/, "")).at(0);
if (Dice.isDice(res)) {
const value = new Dice(res);
return {
value,
display: () => value.toString(),
};
}
private handleDice(dice: string, query: string) {
const d = new Dice(dice);
const [method, n] = query.split(":");
let num = Number(n);
// if (n && n.startsWith("$")) num = this.getFromStack(n);
switch (method) {
case "roll":
return () => d.roll().total;
case "rollAvg":
return () => d.rollAvg();
case "rollTimes":
return () => d.rollTimes(num);
case "rollTimesAvg":
return () => d.rollTimesAvg(num);
case "rollLowest":
return () => d.rollMin();
case "rollHighest":
return () => d.rollMax();
case "rollDropHighest":
return () => sum(...d.roll().results.toSorted().toSpliced(-1, 1));
case "rollDropLowest":
return () => sum(...d.roll().results.toSorted().toSpliced(0, 1));
case "distribution":
return () => DiceChart({ dice: d.getRollDistribution() });
case "distribution.normalized":
return () => DiceChart({ dice: d.getNormalizedRollDistribution() });
default:
return "No valid method provided for dice";
}
return {
value: res,
display() {
return (
this.value.render ?? this.value ?? "Error resolving query: " + query
);
},
};
}
private isArithmetic(resolver: string) {
return resolver.split(/\+|\/|-|\*|\^/).filter((e) => !!e).length > 1;
}
private solveArithmetic(resolver: string) {
private solveArithmetic(resolver: string): StackItem {
const [n1, op, n2] = resolver
.match(/(\$?\d+)([+\-*\/^])(\$?\d+)/)
?.slice(1) || ["", "+", ""];
let num1 = Number(n1),
num2 = Number(n2);
let num1: number = Number(n1),
num2: number = Number(n2);
if (n1.startsWith("$")) num1 = this.getFromStack<number>(n1);
if (n2.startsWith("$")) num2 = this.getFromStack<number>(n2);
if (n1.startsWith("$")) {
const result = this.getFromStack(n1).value;
num1 = result instanceof Dice ? result.roll().total : Number(result);
}
if (n2.startsWith("$")) {
const result = this.getFromStack(n1).value;
num2 = result instanceof Dice ? result.roll().total : Number(result);
}
const thing: StackItem = {
value: () => {
switch (op) {
case "+":
return num1 + num2;
@ -138,11 +155,50 @@ export class TTCQueryResolver {
default:
throw "Arithmetic detected but no proper operator assigned";
}
},
display() {
return typeof this.value === "function" ? this.value() : this.value;
},
};
return thing;
}
public getFromStack<T>(stackIndex: string): T {
public getFromStack(stackIndex: string): StackItem {
const i = Number(stackIndex.replace("$", ""));
const val = this.stack[i] as T;
const val = this.stack[i];
return val;
}
private handleDice(dice: Dice, query: string) {
const [method, n] = query.split(":");
let num = Number(n);
// if (n && n.startsWith("$")) num = this.getFromStack(n);
switch (method) {
case "roll":
return () => dice.roll.apply(dice).total;
case "rollAvg":
return () => dice.rollAvg.apply(dice);
case "rollTimes":
return () => dice.rollTimes.apply(dice, [num]);
case "rollTimesAvg":
return () => dice.rollTimesAvg.apply(dice, [num]);
case "rollLowest":
return () => dice.rollMin.apply(dice);
case "rollHighest":
return () => dice.rollMax.apply(dice);
case "rollDropHighest":
return () =>
sum(...dice.roll.apply(dice).results.toSorted().toSpliced(-1, 1));
case "rollDropLowest":
return () =>
sum(...dice.roll.apply(dice).results.toSorted().toSpliced(0, 1));
case "distribution":
return () => DiceChart({ dice: dice.getRollDistribution.apply(dice) });
case "distribution.normalized":
return () =>
DiceChart({ dice: dice.getNormalizedRollDistribution.apply(dice) });
default:
return () => "No valid method provided for dice";
}
}
}