-
Notifications
You must be signed in to change notification settings - Fork 0
/
microraptor.ts
127 lines (99 loc) · 3.08 KB
/
microraptor.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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
interface EnumWithValue<T, V> {
kind: T,
value?: V
}
type Token = EnumWithValue<TokenKind, string | number>
type PrimaryExpressionNode = EnumWithValue<PrimaryExpressionNodeKind, ExpressionNode | number>
enum TokenKind {
ParensOpen,
Operator,
NumberToken,
ParensClose,
}
enum PrimaryExpressionNodeKind {
number,
expression
}
interface ExpressionNode {
operator: string,
leftSideExpression: PrimaryExpressionNode
rightSideExpression: PrimaryExpressionNode
}
function tokenize(input: String): Token[] {
return [...input]
.filter(t => t !== ' ')
.map((char) => {
switch (char) {
case '(':
return { kind: TokenKind.ParensOpen }
case ')':
return { kind: TokenKind.ParensClose }
case 's':
return { kind: TokenKind.Operator, value: char}
default:
const num = parseInt(char)
if(!isNaN(num)) {
return {kind: TokenKind.NumberToken, value: num }
}
}
throw new Error(`Character '${char}' is not identified`);
})
}
class Parser {
tokens: Token[]
constructor(tokens: Token[]) {
this.tokens = tokens
}
parsePrimaryExpression(): PrimaryExpressionNode {
let expression: PrimaryExpressionNode | null = null
switch (this.tokens[0].kind) {
case TokenKind.NumberToken:
expression = { kind: PrimaryExpressionNodeKind.number, value: this.tokens[0].value as number }
this.tokens.shift()
break;
case TokenKind.ParensOpen:
const value = this.parse()
expression = { kind: PrimaryExpressionNodeKind.expression, value }
break;
}
return expression!
}
parse(): ExpressionNode {
const tokenOpen = this.tokens.shift()!
if (tokenOpen.kind !== TokenKind.ParensOpen) {
throw new Error("Unexpected token");
}
const tokenOperator = this.tokens.shift()!
const operator = tokenOperator.value!.toString()
if (tokenOperator.kind !== TokenKind.Operator) {
throw new Error("Unexpected token");
}
let leftSideExpression = this.parsePrimaryExpression()
let rightSideExpression = this.parsePrimaryExpression()
const tokenParensClose = this.tokens.shift()!
if (tokenParensClose.kind !== TokenKind.ParensClose) {
throw new Error("Unexpected token");
}
return { operator, leftSideExpression, rightSideExpression }
}
}
function evaluate(primaryExpression: PrimaryExpressionNode): number {
switch (primaryExpression.kind) {
case PrimaryExpressionNodeKind.expression:
return interpreter(primaryExpression.value as ExpressionNode)
case PrimaryExpressionNodeKind.number:
return primaryExpression.value as number
}
}
function interpreter(expression: ExpressionNode): number {
const firstEval = evaluate(expression.leftSideExpression)
const secondEval = evaluate(expression.rightSideExpression)
if (expression.operator == "s") {
return firstEval + secondEval
}
throw new Error("Unknow operator");
}
const tokens = tokenize('(s 8 (s 7 9))')
const parser = new Parser(tokens)
const ast = parser.parse()
console.log(interpreter(ast))