~starkingdoms/starkingdoms

731aaaa6ce6390700ad53797b117193576ef8a82 — ghostly_zsh 1 year, 3 months ago 37be9a1
self-recursion works
M kabel/src/codegen.rs => kabel/src/codegen.rs +23 -0
@@ 95,12 95,18 @@ impl Codegen {
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2;
        }
        self.scopes.last_mut().expect("codegen scopes vec was empty").1 += 1;

        let old_unit_ptr = self.vm.unit_ptr;
        let old_ip = self.vm.ip;
        // set unit to write to
        self.vm.unit_ptr = new_unit_ptr;
        self.vm.ip = 0;
        self.vm.units[self.vm.unit_ptr].pool.push(Value::Fun(Function {
            unit_ptr: new_unit_ptr,
            arity: args.len(),
        }));
        self.scopes.last_mut().expect("codegen scopes vec was empty").1 += args.len();
        
        if let ASTType::Block(ref stmts) = block.ast_type {
            self.visit_block(&block, stmts.clone(), ScopeType::Function);


@@ 137,6 143,23 @@ impl Codegen {
        self.vm.ip = old_ip;
    }
    pub fn visit_return(&mut self, ast: &AST, expr: Option<AST>) {
        let mut scopes = self.scopes.clone();
        let mut pop_count = 0;
        /*loop {
            let (scope_type, scope) = scopes.pop().expect("scopes empty in return");
            pop_count += scope;
            if scope_type == ScopeType::Function {
                break;
            }
        }*/
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into());
        self.vm.units[self.vm.unit_ptr].code.push(pop_count as u8);
        if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line {
            self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2));
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2;
        }

        if let Some(expr) = expr {
            self.visit(expr);
        } else {

M kabel/src/macros.rs => kabel/src/macros.rs +13 -13
@@ 301,13 301,13 @@ macro_rules! push_codegen {
#[macro_export]
macro_rules! vm_boolean_equality {
    ($self:expr, $oper:tt) => {
        match ($self.units[$self.unit_ptr].stack.pop().expect(&("Couldn't pop left side of ".to_owned() + stringify!($oper))),
                $self.units[$self.unit_ptr].stack.pop().expect(&("Couldn't pop right side of ".to_owned() + stringify!($oper)))) {
            (Num(v1), Num(v2)) => $self.units[$self.unit_ptr].stack.push(Bool(v1 $oper v2)),
            (Str(v1), Str(v2)) => $self.units[$self.unit_ptr].stack.push(Bool(v1 $oper v2)),
            (Bool(v1), Bool(v2)) => $self.units[$self.unit_ptr].stack.push(Bool(v1 $oper v2)),
            (Null, Null) => $self.units[$self.unit_ptr].stack.push(Bool(true)),
            (_v1, _v2) => $self.units[$self.unit_ptr].stack.push(Bool(false)),
        match ($self.stacks[$self.stack_ptr].pop().expect(&("Couldn't pop left side of ".to_owned() + stringify!($oper))),
                $self.stacks[$self.stack_ptr].pop().expect(&("Couldn't pop right side of ".to_owned() + stringify!($oper)))) {
            (Num(v1), Num(v2)) => $self.stacks[$self.stack_ptr].push(Bool(v1 $oper v2)),
            (Str(v1), Str(v2)) => $self.stacks[$self.stack_ptr].push(Bool(v1 $oper v2)),
            (Bool(v1), Bool(v2)) => $self.stacks[$self.stack_ptr].push(Bool(v1 $oper v2)),
            (Null, Null) => $self.stacks[$self.stack_ptr].push(Bool(true)),
            (_v1, _v2) => $self.stacks[$self.stack_ptr].push(Bool(false)),
        }
    };
}


@@ 315,14 315,14 @@ macro_rules! vm_boolean_equality {
#[macro_export]
macro_rules! vm_boolean_comparison {
    ($self:expr, $oper:tt) => {
        match ($self.units[$self.unit_ptr].stack.pop().expect(&("Couldn't pop left side of ".to_owned() + stringify!($oper))),
                $self.units[$self.unit_ptr].stack.pop().expect(&("Couldn't pop right side of ".to_owned() + stringify!($oper)))) {
            (Num(v1), Num(v2)) => $self.units[$self.unit_ptr].stack.push(Bool(v1 $oper v2)),
            (Str(v1), Str(v2)) => $self.units[$self.unit_ptr].stack.push(Bool(v1 $oper v2)),
            (Bool(v1), Bool(v2)) => $self.units[$self.unit_ptr].stack.push(Bool(v1 $oper v2)),
        match ($self.stacks[$self.stack_ptr].pop().expect(&("Couldn't pop left side of ".to_owned() + stringify!($oper))),
                $self.stacks[$self.stack_ptr].pop().expect(&("Couldn't pop right side of ".to_owned() + stringify!($oper)))) {
            (Num(v1), Num(v2)) => $self.stacks[$self.stack_ptr].push(Bool(v1 $oper v2)),
            (Str(v1), Str(v2)) => $self.stacks[$self.stack_ptr].push(Bool(v1 $oper v2)),
            (Bool(v1), Bool(v2)) => $self.stacks[$self.stack_ptr].push(Bool(v1 $oper v2)),
            (Null, _) => return Err(vm_error!($self, RuntimeErrorKind::WrongType, "Tried to perform comparison \"{}\" on value null", stringify!($tt))),
            (_, Null) => return Err(vm_error!($self, RuntimeErrorKind::WrongType, "Tried to perform comparison \"{}\" on value null", stringify!($tt))),
            (_v1, _v2) => $self.units[$self.unit_ptr].stack.push(Bool(false)),
            (_v1, _v2) => $self.stacks[$self.stack_ptr].push(Bool(false)),
        }
    };
}

M kabel/src/main.rs => kabel/src/main.rs +1 -1
@@ 52,9 52,9 @@ fn main() {
    let codegen = run_codegen(program.to_string(), ast);

    let mut vm = codegen.vm;
    output += &debug_stack(&vm.units[0].stack);
    for (index, _) in vm.units.iter().enumerate() {
        vm.unit_ptr = index;
        output += &format!("{:?}", vm.units[vm.unit_ptr].pool);
        output += "unit ptr: ";
        output += &vm.unit_ptr.to_string();
        output += "\n";

M kabel/src/name_resolution.rs => kabel/src/name_resolution.rs +20 -14
@@ 5,7 5,7 @@ use crate::{ast::{ASTType, AST}, ast_error, ast_from_ast, error::{ErrorKind, Kab
pub struct Resolver {
    text: Vec<String>,
    symbol_table: Vec<HashMap<String, (Symbol, usize)>>, // (Symbol, reference to locals)
    pub locals: Vec<usize>, // scope
    pub locals: Vec<Vec<usize>>, // scope
    pub scope: usize,
    pub errors: Vec<KabelError>,
}


@@ 15,7 15,7 @@ impl Resolver {
        Self {
            text: text.lines().collect::<Vec<&str>>().iter().map(|s| s.to_string()).collect(),
            symbol_table: vec![HashMap::new()],
            locals: Vec::new(),
            locals: vec![Vec::new()],
            scope: 0,
            errors: Vec::new(),
        }


@@ 61,18 61,24 @@ impl Resolver {
                        } else {}
                    }
                }
                self.locals.last_mut().expect("locals last in function push").push(self.scope);
                self.symbol_table.last_mut().unwrap().insert(name.name.clone(),
                 (Symbol::Function(args.len()), self.locals.last().expect("locals last in function symbol len").len()-1));

                self.locals.push(Vec::new());
                self.symbol_table.push(HashMap::new());
                self.locals.last_mut().expect("locals last in function self-reference push").push(self.scope+1);
                self.symbol_table.last_mut().unwrap().insert(name.name.clone(), (Symbol::Var,self.locals.last().expect("locals last in function self-reference len").len()-1));
                for arg in args.clone() {
                    self.symbol_table.last_mut().unwrap().insert(arg.name, (Symbol::Var,0));
                    self.locals.last_mut().expect("locals last in function arg push").push(self.scope+1);
                    self.symbol_table.last_mut().unwrap().insert(arg.name, (Symbol::Var,self.locals.last().expect("locals last in function arg len").len()-1));
                }
                let block = self.visit(*block);
                self.symbol_table.pop();
                self.locals.push(self.scope);
                self.symbol_table.last_mut().unwrap().insert(name.name.clone(),
                 (Symbol::Function(args.len()), self.locals.len()-1));
                self.locals.pop();
                AST {
                    ast_type: Function(name, args, Box::new(block)),
                    extensions: vec![Extension::Resolution(self.scope, self.locals.len()-1)],
                    extensions: vec![Extension::Resolution(self.scope, self.locals.last().expect("locals last in function ast len").len()-1)],
                    start_line: ast.start_line,
                    end_line: ast.end_line,
                    start_column: ast.start_column,


@@ 113,9 119,9 @@ impl Resolver {
                    n_expr3 = Some(self.visit(expr));
                }
                let block = self.visit(*block);
                while let Some(scope) = self.locals.last() {
                while let Some(scope) = self.locals.last().expect("locals failed in For").last() {
                    if self.scope == *scope {
                        self.locals.pop();
                        self.locals.last_mut().expect("locals failed in For pop").pop();
                    } else {
                        break;
                    }


@@ 145,9 151,9 @@ impl Resolver {
                        self.locals.remove(index);
                    }
                }*/
                while let Some(scope) = self.locals.last() {
                while let Some(scope) = self.locals.last().expect("locals last in block").last() {
                    if self.scope == *scope {
                        self.locals.pop();
                        self.locals.last_mut().expect("locals last in block pop").pop();
                    } else {
                        break;
                    }


@@ 158,7 164,7 @@ impl Resolver {
            }
            Decl(name, expr) => {
                let expr = self.visit(*expr);
                self.locals.push(self.scope);
                self.locals.last_mut().expect("locals last in decl push").push(self.scope);
                if self.resolve_var(&name.name).0 {
                    self.errors.push(out_of_scope_var!(self,
                            ErrorKind::VariableAlreadyDeclaredVariable, ast,


@@ 181,10 187,10 @@ impl Resolver {
                        } else {}
                    }
                }
                self.symbol_table.last_mut().unwrap().insert(name.name.clone(), (Symbol::Var, self.locals.len()-1));
                self.symbol_table.last_mut().unwrap().insert(name.name.clone(), (Symbol::Var, self.locals.last().expect("locals last in decl symbol").len()-1));
                AST {
                    ast_type: Decl(name, Box::new(expr)),
                    extensions: vec![Extension::Resolution(self.scope, self.locals.len()-1)],
                    extensions: vec![Extension::Resolution(self.scope, self.locals.last().expect("locals last in decl ast").len()-1)],
                    start_line: ast.start_line,
                    end_line: ast.end_line,
                    start_column: ast.start_column,

M kabel/src/vm.rs => kabel/src/vm.rs +53 -47
@@ 5,15 5,13 @@ use crate::vm_error;
#[derive(Debug, Clone)]
pub struct Unit {
    pub code: Vec<u8>,
    pub stack: Vec<Value>,
    pub pool: Vec<Value>,
    pub lines: Vec<(usize, usize)>, // line #, repeats number of instructions
}
impl Unit {
    pub fn new(code: Vec<u8>, stack: Vec<Value>, pool: Vec<Value>, lines: Vec<(usize, usize)>) -> Self {
    pub fn new(code: Vec<u8>, pool: Vec<Value>, lines: Vec<(usize, usize)>) -> Self {
        Self {
            code,
            stack,
            pool,
            lines,
        }


@@ 21,7 19,6 @@ impl Unit {
    pub fn new_empty() -> Self {
        Self {
            code: Vec::new(),
            stack: Vec::new(),
            pool: Vec::new(),
            lines: Vec::new(),
        }


@@ 32,8 29,10 @@ impl Unit {
pub struct VM {
    pub ip: usize,
    pub unit_ptr: usize,
    pub stack_ptr: usize,
    pub call_stack: Vec<(usize, usize)>, // (unit_ptr, ip)
    pub units: Vec<Unit>,
    pub stacks: Vec<Vec<Value>>,
    text: Vec<String>
}



@@ 43,8 42,10 @@ impl VM {
        Self {
            ip: 0,
            unit_ptr: 0,
            stack_ptr: 0,
            call_stack: Vec::new(),
            units: vec![Unit::new(bytecode, Vec::new(), pool, lines)],
            units: vec![Unit::new(bytecode, pool, lines)],
            stacks: vec![Vec::new()],
            text: text.lines().map(|s| s.to_string()).collect::<Vec<String>>(),
        }
    }


@@ 55,25 56,25 @@ impl VM {
                0x00 => { // LOAD
                    let byte = self.read() as usize;
                    let value = self.units[self.unit_ptr].pool[byte].clone();
                    self.units[self.unit_ptr].stack.push(value);
                    self.stacks[self.stack_ptr].push(value);
                }
                0x01 => { // VAR
                    let ptr = self.read() as usize;
                    let value = self.units[self.unit_ptr].stack[ptr].clone();
                    self.units[self.unit_ptr].stack.push(value);
                    let value = self.stacks[self.stack_ptr][ptr].clone();
                    self.stacks[self.stack_ptr].push(value);
                }
                // ASSIGN
                0x02 => {
                    let value = self.units[self.unit_ptr].stack.pop().unwrap();
                    let value = self.stacks[self.stack_ptr].pop().unwrap();
                    let ptr = self.read();
                    self.units[self.unit_ptr].stack[ptr as usize] = value.clone();
                    self.units[self.unit_ptr].stack.push(value);
                    self.stacks[self.stack_ptr][ptr as usize] = value.clone();
                    self.stacks[self.stack_ptr].push(value);
                }
                0x03 => { // ADD
                    match (self.units[self.unit_ptr].stack.pop().unwrap(), self.units[self.unit_ptr].stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.units[self.unit_ptr].stack.push(Num(v1 + v2)),
                    match (self.stacks[self.stack_ptr].pop().unwrap(), self.stacks[self.stack_ptr].pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.stacks[self.stack_ptr].push(Num(v1 + v2)),
                        (Str(v1), Str(v2)) => {
                            self.units[self.unit_ptr].stack.push(Str(v1 + &v2));
                            self.stacks[self.stack_ptr].push(Str(v1 + &v2));
                        },
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot add booleans"))


@@ 84,8 85,8 @@ impl VM {
                    }
                }
                0x04 => { // SUB
                    match (self.units[self.unit_ptr].stack.pop().unwrap(), self.units[self.unit_ptr].stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.units[self.unit_ptr].stack.push(Num(v1 - v2)),
                    match (self.stacks[self.stack_ptr].pop().unwrap(), self.stacks[self.stack_ptr].pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.stacks[self.stack_ptr].push(Num(v1 - v2)),
                        (Str(_v1), Str(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot subtract strings"))
                        },


@@ 98,11 99,11 @@ impl VM {
                    }
                }
                0x05 => { // MUL
                    match (self.units[self.unit_ptr].stack.pop().unwrap(), self.units[self.unit_ptr].stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.units[self.unit_ptr].stack.push(Num(v1 * v2)),
                    match (self.stacks[self.stack_ptr].pop().unwrap(), self.stacks[self.stack_ptr].pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.stacks[self.stack_ptr].push(Num(v1 * v2)),
                        (Str(v1), Num(v2)) => {
                            if v2.fract() == 0.0 {
                                self.units[self.unit_ptr].stack.push(Str(v1.repeat(v2 as usize)));
                                self.stacks[self.stack_ptr].push(Str(v1.repeat(v2 as usize)));
                            } else {
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Number must be an integer"))
                            }


@@ 119,8 120,8 @@ impl VM {
                    }
                }
                0x06 => { // DIV
                    match (self.units[self.unit_ptr].stack.pop().unwrap(), self.units[self.unit_ptr].stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.units[self.unit_ptr].stack.push(Num(v1 / v2)),
                    match (self.stacks[self.stack_ptr].pop().unwrap(), self.stacks[self.stack_ptr].pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.stacks[self.stack_ptr].push(Num(v1 / v2)),
                        (Str(_v1), Str(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot divide strings"))
                        },


@@ 133,8 134,8 @@ impl VM {
                    }
                }
                0x07 => { // MOD
                    match (self.units[self.unit_ptr].stack.pop().unwrap(), self.units[self.unit_ptr].stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.units[self.unit_ptr].stack.push(Num(v1 % v2)),
                    match (self.stacks[self.stack_ptr].pop().unwrap(), self.stacks[self.stack_ptr].pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.stacks[self.stack_ptr].push(Num(v1 % v2)),
                        (Str(_v1), Str(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform modulus on strings"))
                        },


@@ 147,7 148,7 @@ impl VM {
                    }
                }
                0x08 => { // BITAND
                    match (self.units[self.unit_ptr].stack.pop().unwrap(), self.units[self.unit_ptr].stack.pop().unwrap()) {
                    match (self.stacks[self.stack_ptr].pop().unwrap(), self.stacks[self.stack_ptr].pop().unwrap()) {
                        (Num(v1), Num(v2)) => {
                            if v1.fract() != 0.0 {
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise AND on {}", v1))


@@ 155,7 156,7 @@ impl VM {
                            if v2.fract() != 0.0 {
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise AND on {}", v2))
                            }
                            self.units[self.unit_ptr].stack.push(Num((v1 as u32 & v2 as u32) as f32))
                            self.stacks[self.stack_ptr].push(Num((v1 as u32 & v2 as u32) as f32))
                        }
                        (Str(_v1), Str(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise AND on strings"))


@@ 169,7 170,7 @@ impl VM {
                    }
                }
                0x09 => { // BITXOR
                    match (self.units[self.unit_ptr].stack.pop().unwrap(), self.units[self.unit_ptr].stack.pop().unwrap()) {
                    match (self.stacks[self.stack_ptr].pop().unwrap(), self.stacks[self.stack_ptr].pop().unwrap()) {
                        (Num(v1), Num(v2)) => {
                            if v1.fract() != 0.0 {
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise XOR on {}", v1))


@@ 177,7 178,7 @@ impl VM {
                            if v2.fract() != 0.0 {
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise XOR on {}", v2))
                            }
                            self.units[self.unit_ptr].stack.push(Num((v1 as u32 ^ v2 as u32) as f32))
                            self.stacks[self.stack_ptr].push(Num((v1 as u32 ^ v2 as u32) as f32))
                        }
                        (Str(_v1), Str(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise XOR on strings"))


@@ 191,7 192,7 @@ impl VM {
                    }
                }
                0x0A => { // BITOR
                    match (self.units[self.unit_ptr].stack.pop().unwrap(), self.units[self.unit_ptr].stack.pop().unwrap()) {
                    match (self.stacks[self.stack_ptr].pop().unwrap(), self.stacks[self.stack_ptr].pop().unwrap()) {
                        (Num(v1), Num(v2)) => {
                            if v1.fract() != 0.0 {
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise OR on {}", v1))


@@ 199,7 200,7 @@ impl VM {
                            if v2.fract() != 0.0 {
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise OR on {}", v2))
                            }
                            self.units[self.unit_ptr].stack.push(Num((v1 as u32 | v2 as u32) as f32))
                            self.stacks[self.stack_ptr].push(Num((v1 as u32 | v2 as u32) as f32))
                        }
                        (Str(_v1), Str(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise OR on strings"))


@@ 225,14 226,14 @@ impl VM {
                // LE
                0x10 => vm_boolean_comparison!(self, <=),
                // OR
                0x11 => match (self.units[self.unit_ptr].stack.pop().unwrap(), self.units[self.unit_ptr].stack.pop().unwrap()) {
                0x11 => match (self.stacks[self.stack_ptr].pop().unwrap(), self.stacks[self.stack_ptr].pop().unwrap()) {
                    (Num(_v1), Num(_v2)) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"||\" on non-boolean numbers"))
                    }
                    (Str(_v1), Str(_v2)) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"||\" on non-boolean strings"))
                    },
                    (Bool(v1), Bool(v2)) => self.units[self.unit_ptr].stack.push(Bool(v1 || v2)),
                    (Bool(v1), Bool(v2)) => self.stacks[self.stack_ptr].push(Bool(v1 || v2)),
                    (Null, _) => return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"||\" on non-boolean value null")),
                    (_, Null) => return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"||\" on non-boolean value null")),
                    (v1, v2) => {


@@ 240,14 241,14 @@ impl VM {
                    }
                }
                // AND
                0x12 => match (self.units[self.unit_ptr].stack.pop().unwrap(), self.units[self.unit_ptr].stack.pop().unwrap()) {
                0x12 => match (self.stacks[self.stack_ptr].pop().unwrap(), self.stacks[self.stack_ptr].pop().unwrap()) {
                    (Num(_v1), Num(_v2)) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"&&\" on non-boolean numbers"))
                    }
                    (Str(_v1), Str(_v2)) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"&&\" on non-boolean strings"))
                    },
                    (Bool(v1), Bool(v2)) => self.units[self.unit_ptr].stack.push(Bool(v1 && v2)),
                    (Bool(v1), Bool(v2)) => self.stacks[self.stack_ptr].push(Bool(v1 && v2)),
                    (Null, _) => return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"&&\" on non-boolean value null")),
                    (_, Null) => return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"&&\" on non-boolean value null")),
                    (v1, v2) => {


@@ 255,7 256,7 @@ impl VM {
                    }
                }
                // NOT
                0x13 => match self.units[self.unit_ptr].stack.pop().unwrap() {
                0x13 => match self.stacks[self.stack_ptr].pop().unwrap() {
                    Null => { return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean value null")) }
                    Num(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean number"))


@@ 263,15 264,15 @@ impl VM {
                    Str(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean string"))
                    }
                    Bool(v1) => self.units[self.unit_ptr].stack.push(Bool(!v1)),
                    Bool(v1) => self.stacks[self.stack_ptr].push(Bool(!v1)),
                    Fun(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean function"))
                    }
                }
                // NEG
                0x14 => match self.units[self.unit_ptr].stack.pop().unwrap() {
                0x14 => match self.stacks[self.stack_ptr].pop().unwrap() {
                    Null => { return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number value null")) }
                    Num(v1) => self.units[self.unit_ptr].stack.push(Num(-v1)),
                    Num(v1) => self.stacks[self.stack_ptr].push(Num(-v1)),
                    Str(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number string"))
                    }


@@ 294,7 295,7 @@ impl VM {
                }
                // JNE
                0x17 => {
                    let condition = self.units[self.unit_ptr].stack.pop().unwrap();
                    let condition = self.stacks[self.stack_ptr].pop().unwrap();
                    if let Value::Bool(condition) = condition {
                        if !condition {
                            let loc = self.read_u16();


@@ 310,16 311,19 @@ impl VM {
                // CALL
                0x18 => {
                    let num_args = self.read();
                    let function = self.units[self.unit_ptr].stack.pop().expect("Stack was empty in call");
                    let function = self.stacks[self.stack_ptr].pop().expect("Stack was empty in call");
                    if let Value::Fun(function) = function {
                        self.stacks.push(Vec::new());
                        if num_args as usize != function.arity {
                            return Err(vm_error!(self, RuntimeErrorKind::IncorrectArity, "Function has {} arguments, {} provided", function.arity, num_args))
                        }
                        self.call_stack.push((self.unit_ptr, self.ip));
                        self.stacks[self.stack_ptr+1].push(Value::Fun(function));
                        for _ in 0..num_args {
                            let arg = self.units[self.unit_ptr].stack.pop().expect("Missing arguments in call");
                            self.units[function.unit_ptr as usize].stack.push(arg);
                            let arg = self.stacks[self.stack_ptr].pop().expect("Missing arguments in call");
                            self.stacks[self.stack_ptr+1].push(arg);
                        }
                        self.stack_ptr += 1;
                        self.ip = 0;
                        self.unit_ptr = function.unit_ptr as usize;
                    } else {


@@ 329,22 333,24 @@ impl VM {
                // RET
                0x19 => {
                    let (unit_ptr, ip) = self.call_stack.pop().expect("Call stack empty on RET");
                    let ret = self.units[self.unit_ptr].stack.pop().expect("Missing return value");
                    self.units[self.unit_ptr].stack.clear();
                    let ret = self.stacks[self.stack_ptr].pop().expect("Missing return value");
                    //self.stacks[self.stack_ptr].clear();
                    self.unit_ptr = unit_ptr;
                    self.ip = ip;
                    // returned to code
                    self.units[self.unit_ptr].stack.push(ret);
                    self.stack_ptr -= 1;
                    self.stacks.pop().expect("RET pop stacks failed");
                    self.stacks[self.stack_ptr].push(ret);
                }

                0xFD => { // POP
                    let times = self.read();
                    for _ in 0..times {
                        self.units[self.unit_ptr].stack.pop().expect(&format!("{}: Unable to pop stack", self.ip));
                        self.stacks[self.stack_ptr].pop().expect(&format!("{}: Unable to pop stack", self.ip));
                    }
                }
                0xFE => { // PRINT
                    let value = self.units[self.unit_ptr].stack.pop().unwrap();
                    let value = self.stacks[self.stack_ptr].pop().unwrap();
                    match value {
                        Null => *output += "null",
                        Num(v) => *output += &v.to_string(),


@@ 401,7 407,7 @@ impl Value {
    }
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub struct Function {
    pub unit_ptr: usize,
    pub arity: usize,

M kabel/test/runtime/fn_absent_block.out => kabel/test/runtime/fn_absent_block.out +1 -1
@@ 1,3 1,3 @@
Error 0004: Function "foo" not in scope at line 3, column 5
Error 0004: Variable "foo" not in scope at line 3, column 5
    foo();
    ^

A kabel/test/runtime/max.kab => kabel/test/runtime/max.kab +12 -0
@@ 0,0 1,12 @@
function max(first, second) {
    if(first > second) {
        return first;
    } else {
        return second;
    }
}

print max(1, 2); // 2
print max(3, 6); // 6
print max(-5.2, 5); // 5
print max(5, -5.2); // 5

A kabel/test/runtime/max.out => kabel/test/runtime/max.out +4 -0
@@ 0,0 1,4 @@
2
6
5
5

A kabel/test/runtime/recursive_fib.kab => kabel/test/runtime/recursive_fib.kab +13 -0
@@ 0,0 1,13 @@
function fib(x) {
    if(x == 0) {
        return 0;
    }
    if(x == 1 || x == 2) {
        return 1;
    }
    return fib(x-1) + fib(x-2);
}
print fib(2); // 1
print fib(3); // 2
print fib(4); // 3
print fib(5); // 5

A kabel/test/runtime/recursive_fib.out => kabel/test/runtime/recursive_fib.out +4 -0
@@ 0,0 1,4 @@
1
2
3
5

M kabel/test/syntax/assignment.out => kabel/test/syntax/assignment.out +3 -3
@@ 1,8 1,8 @@
Program
| Decl i
| Decl i []
| | Lit 0
| Expr
| | Assign i
| | Assign i []
| | | Lit string
| Decl _foo
| Decl _foo []
| | Lit 3

M kabel/test/syntax/function.out => kabel/test/syntax/function.out +3 -3
@@ 1,7 1,7 @@
Program
| Function foo one two
| Function foo one two []
| | Block
| | | Return
| | | | Binary Add
| | | | | Lit one
| | | | | Lit two
| | | | | Lit one []
| | | | | Lit two []

M kabel/test/syntax/if_else.out => kabel/test/syntax/if_else.out +5 -5
@@ 1,23 1,23 @@
Program
| If
| | Binary Eq
| | | Lit i
| | | Lit i []
| | | Lit 0
| | Block
| | | Expr
| | | | Assign i
| | | | Assign i []
| | | | | Lit 1
| Else
| | If
| | | Binary Eq
| | | | Lit i
| | | | Lit i []
| | | | Lit 1
| | | Block
| | | | Expr
| | | | | Assign i
| | | | | Assign i []
| | | | | | Lit 2
| | Else
| | | Block
| | | | Expr
| | | | | Assign i
| | | | | Assign i []
| | | | | | Lit 3

M kabel/tmp.kab => kabel/tmp.kab +9 -7
@@ 1,8 1,10 @@
var k = 0;
function bar(i) {
    return i+2;
function fib(x) {
    if(x == 0) {
        return 0;
    }
    if(x == 1 || x == 2) {
        return 1;
    }
    return fib(x-1) + fib(x-2);
}
function foo(i) {
    return i+1;
}
print foo(bar(k));
print fib(35);