def parse_type(self, type_token: Token = None): current = self.tr.current if type_token is None else type_token if current.token_type == TokenType.BLOCK_OPEN: # List type return ListType(self.parse_balanced_brackets(current, self.parse_type, Token(TokenType.BLOCK_CLOSE, ']')))\ .with_code_range(current.code_range) elif current.token_type == TokenType.PAREN_OPEN: # Tuple type def f(): try: t1 = self.parse_type() except: self.tr.skip_to( Token(TokenType.COMMA, ',')) # Skip to comma for error recovery t1 = Error() self.tr.require_type(TokenType.COMMA) t2 = self.parse_type() code_range = CodeRange(t1.code_range.start, t2.code_range.end) return Tuple(t1, t2).with_code_range(code_range) return self.parse_balanced_brackets( current, f, Token(TokenType.PAREN_CLOSE, ')')) elif current.token_type == TokenType.IDENTIFIER: # Type var if type_token is None: self.tr.read() return TypeVarType(current.value, None).with_code_range(current.code_range) else: if type_token is None: self.tr.read() return self.parse_basic_type(current)
def parse_block(self): # TODO order of var decls and stmts def f(): start_range = self.tr.current_code_range() statements = [] while self.tr.current_token_type() != TokenType.CURLY_CLOSE: stmt = self.parse_statement() statements.append(stmt) code_range = CodeRange(start_range.start, self.tr.current_code_range().end) return Block(statements).with_code_range(code_range) return self.parse_balanced_brackets(Token(TokenType.CURLY_OPEN, '{'), f, Token(TokenType.CURLY_CLOSE, '}'))
def parse_fun_decl(self): try: token = self.tr.require_type(TokenType.IDENTIFIER) except: self.errors.append(ExpectedIdentError(self.tr.current)) raise ParseError() name = Text(token.value).with_code_range(token.code_range) def parse_args(): args = [] while self.tr.current_token_type() != TokenType.PAREN_CLOSE: if len(args) > 0: try: self.tr.require_type(TokenType.COMMA) except: self.errors.append( ExpectedSymbolError(',', self.tr.current)) raise ParseError() try: t = self.tr.require_type(TokenType.IDENTIFIER) except: self.errors.append(ExpectedIdentError(self.tr.current)) raise ParseError() args.append(Text(t.value).with_code_range(t.code_range)) return FunArgNames(args) args = self.parse_balanced_brackets(Token(TokenType.PAREN_OPEN, '('), parse_args, Token(TokenType.PAREN_CLOSE, ')')) if self.tr.current_token_type() == TokenType.DOUBLECOLON: self.tr.read() fun_type = self.parse_fun_type() else: fun_type = None block = self.parse_block() try: assert isinstance(block, Error) or len(block.statements) > 0 except: self.errors.append( EmptyFunctionBodyError(name.value, name.code_range)) return FunDecl(name, args, fun_type, block, None).with_code_range(name.code_range)
def f(): try: t1 = self.parse_type() except: self.tr.skip_to( Token(TokenType.COMMA, ',')) # Skip to comma for error recovery t1 = Error() self.tr.require_type(TokenType.COMMA) t2 = self.parse_type() code_range = CodeRange(t1.code_range.start, t2.code_range.end) return Tuple(t1, t2).with_code_range(code_range)
def parse_return_type(self): current = self.tr.current if current.token_type == TokenType.KEYWORD and current.value == 'Void': self.tr.read() return VoidReturn().with_code_range(current.code_range) else: try: t = self.parse_type() except: t = Error() self.tr.skip_to( Token(TokenType.CURLY_OPEN, '{')) # try skipping to { and continue parsing return ValueReturn(t).with_code_range( CodeRange(current.code_range.start, self.tr.current_code_range().end))
def parse_fun_type(self): start = self.tr.current_code_range().start args = [] try: while self.tr.current_token_type() != TokenType.ARROW: args.append(self.parse_type()) except: args.append(Error()) current = self.tr.current self.tr.skip_to(Token( TokenType.ARROW, '->')) # when error, try skipping to arrow and continue parse try: self.tr.require_type(TokenType.ARROW) except: self.errors.append(ExpectedSymbolError('->', current)) raise ParseError() return_type = self.parse_return_type() code_range = CodeRange(start, self.tr.current_code_range().end) return FunctionType(FunArgs(args), return_type).with_code_range(code_range)