struct Function { ~uint8 name, utils.Vector inputs, outputs, ~parse.Node _up, int call_padding, bool m, ~Module mod } ~uint8 BOOL_OPS = "&&,||,^^,!&&,!||,!^^\0" ~uint8 CMP_OPS = "==,<,>,!==,!<,!>,<==,>==\0" /; method Function /; init (~parse.Node n) self.name = utils.strcpy(n`.data) self._up = n self.m = false self.call_padding = 0 Var v self.inputs.init(len v) self.outputs.init(len v) ;/ /; _resolve_dlist (~Module parent, ~parse.Node dl) ~parse.Node tn = NULL ~parse.Node n int reg = 1 int stack_up = 8 /; if (self.m == true) ~Struct s = parent`.related_type() Var p p._init(s) _delete(p.name) p.name = utils.strcpy("self\0") p.ptr_push(0) p.loc = reg reg++ self.inputs.push(~p) ;/ /; if (dl == NULL) return ;/ /; loop (int i = 0; i < dl`.sub.count) [i++] n = dl`.sub.get(i) /; if (n`._type == parse.NTYPE_TYPE) tn = n ;; else if (n`._type == parse.NTYPE_ID) /; if (tn == NULL) _printf("Identifier declared in parameter list before any type was found!\n\0") return ;/ Var p p.init(tn, n) p._resolve_type(parent) # TODO: This is wrong /; if (p.regable() == true && reg < 7) p.loc = reg reg++ ;; else p.loc = 0 - 1 p.offset = stack_up stack_up = stack_up + p.actual_size() ;/ self.inputs.push(~p) ;/ ;/ ;/ /; _resolve_tlist (~Module parent, ~parse.Node tl) ~parse.Node n parse.Node out_id out_id.data = "# OUTPUT #\0" int reg = 1 int stack = 0 /; loop (int i = 0; i < tl`.sub.count) [i++] n = tl`.sub.get(i) /; if (n`._type == parse.NTYPE_TYPE) # TODO: also wrong Var r r.init(n, ~out_id) r._resolve_type(parent) r._id = NULL r._tn = NULL /; if (r.regable() == true && reg < 7) r.loc = reg /; if (reg > 4) r.loc = r.loc + 4 ;/ reg++ ;; else r.loc = 0 - 1 r.offset = stack stack = stack + r.actual_size() ;/ self.outputs.push(~r) ;/ ;/ ;/ /; _resolve_type (~Module parent) ~parse.Node _up = self._up /; if (_up`.sub.count < 1) /; if (self.m == true) self._resolve_dlist(parent, NULL) ;/ return ;/ ~parse.Node lst = _up`.sub.get(0) /; if (lst`._type == parse.NTYPE_DLIST) self._resolve_dlist(parent, lst) /; if (_up`.sub.count > 1) lst = _up`.sub.get(1) ;/ ;; else if (self.m == true) self._resolve_dlist(parent, NULL) ;/ /; if (lst`._type == parse.NTYPE_TLIST) self._resolve_tlist(parent, lst) ;/ int i_size = 0 int o_size = 0 ~Var v /; loop (int i = 0; i < self.inputs.count) [i++] v = self.inputs.get(i) /; if (v`.loc < 0) int vsz = v`.actual_size() i_size = i_size + vsz ;/ ;/ /; loop (int i = 0; i < self.outputs.count) [i++] v = self.outputs.get(i) /; if (v`.loc < 0) int vsz = v`.actual_size() o_size = o_size + vsz ;/ ;/ /; if (o_size > i_size) int padding = o_size - i_size /; loop (int i = 0; i < self.inputs.count) [i++] v = self.inputs.get(i) int off = v`.offset v`.offset = off + padding ;/ self.call_padding = padding ;/ ;/ /; _compute_scope_vars_loop(~Scope s, ~parse.Node upper, int i) ~parse.Node n /; loop (i < upper`.sub.count) [i++] n = upper`.sub.get(i) /; if (n`._type == parse.NTYPE_DECL) s`.mk_aware_node(n) ;; else s`.precheck_stmt(n) ;/ ;/ ;/ /; _compute_scope_vars(~Scope s) ~Var in /; loop (int i = 0; i < self.inputs.count) [i++] in = self.inputs.get(i) s`.mk_aware(in) ;/ int i = self._first_stmt_off() /; if (i < 0) return ;/ ~parse.Node _up = self._up self._compute_scope_vars_loop(s, _up, i) ;/ /; _call_label [~uint8] Scope tmp self.mod`._print(0) tmp.init(self.mod, NULL, self.name) ~uint8 out = tmp.base_label() tmp.end() return out ;/ /; _build_func(~Module parent, ~CompBuf cb) [Scope] Scope out out.init(parent, cb, self.name) out.parent = NULL /; if (parent`.e == true) # Add to the global exports if the parent is exported ~uint8 bl = out.base_label() cb`.add_h("global \0") cb`.add_h(bl) cb`.add_h("\n\0") _delete(bl) ;/ # Write label and opening # Nieve implementation: r10-r15 are callee saved registers # in the TNSL style ABI out.place_base_label() cb`.add_c(" push rbp\n\0") cb`.add_c(" lea rbp, [rsp + 8]\n\0") cb`.add_c(" push r10\n\0") cb`.add_c(" push r11\n\0") cb`.add_c(" push r12\n\0") cb`.add_c(" push r13\n\0") cb`.add_c(" push r14\n\0") cb`.add_c(" push r15 ; scope init\n\n\0") self._compute_scope_vars(~out) # Add all params to the scope ~Var in /; loop (int i = 0; i < self.inputs.count) [i++] in = self.inputs.get(i) /; if (in.is_ref()) ~int32 ptc = in.top_ptrc() int32 set = 0 set = set - 1 ptc` = set ;/ out.mk_set_var(in) ;/ return out ;/ /; _end_func(~Scope scope, ~CompBuf cb) cb`.add_c("\n\0") scope`.place_end_label() cb`.add_c(" lea rsp, [rbp - 56]\n\0") cb`.add_c(" pop r15\n\0") cb`.add_c(" pop r14\n\0") cb`.add_c(" pop r13\n\0") cb`.add_c(" pop r12\n\0") cb`.add_c(" pop r11\n\0") cb`.add_c(" pop r10\n\0") cb`.add_c(" pop rbp\n\0") cb`.add_c(" ret ; scope end\n\n\n\0") scope`.end() ;/ /; _first_stmt_off [int] int i = 0 ~parse.Node _up = self._up /; if (_up`.sub.count < 1) return i - 1 ;/ ~parse.Node n = _up`.sub.get(i) /; if (n`._type == parse.NTYPE_DLIST) i++ /; if (_up`.sub.count > 1) n = _up`.sub.get(1) ;/ ;/ /; if (n`._type == parse.NTYPE_TLIST) i++ ;/ return i ;/ /; _compile (~Module parent, ~CompBuf cb) # Sanity check int i = self._first_stmt_off() # Create scope Scope fscope = self._build_func(parent, cb) /; if (i !< 0) ~parse.Node _up = self._up self._compile_statements(~fscope, _up, i) ;/ # Compile and then end scope self._end_func(~fscope, cb) ;/ # # Compiling individual statements # /; _compile_statements (~Scope s, ~parse.Node _up, int off) ~parse.Node n = NULL /; loop (off < _up`.sub.count) [off++] n = _up`.sub.get(off) /; if (n`._type == parse.NTYPE_FLOW_CONTROL) self._compile_flow_control(s, n) ;; else if (n`._type == parse.NTYPE_ASM) s`.cb`.add_c(" \0") s`.cb`.add_c(n`.data) s`.cb`.add_c(" ; User defined asm\n\0") ;; else if (n`._type == parse.NTYPE_DECL) self._compile_decl(s, n) ;; else if (n`._type == parse.NTYPE_VALUE) Var v = self._compile_value(s, n) /; if (s`.is_tmp(~v)) s`.free_to(~v, true) ;/ v.end() ;; else if (n`._type == parse.NTYPE_IF_BLOCK) off = self._compile_if(s, _up, off) ;; else if (n`._type == parse.NTYPE_LOOP_BLOCK) self._compile_loop(s, n) ;/ ;/ ;/ /; _compile_cf_pre(~Scope s, ~parse.Node _up) [bool] ~parse.Node n = NULL bool last_var = false Var v /; loop (int off = 0; off < _up`.sub.count) [off++] n = _up`.sub.get(off) /; if (n`._type == parse.NTYPE_FLOW_CONTROL) self._compile_flow_control(s, n) ;; else if (n`._type == parse.NTYPE_ASM) s`.cb`.add_c(" \0") s`.cb`.add_c(n`.data) s`.cb`.add_c(" ; User defined asm\n\0") ;; else if (n`._type == parse.NTYPE_DECL) self._compile_decl(s, n) ;; else if (n`._type == parse.NTYPE_VALUE) v = self._compile_value(s, n) int count = _up`.sub.count /; if (off + 1 == count) last_var = true ;; else /; if (s`.is_tmp(~v)) s`.free_to(~v, true) ;/ v.end() ;/ ;; else if (n`._type == parse.NTYPE_IF_BLOCK) off = self._compile_if(s, _up, off) ;; else if (n`._type == parse.NTYPE_LOOP_BLOCK) self._compile_loop(s, n) ;/ ;/ /; if (last_var == true) last_var = false /; if (v.is_struct() == false) # Do cond jmp ~CompBuf buf = s`.cb ~uint8 lab = s`.end_label() /; if (v.loc == 0 && v.offset == 0) # False was passed, always jump to end buf`.add_c(" jmp \0") buf`.add_c(lab) buf`.add_c("\n\0") ;; else if (v.loc !== 0) v.test(s`.cb) buf`.add_c(" je \0") buf`.add_c(lab) buf`.add_c("\n\0") ;/ _delete(lab) last_var = true ;/ /; if (s`.is_tmp(~v) == true) s`.free_to(~v, true) ;/ v.end() ;/ return last_var ;/ /; _compile_cf_post(~Scope s, ~parse.Node pre, post, bool has_pre) ~parse.Node n = NULL bool last_var = false Var v /; loop (int i = 0; i < post`.sub.count) [i++] n = post`.sub.get(i) /; if (n`._type == parse.NTYPE_FLOW_CONTROL) self._compile_flow_control(s, n) ;; else if (n`._type == parse.NTYPE_ASM) s`.cb`.add_c(" \0") s`.cb`.add_c(n`.data) s`.cb`.add_c(" ; User defined asm\n\0") ;; else if (n`._type == parse.NTYPE_DECL) self._compile_decl(s, n) ;; else if (n`._type == parse.NTYPE_VALUE) v = self._compile_value(s, n) int count = post`.sub.count /; if (i + 1 == count) last_var = true ;; else /; if (s`.is_tmp(~v)) s`.free_to(~v, true) ;/ v.end() ;/ ;; else if (n`._type == parse.NTYPE_IF_BLOCK) i = self._compile_if(s, post, i) ;; else if (n`._type == parse.NTYPE_LOOP_BLOCK) self._compile_loop(s, n) ;/ ;/ /; if (last_var == true) last_var = false /; if (v.is_struct() == false) # Do cond jmp ~CompBuf buf = s`.cb ~uint8 lab = s`.end_label() /; if (v.loc == 0 && v.offset == 0) # False was passed, always jump to end buf`.add_c(" jmp \0") buf`.add_c(lab) buf`.add_c("\n\0") ;; else if (v.loc !== 0) v.test(s`.cb) buf`.add_c(" je \0") buf`.add_c(lab) buf`.add_c("\n\0") ;/ _delete(lab) last_var = true ;/ /; if (s`.is_tmp(~v) == true) s`.free_to(~v, true) ;/ v.end() ;/ /; if (last_var == false) /; if (has_pre == true) # Grab last item from pre block and compile as value. Then do similar to above # as if it was the last statement int off = pre`.sub.count - 1 n = pre`.sub.get(off) v = self._compile_value(s, n) # Do cond jmp ~CompBuf buf = s`.cb ~uint8 lab = s`.end_label() /; if (v.loc == 0 && v.offset == 0) # False was passed, always jump to end buf`.add_c(" jmp \0") buf`.add_c(lab) buf`.add_c("\n\0") ;; else if (v.loc !== 0) v.test(s`.cb) buf`.add_c(" je \0") buf`.add_c(lab) buf`.add_c("\n\0") ;/ _delete(lab) /; if (s`.is_tmp(~v) == true) s`.free_to(~v, true) ;/ v.end() ;/ ;/ ;/ /; _compile_if_if(~Scope wrap, ~parse.Node n) /; if (n`.sub.count < 1) # Sanity return ;/ # Generate and pre-compute scope Scope s = wrap`.gen_sub("if\0") ~parse.Node first = n`.sub.get(0) int off = 0 # Compile pre statements if applicable and do conditional jmp /; if (first`._type == parse.NTYPE_SLIST) self._compute_scope_vars_loop(~s, first, 0) self._compute_scope_vars_loop(~s, n, 1) self._compile_cf_pre(~s, first) off = off + 1 ;; else self._compute_scope_vars_loop(~s, n, 0) ;/ # Actually compile all the statements self._compile_statements(~s, n, off) # If we did execute the if branch then we are jumping to the end of the wrapper ~CompBuf cb = wrap`.cb cb`.add_c(" jmp \0") ~uint8 lab = wrap`.end_label() cb`.add_c(lab) cb`.add_c("\n\0") _delete(lab) # Place an anchor for the negative case to latch on to and clean up the scope s.place_end_label() s.end() ;/ /; _compile_if_elif(~Scope wrap, ~parse.Node n) [bool] /; if (n`._type !== parse.NTYPE_ELIF_BLOCK) return false ;/ self._compile_if_if(wrap, n) return true ;/ /; _compile_if_else(~Scope wrap, ~parse.Node n) [bool] /; if (n`._type !== parse.NTYPE_ELSE_BLOCK) return false ;/ self._compile_if_if(wrap, n) return true ;/ /; _compile_if (~Scope s, ~parse.Node n, int off) [int] Scope wrap = s`.gen_sub("wrap\0") ~parse.Node block = n`.sub.get(off) self._compile_if_if(~wrap, block) bool run = true /; loop (run == true) off = off + 1 /; if (off !< n`.sub.count) run = false ;; else block = n`.sub.get(off) /; if (self._compile_if_elif(~wrap, block) == false) run = false ;/ ;/ ;/ /; if (off < n`.sub.count) /; if (self._compile_if_else(~wrap, block) == true) off = off + 1 ;/ ;/ wrap.place_end_label() wrap.end() off = off - 1 return off ;/ /; _compile_loop (~Scope s, ~parse.Node n) # Generate scope Scope ls = s`.gen_sub("loop\0") # Grab pre-statements, post-statements, and offset ~parse.Node pre = NULL ~parse.Node post = NULL int off = 0 /; if (n`.sub.count > 0) ~parse.Node first = n`.sub.get(0) /; if (first`._type == parse.NTYPE_SLIST) /; if (utils.strcmp(first`.data, "(\0") == true) pre = first off++ ;; else if (utils.strcmp(first`.data, "[\0") == true) post = first off++ ;/ ;/ ;/ /; if (n`.sub.count > 1) ~parse.Node second = n`.sub.get(1) /; if (second`._type == parse.NTYPE_SLIST) /; if (utils.strcmp(second`.data, "[\0") == true) post = second off++ ;/ ;/ ;/ # Scope pre-check /; if (pre !== NULL) self._compute_scope_vars_loop(~ls, pre, 0) ;/ /; if (post !== NULL) self._compute_scope_vars_loop(~ls, post, 0) ;/ self._compute_scope_vars_loop(~ls, n, off) # Compile pre statements if applicable and do conditional jmp bool has_pre = false /; if (pre !== NULL) has_pre = self._compile_cf_pre(~ls, pre) ;/ # Place an anchor for repeating the loop to latch on to ls.place_start_label() # Actually compile all the statements self._compile_statements(~ls, n, off) # Place an anchor for continue statements to latch on to ls.place_rep_label() # Compile post-statements if applicable /; if (post !== NULL) self._compile_cf_post(~ls, pre, post, has_pre) ;; else if (has_pre == true) # In this case no post but we have a valid condition in the pre-statements # so we should use that as the rep condition int idx = pre`.sub.count - 1 ~parse.Node last = pre`.sub.get(idx) Var v = self._compile_value(~ls, last) # Do cond jmp ~CompBuf buf = ls.cb ~uint8 lab = ls.end_label() /; if (v.loc == 0 && v.offset == 0) # False was passed, always jump to end buf`.add_c(" jmp \0") buf`.add_c(lab) buf`.add_c("\n\0") ;; else if (v.loc !== 0) v.test(s`.cb) buf`.add_c(" je \0") buf`.add_c(lab) buf`.add_c("\n\0") ;/ _delete(lab) /; if (ls.is_tmp(~v) == true) ls.free_to(~v, true) ;/ v.end() ;/ # Default to repeating the loop ~CompBuf cb = ls.cb cb`.add_c(" jmp \0") ~uint8 lab = ls.start_label() cb`.add_c(lab) cb`.add_c("\n\0") _delete(lab) # Place an anchor for breaks to latch on to and clean up the scope ls.place_end_label() ls.end() ;/ /; _check_return (~parse.Node n) [bool] int expected = self.outputs.count int have = 0 ~parse.Node val /; if (n`.sub.count > 0) val = n`.sub.get(0) val = val`.sub.get(0) /; if (expected > 1 && val`._type == parse.NTYPE_VLIST) have = val`.sub.count ;; else have = 1 ;/ ;/ /; if (have == expected) return true ;/ _print_num("ERROR: Number of return values (%d) does not match expected (\0", have) _print_num("%d)\n\0", expected) _printf("ERROR: To return multiple values enclose them in {}\n\0") _printf("ERROR: To add more return values, add them (comma separated) between [] at the end of the function definition\n\0") return false ;/ /; _compile_return_vals (~Scope s, ~parse.Node n) _printf("Compiling return vals!\n\0") /; if (self.outputs.count == 0) return ;/ n = n`.sub.get(0) /; if (self.outputs.count > 1) n = n`.sub.get(0) ;/ s`.cb`.add_c("\n ; STARTING RETURN\n\0") ~Var out Var cmp /; if (self.outputs.count > 1) ~parse.Node val_node Var tmp utils.Vector tmps tmps.init(len cmp) # Compile all values /; loop (int i = 0; i < self.outputs.count) [i++] val_node = n`.sub.get(i) out = self.outputs.get(i) tmp = s`.mk_tmp(out) cmp = self._compile_value(s, val_node) /; if (tmp.is_ref() == true) int32 pp = 0 pp = pp - 1 tmp.ptr_pop() tmp.ptr_push(pp) ;; else tmp.set(s`.cb, ~cmp) ;/ cmp.end() tmps.push(~tmp) ;/ # Set all outputs ~Var tt /; loop (int i = 0; i < self.outputs.count) [i++] out = self.outputs.get(i) tt = tmps.get(i) out`.set(s`.cb, tt) tt`.end() ;/ s`.free_tmp(tmps.count, false) tmps.end() ;; else out = self.outputs.get(0) cmp = self._compile_value(s, n) out`.set(s`.cb, ~cmp) cmp.end() ;/ ;/ /; _return (~Scope s, ~parse.Node n) /; if (self._check_return(n) !== true) return ;/ # Compute all return values and set the output variables properly self._compile_return_vals(s, n) # Find root scope of function /; loop (s`.parent !== NULL) s = s`.parent ;/ # Generate jump instruction ~uint8 lab = s`.end_label() s`.cb`.add_c(" jmp \0") s`.cb`.add_c(lab) s`.cb`.add_c("\n\0") _delete(lab) ;/ # Should handle break, continue, and return /; _compile_flow_control (~Scope s, ~parse.Node n) /; if (utils.strcmp(n`.data, "return\0") == true) # Compute value and return self._return(s, n) ;; else if (utils.strcmp(n`.data, "continue\0") == true) ~Scope lp = s`.closest_loop() /; if (lp == NULL) return ;/ ~uint8 lab = lp`.rep_label() s`.cb`.add_c(" jmp \0") s`.cb`.add_c(lab) s`.cb`.add_c("\n\0") _delete(lab) ;; else if (utils.strcmp(n`.data, "break\0") == true) ~Scope br = s`.closest_break() /; if (br == NULL) return ;/ ~uint8 lab = br`.rep_label() s`.cb`.add_c(" jmp \0") s`.cb`.add_c(lab) s`.cb`.add_c("\n\0") _delete(lab) ;; else _printf("COMPILER ERROR: The following was detected as flow control, but we do not handle it: '\0") _printf(n`.data) _printf("'\n\0") _printf("COMPILER ERROR: this is likely a bug with the compiler, please report it along with the\n\0") _printf("COMPILER ERROR: code snippit which caused the bug!\n\0") ;/ ;/ # Should handle variable declarations /; _compile_decl(~Scope s, ~parse.Node n) ~parse.Node sub /; loop (int i = 0; i < n`.sub.count) [i++] sub = n`.sub.get(i) /; if (sub`._type == parse.NTYPE_ID) ~Var v = s`._find_var(sub`.data) /; if (v == NULL) _printf("Failed to find variable '\0") _printf(sub`.data) _printf("'\n\0") return ;/ /; if (sub`.sub.count > 0) sub = sub`.sub.get(0) Var val = self._compile_value(s, sub) # Need var name correct _delete(val.name) ~uint8 name = utils.strcpy(v`.name) val.name = name s`.mk_set_var(~val) val.end() ;; else s`.mk_var(v) ;/ ;/ ;/ ;/ /; _compile_call(~Scope s, ~parse.Node params, ~Function f, ~Var _self, bool mth) [Var] Var result ~Var out /; if (f`.outputs.count > 0) # Generate result tmp if required out = f`.outputs.get(0) /; if (out`.loc < 0) result = s`.mk_tmp_stack(out) ;; else result = out`.copy() ;/ ;; else ~Struct t = self._find_literal_type(s, "void\0") result._init(t) ;/ # Save caller saved tmp variables Var handle = s`.save_caller_tmp() # Check that parameter count matches int param_count = params`.sub.count /; if (mth == true) param_count++ ;/ /; if (params`.sub.count == 1) ~parse.Node nn nn = params`.sub.get(0) /; if (nn`._type == parse.NTYPE_VALUE && nn`.sub.count == 0) param_count = param_count - 1 ;/ ;/ /; if (param_count !== f`.inputs.count) ~uint8 blab = s`.base_label() _printf("ERROR: Failed to call function \"\0") _printf(f`.name) _printf("\" from scope \"\0") _printf(blab) _printf("\"\n\0") _print_num(" Number of arguments (%d) did not match number of provided parameters \0", f`.inputs.count) _print_num("(%d)\n\0", param_count) _delete(blab) result.loc = 0 return result ;/ # Then generate a vector to store all the tmp variables which will store the # params as intermediaries utils.Vector tmps tmps.init(len result) tmps._grow(param_count) tmps.count = param_count # Create all stack based tmps ~Var inp Var last_stack last_stack.loc = 0 /; loop (int i = 1; i !> f`.inputs.count) [i++] int pidx = f`.inputs.count - i inp = f`.inputs.get(pidx) /; if (inp`.loc < 0) # Make tmp on stack and compute Var to_set = s`.mk_tmp_stack(inp) /; if (to_set.is_ref() == true) # Correct for ref int32 ptrc = 0 ptrc = ptrc - 1 to_set.ptr_pop() to_set.ptr_push(ptrc) ;/ tmps.replace(pidx, ~to_set) last_stack = to_set ;/ ;/ # Create all register based tmps Var last_reg last_reg.loc = 0 /; loop (int i = 0; i < f`.inputs.count) [i++] inp = f`.inputs.get(i) /; if (inp`.loc > 0) # Make tmp on stack and compute Var to_set = s`.mk_tmp_stack(inp) /; if (to_set.is_ref() == true) # Correct for ref int32 ptrc = 0 ptrc = ptrc - 1 to_set.ptr_pop() to_set.ptr_push(ptrc) ;/ tmps.replace(i, ~to_set) last_reg = to_set ;/ ;/ # Set 'self' if required int use_self = 0 /; if (mth == true) inp = tmps.get(0) Var _sptr = _self`.take_ptr(s`.cb, 1) inp`.set(s`.cb, ~_sptr) _sptr.end() use_self++ ;/ # compile all parameters and put them into tmp vars ~parse.Node pnode /; loop (int i = use_self; i < f`.inputs.count) [i++] pnode = params`.sub.get(i) inp = tmps.get(i) Var param = self._compile_value(s, pnode) inp`.set(s`.cb, ~param) /; if (s`.is_tmp(~param)) s`.free_to(~param, true) ;/ param.end() ;/ # Move all register parameters into registers and free the stack space ~Var ptmp /; loop (int i = 0; i < f`.inputs.count) [i++] inp = f`.inputs.get(i) /; if (inp`.loc > 0) ptmp = tmps.get(i) /; if (inp`.is_ref() == true) Var to_set = inp`.take_ptr(s`.cb, 1) to_set.set(s`.cb, ptmp) to_set.end() ;; else inp`.set(s`.cb, ptmp) ;/ ;/ ;/ /; if (last_stack.loc !== 0) s`.free_after(~last_stack, true) ;/ # Pad out the stack for return if required ~CompBuf buf = s`.cb /; if (f`.call_padding > 0) ~uint8 pad_num = utils.int_to_str(f`.call_padding) buf`.add_c(" sub rsp, \0") buf`.add_c(pad_num) buf`.add_c("\n\0") _delete(pad_num) ;/ # Do call ~uint8 call = f`._call_label() buf`.add_c(" call \0") buf`.add_c(call) buf`.add_c("\n\0") _delete(call) # Generate result variable and move into the result if required /; if (f`.outputs.count > 0) /; if (out`.loc < 0) Var out_pos = out`.copy() /; if (out_pos.is_ref() == true) int32 pp = 0 pp = pp - 1 out_pos.ptr_pop() out_pos.ptr_push(pp) ;/ out_pos.loc = last_stack.loc out_pos.offset = last_stack.offset /; if (f`.call_padding > 0) int offset = out_pos.offset offset = offset - f`.call_padding out_pos.offset = offset ;/ bool is_ref = false /; if (result.is_ref() == true) is_ref = true int32 pp = 0 pp = pp - 1 result.ptr_pop() result.ptr_push(pp) ;/ result.set(s`.cb, ~out_pos) out_pos.end() /; if (is_ref == true) result.ptr_pop() result.ptr_push(0) ;/ ;/ ;/ # Free all in tmp vector /; loop (int i = 0; i < tmps.count) [i++] inp = tmps.get(i) inp`.end() ;/ tmps.end() # Restore tmps in regs s`.restore_caller_tmp(~handle) /; if (result.loc < 0) s`.free_after(~result, true) ;/ return result ;/ /; _set_var_ptr(~void v, ~void new_val) Var v int sz = len v /; loop (int i = 0; i < sz) [i++] ~uint8 out = v + i ~uint8 in = new_val + i out` = in` ;/ ;/ # Chain compilation functions get pretty long. Read at your own risk /; _compile_chain_r_post(~Scope s, ~parse.Node post, ~Var v) # Post op type /; if (utils.strcmp(post`.data, "(\0") == true) _printf("TODO: tnslc does not yet support calling variables\n\0") ;; else if (utils.strcmp(post`.data, "`\0") == true) Var tmp = v`.de_ref() v`.end() self._set_var_ptr(v, ~tmp) ;; else if (utils.strcmp(post`.data, "{\0") == true) post = post`.sub.get(0) # make sure we store variable if it's in a register which may be overwritten /; if (v`.loc > 0) /; if (s`.is_tmp(v) == false) bool is_ref = false /; if (v`.is_ref() == true) is_ref = true int32 pp = 0 pp = pp - 1 v`.ptr_pop() v`.ptr_push(pp) ;/ Var tmp = s`.mk_tmp(v) tmp.set(s`.cb, v) v`.end() /; if (is_ref == true) tmp.ptr_pop() tmp.ptr_push(0) ;/ self._set_var_ptr(v, ~tmp) ;/ ;/ # Compute index Var idx = self._compile_value(s, post) /; if (idx.loc == 6 || idx.loc == 5) ~CompBuf buf = s`.cb buf`.add_c(" mov rax, rdi\n\0") idx.loc = 1 ;/ # Take index and set result Var tmp = v`.index(s`.cb, ~idx, 6) # Clear tmp /; if (s`.is_tmp(v) == true) s`.free_to(v, true) ;/ v`.end() idx.end() self._set_var_ptr(v, ~tmp) ;; else if (utils.strcmp(post`.data, "++\0") == true) # Generate copy (make sure no refs) Var copy = v`.copy() copy.strip_refs() # Make tmp var Var tmp = s`.mk_tmp(~copy) copy.end() # Set the new tmp to the value of the old tmp.set(s`.cb, v) # Increment the old v`.inc(s`.cb) v`.end() self._set_var_ptr(v, ~tmp) ;; else if (utils.strcmp(post`.data, "--\0") == true) # Generate copy (make sure no refs) Var copy = v`.copy() copy.strip_refs() # Make tmp var Var tmp = s`.mk_tmp(~copy) copy.end() # Set the new tmp to the value of the old tmp.set(s`.cb, v) # Decrement the old v`.dec(s`.cb) v`.end() self._set_var_ptr(v, ~tmp) ;/ ;/ /; _compile_chain_r_base (~Scope s, ~parse.Node base, ~utils.Vector v) [Var] # Base case for dot chains Var result result.loc = 0 # Sanity /; if (base`._type !== parse.NTYPE_ID) _printf("TODO: tnslc does not currently support dot-chains with literals or non-identifiers as primary\n\0") _printf(" will look into either intuiting the type from context or requiring a cast in the future\n\0") v`.end() v`.count = 0 return result ;/ # Check for var ~Var vv = s`.find_var(base`.data) /; if (vv == NULL) # If could not find in scope then try to find in module ~uint8 name = base`.data v`.push(~name) ~Module mod = s`.mod vv = mod`.find(SEARCH_VAR, v) /; if (vv !== NULL) result = vv`.as_global() v`.end() v`.count = 0 ;/ ;; else result = vv`.copy() v`.end() v`.count = 0 ;/ # If has a post op /; if (base`.sub.count > 0) ~parse.Node sub = base`.sub.get(0) /; if (utils.strcmp(sub`.data, "(\0") == true) /; if (vv !== NULL) result.end() result.loc = 0 _printf("TODO: tnslc does not yet support calling variables\n\0") return result ;/ # Try to find function. Make sure scope not in method module first. ~Module mod = s`.mod /; if (mod`.is_method() == true) mod = mod`.parent ;/ # Already tried to find in module so can just use v for search. ~Function to_call = mod`.find(SEARCH_FUNC, v) /; if (to_call == NULL) _printf("ERROR: Unable to find function or variable with name \"\0") _printf(base`.data) _printf("\" in scope \"\0") ~uint8 blab = s`.base_label() _printf(blab) _printf("\"\n\0") _delete(blab) v`.end() v`.count = 0 return result ;/ # Do call result = self._compile_call(s, sub, to_call, NULL, false) v`.end() v`.count = 0 ;; else if (result.loc == 0) # Exit _printf("ERROR: Unable to find variable \"\0") _printf(base`.data) _printf("\" in scope \"\0") ~uint8 blab = s`.base_label() _printf(blab) _printf("\"\n\0") _delete(blab) v`.end() v`.count = 0 return result ;; else self._compile_chain_r_post(s, sub, ~result) ;/ ;/ # Loop over all post-ops past the first ~parse.Node post /; loop (int i = 1; i < base`.sub.count) [i++] post = base`.sub.get(i) /; if (utils.strcmp(post`.data, "(\0") == true) result.end() result.loc = 0 _printf("TODO: tnslc does not yet support calling variables\n\0") v`.end() v`.count = 0 return result ;/ self._compile_chain_r_post(s, post, ~result) ;/ return result ;/ # Function: the very long one. /; _compile_chain_r (~Scope s, ~parse.Node n, ~utils.Vector v) [Var] ~parse.Node lhn = n`.sub.get(0) Var working = self._compile_chain_r_base(s, lhn, v) /; if (v`.count == 0) /; if (working.loc == 0) # If we have encountered an error or determined we could not find the variable for certain. return working ;/ ;/ # The way in which chains are parsed puts all dots in the second sub-node # Several points here: if have not found var or func yet need to try # if we can't and there is a post-op, then we need to stop. bool run = true ~parse.Node rhn /; loop (run == true) rhn = n`.sub.get(1) /; if (utils.strcmp(rhn`.data, ".\0") == true) n = rhn rhn = n`.sub.get(0) ;; else run = false ;/ # Sanity /; if (rhn`._type !== parse.NTYPE_ID) _printf("TODO: tnslc does not currently support dot-chains with literals or non-identifiers in the middle\n\0") v`.end() v`.count = 0 working.loc = 0 return working ;/ /; if (v`.count !== 0) # If we haven't yet found the var add to the vec and search the module ~uint8 dat = rhn`.data v`.push(~dat) ~Module mod = s`.mod ~Var find = mod`.find(SEARCH_VAR, v) /; if (find !== NULL) working = find`.as_global() v`.end() v`.count = 0 ;/ # Then check for post-op. /; if (rhn`.sub.count > 0) ~parse.Node post_op = rhn`.sub.get(0) /; if (utils.strcmp(post_op`.data, "(\0") == true) # Try a call /; if (find !== NULL) _printf("TODO: TNSL doesn't currently have good support for function calling by value\n\0") return working ;/ # Try to find function. Does not matter if we are a module since this is multi-part search ~Module mod = s`.mod # Already tried to find in module so can just use v for search. ~Function to_call = mod`.find(SEARCH_FUNC, v) /; if (to_call == NULL) _printf("ERROR: Unable to find function or variable with name \"\0") _printf(rhn`.data) _printf("\" in scope \"\0") ~uint8 blab = s`.base_label() _printf(blab) _printf("\"\n\0") _delete(blab) v`.end() v`.count = 0 return working ;/ # Do call working = self._compile_call(s, post_op, to_call, NULL, false) ;; else if (working.loc == 0) # Didn't find a variable but there was a post op _printf("ERROR: Failed to find variable (dot chain ends in \"\0") _printf(rhn`.data) _printf("\" in scope \"\0") ~uint8 blab = s`.base_label() _printf(blab) _printf("\"\n\0") _delete(blab) v`.end() v`.count = 0 return working ;; else # Do the post op on the found variable self._compile_chain_r_post(s, post_op, ~working) ;/ ;/ ;; else # We already have a var, not doing anything with the vector any more # Check method call int post_idx = 0 /; if (rhn`.sub.count > 0) ~parse.Node post = rhn`.sub.get(0) /; if (utils.strcmp(post`.data, "(\0") == true) # Try method post_idx++ ~Function to_call = working.find_method(rhn`.data) /; if (to_call == NULL) working.end() working.loc = 0 return working ;/ # Create space for the variable in a tmp Var result = self._compile_call(s, post, to_call, ~working, true) working.end() working = result ;/ ;/ /; if (post_idx == 0) # Normal member Var mbr = working.member(s`.cb, rhn`.data) /; if (mbr.loc == 0) # If the member didn't exist we end here # The member function already prints some info working.end() working.loc = 0 return working ;/ _printf("Member:\n\0") mbr._print(0) working.end() working = mbr /; if (rhn`.sub.count > 0) # Do first post-op after the member ~parse.Node post = rhn`.sub.get(0) self._compile_chain_r_post(s, post, ~working) ;/ ;/ ;/ # Loop over rest of post ops ~parse.Node post /; loop (int i = 1; i < rhn`.sub.count) [i++] post = rhn`.sub.get(i) self._compile_chain_r_post(s, post, ~working) ;/ ;/ return working ;/ /; _compile_chain (~Scope s, ~parse.Node n) [Var] # FIND_VAR!!!!! utils.Vector chain chain.init(8) return self._compile_chain_r(s, n, ~chain) ;/ /; _compile_bool_op (~Scope s, ~parse.Node n) [Var] Scope bs = s`.gen_sub("bool\0") ~parse.Node lhn = n`.sub.get(0) ~parse.Node rhn = n`.sub.get(1) ~uint8 op = n`.data ~Struct t = self._find_literal_type(s, "bool\0") Var dummy dummy._init(t) Var out = s`.mk_tmp(~dummy) dummy.end() # Compute left hand side Var lhs = self._compile_value(s, lhn) /; if (s`.is_tmp(~lhs) == true) s`.free_after(~lhs, true) ;/ /; if (lhs.is_struct() == true) _printf("Can not use struct as boolean value\n\0") lhs.end() bs.end() return out ;/ ~CompBuf buf = s`.cb # Pre-operation (jmp or mov) for this bool operator /; if (op{1} == '&') lhs.test(buf) ~uint8 lab = bs.end_label() buf`.add_c(" je \0") buf`.add_c(lab) buf`.add_c("\n\0") ;; else if (op{1} == '|') lhs.test(buf) ~uint8 lab = bs.end_label() buf`.add_c(" jne \0") buf`.add_c(lab) buf`.add_c("\n\0") ;; else if (op{1} == '^') /; if (lhs.loc > 0 && lhs.loc < 11) /; if (s`.is_tmp(~lhs) == false) bool was_ref = false /; if (lhs.is_ref() == true) was_ref = true int32 ptr = 0 ptr = ptr - 1 lhs.ptr_pop() lhs.ptr_push(ptr) ;/ Var tmp = s`.mk_tmp(~lhs) tmp.set(s`.cb, ~lhs) lhs.end() lhs = tmp /; if (was_ref == true) lhs.ptr_pop() lhs.ptr_push(0) ;/ ;/ ;/ ;/ # Compute right hand side Var rhs = self._compile_value(s, rhn) /; if (rhs.is_struct() == true) _printf("Can not use struct as boolean value\n\0") lhs.end() rhs.end() bs.end() return out ;/ # Post compare or test for this particular bool op /; if (op{1} == '^') # Mov lhs to a register bool is_ref = lhs.is_ref() /; if (lhs.loc < 1 || is_ref == true) Var tmp = lhs.copy() tmp.strip_refs() tmp.loc = 3 tmp.offset = 0 tmp.set(s`.cb, ~lhs) lhs.end() lhs = tmp ;/ /; if (op{0} == '!') out.cmov(s`.cb, ~lhs, ~rhs, "e\0") ;; else out.cmov(s`.cb, ~lhs, ~rhs, "e\0") ;/ ;; else rhs.test(buf) bs.place_end_label() /; if (op{0} == '!') out.cset(buf, "e\0") ;; else out.cset(buf, "ne\0") ;/ ;/ lhs.end() rhs.end() s`.free_after(~out, true) bs.end() return out ;/ /; _compile_cmp_op (~Scope s, ~parse.Node n) [Var] ~parse.Node lhn = n`.sub.get(0) ~parse.Node rhn = n`.sub.get(1) # Setup output ~Struct t = self._find_literal_type(s, "bool\0") Var dummy dummy._init(t) Var out = s`.mk_tmp(~dummy) dummy.end() # Compute left hand side Var lhs = self._compile_value(s, lhn) /; if (s`.is_tmp(~lhs) == true) s`.free_after(~lhs, true) ;/ # TODO: move lhs maybe if it is in rdi /; if (lhs.loc > 0 && lhs.loc < 11) /; if (s`.is_tmp(~lhs) == false) bool was_ref = false /; if (lhs.is_ref() == true) was_ref = true int32 ptr = 0 ptr = ptr - 1 lhs.ptr_pop() lhs.ptr_push(ptr) ;/ Var tmp = s`.mk_tmp(~lhs) tmp.set(s`.cb, ~lhs) lhs.end() lhs = tmp /; if (was_ref == true) lhs.ptr_pop() lhs.ptr_push(0) ;/ ;/ ;/ # Compute right hand side Var rhs = self._compile_value(s, rhn) # Check not structs /; if (lhs.is_struct() == true || rhs.is_struct() == true) _printf("Can not compare structs in this version of tnsl\n\0") lhs.end() rhs.end() s`.free_after(~out, true) return out ;/ # Mov lhs to a register bool is_ref = lhs.is_ref() /; if (lhs.loc < 1 || is_ref == true) Var tmp = lhs.copy() tmp.strip_refs() tmp.loc = 3 tmp.offset = 0 tmp.set(s`.cb, ~lhs) lhs.end() lhs = tmp ;/ # Do comparitive move /; if (utils.strcmp(n`.data, "==\0") == true) out.cmov(s`.cb, ~lhs, ~rhs, "e\0") ;; else if (utils.strcmp(n`.data, "<\0") == true) out.cmov(s`.cb, ~lhs, ~rhs, "l\0") ;; else if (utils.strcmp(n`.data, ">\0") == true) out.cmov(s`.cb, ~lhs, ~rhs, "g\0") ;; else if (utils.strcmp(n`.data, "!==\0") == true) out.cmov(s`.cb, ~lhs, ~rhs, "ne\0") ;; else if (utils.strcmp(n`.data, "!<\0") == true) out.cmov(s`.cb, ~lhs, ~rhs, "nl\0") ;; else if (utils.strcmp(n`.data, "!>\0") == true) out.cmov(s`.cb, ~lhs, ~rhs, "ng\0") ;; else if (utils.strcmp(n`.data, "<==\0") == true) out.cmov(s`.cb, ~lhs, ~rhs, "le\0") ;; else if (utils.strcmp(n`.data, ">==\0") == true) out.cmov(s`.cb, ~lhs, ~rhs, "ge\0") ;/ lhs.end() rhs.end() s`.free_after(~out, true) return out ;/ /; _compile_bin (~Scope s, ~parse.Node n) [Var] # TODO /; if (utils.strcmp(n`.data, ".\0") == true) return self._compile_chain(s, n) ;; else if (parse._in_csv(BOOL_OPS, n`.data) == true) return self._compile_bool_op(s, n) ;; else if (parse._in_csv(CMP_OPS, n`.data) == true) return self._compile_cmp_op(s, n) ;/ ~parse.Node lhn = n`.sub.get(0) ~parse.Node rhn = n`.sub.get(1) # If we are setting /; if (utils.strcmp(n`.data, "=\0") == true) Var lhs = self._compile_value(s, lhn) /; if (s`.is_tmp(~lhs) == true) s`.free_after(~lhs, true) ;/ # TODO: move lhs maybe if it is in rdi /; if (lhs.loc > 0 && lhs.loc < 11) /; if (s`.is_tmp(~lhs) == false) bool was_ref = false /; if (lhs.is_ref() == true) was_ref = true int32 ptr = 0 ptr = ptr - 1 lhs.ptr_pop() lhs.ptr_push(ptr) ;/ Var tmp = s`.mk_tmp(~lhs) tmp.set(s`.cb, ~lhs) lhs.end() lhs = tmp /; if (was_ref == true) lhs.ptr_pop() lhs.ptr_push(0) ;/ ;/ ;/ Var rhs = self._compile_value(s, rhn) lhs.set(s`.cb, ~rhs) # Cleanup rhs /; if (s`.is_tmp(~rhs) == true) s`.free_to(~rhs, true) ;/ rhs.end() return lhs ;/ # Otherwise Var lhs = self._compile_value(s, lhn) /; if (s`.is_tmp(~lhs) == false) Var copy = lhs.copy() copy.strip_refs() Var tmp = s`.mk_tmp(~copy) copy.end() tmp.set(s`.cb, ~lhs) lhs.end() lhs = tmp ;/ /; if (lhs.is_ref() == true) Var copy = lhs.copy() copy.strip_refs() Var tmp = s`.mk_tmp(~copy) copy.end() tmp.set(s`.cb, ~lhs) lhs.end() lhs = tmp ;/ Var rhs = self._compile_value(s, rhn) /; if (utils.strcmp(n`.data, "*\0") == true) lhs.mul(s`.cb, ~rhs) ;; else if (utils.strcmp(n`.data, "/\0") == true) lhs.div(s`.cb, ~rhs) ;; else if (utils.strcmp(n`.data, "%\0") == true) lhs.mod(s`.cb, ~rhs) ;; else if (utils.strcmp(n`.data, "+\0") == true) lhs.add(s`.cb, ~rhs) ;; else if (utils.strcmp(n`.data, "-\0") == true) lhs.sub(s`.cb, ~rhs) ;; else if (utils.strcmp(n`.data, "&\0") == true) lhs.and(s`.cb, ~rhs) ;; else if (utils.strcmp(n`.data, "|\0") == true) lhs.or(s`.cb, ~rhs) ;; else if (utils.strcmp(n`.data, "^\0") == true) lhs.xor(s`.cb, ~rhs) ;/ s`.free_after(~lhs, true) rhs.end() return lhs ;/ /; _compile_pre (~Scope s, ~parse.Node n) [Var] Var out /; if (utils.strcmp(n`.data, "-\0") == true) ~parse.Node sub = n`.sub.get(0) out = self._compile_value(s, sub) out.neg(s`.cb) ;; else if (utils.strcmp(n`.data, "!\0") == true) ~parse.Node sub = n`.sub.get(0) out = self._compile_value(s, sub) out.not(s`.cb) ;; else if (utils.strcmp(n`.data, "~\0") == true) ~parse.Node sub = n`.sub.get(0) Var tmp = self._compile_value(s, sub) out = tmp.take_ptr(s`.cb, 5) tmp.end() ;; else if (utils.strcmp(n`.data, "--\0") == true) ~parse.Node sub = n`.sub.get(0) out = self._compile_value(s, sub) out.dec(s`.cb) ;; else if (utils.strcmp(n`.data, "++\0") == true) ~parse.Node sub = n`.sub.get(0) out = self._compile_value(s, sub) out.inc(s`.cb) ;; else if (utils.strcmp(n`.data, "len\0") == true) _printf("len not impl\n\0") ;; else _printf("COMPILER ERROR: \"\0") _printf(n`.data) _printf("\" NOT RECOGNIZED AS A VALID PREOP\n\0") ;/ return out ;/ /; _find_literal_type (~Scope s, ~uint8 name) [~Struct] utils.Vector vec vec.init(8) vec.push(~name) ~Struct out out = s`.mod`.find(SEARCH_STRUCT, ~vec) vec.end() return out ;/ /; _compile_literal (~Scope s, ~parse.Node n) [Var] Var out /; if (utils.strcmp(n`.data, "true\0") == true) ~Struct t = self._find_literal_type(s, "bool\0") out._init(t) out.offset = 1 ;; else if (utils.strcmp(n`.data, "false\0") == true) ~Struct t = self._find_literal_type(s, "bool\0") out._init(t) out.offset = 0 ;; else if (utils.strcmp(n`.data, "null\0") == true) ~Struct t = self._find_literal_type(s, "void\0") out._init(t) out.offset = 0 out.ptr_push(1) ;; else if (n`.data{0} == '\'') ~Struct t = self._find_literal_type(s, "uint8\0") out._init(t) ~uint8 dat = n`.data dat++ uint8 val = utils.unquote_cha(dat) out.offset = val ;; else if (n`.data{0} == '\"') _printf("TODO: in-function strings\n\0") ;; else ~Struct t = self._find_literal_type(s, "int\0") out._init(t) out.offset = utils.cstr_to_int(n`.data) ;/ return out ;/ # Should handle computing a value, delegate to other funcs when needed /; _compile_value (~Scope s, ~parse.Node n) [Var] /; if (n`._type == parse.NTYPE_VALUE) ~parse.Node v = n`.sub.get(0) return self._compile_value(s, v) ;; else if (n`._type == parse.NTYPE_LITERAL) return self._compile_literal(s, n) ;; else if (n`._type == parse.NTYPE_ID) utils.Vector tmp tmp.init(8) Var out = self._compile_chain_r_base(s, n, ~tmp) /; if (tmp.count !== 0) tmp.end() ;; else return out ;/ ;; else if (n`._type == parse.NTYPE_PRE_OP) return self._compile_pre(s, n) ;; else if (n`._type == parse.NTYPE_BIN_OP) return self._compile_bin(s, n) ;/ _printf("COMPILER ERROR: NOT IMPL NODE TYPE \"\0") parse.print_node_type(n) _printf("\" IN ROOT VAL COMPUTATION\n\0") Var out out._init(NULL) return out ;/ /; _print (int idt) _indent(idt) _printf("{ Function : \0") _printf(self.name) _printf("\n\0") _indent(idt + 1) _printf("inputs:\n\0") ~Var prtv /; loop (int i = 0; i < self.inputs.count) [i++] prtv = self.inputs.get(i) prtv`._print(idt + 2) ;/ _indent(idt + 1) _printf("outputs:\n\0") /; loop (int i = 0; i < self.outputs.count) [i++] prtv = self.outputs.get(i) prtv`._print(idt + 2) ;/ _indent(idt) _printf("}\n\0") ;/ /; end _delete(self.name) ~Var v /; loop (int i = 0; i < self.inputs.count) [i++] v = self.inputs.get(i) v`.end() ;/ self.inputs.end() /; loop (int i = 0; i < self.outputs.count) [i++] v = self.outputs.get(i) v`.end() ;/ self.outputs.end() ;/ ;/