AstroLang/src/compiler.rs
2025-06-18 16:44:35 +02:00

916 lines
No EOL
45 KiB
Rust

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<u8>,
pub arg2: Option<f64>,
pub arg3: Option<u8>,
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<Operation>,
variables: Vec<Variable>,
strings: HashMap<u32, String>,
functions: HashMap<u32, Compiled>,
try_catch: Option<u32>
}
#[derive(Debug, Clone)]
struct Variable {
name: String,
id: u32,
start: usize,
end: usize,
no_end: bool
}
#[derive(Debug, Clone)]
struct PrevFunc {
variables: Vec<Variable>,
previous: Option<Box<PrevFunc>>,
}
fn get_register_by_id(registers: &Vec<RegisterState>, 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<RegisterState>, 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<RegisterState>, 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<RegisterState>) -> 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<RegisterState>) {
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<Variable>, name: &str, position: usize, traceback: &PrevFunc) -> Option<Variable> {
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<Operation>, variables: &mut Vec<Variable>, next_var_id: &mut u32, strings: &mut HashMap<u32, String>, next_string_id: &mut u32, functions: &mut HashMap<u32, Compiled>, next_function_id: &mut u32, registers: &mut Vec<RegisterState>, 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<Variable> = 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<Variable> = 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<Variable> = 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<usize> = vec![];
let mut continues: Vec<usize> = 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<Variable> = 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<usize> = vec![];
let mut continues: Vec<usize> = 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<Variable> = 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<ASTPart>, args: Option<Vec<String>>, registers: &mut Vec<RegisterState>, next_var_id: &mut u32, ctx: &Context, traceback: &PrevFunc, try_catch: Option<u32>) -> Compiled {
let mut ops: Vec<Operation> = vec![];
let mut variables: Vec<Variable> = vec![];
let mut strings: HashMap<u32, String> = HashMap::new();
let mut next_string_id: u32 = 1;
let mut functions: HashMap<u32, Compiled> = 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<u8>, 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<u8>, 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<u8>, 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<u8>, Vec<Context>) {
let mut output: Vec<u8> = vec![];
let mut additional: Vec<Vec<u8>> = vec![];
let mut contexts: Vec<Context> = 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<ASTPart>, ctx: &Context) -> (Vec<u8>, Vec<Context>) {
let mut next_var_id: u32 = 1;
let mut registers: Vec<RegisterState> = 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<u8> = 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);
}