-
Notifications
You must be signed in to change notification settings - Fork 0
/
Parser.py
329 lines (266 loc) · 10.8 KB
/
Parser.py
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
329
from Expr import *
from Stmt import *
from typing import List, Optional
from Token import Token
from TokenType import TokenType
class ParseError(Exception):
'''
ParseError class for the Jesse programming language.
The error is raised internally and caught within the parser too.
It doesn't escape the parser.
'''
class Parser:
'''
A recursive descent parser for the Jesse programming language.
jesse: Jesse object
tokens: List of tokens to parse
'''
def __init__(self, jesse:object, source:str, tokens: List[Token]) -> None:
self.jesse = jesse
self.source = source
self.lines = source.splitlines()
self.tokens = tokens
self.current = 0
def parse(self) -> List[Stmt]:
statements = []
while not self.is_at_end():
statements.append(self.declaration())
return statements
def expression(self) -> Expr:
return self.assignment()
def declaration(self) -> Stmt:
try:
if self.match(TokenType.COOK):
return self.cook_declaration()
if self.match(TokenType.BETTER_CALL):
return self.bettercall_declaration("function")
return self.statement()
except ParseError:
self.synchronize()
return None
def statement(self) -> Stmt:
# Check if its a recognized statement
if self.match(TokenType.RETURN):
return self.return_statement()
if self.match(TokenType.THE_ONE_WHO_KNOCKS):
return self.theonewhoknocks_statement()
if self.match(TokenType.JESSE_IF):
return self.jesseif_statement()
if self.match(TokenType.SAY_MY_NAME):
return self.saymyname_statement()
if self.match(TokenType.LEFT_BRACE):
return Block(self.block())
# If not then it must be an expression statement
return self.expression_statement()
def theonewhoknocks_statement(self) -> Stmt:
self.consume(TokenType.LEFT_PAREN, "where's my left paren yo?")
condition = self.expression()
self.consume(TokenType.RIGHT_PAREN, "where's my right paren yo?")
body = self.statement()
return TheOneWhoKnocks(condition, body)
def jesseif_statement(self) -> Stmt:
self.consume(TokenType.LEFT_PAREN, "where's my left paren yo?")
condition = self.expression()
self.consume(TokenType.RIGHT_PAREN, "where's my right paren yo?")
then_branch = self.statement()
else_branch: Stmt = None
if self.match(TokenType.ELSE):
else_branch = self.statement()
return JesseIf(condition, then_branch, else_branch)
def saymyname_statement(self) -> Stmt:
value = self.expression()
self.consume(TokenType.SEMICOLON, "where's my semicolon yo?")
return SayMyName(value)
def return_statement(self) -> Stmt:
keyword = self.previous()
value: Expr = None
if not self.check(TokenType.SEMICOLON):
value = self.expression()
self.consume(TokenType.SEMICOLON, "where's my semicolon yo?")
return Return(keyword, value)
def cook_declaration(self) -> Stmt:
name = self.consume(TokenType.IDENTIFIER, "what's the name of your cook yo")
initializer: Expr = None
if self.match(TokenType.EQUAL):
initializer = self.expression()
self.consume(TokenType.SEMICOLON, "where's my semicolon yo?")
return Cook(name, initializer)
def expression_statement(self) -> Stmt:
expr = self.expression()
self.consume(TokenType.SEMICOLON, "where's my semicolon yo?")
return Expression(expr)
def bettercall_declaration(self, kind: str) -> Stmt:
name = self.consume(TokenType.IDENTIFIER, "what's the name of your function yo")
self.consume(TokenType.LEFT_PAREN, "where's my left paren yo?")
parameters = []
if not self.check(TokenType.RIGHT_PAREN):
while True:
parameters.append(self.consume(TokenType.IDENTIFIER, "what's the name of your parameter yo"))
if not self.match(TokenType.COMMA):
break
self.consume(TokenType.RIGHT_PAREN, "where's my right paren yo?")
self.consume(TokenType.LEFT_BRACE, "where's my left brace yo?")
body = self.block()
return BetterCall(name, parameters, body)
def block(self) -> List[Stmt]:
statements = []
while not self.check(TokenType.RIGHT_BRACE) and not self.is_at_end():
statements.append(self.declaration())
self.consume(TokenType.RIGHT_BRACE, "where's my right brace yo?")
return statements
def assignment(self) -> Expr:
expr = self.logical_or()
if self.match(TokenType.EQUAL):
equals = self.previous()
value = self.assignment()
if isinstance(expr, Variable):
name = expr.name
return Assign(name, value)
self.error(equals, "this is an invalid assignment target yo")
return expr
def logical_or(self) -> Expr:
expr = self.logical_and()
while self.match(TokenType.OR):
operator = self.previous()
right = self.logical_and()
expr = Logical(expr, operator, right)
return expr
def logical_and(self) -> Expr:
expr = self.ternary()
while self.match(TokenType.AND):
operator = self.previous()
right = self.ternary()
expr = Logical(expr, operator, right)
return expr
# TODO: Add support for nested ternary expressions
def ternary(self) -> Expr:
condition = self.equality()
if self.match(TokenType.QUESTION_MARK):
then_branch = self.equality()
if self.match(TokenType.COLON):
else_branch = self.equality()
return Ternary(condition,then_branch,else_branch)
else:
self.error(self.previous(),"this is an invalid ternary statement yo")
return condition
def equality(self) -> Expr:
expr = self.comparison()
while self.match(TokenType.BANG_EQUAL, TokenType.EQUAL_EQUAL):
operator = self.previous()
right = self.comparison()
expr = Binary(expr, operator, right)
return expr
def comparison(self) -> Expr:
expr = self.term()
while self.match(TokenType.GREATER, TokenType.GREATER_EQUAL, TokenType.LESS, TokenType.LESS_EQUAL):
operator = self.previous()
right = self.term()
expr = Binary(expr, operator, right)
return expr
def term(self) -> Expr:
expr = self.factor()
while self.match(TokenType.MINUS, TokenType.PLUS):
operator = self.previous()
right = self.factor()
expr = Binary(expr, operator, right)
return expr
def factor(self) -> Expr:
expr = self.unary()
while self.match(TokenType.SLASH, TokenType.STAR):
operator = self.previous()
right = self.unary()
expr = Binary(expr, operator, right)
return expr
def unary(self) -> Expr:
if self.match(TokenType.BANG, TokenType.MINUS):
operator = self.previous()
right = self.unary()
return Unary(operator, right)
return self.call()
def call(self) -> Expr:
expr = self.primary()
while True:
if self.match(TokenType.LEFT_PAREN):
expr = self.finish_call(expr)
else:
break
return expr
def finish_call(self, callee: Expr) -> Call:
arguments = []
if not self.check(TokenType.RIGHT_PAREN):
arguments.append(self.expression())
while self.match(TokenType.COMMA):
arguments.append(self.expression())
paren = self.consume(TokenType.RIGHT_PAREN, "where's my right paren yo?")
return Call(callee, paren, arguments)
def primary(self) -> Expr:
if self.match(TokenType.DEA):
return Literal(False)
elif self.match(TokenType.CARTEL):
return Literal(True)
elif self.match(TokenType.I_AM_THE_DANGER):
return Literal(None)
elif self.match(TokenType.METH, TokenType.STRING):
return Literal(self.previous().literal)
elif self.match(TokenType.IDENTIFIER):
return Variable(self.previous())
elif self.match(TokenType.LEFT_PAREN):
matching_paren = self.previous()
expr = self.expression()
self.consume(TokenType.RIGHT_PAREN, "you haven't closed this yo",matching=matching_paren)
return Grouping(expr)
raise self.error(self.peek(), "this ain't no expression yo")
def match(self, *types: TokenType) -> bool:
# Try to match the current token with one of the types in the list and consume it if it matches
for token_type in types:
if self.check(token_type):
self.advance()
return True
return False
def consume(self, token_type: TokenType, message: str, matching: Optional[Token] = None) -> Token:
# Try to consume a token of a given token_type
if self.check(token_type):
return self.advance()
if matching:
raise self.error(matching, message)
raise self.error(self.peek(), message)
def check(self, token_type: TokenType) -> bool:
# Check if the current token is of the given token_type
if self.is_at_end():
return False
return self.peek().token_type == token_type
def advance(self) -> Token:
# Advance the current token
if not self.is_at_end():
self.current += 1
return self.previous()
def is_at_end(self) -> bool:
# Pretty self explanatory
return self.peek().token_type == TokenType.EOF
def peek(self) -> Token:
# Return the current token
return self.tokens[self.current]
def previous(self) -> Token:
# Return the previous token
return self.tokens[self.current - 1]
def error(self, token: Token, message: str) -> ParseError:
# Raise a parse error
pos = token.pos
code = self.lines[pos[0] - 1]
self.jesse.error(code,pos,message)
return ParseError()
def synchronize(self) -> None:
# Synchronize the parser
self.advance()
while not self.is_at_end():
if self.previous().token_type == TokenType.SEMICOLON:
return
if self.peek().token_type in (
TokenType.COOK,
TokenType.JESSE_IF,
TokenType.SAY_MY_NAME,
TokenType.THE_ONE_WHO_KNOCKS,
TokenType.BETTER_CALL,
):
return
self.advance()