~starkingdoms/starkingdoms

aee322a55f4e43823d50abee49b074b9eac7af50 — ghostly_zsh 1 year, 3 months ago 1e504d7
no redeclaration, also added hints to errors
M kabel/src/codegen.rs => kabel/src/codegen.rs +0 -1
@@ 262,7 262,6 @@ impl Codegen {
                        self.vm.lines.last_mut().unwrap().1 += 3;
                    }
                    
                    println!("jmp: {}", self.vm.chunk.len());
                    self.patch_jump(start_jmp_loc, 0);
                    self.visit(ast);
                    self.patch_jump(end_jmp_loc, 0);

M kabel/src/error.rs => kabel/src/error.rs +22 -4
@@ 2,16 2,18 @@
pub struct KabelError {
    pub kind: ErrorKind,
    pub message: String,
    pub hint: Option<String>,
    pub line: usize,
    pub column: usize,
    pub code: String,
}

impl KabelError {
    pub fn new(kind: ErrorKind, message: String, line: usize, column: usize, code: String) -> Self {
    pub fn new(kind: ErrorKind, message: String, hint: Option<String>, line: usize, column: usize, code: String) -> Self {
        Self {
            kind,
            message,
            hint,
            line,
            column,
            code,


@@ 32,19 34,27 @@ impl std::fmt::Display for KabelError {
            self.column + 1,
            self.code,
            caret_space
        ))
        ))?;
        if let Some(ref hint) = self.hint {
            f.write_str(&format!("\n{0}{1}", caret_space, hint))?;
        }
        Ok(())
    }
}

impl std::error::Error for KabelError {}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErrorKind {
    UnexpectedEof,
    UnexpectedCharacter,
    UnexpectedToken,
    MissingDelimiter,
    OutOfScope,
    VariableAlreadyDeclaredVariable,
    VariableAlreadyDeclaredFunction,
    FunctionAlreadyDeclaredVariable,
    FunctionAlreadyDeclaredFunction,
    IncorrectArity,
}



@@ 57,6 67,10 @@ impl std::fmt::Display for ErrorKind {
            UnexpectedToken => f.write_str("Unrecognized Token"),
            MissingDelimiter => f.write_str("Missing delimiter"),
            OutOfScope => f.write_str("Out of scope"),
            VariableAlreadyDeclaredVariable => f.write_str("Variable already declared variable"),
            VariableAlreadyDeclaredFunction => f.write_str("Variable already declared function"),
            FunctionAlreadyDeclaredVariable => f.write_str("Function already declared variable"),
            FunctionAlreadyDeclaredFunction => f.write_str("Function already declared function"),
            IncorrectArity => f.write_str("Incorrect arity"),
        }
    }


@@ 71,7 85,11 @@ impl From<ErrorKind> for usize {
            UnexpectedToken => 0x02,
            MissingDelimiter => 0x03,
            OutOfScope => 0x04,
            IncorrectArity => 0x05,
            VariableAlreadyDeclaredVariable => 0x05,
            VariableAlreadyDeclaredFunction => 0x06,
            FunctionAlreadyDeclaredVariable => 0x07,
            FunctionAlreadyDeclaredFunction => 0x08,
            IncorrectArity => 0x09,
        }
    }
}

M kabel/src/lexer.rs => kabel/src/lexer.rs +3 -0
@@ 302,6 302,7 @@ impl Lexer {
                    self.errors.push(KabelError::new(
                        ErrorKind::UnexpectedToken,
                        format!("Stray \"{0}\"", c as char),
                        None,
                        self.line,
                        self.column,
                        self.input[self.line_start..self.current].iter().collect(),


@@ 319,6 320,7 @@ impl Lexer {
            return Err(KabelError::new(
                ErrorKind::UnexpectedEof,
                message.to_string(),
                None,
                self.line,
                self.column,
                self.input[self.line_start..self.current].iter().collect(),


@@ 337,6 339,7 @@ impl Lexer {
            return Err(KabelError::new(
                ErrorKind::UnexpectedEof,
                message.to_string(),
                None,
                self.line,
                self.column,
                self.input[self.line_start..self.current].iter().collect(),

M kabel/src/macros.rs => kabel/src/macros.rs +81 -5
@@ 108,12 108,24 @@ macro_rules! push_output {
    }
}


#[macro_export]
macro_rules! unexpected_token {
    ($self:expr, $message:expr, $token:expr, $hint:expr) => {
        $crate::error::KabelError::new(
            $crate::error::ErrorKind::UnexpectedToken,
            format!($message, $self.text[$token.line][$token.start_column..$token.end_column].to_string()),
            Some($hint),
            $token.line,
            $token.start_column,
            $self.text[$token.line].to_string(),
        )
    };
    ($self:expr, $message:expr, $token:expr) => {
        $crate::error::KabelError::new(
            $crate::error::ErrorKind::UnexpectedToken,
            format!($message, $self.text[$token.line][$token.start_column..$token.end_column].to_string()),
            None,
            $token.line,
            $token.start_column,
            $self.text[$token.line].to_string(),


@@ 122,8 134,32 @@ macro_rules! unexpected_token {
}

#[macro_export]
macro_rules! ast_error {
    ($self:expr, $type:expr, $expr:expr, $message:expr $(, $message_fmt:expr )* ; $hint:expr $(, $hint_fmt:expr )* ) => {
        $crate::error::KabelError::new(
            $type,
            format!($message, $( $message_fmt ),* ),
            Some(format!($hint, $( $hint_fmt ),*)),
            $expr.start_line,
            $expr.start_column,
            $crate::collect_lines!($self.text[$expr.start_line..$expr.end_line+1]),
        )
    };
    ($self:expr, $type:expr, $expr:expr, $message:expr $(, $message_fmt:expr )*) => {
        $crate::error::KabelError::new(
            $type,
            format!($message, $( $message_fmt ),* ),
            None,
            $expr.start_line,
            $expr.start_column,
            $crate::collect_lines!($self.text[$expr.start_line..$expr.end_line+1]),
        )
    };
}

/*#[macro_export]
macro_rules! out_of_scope {
    ($self:expr, $message:expr, $name:expr, $expr:expr) => {
    ($self:expr, $message:expr, $name:expr, $expr:expr, $hint:expr) => {
        $crate::error::KabelError::new(
            $crate::error::ErrorKind::OutOfScope,
            format!($message, $name),


@@ 132,20 168,60 @@ macro_rules! out_of_scope {
            $crate::collect_lines!($self.text[$expr.start_line..$expr.end_line+1]),
        )
    };
}
}*/
#[macro_export]
macro_rules! out_of_scope_var {
    ($self:expr, $message:expr, $name:expr, $expr:expr) => {
    ($self:expr, $type:expr, $expr:expr, $message:expr, $name:expr ; $hint:expr $(, $hint_fmt:expr )* ) => {
        $crate::error::KabelError::new(
            $crate::error::ErrorKind::OutOfScope,
            $type,
            format!($message, $name.name),
            Some(format!($hint, $( $hint_fmt ),*)),
            $expr.start_line,
            $name.start_column,
            $crate::collect_lines!($self.text[$expr.start_line..$expr.end_line+1]),
        )
    };
    ($self:expr, $type:expr, $expr:expr, $message:expr, $name:expr) => {
        $crate::error::KabelError::new(
            $type,
            format!($message, $name.name),
            None,
            $expr.start_line,
            $name.start_column,
            $crate::collect_lines!($self.text[$expr.start_line..$expr.end_line+1]),
        )
    };
}

#[macro_export]
macro_rules! vm_error {
    ($self:expr, $type:expr, $message:expr $(, $message_fmt:expr )* ; $hint:expr $(, $hint_fmt:expr )* ) => {
        {
            let line = $self.find_line();
            $crate::runtime_error::KabelRuntimeError::new(
                $type,
                format!($message, $( $message_fmt ),* ),
                Some(format!(hint, $( $hint_fmt ),*)),
                line,
                $self.text[line].to_string(),
            )
        }
    };
    ($self:expr, $type:expr, $message:expr $(, $message_fmt:expr )*) => {
        {
            let line = $self.find_line();
            $crate::runtime_error::KabelRuntimeError::new(
                $type,
                format!($message, $( $message_fmt ),* ),
                None,
                line,
                $self.text[line].to_string(),
            )
        }
    };
}

/*#[macro_export]
macro_rules! mismatched_types {
    ($self:expr, $message:expr, $( $args:expr ),*) => {
        {


@@ 172,7 248,7 @@ macro_rules! wrong_type {
            )
        }
    };
}
}*/

#[macro_export]
macro_rules! collect_lines {

M kabel/src/name_resolution.rs => kabel/src/name_resolution.rs +51 -4
@@ 1,6 1,6 @@
use std::collections::HashMap;

use crate::{ast::{ASTType, AST}, ast_from_ast, collect_lines, error::{ErrorKind, KabelError}, extension::Extension, out_of_scope, out_of_scope_var};
use crate::{ast::{ASTType, AST}, ast_error, ast_from_ast, collect_lines, error::{ErrorKind, KabelError}, extension::Extension, out_of_scope_var};

pub struct Resolver {
    text: Vec<String>,


@@ 39,6 39,28 @@ impl Resolver {
                }
            }
            Function(name, args, block) => {
                if self.resolve_var(&name.name).0 {
                    self.errors.push(out_of_scope_var!(self,
                            ErrorKind::FunctionAlreadyDeclaredVariable, ast,
                            "Function \"{}\" already declared", name ;
                            "hint: has variable \"{}\" already been declared?", name.name));
                } else {
                    let resolution = self.resolve_function(&name.name, 0);
                    if resolution.is_ok() {
                        self.errors.push(out_of_scope_var!(self,
                                ErrorKind::FunctionAlreadyDeclaredFunction, ast,
                                "Function \"{}\" already declared", name ;
                                "hint: has function \"{}\" already been declared?", name.name));
                    }
                    if let Err((kind, _, _)) = resolution {
                        if kind == ErrorKind::IncorrectArity {
                            self.errors.push(out_of_scope_var!(self,
                                    ErrorKind::FunctionAlreadyDeclaredFunction, ast,
                                    "Function \"{}\" already declared", name ;
                                    "hint: has function \"{}\" already been declared?", name.name));
                        } else {}
                    }
                }
                self.symbol_table.last_mut().unwrap().insert(name.name.clone(), (Symbol::Function(args.len()),0));
                self.symbol_table.push(HashMap::new());
                for arg in args.clone() {


@@ 128,6 150,28 @@ impl Resolver {
            Decl(name, expr) => {
                let expr = self.visit(*expr);
                self.locals.push(self.scope);
                if self.resolve_var(&name.name).0 {
                    self.errors.push(out_of_scope_var!(self,
                            ErrorKind::VariableAlreadyDeclaredVariable, ast,
                            "Variable \"{}\" already declared", name ;
                            "hint: has variable \"{}\" already been declared?", name.name));
                } else {
                    let resolution = self.resolve_function(&name.name, 0);
                    if resolution.is_ok() {
                        self.errors.push(out_of_scope_var!(self,
                                ErrorKind::VariableAlreadyDeclaredFunction, ast,
                                "Variable \"{}\" already declared", name ;
                                "hint: has function \"{}\" already been declared?", name.name));
                    }
                    if let Err((kind, _, _)) = resolution {
                        if kind == ErrorKind::IncorrectArity {
                            self.errors.push(out_of_scope_var!(self,
                                    ErrorKind::VariableAlreadyDeclaredFunction, ast,
                                    "Variable \"{}\" already declared", name ;
                                    "hint: has function \"{}\" already been declared?", name.name));
                        } else {}
                    }
                }
                self.symbol_table.last_mut().unwrap().insert(name.name.clone(), (Symbol::Var, self.locals.len()-1));
                AST {
                    ast_type: Decl(name, Box::new(expr)),


@@ 151,7 195,9 @@ impl Resolver {
                let expr = self.visit(*expr);
                let resolution = self.resolve_var(&name.name);
                if !resolution.0 {
                    self.errors.push(out_of_scope_var!(self, "Variable \"{}\" not in scope", name, ast));
                    self.errors.push(out_of_scope_var!(self,
                            ErrorKind::OutOfScope, ast,
                            "Variable \"{}\" not in scope", name));
                }
                AST {
                    ast_type: Assign(name, Box::new(expr)),


@@ 188,7 234,7 @@ impl Resolver {
                    crate::ast::Lit::Ident(ref name) => {
                        let resolution = self.resolve_var(name);
                        if !resolution.0 {
                            self.errors.push(out_of_scope!(self, "Variable \"{}\" not in scope", name, ast))
                            self.errors.push(ast_error!(self, ErrorKind::OutOfScope, ast, "Variable \"{}\" not in scope", name))
                        } else {
                            return AST {
                                ast_type: Lit(lit),


@@ 207,12 253,13 @@ impl Resolver {
            Call(ident, args) => {
                if let Err(e) = self.resolve_function(&ident.name, args.len()) {
                    match e {
                        (ErrorKind::OutOfScope, _, _) => self.errors.push(out_of_scope!(self, "Function \"{}\" not in scope", ident.name, ast)),
                        (ErrorKind::OutOfScope, _, _) => self.errors.push(ast_error!(self, ErrorKind::OutOfScope, ast, "Function \"{}\" not in scope", ident.name)),
                        (ErrorKind::IncorrectArity, Some(f_arity), Some(arity)) => {
                            self.errors.push(
                                KabelError::new(
                                    ErrorKind::IncorrectArity,
                                    format!("Function {} has {} argument, provided {}", ident.name, f_arity, arity),
                                    None,
                                    ast.start_line,
                                    ast.start_column,
                                    collect_lines!(self.text[ast.start_line-1..ast.end_line-1]),

M kabel/src/parser.rs => kabel/src/parser.rs +5 -0
@@ 372,6 372,7 @@ impl Parser {
            return Err(KabelError::new(
                ErrorKind::UnexpectedEof,
                "Unexpected end of file, expected ;".to_string(),
                None,
                expression.start_line,
                last.end_column,
                self.text[last.line].to_string(),


@@ 1034,6 1035,7 @@ impl Parser {
                return Err(KabelError::new(
                    ErrorKind::MissingDelimiter,
                    "Missing right parenthesis".to_string(),
                    None,
                    right_paren.line,
                    right_paren.start_column,
                    self.text[left_paren.line..right_paren.line].iter().fold("".to_string(), |acc, string| acc + string + "\n"),


@@ 1050,6 1052,7 @@ impl Parser {
            return Err(KabelError::new(
                ErrorKind::MissingDelimiter,
                "Missing right parenthesis".to_string(),
                None,
                e.line,
                e.column,
                collect_lines!(self.text[left_paren.line..expr.end_line]),


@@ 1064,6 1067,7 @@ impl Parser {
            return Err(KabelError::new(
                ErrorKind::UnexpectedEof,
                "Unexpected end of file".to_string(),
                None,
                last_token.line,
                last_token.start_column,
                self.text[last_token.line].clone(),


@@ 1079,6 1083,7 @@ impl Parser {
            return Err(KabelError::new(
                ErrorKind::UnexpectedEof,
                "Unexpected end of file".to_string(),
                None,
                last_token.line,
                last_token.start_column,
                self.text[last_token.line].clone(),

M kabel/src/runtime_error.rs => kabel/src/runtime_error.rs +8 -2
@@ 2,15 2,17 @@
pub struct KabelRuntimeError {
    pub kind: RuntimeErrorKind,
    pub message: String,
    pub hint: Option<String>,
    pub line: usize,
    pub code: String,
}

impl KabelRuntimeError {
    pub fn new(kind: RuntimeErrorKind, message: String, line: usize, code: String) -> Self {
    pub fn new(kind: RuntimeErrorKind, message: String, hint: Option<String>, line: usize, code: String) -> Self {
        Self {
            kind,
            message,
            hint,
            line,
            code,
        }


@@ 26,7 28,11 @@ impl std::fmt::Display for KabelRuntimeError {
            self.message,
            self.line + 1,
            self.code,
        ))
        ))?;
        if let Some(ref hint) = self.hint {
            f.write_str(&format!("\n{0}", hint))?;
        }
        Ok(())
    }
}


M kabel/src/vm.rs => kabel/src/vm.rs +43 -42
@@ 1,6 1,7 @@
use std::collections::HashMap;

use crate::{mismatched_types, opcodes::OpCode, runtime_error::KabelRuntimeError, vm_boolean_binary, wrong_type};
use crate::{opcodes::OpCode, runtime_error::{KabelRuntimeError, RuntimeErrorKind}, vm, vm_boolean_binary};
use crate::vm_error;

#[derive(Clone)]
pub struct VM {


@@ 50,10 51,10 @@ impl VM {
                            self.stack.push(Str(v1 + &v2));
                        },
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(wrong_type!(self, "Cannot add booleans"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot add booleans"))
                        }
                        (v1, v2) => {
                            return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                        }
                    }
                }


@@ 61,13 62,13 @@ impl VM {
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.stack.push(Num(v1 - v2)),
                        (Str(_v1), Str(_v2)) => {
                            return Err(wrong_type!(self, "Cannot subtract strings"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot subtract strings"))
                        },
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(wrong_type!(self, "Cannot subtract booleans"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot subtract booleans"))
                        }
                        (v1, v2) => {
                            return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                        }
                    }
                }


@@ 78,17 79,17 @@ impl VM {
                            if v2.fract() == 0.0 {
                                self.stack.push(Str(v1.repeat(v2 as usize)));
                            } else {
                                return Err(wrong_type!(self, "Number must be an integer"))
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Number must be an integer"))
                            }
                        }
                        (Str(_v1), Str(_v2)) => {
                            return Err(wrong_type!(self, "Cannot multiply strings"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot multiply strings"))
                        },
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(wrong_type!(self, "Cannot multiply booleans"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot multiply booleans"))
                        }
                        (v1, v2) => {
                            return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                        }
                    }
                }


@@ 96,13 97,13 @@ impl VM {
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.stack.push(Num(v1 / v2)),
                        (Str(_v1), Str(_v2)) => {
                            return Err(wrong_type!(self, "Cannot divide strings"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot divide strings"))
                        },
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(wrong_type!(self, "Cannot divide booleans"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot divide booleans"))
                        }
                        (v1, v2) => {
                            return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                        }
                    }
                }


@@ 110,13 111,13 @@ impl VM {
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.stack.push(Num(v1 % v2)),
                        (Str(_v1), Str(_v2)) => {
                            return Err(wrong_type!(self, "Cannot perform modulus on strings"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform modulus on strings"))
                        },
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(wrong_type!(self, "Cannot perform modulus on booleans"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform modulus on booleans"))
                        }
                        (v1, v2) => {
                            return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                        }
                    }
                }


@@ 124,21 125,21 @@ impl VM {
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => {
                            if v1.fract() != 0.0 {
                                return Err(wrong_type!(self, "Cannot perform bitwise AND on {}", v1))
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise AND on {}", v1))
                            }
                            if v2.fract() != 0.0 {
                                return Err(wrong_type!(self, "Cannot perform bitwise AND on {}", v2))
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise AND on {}", v2))
                            }
                            self.stack.push(Num((v1 as u32 & v2 as u32) as f32))
                        }
                        (Str(_v1), Str(_v2)) => {
                            return Err(wrong_type!(self, "Cannot perform bitwise AND on strings"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise AND on strings"))
                        },
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(wrong_type!(self, "Cannot perform bitwise AND on booleans"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise AND on booleans"))
                        }
                        (v1, v2) => {
                            return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                        }
                    }
                }


@@ 146,21 147,21 @@ impl VM {
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => {
                            if v1.fract() != 0.0 {
                                return Err(wrong_type!(self, "Cannot perform bitwise XOR on {}", v1))
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise XOR on {}", v1))
                            }
                            if v2.fract() != 0.0 {
                                return Err(wrong_type!(self, "Cannot perform bitwise XOR on {}", v2))
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise XOR on {}", v2))
                            }
                            self.stack.push(Num((v1 as u32 ^ v2 as u32) as f32))
                        }
                        (Str(_v1), Str(_v2)) => {
                            return Err(wrong_type!(self, "Cannot perform bitwise XOR on strings"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise XOR on strings"))
                        },
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(wrong_type!(self, "Cannot perform bitwise XOR on booleans"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise XOR on booleans"))
                        }
                        (v1, v2) => {
                            return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                        }
                    }
                }


@@ 168,21 169,21 @@ impl VM {
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => {
                            if v1.fract() != 0.0 {
                                return Err(wrong_type!(self, "Cannot perform bitwise OR on {}", v1))
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise OR on {}", v1))
                            }
                            if v2.fract() != 0.0 {
                                return Err(wrong_type!(self, "Cannot perform bitwise OR on {}", v2))
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise OR on {}", v2))
                            }
                            self.stack.push(Num((v1 as u32 | v2 as u32) as f32))
                        }
                        (Str(_v1), Str(_v2)) => {
                            return Err(wrong_type!(self, "Cannot perform bitwise OR on strings"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise OR on strings"))
                        },
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(wrong_type!(self, "Cannot perform bitwise OR on booleans"))
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise OR on booleans"))
                        }
                        (v1, v2) => {
                            return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                        }
                    }
                }


@@ 201,36 202,36 @@ impl VM {
                // OR
                0x11 => match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                    (Num(_v1), Num(_v2)) => {
                        return Err(wrong_type!(self, "Cannot perform logical OR on numbers"))
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform logical OR on numbers"))
                    }
                    (Str(_v1), Str(_v2)) => {
                        return Err(wrong_type!(self, "Cannot perform logical OR on strings"))
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform logical OR on strings"))
                    },
                    (Bool(v1), Bool(v2)) => self.stack.push(Bool(v1 || v2)),
                    (v1, v2) => {
                        return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                        return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                    }
                }
                // AND
                0x12 => match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                    (Num(_v1), Num(_v2)) => {
                        return Err(wrong_type!(self, "Cannot perform logical AND on numbers"))
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform logical AND on numbers"))
                    }
                    (Str(_v1), Str(_v2)) => {
                        return Err(wrong_type!(self, "Cannot perform logical AND on strings"))
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform logical AND on strings"))
                    },
                    (Bool(v1), Bool(v2)) => self.stack.push(Bool(v1 && v2)),
                    (v1, v2) => {
                        return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                        return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                    }
                }
                // NOT
                0x13 => match self.stack.pop().unwrap() {
                    Num(_v1) => {
                        return Err(wrong_type!(self, "Cannot perform logical NOT on numbers"))
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform logical NOT on numbers"))
                    }
                    Str(_v1) => {
                        return Err(wrong_type!(self, "Cannot perform logical NOT on strings"))
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform logical NOT on strings"))
                    }
                    Bool(v1) => self.stack.push(Bool(!v1)),
                }


@@ 238,10 239,10 @@ impl VM {
                0x14 => match self.stack.pop().unwrap() {
                    Num(v1) => self.stack.push(Num(-v1)),
                    Str(_v1) => {
                        return Err(wrong_type!(self, "Cannot negate strings"))
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot negate strings"))
                    }
                    Bool(_v1) => {
                        return Err(wrong_type!(self, "Cannot negate bools"))
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot negate bools"))
                    }
                }
                // JMP


@@ 265,7 266,7 @@ impl VM {
                            self.read_u16();
                        }
                    } else {
                        return Err(wrong_type!(self, "if must have condition of type boolean"))
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "if must have condition of type boolean"))
                    }
                }


A kabel/test/runtime/fibonacci.kab => kabel/test/runtime/fibonacci.kab +11 -0
@@ 0,0 1,11 @@
var iters = 30;

var x = 0;
var y = 1;
var z = 0;
for(var i = 0; i < iters; i++) {
    print x;
    z = (x + y);
    x = y;
    y = z;
}

A kabel/test/runtime/fibonacci.out => kabel/test/runtime/fibonacci.out +30 -0
@@ 0,0 1,30 @@
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229

M kabel/tmp.kab => kabel/tmp.kab +2 -8
@@ 1,8 1,2 @@
for (var i = 1; i < 101; i++) {
  //if (i % 3 != 0 && i % 5 != 0) { print i; }
  //else if (i % 3 == 0 && i % 5 != 0) { print "Fizz"; }
  //else if (i % 3 != 0 && i % 5 == 0) { print "Buzz"; }
  //else if (i % 3 == 0 && i % 5 == 0) { print "FizzBuzz"; }
  print i;
  break;
}
var i = 0;
function i() {}