From 3590d0ec8e545868de9305281b01e2fccccc6231 Mon Sep 17 00:00:00 2001 From: Overpeek <overpeek.fin@gmail.com> Date: Wed, 5 Jan 2022 23:45:01 +0200 Subject: [PATCH] allow dropping multiplication sign const declaration shorthand division by zero recursion depth limit --- README.md | 19 +++++++++++------ calc_interpreter.py | 24 ++++++++++++++++----- calc_parser.py | 51 ++++++++++++++++++++++++++++++++++++++++++++- main.py | 6 +++--- 4 files changed, 85 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index da30590..f5cfbe5 100644 --- a/README.md +++ b/README.md @@ -31,18 +31,18 @@ $ ./main.py $ ./main.py -d > 4+4 Debug: lexer returned: '[(1, '4'), (3, '+'), (1, '4'), (0, None)]' -Debug: parser returned: '<interpreter.BinaryOp object at 0x7f0f0b51e790>' +Debug: parser returned: '((4.0)+(4.0))' 8.0 > ``` ```bash -$ ./main.py -c "5*(0+5+0)" +$ ./main.py -c "5(0+5+0)" 25.0 ``` ```bash -$ ./main.py -dc "3*sin(cos(2))" +$ ./main.py -dc "3sin(cos(2))" Debug: lexer returned: '[(1, '3'), (3, '*'), (4, 'sin'), (2, '('), (4, 'cos'), (2, '('), (1, '2'), (2, ')'), (2, ')'), (0, None)]' -Debug: parser returned: '<interpreter.BinaryOp object at 0x7f7a676a7790>' +Debug: parser returned: '((3.0)*sin(cos((2.0))))' -1.2127174615567973 ``` ```bash @@ -77,7 +77,7 @@ AST kerättyä se suoritetaan tulkilla. Se käy rekursiivisesti läpi kaikki sen ### Funktiot: ```bash $ ./main.py -> fn f(x) = 2*x^3-4*x+12 +> fn f(x) = 2x^3-4x+12 ok > f(2) 20.0 @@ -100,10 +100,17 @@ ok 54.0 > ``` +```bash +$ x = 3pi +ok +> x +9.42477796076938 +> +``` #### Funktiot tallennetaan: ```bash $ ./main.py -> fn f(x) = 2*x^3-4*x+12 +> fn f(x) = 2x^3-4x+12 ok > ^C $ ./main.py diff --git a/calc_interpreter.py b/calc_interpreter.py index 1e83dce..10ffacc 100644 --- a/calc_interpreter.py +++ b/calc_interpreter.py @@ -38,9 +38,11 @@ class Memory: "round": lambda memory, x: Result.ok(round(x)), }; - self.custom_functions = {} + self.custom_functions = {}; - self.arguments = {} + self.arguments = {}; + + self.depth = 0; self.load(); @@ -74,6 +76,15 @@ class Num: def __str__(self): return f"{self.val}"; +def call_function(func, memory, *args): + memory.depth += 1; + if memory.depth >= 16: + return Result.err(f"Error: Recursion limit"); + + result = func(memory, *args); + memory.depth -= 1; + return result; + class Const: def __init__(self, name): self.name = name; @@ -95,7 +106,7 @@ class Const: if func != None: func_argc = func.argc() if custom else (len(inspect.signature(func).parameters) - 1); if func_argc == 0: - return func(memory); + return call_function(func, memory); if const == None: if func != None: @@ -141,7 +152,7 @@ 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}"); - calc_result = func(memory, *args); + calc_result = call_function(func, memory, *args); if calc_result.is_err: return calc_result; calc_result = calc_result.inner; @@ -226,7 +237,10 @@ class BinaryOp: elif self.op == '*': return Result.ok(lhs * rhs); elif self.op == '/': - return Result.ok(lhs / rhs); + if rhs == 0: + return Result.err(f"Error: Division by zero. LHS: {lhs}"); + else: + return Result.ok(lhs / rhs); elif self.op == '^': return Result.ok(lhs ** rhs); else: diff --git a/calc_parser.py b/calc_parser.py index 73d0f1a..d2ca054 100644 --- a/calc_parser.py +++ b/calc_parser.py @@ -6,7 +6,34 @@ from calc_interpreter import *; def parse(debug_mode: bool, tokens: []): - return parse_stmt(debug_mode, tokens); + # insert missing multiplication ops + index = 0; + while index < len(tokens) - 1: + left = tokens[index]; + right = tokens[index + 1]; + + r_num = right[0] == calc_lexer.TOKEN_NUM; + l_num = left[0] == calc_lexer.TOKEN_NUM; + r_ident = right[0] == calc_lexer.TOKEN_IDENT; + l_r_group = left[0] == calc_lexer.TOKEN_GROUP and left[1] == ')'; + r_l_group = right[0] == calc_lexer.TOKEN_GROUP and right[1] == '('; + + if (l_num and r_ident) or (l_num and r_l_group) or (l_r_group and r_l_group) or (l_r_group and r_l_group) or (l_r_group and r_num) or (l_r_group and r_ident): + tokens.insert(index + 1, (calc_lexer.TOKEN_OP, '*')); + + index += 1; + + ast = parse_stmt(debug_mode, tokens); + + if ast.is_err: + return ast; + + if len(tokens) > 1: + tokens.pop(len(tokens) - 1); + token_str = ' '.join([str(token[1]) for token in tokens]); + return Result.err(f"Error: leftover tokens: '{token_str}'"); + + return ast; # ------- # calc_parsers @@ -194,9 +221,28 @@ def parse_fn_impl(tokens: []): return Result.ok(ImplFunc(name, csv, expr)); +def parse_const_impl(tokens: []): + name = parse_ident(tokens); + if name.is_err: + return name; + name = name.inner; + + if not is_eq(*tokens.pop(0)): + return Result.err("Expected '='"); + + expr = parse_expr(tokens); + if expr.is_err: + return expr; + expr = expr.inner; + + return Result.ok(ImplFunc(name, [], expr)); + + def parse_stmt(debug_mode: bool, tokens: []): if is_fn_keyword(*tokens[0]): return parse_fn_impl(tokens); + elif is_eq(*tokens[1]): + return parse_const_impl(tokens); else: return parse_expr(tokens); @@ -213,5 +259,8 @@ def is_comma(token_type, token): def is_fn_keyword(token_type, token): return token_type == calc_lexer.TOKEN_IDENT and token == "fn"; +def is_cn_keyword(token_type, token): + return token_type == calc_lexer.TOKEN_IDENT and token == "cn"; + def is_eq(token_type, token): return token_type == calc_lexer.TOKEN_EQ; \ No newline at end of file diff --git a/main.py b/main.py index 1a0072d..7bbf716 100755 --- a/main.py +++ b/main.py @@ -11,7 +11,7 @@ from calc_interpreter import Memory; def run(debug_mode: bool, dump: bool, text: str): tokens = calc_lexer.lex(debug_mode, text); if debug_mode: - print(f"Debug: calc_lexer returned: '{tokens}'"); + print(f"Debug: lexer returned: '{tokens}'"); ast = calc_parser.parse(debug_mode, tokens); if ast.is_err: @@ -19,14 +19,14 @@ def run(debug_mode: bool, dump: bool, text: str): return True; ast = ast.inner; if debug_mode: - print(f"Debug: calc_parser returned: '{ast}'"); + print(f"Debug: parser returned: '{ast}'"); if dump: print(f"{ast}"); else: calc_result = ast.visit(Memory()); if calc_result.is_err: - print(f"calc_interpreter error: {calc_result.inner}"); + print(f"Interpreter error: {calc_result.inner}"); return True; calc_result = calc_result.inner; print(f"{calc_result}"); -- GitLab