use std::{collections::HashMap, fs, process, vec}; use crate::{errors::{create_error, print_error, ErrorSubType, ErrorType}, lexer::lex, parser::{parse, ASTPart}, Context}; const ASXVERSION: [u8; 3] = [1,0,0]; #[derive(Debug, Clone)] pub struct Operation { pub opcode: u8, pub arg1: Option, pub arg2: Option, pub arg3: Option, pub pos: u32, } #[derive(Debug, Clone)] struct RegisterState { id: u8, used: bool, variable: u32, last_used: usize, } #[derive(Debug, Clone)] struct AllocateResult { register: u8, unbind_before: bool, } #[derive(Debug, Clone)] struct Compiled { operations: Vec, variables: Vec, strings: HashMap, functions: HashMap, try_catch: Option } #[derive(Debug, Clone)] struct Variable { name: String, id: u32, start: usize, end: usize, no_end: bool } #[derive(Debug, Clone)] struct PrevFunc { variables: Vec, previous: Option>, } fn get_register_by_id(registers: &Vec, id: u8) -> RegisterState { for register in registers { if register.id == id { return register.clone(); } } return RegisterState { id: 0, used: false, variable: 0, last_used: 0, }; } fn get_register_by_variable(registers: &Vec, variable: Variable) -> RegisterState { for register in registers { if register.variable == variable.id { return register.clone(); } } return RegisterState { id: 0, used: false, variable: 0, last_used: 0, }; } fn set_register(registers: &mut Vec, new_state: RegisterState) { if new_state.id == 0 { return; } for i in 0..registers.len() { if registers[i].id == new_state.id { registers[i] = new_state; return; } } } fn allocate_register(registers: &Vec) -> AllocateResult { for register in registers { if register.id != 0 && !register.used { return AllocateResult { register: register.id, unbind_before: false, }; } } //Find the one that wasn't used for the longest time let mut oldest: usize = 0; let mut oldest_register: u8 = 0; let mut oldest_temp: usize = 0; let mut oldest_temp_register: u8 = 0; let mut tempc = 0; for register in registers { if ((register.last_used < oldest) || (oldest == 0)) && register.id != 0 { oldest = register.last_used; oldest_register = register.id; } if ((register.last_used < oldest_temp) || (oldest_temp == 0)) && register.id != 0 && register.variable == 0 { oldest_temp = register.last_used; oldest_temp_register = register.id; } if register.id != 0 && register.variable == 0 { tempc += 1; } } if oldest_temp_register != 0 && tempc > 3 { return AllocateResult { register: oldest_temp_register, unbind_before: false, }; } return AllocateResult { register: oldest_register, unbind_before: true, }; } fn garbage_collect_registers(_registers: &mut Vec) { return; /*for register in registers { if register.used && register.variable == 0 { register.used = false; register.last_used = 0; } }*/ } fn get_variable_by_name(variables: &Vec, name: &str, position: usize, traceback: &PrevFunc) -> Option { for variable in variables { if variable.name == name { if variable.start <= position && (variable.no_end || variable.end >= position) { return Some(variable.clone()); } } } let mut cur = traceback.clone(); loop { for variable in &cur.variables { if variable.name == name { return Some(variable.clone()); } } if let Some(prev) = &cur.previous { cur = *prev.clone(); } else { break; } } None } fn do_ast_op(ast_op: ASTPart, op_count: &mut usize, ops: &mut Vec, variables: &mut Vec, next_var_id: &mut u32, strings: &mut HashMap, next_string_id: &mut u32, functions: &mut HashMap, next_function_id: &mut u32, registers: &mut Vec, ctx: &Context, traceback: &PrevFunc) -> u8 { *op_count += 1; match ast_op { ASTPart::Number(num) => { let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: num.pos as u32 }); } ops.push(Operation { opcode: 3, arg1: Some(reg.register), arg2: Some(num.value), arg3: None, pos: num.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: 0, last_used: *op_count }); return reg.register; }, ASTPart::String(str) => { let str_id = *next_string_id; strings.insert(str_id, str.value); *next_string_id += 1; let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: str.pos as u32 }); } ops.push(Operation { opcode: 1, arg1: Some(reg.register), arg2: Some(str_id as f64), arg3: None, pos: str.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: 0, last_used: *op_count }); return reg.register; }, ASTPart::Boolean(bool) => { let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: bool.pos as u32 }); } ops.push(Operation { opcode: 4, arg1: Some(reg.register), arg2: Some((bool.value as i64) as f64), arg3: None, pos: bool.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: 0, last_used: *op_count }); return reg.register; }, ASTPart::Null(nul) => { let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: nul.pos as u32 }); } ops.push(Operation { opcode: 6, arg1: Some(reg.register), arg2: Some(0 as f64), arg3: None, pos: nul.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: 0, last_used: *op_count }); return reg.register; }, ASTPart::VarRead(var_read) => { if get_variable_by_name(variables, &var_read.variable, ops.len(), traceback).is_none() { let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: var_read.pos as u32 }); } let str_id = *next_string_id; strings.insert(str_id, var_read.variable.clone()); *next_string_id += 1; ops.push(Operation { opcode: 1, arg1: Some(reg.register), arg2: Some(str_id as f64), arg3: None, pos: var_read.pos as u32 }); ops.push(Operation { opcode: 30, arg1: Some(0), arg2: Some(reg.register as f64), arg3: Some(reg.register), pos: var_read.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: 0, last_used: *op_count }); return reg.register; } let reg = get_register_by_variable(registers, get_variable_by_name(variables, &var_read.variable, ops.len(), traceback).expect("Variable should exist")); if reg.id == 0 { let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: var_read.pos as u32 }); } ops.push(Operation { opcode: 2, arg1: Some(reg.register), arg2: Some(get_variable_by_name(variables, &var_read.variable, ops.len(), traceback).expect("Variable should exist").id as f64), arg3: None, pos: var_read.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: get_variable_by_name(variables, &var_read.variable, ops.len(), traceback).expect("Variable should exist").id, last_used: *op_count }); return reg.register; } else { return reg.id; } }, ASTPart::Function(func) => { let func_id = *next_function_id; let self_tb = PrevFunc { variables: variables.clone(), previous: Some(Box::new(traceback.clone())), }; functions.insert(func_id, compile_function(func.body, Some(func.args), registers, next_var_id, ctx, &self_tb, None)); *next_function_id += 1; let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: func.pos as u32 }); } ops.push(Operation { opcode: 5, arg1: Some(reg.register), arg2: Some(func_id as f64), arg3: None, pos: func.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: 0, last_used: *op_count }); return reg.register; }, ASTPart::Call(call) => { for arg in call.args { let arg_reg = do_ast_op(arg, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); ops.push(Operation { opcode: 28, arg1: Some(arg_reg), arg2: None, arg3: None, pos: call.pos as u32 }); let reg = get_register_by_id(registers, arg_reg); if reg.variable == 0 { set_register(registers, RegisterState { id: arg_reg, used: false, variable: 0, last_used: 0 }); } } let func = do_ast_op(*call.function, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); let ret_reg = allocate_register(registers); if ret_reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(ret_reg.register), arg2: None, arg3: None, pos: call.pos as u32 }); } ops.push(Operation { opcode: 27, arg1: Some(func), arg2: Some(ret_reg.register as f64), arg3: None, pos: call.pos as u32 }); set_register(registers, RegisterState { id: ret_reg.register, used: true, variable: 0, last_used: *op_count }); return ret_reg.register; }, ASTPart::If(if_part) => { let condition_reg = do_ast_op(*if_part.condition, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); ops.push(Operation { opcode: 24, arg1: Some(condition_reg), arg2: Some(condition_reg as f64), arg3: None, pos: if_part.pos as u32 }); //Update the lastif variable if get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).is_none() { variables.push(Variable { name: String::from("__LASTIF"), id: *next_var_id, start: ops.len(), end: 0, no_end: true }); *next_var_id += 1; } ops.push(Operation { opcode: 7, arg1: Some(condition_reg), arg2: Some(get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").id as f64), arg3: None, pos: if_part.pos as u32 }); set_register(registers, RegisterState { id: condition_reg, used: true, variable: get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").id, last_used: *op_count }); let op_placeholder = ops.len(); ops.push(Operation { opcode: 0, arg1: None, arg2: None, arg3: None, pos: if_part.pos as u32 }); let mut fake_vars: Vec = vec![]; let tb = PrevFunc { variables: variables.clone(), previous: Some(Box::new(traceback.clone())), }; for if_op in if_part.body { do_ast_op(if_op, op_count, ops, &mut fake_vars, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, &tb); } for fake_var in fake_vars { variables.push(Variable { name: fake_var.name, id: fake_var.id, start: fake_var.start, end: ops.len()-1, no_end: false }); } ops[op_placeholder] = Operation { opcode: 26, arg1: Some(condition_reg), arg2: Some(ops.len() as f64), arg3: None, pos: if_part.pos as u32, }; //Unbind the lastif variable if get_register_by_id(registers, condition_reg).variable == get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").id { ops.push(Operation { opcode: 8, arg1: Some(condition_reg), arg2: None, arg3: None, pos: if_part.pos as u32 }); set_register(registers, RegisterState { id: condition_reg, used: false, variable: 0, last_used: 0 }); } garbage_collect_registers(registers); }, ASTPart::Else(else_part) => { if get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).is_none() { let err = create_error(&format!("Else used without an if statement before it"), else_part.pos, ErrorType::SemanticError, ErrorSubType::ElseWithoutIf, ctx); print_error(&err); process::exit(1); } let reg = get_register_by_variable(registers, get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").clone()); let else_condition_reg; if reg.id == 0 { let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: else_part.pos as u32 }); } ops.push(Operation { opcode: 2, arg1: Some(reg.register), arg2: Some(get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").id as f64), arg3: None, pos: else_part.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").id, last_used: *op_count }); else_condition_reg = reg.register; } else { else_condition_reg = reg.id; } ops.push(Operation { opcode: 24, arg1: Some(else_condition_reg), arg2: Some(else_condition_reg as f64), arg3: None, pos: else_part.pos as u32 }); let op_placeholder = ops.len(); ops.push(Operation { opcode: 0, arg1: None, arg2: None, arg3: None, pos: else_part.pos as u32 }); let tb = PrevFunc { variables: variables.clone(), previous: Some(Box::new(traceback.clone())), }; let mut fake_vars: Vec = vec![]; for else_op in else_part.body { do_ast_op(else_op, op_count, ops, &mut fake_vars, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, &tb); } for fake_var in fake_vars { variables.push(Variable { name: fake_var.name, id: fake_var.id, start: fake_var.start, end: ops.len()-1, no_end: false }); } ops[op_placeholder] = Operation { opcode: 26, arg1: Some(else_condition_reg), arg2: Some(ops.len() as f64), arg3: None, pos: else_part.pos as u32, }; //Unbind the lastif variable if get_register_by_id(registers, else_condition_reg).variable == get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").id { ops.push(Operation { opcode: 8, arg1: Some(else_condition_reg), arg2: None, arg3: None, pos: else_part.pos as u32 }); set_register(registers, RegisterState { id: else_condition_reg, used: false, variable: 0, last_used: 0 }); } garbage_collect_registers(registers); }, ASTPart::ElseIf(elseif_part) => { if get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).is_none() { let err = create_error(&format!("Else if used without an if statement before it"), elseif_part.pos, ErrorType::SemanticError, ErrorSubType::ElseWithoutIf, ctx); print_error(&err); process::exit(1); } let reg = get_register_by_variable(registers, get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").clone()); let else_condition_reg; if reg.id == 0 { let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: elseif_part.pos as u32 }); } ops.push(Operation { opcode: 2, arg1: Some(reg.register), arg2: Some(get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").id as f64), arg3: None, pos: elseif_part.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").id, last_used: *op_count }); else_condition_reg = reg.register; } else { else_condition_reg = reg.id; } ops.push(Operation { opcode: 24, arg1: Some(else_condition_reg), arg2: Some(else_condition_reg as f64), arg3: None, pos: elseif_part.pos as u32 }); let condition_reg = do_ast_op(*elseif_part.condition, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); ops.push(Operation { opcode: 16, arg1: Some(else_condition_reg), arg2: Some(condition_reg as f64), arg3: Some(condition_reg), pos: elseif_part.pos as u32 }); ops.push(Operation { opcode: 24, arg1: Some(condition_reg), arg2: Some(condition_reg as f64), arg3: None, pos: elseif_part.pos as u32 }); //Update the lastif variable ops.push(Operation { opcode: 7, arg1: Some(condition_reg), arg2: Some(get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").id as f64), arg3: None, pos: elseif_part.pos as u32 }); set_register(registers, RegisterState { id: condition_reg, used: true, variable: get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").id, last_used: *op_count }); let op_placeholder = ops.len(); ops.push(Operation { opcode: 0, arg1: None, arg2: None, arg3: None, pos: elseif_part.pos as u32 }); let tb = PrevFunc { variables: variables.clone(), previous: Some(Box::new(traceback.clone())), }; let mut fake_vars: Vec = vec![]; for elseif_op in elseif_part.body { do_ast_op(elseif_op, op_count, ops, &mut fake_vars, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, &tb); } for fake_var in fake_vars { variables.push(Variable { name: fake_var.name, id: fake_var.id, start: fake_var.start, end: ops.len()-1, no_end: false }); } ops[op_placeholder] = Operation { opcode: 26, arg1: Some(condition_reg), arg2: Some(ops.len() as f64), arg3: None, pos: elseif_part.pos as u32 }; //Unbind the lastif variable if get_register_by_id(registers, condition_reg).variable == get_variable_by_name(variables, "__LASTIF", ops.len(), traceback).expect("__LASTIF should exist").id { ops.push(Operation { opcode: 8, arg1: Some(condition_reg), arg2: None, arg3: None, pos: elseif_part.pos as u32 }); set_register(registers, RegisterState { id: condition_reg, used: false, variable: 0, last_used: 0 }); } garbage_collect_registers(registers); }, ASTPart::While(while_part) => { let start = ops.len(); let condition_reg = do_ast_op(*while_part.condition, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); ops.push(Operation { opcode: 24, arg1: Some(condition_reg), arg2: Some(condition_reg as f64), arg3: None, pos: while_part.pos as u32 }); let op_placeholder = ops.len(); let mut breaks: Vec = vec![]; let mut continues: Vec = vec![]; ops.push(Operation { opcode: 0, arg1: None, arg2: None, arg3: None, pos: while_part.pos as u32 }); let tb = PrevFunc { variables: variables.clone(), previous: Some(Box::new(traceback.clone())), }; let mut fake_vars: Vec = vec![]; for while_op in while_part.body { match while_op { ASTPart::Break(_) => { breaks.push(ops.len()); ops.push(Operation { opcode: 0, arg1: None, arg2: None, arg3: None, pos: while_part.pos as u32 }); }, ASTPart::Continue(_) => { continues.push(ops.len()); ops.push(Operation { opcode: 0, arg1: None, arg2: None, arg3: None, pos: while_part.pos as u32 }); }, _ => { do_ast_op(while_op, op_count, ops, &mut fake_vars, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, &tb); } } } for fake_var in fake_vars { variables.push(Variable { name: fake_var.name, id: fake_var.id, start: fake_var.start, end: ops.len()-1, no_end: false }); } ops.push(Operation { opcode: 25, arg1: None, arg2: Some(start as f64), arg3: None, pos: while_part.pos as u32 }); ops[op_placeholder] = Operation { opcode: 26, arg1: Some(condition_reg), arg2: Some(ops.len() as f64), arg3: None, pos: while_part.pos as u32 }; for brk in breaks { ops[brk] = Operation { opcode: 25, arg1: None, arg2: Some(ops.len() as f64), arg3: None, pos: while_part.pos as u32 }; } for cont in continues { ops[cont] = Operation { opcode: 25, arg1: None, arg2: Some(start as f64), arg3: None, pos: while_part.pos as u32 }; } }, ASTPart::Break(brk) => { let err = create_error(&format!("Unexpected break outside of loop"), brk.pos, ErrorType::SemanticError, ErrorSubType::BreakContinueWithoutLoop, ctx); print_error(&err); process::exit(1); }, ASTPart::Continue(cont) => { let err = create_error(&format!("Unexpected continue outside of loop"), cont.pos, ErrorType::SemanticError, ErrorSubType::BreakContinueWithoutLoop, ctx); print_error(&err); process::exit(1); }, ASTPart::For(for_part) => { do_ast_op(*for_part.init, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); let start = ops.len(); let condition_reg = do_ast_op(*for_part.condition, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); ops.push(Operation { opcode: 24, arg1: Some(condition_reg), arg2: Some(condition_reg as f64), arg3: None, pos: for_part.pos as u32 }); let op_placeholder = ops.len(); let mut breaks: Vec = vec![]; let mut continues: Vec = vec![]; ops.push(Operation { opcode: 0, arg1: None, arg2: None, arg3: None, pos: for_part.pos as u32 }); let tb = PrevFunc { variables: variables.clone(), previous: Some(Box::new(traceback.clone())), }; let mut fake_vars: Vec = vec![]; for for_op in for_part.body { match for_op { ASTPart::Break(_) => { breaks.push(ops.len()); ops.push(Operation { opcode: 0, arg1: None, arg2: None, arg3: None, pos: for_part.pos as u32 }); }, ASTPart::Continue(_) => { continues.push(ops.len()); ops.push(Operation { opcode: 0, arg1: None, arg2: None, arg3: None, pos: for_part.pos as u32 }); }, _ => { do_ast_op(for_op, op_count, ops, &mut fake_vars, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, &tb); } } } for fake_var in fake_vars { variables.push(Variable { name: fake_var.name, id: fake_var.id, start: fake_var.start, end: ops.len()-1, no_end: false }); } do_ast_op(*for_part.update, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); ops.push(Operation { opcode: 25, arg1: None, arg2: Some(start as f64), arg3: None, pos: for_part.pos as u32 }); ops[op_placeholder] = Operation { opcode: 26, arg1: Some(condition_reg), arg2: Some(ops.len() as f64), arg3: None, pos: for_part.pos as u32 }; for brk in breaks { ops[brk] = Operation { opcode: 25, arg1: None, arg2: Some(ops.len() as f64), arg3: None, pos: for_part.pos as u32 }; } for cont in continues { ops[cont] = Operation { opcode: 25, arg1: None, arg2: Some(start as f64), arg3: None, pos: for_part.pos as u32 }; } }, ASTPart::Return(ret) => { let ret_reg = do_ast_op(*ret.value, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); ops.push(Operation { opcode: 29, arg1: Some(ret_reg), arg2: None, arg3: None, pos: ret.pos as u32 }); }, ASTPart::Operation(op) => { let left_reg = do_ast_op(*op.left, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); let right_reg = do_ast_op(*op.right, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: op.pos as u32 }); } let opcode = match op.operator.as_str() { "+" => 10, "-" => 11, "*" => 12, "/" => 13, "^" => 14, "%" => 15, "&" => 16, "|" => 17, "==" => 18, "!=" => 19, ">" => 20, ">=" => 21, "<" => 22, "<=" => 23, "!" => { ops.push(Operation { opcode: 24, arg1: Some(left_reg), arg2: Some(reg.register as f64), arg3: None, pos: op.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: 0, last_used: *op_count }); return reg.register; }, _ => { let err = create_error(&format!("Unknown operator `{}`", op.operator), op.pos, ErrorType::SyntaxError, ErrorSubType::UnknownOperation, ctx); print_error(&err); process::exit(1); }, }; ops.push(Operation { opcode, arg1: Some(left_reg), arg2: Some(right_reg as f64), arg3: Some(reg.register), pos: op.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: 0, last_used: *op_count }); return reg.register; }, ASTPart::VarUpdate(upd) => { if get_variable_by_name(variables, &upd.variable, ops.len(), traceback).is_none() { let err = create_error(&format!("Variable `{}` does not exist", upd.variable), upd.pos, ErrorType::SemanticError, ErrorSubType::VariableNotFound, ctx); print_error(&err); process::exit(1); } let value_reg = do_ast_op(*upd.value, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); let reg = get_register_by_variable(registers, get_variable_by_name(variables, &upd.variable, ops.len(), traceback).expect("Variable should exist").clone()); if reg.id != 0 { ops.push(Operation { opcode: 8, arg1: Some(reg.id), arg2: None, arg3: None, pos: upd.pos as u32 }); set_register(registers, RegisterState { id: reg.id, used: false, variable: 0, last_used: 0 }); } ops.push(Operation { opcode: 7, arg1: Some(value_reg), arg2: Some(get_variable_by_name(variables, &upd.variable, ops.len(), traceback).expect("Variable should exist").id as f64), arg3: None, pos: upd.pos as u32 }); set_register(registers, RegisterState { id: value_reg, used: true, variable: get_variable_by_name(variables, &upd.variable, ops.len(), traceback).expect("Variable should exist").id, last_used: *op_count }); garbage_collect_registers(registers); }, ASTPart::Assigment(asign) => { if get_variable_by_name(variables, &asign.variable, ops.len(), traceback).is_some() { let err = create_error(&format!("Variable `{}` already exists", asign.variable), asign.pos, ErrorType::SemanticError, ErrorSubType::VariableAlreadyExists, ctx); print_error(&err); process::exit(1); } let reg = do_ast_op(*asign.value, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); variables.push(Variable { name: asign.variable.clone(), id: *next_var_id, start: ops.len(), end: 0, no_end: true }); *next_var_id += 1; ops.push(Operation { opcode: 7, arg1: Some(reg), arg2: Some(get_variable_by_name(variables, &asign.variable, ops.len(), traceback).expect("Variable should exist").id as f64), arg3: None, pos: asign.pos as u32 }); set_register(registers, RegisterState { id: reg, used: true, variable: get_variable_by_name(variables, &asign.variable, ops.len(), traceback).expect("Variable should exist").id, last_used: *op_count }); garbage_collect_registers(registers); }, ASTPart::Table(tbl) => { let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: tbl.pos as u32 }); } ops.push(Operation { opcode: 6, arg1: Some(reg.register), arg2: Some(1 as f64), arg3: None, pos: tbl.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: 0, last_used: *op_count }); for val in tbl.values { let key_reg = do_ast_op(val.key, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); let value_reg = do_ast_op(val.value, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); ops.push(Operation { opcode: 31, arg1: Some(reg.register), arg2: Some(key_reg as f64), arg3: Some(value_reg), pos: tbl.pos as u32 }); } return reg.register; }, ASTPart::TableGet(tbl_get) => { let table_reg = do_ast_op(*tbl_get.table, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); let key_reg = do_ast_op(*tbl_get.key, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: tbl_get.pos as u32 }); } ops.push(Operation { opcode: 30, arg1: Some(table_reg), arg2: Some(key_reg as f64), arg3: Some(reg.register), pos: tbl_get.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: 0, last_used: *op_count }); return reg.register; }, ASTPart::TableSet(tbl_set) => { let table_reg = do_ast_op(*tbl_set.table, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); let key_reg = do_ast_op(*tbl_set.key, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); let value_reg = do_ast_op(*tbl_set.value, op_count, ops, variables, next_var_id, strings, next_string_id, functions, next_function_id, registers, ctx, traceback); ops.push(Operation { opcode: 31, arg1: Some(table_reg), arg2: Some(key_reg as f64), arg3: Some(value_reg), pos: tbl_set.pos as u32 }); }, ASTPart::Import(impr) => { let fi = fs::read_to_string(&impr.path); match fi { Ok(data) => { let imp_ctx = Context { file: String::from(&impr.path), raw_file: data.clone(), c_funcid: 0, known: true }; let self_tb = PrevFunc { variables: variables.clone(), previous: Some(Box::new(traceback.clone())), }; let lexed = lex(data, &imp_ctx); let ast = parse(lexed, &imp_ctx); let compiled = compile_function(ast, None, registers, next_var_id, &imp_ctx, &self_tb, None); functions.insert(*next_function_id, compiled); *next_function_id += 1; let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: impr.pos as u32 }); } ops.push(Operation { opcode: 5, arg1: Some(reg.register), arg2: Some((*next_function_id-1) as f64), arg3: None, pos: impr.pos as u32 }); ops.push(Operation { opcode: 27, arg1: Some(reg.register), arg2: Some(reg.register as f64), arg3: None, pos: impr.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: 0, last_used: *op_count }); return reg.register; }, Err(e) => { let err = create_error(&format!("Failed to read file `{}`: {}", impr.path, e), impr.pos, ErrorType::IOError, ErrorSubType::FileError, ctx); print_error(&err); process::exit(1); } } }, ASTPart::TryCatch(tc) => { let self_tb = PrevFunc { variables: variables.clone(), previous: Some(Box::new(traceback.clone())), }; let catch_f = compile_function(tc.catch_block.body, Some(tc.catch_block.args), registers, next_var_id, ctx, &self_tb, None); let mut try_f = compile_function(tc.try_block.body, None, registers, next_var_id, ctx, &self_tb, Some(0)); let mut nextt_f = 1; for (fid, _) in &try_f.functions { if fid >= &nextt_f { nextt_f = fid+1; } } try_f.functions.insert(nextt_f, catch_f); try_f.try_catch = Some(nextt_f); functions.insert(*next_function_id, try_f); *next_function_id += 1; let reg = allocate_register(registers); if reg.unbind_before { ops.push(Operation { opcode: 8, arg1: Some(reg.register), arg2: None, arg3: None, pos: tc.pos as u32 }); } ops.push(Operation { opcode: 5, arg1: Some(reg.register), arg2: Some((*next_function_id-1) as f64), arg3: None, pos: tc.pos as u32 }); ops.push(Operation { opcode: 27, arg1: Some(reg.register), arg2: None, arg3: None, pos: tc.pos as u32 }); set_register(registers, RegisterState { id: reg.register, used: true, variable: 0, last_used: *op_count }); }, _ => {} } return 0; } fn compile_function(ast: Vec, args: Option>, registers: &mut Vec, next_var_id: &mut u32, ctx: &Context, traceback: &PrevFunc, try_catch: Option) -> Compiled { let mut ops: Vec = vec![]; let mut variables: Vec = vec![]; let mut strings: HashMap = HashMap::new(); let mut next_string_id: u32 = 1; let mut functions: HashMap = HashMap::new(); let mut next_function_id: u32 = 1; let empty_tb = PrevFunc { variables: vec![], previous: None, }; match args { Some(arg_list) => { for arg in arg_list { if get_variable_by_name(&variables, &arg, 0, &empty_tb).is_some() { let err = create_error(&format!("Argument `{}` already exists", arg), 0, ErrorType::SemanticError, ErrorSubType::ArgumentDuplication, ctx); print_error(&err); process::exit(1); } variables.push( Variable { name: arg, id: *next_var_id, start: 0, end: 0, no_end: true }); *next_var_id += 1; } }, None => {} } let mut op_count = 0; for ast_op in ast { do_ast_op(ast_op, &mut op_count, &mut ops, &mut variables, next_var_id, &mut strings, &mut next_string_id, &mut functions, &mut next_function_id, registers, ctx, traceback); } ops.push(Operation { opcode: 29, arg1: Some(0), arg2: None, arg3: None, pos: ctx.raw_file.len() as u32 }); for var in &mut variables { if var.no_end { var.end = ops.len() - 1; } } return Compiled { operations: ops, variables, strings, functions, try_catch }; } fn append_be_num(output: &mut Vec, size: usize, num: usize) { //appends a big-endian u number to the output for i in (0..size).rev() { output.push(((num >> (i * 8)) & 0xFF) as u8); } } fn append_string(output: &mut Vec, input: String, add_len: bool) { let bytes = input.as_bytes(); if add_len { append_be_num(output, 4, bytes.len()); } output.extend_from_slice(bytes); } fn append_binary(output: &mut Vec, input: String) { let mut inp = input.clone(); while inp.len() >= 8 { let byte = bin_to_num(inp[..8].to_string()); output.push(byte as u8); inp = inp[8..].to_string(); } } fn num_to_bin(number: usize, size: usize) -> String { let mut bin = String::new(); for i in (0..size).rev() { if (number >> i) & 1 == 1 { bin.push('1'); } else { bin.push('0'); } } return bin; } fn bin_to_num(bin: String) -> usize { let mut num = 0; for (i, c) in bin.chars().rev().enumerate() { if c == '1' { num += 1 << i; } } return num; } fn compile_body(compiled: Compiled, fpos: &mut usize, ctx: &Context) -> (Vec, Vec) { let mut output: Vec = vec![]; let mut additional: Vec> = vec![]; let mut contexts: Vec = vec![ctx.clone()]; //function //function headers append_be_num(&mut output, 4, compiled.variables.len()); for var in compiled.variables { append_string(&mut output, var.name, true); append_be_num(&mut output, 3, var.id as usize); append_be_num(&mut output, 4, var.start); append_be_num(&mut output, 4, var.end); } append_be_num(&mut output, 4, compiled.strings.len()); for strs in compiled.strings.keys() { append_be_num(&mut output, 3, *strs as usize); append_string(&mut output, compiled.strings[strs].clone(), true); } append_be_num(&mut output, 4, compiled.functions.len()); for funcs in compiled.functions.keys() { *fpos += 1; append_be_num(&mut output, 3, *funcs as usize); append_be_num(&mut output, 4, *fpos); let (compiled, mut context) = compile_body(compiled.functions[funcs].clone(), fpos, ctx); for c in &mut context { c.c_funcid = *fpos } contexts.extend_from_slice(&context); additional.push(compiled); } match compiled.try_catch { Some(tc) => { append_be_num(&mut output, 4, tc as usize); }, None => { append_be_num(&mut output, 4, 0); } } //function body append_be_num(&mut output, 4, compiled.operations.len()); for ops in compiled.operations { let mut op_bin = num_to_bin(ops.opcode.into(), 5); if let Some(arg1) = ops.arg1 { op_bin.push_str(&num_to_bin(arg1.into(), 4)); } else { op_bin.push_str(&num_to_bin(0, 4)); } if let Some(arg2) = ops.arg2 { op_bin.push_str(&num_to_bin(arg2.to_bits() as usize, 64)); } else { op_bin.push_str(&num_to_bin(0, 64)); } if let Some(arg3) = ops.arg3 { op_bin.push_str(&num_to_bin(arg3.into(), 4)); } else { op_bin.push_str(&num_to_bin(0, 4)); } op_bin.push_str(&num_to_bin(0, 3)); append_binary(&mut output, op_bin); append_be_num(&mut output, 4, ops.pos as usize); } for addition in additional { output.extend_from_slice(&addition); } return (output, contexts); } pub fn compile(ast: Vec, ctx: &Context) -> (Vec, Vec) { let mut next_var_id: u32 = 1; let mut registers: Vec = vec![]; for i in 0..16 { registers.push(RegisterState { id: i as u8, used: false, variable: 0, last_used: 0, }); } let empty_tb = PrevFunc { variables: vec![], previous: None, }; let compiled = compile_function(ast, None, &mut registers, &mut next_var_id, ctx, &empty_tb, None); let mut output: Vec = vec![]; //doctype specifier append_string(&mut output, String::from("ASX"), false); //version output.push(ASXVERSION[0]); output.push(ASXVERSION[1]); output.push(ASXVERSION[2]); //number of functions output.push(0); output.push(0); output.push(0); output.push(0); //functions let mut fpos = 0; let (body, contexts) = compile_body(compiled, &mut fpos, ctx); output.extend_from_slice(&body); //update function count fpos += 1; output[6] = ((fpos >> 24) & 0xFF) as u8; output[7] = ((fpos >> 16) & 0xFF) as u8; output[8] = ((fpos >> 8) & 0xFF) as u8; output[9] = (fpos & 0xFF) as u8; return (output, contexts); }