~starkingdoms/starkingdoms

87db7538d05166cb8cdb701886b38b32de9bc708 — core 8 months ago 016f6ce
cargo fmt
M crates/client/src/components.rs => crates/client/src/components.rs +1 -1
@@ 16,7 16,7 @@ pub struct Transform {
#[derive(Resource, Debug)]
pub struct PlayerResources {
    pub fuel_amount: u32,
    pub fuel_max: u32
    pub fuel_max: u32,
}

impl Transform {

M crates/client/src/lib.rs => crates/client/src/lib.rs +4 -5
@@ 1,13 1,13 @@
use crate::components::PlayerResources;
use bevy_ecs::{event::Events, world::World};
use components::{Camera, Chat, Part, Player, RecvPacket, SendPacket, Texture, Transform};
use nalgebra::{Rotation2, Scale3, Translation3};
use networking::ws::Ws;
use rendering::App;
use platform::assets::Assets;
use rendering::assets::AssetLoader;
use rendering::App;
use tracing::info;
use winit::event_loop::{ControlFlow, EventLoop};
use crate::components::PlayerResources;
use rendering::assets::AssetLoader;

#[cfg(target_arch = "wasm32")]
#[path = "wasm/mod.rs"]


@@ 51,8 51,7 @@ pub fn start() {
        fuel_max: 0,
    });
    world.insert_resource(Chat {
        messages: vec![
        ],
        messages: vec![],
        textbox: String::new(),
    });


M crates/client/src/networking/mod.rs => crates/client/src/networking/mod.rs +7 -6
@@ 9,7 9,10 @@ use bevy_ecs::{
use nalgebra::{Rotation2, Scale3, Translation3};
use starkingdoms_common::{packet::Packet, PartType, PlanetType};

use crate::components::{Camera, Chat, Menu, Part, Player, PlayerResources, RecvPacket, SendPacket, ServerId, SpriteBundle, Texture, Transform};
use crate::components::{
    Camera, Chat, Menu, Part, Player, PlayerResources, RecvPacket, SendPacket, ServerId,
    SpriteBundle, Texture, Transform,
};

#[cfg(target_arch = "wasm32")]
#[path = "ws_wasm.rs"]


@@ 194,10 197,7 @@ pub fn process_packets(
                    }
                }
            }
            EnergyUpdate {
                amount,
                max,
            } => {
            EnergyUpdate { amount, max } => {
                let mut r = world.resource_mut::<PlayerResources>();
                r.fuel_amount = *amount;
                r.fuel_max = *max;


@@ 216,7 216,8 @@ pub fn process_packets(
            }
            Message { actor, content, .. } => {
                let mut chat = world.get_resource_mut::<Chat>().unwrap();
                chat.messages.push(format!("{}: {}", actor.clone(), content.clone()));
                chat.messages
                    .push(format!("{}: {}", actor.clone(), content.clone()));
            }
            PlayerLeave { id } => {
                let mut part_query = world.query_filtered::<(Entity, &ServerId), With<Part>>();

M crates/client/src/rendering/assets.rs => crates/client/src/rendering/assets.rs +1 -1
@@ 26,4 26,4 @@ pub struct ImgData {
pub trait AssetLoader {
    fn new() -> Self;
    fn get(&self, local_path: impl Into<String>) -> Option<ImgData>;
}
\ No newline at end of file
}

M crates/client/src/rendering/mod.rs => crates/client/src/rendering/mod.rs +35 -21
@@ 33,12 33,8 @@ use winit::keyboard::{KeyCode, PhysicalKey};
#[cfg(target_arch = "wasm32")]
use winit::platform::web::{WindowAttributesExtWebSys, WindowExtWebSys};
use winit::{
    application::ApplicationHandler,
    dpi::LogicalSize,
    event::WindowEvent,
    event_loop::ActiveEventLoop,
    raw_window_handle::HasWindowHandle,
    window::{Window},
    application::ApplicationHandler, dpi::LogicalSize, event::WindowEvent,
    event_loop::ActiveEventLoop, raw_window_handle::HasWindowHandle, window::Window,
};

use crate::components::{Camera, Menu, Player, RecvPacket, SendPacket, Texture, Transform};


@@ 555,33 551,51 @@ impl ApplicationHandler for App {
                let assets = self.world.resource::<crate::platform::assets::Assets>();
                match assets.get("starfield.svg") {
                    Some(image) => {
                        let texture_object = gl.create_texture().expect("Failed to create texture object");
                        let texture_object = gl
                            .create_texture()
                            .expect("Failed to create texture object");
                        gl.bind_texture(glow::TEXTURE_2D, Some(texture_object));
                        gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::LINEAR_MIPMAP_LINEAR as i32);
                        gl.tex_image_2d(glow::TEXTURE_2D, 0, glow::RGBA as i32,
                            image.width as i32, image.height as i32, 0, glow::RGBA,
                            glow::UNSIGNED_BYTE, PixelUnpackData::Slice(Some(&image.bytes)));
                        gl.tex_parameter_i32(
                            glow::TEXTURE_2D,
                            glow::TEXTURE_MIN_FILTER,
                            glow::LINEAR_MIPMAP_LINEAR as i32,
                        );
                        gl.tex_image_2d(
                            glow::TEXTURE_2D,
                            0,
                            glow::RGBA as i32,
                            image.width as i32,
                            image.height as i32,
                            0,
                            glow::RGBA,
                            glow::UNSIGNED_BYTE,
                            PixelUnpackData::Slice(Some(&image.bytes)),
                        );
                        gl.generate_mipmap(glow::TEXTURE_2D);

                        self.textures.insert("starfield.svg".to_string(), texture_object);
                        self.textures
                            .insert("starfield.svg".to_string(), texture_object);
                    }
                    None => {}
                }
            }
            if self.textures.contains_key("starfield.svg") {
                gl.bind_texture(glow::TEXTURE_2D, self.textures.get("starfield.svg").copied());
                gl.bind_texture(
                    glow::TEXTURE_2D,
                    self.textures.get("starfield.svg").copied(),
                );

                let camera = self.world.get_resource::<Camera>().unwrap();
                let x = -(camera.x + camera.x.signum() * 200.0)
                    + camera.x % 400.0;
                let y = -(camera.y + camera.y.signum() * 200.0)
                    + camera.y % 400.0;
                let x = -(camera.x + camera.x.signum() * 200.0) + camera.x % 400.0;
                let y = -(camera.y + camera.y.signum() * 200.0) + camera.y % 400.0;
                let x_range = camera.width as f32 / camera.zoom / 400.0;
                let y_range = camera.height as f32 / camera.zoom / 400.0;
                for i in ((-x_range/2.0) as i32 - 1)..=((x_range/2.0) as i32 + 1) {
                    for j in ((-y_range/2.0) as i32 - 1)..=((y_range/2.0) as i32 + 1) {
                        let model = Translation3::new(x + (i*400) as f32, y + (j*400) as f32, 0.0).to_homogeneous()
                            * Scale3::new(200.0, 200.0, 1.0).to_homogeneous();
                for i in ((-x_range / 2.0) as i32 - 1)..=((x_range / 2.0) as i32 + 1) {
                    for j in ((-y_range / 2.0) as i32 - 1)..=((y_range / 2.0) as i32 + 1) {
                        let model =
                            Translation3::new(x + (i * 400) as f32, y + (j * 400) as f32, 0.0)
                                .to_homogeneous()
                                * Scale3::new(200.0, 200.0, 1.0).to_homogeneous();
                        gl.uniform_matrix_4_f32_slice(model_loc.as_ref(), false, model.as_slice());
                        gl.draw_elements(glow::TRIANGLES, 6, glow::UNSIGNED_INT, 0);
                    }

M crates/client/src/ui/colors.rs => crates/client/src/ui/colors.rs +1 -1
@@ 30,4 30,4 @@ color!(SURFACE_1, rgb(69, 71, 90));
color!(SURFACE_0, rgb(49, 50, 68));
color!(BASE, rgb(30, 30, 46));
color!(MANTLE, rgb(24, 24, 37));
color!(CRUST, rgb(17, 17, 27));
\ No newline at end of file
color!(CRUST, rgb(17, 17, 27));

M crates/client/src/ui/mod.rs => crates/client/src/ui/mod.rs +37 -21
@@ 3,14 3,14 @@ mod widgets;

use std::f32;

use crate::components::{Camera, Chat, Menu, Player, PlayerResources, SendPacket, Transform};
use crate::ui::widgets::{progress_bar, RichTextExt};
use bevy_ecs::entity::Entity;
use bevy_ecs::event::Events;
use bevy_ecs::prelude::With;
use bevy_ecs::world::World;
use egui::{Align, Align2, CursorIcon, Layout, Margin, Order, RichText, Shadow, Visuals};
use starkingdoms_common::packet::Packet;
use crate::components::{Camera, Chat, Menu, Player, PlayerResources, SendPacket, Transform};
use crate::ui::widgets::{progress_bar, RichTextExt};

pub fn init_ui(ctx: egui::Context) {
    // set colors


@@ 19,7 19,6 @@ pub fn init_ui(ctx: egui::Context) {
    let mut visuals = Visuals::default();
    visuals.dark_mode = true;


    // TODO(core): code_bg_colors

    visuals.warn_fg_color = colors::YELLOW; // used for warning text


@@ 43,17 42,18 @@ pub fn init_ui(ctx: egui::Context) {

    visuals.hyperlink_color = colors::BLUE;



    visuals.widgets.noninteractive.fg_stroke.color = colors::TEXT; // standard text color


    style.visuals = visuals;

    ctx.set_style(style);
}

pub fn draw_ui(ctx: &egui::Context, world: &mut World, send_packet_events: &mut Events<SendPacket>) {
pub fn draw_ui(
    ctx: &egui::Context,
    world: &mut World,
    send_packet_events: &mut Events<SendPacket>,
) {
    draw_status_bar(ctx, world);
    draw_chat(ctx, world, send_packet_events);
    draw_crafting(ctx, world);


@@ 71,11 71,14 @@ pub fn draw_status_bar(ctx: &egui::Context, world: &mut World) {
        .order(Order::Foreground)
        .anchor(Align2::CENTER_BOTTOM, [0.0, -5.0])
        .show(ctx, |ui| {

            ui.horizontal(|ui| {
                ui.vertical(|ui| {
                    ui.label(RichText::new("Position:").stk_weak());
                    ui.label(RichText::new(format!("{:.0}, {:.0}", player_position.translation.x / 10.0, player_position.translation.y / 10.0)))
                    ui.label(RichText::new(format!(
                        "{:.0}, {:.0}",
                        player_position.translation.x / 10.0,
                        player_position.translation.y / 10.0
                    )))
                });

                ui.add_space(8.0);


@@ 84,20 87,30 @@ pub fn draw_status_bar(ctx: &egui::Context, world: &mut World) {
                    ui.horizontal(|ui| {
                        ui.label(RichText::new("Fuel:").stk_weak());
                        ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
                            ui.label(RichText::new(format!("{}/{} ({:.2}%)", player_resources.fuel_amount, player_resources.fuel_max, player_resources.fuel_amount as f32 / player_resources.fuel_max as f32 * 100.0)))
                            ui.label(RichText::new(format!(
                                "{}/{} ({:.2}%)",
                                player_resources.fuel_amount,
                                player_resources.fuel_max,
                                player_resources.fuel_amount as f32
                                    / player_resources.fuel_max as f32
                                    * 100.0
                            )))
                        });

                    });

                    ui.add(
                        progress_bar(player_resources.fuel_amount as f32 / player_resources.fuel_max as f32)
                    );
                    ui.add(progress_bar(
                        player_resources.fuel_amount as f32 / player_resources.fuel_max as f32,
                    ));
                })
            });
        });
}

pub fn draw_chat(ctx: &egui::Context, world: &mut World, send_packet_events: &mut Events<SendPacket>) {
pub fn draw_chat(
    ctx: &egui::Context,
    world: &mut World,
    send_packet_events: &mut Events<SendPacket>,
) {
    let mut chat = world.get_resource_mut::<Chat>().unwrap();

    egui::Window::new("Chat")


@@ 122,8 135,10 @@ pub fn draw_chat(ctx: &egui::Context, world: &mut World, send_packet_events: &mu
                });
            ui.horizontal(|ui| {
                let output = egui::TextEdit::singleline(&mut chat.textbox).show(ui);
                if ui.button("Send").clicked() ||
                    (ctx.input(|i| i.key_pressed(egui::Key::Enter)) && output.response.lost_focus()) {
                if ui.button("Send").clicked()
                    || (ctx.input(|i| i.key_pressed(egui::Key::Enter))
                        && output.response.lost_focus())
                {
                    send_packet_events.send(SendPacket(Packet::SendMessage {
                        target: None,
                        content: chat.textbox.clone(),


@@ 141,9 156,10 @@ pub fn draw_crafting(ctx: &egui::Context, world: &mut World) {
        egui::Window::new("Crafting")
            .id(format!("Crafting{}", entity.index()).into())
            .pivot(Align2::LEFT_BOTTOM)
            .fixed_pos(((menu.translation.x + camera.x) * camera.zoom + camera.width as f32 / 2.0,
                (menu.translation.y + camera.y) * camera.zoom + camera.height as f32 / 2.0))
            .show(ctx, |ui| {
            });
            .fixed_pos((
                (menu.translation.x + camera.x) * camera.zoom + camera.width as f32 / 2.0,
                (menu.translation.y + camera.y) * camera.zoom + camera.height as f32 / 2.0,
            ))
            .show(ctx, |ui| {});
    }
}

M crates/client/src/ui/widgets.rs => crates/client/src/ui/widgets.rs +1 -1
@@ 13,4 13,4 @@ impl RichTextExt for RichText {
    fn stk_weak(self) -> Self {
        self.color(super::colors::OVERLAY_2)
    }
}
\ No newline at end of file
}

M crates/client/src/wasm/assets.rs => crates/client/src/wasm/assets.rs +1 -1
@@ 4,11 4,11 @@ use std::{
    sync::{Arc, Mutex},
};

use crate::rendering::assets::{AssetLoader, ImgData};
use bevy_ecs::system::Resource;
use image::EncodableLayout;
use poll_promise::Promise;
use resvg::{tiny_skia, usvg};
use crate::rendering::assets::{AssetLoader, ImgData};

#[derive(Resource)]
pub struct Assets {

M crates/kabel/src/ast.rs => crates/kabel/src/ast.rs +7 -8
@@ 11,9 11,9 @@ pub enum ASTType {

    // statements
    Function(Name, Vec<Name>, Box<AST>), // name, args, block
    Return(Box<Option<AST>>),               // expression
    Loop(Box<AST>),                         // block
    While(Box<AST>, Box<AST>),              // condition, block
    Return(Box<Option<AST>>),            // expression
    Loop(Box<AST>),                      // block
    While(Box<AST>, Box<AST>),           // condition, block
    For(
        Box<Option<AST>>,
        Box<Option<AST>>,


@@ 22,10 22,10 @@ pub enum ASTType {
    ), // expr1, expr2, expr3, block
    Break,
    Continue,
    If(Box<AST>, Box<AST>, Box<Option<AST>>),   // condition, block, else/else if
    Block(Vec<AST>),                            // statements
    Decl(Name, Box<AST>),                       // identifier, expression
    Expr(Box<AST>),                             // expr
    If(Box<AST>, Box<AST>, Box<Option<AST>>), // condition, block, else/else if
    Block(Vec<AST>),                          // statements
    Decl(Name, Box<AST>),                     // identifier, expression
    Expr(Box<AST>),                           // expr
    // REMOVE LATER
    Print(Box<AST>),



@@ 93,4 93,3 @@ pub enum LhsAssignType {
    Ident(String),
    Subscript(Box<AST>, Box<AST>),
}


M crates/kabel/src/codegen.rs => crates/kabel/src/codegen.rs +356 -140
@@ 1,4 1,10 @@
use crate::{ast::{ASTType, BinOp, LhsAssign, LhsAssignType, Lit, Name, UnOp, AST}, codegen_binary, codegen_unary, extension::Extension, opcodes::OpCode, vm::{Function, Unit, Value, VM}};
use crate::{
    ast::{ASTType, BinOp, LhsAssign, LhsAssignType, Lit, Name, UnOp, AST},
    codegen_binary, codegen_unary,
    extension::Extension,
    opcodes::OpCode,
    vm::{Function, Unit, Value, VM},
};

pub struct Codegen {
    pub vm: VM,


@@ 10,8 16,7 @@ pub struct Codegen {
impl Codegen {
    pub fn new(text: String) -> Self {
        Codegen {
            vm: VM::new(Vec::new(), Vec::new(),
                Vec::new(), text),
            vm: VM::new(Vec::new(), Vec::new(), Vec::new(), text),
            scopes: vec![(ScopeType::Main, 0)],
            break_stack: Vec::new(),
            continue_stack: Vec::new(),


@@ 88,24 93,32 @@ impl Codegen {
        self.vm.units.push(Unit::new_empty());

        // add function to constant pool and add a load expression in its place
        let new_unit_ptr = self.vm.units.len()-1;
        let new_unit_ptr = self.vm.units.len() - 1;
        self.vm.variables.push(Value::Fun(Function {
            unit_ptr: new_unit_ptr,
            arity: args.len(),
        }));
        self.scopes.last_mut().expect("codegen scopes vec was empty").1 += 1;
        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();
        
        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.kind {
            self.visit_block(&block, stmts.clone(), ScopeType::Function);
        }


@@ 113,12 126,18 @@ impl Codegen {
            Some(instr) => {
                if *instr != <OpCode as Into<u8>>::into(OpCode::RET) {
                    self.vm.units[self.vm.unit_ptr].pool.push(Value::Null);
                    self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into());
                    let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8;
                    self.vm.units[self.vm.unit_ptr]
                        .code
                        .push(OpCode::LOAD.into());
                    let loc = (self.vm.units[self.vm.unit_ptr].pool.len() - 1) as u8;
                    self.vm.units[self.vm.unit_ptr].code.push(loc);
                    self.vm.units[self.vm.unit_ptr].code.push(OpCode::RET.into());
                    self.vm.units[self.vm.unit_ptr]
                        .code
                        .push(OpCode::RET.into());
                    if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line {
                        self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 3));
                        self.vm.units[self.vm.unit_ptr]
                            .lines
                            .push((block.end_line, 3));
                    } else {
                        self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3;
                    }


@@ 126,12 145,18 @@ impl Codegen {
            }
            None => {
                self.vm.units[self.vm.unit_ptr].pool.push(Value::Null);
                self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into());
                let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8;
                self.vm.units[self.vm.unit_ptr]
                    .code
                    .push(OpCode::LOAD.into());
                let loc = (self.vm.units[self.vm.unit_ptr].pool.len() - 1) as u8;
                self.vm.units[self.vm.unit_ptr].code.push(loc);
                self.vm.units[self.vm.unit_ptr].code.push(OpCode::RET.into());
                self.vm.units[self.vm.unit_ptr]
                    .code
                    .push(OpCode::RET.into());
                if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line {
                    self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 3));
                    self.vm.units[self.vm.unit_ptr]
                        .lines
                        .push((block.end_line, 3));
                } else {
                    self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3;
                }


@@ 145,18 170,26 @@ impl Codegen {
            self.visit(expr);
        } else {
            self.vm.units[self.vm.unit_ptr].pool.push(Value::Null);
            self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into());
            let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8;
            self.vm.units[self.vm.unit_ptr]
                .code
                .push(OpCode::LOAD.into());
            let loc = (self.vm.units[self.vm.unit_ptr].pool.len() - 1) as u8;
            self.vm.units[self.vm.unit_ptr].code.push(loc);
            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));
                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;
            }
        }
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::RET.into());
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::RET.into());
        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, 1));
            self.vm.units[self.vm.unit_ptr]
                .lines
                .push((ast.end_line, 1));
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 1;
        }


@@ 167,25 200,37 @@ impl Codegen {

        let end_jmp = self.vm.units[self.vm.unit_ptr].code.len();
        self.visit(condition.clone());
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::JNE.into());
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::JNE.into());
        self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder
        self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder
        let start_jmp_loc = self.vm.units[self.vm.unit_ptr].code.len()-2;
        let start_jmp_loc = self.vm.units[self.vm.unit_ptr].code.len() - 2;
        if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != condition.end_line {
            self.vm.units[self.vm.unit_ptr].lines.push((condition.end_line, 3));
            self.vm.units[self.vm.unit_ptr]
                .lines
                .push((condition.end_line, 3));
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3;
        }
        if let ASTType::Block(ref stmts) = block.kind {
            self.visit_block(&block, stmts.clone(), ScopeType::Loop);
        }
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::JMP_UP.into());
        let current = self.vm.units[self.vm.unit_ptr].code.len()+2;
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::JMP_UP.into());
        let current = self.vm.units[self.vm.unit_ptr].code.len() + 2;
        let current_to_start = current - end_jmp;
        self.vm.units[self.vm.unit_ptr].code.push(((current_to_start >> 8) & 0xFF) as u8);
        self.vm.units[self.vm.unit_ptr].code.push((current_to_start & 0xFF) as u8);
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(((current_to_start >> 8) & 0xFF) as u8);
        self.vm.units[self.vm.unit_ptr]
            .code
            .push((current_to_start & 0xFF) as u8);
        if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line {
            self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 3));
            self.vm.units[self.vm.unit_ptr]
                .lines
                .push((block.end_line, 3));
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3;
        }


@@ 195,15 240,23 @@ impl Codegen {
        for loc in breaks {
            self.patch_jump(loc, 0);
        }
        let continues = self.continue_stack.pop().expect("continue stack empty on pop");
        let continues = self
            .continue_stack
            .pop()
            .expect("continue stack empty on pop");
        for loc in continues {
            let jump = loc - end_jmp + 2;
            self.vm.units[self.vm.unit_ptr].code[loc] = ((jump >> 8) & 0xFF) as u8;
            self.vm.units[self.vm.unit_ptr].code[loc + 1] = (jump & 0xFF) as u8;
        }
    }
    pub fn visit_for(&mut self, expr1: Option<AST>, expr2: Option<AST>,
                        expr3: Option<AST>, block: AST) {
    pub fn visit_for(
        &mut self,
        expr1: Option<AST>,
        expr2: Option<AST>,
        expr3: Option<AST>,
        block: AST,
    ) {
        self.scopes.push((ScopeType::Other, 0));

        self.break_stack.push(Vec::new());


@@ 216,12 269,16 @@ impl Codegen {
        let mut start_jmp_loc = None;
        if let Some(expr2) = expr2 {
            self.visit(expr2.clone());
            self.vm.units[self.vm.unit_ptr].code.push(OpCode::JNE.into());
            self.vm.units[self.vm.unit_ptr]
                .code
                .push(OpCode::JNE.into());
            self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder
            self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder
            start_jmp_loc = Some(self.vm.units[self.vm.unit_ptr].code.len()-2);
            start_jmp_loc = Some(self.vm.units[self.vm.unit_ptr].code.len() - 2);
            if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != expr2.end_line {
                self.vm.units[self.vm.unit_ptr].lines.push((expr2.end_line, 3));
                self.vm.units[self.vm.unit_ptr]
                    .lines
                    .push((expr2.end_line, 3));
            } else {
                self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3;
            }


@@ 229,7 286,10 @@ impl Codegen {
        if let ASTType::Block(ref stmts) = block.kind {
            self.visit_block(&block, stmts.clone(), ScopeType::Loop);
        }
        let continues = self.continue_stack.pop().expect("continue stack empty on pop");
        let continues = self
            .continue_stack
            .pop()
            .expect("continue stack empty on pop");
        for loc in continues {
            /*let jump = loc - end_jmp + 2;
            self.vm.units[self.vm.unit_ptr].code[loc] = ((jump >> 8) & 0xFF) as u8;


@@ 238,23 298,35 @@ impl Codegen {
        }
        if let Some(expr3) = expr3 {
            self.visit(expr3);
            self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into());
            self.vm.units[self.vm.unit_ptr]
                .code
                .push(OpCode::POP.into());
            self.vm.units[self.vm.unit_ptr].code.push(0x01);
            if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line {
                self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 2));
                self.vm.units[self.vm.unit_ptr]
                    .lines
                    .push((block.end_line, 2));
            } else {
                self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2;
            }
        }

        // write instruction to loop
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::JMP_UP.into());
        let current = self.vm.units[self.vm.unit_ptr].code.len()+2;
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::JMP_UP.into());
        let current = self.vm.units[self.vm.unit_ptr].code.len() + 2;
        let current_to_start = current - end_jmp;
        self.vm.units[self.vm.unit_ptr].code.push(((current_to_start >> 8) & 0xFF) as u8);
        self.vm.units[self.vm.unit_ptr].code.push((current_to_start & 0xFF) as u8);
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(((current_to_start >> 8) & 0xFF) as u8);
        self.vm.units[self.vm.unit_ptr]
            .code
            .push((current_to_start & 0xFF) as u8);
        if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line {
            self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 3));
            self.vm.units[self.vm.unit_ptr]
                .lines
                .push((block.end_line, 3));
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3;
        }


@@ 264,20 336,22 @@ impl Codegen {
            self.patch_jump(loc, 0);
        }


        if let Some(loc) = start_jmp_loc {
            self.patch_jump(loc, 0);
        }

        let (_scope_type, variables) = self.scopes.pop().expect("popped scope in block");
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into());
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::POP.into());
        self.vm.units[self.vm.unit_ptr].code.push(variables as u8);
        if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line {
            self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 2));
            self.vm.units[self.vm.unit_ptr]
                .lines
                .push((block.end_line, 2));
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2;
        }

    }
    pub fn visit_break(&mut self, ast: &AST) {
        let mut scopes = self.scopes.clone();


@@ 289,18 363,27 @@ impl Codegen {
                break;
            }
        }
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into());
        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);

        self.vm.units[self.vm.unit_ptr].code.push(OpCode::JMP.into());
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::JMP.into());
        self.vm.units[self.vm.unit_ptr].code.push(0xFF);
        self.vm.units[self.vm.unit_ptr].code.push(0xFF);
        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, 5));
            self.vm.units[self.vm.unit_ptr]
                .lines
                .push((ast.end_line, 5));
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 5;
        }
        self.break_stack.last_mut().expect("break not in a loop").push(self.vm.units[self.vm.unit_ptr].code.len()-2);
        self.break_stack
            .last_mut()
            .expect("break not in a loop")
            .push(self.vm.units[self.vm.unit_ptr].code.len() - 2);
    }
    pub fn visit_continue(&mut self, ast: &AST) {
        let mut scopes = self.scopes.clone();


@@ 312,27 395,40 @@ impl Codegen {
                break;
            }
        }
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into());
        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);

        self.vm.units[self.vm.unit_ptr].code.push(OpCode::JMP.into());
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::JMP.into());
        self.vm.units[self.vm.unit_ptr].code.push(0xFF);
        self.vm.units[self.vm.unit_ptr].code.push(0xFF);
        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, 5));
            self.vm.units[self.vm.unit_ptr]
                .lines
                .push((ast.end_line, 5));
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 5;
        }
        self.continue_stack.last_mut().expect("continue not in a loop").push(self.vm.units[self.vm.unit_ptr].code.len()-2);
        self.continue_stack
            .last_mut()
            .expect("continue not in a loop")
            .push(self.vm.units[self.vm.unit_ptr].code.len() - 2);
    }
    pub fn visit_if(&mut self, condition: AST, block: AST, else_expr: Option<AST>) {
        self.visit(condition.clone());
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::JNE.into());
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::JNE.into());
        self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder
        self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder
        let start_jmp_loc = self.vm.units[self.vm.unit_ptr].code.len()-2;
        let start_jmp_loc = self.vm.units[self.vm.unit_ptr].code.len() - 2;
        if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != condition.end_line {
            self.vm.units[self.vm.unit_ptr].lines.push((condition.end_line, 5));
            self.vm.units[self.vm.unit_ptr]
                .lines
                .push((condition.end_line, 5));
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 5;
        }


@@ 342,39 438,48 @@ impl Codegen {
        if let Some(ast) = else_expr {
            match ast.kind {
                ASTType::If(_, _, _) => {
                    self.vm.units[self.vm.unit_ptr].code.push(OpCode::JMP.into());
                    self.vm.units[self.vm.unit_ptr]
                        .code
                        .push(OpCode::JMP.into());
                    self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder
                    self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder
                    let end_jmp_loc = self.vm.units[self.vm.unit_ptr].code.len()-2;
                    let end_jmp_loc = self.vm.units[self.vm.unit_ptr].code.len() - 2;
                    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, 3));
                        self.vm.units[self.vm.unit_ptr]
                            .lines
                            .push((ast.end_line, 3));
                    } else {
                        self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3;
                    }
                    

                    self.patch_jump(start_jmp_loc, 0);
                    self.visit(ast);
                    self.patch_jump(end_jmp_loc, 0);
                }
                ASTType::Block(_) => {

                    self.vm.units[self.vm.unit_ptr].code.push(OpCode::JMP.into());
                    self.vm.units[self.vm.unit_ptr]
                        .code
                        .push(OpCode::JMP.into());
                    self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder
                    self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder
                    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, 3));
                        self.vm.units[self.vm.unit_ptr]
                            .lines
                            .push((ast.end_line, 3));
                    } else {
                        self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3;
                    }
                    
                    let end_jmp_loc = self.vm.units[self.vm.unit_ptr].code.len()-2;

                    let end_jmp_loc = self.vm.units[self.vm.unit_ptr].code.len() - 2;
                    self.patch_jump(start_jmp_loc, 0); // jmp to else
                    if let ASTType::Block(ref stmts) = ast.kind {
                        self.visit_block(&ast, stmts.clone(), ScopeType::If);
                    }
                    self.patch_jump(end_jmp_loc, 0); // jmp to after else
                }
                _ => { println!("unimplemented"); }
                _ => {
                    println!("unimplemented");
                }
            }
        } else {
            self.patch_jump(start_jmp_loc, 0);


@@ 386,36 491,55 @@ impl Codegen {
            self.visit(stmt);
        }
        let (_scope_type, variables) = self.scopes.pop().expect("popped scope in block");
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into());
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::POP.into());
        self.vm.units[self.vm.unit_ptr].code.push(variables as u8);
        match self.vm.units[self.vm.unit_ptr].lines.last() {
            Some(last) => if last.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;
            Some(last) => {
                if last.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;
                }
            }
            None => self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2))
            None => self.vm.units[self.vm.unit_ptr]
                .lines
                .push((ast.end_line, 2)),
        }
    }
    pub fn visit_decl(&mut self, ast: &AST, _name: Name, expr: AST) {
        self.visit(expr);
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::DECL.into());
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::DECL.into());
        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, 1));
            self.vm.units[self.vm.unit_ptr]
                .lines
                .push((ast.end_line, 1));
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 1;
        }
        #[allow(irrefutable_let_patterns)]
        if let Extension::Resolution(_scope, _ptr) = ast.extensions[0] {
            self.scopes.last_mut().expect("codegen scopes vec was empty").1 += 1;
            self.scopes
                .last_mut()
                .expect("codegen scopes vec was empty")
                .1 += 1;
        }
    }
    pub fn visit_expr_stmt(&mut self, ast: &AST, expr: AST) {
        self.visit(expr);
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into());
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::POP.into());
        self.vm.units[self.vm.unit_ptr].code.push(0x01);
        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));
            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;
        }


@@ 423,9 547,13 @@ impl Codegen {
    // REMOVE LATER
    pub fn visit_print(&mut self, ast: &AST, expr: AST) {
        self.visit(expr);
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::PRINT.into());
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::PRINT.into());
        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, 1));
            self.vm.units[self.vm.unit_ptr]
                .lines
                .push((ast.end_line, 1));
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 1;
        }


@@ 437,10 565,14 @@ impl Codegen {
            LhsAssignType::Ident(_) => {
                #[allow(irrefutable_let_patterns)]
                if let Extension::Resolution(_scope, ptr) = ast.extensions[0] {
                    self.vm.units[self.vm.unit_ptr].code.push(OpCode::ASN.into());
                    self.vm.units[self.vm.unit_ptr]
                        .code
                        .push(OpCode::ASN.into());
                    self.vm.units[self.vm.unit_ptr].code.push(ptr 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));
                        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;
                    }


@@ 451,10 583,14 @@ impl Codegen {

                #[allow(irrefutable_let_patterns)]
                if let Extension::Resolution(_scope, ptr) = name.extensions[0] {
                    self.vm.units[self.vm.unit_ptr].code.push(OpCode::REF.into());
                    self.vm.units[self.vm.unit_ptr]
                        .code
                        .push(OpCode::REF.into());
                    self.vm.units[self.vm.unit_ptr].code.push(ptr 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));
                        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;
                    }


@@ 462,10 598,14 @@ impl Codegen {
                    panic!("Subscript assignment should always have a Resolution");
                }

                self.vm.units[self.vm.unit_ptr].code.push(OpCode::ASNARR.into());
                self.vm.units[self.vm.unit_ptr]
                    .code
                    .push(OpCode::ASNARR.into());

                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, 1));
                    self.vm.units[self.vm.unit_ptr]
                        .lines
                        .push((ast.end_line, 1));
                } else {
                    self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 1;
                }


@@ 475,21 615,32 @@ impl Codegen {
    pub fn visit_anonymous(&mut self, params: Vec<Name>, block: AST) {
        self.vm.units.push(Unit::new_empty());

        let new_unit_ptr = self.vm.units.len()-1;
        self.vm.units[self.vm.unit_ptr].pool.push(Value::Fun(Function {
            unit_ptr: new_unit_ptr,
            arity: params.len(),
        }));
        let new_unit_ptr = self.vm.units.len() - 1;
        self.vm.units[self.vm.unit_ptr]
            .pool
            .push(Value::Fun(Function {
                unit_ptr: new_unit_ptr,
                arity: params.len(),
            }));
        // load function to stack
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into());
        let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8;
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::LOAD.into());
        let loc = (self.vm.units[self.vm.unit_ptr].pool.len() - 1) as u8;
        self.vm.units[self.vm.unit_ptr].code.push(loc);
        if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line {
            self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 2));
        if self.vm.units[self.vm.unit_ptr].lines.len() == 0
            || self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line
        {
            self.vm.units[self.vm.unit_ptr]
                .lines
                .push((block.end_line, 2));
        } 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;
        self.scopes
            .last_mut()
            .expect("codegen scopes vec was empty")
            .1 += 1;

        // enter function unit
        let old_unit_ptr = self.vm.unit_ptr;


@@ 498,8 649,11 @@ impl Codegen {
        self.vm.ip = 0;

        // update scopes with number of parameters
        self.scopes.last_mut().expect("codegen scopes vec was empty").1 += params.len();
        
        self.scopes
            .last_mut()
            .expect("codegen scopes vec was empty")
            .1 += params.len();

        if let ASTType::Block(ref stmts) = block.kind {
            self.visit_block(&block, stmts.clone(), ScopeType::Function);
        }


@@ 508,12 662,18 @@ impl Codegen {
            Some(instr) => {
                if *instr != <OpCode as Into<u8>>::into(OpCode::RET) {
                    self.vm.units[self.vm.unit_ptr].pool.push(Value::Null);
                    self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into());
                    let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8;
                    self.vm.units[self.vm.unit_ptr]
                        .code
                        .push(OpCode::LOAD.into());
                    let loc = (self.vm.units[self.vm.unit_ptr].pool.len() - 1) as u8;
                    self.vm.units[self.vm.unit_ptr].code.push(loc);
                    self.vm.units[self.vm.unit_ptr].code.push(OpCode::RET.into());
                    self.vm.units[self.vm.unit_ptr]
                        .code
                        .push(OpCode::RET.into());
                    if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line {
                        self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 3));
                        self.vm.units[self.vm.unit_ptr]
                            .lines
                            .push((block.end_line, 3));
                    } else {
                        self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3;
                    }


@@ 521,36 681,48 @@ impl Codegen {
            }
            None => {
                self.vm.units[self.vm.unit_ptr].pool.push(Value::Null);
                self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into());
                let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8;
                self.vm.units[self.vm.unit_ptr]
                    .code
                    .push(OpCode::LOAD.into());
                let loc = (self.vm.units[self.vm.unit_ptr].pool.len() - 1) as u8;
                self.vm.units[self.vm.unit_ptr].code.push(loc);
                self.vm.units[self.vm.unit_ptr].code.push(OpCode::RET.into());
                self.vm.units[self.vm.unit_ptr]
                    .code
                    .push(OpCode::RET.into());
                if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line {
                    self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 3));
                    self.vm.units[self.vm.unit_ptr]
                        .lines
                        .push((block.end_line, 3));
                } else {
                    self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3;
                }
            }
        };
        

        self.vm.unit_ptr = old_unit_ptr;
        self.vm.ip = old_ip;
    }
    pub fn visit_subscript(&mut self, ast: &AST, left: AST, right: AST) {
        self.visit(left);
        self.visit(right);
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::SCR.into());
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::SCR.into());
        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, 1));
            self.vm.units[self.vm.unit_ptr]
                .lines
                .push((ast.end_line, 1));
        } else {
            self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 1;
        }
    }
    pub fn visit_binary(&mut self, left: AST, oper: BinOp, right: AST) {
        use crate::ast::BinOp::*;
        codegen_binary!(self, left, right, oper, Add, ADD, Sub, SUB, Mul, MUL,
            Div, DIV, Mod, MOD, BitAnd, BITAND, BitXor, BITXOR, BitOr, BITOR,
            Eq, EQ, Ne, NE, Gr, GR, Ge, GE, Ls, LS, Le, LE, Or, OR, And, AND);
        codegen_binary!(
            self, left, right, oper, Add, ADD, Sub, SUB, Mul, MUL, Div, DIV, Mod, MOD, BitAnd,
            BITAND, BitXor, BITXOR, BitOr, BITOR, Eq, EQ, Ne, NE, Gr, GR, Ge, GE, Ls, LS, Le, LE,
            Or, OR, And, AND
        );
    }
    pub fn visit_unary(&mut self, oper: UnOp, right: AST) {
        use crate::ast::UnOp::*;


@@ 560,33 732,55 @@ impl Codegen {
        match lit {
            Lit::Num(value) => {
                self.vm.units[self.vm.unit_ptr].pool.push(Value::Num(value));
                self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into());
                let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8;
                self.vm.units[self.vm.unit_ptr]
                    .code
                    .push(OpCode::LOAD.into());
                let loc = (self.vm.units[self.vm.unit_ptr].pool.len() - 1) as u8;
                self.vm.units[self.vm.unit_ptr].code.push(loc);
                if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || 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));
                if self.vm.units[self.vm.unit_ptr].lines.len() == 0
                    || 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;
                }
            }
            Lit::Str(value) => {
                self.vm.units[self.vm.unit_ptr].pool.push(Value::Str(value.into()));
                self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into());
                let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8;
                self.vm.units[self.vm.unit_ptr]
                    .pool
                    .push(Value::Str(value.into()));
                self.vm.units[self.vm.unit_ptr]
                    .code
                    .push(OpCode::LOAD.into());
                let loc = (self.vm.units[self.vm.unit_ptr].pool.len() - 1) as u8;
                self.vm.units[self.vm.unit_ptr].code.push(loc);
                if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || 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));
                if self.vm.units[self.vm.unit_ptr].lines.len() == 0
                    || 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;
                }
            }
            Lit::Bool(value) => {
                self.vm.units[self.vm.unit_ptr].pool.push(Value::Bool(value));
                self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into());
                let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8;
                self.vm.units[self.vm.unit_ptr]
                    .pool
                    .push(Value::Bool(value));
                self.vm.units[self.vm.unit_ptr]
                    .code
                    .push(OpCode::LOAD.into());
                let loc = (self.vm.units[self.vm.unit_ptr].pool.len() - 1) as u8;
                self.vm.units[self.vm.unit_ptr].code.push(loc);
                if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || 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));
                if self.vm.units[self.vm.unit_ptr].lines.len() == 0
                    || 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;
                }


@@ 595,16 789,24 @@ impl Codegen {
                for expr in &exprs {
                    self.visit(expr.clone());
                }
                self.vm.units[self.vm.unit_ptr].code.push(OpCode::LIST.into());
                self.vm.units[self.vm.unit_ptr]
                    .code
                    .push(OpCode::LIST.into());
                self.vm.units[self.vm.unit_ptr].code.push(exprs.len() as u8);
                if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || 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));
                if self.vm.units[self.vm.unit_ptr].lines.len() == 0
                    || 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;
                }
            }
            Lit::Ident(_name) => {
                self.vm.units[self.vm.unit_ptr].code.push(OpCode::VAR.into());
                self.vm.units[self.vm.unit_ptr]
                    .code
                    .push(OpCode::VAR.into());
                #[allow(irrefutable_let_patterns)]
                if let Extension::Resolution(_scope, ptr) = ast.extensions[0] {
                    /*println!("line: {} ptr: {} locals: {:?}", ast.end_line, ptr, self.locals);


@@ 612,8 814,12 @@ impl Codegen {
                    println!("slot: {}", *slot);
                    self.vm.units[self.vm.unit_ptr].code.push(*slot);*/
                    self.vm.units[self.vm.unit_ptr].code.push(ptr as u8);
                    if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || 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));
                    if self.vm.units[self.vm.unit_ptr].lines.len() == 0
                        || 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;
                    }


@@ 627,10 833,16 @@ impl Codegen {
            self.visit(arg.clone());
        }
        self.visit(ident);
        self.vm.units[self.vm.unit_ptr].code.push(OpCode::CALL.into());
        self.vm.units[self.vm.unit_ptr]
            .code
            .push(OpCode::CALL.into());
        self.vm.units[self.vm.unit_ptr].code.push(args.len() as u8);
        if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || 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));
        if self.vm.units[self.vm.unit_ptr].lines.len() == 0
            || 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;
        }


@@ 645,5 857,9 @@ impl Codegen {

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ScopeType {
    Main, Function, Loop, If, Other,
    Main,
    Function,
    Loop,
    If,
    Other,
}

M crates/kabel/src/debug.rs => crates/kabel/src/debug.rs +74 -47
@@ 1,4 1,9 @@
use crate::{ast::{LhsAssignType, AST}, lexer::Token, push_codegen, push_output, vm::{Value, VM}};
use crate::{
    ast::{LhsAssignType, AST},
    lexer::Token,
    push_codegen, push_output,
    vm::{Value, VM},
};

pub fn debug_token_array(tokens: Vec<Token>) -> String {
    let mut output = "".to_string();


@@ 6,13 11,13 @@ pub fn debug_token_array(tokens: Vec<Token>) -> String {
        output += &token.token_type.to_string();
        output += "\n";
    }
    output[..output.len()-1].to_string()
    output[..output.len() - 1].to_string()
}
pub fn debug_ast(ast: AST, level: usize) -> String {
    use crate::ast::ASTType::*;
    use crate::ast::BinOp::*;
    use crate::ast::UnOp::*;
    use crate::ast::Lit::*;
    use crate::ast::UnOp::*;
    let mut output = "".to_string();
    match ast.kind {
        Program(asts) => {


@@ 20,7 25,7 @@ pub fn debug_ast(ast: AST, level: usize) -> String {
            output += "Program";
            for ast in asts {
                output += "\n";
                output += &debug_ast(ast, level+1);
                output += &debug_ast(ast, level + 1);
            }
        }
        Function(name, params, block) => {


@@ 32,47 37,47 @@ pub fn debug_ast(ast: AST, level: usize) -> String {
            }
            output += &format!(" {:?}", ast.extensions);
            output += "\n";
            output += &debug_ast(*block, level+1);
            output += &debug_ast(*block, level + 1);
        }
        Return(expr) => {
            output += &"| ".repeat(level);
            output += "Return";
            if let Some(expr) = *expr {
                output += "\n";
                output += &debug_ast(expr, level+1);
                output += &debug_ast(expr, level + 1);
            }
        }
        Loop(block) => {
            output += &"| ".repeat(level);
            output += "Loop";
            output += "\n";
            output += &debug_ast(*block, level+1);
            output += &debug_ast(*block, level + 1);
        }
        While(condition, block) => {
            output += &"| ".repeat(level);
            output += "While";
            output += "\n";
            output += &debug_ast(*condition, level+1);
            output += &debug_ast(*condition, level + 1);
            output += "\n";
            output += &debug_ast(*block, level+1);
            output += &debug_ast(*block, level + 1);
        }
        For(expr1, expr2, expr3, block) => {
            output += &"| ".repeat(level);
            output += "For";
            if let Some(expr1) = *expr1 {
                output += "\n";
                output += &debug_ast(expr1, level+1);
                output += &debug_ast(expr1, level + 1);
            }
            if let Some(expr2) = *expr2 {
                output += "\n";
                output += &debug_ast(expr2, level+1);
                output += &debug_ast(expr2, level + 1);
            }
            if let Some(expr3) = *expr3 {
                output += "\n";
                output += &debug_ast(expr3, level+1);
                output += &debug_ast(expr3, level + 1);
            }
            output += "\n";
            output += &debug_ast(*block, level+1);
            output += &debug_ast(*block, level + 1);
        }
        Break => {
            output += &"| ".repeat(level);


@@ 85,15 90,15 @@ pub fn debug_ast(ast: AST, level: usize) -> String {
        If(condition, block, else_expr) => {
            output += &"| ".repeat(level);
            output += "If\n";
            output += &debug_ast(*condition, level+1);
            output += &debug_ast(*condition, level + 1);
            output += "\n";
            output += &debug_ast(*block, level+1);
            output += &debug_ast(*block, level + 1);
            if let Some(else_expr) = *else_expr {
                output += "\n";
                output += &"| ".repeat(level);
                output += "Else";
                output += "\n";
                output += &debug_ast(else_expr, level+1);
                output += &debug_ast(else_expr, level + 1);
            }
        }
        Block(asts) => {


@@ 101,19 106,19 @@ pub fn debug_ast(ast: AST, level: usize) -> String {
            output += "Block";
            for ast in asts {
                output += "\n";
                output += &debug_ast(ast, level+1);
                output += &debug_ast(ast, level + 1);
            }
        }
        Expr(expr) => {
            output += &"| ".repeat(level);
            output += "Expr\n";
            output += &debug_ast(*expr, level+1);
            output += &debug_ast(*expr, level + 1);
        }
        // REMOVE LATER
        Print(expr) => {
            output += &"| ".repeat(level);
            output += "Print\n";
            output += &debug_ast(*expr, level+1);
            output += &debug_ast(*expr, level + 1);
        }
        Decl(name, expr) => {
            output += &"| ".repeat(level);


@@ 121,7 126,7 @@ pub fn debug_ast(ast: AST, level: usize) -> String {
            output += &name.name;
            output += &format!(" {:?}", ast.extensions);
            output += "\n";
            output += &debug_ast(*expr, level+1);
            output += &debug_ast(*expr, level + 1);
        }
        Assign(lhs, expr) => {
            output += &"| ".repeat(level);


@@ 132,16 137,16 @@ pub fn debug_ast(ast: AST, level: usize) -> String {
                }
                LhsAssignType::Subscript(name, index) => {
                    output += "\n";
                    output += &"| ".repeat(level+1);
                    output += &"| ".repeat(level + 1);
                    output += "Subscript\n";
                    output += &debug_ast(*name, level+2);
                    output += &debug_ast(*name, level + 2);
                    output += "\n";
                    output += &debug_ast(*index, level+2);
                    output += &debug_ast(*index, level + 2);
                }
            }
            output += &format!(" {:?}", ast.extensions);
            output += "\n";
            output += &debug_ast(*expr, level+1);
            output += &debug_ast(*expr, level + 1);
        }
        Anonymous(params, block) => {
            output += &"| ".repeat(level);


@@ 150,42 155,42 @@ pub fn debug_ast(ast: AST, level: usize) -> String {
                output += &(" ".to_string() + &param.name);
            }
            output += "\n";
            output += &debug_ast(*block, level+1);
            output += &debug_ast(*block, level + 1);
        }
        Ternary(condition, true_expr, false_expr) => {
            output += &"| ".repeat(level);
            output += "Ternary\n";
            output += &debug_ast(*condition, level+1);
            output += &debug_ast(*condition, level + 1);
            output += "\n";
            output += &debug_ast(*true_expr, level+1);
            output += &debug_ast(*true_expr, level + 1);
            output += "\n";
            output += &debug_ast(*false_expr, level+1);
            output += &debug_ast(*false_expr, level + 1);
        }
        Subscript(array, index) => {
            output += &"| ".repeat(level);
            output += "Subscript\n";
            output += &debug_ast(*array, level+1);
            output += &debug_ast(*array, level + 1);
            output += "\n";
            output += &debug_ast(*index, level+1);
            output += &debug_ast(*index, level + 1);
        }
        Binary(left, oper, right) => {
            output += &"| ".repeat(level);
            output += "Binary ";
            push_output!(oper, output,
                Add, Sub, Mul, Div, Mod, BitAnd, BitXor, BitOr,
                Eq, Ne, Gr, Ge, Ls, Le, Or, And);
            push_output!(
                oper, output, Add, Sub, Mul, Div, Mod, BitAnd, BitXor, BitOr, Eq, Ne, Gr, Ge, Ls,
                Le, Or, And
            );
            output += "\n";
            output += &debug_ast(*left, level+1);
            output += &debug_ast(*left, level + 1);
            output += "\n";
            output += &debug_ast(*right, level+1);
            output += &debug_ast(*right, level + 1);
        }
        Unary(oper, right) => {
            output += &"| ".repeat(level);
            output += "Unary ";
            push_output!(oper, output,
                Not, Neg);
            push_output!(oper, output, Not, Neg);
            output += "\n";
            output += &debug_ast(*right, level+1);
            output += &debug_ast(*right, level + 1);
        }
        Lit(lit) => {
            output += &"| ".repeat(level);


@@ 207,7 212,7 @@ pub fn debug_ast(ast: AST, level: usize) -> String {
                Array(value) => {
                    for value in value {
                        output += "\n";
                        output += &debug_ast(value, level+1);
                        output += &debug_ast(value, level + 1);
                    }
                }
            }


@@ 215,18 220,18 @@ pub fn debug_ast(ast: AST, level: usize) -> String {
        Call(name, args) => {
            output += &"| ".repeat(level);
            output += "Call\n";
            output += &debug_ast(*name, level+1);
            output += &debug_ast(*name, level + 1);
            for arg in args {
                output += "\n";
                output += &debug_ast(arg, level+1);
                output += &debug_ast(arg, level + 1);
            }
        }
        Member(left, right) => {
            output += &"| ".repeat(level);
            output += "Member\n";
            output += &debug_ast(*left, level+1);
            output += &debug_ast(*left, level + 1);
            output += "\n";
            output += &debug_ast(*right, level+1);
            output += &debug_ast(*right, level + 1);
        }
    }
    output


@@ 239,9 244,31 @@ pub fn debug_bytecode(vm: &VM) -> String {
    let mut vm = vm.clone();
    while vm.ip < vm.units[vm.unit_ptr].code.len() {
        // remove PRINT later
        push_codegen!(vm.units[vm.unit_ptr].code[vm.ip].into(), vm, output, ADD, SUB, MUL, DIV, MOD, BITAND,
            BITXOR, BITOR, EQ, NE, GR, GE, LS, LE, OR, AND, NOT, NEG,
            PRINT, ERR);
        push_codegen!(
            vm.units[vm.unit_ptr].code[vm.ip].into(),
            vm,
            output,
            ADD,
            SUB,
            MUL,
            DIV,
            MOD,
            BITAND,
            BITXOR,
            BITOR,
            EQ,
            NE,
            GR,
            GE,
            LS,
            LE,
            OR,
            AND,
            NOT,
            NEG,
            PRINT,
            ERR
        );
        match vm.units[vm.unit_ptr].code[vm.ip].into() {
            LOAD => {
                output += &vm.ip.to_string();


@@ 270,7 297,7 @@ pub fn debug_bytecode(vm: &VM) -> String {
                output += &vm.units[vm.unit_ptr].code[vm.ip].to_string();
                output += "\n";
            }
            ASN => { 
            ASN => {
                output += &vm.ip.to_string();
                output += " ";
                output += &vm.find_line().to_string();

M crates/kabel/src/error.rs => crates/kabel/src/error.rs +8 -1
@@ 9,7 9,14 @@ pub struct KabelError {
}

impl KabelError {
    pub fn new(kind: ErrorKind, message: String, hint: Option<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,

M crates/kabel/src/lexer.rs => crates/kabel/src/lexer.rs +61 -10
@@ 53,7 53,10 @@ impl Lexer {
        let result = self.read_next_token();
        match result {
            Ok(b) => b,
            Err(e) => { self.errors.push(e); true }
            Err(e) => {
                self.errors.push(e);
                true
            }
        }
    }



@@ 429,15 432,63 @@ pub enum TokenType {
impl Display for TokenType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use TokenType::*;
        token_display!(*self, f, Function, Return, Loop, While, For,
            Break, Continue, If, Else, Var, True, False, Print,
                Star, StarEqual, Slash, SlashEqual, Percent, PercentEqual,
            Plus, PlusPlus, PlusEqual, Minus, MinusMinus, MinusEqual,
            LeftParen, RightParen, LeftBrace, RightBrace,
            LeftSquare, RightSquare, Equal, EqualEqual,
            Bang, BangEqual, Greater, GreaterEqual, Less,
            LessEqual, And, AndEqual, AndAnd, Or, OrEqual, OrOr,
            Caret, CaretEqual, Period, Comma, Semicolon, Colon, Question, Arrow);
        token_display!(
            *self,
            f,
            Function,
            Return,
            Loop,
            While,
            For,
            Break,
            Continue,
            If,
            Else,
            Var,
            True,
            False,
            Print,
            Star,
            StarEqual,
            Slash,
            SlashEqual,
            Percent,
            PercentEqual,
            Plus,
            PlusPlus,
            PlusEqual,
            Minus,
            MinusMinus,
            MinusEqual,
            LeftParen,
            RightParen,
            LeftBrace,
            RightBrace,
            LeftSquare,
            RightSquare,
            Equal,
            EqualEqual,
            Bang,
            BangEqual,
            Greater,
            GreaterEqual,
            Less,
            LessEqual,
            And,
            AndEqual,
            AndAnd,
            Or,
            OrEqual,
            OrOr,
            Caret,
            CaretEqual,
            Period,
            Comma,
            Semicolon,
            Colon,
            Question,
            Arrow
        );
        match *self {
            Ident(ref name) => {
                f.write_str("Ident ")?;

M crates/kabel/src/lib.rs => crates/kabel/src/lib.rs +7 -7
@@ 4,22 4,22 @@ use std::time::Instant;
use ast::AST;
use codegen::Codegen;
use lexer::{Lexer, Token};
use parser::Parser;
use name_resolution::Resolver;
use parser::Parser;

pub mod ast;
pub mod codegen;
pub mod debug;
pub mod error;
pub mod runtime_error;
pub mod extension;
pub mod lexer;
pub mod macros;
pub mod parser;
pub mod ast;
pub mod name_resolution;
pub mod opcodes;
pub mod codegen;
pub mod vm;
pub mod parser;
pub mod runtime_error;
pub mod test;
pub mod extension;
pub mod vm;

pub fn run_lexer(input: String) -> Lexer {
    let mut lexer = Lexer::new(input);

M crates/kabel/src/macros.rs => crates/kabel/src/macros.rs +13 -4
@@ 123,13 123,15 @@ 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()),
            format!(
                $message,
                $self.text[$token.line][$token.start_column..$token.end_column].to_string()
            ),
            Some($hint),
            $token.line,
            $token.start_column,


@@ 139,7 141,10 @@ macro_rules! unexpected_token {
    ($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()),
            format!(
                $message,
                $self.text[$token.line][$token.start_column..$token.end_column].to_string()
            ),
            None,
            $token.line,
            $token.start_column,


@@ 268,7 273,11 @@ macro_rules! wrong_type {
#[macro_export]
macro_rules! collect_lines {
    ($string:expr) => {
        $string.iter().fold("".to_string(), |acc, string| acc + string + "\n").trim_end().to_string()
        $string
            .iter()
            .fold("".to_string(), |acc, string| acc + string + "\n")
            .trim_end()
            .to_string()
    };
}


M crates/kabel/src/main.rs => crates/kabel/src/main.rs +5 -3
@@ 2,7 2,10 @@

use std::{env, fs, process::exit};

use kabel::{debug::{debug_ast, debug_bytecode}, run_codegen, run_lexer, run_parser, run_semantic_analysis};
use kabel::{
    debug::{debug_ast, debug_bytecode},
    run_codegen, run_lexer, run_parser, run_semantic_analysis,
};

fn main() {
    let args: Vec<String> = env::args().collect();


@@ 10,8 13,7 @@ fn main() {
        println!("Must provide kabel file to run");
        exit(1);
    }
    let program =
        fs::read_to_string(args[1].clone()).unwrap();
    let program = fs::read_to_string(args[1].clone()).unwrap();

    let mut output = "".to_string();


M crates/kabel/src/name_resolution.rs => crates/kabel/src/name_resolution.rs +184 -35
@@ 1,11 1,17 @@
use std::collections::HashMap;

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

pub struct Resolver {
    text: Vec<String>,
    symbol_table: Vec<HashMap<String, (Symbol, usize)>>, // (Symbol, reference to locals)
    pub locals: Vec<Vec<usize>>, // scope
    pub locals: Vec<Vec<usize>>,                         // scope
    pub scope: usize,
    pub errors: Vec<KabelError>,
}


@@ 13,7 19,12 @@ pub struct Resolver {
impl Resolver {
    pub fn new(text: String) -> Self {
        Self {
            text: text.lines().collect::<Vec<&str>>().iter().map(|s| s.to_string()).collect(),
            text: text
                .lines()
                .collect::<Vec<&str>>()
                .iter()
                .map(|s| s.to_string())
                .collect(),
            symbol_table: vec![HashMap::new()],
            locals: vec![Vec::new()],
            scope: 0,


@@ 58,27 69,73 @@ impl Resolver {
                                    ErrorKind::FunctionAlreadyDeclaredFunction, ast,
                                    "Function \"{}\" already declared", name ;
                                    "hint: has function \"{}\" already been declared?", name.name));
                        } else {}
                        } 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
                    .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));
                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.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));
                    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.pop();
                AST {
                    kind: Function(name, args, Box::new(block)),
                    extensions: vec![Extension::Resolution(self.scope, self.locals.last().expect("locals last in function ast len").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,


@@ 101,8 158,12 @@ impl Resolver {
                let block = self.visit(*block);
                ast_from_ast!(AST, While(Box::new(condition), Box::new(block)), ast, ast)
            }
            Break => { ast_from_ast!(AST, Break, ast, ast) }
            Continue => { ast_from_ast!(AST, Continue, ast, ast) }
            Break => {
                ast_from_ast!(AST, Break, ast, ast)
            }
            Continue => {
                ast_from_ast!(AST, Continue, ast, ast)
            }
            For(expr1, expr2, expr3, block) => {
                self.symbol_table.push(HashMap::new());
                self.scope += 1;


@@ 121,14 182,27 @@ impl Resolver {
                let block = self.visit(*block);
                while let Some(scope) = self.locals.last().expect("locals failed in For").last() {
                    if self.scope == *scope {
                        self.locals.last_mut().expect("locals failed in For pop").pop();
                        self.locals
                            .last_mut()
                            .expect("locals failed in For pop")
                            .pop();
                    } else {
                        break;
                    }
                }
                self.scope -= 1;
                self.symbol_table.pop();
                ast_from_ast!(AST, For(Box::new(n_expr1), Box::new(n_expr2), Box::new(n_expr3), Box::new(block)), ast, ast)
                ast_from_ast!(
                    AST,
                    For(
                        Box::new(n_expr1),
                        Box::new(n_expr2),
                        Box::new(n_expr3),
                        Box::new(block)
                    ),
                    ast,
                    ast
                )
            }
            If(condition, block, else_expr) => {
                let condition = self.visit(*condition);


@@ 137,7 211,12 @@ impl Resolver {
                if let Some(else_expr) = *else_expr {
                    n_else_expr = Some(self.visit(else_expr));
                }
                ast_from_ast!(AST, If(Box::new(condition), Box::new(block), Box::new(n_else_expr)), ast, ast)
                ast_from_ast!(
                    AST,
                    If(Box::new(condition), Box::new(block), Box::new(n_else_expr)),
                    ast,
                    ast
                )
            }
            Block(stmts) => {
                self.symbol_table.push(HashMap::new());


@@ 153,7 232,10 @@ impl Resolver {
                }*/
                while let Some(scope) = self.locals.last().expect("locals last in block").last() {
                    if self.scope == *scope {
                        self.locals.last_mut().expect("locals last in block pop").pop();
                        self.locals
                            .last_mut()
                            .expect("locals last in block pop")
                            .pop();
                    } else {
                        break;
                    }


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


@@ 185,13 270,27 @@ impl Resolver {
                                    ErrorKind::VariableAlreadyDeclaredFunction, ast,
                                    "Variable \"{}\" already declared", name ;
                                    "hint: has function \"{}\" already been declared?", name.name));
                        } else {}
                        } else {
                        }
                    }
                }
                self.symbol_table.last_mut().unwrap().insert(name.name.clone(), (Symbol::Var, self.locals.last().expect("locals last in decl symbol").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 {
                    kind: Decl(name, Box::new(expr)),
                    extensions: vec![Extension::Resolution(self.scope, self.locals.last().expect("locals last in decl ast").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,


@@ 207,15 306,19 @@ impl Resolver {
                let expr = self.visit(*expr);
                ast_from_ast!(AST, Print(Box::new(expr)), ast, ast)
            }
            Assign(lhs , expr) => {
            Assign(lhs, expr) => {
                let expr = self.visit(*expr);
                match lhs.kind.clone() {
                    LhsAssignType::Ident(name) => {
                        let resolution = self.resolve_var(&name);
                        if !resolution.0 {
                            self.errors.push(out_of_scope_var!(self,
                                    ErrorKind::OutOfScope, lhs,
                                    "Variable \"{}\" not in scope", name));
                            self.errors.push(out_of_scope_var!(
                                self,
                                ErrorKind::OutOfScope,
                                lhs,
                                "Variable \"{}\" not in scope",
                                name
                            ));
                        }
                        AST {
                            kind: Assign(lhs, Box::new(expr)),


@@ 230,7 333,15 @@ impl Resolver {
                        let name = self.visit(*name);
                        let index = self.visit(*index);
                        AST {
                            kind: Assign(ast_from_ast!(LhsAssign, LhsAssignType::Subscript(Box::new(name), Box::new(index)), lhs, lhs), Box::new(expr)),
                            kind: Assign(
                                ast_from_ast!(
                                    LhsAssign,
                                    LhsAssignType::Subscript(Box::new(name), Box::new(index)),
                                    lhs,
                                    lhs
                                ),
                                Box::new(expr),
                            ),
                            extensions: Vec::new(),
                            start_line: ast.start_line,
                            end_line: ast.end_line,


@@ 243,10 354,26 @@ impl Resolver {
            Anonymous(params, block) => {
                self.locals.push(Vec::new());
                self.symbol_table.push(HashMap::new());
                self.locals.last_mut().expect("locals last in anonymous self-reference push").push(self.scope);
                self.locals
                    .last_mut()
                    .expect("locals last in anonymous self-reference push")
                    .push(self.scope);
                for param in params.clone() {
                    self.locals.last_mut().expect("locals last in anonymous param push").push(self.scope+1);
                    self.symbol_table.last_mut().unwrap().insert(param.name, (Symbol::Var,self.locals.last().expect("locals last in anonymous param len").len()-1));
                    self.locals
                        .last_mut()
                        .expect("locals last in anonymous param push")
                        .push(self.scope + 1);
                    self.symbol_table.last_mut().unwrap().insert(
                        param.name,
                        (
                            Symbol::Var,
                            self.locals
                                .last()
                                .expect("locals last in anonymous param len")
                                .len()
                                - 1,
                        ),
                    );
                }
                let block = self.visit(*block);
                self.symbol_table.pop();


@@ 257,7 384,16 @@ impl Resolver {
                let condition = self.visit(*condition);
                let true_expr = self.visit(*true_expr);
                let false_expr = self.visit(*false_expr);
                ast_from_ast!(AST, Ternary(Box::new(condition), Box::new(true_expr), Box::new(false_expr)), ast, ast)
                ast_from_ast!(
                    AST,
                    Ternary(
                        Box::new(condition),
                        Box::new(true_expr),
                        Box::new(false_expr)
                    ),
                    ast,
                    ast
                )
            }
            Subscript(array, index) => {
                let array = self.visit(*array);


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


@@ 313,7 455,9 @@ impl Resolver {
            /*Member(left, right) => {
                self.visit_member(*left, *right);
            }*/
            _ => { panic!("not implemented") } // not implemented
            _ => {
                panic!("not implemented")
            } // not implemented
        }
    }
    // TODO: make visit_member not throw out of scope errors


@@ 323,13 467,18 @@ impl Resolver {
    }*/
    fn resolve_var(&self, name: &String) -> (bool, usize, usize) {
        for (scope_num, scope) in self.symbol_table.iter().enumerate().rev() {
            if let Some((Symbol::Var, place)) | Some((Symbol::Function(_), place)) = scope.get(name) {
            if let Some((Symbol::Var, place)) | Some((Symbol::Function(_), place)) = scope.get(name)
            {
                return (true, *place, scope_num);
            }
        }
        (false, 0, 0)
    }
    fn resolve_function(&mut self, name: &String, arity: usize) -> Result<usize, (ErrorKind, Option<usize>, Option<usize>)>{
    fn resolve_function(
        &mut self,
        name: &String,
        arity: usize,
    ) -> Result<usize, (ErrorKind, Option<usize>, Option<usize>)> {
        for scope in self.symbol_table.iter().rev() {
            if let Some((Symbol::Function(f_arity), place)) = scope.get(name) {
                if *f_arity == arity {

M crates/kabel/src/opcodes.rs => crates/kabel/src/opcodes.rs +43 -42
@@ 48,52 48,53 @@ impl From<OpCode> for u8 {
    fn from(value: OpCode) -> Self {
        use OpCode::*;
        match value {
            LOAD    => 0x00,
            VAR     => 0x01,
            REF     => 0x02,
            ASN     => 0x03,
            ASNARR  => 0x04,
            DECL    => 0x05,

            ADD     => 0x06,
            SUB     => 0x07,
            MUL     => 0x08,
            DIV     => 0x09,
            MOD     => 0x0A,
            BITAND  => 0x0B,
            BITXOR  => 0x0C,
            BITOR   => 0x0D,
            EQ      => 0x0E,
            NE      => 0x0F,
            GR      => 0x10,
            GE      => 0x11,
            LS      => 0x12,
            LE      => 0x13,
            OR      => 0x14,
            AND     => 0x15,

            NOT     => 0x16,
            NEG     => 0x17,

            JMP     => 0x18,
            JMP_UP  => 0x19,
            JNE     => 0x1A,

            CALL    => 0x1B,
            RET     => 0x1C,

            LIST    => 0x1D,
            SCR     => 0x1E,

            POP     => 0xFD,
            PRINT   => 0xFE,
            ERR     => 0xFF
            LOAD => 0x00,
            VAR => 0x01,
            REF => 0x02,
            ASN => 0x03,
            ASNARR => 0x04,
            DECL => 0x05,

            ADD => 0x06,
            SUB => 0x07,
            MUL => 0x08,
            DIV => 0x09,
            MOD => 0x0A,
            BITAND => 0x0B,
            BITXOR => 0x0C,
            BITOR => 0x0D,
            EQ => 0x0E,
            NE => 0x0F,
            GR => 0x10,
            GE => 0x11,
            LS => 0x12,
            LE => 0x13,
            OR => 0x14,
            AND => 0x15,

            NOT => 0x16,
            NEG => 0x17,

            JMP => 0x18,
            JMP_UP => 0x19,
            JNE => 0x1A,

            CALL => 0x1B,
            RET => 0x1C,

            LIST => 0x1D,
            SCR => 0x1E,

            POP => 0xFD,
            PRINT => 0xFE,
            ERR => 0xFF,
        }
    }
}
impl From<u8> for OpCode {
    fn from(value: u8) -> Self {
        use OpCode::*; match value {
        use OpCode::*;
        match value {
            0x00 => LOAD,
            0x01 => VAR,
            0x02 => REF,


@@ 133,7 134,7 @@ impl From<u8> for OpCode {

            0xFD => POP,
            0xFE => PRINT,
            _ => ERR
            _ => ERR,
        }
    }
}

M crates/kabel/src/parser.rs => crates/kabel/src/parser.rs +307 -109
@@ 1,7 1,11 @@
use crate::ast::{ASTType, BinOp, Lit, UnOp, AST};
use crate::{
    ast::{LhsAssign, LhsAssignType}, ast_from_ast, ast_from_ast_token, ast_from_token, ast_from_token_ast, collect_lines, error::{ErrorKind, KabelError}, lexer::{Token, TokenType}, lit, name, unexpected_token
    ast::{LhsAssign, LhsAssignType},
    ast_from_ast, ast_from_ast_token, ast_from_token, ast_from_token_ast, collect_lines,
    error::{ErrorKind, KabelError},
    lexer::{Token, TokenType},
    lit, name, unexpected_token,
};
use crate::ast::{AST, ASTType, BinOp, UnOp, Lit};

pub struct Parser {
    input: Vec<Token>,


@@ 15,7 19,12 @@ impl Parser {
    pub fn new(text: String, input: Vec<Token>) -> Self {
        Self {
            input: input.clone(),
            text: text.lines().collect::<Vec<&str>>().iter().map(|s| s.to_string()).collect(),
            text: text
                .lines()
                .collect::<Vec<&str>>()
                .iter()
                .map(|s| s.to_string())
                .collect(),
            current: 0,
            token: input[0].clone(),
            errors: Vec::new(),


@@ 72,7 81,11 @@ impl Parser {
                    if let TokenType::Ident(name) = ident.token_type {
                        expressions.push(name!(name, ident));
                    } else {
                        return Err(unexpected_token!(self, "Expected identifier but found {}", ident));
                        return Err(unexpected_token!(
                            self,
                            "Expected identifier but found {}",
                            ident
                        ));
                    }
                    if let TokenType::Comma = self.peek()?.token_type {
                        self.read_token()?;


@@ 81,20 94,25 @@ impl Parser {
                let right_paren = self.read_token()?;
                if let TokenType::RightParen = right_paren.token_type {
                    let block = self.block()?;
                    return Ok(ast_from_token_ast!(AST,
                        ASTType::Function(
                            name!(name, ident),
                            expressions,
                            Box::new(block.clone())
                        ),
                    return Ok(ast_from_token_ast!(
                        AST,
                        ASTType::Function(name!(name, ident), expressions, Box::new(block.clone())),
                        function_ident,
                        block
                    ));
                } else {
                    return Err(unexpected_token!(self, "Expected ) but found {}", right_paren));
                    return Err(unexpected_token!(
                        self,
                        "Expected ) but found {}",
                        right_paren
                    ));
                }
            } else {
                return Err(unexpected_token!(self, "Expected ( but found {}", left_paren));
                return Err(unexpected_token!(
                    self,
                    "Expected ( but found {}",
                    left_paren
                ));
            }
        } else {
            return Err(unexpected_token!(


@@ 109,7 127,8 @@ impl Parser {
        let return_ident = self.read_token()?;
        if let TokenType::Semicolon = self.peek()?.token_type {
            let semicolon = self.read_token()?;
            return Ok(ast_from_token!(AST,
            return Ok(ast_from_token!(
                AST,
                ASTType::Return(Box::new(None)),
                return_ident,
                semicolon


@@ 118,20 137,26 @@ impl Parser {
        let expression = self.expression()?;
        let semicolon = self.read_token()?;
        if let TokenType::Semicolon = semicolon.token_type {
            Ok(ast_from_token!(AST,
            Ok(ast_from_token!(
                AST,
                ASTType::Return(Box::new(Some(expression))),
                return_ident,
                semicolon
            ))
        } else {
            return Err(unexpected_token!(self, "Expected ; but found {}", semicolon));
            return Err(unexpected_token!(
                self,
                "Expected ; but found {}",
                semicolon
            ));
        }
    }

    pub fn loop_statement(&mut self) -> Result<AST, KabelError> {
        let loop_ident = self.read_token()?;
        let block = self.block()?;
        Ok(ast_from_token_ast!(AST,
        Ok(ast_from_token_ast!(
            AST,
            ASTType::Loop(Box::new(block.clone())),
            loop_ident,
            block


@@ 146,16 171,25 @@ impl Parser {
            let right_paren = self.read_token()?;
            if let TokenType::RightParen = right_paren.token_type {
                let block = self.block()?;
                return Ok(ast_from_token_ast!(AST,
                return Ok(ast_from_token_ast!(
                    AST,
                    ASTType::While(Box::new(condition), Box::new(block.clone())),
                    while_ident,
                    block
                ));
            } else {
                return Err(unexpected_token!(self, "Expected ) but found {}", right_paren));
                return Err(unexpected_token!(
                    self,
                    "Expected ) but found {}",
                    right_paren
                ));
            }
        } else {
            return Err(unexpected_token!(self, "Expected ( but found {}", left_paren));
            return Err(unexpected_token!(
                self,
                "Expected ( but found {}",
                left_paren
            ));
        }
    }



@@ 172,9 206,14 @@ impl Parser {
                } else {
                    expression1 = Some(self.expression()?);
                    let semicolon = self.read_token()?;
                    if let TokenType::Semicolon = semicolon.token_type {} else {
                    if let TokenType::Semicolon = semicolon.token_type {
                    } else {
                        self.current -= 1;
                        return Err(unexpected_token!(self, "Expected ; but found {}", semicolon));
                        return Err(unexpected_token!(
                            self,
                            "Expected ; but found {}",
                            semicolon
                        ));
                    }
                }
            }


@@ 188,14 227,16 @@ impl Parser {
            if let TokenType::Semicolon = semicolon_2.token_type {
                let expression3;
                if let TokenType::RightParen = self.peek()?.token_type {
                    expression3 = None; } else {
                    expression3 = None;
                } else {
                    expression3 = Some(self.expression()?);
                }
                let right_paren = self.read_token()?;
                if let TokenType::RightParen = right_paren.token_type {
                    let block = self.block()?;
                    //return Ok(Self::build_for(for_ident, semicolon_2, expression1, expression2, expression3, block));
                    return Ok(ast_from_token_ast!(AST,
                    return Ok(ast_from_token_ast!(
                        AST,
                        ASTType::For(
                            Box::new(expression1),
                            Box::new(expression2),


@@ 206,13 247,25 @@ impl Parser {
                        block
                    ));
                } else {
                    return Err(unexpected_token!(self, "Expected ) but found {}", right_paren));
                    return Err(unexpected_token!(
                        self,
                        "Expected ) but found {}",
                        right_paren
                    ));
                }
            } else {
                return Err(unexpected_token!(self, "Expected ; but found {}", semicolon_2));
                return Err(unexpected_token!(
                    self,
                    "Expected ; but found {}",
                    semicolon_2
                ));
            }
        } else {
            return Err(unexpected_token!(self, "Expected ( but found {}", left_paren));
            return Err(unexpected_token!(
                self,
                "Expected ( but found {}",
                left_paren
            ));
        }
    }
    /*fn build_for(for_ident: Token, semicolon_2: Token, expression1: Option<AST>, expression2: Option<AST>, expression3: Option<AST>, block: AST) -> AST {


@@ 243,7 296,11 @@ impl Parser {
        if let TokenType::Semicolon = semicolon.token_type {
            Ok(ast_from_token!(AST, ASTType::Break, break_ident, semicolon))
        } else {
            Err(unexpected_token!(self, "Expected ; but found {}", semicolon))
            Err(unexpected_token!(
                self,
                "Expected ; but found {}",
                semicolon
            ))
        }
    }



@@ 251,9 308,18 @@ impl Parser {
        let continue_ident = self.read_token()?;
        let semicolon = self.read_token()?;
        if let TokenType::Semicolon = semicolon.token_type {
            Ok(ast_from_token!(AST, ASTType::Continue, continue_ident, semicolon))
            Ok(ast_from_token!(
                AST,
                ASTType::Continue,
                continue_ident,
                semicolon
            ))
        } else {
            Err(unexpected_token!(self, "Expected ; but found {}", semicolon))
            Err(unexpected_token!(
                self,
                "Expected ; but found {}",
                semicolon
            ))
        }
    }



@@ 271,7 337,8 @@ impl Parser {
                        self.read_token()?;
                        if let TokenType::LeftBrace = self.peek()?.token_type {
                            let else_block = self.block()?;
                            return Ok(ast_from_token_ast!(AST,
                            return Ok(ast_from_token_ast!(
                                AST,
                                ASTType::If(
                                    Box::new(condition),
                                    Box::new(block.clone()),


@@ 284,7 351,8 @@ impl Parser {
                        let else_if_ident = self.peek()?;
                        if let TokenType::If = else_if_ident.token_type {
                            let else_if = self.if_statement()?;
                            return Ok(ast_from_token_ast!(AST,
                            return Ok(ast_from_token_ast!(
                                AST,
                                ASTType::If(
                                    Box::new(condition),
                                    Box::new(block.clone()),


@@ 297,16 365,25 @@ impl Parser {
                        return Err(unexpected_token!(self, "Unexpected token {}", else_ident));
                    }
                }
                return Ok(ast_from_token_ast!(AST,
                return Ok(ast_from_token_ast!(
                    AST,
                    ASTType::If(Box::new(condition), Box::new(block.clone()), Box::new(None)),
                    if_ident,
                    block
                ));
            } else {
                return Err(unexpected_token!(self, "Expected ) but found {}", right_paren));
                return Err(unexpected_token!(
                    self,
                    "Expected ) but found {}",
                    right_paren
                ));
            }
        } else {
            return Err(unexpected_token!(self, "Expected ( but found {}", left_paren));
            return Err(unexpected_token!(
                self,
                "Expected ( but found {}",
                left_paren
            ));
        }
    }



@@ 318,9 395,18 @@ impl Parser {
                stmts.push(self.statement()?);
            }
            let right_brace = self.read_token()?;
            return Ok(ast_from_token!(AST, ASTType::Block(stmts), left_brace, right_brace));
            return Ok(ast_from_token!(
                AST,
                ASTType::Block(stmts),
                left_brace,
                right_brace
            ));
        } else {
            return Err(unexpected_token!(self, "Expected {{ but found {}", left_brace));
            return Err(unexpected_token!(
                self,
                "Expected {{ but found {}",
                left_brace
            ));
        }
    }



@@ 333,7 419,8 @@ impl Parser {
                let expr = self.expression()?;
                let semicolon = self.read_token()?;
                if let TokenType::Semicolon = semicolon.token_type {
                    return Ok(ast_from_token!(AST,
                    return Ok(ast_from_token!(
                        AST,
                        ASTType::Decl(name!(name, ident), Box::new(expr.clone())),
                        var,
                        semicolon


@@ 359,9 446,18 @@ impl Parser {
        let expression = self.expression()?;
        let semicolon = self.read_token()?;
        if matches!(semicolon.token_type, TokenType::Semicolon) {
            Ok(ast_from_token!(AST, ASTType::Print(Box::new(expression)), print_ident, semicolon))
            Ok(ast_from_token!(
                AST,
                ASTType::Print(Box::new(expression)),
                print_ident,
                semicolon
            ))
        } else {
            Err(unexpected_token!(self, "Expected ; but found {}", semicolon))
            Err(unexpected_token!(
                self,
                "Expected ; but found {}",
                semicolon
            ))
        }
    }



@@ 380,11 476,19 @@ impl Parser {
        }
        let semicolon = self.read_token()?;
        if let TokenType::Semicolon = semicolon.token_type {
            return Ok(ast_from_ast_token!(AST, ASTType::Expr(Box::new(expression.clone())),
                expression, semicolon));
            return Ok(ast_from_ast_token!(
                AST,
                ASTType::Expr(Box::new(expression.clone())),
                expression,
                semicolon
            ));
        } else {
            self.current -= 1;
            return Err(unexpected_token!(self, "Expected ; but found {}", semicolon));
            return Err(unexpected_token!(
                self,
                "Expected ; but found {}",
                semicolon
            ));
        }
    }



@@ 401,9 505,18 @@ impl Parser {
                self.read_token()?;
                let index = self.expression()?;
                let right_square = self.read_token()?;
                lhs_type = ast_from_token!(LhsAssign, LhsAssignType::Subscript(Box::new(lit!(Ident, name.clone(), ident)), Box::new(index)), ident, right_square);
                lhs_type = ast_from_token!(
                    LhsAssign,
                    LhsAssignType::Subscript(
                        Box::new(lit!(Ident, name.clone(), ident)),
                        Box::new(index)
                    ),
                    ident,
                    right_square
                );
            } else {
                lhs_type = ast_from_token!(LhsAssign, LhsAssignType::Ident(name.clone()), ident, ident);
                lhs_type =
                    ast_from_token!(LhsAssign, LhsAssignType::Ident(name.clone()), ident, ident);
            }
            if self.current >= self.input.len() {
                self.current -= 1;


@@ 422,20 535,20 @@ impl Parser {
                let binop = self.read_token()?;
                let expr = self.expression()?;
                if binop.token_type == TokenType::Equal {
                    return Ok(ast_from_token_ast!(AST,
                        ASTType::Assign(
                            lhs_type,
                            Box::new(expr.clone())
                        ),
                    return Ok(ast_from_token_ast!(
                        AST,
                        ASTType::Assign(lhs_type, Box::new(expr.clone())),
                        ident,
                        expr
                    ));
                } else if binop.token_type == TokenType::PlusEqual {
                    return Ok(ast_from_token_ast!(AST,
                    return Ok(ast_from_token_ast!(
                        AST,
                        ASTType::Assign(
                            lhs_type,
                            Box::new(
                                ast_from_ast!(AST,
                                ast_from_ast!(
                                    AST,
                                    ASTType::Binary(
                                        Box::new(lit!(Ident, name, ident)),
                                        BinOp::Add,


@@ 451,11 564,13 @@ impl Parser {
                        expr
                    ));
                } else if binop.token_type == TokenType::MinusEqual {
                    return Ok(ast_from_token_ast!(AST,
                    return Ok(ast_from_token_ast!(
                        AST,
                        ASTType::Assign(
                            lhs_type,
                            Box::new(
                                ast_from_ast!(AST,
                                ast_from_ast!(
                                    AST,
                                    ASTType::Binary(
                                        Box::new(lit!(Ident, name, ident)),
                                        BinOp::Sub,


@@ 471,11 586,13 @@ impl Parser {
                        expr
                    ));
                } else if binop.token_type == TokenType::StarEqual {
                    return Ok(ast_from_token_ast!(AST,
                    return Ok(ast_from_token_ast!(
                        AST,
                        ASTType::Assign(
                            lhs_type,
                            Box::new(
                                ast_from_ast!(AST,
                                ast_from_ast!(
                                    AST,
                                    ASTType::Binary(
                                        Box::new(lit!(Ident, name, ident)),
                                        BinOp::Mul,


@@ 491,11 608,13 @@ impl Parser {
                        expr
                    ));
                } else if binop.token_type == TokenType::SlashEqual {
                    return Ok(ast_from_token_ast!(AST,
                    return Ok(ast_from_token_ast!(
                        AST,
                        ASTType::Assign(
                            lhs_type,
                            Box::new(
                                ast_from_ast!(AST,
                                ast_from_ast!(
                                    AST,
                                    ASTType::Binary(
                                        Box::new(lit!(Ident, name, ident)),
                                        BinOp::Div,


@@ 511,11 630,13 @@ impl Parser {
                        expr
                    ));
                } else if binop.token_type == TokenType::PercentEqual {
                    return Ok(ast_from_token_ast!(AST,
                    return Ok(ast_from_token_ast!(
                        AST,
                        ASTType::Assign(
                            lhs_type,
                            Box::new(
                                ast_from_ast!(AST,
                                ast_from_ast!(
                                    AST,
                                    ASTType::Binary(
                                        Box::new(lit!(Ident, name, ident)),
                                        BinOp::Mod,


@@ 531,11 652,13 @@ impl Parser {
                        expr
                    ));
                } else if binop.token_type == TokenType::AndEqual {
                    return Ok(ast_from_token_ast!(AST,
                    return Ok(ast_from_token_ast!(
                        AST,
                        ASTType::Assign(
                            lhs_type,
                            Box::new(
                                ast_from_ast!(AST,
                                ast_from_ast!(
                                    AST,
                                    ASTType::Binary(
                                        Box::new(lit!(Ident, name, ident)),
                                        BinOp::BitAnd,


@@ 551,11 674,13 @@ impl Parser {
                        expr
                    ));
                } else if binop.token_type == TokenType::CaretEqual {
                    return Ok(ast_from_token_ast!(AST,
                    return Ok(ast_from_token_ast!(
                        AST,
                        ASTType::Assign(
                            lhs_type,
                            Box::new(
                                ast_from_ast!(AST,
                                ast_from_ast!(
                                    AST,
                                    ASTType::Binary(
                                        Box::new(lit!(Ident, name, ident)),
                                        BinOp::BitXor,


@@ 571,11 696,13 @@ impl Parser {
                        expr
                    ));
                } else {
                    return Ok(ast_from_token_ast!(AST,
                    return Ok(ast_from_token_ast!(
                        AST,
                        ASTType::Assign(
                            lhs_type,
                            Box::new(
                                ast_from_ast!(AST,
                                ast_from_ast!(
                                    AST,
                                    ASTType::Binary(
                                        Box::new(lit!(Ident, name, ident)),
                                        BinOp::BitOr,


@@ 613,7 740,11 @@ impl Parser {
                            if let TokenType::Ident(name) = ident.token_type {
                                params.push(name!(name, ident));
                            } else {
                                return Err(unexpected_token!(self, "Expected identifier but found {}", ident));
                                return Err(unexpected_token!(
                                    self,
                                    "Expected identifier but found {}",
                                    ident
                                ));
                            }
                            if let TokenType::Comma = self.peek()?.token_type {
                                self.read_token()?;


@@ 624,10 755,19 @@ impl Parser {
                            self.read_token()?;
                            if let TokenType::LeftBrace = self.peek()?.token_type {
                                let block = self.block()?;
                                return Ok(ast_from_token_ast!(AST, ASTType::Anonymous(params, Box::new(block.clone())), left_paren, block));
                                return Ok(ast_from_token_ast!(
                                    AST,
                                    ASTType::Anonymous(params, Box::new(block.clone())),
                                    left_paren,
                                    block
                                ));
                            } else {
                                let left_brace = self.read_token()?;
                                return Err(unexpected_token!(self, "Expected {{ but found {}", left_brace));
                                return Err(unexpected_token!(
                                    self,
                                    "Expected {{ but found {}",
                                    left_brace
                                ));
                            }
                        } else {
                            let arrow = self.read_token()?;


@@ 640,10 780,19 @@ impl Parser {
                            self.read_token()?;
                            if let TokenType::LeftBrace = self.peek()?.token_type {
                                let block = self.block()?;
                                return Ok(ast_from_token_ast!(AST, ASTType::Anonymous(params, Box::new(block.clone())), left_paren, block));
                                return Ok(ast_from_token_ast!(
                                    AST,
                                    ASTType::Anonymous(params, Box::new(block.clone())),
                                    left_paren,
                                    block
                                ));
                            } else {
                                let left_brace = self.read_token()?;
                                return Err(unexpected_token!(self, "Expected {{ but found {}", left_brace));
                                return Err(unexpected_token!(
                                    self,
                                    "Expected {{ but found {}",
                                    left_brace
                                ));
                            }
                        } else {
                            let arrow = self.read_token()?;


@@ 659,7 808,12 @@ impl Parser {
                    if let TokenType::Arrow = self.peek()?.token_type {
                        self.read_token()?;
                        let block = self.block()?;
                        return Ok(ast_from_token_ast!(AST, ASTType::Anonymous(Vec::new(), Box::new(block.clone())), left_paren, block));
                        return Ok(ast_from_token_ast!(
                            AST,
                            ASTType::Anonymous(Vec::new(), Box::new(block.clone())),
                            left_paren,
                            block
                        ));
                    } else {
                        let arrow = self.read_token()?;
                        return Err(unexpected_token!(self, "Expected => but found {}", arrow));


@@ 682,7 836,8 @@ impl Parser {
            if let TokenType::Colon = self.peek()?.token_type {
                self.read_token()?;
                let false_expr = self.expression()?;
                return Ok(ast_from_ast!(AST,
                return Ok(ast_from_ast!(
                    AST,
                    ASTType::Ternary(
                        Box::new(condition.clone()),
                        Box::new(true_expr),


@@ 692,7 847,11 @@ impl Parser {
                    false_expr
                ));
            } else {
                return Err(unexpected_token!(self, "Expected : but found {}", self.token));
                return Err(unexpected_token!(
                    self,
                    "Expected : but found {}",
                    self.token
                ));
            }
        }



@@ 705,7 864,8 @@ impl Parser {
        while self.current < self.input.len() && self.peek()?.token_type == TokenType::OrOr {
            self.read_token()?;
            let right = self.logical_and()?;
            left = ast_from_ast!(AST,
            left = ast_from_ast!(
                AST,
                ASTType::Binary(Box::new(left.clone()), BinOp::Or, Box::new(right.clone())),
                left,
                right


@@ 720,7 880,8 @@ impl Parser {
        while self.current < self.input.len() && self.peek()?.token_type == TokenType::AndAnd {
            self.read_token()?;
            let right = self.bit_and()?;
            left = ast_from_ast!(AST,
            left = ast_from_ast!(
                AST,
                ASTType::Binary(Box::new(left.clone()), BinOp::And, Box::new(right.clone())),
                left,
                right


@@ 735,7 896,8 @@ impl Parser {
        while self.current < self.input.len() && self.peek()?.token_type == TokenType::And {
            self.read_token()?;
            let right = self.bit_xor()?;
            left = ast_from_ast!(AST,
            left = ast_from_ast!(
                AST,
                ASTType::Binary(
                    Box::new(left.clone()),
                    BinOp::BitAnd,


@@ 754,7 916,8 @@ impl Parser {
        while self.current < self.input.len() && self.peek()?.token_type == TokenType::Caret {
            self.read_token()?;
            let right = self.bit_or()?;
            left = ast_from_ast!(AST,
            left = ast_from_ast!(
                AST,
                ASTType::Binary(
                    Box::new(left.clone()),
                    BinOp::BitXor,


@@ 773,7 936,8 @@ impl Parser {
        while self.current < self.input.len() && self.peek()?.token_type == TokenType::Or {
            self.read_token()?;
            let right = self.equality()?;
            left = ast_from_ast!(AST,
            left = ast_from_ast!(
                AST,
                ASTType::Binary(
                    Box::new(left.clone()),
                    BinOp::BitOr,


@@ 796,13 960,15 @@ impl Parser {
            let binop = self.read_token()?;
            let right = self.comparison()?;
            if binop.token_type == TokenType::EqualEqual {
                left = ast_from_ast!(AST,
                left = ast_from_ast!(
                    AST,
                    ASTType::Binary(Box::new(left.clone()), BinOp::Eq, Box::new(right.clone())),
                    left,
                    right
                );
            } else {
                left = ast_from_ast!(AST,
                left = ast_from_ast!(
                    AST,
                    ASTType::Binary(Box::new(left.clone()), BinOp::Ne, Box::new(right.clone())),
                    left,
                    right


@@ 825,25 991,29 @@ impl Parser {
            let binop = self.read_token()?;
            let right = self.term()?;
            if binop.token_type == TokenType::Less {
                left = ast_from_ast!(AST,
                left = ast_from_ast!(
                    AST,
                    ASTType::Binary(Box::new(left.clone()), BinOp::Ls, Box::new(right.clone())),
                    left,
                    right
                );
            } else if binop.token_type == TokenType::LessEqual {
                left = ast_from_ast!(AST,
                left = ast_from_ast!(
                    AST,
                    ASTType::Binary(Box::new(left.clone()), BinOp::Le, Box::new(right.clone())),
                    left,
                    right
                );
            } else if binop.token_type == TokenType::Greater {
                left = ast_from_ast!(AST,
                left = ast_from_ast!(
                    AST,
                    ASTType::Binary(Box::new(left.clone()), BinOp::Gr, Box::new(right.clone())),
                    left,
                    right
                );
            } else {
                left = ast_from_ast!(AST,
                left = ast_from_ast!(
                    AST,
                    ASTType::Binary(Box::new(left.clone()), BinOp::Ge, Box::new(right.clone())),
                    left,
                    right


@@ 865,13 1035,15 @@ impl Parser {
            let right = self.factor()?;

            if binop.token_type == TokenType::Plus {
                left = ast_from_ast!(AST,
                left = ast_from_ast!(
                    AST,
                    ASTType::Binary(Box::new(left.clone()), BinOp::Add, Box::new(right.clone())),
                    left,
                    right
                );
            } else {
                left = ast_from_ast!(AST,
                left = ast_from_ast!(
                    AST,
                    ASTType::Binary(Box::new(left.clone()), BinOp::Sub, Box::new(right.clone())),
                    left,
                    right


@@ 892,19 1064,22 @@ impl Parser {
            let right = self.unary()?;

            if binop.token_type == TokenType::Star {
                left = ast_from_ast!(AST,
                left = ast_from_ast!(
                    AST,
                    ASTType::Binary(Box::new(left.clone()), BinOp::Mul, Box::new(right.clone())),
                    left,
                    right
                );
            } else if binop.token_type == TokenType::Slash {
                left = ast_from_ast!(AST,
                left = ast_from_ast!(
                    AST,
                    ASTType::Binary(Box::new(left.clone()), BinOp::Div, Box::new(right.clone())),
                    left,
                    right
                );
            } else {
                left = ast_from_ast!(AST,
                left = ast_from_ast!(
                    AST,
                    ASTType::Binary(Box::new(left.clone()), BinOp::Mod, Box::new(right.clone())),
                    left,
                    right


@@ 918,13 1093,15 @@ impl Parser {
            let token = self.read_token()?;
            let unary = self.unary()?;
            if token.token_type == TokenType::Bang {
                return Ok(ast_from_token_ast!(AST,
                return Ok(ast_from_token_ast!(
                    AST,
                    ASTType::Unary(UnOp::Not, Box::new(unary.clone())),
                    token,
                    unary
                ));
            } else {
                return Ok(ast_from_token_ast!(AST,
                return Ok(ast_from_token_ast!(
                    AST,
                    ASTType::Unary(UnOp::Neg, Box::new(unary.clone())),
                    token,
                    unary


@@ 942,13 1119,18 @@ impl Parser {
            let expr = self.expression()?;
            let right_brace = self.read_token()?;
            if let TokenType::RightSquare = right_brace.token_type {
                primary = ast_from_ast_token!(AST,
                primary = ast_from_ast_token!(
                    AST,
                    ASTType::Subscript(Box::new(primary.clone()), Box::new(expr)),
                    primary,
                    right_brace
                );
            } else {
                return Err(unexpected_token!(self, "Expected ] but found {}", right_brace));
                return Err(unexpected_token!(
                    self,
                    "Expected ] but found {}",
                    right_brace
                ));
            }
        }



@@ 1001,7 1183,8 @@ impl Parser {
            }
        }
        let right_square = self.read_token()?;
        Ok(ast_from_token!(AST,
        Ok(ast_from_token!(
            AST,
            ASTType::Lit(Lit::Array(expressions)),
            left_square,
            right_square


@@ 1018,7 1201,8 @@ impl Parser {
                    if self.current < self.input.len() {
                        if let TokenType::LeftParen = self.peek()?.token_type {
                            let call = self.call(child)?;
                            expr = ast_from_ast!(AST,
                            expr = ast_from_ast!(
                                AST,
                                ASTType::Member(Box::new(expr.clone()), Box::new(call.clone())),
                                expr,
                                call


@@ 1029,7 1213,8 @@ impl Parser {
                            continue;
                        }
                    }
                    expr = ast_from_ast_token!(AST,
                    expr = ast_from_ast_token!(
                        AST,
                        ASTType::Member(
                            Box::new(expr.clone()),
                            Box::new(lit!(Ident, child_str, child))


@@ 1060,7 1245,8 @@ impl Parser {
        }
        let right_paren = self.read_token()?;
        if let TokenType::Ident(name) = ident.token_type {
            return Ok(ast_from_token!(AST,
            return Ok(ast_from_token!(
                AST,
                ASTType::Call(Box::new(lit!(Ident, name, ident)), expressions),
                ident,
                right_paren


@@ 1072,10 1258,17 @@ impl Parser {
        if let TokenType::Ident(name) = ident.token_type {
            let oper = self.read_token()?;
            if oper.token_type == TokenType::PlusPlus {
                return Ok(ast_from_token!(AST,
                return Ok(ast_from_token!(
                    AST,
                    ASTType::Assign(
                        ast_from_token!(LhsAssign, LhsAssignType::Ident(name.clone()), ident, ident),
                        Box::new(ast_from_token!(AST,
                        ast_from_token!(
                            LhsAssign,
                            LhsAssignType::Ident(name.clone()),
                            ident,
                            ident
                        ),
                        Box::new(ast_from_token!(
                            AST,
                            ASTType::Binary(
                                Box::new(lit!(Ident, name, ident)),
                                BinOp::Add,


@@ 1089,10 1282,17 @@ impl Parser {
                    oper
                ));
            } else {
                return Ok(ast_from_token!(AST,
                return Ok(ast_from_token!(
                    AST,
                    ASTType::Assign(
                        ast_from_token!(LhsAssign, LhsAssignType::Ident(name.clone()), ident, ident),
                        Box::new(ast_from_token!(AST,
                        ast_from_token!(
                            LhsAssign,
                            LhsAssignType::Ident(name.clone()),
                            ident,
                            ident
                        ),
                        Box::new(ast_from_token!(
                            AST,
                            ASTType::Binary(
                                Box::new(lit!(Ident, name, ident)),
                                BinOp::Sub,


@@ 1121,15 1321,13 @@ impl Parser {
                    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"),
                    self.text[left_paren.line..right_paren.line]
                        .iter()
                        .fold("".to_string(), |acc, string| acc + string + "\n"),
                ));
            }
            self.read_token()?;
            return Ok(ast_from_token!(AST,
                expr.kind,
                left_paren,
                right_paren
            ));
            return Ok(ast_from_token!(AST, expr.kind, left_paren, right_paren));
        }
        if let Err(e) = right_paren {
            return Err(KabelError::new(

M crates/kabel/src/runtime_error.rs => crates/kabel/src/runtime_error.rs +7 -1
@@ 8,7 8,13 @@ pub struct KabelRuntimeError {
}

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

M crates/kabel/src/test.rs => crates/kabel/src/test.rs +4 -1
@@ 1,6 1,9 @@
use test_each_file::test_each_file;

use crate::{debug::{debug_ast, debug_token_array}, run_lexer, run_parser};
use crate::{
    debug::{debug_ast, debug_token_array},
    run_lexer, run_parser,
};

test_each_file! { for ["kab", "out"] in "./crates/kabel/test/runtime/" => test }
test_each_file! { for ["kab", "out"] in "./crates/kabel/test/lexer/" => test_lexer }

M crates/kabel/src/vm.rs => crates/kabel/src/vm.rs +447 -112
@@ 1,6 1,8 @@

use crate::{runtime_error::{KabelRuntimeError, RuntimeErrorKind}, vm_boolean_comparison, vm_boolean_equality};
use crate::vm_error;
use crate::{
    runtime_error::{KabelRuntimeError, RuntimeErrorKind},
    vm_boolean_comparison, vm_boolean_equality,
};

#[derive(Debug, Clone)]
pub struct Unit {


@@ 10,11 12,7 @@ pub struct Unit {
}
impl Unit {
    pub fn new(code: Vec<u8>, pool: Vec<Value>, lines: Vec<(usize, usize)>) -> Self {
        Self {
            code,
            pool,
            lines,
        }
        Self { code, pool, lines }
    }
    pub fn new_empty() -> Self {
        Self {


@@ 34,12 32,16 @@ pub struct VM {
    pub stack: Vec<Value>,
    pub variables: Vec<Value>,
    pub variables_offset: usize,
    text: Vec<String>
    text: Vec<String>,
}

impl VM {
    pub fn new(bytecode: Vec<u8>, lines: Vec<(usize, usize)>, pool: Vec<Value>,
        text: String) -> Self {
    pub fn new(
        bytecode: Vec<u8>,
        lines: Vec<(usize, usize)>,
        pool: Vec<Value>,
        text: String,
    ) -> Self {
        Self {
            ip: 0,
            unit_ptr: 0,


@@ 55,35 57,45 @@ impl VM {
        use Value::*;
        while self.ip < self.units[self.unit_ptr].code.len() {
            match self.read() {
                0x00 => { // LOAD
                0x00 => {
                    // LOAD
                    let byte = self.read() as usize;
                    println!("ip: {}", self.ip);
                    let value = self.units[self.unit_ptr].pool[byte].clone();
                    self.stack.push(value);
                }
                0x01 => { // VAR
                0x01 => {
                    // VAR
                    let ptr = self.read() as usize;
                    let value = self.variables[ptr+self.variables_offset].clone();
                    let value = self.variables[ptr + self.variables_offset].clone();
                    self.stack.push(value);
                }
                0x02 => { // REF
                0x02 => {
                    // REF
                    let ptr = self.read() as usize;
                    self.stack.push(Ref(ptr));
                }
                0x03 => { // ASN
                0x03 => {
                    // ASN
                    let value = self.stack.pop().unwrap();
                    let ptr = self.read();
                    let offset = self.call_stack.last().expect("var call stack last").2;
                    self.variables[ptr as usize + offset] = value.clone();
                    self.stack.push(value);
                }
                0x04 => { // ASNARR
                0x04 => {
                    // ASNARR
                    let listref = self.stack.pop().unwrap();
                    let index = self.stack.pop().unwrap();
                    let value = self.stack.pop().unwrap();
                    if let Value::Num(index) = index {
                        if index.fract() != 0.0 {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Subscript index must be an integer but found {}", index))
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Subscript index must be an integer but found {}",
                                index
                            ));
                        }
                        let index = index as usize;
                        if let Value::Ref(listref) = listref {


@@ 91,166 103,333 @@ impl VM {
                            if let Value::List(ref mut list) = list {
                                let len = list.len();
                                if index > len {
                                    return Err(vm_error!(self, RuntimeErrorKind::ArrayOutOfBounds, "List length is {} but found index {}", len, index))
                                    return Err(vm_error!(
                                        self,
                                        RuntimeErrorKind::ArrayOutOfBounds,
                                        "List length is {} but found index {}",
                                        len,
                                        index
                                    ));
                                }
                                println!("index: {}", index);
                                list[index] = value.clone();
                                self.stack.push(value);
                            } else {
                                let type_str = list.type_str();
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to subscript non-list type {}", type_str))
                                return Err(vm_error!(
                                    self,
                                    RuntimeErrorKind::WrongType,
                                    "Tried to subscript non-list type {}",
                                    type_str
                                ));
                            }
                        } else {
                            panic!("Something went wrong in kabel, listref was not a Ref");
                        }
                    } else {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to use non-integer type {} to subscript list", index.type_str()))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to use non-integer type {} to subscript list",
                            index.type_str()
                        ));
                    }
                }
                0x05 => { // DECL
                0x05 => {
                    // DECL
                    let value = self.stack.pop().unwrap();
                    self.variables.push(value);
                }
                0x06 => { // ADD
                0x06 => {
                    // ADD
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.stack.push(Num(v1 + v2)),
                        (Str(v1), Str(v2)) => {
                            self.stack.push(Str(v1.clone() + &v2));
                        },
                        }
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot add booleans"))
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Cannot add booleans"
                            ))
                        }
                        (v1, v2) => {
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "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()
                            ))
                        }
                    }
                }
                0x07 => { // SUB
                0x07 => {
                    // SUB
                    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(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot subtract strings"))
                        },
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Cannot subtract strings"
                            ))
                        }
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot subtract booleans"))
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Cannot subtract booleans"
                            ))
                        }
                        (v1, v2) => {
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "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()
                            ))
                        }
                    }
                }
                0x08 => { // MUL
                0x08 => {
                    // MUL
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.stack.push(Num(v1 * v2)),
                        (Str(v1), Num(v2)) => {
                            if v2.fract() == 0.0 {
                                self.stack.push(Str(v1.repeat(v2 as usize)));
                            } else {
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Number must be an integer"))
                                return Err(vm_error!(
                                    self,
                                    RuntimeErrorKind::WrongType,
                                    "Number must be an integer"
                                ));
                            }
                        }
                        (Str(_v1), Str(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot multiply strings"))
                        },
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Cannot multiply strings"
                            ))
                        }
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot multiply booleans"))
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Cannot multiply booleans"
                            ))
                        }
                        (v1, v2) => {
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "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()
                            ))
                        }
                    }
                }
                0x09 => { // DIV
                0x09 => {
                    // DIV
                    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(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot divide strings"))
                        },
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Cannot divide strings"
                            ))
                        }
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot divide booleans"))
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Cannot divide booleans"
                            ))
                        }
                        (v1, v2) => {
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "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()
                            ))
                        }
                    }
                }
                0x0A => { // MOD
                0x0A => {
                    // MOD
                    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(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform modulus on strings"))
                        },
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Cannot perform modulus on strings"
                            ))
                        }
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform modulus on booleans"))
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Cannot perform modulus on booleans"
                            ))
                        }
                        (v1, v2) => {
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "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()
                            ))
                        }
                    }
                }
                0x0B => { // BITAND
                0x0B => {
                    // BITAND
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => {
                            if v1.fract() != 0.0 {
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "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(vm_error!(self, RuntimeErrorKind::WrongType, "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(vm_error!(self, RuntimeErrorKind::WrongType, "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(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise AND on booleans"))
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Cannot perform bitwise AND on booleans"
                            ))
                        }
                        (v1, v2) => {
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "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()
                            ))
                        }
                    }
                }
                0x0C => { // BITXOR
                0x0C => {
                    // BITXOR
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => {
                            if v1.fract() != 0.0 {
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "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(vm_error!(self, RuntimeErrorKind::WrongType, "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(vm_error!(self, RuntimeErrorKind::WrongType, "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(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise XOR on booleans"))
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Cannot perform bitwise XOR on booleans"
                            ))
                        }
                        (v1, v2) => {
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "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()
                            ))
                        }
                    }
                }
                0x0D => { // BITOR
                0x0D => {
                    // BITOR
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => {
                            if v1.fract() != 0.0 {
                                return Err(vm_error!(self, RuntimeErrorKind::WrongType, "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(vm_error!(self, RuntimeErrorKind::WrongType, "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(vm_error!(self, RuntimeErrorKind::WrongType, "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(vm_error!(self, RuntimeErrorKind::WrongType, "Cannot perform bitwise OR on booleans"))
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Cannot perform bitwise OR on booleans"
                            ))
                        }
                        (v1, v2) => {
                            return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "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()
                            ))
                        }
                    }
                }


@@ 269,73 448,177 @@ impl VM {
                // OR
                0x14 => match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                    (Num(_v1), Num(_v2)) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"||\" on non-boolean numbers"))
                        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"))
                    },
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"||\" on non-boolean strings"
                        ))
                    }
                    (Bool(v1), Bool(v2)) => self.stack.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")),
                    (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) => {
                        return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "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
                0x15 => match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                    (Num(_v1), Num(_v2)) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"&&\" on non-boolean numbers"))
                        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"))
                    },
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"&&\" on non-boolean strings"
                        ))
                    }
                    (Bool(v1), Bool(v2)) => self.stack.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")),
                    (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) => {
                        return Err(vm_error!(self, RuntimeErrorKind::MismatchedTypes, "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
                0x16 => match self.stack.pop().unwrap() {
                    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"
                        ))
                    }
                    Num(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean number"))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"!\" on non-boolean number"
                        ))
                    }
                    Str(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean string"))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"!\" on non-boolean string"
                        ))
                    }
                    Bool(v1) => self.stack.push(Bool(!v1)),
                    List(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean list"))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"!\" on non-boolean list"
                        ))
                    }
                    Fun(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean function"))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"!\" on non-boolean function"
                        ))
                    }
                    Ref(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean reference"))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"!\" on non-boolean reference"
                        ))
                    }
                }
                },
                // NEG
                0x17 => match self.stack.pop().unwrap() {
                    Null => { return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number value null")) }
                    Null => {
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"-\" on non-number value null"
                        ))
                    }
                    Num(v1) => self.stack.push(Num(-v1)),
                    Str(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number string"))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"-\" on non-number string"
                        ))
                    }
                    Bool(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number boolean"))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"-\" on non-number boolean"
                        ))
                    }
                    List(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-number list"))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"!\" on non-number list"
                        ))
                    }
                    Fun(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number function"))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"-\" on non-number function"
                        ))
                    }
                    Ref(_v1) => {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number reference"))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to perform \"-\" on non-number reference"
                        ))
                    }
                }
                },
                // JMP
                0x18 => {
                    let loc = self.read_u16();


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



@@ 367,20 654,34 @@ impl VM {
                    let function = self.stack.pop().expect("Stack was empty in call");
                    if let Value::Fun(function) = function {
                        if num_args as usize != function.arity {
                            return Err(vm_error!(self, RuntimeErrorKind::IncorrectArity, "Function has {} arguments, {} provided", function.arity, num_args))
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::IncorrectArity,
                                "Function has {} arguments, {} provided",
                                function.arity,
                                num_args
                            ));
                        }
                        self.variables_offset = self.stack.len() - num_args as usize;
                        self.call_stack.push((self.unit_ptr, self.ip, self.variables_offset));
                        self.stack.insert(self.stack.len()-num_args as usize, Value::Fun(function));
                        self.call_stack
                            .push((self.unit_ptr, self.ip, self.variables_offset));
                        self.stack
                            .insert(self.stack.len() - num_args as usize, Value::Fun(function));
                        self.ip = 0;
                        self.unit_ptr = function.unit_ptr as usize;
                    } else {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to call non-function type {}", function.type_str()))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to call non-function type {}",
                            function.type_str()
                        ));
                    }
                }
                // RET
                0x1C => {
                    let (unit_ptr, ip, variables_offset) = self.call_stack.pop().expect("Call stack empty on RET");
                    let (unit_ptr, ip, variables_offset) =
                        self.call_stack.pop().expect("Call stack empty on RET");
                    let ret = self.stack.pop().expect("Missing return value");
                    self.stack = self.stack[..variables_offset].to_vec();
                    self.variables_offset = self.call_stack.last().expect("call stack empty").2;


@@ 393,7 694,10 @@ impl VM {
                // LIST
                0x1D => {
                    let len = self.read();
                    let list = self.stack.drain(self.stack.len()-len as usize..).collect();
                    let list = self
                        .stack
                        .drain(self.stack.len() - len as usize..)
                        .collect();
                    self.stack.push(Value::List(list));
                }
                // SCR


@@ 402,29 706,54 @@ impl VM {
                    let list = self.stack.pop().expect("stack empty on subscript list");
                    if let Value::Num(index) = index {
                        if index.fract() != 0.0 {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Subscript index must be an integer but found {}", index))
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Subscript index must be an integer but found {}",
                                index
                            ));
                        }
                        let index = index as usize;
                        if let Value::List(list) = list {
                            if index > list.len() {
                                return Err(vm_error!(self, RuntimeErrorKind::ArrayOutOfBounds, "List length is {} but found index {}", list.len(), index))
                                return Err(vm_error!(
                                    self,
                                    RuntimeErrorKind::ArrayOutOfBounds,
                                    "List length is {} but found index {}",
                                    list.len(),
                                    index
                                ));
                            }
                            self.stack.push(list[index].clone());
                        } else {
                            return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to subscript non-list type {}", list.type_str()))
                            return Err(vm_error!(
                                self,
                                RuntimeErrorKind::WrongType,
                                "Tried to subscript non-list type {}",
                                list.type_str()
                            ));
                        }
                    } else {
                        return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to use non-integer type {} to subscript list", index.type_str()))
                        return Err(vm_error!(
                            self,
                            RuntimeErrorKind::WrongType,
                            "Tried to use non-integer type {} to subscript list",
                            index.type_str()
                        ));
                    }
                }

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


@@ 460,7 789,8 @@ impl VM {
        self.ip += 1;
        byte_one | byte_two
    }
    pub fn find_line(&self) -> usize { // returns line # at ip
    pub fn find_line(&self) -> usize {
        // returns line # at ip
        let mut line_ip = 0;
        for (line, rep) in self.units[self.unit_ptr].lines.clone() {
            if line_ip + rep > self.ip {


@@ 474,7 804,12 @@ impl VM {

#[derive(Debug, Clone)]
pub enum Value {
    Null, Num(f32), Str(String), Bool(bool), List(Vec<Value>), Fun(Function),
    Null,
    Num(f32),
    Str(String),
    Bool(bool),
    List(Vec<Value>),
    Fun(Function),
    Ref(usize),
}



@@ 510,7 845,7 @@ impl ToString for Value {
                output
            }
            Fun(v) => format!("<function {}>", v.unit_ptr),
            Ref(v) => format!("<reference {}>", v)
            Ref(v) => format!("<reference {}>", v),
        }
    }
}

M crates/server/src/crafting/components.rs => crates/server/src/crafting/components.rs +9 -10
@@ 5,16 5,16 @@ use starkingdoms_common::PlanetType;

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum MaterialType {
    Plasma, // Sun
    Plasma,    // Sun
    Composite, // Mercury
    Sulfur, // Venus
    Silicon, // Moon
    Iron, // Mars
    Hydrogen, // Jupiter
    Helium, // Saturn
    Rubber, // Uranus
    Methane, // Neptune
    Ice, // Pluto
    Sulfur,    // Venus
    Silicon,   // Moon
    Iron,      // Mars
    Hydrogen,  // Jupiter
    Helium,    // Saturn
    Rubber,    // Uranus
    Methane,   // Neptune
    Ice,       // Pluto
}
impl TryFrom<PlanetType> for MaterialType {
    type Error = ();


@@ 50,4 50,3 @@ pub struct VarietyMaterialStorage {

#[derive(Component, Debug, Clone, Default)]
pub struct IsMining(pub bool);


M crates/server/src/crafting/mining.rs => crates/server/src/crafting/mining.rs +23 -8
@@ 1,16 1,25 @@
use crate::{module::component::Attach, planet::PlanetType};
use bevy::prelude::{Children, Entity, Query, Res};
use bevy_rapier2d::plugin::RapierContext;
use crate::{module::component::Attach, planet::PlanetType};

use super::components::{IsMining, VarietyMaterialStorage};
pub fn mine_materials(
    rapier_context: Res<RapierContext>,
    planet_query: Query<(&PlanetType, &Children)>,
    mut mineable_query: Query<(Entity, &mut Attach, Option<&IsMining>, Option<&mut VarietyMaterialStorage>)>,
    mut mineable_query: Query<(
        Entity,
        &mut Attach,
        Option<&IsMining>,
        Option<&mut VarietyMaterialStorage>,
    )>,
) {
    for (planet_type, children) in &planet_query {
        for (entity1, entity2, intersecting) in rapier_context.intersection_pairs_with(*children.first().unwrap()) {
            if !intersecting { continue }
        for (entity1, entity2, intersecting) in
            rapier_context.intersection_pairs_with(*children.first().unwrap())
        {
            if !intersecting {
                continue;
            }
            let other = if *children.first().unwrap() == entity1 {
                entity2
            } else {


@@ 22,7 31,7 @@ pub fn mine_materials(
            };
            let associated_player = match attach.associated_player {
                Some(e) => e,
                None => entity
                None => entity,
            };
            // is the module mining
            if let Some(mineable) = mineable {


@@ 33,7 42,9 @@ pub fn mine_materials(
                            if let Ok(material) = planet_type.0.try_into() {
                                match storage.materials.get_mut(&material) {
                                    Some(v) => *v += 1,
                                    None => { storage.materials.insert(material, 1); }
                                    None => {
                                        storage.materials.insert(material, 1);
                                    }
                                }
                            }
                        }


@@ 45,7 56,12 @@ pub fn mine_materials(
}
pub fn find_storage(
    player: Entity,
    mineable_query: &Query<(Entity, &mut Attach, Option<&IsMining>, Option<&mut VarietyMaterialStorage>)>,
    mineable_query: &Query<(
        Entity,
        &mut Attach,
        Option<&IsMining>,
        Option<&mut VarietyMaterialStorage>,
    )>,
) -> Option<Entity> {
    for (entity, attach, _, storage) in mineable_query.iter() {
        if let Some(storage) = storage {


@@ 65,7 81,6 @@ pub fn find_storage(
                return Some(entity);
            }
        }

    }
    return None;
}

M crates/server/src/module/mod.rs => crates/server/src/module/mod.rs +5 -5
@@ 11,8 11,7 @@ use starkingdoms_common::proto_transform;

use crate::ws::PacketMessageConvert;
use crate::{
    capacity, config::StkConfig, part, planet::PlanetType, player::component::Player,
    ws::WsEvent,
    capacity, config::StkConfig, part, planet::PlanetType, player::component::Player, ws::WsEvent,
};
use starkingdoms_common::PartType as c_PartType;
use starkingdoms_common::PlanetType as c_PlanetType;


@@ 123,7 122,7 @@ pub fn detach_recursive(
    energy += capacity!(*part_type);
    commands.entity(entity).remove::<Attach>();
    flags.attached = false;
    if *part_type == c_PartType::LandingThrusterSuspension .into(){
    if *part_type == c_PartType::LandingThrusterSuspension.into() {
        let parent = attach.parent.unwrap();
        let parent_attach = attached_query.get(parent).unwrap().3;
        commands.entity(parent).insert(LooseAttach {


@@ 565,7 564,8 @@ fn convert_modules_recursive(
                        });
                    attach.children[2] = Some(suspension.id());

                    increase_capacity_by += part!(c_PartType::LandingThruster.into()).energy_capacity;
                    increase_capacity_by +=
                        part!(c_PartType::LandingThruster.into()).energy_capacity;

                    let packet = Packet::DespawnPart { id: child.index() };



@@ 652,7 652,7 @@ pub fn break_modules(
        }
        let handle = match joints.get(&entity) {
            Some(handle) => handle,
            None => continue
            None => continue,
        };
        let joint = rapier_context.impulse_joints.get(*handle).unwrap();
        if joint.impulses.magnitude() > 2.0 {

M crates/server/src/module/save.rs => crates/server/src/module/save.rs +5 -2
@@ 5,8 5,11 @@ use bevy_rapier2d::prelude::*;
use starkingdoms_common::{packet::Packet, PartType as c_PartType, SaveModule};

use crate::{
    capacity, mass, planet::PlanetType, player::component::Player, ws::{PacketMessageConvert, WsEvent}, Attach, CanAttach,
    LooseAttach, PartBundle, PartFlags, PartType,
    capacity, mass,
    planet::PlanetType,
    player::component::Player,
    ws::{PacketMessageConvert, WsEvent},
    Attach, CanAttach, LooseAttach, PartBundle, PartFlags, PartType,
};

pub fn load_savefile(

M crates/server/src/planet.rs => crates/server/src/planet.rs +57 -19
@@ 29,7 29,9 @@ pub fn spawn_planets(mut commands: Commands) {
        .insert(ReadMassProperties::default())
        .with_children(|children| {
            children
                .spawn(Collider::ball(planet!(PlanetType(c_PlanetType::Sun)).size + 0.3))
                .spawn(Collider::ball(
                    planet!(PlanetType(c_PlanetType::Sun)).size + 0.3,
                ))
                .insert(ActiveEvents::COLLISION_EVENTS)
                .insert(Sensor);
        })


@@ 40,14 42,18 @@ pub fn spawn_planets(mut commands: Commands) {
            planet_type: PlanetType(c_PlanetType::Mercury),
            transform: TransformBundle::from(mercury_pos),
        })
        .insert(Collider::ball(planet!(PlanetType(c_PlanetType::Mercury)).size))
        .insert(Collider::ball(
            planet!(PlanetType(c_PlanetType::Mercury)).size,
        ))
        .insert(AdditionalMassProperties::Mass(
            planet!(PlanetType(c_PlanetType::Mercury)).mass,
        ))
        .insert(ReadMassProperties::default())
        .with_children(|children| {
            children
                .spawn(Collider::ball(planet!(PlanetType(c_PlanetType::Mercury)).size + 0.3))
                .spawn(Collider::ball(
                    planet!(PlanetType(c_PlanetType::Mercury)).size + 0.3,
                ))
                .insert(ActiveEvents::COLLISION_EVENTS)
                .insert(Sensor);
        })


@@ 58,14 64,18 @@ pub fn spawn_planets(mut commands: Commands) {
            planet_type: PlanetType(c_PlanetType::Venus),
            transform: TransformBundle::from(venus_pos),
        })
        .insert(Collider::ball(planet!(PlanetType(c_PlanetType::Venus)).size))
        .insert(Collider::ball(
            planet!(PlanetType(c_PlanetType::Venus)).size,
        ))
        .insert(AdditionalMassProperties::Mass(
            planet!(PlanetType(c_PlanetType::Venus)).mass,
        ))
        .insert(ReadMassProperties::default())
        .with_children(|children| {
            children
                .spawn(Collider::ball(planet!(PlanetType(c_PlanetType::Venus)).size + 0.3))
                .spawn(Collider::ball(
                    planet!(PlanetType(c_PlanetType::Venus)).size + 0.3,
                ))
                .insert(ActiveEvents::COLLISION_EVENTS)
                .insert(Sensor);
        })


@@ 76,14 86,18 @@ pub fn spawn_planets(mut commands: Commands) {
            planet_type: PlanetType(c_PlanetType::Earth),
            transform: TransformBundle::from(earth_pos),
        })
        .insert(Collider::ball(planet!(PlanetType(c_PlanetType::Earth)).size))
        .insert(Collider::ball(
            planet!(PlanetType(c_PlanetType::Earth)).size,
        ))
        .insert(AdditionalMassProperties::Mass(
            planet!(PlanetType(c_PlanetType::Earth)).mass,
        ))
        .insert(ReadMassProperties::default())
        .with_children(|children| {
            children
                .spawn(Collider::ball(planet!(PlanetType(c_PlanetType::Earth)).size + 0.3))
                .spawn(Collider::ball(
                    planet!(PlanetType(c_PlanetType::Earth)).size + 0.3,
                ))
                .insert(ActiveEvents::COLLISION_EVENTS)
                .insert(Sensor);
        })


@@ 101,7 115,9 @@ pub fn spawn_planets(mut commands: Commands) {
        .insert(ReadMassProperties::default())
        .with_children(|children| {
            children
                .spawn(Collider::ball(planet!(PlanetType(c_PlanetType::Moon)).size + 0.1))
                .spawn(Collider::ball(
                    planet!(PlanetType(c_PlanetType::Moon)).size + 0.1,
                ))
                .insert(ActiveEvents::COLLISION_EVENTS)
                .insert(Sensor);
        })


@@ 119,7 135,9 @@ pub fn spawn_planets(mut commands: Commands) {
        .insert(ReadMassProperties::default())
        .with_children(|children| {
            children
                .spawn(Collider::ball(planet!(PlanetType(c_PlanetType::Mars)).size + 0.1))
                .spawn(Collider::ball(
                    planet!(PlanetType(c_PlanetType::Mars)).size + 0.1,
                ))
                .insert(ActiveEvents::COLLISION_EVENTS)
                .insert(Sensor);
        })


@@ 130,14 148,18 @@ pub fn spawn_planets(mut commands: Commands) {
            planet_type: PlanetType(c_PlanetType::Jupiter),
            transform: TransformBundle::from(jupiter_pos),
        })
        .insert(Collider::ball(planet!(PlanetType(c_PlanetType::Jupiter)).size))
        .insert(Collider::ball(
            planet!(PlanetType(c_PlanetType::Jupiter)).size,
        ))
        .insert(AdditionalMassProperties::Mass(
            planet!(PlanetType(c_PlanetType::Jupiter)).mass,
        ))
        .insert(ReadMassProperties::default())
        .with_children(|children| {
            children
                .spawn(Collider::ball(planet!(PlanetType(c_PlanetType::Jupiter)).size + 0.1))
                .spawn(Collider::ball(
                    planet!(PlanetType(c_PlanetType::Jupiter)).size + 0.1,
                ))
                .insert(ActiveEvents::COLLISION_EVENTS)
                .insert(Sensor);
        })


@@ 148,14 170,18 @@ pub fn spawn_planets(mut commands: Commands) {
            planet_type: PlanetType(c_PlanetType::Saturn),
            transform: TransformBundle::from(saturn_pos),
        })
        .insert(Collider::ball(planet!(PlanetType(c_PlanetType::Saturn)).size))
        .insert(Collider::ball(
            planet!(PlanetType(c_PlanetType::Saturn)).size,
        ))
        .insert(AdditionalMassProperties::Mass(
            planet!(PlanetType(c_PlanetType::Saturn)).mass,
        ))
        .insert(ReadMassProperties::default())
        .with_children(|children| {
            children
                .spawn(Collider::ball(planet!(PlanetType(c_PlanetType::Saturn)).size + 0.1))
                .spawn(Collider::ball(
                    planet!(PlanetType(c_PlanetType::Saturn)).size + 0.1,
                ))
                .insert(ActiveEvents::COLLISION_EVENTS)
                .insert(Sensor);
        })


@@ 166,14 192,18 @@ pub fn spawn_planets(mut commands: Commands) {
            planet_type: PlanetType(c_PlanetType::Uranus),
            transform: TransformBundle::from(uranus_pos),
        })
        .insert(Collider::ball(planet!(PlanetType(c_PlanetType::Uranus)).size))
        .insert(Collider::ball(
            planet!(PlanetType(c_PlanetType::Uranus)).size,
        ))
        .insert(AdditionalMassProperties::Mass(
            planet!(PlanetType(c_PlanetType::Uranus)).mass,
        ))
        .insert(ReadMassProperties::default())
        .with_children(|children| {
            children
                .spawn(Collider::ball(planet!(PlanetType(c_PlanetType::Uranus)).size + 0.1))
                .spawn(Collider::ball(
                    planet!(PlanetType(c_PlanetType::Uranus)).size + 0.1,
                ))
                .insert(ActiveEvents::COLLISION_EVENTS)
                .insert(Sensor);
        })


@@ 184,14 214,18 @@ pub fn spawn_planets(mut commands: Commands) {
            planet_type: PlanetType(c_PlanetType::Neptune),
            transform: TransformBundle::from(neptune_pos),
        })
        .insert(Collider::ball(planet!(PlanetType(c_PlanetType::Neptune)).size))
        .insert(Collider::ball(
            planet!(PlanetType(c_PlanetType::Neptune)).size,
        ))
        .insert(AdditionalMassProperties::Mass(
            planet!(PlanetType(c_PlanetType::Neptune)).mass,
        ))
        .insert(ReadMassProperties::default())
        .with_children(|children| {
            children
                .spawn(Collider::ball(planet!(PlanetType(c_PlanetType::Neptune)).size + 0.1))
                .spawn(Collider::ball(
                    planet!(PlanetType(c_PlanetType::Neptune)).size + 0.1,
                ))
                .insert(ActiveEvents::COLLISION_EVENTS)
                .insert(Sensor);
        })


@@ 202,14 236,18 @@ pub fn spawn_planets(mut commands: Commands) {
            planet_type: PlanetType(c_PlanetType::Pluto),
            transform: TransformBundle::from(pluto_pos),
        })
        .insert(Collider::ball(planet!(PlanetType(c_PlanetType::Pluto)).size))
        .insert(Collider::ball(
            planet!(PlanetType(c_PlanetType::Pluto)).size,
        ))
        .insert(AdditionalMassProperties::Mass(
            planet!(PlanetType(c_PlanetType::Pluto)).mass,
        ))
        .insert(ReadMassProperties::default())
        .with_children(|children| {
            children
                .spawn(Collider::ball(planet!(PlanetType(c_PlanetType::Pluto)).size + 0.1))
                .spawn(Collider::ball(
                    planet!(PlanetType(c_PlanetType::Pluto)).size + 0.1,
                ))
                .insert(ActiveEvents::COLLISION_EVENTS)
                .insert(Sensor);
        })

M crates/server/src/player/client_login.rs => crates/server/src/player/client_login.rs +16 -7
@@ 6,13 6,21 @@ use hmac::{Hmac, Mac};
use jwt::VerifyWithKey;
use rand::Rng;
use sha2::Sha256;
use starkingdoms_common::{packet::{MessageType, Packet, Part, Planet, ProtoPartFlags}, proto_part_flags, proto_transform, unpack_savefile, PartType as c_PartType};
use starkingdoms_common::{
    packet::{MessageType, Packet, Part, Planet, ProtoPartFlags},
    proto_part_flags, proto_transform, unpack_savefile, PartType as c_PartType,
};

use crate::{
    config::StkConfig, crafting::components::{IsMining, VarietyMaterialStorage}, module::{
    config::StkConfig,
    crafting::components::{IsMining, VarietyMaterialStorage},
    module::{
        component::{Attach, CanAttach, LooseAttach, PartBundle, PartFlags, PartType},
        save::load_savefile,
    }, planet::PlanetType, ws::{PacketMessageConvert, WsEvent}, AppKeys, UserToken, CLIENT_SCALE
    },
    planet::PlanetType,
    ws::{PacketMessageConvert, WsEvent},
    AppKeys, UserToken, CLIENT_SCALE,
};

use super::component::{Input, Player};


@@ 232,9 240,7 @@ pub fn packet_stream(
    transform: Transform,
) {
    // response in the handshake
    let packet = Packet::LoginResponse {
        id: index,
    };
    let packet = Packet::LoginResponse { id: index };
    event_queue.push(WsEvent::Send {
        to: *from,
        message: packet.into_message(),


@@ 324,7 330,10 @@ pub fn packet_stream(
        },
    ));
    for part in parts {
        let packet = Packet::SpawnPart { id: part.0, part: part.1 };
        let packet = Packet::SpawnPart {
            id: part.0,
            part: part.1,
        };
        event_queue.push(WsEvent::Send {
            to: *from,
            message: packet.into_message(),

M crates/server/src/player/mod.rs => crates/server/src/player/mod.rs +15 -4
@@ 8,10 8,18 @@ use send_message::send_message;
use starkingdoms_common::{packet::Packet, PartType as c_PartType};

use crate::{
    config::StkConfig, crafting::components::IsMining, err_or_cont, mathutil::rot2d, module::{
    config::StkConfig,
    crafting::components::IsMining,
    err_or_cont,
    mathutil::rot2d,
    module::{
        component::{Attach, CanAttach, LooseAttach, PartFlags, PartType},
        PART_HALF_SIZE,
    }, part, planet::PlanetType, ws::{PacketMessageConvert, WsEvent}, AppKeys, CLIENT_SCALE
    },
    part,
    planet::PlanetType,
    ws::{PacketMessageConvert, WsEvent},
    AppKeys, CLIENT_SCALE,
};

pub mod client_login;


@@ 294,12 302,15 @@ pub fn player_input_update(

        // process each thruster on hearty
        for (force_multiplier, x_offset, y_offset) in thrusters {
            if force_multiplier != 0.0 && player.energy >= part!(c_PartType::Hearty.into()).thruster_energy {
            if force_multiplier != 0.0
                && player.energy >= part!(c_PartType::Hearty.into()).thruster_energy
            {
                player.energy -= part!(c_PartType::Hearty.into()).thruster_energy;
                let thruster_pos_uncast = vec2(x_offset, y_offset);
                let thruster_pos_cast =
                    rot2d(thruster_pos_uncast, rot) + transform.translation.xy();
                let thruster_force = force_multiplier * part!(c_PartType::Hearty.into()).thruster_force;
                let thruster_force =
                    force_multiplier * part!(c_PartType::Hearty.into()).thruster_force;
                let thruster_vec = vec2(-thruster_force * rot.sin(), thruster_force * rot.cos());
                let thruster_force = ExternalForce::at_point(
                    thruster_vec,

M crates/server/src/player/packet.rs => crates/server/src/player/packet.rs +4 -1
@@ 1,5 1,8 @@
use bevy::{ecs::event::ManualEventReader, prelude::*};
use starkingdoms_common::{packet::{Packet, Part, Planet}, proto_part_flags, proto_transform};
use starkingdoms_common::{
    packet::{Packet, Part, Planet},
    proto_part_flags, proto_transform,
};

use crate::{
    module::component::{Attach, PartFlags, PartType},

M crates/server/src/player/player_mouse_input.rs => crates/server/src/player/player_mouse_input.rs +9 -5
@@ 2,9 2,15 @@ use bevy::{math::vec3, prelude::*};
use bevy_rapier2d::prelude::*;

use crate::{
    crafting::components::IsMining, module::component::{Attach, CanAttach, LooseAttach, PartFlags, PartType}, planet::PlanetType, ws::{PacketMessageConvert, WsEvent}
    crafting::components::IsMining,
    module::component::{Attach, CanAttach, LooseAttach, PartFlags, PartType},
    planet::PlanetType,
    ws::{PacketMessageConvert, WsEvent},
};
use starkingdoms_common::{
    packet::{ButtonType, Packet},
    PartType as c_PartType,
};
use starkingdoms_common::{packet::{ButtonType, Packet}, PartType as c_PartType};

use super::component::Player;



@@ 187,9 193,7 @@ pub fn mouse_picking(
            if *button == ButtonType::Right {
                send_events.push(WsEvent::Send {
                    to: q_player.addr,
                    message: Packet::OpenCraftingUi {
                        id: entity.index(),
                    }.into_message(),
                    message: Packet::OpenCraftingUi { id: entity.index() }.into_message(),
                });
                // toggle mining
                /*if let Ok(mut is_mining) = mining_query.get_mut(entity) {

M crates/xtask/src/main.rs => crates/xtask/src/main.rs +46 -27
@@ 1,3 1,5 @@
use colored::Colorize;
use notify::{Event, EventKind, RecursiveMode, Watcher};
use std::env::{args, var};
use std::fs;
use std::io::Cursor;


@@ 7,8 9,6 @@ use std::sync::mpsc;
use std::sync::mpsc::TryRecvError;
use std::thread::sleep;
use std::time::Duration;
use colored::Colorize;
use notify::{Event, EventKind, RecursiveMode, Watcher};
use tiny_http::{Response, Server, StatusCode};
use wasm_pack::command::build::{BuildOptions, Target};
use wasm_pack::command::run_wasm_pack;


@@ 56,11 56,18 @@ fn build_client() -> anyhow::Result<()> {
fn try_build_client() -> bool {
    match build_client() {
        Ok(_) => {
            println!("{} -- Client package built successfully", "✓ Success".green().bold());
            println!(
                "{} -- Client package built successfully",
                "✓ Success".green().bold()
            );
            true
        },
        }
        Err(e) => {
            eprintln!("{} -- Client package failed to build: {}", "✗ Failed".red().bold(), e);
            eprintln!(
                "{} -- Client package failed to build: {}",
                "✗ Failed".red().bold(),
                e
            );
            false
        }
    }


@@ 75,15 82,21 @@ fn start_server() {
        if path == Path::new("/") {
            path = Path::new("/index.html");
        }
        

        let path = path.strip_prefix(Path::new("/")).unwrap();
        

        let full_path = workspace_dir().join("crates/client").join(path);
        

        let content = match fs::read(full_path.clone()) {
            Ok(r) => r,
            Err(_) => {
                let _ = req.respond(Response::new(StatusCode::from(404), vec![], Cursor::new(vec![]), None, None));
                let _ = req.respond(Response::new(
                    StatusCode::from(404),
                    vec![],
                    Cursor::new(vec![]),
                    None,
                    None,
                ));
                continue;
            }
        };


@@ 100,17 113,16 @@ fn start_server() {
                &b"application/wasm"[..]
            } else {
                &b"application/octet-stream"[..]
            }
        ).unwrap();
        
            },
        )
        .unwrap();

        let response = Response::new(
            StatusCode::from(200),
            vec![
                header
            ],
            vec![header],
            Cursor::new(content),
            Some(len),
            None
            None,
        );
        let _ = req.respond(response);
    }


@@ 125,7 137,7 @@ fn main() {
            if !try_build_client() {
                exit(1);
            }
        },
        }
        "watch" | "serve" => {
            let serve = subcommand == "serve";



@@ 144,7 156,9 @@ fn main() {

            // Add a path to be watched. All files and directories at that path and
            // below will be monitored for changes.
            watcher.watch(&workspace_dir().join("crates"), RecursiveMode::Recursive).unwrap();
            watcher
                .watch(&workspace_dir().join("crates"), RecursiveMode::Recursive)
                .unwrap();
            println!("{}", "[Watch] 🛈 Watching for file changes".blue().bold());
            // Block forever, printing out events as they come in



@@ 170,8 184,8 @@ fn main() {
                        } else {
                            rx.recv().unwrap()
                        }
                    },
                    Err(TryRecvError::Disconnected) => panic!("{:?}", TryRecvError::Disconnected)
                    }
                    Err(TryRecvError::Disconnected) => panic!("{:?}", TryRecvError::Disconnected),
                };

                match res {


@@ 179,19 193,24 @@ fn main() {
                        if let EventKind::Modify(_) = event.kind {
                            let has_non_generated_update = false;
                            for path in &event.paths {
                                if !path.to_str().unwrap().contains("client/pkg") && !path.to_str().unwrap().ends_with("~") {
                                if !path.to_str().unwrap().contains("client/pkg")
                                    && !path.to_str().unwrap().ends_with("~")
                                {
                                    needs_rebuild = true;
                                }
                            }
                        }

                    },
                    }
                    Err(e) => {
                        eprintln!("{} -- Error watching for files: {}", "[Watch] ✗ Error".red().bold(), e);
                    },
                        eprintln!(
                            "{} -- Error watching for files: {}",
                            "[Watch] ✗ Error".red().bold(),
                            e
                        );
                    }
                }
            }
        },
        _ => panic!("unsupported command")
        }
        _ => panic!("unsupported command"),
    }
}