diff --git a/parsed.py b/parsed.py index 25e60cb..a37d0ea 100644 --- a/parsed.py +++ b/parsed.py @@ -2,6 +2,15 @@ from enum import Enum, auto from position import Node from typing import Optional, List +class Type: + def __str__(self) -> str: + raise NotImplementedError() + +class TypeError(Type): + def __init__(self, message: str) -> None: + super().__init__() + self.message = message + class Pattern: def __str__(self) -> str: raise NotImplementedError() @@ -11,6 +20,14 @@ class PatternError(Pattern): super().__init__() self.message = message +class Parameter: + def __init__(self, subject: Pattern, value_type: Type) -> None: + self.subject = subject + self.value_type = value_type + + def __str__(self) -> str: + return f"Parameter {{ subject: {self.subject}, value_type: {self.value_type} }}" + class Expr: def __str__(self) -> str: raise NotImplementedError() @@ -76,6 +93,14 @@ class Unit(Expr): def __str__(self) -> str: return "Unit" +class Tuple(Expr): + def __init__(self, values: List[Node[Expr]]) -> None: + super().__init__() + self.values = values + + def __str__(self) -> str: + values = ", ".join(node.__str__() for node in self.values) + return f"Tuple {{ values: [{values}] }}" class Block(Expr): def __init__(self, statements: List[Node[Expr]], value: Optional[Node[Expr]]) -> None: @@ -87,6 +112,16 @@ class Block(Expr): statements = ", ".join(node.__str__() for node in self.statements) return f"Block {{ statements: [{statements}], value: {self.value} }}" +class Lambda(Expr): + def __init__(self, params: List[Node[Parameter]], body: Node[Expr]) -> None: + super().__init__() + self.params = params + self.body = body + + def __str__(self) -> str: + params = ", ".join(node.__str__() for node in self.params) + return f"Lambda {{ params: [{params}], body: {self.body} }}" + class If(Expr): def __init__(self, condition: Node[Expr], truthy: Node[Expr], falsy: Optional[Node[Expr]]) -> None: @@ -238,3 +273,49 @@ class Assign(Expr): def __str__(self) -> str: return f"Assign {{ assign_type: {self.assign_type}, subject: {self.subject}, value: {self.value} }}" + +class Let(Expr): + def __init__(self, subject: Node[Pattern], value_type: Optional[Node[Type]], value: Node[Expr]) -> None: + super().__init__() + self.subject = subject + self.value_type = value_type + self.value = value + + def __str__(self) -> str: + return f"Let {{ subject: {self.subject}, value_type: {self.value_type}, value: {self.value} }}" + +class Function(Expr): + def __init__(self, subject_id: str, params: List[Node[Parameter]], body: Node[Expr]) -> None: + super().__init__() + self.subject_id = subject_id + self.params = params + self.body = body + + def __str__(self) -> str: + params = ", ".join(node.__str__() for node in self.params) + return f"Function {{ subject_id: {self.subject_id}, params: [{params}], body: {self.body} }}" + +class Return(Expr): + def __init__(self, value: Optional[Node[Expr]]) -> None: + super().__init__() + self.value = value + + def __str__(self) -> str: + return f"Return {{ value: {self.value} }}" + +class Break(Expr): + def __init__(self, value: Optional[Node[Expr]]) -> None: + super().__init__() + self.value = value + + def __str__(self) -> str: + return f"Break {{ value: {self.value} }}" + +class Continue(Expr): + def __init__(self, value: Optional[Node[Expr]]) -> None: + super().__init__() + self.value = value + + def __str__(self) -> str: + return f"Continue {{ value: {self.value} }}" + diff --git a/parser.py b/parser.py index b0e7975..c3b356c 100644 --- a/parser.py +++ b/parser.py @@ -1,7 +1,8 @@ from tokens import Token, TokenType, TokenIterator -from position import Span, Node -from parsed import Assign, AssignType, Binary, BinaryType, Block, Call, MatchArm, PatternError, Expr, For, Id, If, Index, Int, Char, Loop, Pattern, String, ExprError, StructMember, TupleMember, Unary, UnaryType, While +from position import Position, Span, Node +from parsed import Assign, AssignType, Binary, BinaryType, Block, Break, Call, Continue, Match, MatchArm, PatternError, Expr, For, Id, If, Index, Int, Char, Loop, Pattern, Return, String, ExprError, StructMember, Tuple, TupleMember, Unary, UnaryType, Unit, While from typing import List, Optional +from utils import Result, Ok, Err class Parser: @@ -234,6 +235,8 @@ class Parser: value = token.text_slice(self.text) self.step() return Node(String(value), token.span) + elif self.current_is(TokenType.LParen): + return self.parse_unit_group_tuple() elif self.current_is(TokenType.LBrace): return self.parse_block() elif self.current_is(TokenType.KwIf): @@ -249,6 +252,38 @@ class Parser: self.step() return Node(ExprError("expected value"), token.span) + def parse_unit_group_tuple(self) -> Node[Expr]: + begin = self.current().span + self.step() + if self.current_is(TokenType.RParen): + end = self.current().span + self.step() + return Node(Unit(), begin.to(end)) + else: + first_expr = self.parse_expr() + if self.current_is(TokenType.RParen): + end = self.current().span + self.step() + return Node(first_expr.value, begin.to(end)) + elif self.current_is(TokenType.Comma): + values = [first_expr] + end = self.current().span + while self.current_is(TokenType.Comma): + end = self.current().span + self.step() + if self.done() or self.current().token_type == TokenType.RParen: + break + value = self.parse_expr() + end = value.span + values.append(value) + if not self.current_is(TokenType.RParen): + return Node(ExprError("expected ')'"), begin.to(end)) + end = self.current().span + self.step() + return Node(Tuple(values), begin.to(end)) + else: + return Node(ExprError("expected ')' or ','"), begin.to(first_expr.span)) + def parse_block(self) -> Node[Expr]: begin = self.current().span self.step() @@ -288,17 +323,58 @@ class Parser: def parse_match(self) -> Node[Expr]: begin = self.current().span self.step() + value = self.parse_expr() if not self.current_is(TokenType.LBrace): return Node(ExprError("expected '{'"), begin) + lbrace_span = self.current().span self.step() arms: List[Node[MatchArm]] = [] if not self.done() and self.current() != TokenType.RBrace: + arm = self.parse_match_arm() + if not arm.ok(): + return Node(ExprError(arm.error().value), begin.to(arm.error().span)) + arms.append(arm.value()) + while not self.done() and self.current() != TokenType.RBrace: + arm = self.parse_match_arm() + if not arm.ok(): + return Node(ExprError(arm.error().value), begin.to(arm.error().span)) + arms.append(arm.value()) + if not self.current_is(TokenType.RBrace): + if len(arms) > 0: + end = arms[-1].span + else: + end = lbrace_span + return Node(ExprError("expected '}'"), begin.to(end)) + rbrace_span = self.current().span + self.step() + return Node(Match(value, arms), begin.to(rbrace_span)) - def parse_match_arm(self) -> Node[MatchArm]: - pass - def parse_match_arm_statement(self) -> Node[Expr]: - pass + def parse_match_arm(self) -> Result[Node[MatchArm], Node[str]]: + pattern = self.parse_pattern() + if not self.current_is(TokenType.EqualLT): + return Err(Node("expected '=>'", pattern.span.to(pattern.span))) + self.step() + if self.current_is(TokenType.LParen): + expr = self.parse_block() + if self.current_is(TokenType.Comma): + self.step() + else: + expr = self.parse_match_arm_expr() + if not self.current_is(TokenType.Comma): + return Err(Node("expected ','", pattern.span.to(expr.span))) + self.step() + return Ok(Node(MatchArm(pattern, expr), pattern.span.to(expr.span))) + + def parse_match_arm_expr(self) -> Node[Expr]: + if self.current_is(TokenType.KwReturn): + return self.parse_return() + elif self.current_is(TokenType.KwBreak): + return self.parse_break() + elif self.current_is(TokenType.KwContinue): + return self.parse_continue() + else: + return self.parse_expr() def parse_loop(self) -> Node[Expr]: begin = self.current().span @@ -332,6 +408,31 @@ class Parser: body = self.parse_block() return Node(For(subject, value, body), begin.to(body.span)) + + + def parse_return(self) -> Node[Expr]: + begin = self.current().span + self.step() + value: Optional[Node[Expr]] = None + if not self.done() and self.current() not in [TokenType.Comma, TokenType.Semicolon]: + value = self.parse_expr() + return Node(Return(value), begin.to(value.span if value is not None else begin)) + + def parse_break(self) -> Node[Expr]: + begin = self.current().span + self.step() + value: Optional[Node[Expr]] = None + if not self.done() and self.current() not in [TokenType.Comma, TokenType.Semicolon]: + value = self.parse_expr() + return Node(Break(value), begin.to(value.span if value is not None else begin)) + + def parse_continue(self) -> Node[Expr]: + begin = self.current().span + self.step() + value: Optional[Node[Expr]] = None + if not self.done() and self.current() not in [TokenType.Comma, TokenType.Semicolon]: + value = self.parse_expr() + return Node(Continue(value), begin.to(value.span if value is not None else begin)) def parse_pattern(self) -> Node[Pattern]: return Node(PatternError("not implemented"), self.current().span) diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..e7102a9 --- /dev/null +++ b/utils.py @@ -0,0 +1,50 @@ +from typing import Generic, TypeVar + +class UnwrapException(Exception): + def __init__(self, *args: object) -> None: + super().__init__(*args) + +T = TypeVar("T") +E = TypeVar("E") + +class Result(Generic[T, E]): + def __init__(self) -> None: + super().__init__() + + def ok(self) -> bool: + raise NotImplementedError() + + def value(self) -> T: + raise NotImplementedError() + + def error(self) -> E: + raise NotImplementedError() + +class Ok(Result[T, E]): + def __init__(self, value: T) -> None: + super().__init__() + self.__value = value + + def ok(self) -> bool: + return True + + def value(self) -> T: + return self.__value + + def error(self) -> E: + raise UnwrapException("cannot unwrap error of ok result") + +class Err(Result[T, E]): + def __init__(self, error: E) -> None: + super().__init__() + self.__error = error + + def ok(self) -> bool: + return False + + def value(self) -> T: + raise UnwrapException("cannot unwrap value of error result") + + def error(self) -> E: + return self.__error +