Created
October 29, 2023 08:38
-
-
Save Noktomezo/e2cddf413ba8d37d0041d96214032f4e to your computer and use it in GitHub Desktop.
Simple arithmetic parser without error recovery
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class ArithmeticParser { | |
private readonly input: string | |
private pos: number | |
public constructor(input: string) { | |
this.input = input | |
this.pos = 0 | |
} | |
private _match(regexp: RegExp): string | null { | |
while (/\s+/.test(this.input[this.pos])) this.pos++ | |
const match = this.input.slice(this.pos).match(regexp) | |
if (match && match.index === 0) { | |
this.pos += match[0].length | |
return match[0] | |
} | |
return null | |
} | |
private _number(): number | null { | |
const numStr = this._match(/^(0|[1-9](\d+)?)/) | |
return numStr ? Number.parseFloat(numStr) : null | |
} | |
private _factor(): number | null { | |
const num = this._number() | |
if (num !== null) return num | |
if (this._match(/^\(/)) { | |
const expr = this._expression() | |
if (this._match(/^\)/)) return expr | |
} | |
return null | |
} | |
private _power(): number | null { | |
let base = this._factor() ?? 0 | |
while (true) { | |
const op = this._match(/^\^/) | |
if (op === null) break | |
const exponent = this._factor() | |
if (exponent === null) return null | |
base **= exponent | |
} | |
return base | |
} | |
private _term(): number | null { | |
let left = this._power() ?? 0 | |
while (true) { | |
const op = this._match(/^[*/]/) | |
if (op === null) break | |
const right = this._power() | |
if (right === null) return null | |
if (op === '*') left *= right | |
else left /= right | |
} | |
return left | |
} | |
private _expression(): number | null { | |
let left = this._term() ?? 0 | |
while (true) { | |
const op = this._match(/^[+-]/) | |
if (op === null) break | |
const right = this._term() | |
if (right === null) return null | |
if (op === '+') left += right | |
else left -= right | |
} | |
return left | |
} | |
public parse(): number | null { | |
const result = this._expression() | |
if (result !== null && this.pos === this.input.length) return result | |
return null | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment