From 229480762df681bb3656f50df14da99054702a04 Mon Sep 17 00:00:00 2001 From: Vedanth Padmaraman Date: Wed, 26 Jun 2024 12:11:20 +0530 Subject: [PATCH 01/10] Better approach to returns --- src/backend/c-lisp.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/backend/c-lisp.py b/src/backend/c-lisp.py index 6f69e57..deb32bf 100755 --- a/src/backend/c-lisp.py +++ b/src/backend/c-lisp.py @@ -87,10 +87,25 @@ def gen_function(self, func): parm_types.append(parm[1]) self.symbol_types[parm[0]] = parm[1] self.function_types[name] = [ret_type, parm_types] + + # Return value, instruction and label + ret_sym, ret_label = [random_label(CLISP_PREFIX, [extra]) for extra in ("ret_sym", "ret_lbl")] + self.ret_jmp_instr = ["jmp", ret_label] + self.ret_sym_and_type = [ret_sym, ret_type] + + body_instrs = self.gen_compound_stmt(func[2:], new_scope=False) + if len(body_instrs) > 0: + if not body_instrs[-1] == self.ret_jmp_instr: + raise CodegenError(f"Implicit return in function {func[1]}") + body_instrs.append(["label", ret_label]) + if ret_type == "void": + body_instrs.append(["ret"]) + else: + body_instrs.append(["ret", ret_sym]) return [ "define", func[1], - *self.gen_compound_stmt(func[2:], new_scope=False), + *body_instrs, ] def gen_stmt(self, stmt): @@ -233,11 +248,14 @@ def is_ret_stmt(self, stmt): def gen_ret_stmt(self, stmt): if len(stmt) == 1: - return [["ret"]] + return [self.ret_jmp_instr] elif len(stmt) == 2: res_sym = random_label(CLISP_PREFIX) - instr_list = self.gen_expr(stmt[1], res_sym=res_sym) - instr_list.append(["ret", res_sym]) + instr_list = [ + *self.gen_expr(stmt[1], res_sym=res_sym), + ["set", self.ret_sym_and_type, ["id", res_sym]], + self.ret_jmp_instr, + ] return instr_list else: raise CodegenError( From 202f2afed2390d8d20356f079db0b180ea8186e0 Mon Sep 17 00:00:00 2001 From: Vedanth Padmaraman Date: Wed, 26 Jun 2024 12:25:03 +0530 Subject: [PATCH 02/10] Report error when an undelared function is called --- src/backend/c-lisp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/c-lisp.py b/src/backend/c-lisp.py index deb32bf..6dc62f0 100755 --- a/src/backend/c-lisp.py +++ b/src/backend/c-lisp.py @@ -318,6 +318,8 @@ def gen_call_expr(self, expr, res_sym): arg_syms.append(arg_sym) instr_list += self.gen_expr(arg, res_sym=arg_sym) name = expr[1] + if not name in self.function_types: + raise CodegenError(f"Call to undeclared function: {name}") instr_list.append( ["set", [res_sym, self.function_types[name][0]], ["call", name, *arg_syms]] ) From c7d7b5bbc71b9ed5bc4a574737efd4d2be52ccb3 Mon Sep 17 00:00:00 2001 From: Vedanth Padmaraman Date: Thu, 27 Jun 2024 13:37:57 +0530 Subject: [PATCH 03/10] Allow undefined return values --- src/backend/c-lisp.py | 51 +++++++++++++++--------- src/backend/tests/c-lisp/for-return.out | 1 + src/backend/tests/c-lisp/for-return.sexp | 29 ++++++++++++++ 3 files changed, 63 insertions(+), 18 deletions(-) create mode 100644 src/backend/tests/c-lisp/for-return.out create mode 100644 src/backend/tests/c-lisp/for-return.sexp diff --git a/src/backend/c-lisp.py b/src/backend/c-lisp.py index 6dc62f0..7cccdcf 100755 --- a/src/backend/c-lisp.py +++ b/src/backend/c-lisp.py @@ -88,20 +88,38 @@ def gen_function(self, func): self.symbol_types[parm[0]] = parm[1] self.function_types[name] = [ret_type, parm_types] - # Return value, instruction and label - ret_sym, ret_label = [random_label(CLISP_PREFIX, [extra]) for extra in ("ret_sym", "ret_lbl")] - self.ret_jmp_instr = ["jmp", ret_label] - self.ret_sym_and_type = [ret_sym, ret_type] - - body_instrs = self.gen_compound_stmt(func[2:], new_scope=False) - if len(body_instrs) > 0: - if not body_instrs[-1] == self.ret_jmp_instr: - raise CodegenError(f"Implicit return in function {func[1]}") - body_instrs.append(["label", ret_label]) + if len(func) > 2: + # Return value, instruction and label + self.ret_ptr_sym, ret_label, ret_alloc_size_sym, ret_val_sym = [ + random_label(CLISP_PREFIX, [extra]) + for extra in ("ret_ptr", "ret_lbl", "ret_alloc", "ret_val") + ] + self.ret_jmp_instr = ["jmp", ret_label] if ret_type == "void": - body_instrs.append(["ret"]) + ret_alloc_instrs = [] + ret_instrs = [["ret"]] else: - body_instrs.append(["ret", ret_sym]) + ret_alloc_instrs = [ + ["set", [ret_alloc_size_sym, "int"], ["const", 1]], + [ + "set", + [self.ret_ptr_sym, ["ptr", ret_type]], + ["alloc", ret_alloc_size_sym], + ], + ] + ret_instrs = [ + ["set", [ret_val_sym, ret_type], ["load", self.ret_ptr_sym]], + ["ret", ret_val_sym], + ] + body_instrs = [ + *ret_alloc_instrs, + *self.gen_compound_stmt(func[2:], new_scope=False), + ["label", ret_label], + *ret_instrs, + ] + else: + body_instrs = [] + return [ "define", func[1], @@ -253,7 +271,7 @@ def gen_ret_stmt(self, stmt): res_sym = random_label(CLISP_PREFIX) instr_list = [ *self.gen_expr(stmt[1], res_sym=res_sym), - ["set", self.ret_sym_and_type, ["id", res_sym]], + ["store", self.ret_ptr_sym, res_sym], self.ret_jmp_instr, ] return instr_list @@ -348,9 +366,7 @@ def gen_fixed_type_expr(self, expr, res_sym): typ, n_ops = self.fixed_op_types[opcode] if not (len(expr) == n_ops + 1): raise CodegenError(f"`{opcode}` takes only 2 operands: {expr}") - in_syms = [ - random_label(CLISP_PREFIX, [f"inp_{n}"]) for n in range(n_ops) - ] + in_syms = [random_label(CLISP_PREFIX, [f"inp_{n}"]) for n in range(n_ops)] input_instrs = [] for n in range(n_ops): input_instrs += [*self.gen_expr(expr[n + 1], in_syms[n])] @@ -396,8 +412,7 @@ def gen_store_expr(self, expr, res_sym): raise CodegenError(f"Bad store expression: {expr}") val_sym, ptr_sym = [ - random_label(CLISP_PREFIX, [extra]) - for extra in ("val", "ptr") + random_label(CLISP_PREFIX, [extra]) for extra in ("val", "ptr") ] return [ *self.gen_expr(expr[1], res_sym=ptr_sym), diff --git a/src/backend/tests/c-lisp/for-return.out b/src/backend/tests/c-lisp/for-return.out new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/src/backend/tests/c-lisp/for-return.out @@ -0,0 +1 @@ +4 diff --git a/src/backend/tests/c-lisp/for-return.sexp b/src/backend/tests/c-lisp/for-return.sexp new file mode 100644 index 0000000..abb4280 --- /dev/null +++ b/src/backend/tests/c-lisp/for-return.sexp @@ -0,0 +1,29 @@ +(c-lisp + (define ((print int) (n int))) + + (define ((cons void) (arr (ptr int)) (n int)) + (declare (i int)) + (for ((set i 0) + (lt i n) + (set i (add i 1))) + (store + (ptradd arr i) + (mul i 3)))) + + (define ((find int) (arr (ptr int)) (key int) (n int)) + (declare (i int)) + (for ((set i 0) + (lt i n) + (set i (add i 1))) + (if (eq key + (load (ptradd arr i))) + (ret i)))) + + (define ((main void)) + (declare (input (ptr int))) + (declare (i int)) + + (set input (alloc int 10)) + (call cons input 10) + (call print (call find input 12 10)) + (ret))) From 0549e518a612877465839c927154f0138269d3e5 Mon Sep 17 00:00:00 2001 From: Vedanth Padmaraman Date: Thu, 27 Jun 2024 14:34:29 +0530 Subject: [PATCH 04/10] Bring up error testing infrastructure + test for undeclared function --- src/backend/c-lisp.py | 2 +- src/backend/tests/c-lisp/errors/turnt.toml | 1 + src/backend/tests/c-lisp/errors/undeclared-fn.out | 2 ++ src/backend/tests/c-lisp/errors/undeclared-fn.sexp | 3 +++ 4 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 src/backend/tests/c-lisp/errors/turnt.toml create mode 100644 src/backend/tests/c-lisp/errors/undeclared-fn.out create mode 100644 src/backend/tests/c-lisp/errors/undeclared-fn.sexp diff --git a/src/backend/c-lisp.py b/src/backend/c-lisp.py index 7cccdcf..c82b9bf 100755 --- a/src/backend/c-lisp.py +++ b/src/backend/c-lisp.py @@ -148,7 +148,7 @@ def gen_stmt(self, stmt): else: return self.gen_expr(stmt) except Exception as e: - print(f"Error in statement: {stmt}") + print(f"Error in statement: {stmt}", file = sys.stderr) raise e def is_while_stmt(self, stmt): diff --git a/src/backend/tests/c-lisp/errors/turnt.toml b/src/backend/tests/c-lisp/errors/turnt.toml new file mode 100644 index 0000000..0a7454e --- /dev/null +++ b/src/backend/tests/c-lisp/errors/turnt.toml @@ -0,0 +1 @@ +command = "guile ../../../utils/sexp-json.scm < {filename} | python ../../../c-lisp.py 2>&1 | sed -e '/^[ (Traceback)]/d'" diff --git a/src/backend/tests/c-lisp/errors/undeclared-fn.out b/src/backend/tests/c-lisp/errors/undeclared-fn.out new file mode 100644 index 0000000..1f38ce8 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/undeclared-fn.out @@ -0,0 +1,2 @@ +Error in statement: ['call', 'print', 4] +CodegenError: Call to undeclared function: print diff --git a/src/backend/tests/c-lisp/errors/undeclared-fn.sexp b/src/backend/tests/c-lisp/errors/undeclared-fn.sexp new file mode 100644 index 0000000..eeac15a --- /dev/null +++ b/src/backend/tests/c-lisp/errors/undeclared-fn.sexp @@ -0,0 +1,3 @@ +(c-lisp + (define ((main int)) + (call print 4))) From b480b5bffed56f487f9b08a9a1255d256b8e4880 Mon Sep 17 00:00:00 2001 From: Vedanth Padmaraman Date: Thu, 27 Jun 2024 14:44:44 +0530 Subject: [PATCH 05/10] Add more test cases for return statement positioning --- src/backend/tests/c-lisp/for-return.out | 4 +++- src/backend/tests/c-lisp/for-return.sexp | 20 +++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/backend/tests/c-lisp/for-return.out b/src/backend/tests/c-lisp/for-return.out index b8626c4..de2df04 100644 --- a/src/backend/tests/c-lisp/for-return.out +++ b/src/backend/tests/c-lisp/for-return.out @@ -1 +1,3 @@ -4 +12 +-2 +-1 diff --git a/src/backend/tests/c-lisp/for-return.sexp b/src/backend/tests/c-lisp/for-return.sexp index abb4280..ac64eb4 100644 --- a/src/backend/tests/c-lisp/for-return.sexp +++ b/src/backend/tests/c-lisp/for-return.sexp @@ -19,11 +19,29 @@ (load (ptradd arr i))) (ret i)))) + (define ((verify int) (arr (ptr int)) (key int) (idx int) (len int)) + (declare (val int)) + (if (lt idx len) + ((set val (load (ptradd arr idx))) + (if (eq val key) + (ret val) + (ret -1))) + (ret -2))) + (define ((main void)) (declare (input (ptr int))) (declare (i int)) (set input (alloc int 10)) (call cons input 10) - (call print (call find input 12 10)) + + (declare (12-idx int)) + (declare (13-idx int)) + + (set 12-idx (call find input 12 10)) + (set 13-idx (call find input 13 10)) + + (call print (call verify input 12 12-idx 10)) + (call print (call verify input 13 13-idx 10)) + (call print (call verify input 12 2 10)) (ret))) From a3f401e378aa6ad00566413dfcfcfe51daa66232 Mon Sep 17 00:00:00 2001 From: Vedanth Padmaraman Date: Thu, 27 Jun 2024 16:06:10 +0530 Subject: [PATCH 06/10] More error tests + some cleanup --- src/backend/c-lisp.py | 18 ++++++------------ src/backend/tests/c-lisp/errors/redeclare.out | 2 ++ src/backend/tests/c-lisp/errors/redeclare.sexp | 9 +++++++++ .../tests/c-lisp/errors/undeclared-var.out | 2 ++ .../tests/c-lisp/errors/undeclared-var.sexp | 4 ++++ 5 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 src/backend/tests/c-lisp/errors/redeclare.out create mode 100644 src/backend/tests/c-lisp/errors/redeclare.sexp create mode 100644 src/backend/tests/c-lisp/errors/undeclared-var.out create mode 100644 src/backend/tests/c-lisp/errors/undeclared-var.sexp diff --git a/src/backend/c-lisp.py b/src/backend/c-lisp.py index c82b9bf..317044b 100755 --- a/src/backend/c-lisp.py +++ b/src/backend/c-lisp.py @@ -148,7 +148,7 @@ def gen_stmt(self, stmt): else: return self.gen_expr(stmt) except Exception as e: - print(f"Error in statement: {stmt}", file = sys.stderr) + print(f"Error in statement: {stmt}", file=sys.stderr) raise e def is_while_stmt(self, stmt): @@ -300,9 +300,6 @@ def is_set_expr(self, expr): def gen_set_expr(self, expr, res_sym): name = expr[1] scoped_name = self.scoped_lookup(name) - if not scoped_name in self.symbol_types: - raise CodegenError(f"Cannot set undeclared variable: {name}") - instr_list = self.gen_expr(expr[2], res_sym=res_sym) instr_list.append( ["set", [scoped_name, self.symbol_types[scoped_name]], ["id", res_sym]] @@ -348,14 +345,11 @@ def is_var_expr(self, expr): def gen_var_expr(self, expr, res_sym): scoped_name = self.scoped_lookup(expr) - if scoped_name in self.symbol_types: - typ = self.symbol_types[scoped_name] - instr_list = [["set", [res_sym, typ], ["id", scoped_name]]] - if typ[0] == "ptr": - self.pointer_types[res_sym] = typ - return instr_list - else: - raise CodegenError(f"Reference to undeclared variable: {expr}") + typ = self.symbol_types[scoped_name] + instr_list = [["set", [res_sym, typ], ["id", scoped_name]]] + if typ[0] == "ptr": + self.pointer_types[res_sym] = typ + return instr_list def is_fixed_type_expr(self, expr): return expr[0] in self.fixed_op_types diff --git a/src/backend/tests/c-lisp/errors/redeclare.out b/src/backend/tests/c-lisp/errors/redeclare.out new file mode 100644 index 0000000..8243c4a --- /dev/null +++ b/src/backend/tests/c-lisp/errors/redeclare.out @@ -0,0 +1,2 @@ +Error in statement: ['declare', ['var', 'bool']] +CodegenError: Re-declaration of variable var diff --git a/src/backend/tests/c-lisp/errors/redeclare.sexp b/src/backend/tests/c-lisp/errors/redeclare.sexp new file mode 100644 index 0000000..4cc7b85 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/redeclare.sexp @@ -0,0 +1,9 @@ +(c-lisp + (define ((main void)) + (declare (var int)) + + (if #t + ((declare (var float)) + (set var 0.5))) + + (declare (var bool)))) diff --git a/src/backend/tests/c-lisp/errors/undeclared-var.out b/src/backend/tests/c-lisp/errors/undeclared-var.out new file mode 100644 index 0000000..29c1e34 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/undeclared-var.out @@ -0,0 +1,2 @@ +Error in statement: ['set', 'var', 5] +CodegenError: Undeclared symbol: var diff --git a/src/backend/tests/c-lisp/errors/undeclared-var.sexp b/src/backend/tests/c-lisp/errors/undeclared-var.sexp new file mode 100644 index 0000000..e6d7e80 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/undeclared-var.sexp @@ -0,0 +1,4 @@ +(c-lisp + (define ((main int)) + (set var 5) + (ret var))) From 9b74fe5e2f9e61a329a68d9b30f6b5c7bc289bda Mon Sep 17 00:00:00 2001 From: Vedanth Padmaraman Date: Thu, 27 Jun 2024 16:59:54 +0530 Subject: [PATCH 07/10] Comments regarding the new return codegen --- src/backend/c-lisp.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/backend/c-lisp.py b/src/backend/c-lisp.py index 317044b..cb3266f 100755 --- a/src/backend/c-lisp.py +++ b/src/backend/c-lisp.py @@ -88,18 +88,18 @@ def gen_function(self, func): self.symbol_types[parm[0]] = parm[1] self.function_types[name] = [ret_type, parm_types] + # If the function has a body, we need to codegen for it if len(func) > 2: - # Return value, instruction and label + # Set up a return label and a return variable, if needed self.ret_ptr_sym, ret_label, ret_alloc_size_sym, ret_val_sym = [ random_label(CLISP_PREFIX, [extra]) for extra in ("ret_ptr", "ret_lbl", "ret_alloc", "ret_val") ] self.ret_jmp_instr = ["jmp", ret_label] - if ret_type == "void": - ret_alloc_instrs = [] - ret_instrs = [["ret"]] - else: + if not ret_type == "void": + # Function returns something, so we have to maintain a return variable ret_alloc_instrs = [ + # Allocate space for the return variable ["set", [ret_alloc_size_sym, "int"], ["const", 1]], [ "set", @@ -107,17 +107,25 @@ def gen_function(self, func): ["alloc", ret_alloc_size_sym], ], ] - ret_instrs = [ + ret_label_instrs = [ + # Load the return variable and return it. + # Control jumps here for function return. + ["label", ret_label], ["set", [ret_val_sym, ret_type], ["load", self.ret_ptr_sym]], ["ret", ret_val_sym], ] + else: + # No return value, so no need to maintain a return variable + ret_alloc_instrs = [] + ret_label_instrs = [["label", ret_label], ["ret"]] + body_instrs = [ *ret_alloc_instrs, - *self.gen_compound_stmt(func[2:], new_scope=False), - ["label", ret_label], - *ret_instrs, + *self.gen_compound_stmt(func[2:], new_scope=False), # C-Lisp function body + *ret_label_instrs, ] else: + # This is a declaration without a body body_instrs = [] return [ From 7094c42cfbe4b4e5f372565c19872c563db0282c Mon Sep 17 00:00:00 2001 From: Vedanth Padmaraman Date: Fri, 28 Jun 2024 12:06:28 +0530 Subject: [PATCH 08/10] More error tests --- src/backend/c-lisp.py | 10 ++++++---- src/backend/tests/c-lisp/errors/alloc-bad.out | 2 ++ src/backend/tests/c-lisp/errors/alloc-bad.sexp | 4 ++++ src/backend/tests/c-lisp/errors/bad-expression.out | 2 ++ .../tests/c-lisp/errors/bad-expression.sexp | 4 ++++ src/backend/tests/c-lisp/errors/declare-bad.out | 2 ++ src/backend/tests/c-lisp/errors/declare-bad.sexp | 4 ++++ src/backend/tests/c-lisp/errors/fixed-op-count.out | 2 ++ .../tests/c-lisp/errors/fixed-op-count.sexp | 3 +++ src/backend/tests/c-lisp/errors/fn-proto-bad.out | 1 + src/backend/tests/c-lisp/errors/fn-proto-bad.sexp | 3 +++ src/backend/tests/c-lisp/errors/for-bad.out | 2 ++ src/backend/tests/c-lisp/errors/for-bad.sexp | 6 ++++++ src/backend/tests/c-lisp/errors/if-bad.out | 2 ++ src/backend/tests/c-lisp/errors/if-bad.sexp | 8 ++++++++ src/backend/tests/c-lisp/errors/load-bad.out | 2 ++ src/backend/tests/c-lisp/errors/load-bad.sexp | 4 ++++ src/backend/tests/c-lisp/errors/not-clisp.out | 1 + src/backend/tests/c-lisp/errors/not-clisp.sexp | 3 +++ src/backend/tests/c-lisp/errors/not-function.out | 1 + src/backend/tests/c-lisp/errors/not-function.sexp | 4 ++++ src/backend/tests/c-lisp/errors/ptradd-bad.out | 2 ++ src/backend/tests/c-lisp/errors/ptradd-bad.sexp | 5 +++++ src/backend/tests/c-lisp/errors/redeclare.out | 2 +- src/backend/tests/c-lisp/errors/redeclare.sexp | 6 +++--- src/backend/tests/c-lisp/errors/store-bad.out | 2 ++ src/backend/tests/c-lisp/errors/store-bad.sexp | 5 +++++ src/backend/tests/c-lisp/errors/while-bad.out | 2 ++ src/backend/tests/c-lisp/errors/while-bad.sexp | 4 ++++ src/backend/tests/c-lisp/for-return.sexp | 14 +++++++------- 30 files changed, 97 insertions(+), 15 deletions(-) create mode 100644 src/backend/tests/c-lisp/errors/alloc-bad.out create mode 100644 src/backend/tests/c-lisp/errors/alloc-bad.sexp create mode 100644 src/backend/tests/c-lisp/errors/bad-expression.out create mode 100644 src/backend/tests/c-lisp/errors/bad-expression.sexp create mode 100644 src/backend/tests/c-lisp/errors/declare-bad.out create mode 100644 src/backend/tests/c-lisp/errors/declare-bad.sexp create mode 100644 src/backend/tests/c-lisp/errors/fixed-op-count.out create mode 100644 src/backend/tests/c-lisp/errors/fixed-op-count.sexp create mode 100644 src/backend/tests/c-lisp/errors/fn-proto-bad.out create mode 100644 src/backend/tests/c-lisp/errors/fn-proto-bad.sexp create mode 100644 src/backend/tests/c-lisp/errors/for-bad.out create mode 100644 src/backend/tests/c-lisp/errors/for-bad.sexp create mode 100644 src/backend/tests/c-lisp/errors/if-bad.out create mode 100644 src/backend/tests/c-lisp/errors/if-bad.sexp create mode 100644 src/backend/tests/c-lisp/errors/load-bad.out create mode 100644 src/backend/tests/c-lisp/errors/load-bad.sexp create mode 100644 src/backend/tests/c-lisp/errors/not-clisp.out create mode 100644 src/backend/tests/c-lisp/errors/not-clisp.sexp create mode 100644 src/backend/tests/c-lisp/errors/not-function.out create mode 100644 src/backend/tests/c-lisp/errors/not-function.sexp create mode 100644 src/backend/tests/c-lisp/errors/ptradd-bad.out create mode 100644 src/backend/tests/c-lisp/errors/ptradd-bad.sexp create mode 100644 src/backend/tests/c-lisp/errors/store-bad.out create mode 100644 src/backend/tests/c-lisp/errors/store-bad.sexp create mode 100644 src/backend/tests/c-lisp/errors/while-bad.out create mode 100644 src/backend/tests/c-lisp/errors/while-bad.sexp diff --git a/src/backend/c-lisp.py b/src/backend/c-lisp.py index a86ca02..616095e 100755 --- a/src/backend/c-lisp.py +++ b/src/backend/c-lisp.py @@ -96,7 +96,7 @@ def gen_function(self, func): for extra in ("ret_ptr", "ret_lbl", "ret_alloc", "ret_val") ] self.ret_jmp_instr = ["jmp", ret_label] - if not ret_type == "void": + if ret_type != "void": # Function returns something, so we have to maintain a return variable ret_alloc_instrs = [ # Allocate space for the return variable @@ -121,7 +121,9 @@ def gen_function(self, func): body_instrs = [ *ret_alloc_instrs, - *self.gen_compound_stmt(func[2:], new_scope=False), # C-Lisp function body + *self.gen_compound_stmt( + func[2:], new_scope=False + ), # C-Lisp function body *ret_label_instrs, ] else: @@ -260,7 +262,7 @@ def is_decl_stmt(self, stmt): def gen_decl_stmt(self, stmt): if not len(stmt) == 3: - raise CodegenError(f"bad declare statement: {stmt}") + raise CodegenError(f"Bad declare statement: {stmt}") name, typ = stmt[1], stmt[2] scoped_name = self.construct_scoped_name(name, self.scopes) @@ -341,7 +343,7 @@ def gen_call_expr(self, expr, res_sym): arg_syms.append(arg_sym) instr_list += self.gen_expr(arg, res_sym=arg_sym) name = expr[1] - if not name in self.function_types: + if name not in self.function_types: raise CodegenError(f"Call to undeclared function: {name}") instr_list.append( ["set", [res_sym, self.function_types[name][0]], ["call", name, *arg_syms]] diff --git a/src/backend/tests/c-lisp/errors/alloc-bad.out b/src/backend/tests/c-lisp/errors/alloc-bad.out new file mode 100644 index 0000000..b9e99d8 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/alloc-bad.out @@ -0,0 +1,2 @@ +Error in statement: ['set', 'arr', ['alloc', 3]] +CodegenError: Bad alloc expression: ['alloc', 3] diff --git a/src/backend/tests/c-lisp/errors/alloc-bad.sexp b/src/backend/tests/c-lisp/errors/alloc-bad.sexp new file mode 100644 index 0000000..33adc4a --- /dev/null +++ b/src/backend/tests/c-lisp/errors/alloc-bad.sexp @@ -0,0 +1,4 @@ +(c-lisp + (define ((main void)) + (declare arr (ptr float)) + (set arr (alloc 3)))) diff --git a/src/backend/tests/c-lisp/errors/bad-expression.out b/src/backend/tests/c-lisp/errors/bad-expression.out new file mode 100644 index 0000000..07c8b50 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/bad-expression.out @@ -0,0 +1,2 @@ +Error in statement: ['while', [True], ['call', 'print', 4]] +CodegenError: Bad expression: [True] diff --git a/src/backend/tests/c-lisp/errors/bad-expression.sexp b/src/backend/tests/c-lisp/errors/bad-expression.sexp new file mode 100644 index 0000000..a0a6e04 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/bad-expression.sexp @@ -0,0 +1,4 @@ +(c-lisp + (define ((main int)) + (while (#t) + (call print 4)))) diff --git a/src/backend/tests/c-lisp/errors/declare-bad.out b/src/backend/tests/c-lisp/errors/declare-bad.out new file mode 100644 index 0000000..c1aba82 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/declare-bad.out @@ -0,0 +1,2 @@ +Error in statement: ['declare', 'a'] +CodegenError: Bad declare statement: ['declare', 'a'] diff --git a/src/backend/tests/c-lisp/errors/declare-bad.sexp b/src/backend/tests/c-lisp/errors/declare-bad.sexp new file mode 100644 index 0000000..361879e --- /dev/null +++ b/src/backend/tests/c-lisp/errors/declare-bad.sexp @@ -0,0 +1,4 @@ +(c-lisp + (define ((main void)) + (declare a) + (call print a))) diff --git a/src/backend/tests/c-lisp/errors/fixed-op-count.out b/src/backend/tests/c-lisp/errors/fixed-op-count.out new file mode 100644 index 0000000..ee48f23 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/fixed-op-count.out @@ -0,0 +1,2 @@ +Error in statement: ['ret', ['add', 3, 4, 5]] +CodegenError: `add` takes only 2 operands: ['add', 3, 4, 5] diff --git a/src/backend/tests/c-lisp/errors/fixed-op-count.sexp b/src/backend/tests/c-lisp/errors/fixed-op-count.sexp new file mode 100644 index 0000000..41334c0 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/fixed-op-count.sexp @@ -0,0 +1,3 @@ +(c-lisp + (define ((main int)) + (ret (add 3 4 5)))) diff --git a/src/backend/tests/c-lisp/errors/fn-proto-bad.out b/src/backend/tests/c-lisp/errors/fn-proto-bad.out new file mode 100644 index 0000000..7dabea7 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/fn-proto-bad.out @@ -0,0 +1 @@ +CodegenError: Bad function prototype: ['main', 'int'] diff --git a/src/backend/tests/c-lisp/errors/fn-proto-bad.sexp b/src/backend/tests/c-lisp/errors/fn-proto-bad.sexp new file mode 100644 index 0000000..e5019ad --- /dev/null +++ b/src/backend/tests/c-lisp/errors/fn-proto-bad.sexp @@ -0,0 +1,3 @@ +(c-lisp + (define (main int) + (ret 0))) diff --git a/src/backend/tests/c-lisp/errors/for-bad.out b/src/backend/tests/c-lisp/errors/for-bad.out new file mode 100644 index 0000000..6824c13 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/for-bad.out @@ -0,0 +1,2 @@ +Error in statement: ['for', [['lt', 'i', 10], ['set', ['add', 'i', 1]], ['call', 'print', 'i']]] +CodegenError: Bad for statement: ['for', [['lt', 'i', 10], ['set', ['add', 'i', 1]], ['call', 'print', 'i']]] diff --git a/src/backend/tests/c-lisp/errors/for-bad.sexp b/src/backend/tests/c-lisp/errors/for-bad.sexp new file mode 100644 index 0000000..f1f1d34 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/for-bad.sexp @@ -0,0 +1,6 @@ +(c-lisp + (define ((main void)) + (declare i int) + (set i 0) + (for ((lt i 10) (set (add i 1)) + (call print i))))) diff --git a/src/backend/tests/c-lisp/errors/if-bad.out b/src/backend/tests/c-lisp/errors/if-bad.out new file mode 100644 index 0000000..e2e33f0 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/if-bad.out @@ -0,0 +1,2 @@ +Error in statement: ['if', True, ['call', 'print', 5], ['call', 'print', 4], ['call', 'print', 3]] +CodegenError: Bad if statement: ['if', True, ['call', 'print', 5], ['call', 'print', 4], ['call', 'print', 3]] diff --git a/src/backend/tests/c-lisp/errors/if-bad.sexp b/src/backend/tests/c-lisp/errors/if-bad.sexp new file mode 100644 index 0000000..6021cd0 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/if-bad.sexp @@ -0,0 +1,8 @@ +(c-lisp + (define ((print int) (n int))) + + (define ((main void)) + (if #t + (call print 5) + (call print 4) + (call print 3)))) diff --git a/src/backend/tests/c-lisp/errors/load-bad.out b/src/backend/tests/c-lisp/errors/load-bad.out new file mode 100644 index 0000000..963ebb9 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/load-bad.out @@ -0,0 +1,2 @@ +Error in statement: ['load', 'argv', 'a'] +CodegenError: Bad load expression: ['load', 'argv', 'a'] diff --git a/src/backend/tests/c-lisp/errors/load-bad.sexp b/src/backend/tests/c-lisp/errors/load-bad.sexp new file mode 100644 index 0000000..76c394e --- /dev/null +++ b/src/backend/tests/c-lisp/errors/load-bad.sexp @@ -0,0 +1,4 @@ +(c-lisp + (define ((main void) (argc int) (argv (ptr (ptr int)))) + (declare a (ptr int)) + (load argv a))) diff --git a/src/backend/tests/c-lisp/errors/not-clisp.out b/src/backend/tests/c-lisp/errors/not-clisp.out new file mode 100644 index 0000000..c5cd791 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/not-clisp.out @@ -0,0 +1 @@ +CodegenError: Input not a C-Lisp program diff --git a/src/backend/tests/c-lisp/errors/not-clisp.sexp b/src/backend/tests/c-lisp/errors/not-clisp.sexp new file mode 100644 index 0000000..d38802c --- /dev/null +++ b/src/backend/tests/c-lisp/errors/not-clisp.sexp @@ -0,0 +1,3 @@ +(brilisp + (define ((main int)) + (ret 0))) diff --git a/src/backend/tests/c-lisp/errors/not-function.out b/src/backend/tests/c-lisp/errors/not-function.out new file mode 100644 index 0000000..e5695c3 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/not-function.out @@ -0,0 +1 @@ +CodegenError: Not a function: ['declare', 'a', 'int'] diff --git a/src/backend/tests/c-lisp/errors/not-function.sexp b/src/backend/tests/c-lisp/errors/not-function.sexp new file mode 100644 index 0000000..0c65d85 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/not-function.sexp @@ -0,0 +1,4 @@ +(c-lisp + (declare a int) + (set a 5) + (return a)) diff --git a/src/backend/tests/c-lisp/errors/ptradd-bad.out b/src/backend/tests/c-lisp/errors/ptradd-bad.out new file mode 100644 index 0000000..3dcf006 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/ptradd-bad.out @@ -0,0 +1,2 @@ +Error in statement: ['call', 'print', ['ptradd', 'arr', 4, 3]] +CodegenError: Bad ptradd expression: ['ptradd', 'arr', 4, 3] diff --git a/src/backend/tests/c-lisp/errors/ptradd-bad.sexp b/src/backend/tests/c-lisp/errors/ptradd-bad.sexp new file mode 100644 index 0000000..066a1df --- /dev/null +++ b/src/backend/tests/c-lisp/errors/ptradd-bad.sexp @@ -0,0 +1,5 @@ +(c-lisp + (define ((main int)) + (declare arr (ptr int)) + (set arr (alloc int 20)) + (call print (ptradd arr 4 3)))) diff --git a/src/backend/tests/c-lisp/errors/redeclare.out b/src/backend/tests/c-lisp/errors/redeclare.out index 8243c4a..6814420 100644 --- a/src/backend/tests/c-lisp/errors/redeclare.out +++ b/src/backend/tests/c-lisp/errors/redeclare.out @@ -1,2 +1,2 @@ -Error in statement: ['declare', ['var', 'bool']] +Error in statement: ['declare', 'var', 'bool'] CodegenError: Re-declaration of variable var diff --git a/src/backend/tests/c-lisp/errors/redeclare.sexp b/src/backend/tests/c-lisp/errors/redeclare.sexp index 4cc7b85..9d1dac6 100644 --- a/src/backend/tests/c-lisp/errors/redeclare.sexp +++ b/src/backend/tests/c-lisp/errors/redeclare.sexp @@ -1,9 +1,9 @@ (c-lisp (define ((main void)) - (declare (var int)) + (declare var int) (if #t - ((declare (var float)) + ((declare var float) (set var 0.5))) - (declare (var bool)))) + (declare var bool))) diff --git a/src/backend/tests/c-lisp/errors/store-bad.out b/src/backend/tests/c-lisp/errors/store-bad.out new file mode 100644 index 0000000..eeb1d1c --- /dev/null +++ b/src/backend/tests/c-lisp/errors/store-bad.out @@ -0,0 +1,2 @@ +Error in statement: ['store', 'a', 5, 6, 7] +CodegenError: Bad store expression: ['store', 'a', 5, 6, 7] diff --git a/src/backend/tests/c-lisp/errors/store-bad.sexp b/src/backend/tests/c-lisp/errors/store-bad.sexp new file mode 100644 index 0000000..30e268b --- /dev/null +++ b/src/backend/tests/c-lisp/errors/store-bad.sexp @@ -0,0 +1,5 @@ +(c-lisp + (define ((main void)) + (declare a (ptr int)) + (set a (alloc int 3)) + (store a 5 6 7))) diff --git a/src/backend/tests/c-lisp/errors/while-bad.out b/src/backend/tests/c-lisp/errors/while-bad.out new file mode 100644 index 0000000..3d9e5ce --- /dev/null +++ b/src/backend/tests/c-lisp/errors/while-bad.out @@ -0,0 +1,2 @@ +Error in statement: ['while', ['call', 'print', 5]] +CodegenError: Bad while statement: ['while', ['call', 'print', 5]] diff --git a/src/backend/tests/c-lisp/errors/while-bad.sexp b/src/backend/tests/c-lisp/errors/while-bad.sexp new file mode 100644 index 0000000..c768f34 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/while-bad.sexp @@ -0,0 +1,4 @@ +(c-lisp + (define ((main int)) + (while + (call print 5)))) diff --git a/src/backend/tests/c-lisp/for-return.sexp b/src/backend/tests/c-lisp/for-return.sexp index ac64eb4..43e35d3 100644 --- a/src/backend/tests/c-lisp/for-return.sexp +++ b/src/backend/tests/c-lisp/for-return.sexp @@ -2,7 +2,7 @@ (define ((print int) (n int))) (define ((cons void) (arr (ptr int)) (n int)) - (declare (i int)) + (declare i int) (for ((set i 0) (lt i n) (set i (add i 1))) @@ -11,7 +11,7 @@ (mul i 3)))) (define ((find int) (arr (ptr int)) (key int) (n int)) - (declare (i int)) + (declare i int) (for ((set i 0) (lt i n) (set i (add i 1))) @@ -20,7 +20,7 @@ (ret i)))) (define ((verify int) (arr (ptr int)) (key int) (idx int) (len int)) - (declare (val int)) + (declare val int) (if (lt idx len) ((set val (load (ptradd arr idx))) (if (eq val key) @@ -29,14 +29,14 @@ (ret -2))) (define ((main void)) - (declare (input (ptr int))) - (declare (i int)) + (declare input (ptr int)) + (declare i int) (set input (alloc int 10)) (call cons input 10) - (declare (12-idx int)) - (declare (13-idx int)) + (declare 12-idx int) + (declare 13-idx int) (set 12-idx (call find input 12 10)) (set 13-idx (call find input 13 10)) From 1974be5a2f49d0a4ea3b9f3c2541a68729086eb7 Mon Sep 17 00:00:00 2001 From: Vedanth Padmaraman Date: Fri, 28 Jun 2024 13:35:13 +0530 Subject: [PATCH 09/10] Implement function `verify_shape` to check shapes of lists --- src/backend/c-lisp.py | 4 ++++ src/backend/tests/c-lisp/errors/set-bad.out | 2 ++ src/backend/tests/c-lisp/errors/set-bad.sexp | 3 +++ src/backend/utils/shape.py | 17 +++++++++++++++++ 4 files changed, 26 insertions(+) create mode 100644 src/backend/tests/c-lisp/errors/set-bad.out create mode 100644 src/backend/tests/c-lisp/errors/set-bad.sexp create mode 100644 src/backend/utils/shape.py diff --git a/src/backend/c-lisp.py b/src/backend/c-lisp.py index 616095e..5e70602 100755 --- a/src/backend/c-lisp.py +++ b/src/backend/c-lisp.py @@ -2,6 +2,7 @@ import json import sys from utils.random import random_label +from utils.shape import verify_shape CLISP_PREFIX = "tmp_clisp" @@ -308,6 +309,9 @@ def is_set_expr(self, expr): return expr[0] == "set" def gen_set_expr(self, expr, res_sym): + if not verify_shape(expr, [str, str, None]): + raise CodegenError(f"Bad set expression: {expr}") + name = expr[1] scoped_name = self.scoped_lookup(name) instr_list = self.gen_expr(expr[2], res_sym=res_sym) diff --git a/src/backend/tests/c-lisp/errors/set-bad.out b/src/backend/tests/c-lisp/errors/set-bad.out new file mode 100644 index 0000000..d8a3ca3 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/set-bad.out @@ -0,0 +1,2 @@ +Error in statement: ['set', ['declare', 'var', 'int'], 4] +CodegenError: Bad set expression: ['set', ['declare', 'var', 'int'], 4] diff --git a/src/backend/tests/c-lisp/errors/set-bad.sexp b/src/backend/tests/c-lisp/errors/set-bad.sexp new file mode 100644 index 0000000..876d9e3 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/set-bad.sexp @@ -0,0 +1,3 @@ +(c-lisp + (define ((main int)) + (set (declare var int) 4))) diff --git a/src/backend/utils/shape.py b/src/backend/utils/shape.py new file mode 100644 index 0000000..d8590bd --- /dev/null +++ b/src/backend/utils/shape.py @@ -0,0 +1,17 @@ +def verify_shape(obj, template): + """ + Verify the shape of `lst` with `template`. + For example, ["set", "a", 5] has shape [str, str, int]. + `None` can be used as a wildcard; so ["set", "a", ["add", 5, 1]] matches [str, str, None] + """ + if template is None: + return True + elif not isinstance(template, list): + return isinstance(obj, template) + else: + if not isinstance(obj, list): + return False + for c in range(len(obj)): + if not verify_shape(obj[c], template[c]): + return False + return True From 856ec9448ab6c68ba49c2e1c0b9a640df451138a Mon Sep 17 00:00:00 2001 From: Vedanth Padmaraman Date: Fri, 28 Jun 2024 14:50:22 +0530 Subject: [PATCH 10/10] More testcases, including some natural human errors --- src/backend/c-lisp.py | 2 +- .../tests/c-lisp/errors/fixed-op-count-1.out | 2 ++ .../tests/c-lisp/errors/fixed-op-count-1.sexp | 3 +++ .../{fixed-op-count.out => fixed-op-count-2.out} | 0 ...{fixed-op-count.sexp => fixed-op-count-2.sexp} | 0 src/backend/tests/c-lisp/errors/ret-bad.out | 2 ++ src/backend/tests/c-lisp/errors/ret-bad.sexp | 3 +++ src/backend/tests/c-lisp/errors/scope.out | 2 ++ src/backend/tests/c-lisp/errors/scope.sexp | 15 +++++++++++++++ 9 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/backend/tests/c-lisp/errors/fixed-op-count-1.out create mode 100644 src/backend/tests/c-lisp/errors/fixed-op-count-1.sexp rename src/backend/tests/c-lisp/errors/{fixed-op-count.out => fixed-op-count-2.out} (100%) rename src/backend/tests/c-lisp/errors/{fixed-op-count.sexp => fixed-op-count-2.sexp} (100%) create mode 100644 src/backend/tests/c-lisp/errors/ret-bad.out create mode 100644 src/backend/tests/c-lisp/errors/ret-bad.sexp create mode 100644 src/backend/tests/c-lisp/errors/scope.out create mode 100644 src/backend/tests/c-lisp/errors/scope.sexp diff --git a/src/backend/c-lisp.py b/src/backend/c-lisp.py index 5e70602..6b00556 100755 --- a/src/backend/c-lisp.py +++ b/src/backend/c-lisp.py @@ -373,7 +373,7 @@ def gen_fixed_type_expr(self, expr, res_sym): opcode = expr[0] typ, n_ops = self.fixed_op_types[opcode] if not (len(expr) == n_ops + 1): - raise CodegenError(f"`{opcode}` takes only 2 operands: {expr}") + raise CodegenError(f"`{opcode}` takes only {n_ops} operands: {expr}") in_syms = [random_label(CLISP_PREFIX, [f"inp_{n}"]) for n in range(n_ops)] input_instrs = [] for n in range(n_ops): diff --git a/src/backend/tests/c-lisp/errors/fixed-op-count-1.out b/src/backend/tests/c-lisp/errors/fixed-op-count-1.out new file mode 100644 index 0000000..4cef219 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/fixed-op-count-1.out @@ -0,0 +1,2 @@ +Error in statement: ['ret', ['not', True, False]] +CodegenError: `not` takes only 1 operands: ['not', True, False] diff --git a/src/backend/tests/c-lisp/errors/fixed-op-count-1.sexp b/src/backend/tests/c-lisp/errors/fixed-op-count-1.sexp new file mode 100644 index 0000000..bc9e359 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/fixed-op-count-1.sexp @@ -0,0 +1,3 @@ +(c-lisp + (define ((main bool)) + (ret (not #t #f)))) diff --git a/src/backend/tests/c-lisp/errors/fixed-op-count.out b/src/backend/tests/c-lisp/errors/fixed-op-count-2.out similarity index 100% rename from src/backend/tests/c-lisp/errors/fixed-op-count.out rename to src/backend/tests/c-lisp/errors/fixed-op-count-2.out diff --git a/src/backend/tests/c-lisp/errors/fixed-op-count.sexp b/src/backend/tests/c-lisp/errors/fixed-op-count-2.sexp similarity index 100% rename from src/backend/tests/c-lisp/errors/fixed-op-count.sexp rename to src/backend/tests/c-lisp/errors/fixed-op-count-2.sexp diff --git a/src/backend/tests/c-lisp/errors/ret-bad.out b/src/backend/tests/c-lisp/errors/ret-bad.out new file mode 100644 index 0000000..74c3607 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/ret-bad.out @@ -0,0 +1,2 @@ +Error in statement: ['ret', 0, 1] +CodegenError: Return statement can contain only 1 optional return value: ['ret', 0, 1] diff --git a/src/backend/tests/c-lisp/errors/ret-bad.sexp b/src/backend/tests/c-lisp/errors/ret-bad.sexp new file mode 100644 index 0000000..b0687ee --- /dev/null +++ b/src/backend/tests/c-lisp/errors/ret-bad.sexp @@ -0,0 +1,3 @@ +(c-lisp + (define ((main int)) + (ret 0 1))) diff --git a/src/backend/tests/c-lisp/errors/scope.out b/src/backend/tests/c-lisp/errors/scope.out new file mode 100644 index 0000000..ff124d4 --- /dev/null +++ b/src/backend/tests/c-lisp/errors/scope.out @@ -0,0 +1,2 @@ +Error in statement: ['call', 'print', 'val'] +CodegenError: Undeclared symbol: val diff --git a/src/backend/tests/c-lisp/errors/scope.sexp b/src/backend/tests/c-lisp/errors/scope.sexp new file mode 100644 index 0000000..a42ed8f --- /dev/null +++ b/src/backend/tests/c-lisp/errors/scope.sexp @@ -0,0 +1,15 @@ +(c-lisp + (define ((print int) (n int))) + + (define ((main void)) + (declare i int) + + (for ((set i 0) + (lt i 100) + (set i (add i 1))) + (declare val int) + (if (eq + i + (mul (div i 7) 7)) + (set val i))) + (call print val)))