-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexpression_parser.ts
99 lines (95 loc) · 2.51 KB
/
expression_parser.ts
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
import { assertEquals } from 'https://deno.land/[email protected]/assert/mod.ts'
export type Expression =
| { op: '+' | '-' | '*' | '/'; a: Expression; b: Expression }
| { value: number }
type ParseResult = Generator<{ expr: Expression; string: string }>
function * parseFloat (string: string): ParseResult {
for (const regex of [
/[-+](?:\d+\.?|\d*\.\d+)(?:e[-+]?\d+)?$/,
/(?:\d+\.?|\d*\.\d+)(?:e[-+]?\d+)?$/
]) {
const match = string.match(regex)
if (!match) {
continue
}
const number = +match[0]
if (Number.isFinite(number)) {
yield {
expr: { value: number },
string: string.slice(0, -match[0].length)
}
}
}
}
function * parseLitExpr (string: string): ParseResult {
yield * parseFloat(string)
if (string[string.length - 1] === ')') {
for (const result of parseAddExpr(string.slice(0, -1))) {
if (result.string[result.string.length - 1] === '(') {
yield { ...result, string: result.string.slice(0, -1) }
}
}
}
}
function * parseMulExpr (string: string): ParseResult {
for (const right of parseLitExpr(string)) {
const op = right.string[right.string.length - 1]
if (op === '*' || op === '/') {
for (const left of parseMulExpr(right.string.slice(0, -1))) {
yield { ...left, expr: { op, a: left.expr, b: right.expr } }
}
}
}
yield * parseLitExpr(string)
}
function * parseAddExpr (string: string): ParseResult {
for (const right of parseMulExpr(string)) {
const op = right.string[right.string.length - 1]
if (op === '+' || op === '-') {
for (const left of parseAddExpr(right.string.slice(0, -1))) {
yield { ...left, expr: { op, a: left.expr, b: right.expr } }
}
}
}
yield * parseMulExpr(string)
}
export function parse (expression: string): Expression | null {
for (const result of parseAddExpr(expression.replace(/\s/g, ''))) {
if (result.string === '') {
return result.expr
}
}
return null
}
Deno.test({
name: 'expression_parser',
fn () {
assertEquals(parse('3 + 2'), {
op: '+',
a: { value: 3 },
b: { value: 2 }
})
assertEquals(parse('3 + 2 + 1'), {
op: '+',
a: {
op: '+',
a: { value: 3 },
b: { value: 2 }
},
b: { value: 1 }
})
assertEquals(parse('3 * (4 - 5) + 2'), {
op: '+',
a: {
op: '*',
a: { value: 3 },
b: {
op: '-',
a: { value: 4 },
b: { value: 5 }
}
},
b: { value: 2 }
})
}
})