diff --git a/evaluator.py b/evaluator.py index 0c71648..269dc06 100644 --- a/evaluator.py +++ b/evaluator.py @@ -1,10 +1,15 @@ -from typing import List, Optional +from typing import Dict, List, Optional, cast from position import Node +from utils import Err, Ok, Result import parsed class Value: pass +class Unit(Value): + def __init__(self) -> None: + super().__init__() + class Int(Value): def __init__(self, value: int) -> None: super().__init__() @@ -48,6 +53,20 @@ class ContinueResult(EvalResult): super().__init__() self.value = value +class PanicResult(EvalResult): + def __init__(self, message: str) -> None: + super().__init__() + self.message = message + +class SymbolTable: + def __init__(self) -> None: + self.symbols: Dict[str, Value] = {} + + def define(self, symbol_id: str, value: Value) -> Result[None, str]: + if symbol_id in self.symbols: + return Err("symbol already defined") + return Ok(None) + class Evaluator: def __init__(self) -> None: pass @@ -57,60 +76,60 @@ class Evaluator: self.eval_expr(node) def eval_expr(self, node: Node[parsed.Expr]) -> EvalResult: - if node.value is parsed.ExprError: + if isinstance(node.value, parsed.ExprError): + return PanicResult(f"error: {cast(parsed.ExprError, node.value).message}, at {node.span}") + elif isinstance(node.value, parsed.Id): raise NotImplementedError() - elif node.value is parsed.Id: + elif isinstance(node.value, parsed.Int): + return ValueResult(Int(cast(parsed.Int, node.value).value)) + elif isinstance(node.value, parsed.Char): + return ValueResult(Char(cast(parsed.Char, node.value).value)) + elif isinstance(node.value, parsed.String): + return ValueResult(String(cast(parsed.String, node.value).value)) + elif isinstance(node.value, parsed.Bool): + return ValueResult(Bool(cast(parsed.Bool, node.value).value)) + elif isinstance(node.value, parsed.Unit): + return ValueResult(Unit()) + elif isinstance(node.value, parsed.Tuple): raise NotImplementedError() - elif node.value is parsed.Int: + elif isinstance(node.value, parsed.Block): raise NotImplementedError() - elif node.value is parsed.Char: + elif isinstance(node.value, parsed.Lambda): raise NotImplementedError() - elif node.value is parsed.String: + elif isinstance(node.value, parsed.If): raise NotImplementedError() - elif node.value is parsed.Bool: + elif isinstance(node.value, parsed.Match): raise NotImplementedError() - elif node.value is parsed.Unit: + elif isinstance(node.value, parsed.Loop): raise NotImplementedError() - elif node.value is parsed.Tuple: + elif isinstance(node.value, parsed.While): raise NotImplementedError() - elif node.value is parsed.Block: + elif isinstance(node.value, parsed.For): raise NotImplementedError() - elif node.value is parsed.Lambda: + elif isinstance(node.value, parsed.StructMember): raise NotImplementedError() - elif node.value is parsed.If: + elif isinstance(node.value, parsed.TupleMember): raise NotImplementedError() - elif node.value is parsed.Match: + elif isinstance(node.value, parsed.Index): raise NotImplementedError() - elif node.value is parsed.Loop: + elif isinstance(node.value, parsed.Call): raise NotImplementedError() - elif node.value is parsed.While: + elif isinstance(node.value, parsed.Unary): raise NotImplementedError() - elif node.value is parsed.For: + elif isinstance(node.value, parsed.Binary): raise NotImplementedError() - elif node.value is parsed.StructMember: + elif isinstance(node.value, parsed.Assign): raise NotImplementedError() - elif node.value is parsed.TupleMember: + elif isinstance(node.value, parsed.Let): raise NotImplementedError() - elif node.value is parsed.Index: + elif isinstance(node.value, parsed.Function): raise NotImplementedError() - elif node.value is parsed.Call: + elif isinstance(node.value, parsed.Return): raise NotImplementedError() - elif node.value is parsed.Unary: + elif isinstance(node.value, parsed.Break): raise NotImplementedError() - elif node.value is parsed.Binary: - raise NotImplementedError() - elif node.value is parsed.Assign: - raise NotImplementedError() - elif node.value is parsed.Let: - raise NotImplementedError() - elif node.value is parsed.Function: - raise NotImplementedError() - elif node.value is parsed.Return: - raise NotImplementedError() - elif node.value is parsed.Break: - raise NotImplementedError() - elif node.value is parsed.Continue: + elif isinstance(node.value, parsed.Continue): raise NotImplementedError() else: - raise NotImplementedError() + raise NotImplementedError(str(node)) diff --git a/main.py b/main.py index 0259721..8ad342c 100644 --- a/main.py +++ b/main.py @@ -1,14 +1,20 @@ from lexer import Lexer from parser import Parser +from evaluator import Evaluator def main() -> None: - text = "\"\\\"hello\\\\\"" + text = "\"hello\"" lexer = Lexer(text) parser = Parser(text, lexer) + print("parsing...") parsed = parser.parse() + print("ast = ") for node in parsed: print(node) + evaluator = Evaluator() + print("evaluating...") + evaluator.evaluate(parsed) if __name__ == "__main__": main() diff --git a/parsed.py b/parsed.py index a37d0ea..fd38058 100644 --- a/parsed.py +++ b/parsed.py @@ -1,6 +1,7 @@ from enum import Enum, auto from position import Node from typing import Optional, List +from utils import escape_string class Type: def __str__(self) -> str: @@ -64,7 +65,7 @@ class Char(Expr): self.value = value def __str__(self) -> str: - return f"Char('{self.value}')" + return f"Char('{escape_string(self.value)}')" class String(Expr): @@ -73,7 +74,7 @@ class String(Expr): self.value = value def __str__(self) -> str: - return f"String(\"{self.value}\")" + return f"String(\"{escape_string(self.value)}\")" class Bool(Expr): @@ -99,7 +100,7 @@ class Tuple(Expr): self.values = values def __str__(self) -> str: - values = ", ".join(node.__str__() for node in self.values) + values = ", ".join(str(node) for node in self.values) return f"Tuple {{ values: [{values}] }}" class Block(Expr): @@ -109,7 +110,7 @@ class Block(Expr): self.value = value def __str__(self) -> str: - statements = ", ".join(node.__str__() for node in self.statements) + statements = ", ".join(str(node) for node in self.statements) return f"Block {{ statements: [{statements}], value: {self.value} }}" class Lambda(Expr): @@ -119,7 +120,7 @@ class Lambda(Expr): self.body = body def __str__(self) -> str: - params = ", ".join(node.__str__() for node in self.params) + params = ", ".join(str(node) for node in self.params) return f"Lambda {{ params: [{params}], body: {self.body} }}" @@ -205,7 +206,7 @@ class Call(Expr): self.arguments = arguments def __str__(self) -> str: - arguments = ", ".join(node.__str__() for node in self.arguments) + arguments = ", ".join(str(node) for node in self.arguments) return f"Index {{ subject: {self.subject}, arguments: {arguments} }}" class UnaryType(Enum): @@ -292,7 +293,7 @@ class Function(Expr): self.body = body def __str__(self) -> str: - params = ", ".join(node.__str__() for node in self.params) + params = ", ".join(str(node) for node in self.params) return f"Function {{ subject_id: {self.subject_id}, params: [{params}], body: {self.body} }}" class Return(Expr): diff --git a/parser.py b/parser.py index c3b356c..1a397b3 100644 --- a/parser.py +++ b/parser.py @@ -2,7 +2,7 @@ from tokens import Token, TokenType, TokenIterator 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 +from utils import Result, Ok, Err, unescape_string class Parser: @@ -217,24 +217,28 @@ class Parser: def parse_operand(self) -> Node[Expr]: if self.current_is(TokenType.Id): token = self.current() - value = token.text_slice(self.text) self.step() + value = token.text_slice(self.text) return Node(Id(value), token.span) elif self.current_is(TokenType.Int): token = self.current() - value = int(token.text_slice(self.text)) self.step() + value = int(token.text_slice(self.text)) return Node(Int(value), token.span) elif self.current_is(TokenType.Char): token = self.current() - value = token.text_slice(self.text) self.step() - return Node(Char(value), token.span) + value = unescape_string(token.text_slice(self.text)[1:-1]) + if not value.ok(): + return Node(ExprError(value.error()), token.span) + return Node(Char(value.value()), token.span) elif self.current_is(TokenType.String): token = self.current() - value = token.text_slice(self.text) self.step() - return Node(String(value), token.span) + value = unescape_string(token.text_slice(self.text)[1:-1]) + if not value.ok(): + return Node(ExprError(value.error()), token.span) + return Node(String(value.value()), token.span) elif self.current_is(TokenType.LParen): return self.parse_unit_group_tuple() elif self.current_is(TokenType.LBrace): diff --git a/position.py b/position.py index 7a526e3..9f17662 100644 --- a/position.py +++ b/position.py @@ -1,12 +1,14 @@ from __future__ import annotations from typing import NamedTuple, TypeVar, Generic - class Position(NamedTuple): index: int line: int col: int + def __str__(self) -> str: + return f"{self.line}:{self.col}" + class Span(NamedTuple): begin: Position end: Position @@ -14,6 +16,9 @@ class Span(NamedTuple): def to(self, end: Span) -> Span: return Span(self.begin, end.end) + def __str__(self) -> str: + return f"{self.begin} to {self.end}" + T = TypeVar("T") @@ -25,4 +30,4 @@ class Node(Generic[T]): self.span = span def __str__(self) -> str: - return self.value.__str__() + return str(self.value) diff --git a/utils.py b/utils.py index e7102a9..33669b3 100644 --- a/utils.py +++ b/utils.py @@ -48,3 +48,47 @@ class Err(Result[T, E]): def error(self) -> E: return self.__error +def unescape_string(value: str) -> Result[str, str]: + result = "" + i = 0 + while i < len(value): + if value[i] == "\\": + i += 1 + if i >= len(value): + return Err("unescaped '\\'") + elif value[i] == "0": + result += "\0" + elif value[i] == "n": + result += "\n" + elif value[i] == "t": + result += "\t" + elif value[i] == "r": + result += "\r" + else: + result += value[i] + else: + result += value[i] + i += 1 + return Ok(result) + +def escape_string(value: str) -> str: + result = "" + for char in value: + if char == "\0": + result += "\\0" + elif char == "\n": + result += "\\n" + elif char == "\t": + result += "\\t" + elif char == "\r": + result += "\\r" + elif char == "\"": + result += "\\\"" + elif char == "\'": + result += "\\'" + elif char == "\\": + result += "\\\\" + else: + result += char + return result +