From b7d5646badd6319bf0fc8ae51e7490e01fb42cea Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 10 Mar 2019 02:13:22 -0300 Subject: [PATCH] add classes for AST nodes - add Reader.expect, Reader.next_safe - fix fn_read_args, use TypedVar - use Reader.expect in read_function's post-fn check - use Function in read_function - add basics of blocks using { and } in read_start --- jortsc/parser/ast_nodes.py | 13 +++++++++ jortsc/parser/syntatic.py | 60 +++++++++++++++++++++++++++++++------- 2 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 jortsc/parser/ast_nodes.py diff --git a/jortsc/parser/ast_nodes.py b/jortsc/parser/ast_nodes.py new file mode 100644 index 0000000..4d72ec2 --- /dev/null +++ b/jortsc/parser/ast_nodes.py @@ -0,0 +1,13 @@ +from dataclasses import dataclass + +@dataclass +class TypedVar: + type_: str + name: str + + +@dataclass +class Function: + name: str + arguments: str + block: list diff --git a/jortsc/parser/syntatic.py b/jortsc/parser/syntatic.py index f7a3858..7906127 100644 --- a/jortsc/parser/syntatic.py +++ b/jortsc/parser/syntatic.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List from jortsc.parser.lexer import Token, TokenType +from jortsc.parser.ast_nodes import Function, TypedVar class ParseError(Exception): @@ -24,36 +25,60 @@ class Reader: 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 = [] - token = reader.next() + # 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, argname = reader.next(), reader.next() - cur.append((argtype, argname)) + 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.""" - token = reader.next() - - if token.type_ == TokenType.whitespace: - pass - else: - raise ParseError('Expected whitespace') + 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) @@ -62,7 +87,7 @@ def read_function(reader: Reader): fn_args = _fn_read_args(reader) block = read_start(reader) - return (fn_name, fn_args, block) + return Function(fn_name, fn_args, block) def read_reserved(token: Token, reader: Reader): @@ -73,16 +98,29 @@ def read_reserved(token: Token, reader: 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) - ast.extend(res) + # handle blocks + elif token.value == '{': + while True: + token = reader.peek() + + if token.value == '}': + break + + res.extend(read_start(reader)) + + ast.append(res) return ast