from typing import Optional, Any, List from jortsc.parser.lexer import Token, TokenType from jortsc.parser.ast_nodes import Function, TypedVar class ParseError(Exception): """Represents a parse error.""" pass class Reader: """Main reader class""" def __init__(self, tokens: List[Token]): self.tokens = tokens self.cur = 0 def next(self) -> Optional[Token]: """Fetch the current token then skip to the next one.""" try: token = self.tokens[self.cur] except IndexError: return None self.cur += 1 return token def expect(self, token_type: TokenType) -> Token: """Check for a specific token type and error if it fails""" token = self.next() if token.type_ != token_type: raise ParseError(f'Expected {token_type}, got {token.type_}') return token def next_safe(self) -> Token: """'Safe' version of next(). Raises an 'Unexpected EOF' error if next() returns None. """ token = self.next() if token is None: raise ParseError('Unexpected EOF') return token def _fn_read_args(reader: Reader, cur: List = None) -> List: """Recursively read the arguments of the function.""" if cur is None: cur = [] # it can be an identifier for the arguments' type, OR a RPAREN # if it is rparen, we stop # if it isnt, we keep going until that happens token = reader.next_safe() if token.value == ')': return cur argtype = token reader.expect(TokenType.whitespace) argname = reader.next_safe() cur.append(TypedVar(argtype.value, argname.value)) return _fn_read_args(reader, cur) def read_function(reader: Reader): """Read a function block.""" reader.expect(TokenType.whitespace) token = reader.next() fn_name = '_anonymous' fn_args = [] print('function token', token) if token.type_ == TokenType.identifier: fn_name = token.value fn_args = _fn_read_args(reader) block = read_start(reader) elif token.value == '(': fn_args = _fn_read_args(reader) block = read_start(reader) return Function(fn_name, fn_args, block) def read_reserved(token: Token, reader: Reader): """Read reserved statements.""" if token.value == 'fn': return read_function(reader) def read_start(reader: Reader): """Read the start of a program.""" print('reader', reader) token = reader.next() ast = [] res = [] print('cur', token) # TODO: statement handling if token.type_ == TokenType.reserved: res = read_reserved(token, reader) # handle blocks elif token.value == '{': while True: token = reader.peek() if token.value == '}': break res.extend(read_start(reader)) ast.append(res) return ast def syntatic(tokens: List[Token]): """Create an AST out of the tokens.""" return read_start(Reader(tokens))