This commit is contained in:
Tangent Wantwight 2024-05-17 20:50:54 -04:00
parent 62593b0ab8
commit bf44d84e17
2 changed files with 53 additions and 6 deletions

View file

@ -4,7 +4,7 @@ import { Expr } from "./expr";
describe("expr", () => {
test.each([
["1", "1"],
// ["1 + 2", "3"],
["1 + 2", "3"],
// ["1 - 2", "-1"],
// ["1 * 2", "2"],
// ["1 / 2", "0.5"],

View file

@ -10,7 +10,7 @@ export function Expr({}, argv: TextPiece[]): ProcResult {
const expression = argv.map(AsText).join(" ").trim();
const parser = new ExpressionParser(expression);
const result = parser.parsePrefixOp();
const result = parser.parseSubExpression(0);
if ("value" in result) {
return { text: String(result.value) };
@ -36,17 +36,40 @@ type Value = { value: number };
type TokenHandler = {
leftBindingPower: number;
token: RegExp;
parse: (matched: string, parser: ExpressionParser) => Value | ErrorResult;
parse: (
left: Value,
matched: string,
parser: ExpressionParser
) => Value | ErrorResult;
};
function map(
value: Value | ErrorResult,
op: (value: number) => Value | ErrorResult
): Value | ErrorResult {
if ("error" in value) {
return value;
} else {
return op(value.value);
}
}
const Operators: TokenHandler[] = [
{
leftBindingPower: -1,
token: NUMBER_TOKEN,
parse: (matched) => ({ value: Number(matched) }),
parse: (left, matched) => ({ value: Number(matched) }),
},
{
leftBindingPower: 10,
token: PLUS_TOKEN,
parse: ({ value: left }, matched, parser) =>
map(parser.parseSubExpression(11), (right) => ({ value: left + right })),
},
];
const ZERO = { value: 0 };
class ExpressionParser {
pos: number = 0;
@ -64,11 +87,11 @@ class ExpressionParser {
}
/** Tries to match one of the given tokens & returns the value of the handler. If none match, returns null. */
tryAll(options: TokenHandler[]): Value | ErrorResult | null {
tryAll(left: Value, options: TokenHandler[]): Value | ErrorResult | null {
for (const option of options) {
const match = this.next(option.token);
if (match) {
return option.parse(match, this);
return option.parse(left, match, this);
}
}
return null;
@ -77,10 +100,34 @@ class ExpressionParser {
parsePrefixOp(): Value | ErrorResult {
return (
this.tryAll(
ZERO,
Operators.filter((operator) => operator.leftBindingPower == -1)
) ?? { error: "Couldn't parse number" }
);
}
parseSubExpression(rightBindingPower: number): Value | ErrorResult {
let value = this.parsePrefixOp();
if ("error" in value) {
return value;
}
do {
const newValue = this.tryAll(
value,
Operators.filter(
(operator) => operator.leftBindingPower > rightBindingPower
)
);
if (newValue == null) {
break;
}
value = newValue;
} while (!("error" in value));
return value;
}
}
// parse expression: