diff options
| author | mryouse | 2022-05-22 20:34:02 +0000 |
|---|---|---|
| committer | mryouse | 2022-05-22 20:34:02 +0000 |
| commit | 28e89dff46b23b2fd73704d46db96b86e7e35e3c (patch) | |
| tree | bd6c8de5d5f302a52855d6896c21c6927d73398b | |
| parent | 435934f89b9d3d15b7a22514a40a641d2fe74c31 (diff) | |
refactor: structs into their own file
| -rw-r--r-- | interpreter.py | 20 | ||||
| -rw-r--r-- | lexer.py | 67 | ||||
| -rw-r--r-- | parser.py | 11 | ||||
| -rw-r--r-- | structs.py | 92 |
4 files changed, 109 insertions, 81 deletions
diff --git a/interpreter.py b/interpreter.py index 4adb972..e888944 100644 --- a/interpreter.py +++ b/interpreter.py @@ -1,4 +1,4 @@ -from parser import Expr +from structs import Literal, Symbol, List class Function: @@ -21,7 +21,6 @@ class Builtin(Function): def call(self, expr, env): if self.arities is not None and len(expr.args[1:]) not in self.arities: - #if self.arity >= 0 and len(args) != self.arity: fmt = f"[{self.arities[0]}" for arity in self.arities[1:]: fmt += f", {arity}" @@ -38,7 +37,7 @@ class UserFunction(Function): this_env = Environment(env) for idx, param in enumerate(self.params): # TODO this is wrong!!! this won't always be a literal - this_env.register(param.name, Expr.Literal(evaluate(expr.args[idx+1],env))) + this_env.register(param.name, Literal(evaluate(expr.args[idx+1],env))) return interpret(self.body, this_env) class Environment: @@ -81,15 +80,14 @@ def interpret(exprs, env=GLOBALS): return ret def evaluate(expr, env): - if isinstance(expr, Expr.Literal): + if isinstance(expr, Literal): return expr.value - elif isinstance(expr, Expr.Symbol): + elif isinstance(expr, Symbol): if not env.contains(expr.name): raise Exception(f"no such symbol: {expr}") return evaluate(env.get(expr.name), env) - #name = expr.symbol.name - if not isinstance(expr.args[0], Expr.Symbol): + if not isinstance(expr.args[0], Symbol): raise Exception("can't evaluate without a symbol") name = expr.args[0].name if name == "def": @@ -234,7 +232,7 @@ def interpretPrint(symbol, args, env): GLOBALS.register("print", Builtin(interpretPrint, 1)) def interpretDef(symbol, args, env): - if not isinstance(args[0], Expr.Symbol): + if not isinstance(args[0], Symbol): raise Exception("'def' requires a string literal as a name") name = args[0].name # NOTE: we are not evaluating the name!! if not isinstance(name, str): @@ -285,7 +283,7 @@ def interpretForCount(symbol, args, env): new_env = Environment(env) ret = None for idx in range(0, num): - new_env.register("idx", Expr.Literal(idx + 1)) + new_env.register("idx", Literal(idx + 1)) for arg in args[1:]: ret = evaluate(arg, new_env) return ret @@ -300,7 +298,7 @@ def interpretPipe(symbol, args, env): for arg in args: if pipe is not None: new_env.register("items", pipe) - pipe = Expr.Literal(evaluate(arg, new_env)) + pipe = Literal(evaluate(arg, new_env)) return pipe GLOBALS.register("|", Builtin(interpretPipe)) @@ -322,7 +320,7 @@ def interpretFunc(symbol, args, env): # func <name> (args) (exprs) if len(args) < 3: raise Exception("'func' takes a name, arguments, and at least one expression") - if not isinstance(args[0], Expr.Symbol): + if not isinstance(args[0], Symbol): raise Exception("'func' requires a string literal as a name") name = args[0].name # NOTE: we are not evaluating the name!! @@ -1,6 +1,4 @@ -from dataclasses import dataclass -from enum import Enum, auto -from typing import Any +from structs import TokenType, Token import sys class LexError(BaseException): @@ -8,59 +6,6 @@ class LexError(BaseException): def __init__(self, message, line): super().__init__(f"line {line}: {message}") -class TokenType(Enum): - - PRINT = auto() - - OPEN_PAREN = auto() - CLOSE_PAREN = auto() - - EOF = auto() - - # literals - INT = auto() - FLOAT = auto() - STRING = auto() - TRUE = auto() - FALSE = auto() - - # arithmetic - PLUS = auto() - DASH = auto() - STAR = auto() - SLASH = auto() - - # strings - DOUBLE_QUOTE = auto() - - # comparison - GREATER = auto() - GREATER_EQUAL = auto() - LESS = auto() - LESS_EQUAL = auto() - EQUAL = auto() - NOT = auto() - AND = auto() - OR = auto() - - # flow - IF = auto() - FOR_COUNT = auto() - PIPE = auto() - - # keywords - DEF = auto() - LAMBDA = auto() - - # symbols - SYMBOL = auto() - - # types - INT_TYPE = auto() - FLOAT_TYPE = auto() - STRING_TYPE = auto() - ANY_TYPE = auto() - types = { ":int": TokenType.INT_TYPE, ":float": TokenType.FLOAT_TYPE, @@ -88,16 +33,6 @@ keywords = { "lambda": TokenType.LAMBDA } -@dataclass -class Token: - type_: TokenType - text: str - value: Any - line: int - - def __str__(self): - return f"{self.type_.name} {self.text} {self.line}" - WHITESPACE = [" ", "\n", "\t"] SEPARATORS = WHITESPACE + [")"] DIGITS = list("0123456789") @@ -1,3 +1,5 @@ +from structs import TokenType, Literal, Symbol, Type, List +''' from lexer import TokenType class Expr: @@ -34,6 +36,7 @@ class Expr: visitor.visitNary(self) def __str__(self): return "(" + " ".join(f"{arg}" for arg in self.args) + ")" +''' def parseExpression(token, prev, tokens): idx = 0 @@ -57,16 +60,16 @@ def parseExpression(token, prev, tokens): idx += inc prev = token - return Expr.List(args), idx + 2 # parens + return List(args), idx + 2 # parens def parseSymbol(token, prev, tokens): - return Expr.Symbol(token.text), 1 + return Symbol(token.text), 1 def parseLiteral(token, prev, tokens): - return Expr.Literal(token.value), 1 + return Literal(token.value), 1 def parseType(token, prev, tokens): - return Expr.Type(token.text), 1 + return Type(token.text), 1 def parse(tokens): idx = 0 diff --git a/structs.py b/structs.py new file mode 100644 index 0000000..02f2c50 --- /dev/null +++ b/structs.py @@ -0,0 +1,92 @@ +from dataclasses import dataclass +from enum import Enum, auto +from typing import Any + +# tokens and types +# NOTE: this can probably be simplified +class TokenType(Enum): + + PRINT = auto() + + OPEN_PAREN = auto() + CLOSE_PAREN = auto() + + EOF = auto() + + # literals + INT = auto() + FLOAT = auto() + STRING = auto() + TRUE = auto() + FALSE = auto() + + # arithmetic + PLUS = auto() + DASH = auto() + STAR = auto() + SLASH = auto() + + # strings + DOUBLE_QUOTE = auto() + + # comparison + GREATER = auto() + GREATER_EQUAL = auto() + LESS = auto() + LESS_EQUAL = auto() + EQUAL = auto() + NOT = auto() + AND = auto() + OR = auto() + + # flow + IF = auto() + FOR_COUNT = auto() + PIPE = auto() + + # keywords + DEF = auto() + LAMBDA = auto() + + # symbols + SYMBOL = auto() + + # types + INT_TYPE = auto() + FLOAT_TYPE = auto() + STRING_TYPE = auto() + ANY_TYPE = auto() + +@dataclass +class Token: + type_: TokenType + text: str + value: Any + line: int + + def __str__(self): + return f"{self.type_.name} {self.text} {self.line}" + +class Literal: + def __init__(self, value): + self.value = value + def __str__(self): + return f"{self.value}" + +class Type: + def __init__(self, name): + self.name = name + def __str__(self): + return self.name + +class Symbol: + def __init__(self, name): + self.name = name + def __str__(self): + return f"'{self.name}" + +class List: + def __init__(self, args): + self.args = args + def __str__(self): + return "(" + " ".join(f"{arg}" for arg in self.args) + ")" |
