-
Notifications
You must be signed in to change notification settings - Fork 13
/
options_parser.bmx
328 lines (266 loc) · 6.25 KB
/
options_parser.bmx
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
SuperStrict
Import brl.map
Import "stringbuffer_core.bmx"
Global compilerOptions:TValues
Function EvalOption:Int(line:String)
If Not line Then
Return True
End If
Local tok:TOptTokenizer = New TOptTokenizer.Create(line.ToLower())
Local parser:TOptParser = New TOptParser.Create(tok, compilerOptions)
Return parser.Eval()
End Function
Type TOptParser
Field tokenizer:TOptTokenizer
Field token:TOptToken
Field values:TValues
Method Create:TOptParser(tokenizer:TOptTokenizer, values:TValues)
Self.tokenizer = tokenizer
Self.values = values
Return Self
End Method
Method Eval:Int()
NextToke
Local expr:TExpr = Parse()
Return expr.Eval()
End Method
Method NextToke()
token = tokenizer.NextToken()
End Method
Method Parse:TExpr()
Return ParseOrExpr()
End Method
Method ParseOrExpr:TExpr()
Local expr:TExpr = ParseAndExpr()
Repeat
If token.tokType = TOK_OR Then
NextToke
Local rhs:TExpr = ParseAndExpr()
expr = New TBinaryExpr.Create(TOK_OR, expr, rhs)
Else
Return expr
End If
Forever
Return expr
End Method
Method ParseAndExpr:TExpr()
Local expr:TExpr = ParseCompareExpr()
Repeat
If token.tokType = TOK_AND Then
NextToke
Local rhs:TExpr = ParseNotExpr()
expr = New TBinaryExpr.Create(TOK_AND, expr, rhs)
Else
Return expr
End If
Forever
End Method
Method ParseCompareExpr:TExpr()
Local expr:TExpr = ParseNotExpr()
Repeat
If token.tokType = TOK_LT Or token.tokType = TOK_GT Or token.tokType = TOK_EQ Then
NextToke
Local rhs:TExpr = ParseNotExpr()
expr = New TBinaryCompareExpr.Create(token.tokType, expr, rhs)
Else
Return expr
End If
Forever
End Method
Method ParseNotExpr:TExpr()
If token.tokType = TOK_NOT Then
NextToke
Local expr:TExpr = ParseNotExpr()
Return New TNotExpr.Create(expr)
End If
Return ParsePrimaryExpr()
End Method
Method ParsePrimaryExpr:TExpr()
Local expr:TExpr
Select token.tokType
Case TOK_LPAREN
NextToke
expr = Parse()
If token.tokType <> TOK_RPAREN Then
Throw "Expected ')'"
End If
Case TOK_IDENT
Local value:Int = values.Value(token.value)
expr = New TIdentExpr.Create(token.value, value)
NextToke
Case TOK_RPAREN
Throw "Unexpected ')'"
End Select
Return expr
End Method
End Type
Type TOptToken
Field tokType:Int
Field value:String
Method Create:TOptToken(tokType:Int, value:String)
Self.tokType = tokType
Self.value = value
Return Self
End Method
End Type
Type TOptTokenizer
Field line:String
Field pos:Int
Method Create:TOptTokenizer(line:String)
Self.line = line
Return Self
End Method
Method NextToken:TOptToken()
While True
If pos = line.length
Return New TOptToken.Create(TOK_EOL, Null)
End If
Local char:Int = line[pos]
pos :+ 1
If char = Asc("(") Then
Return New TOptToken.Create(TOK_LPAREN, "(")
Else If char = Asc(")") Then
Return New TOptToken.Create(TOK_RPAREN, ")")
Else If char = Asc("<") Then
Return New TOptToken.Create(TOK_LT, "<")
Else If char = Asc(">") Then
Return New TOptToken.Create(TOK_GT, ">")
Else If char = Asc("=") Then
Return New TOptToken.Create(TOK_EQ, "=")
Else If IsAlphaNumeric(char) Then
Return NextIdentToken(char)
Else If Not IsWhitespace(char) Then
Throw "Unexpected character : " + Chr(char)
End If
Wend
End Method
Method NextIdentToken:TOptToken(char:Int)
Local sb:TStringBuffer = TStringBuffer.Create(Chr(char))
While True
char = Peek()
If Not IsAlphaNumeric(char) Then
Exit
End If
pos :+ 1
sb.Append(Chr(char))
Wend
Local token:String = sb.ToString().ToLower()
Select token
Case "not"
Return New TOptToken.Create(TOK_NOT, token)
Case "and"
Return New TOptToken.Create(TOK_AND, token)
Case "or"
Return New TOptToken.Create(TOK_OR, token)
Default
Return New TOptToken.Create(TOK_IDENT, token)
End Select
End Method
Method IsAlphaNumeric:Int(char:Int)
Return (char >= Asc("A") And char <= Asc("Z")) Or (char >= Asc("a") And char <= Asc("z")) Or (char >= Asc("0") And char <= Asc("9")) Or char = Asc("_")
End Method
Method IsWhitespace:Int(char:Int)
Return char = Asc(" ") Or char = Asc("~t")
End Method
Method Peek:Int()
If pos < line.length Then
Return line[pos]
End If
Return 0
End Method
End Type
Type TExpr
Method Eval:Int() Abstract
End Type
Type TNotExpr Extends TExpr
Field expr:TExpr
Method Create:TNotExpr(expr:TExpr)
Self.expr = expr
Return Self
End Method
Method Eval:Int()
If Not expr Then
Throw "Missing expression"
End If
Return Not expr.Eval()
End Method
End Type
Type TBinaryExpr Extends TExpr
Field op:Int
Field lhs:TExpr
Field rhs:TExpr
Method Create:TBinaryExpr(op:Int, lhs:TExpr, rhs:TExpr)
Self.op = op
Self.rhs = rhs
Self.lhs = lhs
Return Self
End Method
Method Eval:Int()
If Not lhs Or Not rhs Then
Throw "Missing expression"
End If
Select op
Case TOK_OR
Return lhs.Eval() Or rhs.Eval()
Case TOK_AND
Return lhs.Eval() And rhs.Eval()
End Select
End Method
End Type
Type TBinaryCompareExpr Extends TBinaryExpr
Method Eval:Int()
If Not lhs Or Not rhs Then
Throw "Missing expression"
End If
Select op
Case TOK_LT
Return lhs.Eval() < rhs.Eval()
Case TOK_GT
Return lhs.Eval() > rhs.Eval()
Case TOK_EQ
Return lhs.Eval() = rhs.Eval()
End Select
End Method
End Type
Type TIdentExpr Extends TExpr
Field ident:String
Field value:Int
Method Create:TIdentExpr(ident:String, value:Int)
Self.ident = ident
Self.value = value
Return Self
End Method
Method Eval:Int()
Return value
End Method
End Type
Type TInt
Field value:Int
Method Create:TInt(value:Int)
Self.value = value
Return Self
End Method
End Type
Type TValues
Field values:TMap = New TMap
Method Add(key:String, value:Int)
values.Insert(key, New TInt.Create(value))
End Method
Method Value:Int(key:String)
Local obj:Object = values.ValueForKey(key)
If obj Then
Return TInt(obj).value
End If
Return 0
End Method
End Type
Const TOK_IDENT:Int = 0
Const TOK_NOT:Int = 1
Const TOK_AND:Int = 2
Const TOK_OR:Int = 3
Const TOK_LPAREN:Int = 4
Const TOK_RPAREN:Int = 5
Const TOK_EOL:Int = 6
Const TOK_LT:Int = 7
Const TOK_GT:Int = 8
Const TOK_EQ:Int = 9