diff --git a/README.md b/README.md index 9ef11d381add323c7d27828014896370cea4d2da..da30590cf73397b3a12480e807317844228d6ebf 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,9 @@ $ ./main.py -cf "5*5+3" #### Toiminta: -Ohjelma syöttää ensiksi merkkijonon `lex` funktiolle `lexer.py` moduulissa. Se käy merkkijonon läpi ja tuottaa jonon eri tokeneita. Ne voi olla numeroita, tekstiä, sulkeita, numerooperaattoreita, pilkkuja tai '=' merkkejä. +Ohjelma syöttää ensiksi merkkijonon `lex` funktiolle `calc_lexer.py` moduulissa. Se käy merkkijonon läpi ja tuottaa jonon eri tokeneita. Ne voi olla numeroita, tekstiä, sulkeita, numerooperaattoreita, pilkkuja tai '=' merkkejä. -Seuraavaksi tokenijono syötetään `parse` funktioon `parser.py` moduulissa. Se käy läpi tokenijonon yrittäen kerätä Abstract Syntax Tree (AST). Se alkaa `stmt` parserista, joka voi olla joko `fn_impl` tai `expr`, joista `fn_impl` on priorisoitu. `fn_impl` parseri yrittää kerätä tokenijonosta järjestyksessä `fn`, funktion nimen, funktion parametrit (joita voi olla 0 tai useampi), '=' merkki ja lopuksi `expr`. `expr` puolestaan kerää niin monta plus tai miinuslaskua kuin mahdollista muodossa `term` + `+`/`-` + `term`. `term` kerää samalla tavalla niin monta kerto tai jakolaskua kuin mahdollista muodossa `atom` + `*`/`/` + `atom`. `atom` lähes samalla tavalla mutta oikealta vasemmalle kerää niin monta potenssilaskua kuin mahdollista `factor` + `^` + `factor`. `factor` on hieman monimutkaisempi, se yrittää järjestyksessä yhtä: +Seuraavaksi tokenijono syötetään `parse` funktioon `calc_parser.py` moduulissa. Se käy läpi tokenijonon yrittäen kerätä Abstract Syntax Tree (AST). Se alkaa `stmt` parserista, joka voi olla joko `fn_impl` tai `expr`, joista `fn_impl` on priorisoitu. `fn_impl` parseri yrittää kerätä tokenijonosta järjestyksessä `fn`, funktion nimen, funktion parametrit (joita voi olla 0 tai useampi), '=' merkki ja lopuksi `expr`. `expr` puolestaan kerää niin monta plus tai miinuslaskua kuin mahdollista muodossa `term` + `+`/`-` + `term`. `term` kerää samalla tavalla niin monta kerto tai jakolaskua kuin mahdollista muodossa `atom` + `*`/`/` + `atom`. `atom` lähes samalla tavalla mutta oikealta vasemmalle kerää niin monta potenssilaskua kuin mahdollista `factor` + `^` + `factor`. `factor` on hieman monimutkaisempi, se yrittää järjestyksessä yhtä: - `+`/`-` + `factor` Rekursiivisesti kerää kaikki etumerkit. Esim: `- - - - 4` - `num` @@ -80,7 +80,7 @@ $ ./main.py > fn f(x) = 2*x^3-4*x+12 ok > f(2) -22.0 +20.0 > ``` ```bash @@ -107,7 +107,7 @@ $ ./main.py ok > ^C $ ./main.py -> f(x) -22.0 +> f(2) +20.0 > ``` \ No newline at end of file diff --git a/interpreter.py b/calc_interpreter.py similarity index 93% rename from interpreter.py rename to calc_interpreter.py index 9a9cff31331d879005a1d193313ef46b3910c87b..1e83dceb7a30b75ee898e9398ae1c1192bb8e5f2 100644 --- a/interpreter.py +++ b/calc_interpreter.py @@ -2,12 +2,12 @@ import pickle; import math; import inspect; -from result import Result; +from calc_result import Result; # ------------------ -# Interpreter memory +# calc_interpreter memory # ------------------ save_file = "functions.pickle"; @@ -63,7 +63,10 @@ class Factor: class Num: def __init__(self, val): - self.val = float(val); + if val == ".": + self.val = 0.0; + else: + self.val = float(val); def visit(self, memory: Memory): return Result.ok(self.val); @@ -138,11 +141,11 @@ class Func: if func_argc != given_argc: return Result.err(f"Error: function argument count mismatch for: '{self.func}' got {given_argc} expected {func_argc}"); - result = func(memory, *args); - if result.is_err: - return result; - result = result.inner; - return Result.ok(result); + calc_result = func(memory, *args); + if calc_result.is_err: + return calc_result; + calc_result = calc_result.inner; + return Result.ok(calc_result); def __str__(self): return f"{self.func}({','.join([val.__str__() for val in self.csv])})"; @@ -170,10 +173,10 @@ class ImplFunc: for (arg, name) in zip(args, self.csv): memory.arguments[name] = arg; - result = self.body.visit(memory); + calc_result = self.body.visit(memory); memory.arguments.clear(); - return result + return calc_result def __str__(self): return f"fn {self.name}({','.join(self.csv)}) = {self.body}"; diff --git a/lexer.py b/calc_lexer.py similarity index 90% rename from lexer.py rename to calc_lexer.py index bf4ff9e7c5fec4604bb391d208985759ffde88be..1aa06df640fab6a4a670c8585edb3b38e71ab5e5 100644 --- a/lexer.py +++ b/calc_lexer.py @@ -20,7 +20,7 @@ def lex(debug_mode: bool, text: str): # if debug_mode: # print(f"Debug: lexing: {c}. Building num: {building_num}. Building ident: {building_ident}"); - # num lexer + # num calc_lexer if not building_ident and (c.isdigit() or c == '.'): building_num = True; num += c; @@ -30,7 +30,7 @@ def lex(debug_mode: bool, text: str): building_num = False; num = ""; - # ident lexer + # ident calc_lexer if c.isalnum(): building_ident = True; ident += c; @@ -40,19 +40,19 @@ def lex(debug_mode: bool, text: str): building_ident = False; ident = ""; - # group lexer + # group calc_lexer if c in '()': tokens.append((TOKEN_GROUP, c)); - # op lexer + # op calc_lexer elif c in '+-*/^': tokens.append((TOKEN_OP, c)); - # comma lexer + # comma calc_lexer elif c == ',': tokens.append((TOKEN_COMMA, c)); - # eq lexer + # eq calc_lexer elif c == '=': tokens.append((TOKEN_EQ, c)); diff --git a/parser.py b/calc_parser.py similarity index 82% rename from parser.py rename to calc_parser.py index 77168a35a0269dd257e25940fc2b4927a3870db4..73d0f1ae9e7ffd78eb7f4f018bba85c668c4823c 100644 --- a/parser.py +++ b/calc_parser.py @@ -1,7 +1,7 @@ -import lexer; +import calc_lexer; import main; -from result import *; -from interpreter import *; +from calc_result import *; +from calc_interpreter import *; @@ -9,30 +9,30 @@ def parse(debug_mode: bool, tokens: []): return parse_stmt(debug_mode, tokens); # ------- -# parsers +# calc_parsers # ------- def parse_factor(tokens: []): (token_type, token) = tokens.pop(0); - if token_type == lexer.TOKEN_OP: + if token_type == calc_lexer.TOKEN_OP: # parse unary op factor = parse_factor(tokens); if factor.is_err: return factor; factor = factor.inner; return Result.ok(Factor(UnaryOp(token, factor))); - elif token_type == lexer.TOKEN_NUM: + elif token_type == calc_lexer.TOKEN_NUM: # parse num return Result.ok(Factor(Num(token))); - elif token_type == lexer.TOKEN_GROUP and token == '(': + elif token_type == calc_lexer.TOKEN_GROUP and token == '(': # parse (expr) tokens.insert(0, (token_type, token)); return parse_factor_paren(tokens); - elif token_type == lexer.TOKEN_IDENT: + elif token_type == calc_lexer.TOKEN_IDENT: # parse function or a const (paren_token_type, paren_token) = tokens[0]; - if paren_token_type == lexer.TOKEN_GROUP and paren_token == '(': + if paren_token_type == calc_lexer.TOKEN_GROUP and paren_token == '(': # parse function call csv = parse_csv_expr(tokens); if csv.is_err: @@ -44,7 +44,7 @@ def parse_factor(tokens: []): # parse const return Result.ok(Const(token)); - elif token_type == lexer.TOKEN_EOF: + elif token_type == calc_lexer.TOKEN_EOF: return Result.err(f"Unexpected EOF"); else: return Result.err(f"Expected '(', '<num>', '-' or '+'"); @@ -52,7 +52,7 @@ def parse_factor(tokens: []): def parse_factor_paren(tokens: []): (token_type, token) = tokens.pop(0); # group begins with '(' - if token_type != lexer.TOKEN_GROUP or token != '(': + if token_type != calc_lexer.TOKEN_GROUP or token != '(': return Result.ok(f"Expected '('"); expr = parse_expr(tokens); @@ -62,7 +62,7 @@ def parse_factor_paren(tokens: []): # group ends with ')' (end_group_token_type, end_group_token) = tokens.pop(0); - if end_group_token_type == lexer.TOKEN_GROUP and end_group_token == ')': + if end_group_token_type == calc_lexer.TOKEN_GROUP and end_group_token == ')': return Result.ok(expr); else: return Result.err(f"Expected ')'"); @@ -72,7 +72,7 @@ def parse_csv(tokens: [], val): (token_type, token) = tokens.pop(0); # group begins with '(' - if token_type != lexer.TOKEN_GROUP or token != '(': + if token_type != calc_lexer.TOKEN_GROUP or token != '(': return Result.ok(f"Expected '('"); # empty csv @@ -103,7 +103,7 @@ def parse_csv_ident(tokens: []): def parse_ident(tokens: []): (token_type, token) = tokens.pop(0); - if token_type == lexer.TOKEN_IDENT: + if token_type == calc_lexer.TOKEN_IDENT: return Result.ok(token); else: return Result.err(f"Expected ident"); @@ -118,7 +118,7 @@ def parse_infix_list(tokens: [], sides, op_token: str): while True: (token_type, token) = tokens.pop(0); - if token_type != lexer.TOKEN_OP or not token in op_token: + if token_type != calc_lexer.TOKEN_OP or not token in op_token: tokens.insert(0, (token_type, token)); break; @@ -205,13 +205,13 @@ def parse_stmt(debug_mode: bool, tokens: []): # ------- def is_end_paren(token_type, token): - return token_type == lexer.TOKEN_GROUP and token == ')'; + return token_type == calc_lexer.TOKEN_GROUP and token == ')'; def is_comma(token_type, token): - return token_type == lexer.TOKEN_COMMA; + return token_type == calc_lexer.TOKEN_COMMA; def is_fn_keyword(token_type, token): - return token_type == lexer.TOKEN_IDENT and token == "fn"; + return token_type == calc_lexer.TOKEN_IDENT and token == "fn"; def is_eq(token_type, token): - return token_type == lexer.TOKEN_EQ; \ No newline at end of file + return token_type == calc_lexer.TOKEN_EQ; \ No newline at end of file diff --git a/result.py b/calc_result.py similarity index 100% rename from result.py rename to calc_result.py diff --git a/main.py b/main.py index e6e332c44a85b80be93b0a5c6fef4584aaa9cac1..1a0072d761117a786534ce57667ec4d4bff1746c 100755 --- a/main.py +++ b/main.py @@ -2,34 +2,34 @@ import sys; -import lexer; -import parser; -from interpreter import Memory; +import calc_lexer; +import calc_parser; +from calc_interpreter import Memory; def run(debug_mode: bool, dump: bool, text: str): - tokens = lexer.lex(debug_mode, text); + tokens = calc_lexer.lex(debug_mode, text); if debug_mode: - print(f"Debug: lexer returned: '{tokens}'"); + print(f"Debug: calc_lexer returned: '{tokens}'"); - ast = parser.parse(debug_mode, tokens); + ast = calc_parser.parse(debug_mode, tokens); if ast.is_err: print(f"Parse error: {ast.inner}"); return True; ast = ast.inner; if debug_mode: - print(f"Debug: parser returned: '{ast}'"); + print(f"Debug: calc_parser returned: '{ast}'"); if dump: print(f"{ast}"); else: - result = ast.visit(Memory()); - if result.is_err: - print(f"Interpreter error: {result.inner}"); + calc_result = ast.visit(Memory()); + if calc_result.is_err: + print(f"calc_interpreter error: {calc_result.inner}"); return True; - result = result.inner; - print(f"{result}"); + calc_result = calc_result.inner; + print(f"{calc_result}"); return True;