def parse_declaration(self) -> list: # Int thing # Int thing = expr # TODO: module.Thingy thing # # "Int thing = expr" produces overlapping Declaration and # Assignment nodes, that's why this returns a list of nodes datatype = self.parse_name() # TODO: module's Thing variable = self.parse_name() if self.tokens.coming_up().kind == 'NEWLINE': self.tokens.pop_newline() return [ Declaration(Location.between(datatype, variable), datatype, variable.name) ] self.tokens.check_and_pop('OP', '=') initial_value = self.parse_expression() self.tokens.pop_newline() return [ Declaration(Location.between(datatype, variable), datatype, variable.name), Assignment(Location.between(variable, initial_value), variable, initial_value) ]
def parse_function_def(self): # function main() { ... } # function thing() returns Int { ... } function_keyword = self.tokens.check_and_pop('NAME', 'function') name = self.parse_name() args, junk = self._parse_comma_list('(', ')', parsemethod=self._type_and_name) if self.tokens.coming_up().startswith(['NAME', 'returns']): self.tokens.pop() returntype = self.parse_name() else: returntype = None opening_brace = self.tokens.check_and_pop('OP', '{') if self.tokens.coming_up().kind == 'NEWLINE': self.tokens.pop_newline() body = [] while not self.tokens.coming_up().startswith(['OP', '}']): body.extend(self.parse_statement()) closing_brace = self.tokens.check_and_pop('OP', '}') self.tokens.pop_newline() return FunctionDef(Location.between(function_keyword, closing_brace), name.name, args, returntype, body)
def parse_expression(self): coming_up = self.tokens.coming_up() if coming_up.kind == 'NAME': # hello result = self.parse_name() elif coming_up.kind == 'STRING': # "hello" result = self.parse_string() elif coming_up.kind == 'INTEGER': # 123 result = self.parse_integer() elif coming_up.startswith(['OP', '(']): result = self.parse_parentheses() else: raise CompileError( "this should be variable name, string, integer or '('", coming_up.location) # check for function calls, this is a while loop to allow # function calls like thing()()() while self.tokens.coming_up().startswith(['OP', '(']): args, stop_token = self._parse_comma_list('(', ')') result = FunctionCall(Location.between(result, stop_token), result, args) return result
def _parse_comma_list(self, start='(', stop=')', parsemethod=None): # ( ) # ( element ) # ( element , ) # ( element , element ) # ( element , element , ) # ... if parsemethod is None: parsemethod = self.parse_expression start_token = self.tokens.check_and_pop('OP', start) if self.tokens.coming_up().startswith(['OP', stop]): # empty list return ([], self.tokens.pop()) elements = [] while True: if self.tokens.coming_up().startswith(['OP', ',']): raise CompileError("don't put a ',' here", self.tokens.coming_up().location) elements.append(parsemethod()) if self.tokens.coming_up().startswith(['OP', stop]): return (elements, self.tokens.pop()) comma = self.tokens.check_and_pop('OP', ',') if self.tokens.coming_up().startswith(['OP', ',']): raise CompileError( "two ',' characters", Location.between(comma, self.tokens.coming_up())) if self.tokens.coming_up().startswith(['OP', stop]): return (elements, self.tokens.pop())
def assignment(self): # thing = value # TODO: thing's stuff = value target = self.parse_name() self.tokens.check_and_pop('OP', '=') value = self.parse_expression() self.tokens.pop_newline() return Assignment(Location.between(target, value), target, value)
def parse_if(self): # if cond { statements } the_if = self.tokens.check_and_pop('NAME', 'if') condition = self.parse_expression() self.tokens.check_and_pop('OP', '{') body = [] # allow "if thing { }" without a newline if not self.tokens.coming_up().startswith(['OP', '}']): self.tokens.pop_newline() while not self.tokens.coming_up().startswith(['OP', '}']): body.extend(self.parse_statement()) closing_brace = self.tokens.check_and_pop('OP', '}') self.tokens.pop_newline() return If(Location.between(the_if, closing_brace), condition, body)
def parse_return(self): the_return = self.tokens.check_and_pop('NAME', 'return') value = self.parse_expression() self.tokens.pop_newline() return Return(Location.between(the_return, value), value)