added CLI
This commit is contained in:
parent
bd5ce01234
commit
5d43674d0e
8 changed files with 395 additions and 357 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
/target
|
||||
compiled.asx
|
|
@ -689,7 +689,6 @@ fn compile_function(ast: Vec<ASTPart>, args: Option<Vec<String>>, registers: &mu
|
|||
var.end = ops.len() - 1;
|
||||
}
|
||||
}
|
||||
println!("Compiled operations: {:?}", ops);
|
||||
|
||||
return Compiled {
|
||||
operations: ops,
|
||||
|
|
206
src/decompiler.rs
Normal file
206
src/decompiler.rs
Normal file
|
@ -0,0 +1,206 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
const ASXVERSION: [u8; 3] = [0,1,0];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DecompiledFunction {
|
||||
pub body: Vec<DecompiledOperation>,
|
||||
pub variables: Vec<Variable>,
|
||||
pub strings: HashMap<u32, String>,
|
||||
pub functions: HashMap<u32, u32>
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DecompiledOperation {
|
||||
pub opcode: u8,
|
||||
pub arg1: u8,
|
||||
pub arg2: i64,
|
||||
pub arg3: u8,
|
||||
pub pos: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Variable {
|
||||
pub name: String,
|
||||
pub id: u32,
|
||||
pub start: usize,
|
||||
pub end: usize
|
||||
}
|
||||
|
||||
pub struct DecompiledData {
|
||||
pub functions: Vec<DecompiledFunction>,
|
||||
pub func_count: usize,
|
||||
}
|
||||
|
||||
pub fn operation_to_name(opcode: u8) -> String {
|
||||
match opcode {
|
||||
0 => "HLT".to_string(),
|
||||
1 => "LDS".to_string(),
|
||||
2 => "LDM".to_string(),
|
||||
3 => "LDI".to_string(),
|
||||
4 => "LDB".to_string(),
|
||||
5 => "LDF".to_string(),
|
||||
6 => "LDN".to_string(),
|
||||
7 => "ASS".to_string(),
|
||||
8 => "UNB".to_string(),
|
||||
9 => "MOV".to_string(),
|
||||
10 => "ADD".to_string(),
|
||||
11 => "SUB".to_string(),
|
||||
12 => "MUL".to_string(),
|
||||
13 => "DIV".to_string(),
|
||||
14 => "POW".to_string(),
|
||||
15 => "MOD".to_string(),
|
||||
16 => "AND".to_string(),
|
||||
17 => "OR".to_string(),
|
||||
18 => "EQ".to_string(),
|
||||
19 => "NEQ".to_string(),
|
||||
20 => "GRE".to_string(),
|
||||
21 => "GRQ".to_string(),
|
||||
22 => "LES".to_string(),
|
||||
23 => "LEQ".to_string(),
|
||||
24 => "NOT".to_string(),
|
||||
25 => "JMP".to_string(),
|
||||
26 => "CJP".to_string(),
|
||||
27 => "CAL".to_string(),
|
||||
28 => "PSH".to_string(),
|
||||
29 => "RET".to_string(),
|
||||
30 => "GET".to_string(),
|
||||
31 => "SET".to_string(),
|
||||
_ => panic!("Unknown operation code: {}", opcode),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_be_num(input: &[u8]) -> usize {
|
||||
let mut result: usize = 0;
|
||||
for i in 0..input.len() {
|
||||
result <<= 8;
|
||||
result |= input[i] as usize;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn read_str(input: &[u8]) -> String {
|
||||
String::from_utf8(input.to_vec()).expect("Invalid UTF-8 sequence")
|
||||
}
|
||||
|
||||
fn read_bin(input: &[u8]) -> String {
|
||||
let mut result = String::new();
|
||||
for &byte in input {
|
||||
result.push_str(&format!("{:08b}", byte));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn bin_to_num(bin: String) -> usize {
|
||||
let mut result: usize = 0;
|
||||
for (i, c) in bin.chars().rev().enumerate() {
|
||||
if c == '1' {
|
||||
result += 1 << i;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn bin_to_snum(bin: String) -> i64 {
|
||||
let mut result: i64 = 0;
|
||||
let mut is_negative = false;
|
||||
for (i, c) in bin.chars().rev().enumerate() {
|
||||
if c == '1' {
|
||||
if i == bin.len() - 1 {
|
||||
is_negative = true;
|
||||
} else {
|
||||
result += 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if is_negative {
|
||||
result = -result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn load_func(data: Vec<u8>, offset: &mut usize) -> DecompiledFunction {
|
||||
let var_id_len = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
let mut variables: Vec<Variable> = vec![];
|
||||
for _ in 0..var_id_len {
|
||||
let name_len = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
let name = read_str(&data[*offset..*offset + name_len]);
|
||||
*offset += name_len;
|
||||
let var_id = read_be_num(&data[*offset..*offset + 3]) as u32;
|
||||
*offset += 3;
|
||||
let start = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
let end = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
variables.push(Variable { name: name, id: var_id, start: start, end: end });
|
||||
}
|
||||
let mut strings: HashMap<u32, String> = HashMap::new();
|
||||
let string_count = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
for _ in 0..string_count {
|
||||
let str_id = read_be_num(&data[*offset..*offset + 3]) as u32;
|
||||
*offset += 3;
|
||||
let str_len = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
let str_value = read_str(&data[*offset..*offset + str_len]);
|
||||
*offset += str_len;
|
||||
strings.insert(str_id, str_value);
|
||||
}
|
||||
let mut functions: HashMap<u32, u32> = HashMap::new();
|
||||
let function_count = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
for _ in 0..function_count {
|
||||
let func_id = read_be_num(&data[*offset..*offset + 3]) as u32;
|
||||
*offset += 3;
|
||||
let func_pos = read_be_num(&data[*offset..*offset + 4]) as u32;
|
||||
*offset += 4;
|
||||
functions.insert(func_id, func_pos);
|
||||
}
|
||||
let mut body: Vec<DecompiledOperation> = Vec::new();
|
||||
let instr_len: usize = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
for _ in 0..instr_len {
|
||||
let op = read_bin(&data[*offset..*offset + 10]);
|
||||
*offset += 10;
|
||||
let op_code = bin_to_num(op[0..5].to_string());
|
||||
let arg1 = bin_to_num(op[5..9].to_string());
|
||||
let arg2 = bin_to_snum(op[9..73].to_string());
|
||||
let arg3 = bin_to_num(op[73..77].to_string());
|
||||
let pos = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
body.push(DecompiledOperation {
|
||||
opcode: op_code as u8,
|
||||
arg1: arg1 as u8,
|
||||
arg2: arg2,
|
||||
arg3: arg3 as u8,
|
||||
pos: pos,
|
||||
});
|
||||
}
|
||||
return DecompiledFunction {
|
||||
body: body,
|
||||
variables: variables,
|
||||
strings: strings,
|
||||
functions: functions,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn process(data: &Vec<u8>) -> DecompiledData {
|
||||
if data[0..3] != *"ASX".as_bytes() {
|
||||
panic!("Invalid ASX file header");
|
||||
}
|
||||
if data[3..6] != ASXVERSION {
|
||||
panic!("Unsupported ASX version");
|
||||
}
|
||||
let func_count = read_be_num(&data[6..10]);
|
||||
let mut offset = 10;
|
||||
let mut functions: Vec<DecompiledFunction> = Vec::new();
|
||||
while offset < data.len() {
|
||||
let func = load_func(data.clone(), &mut offset);
|
||||
functions.push(func);
|
||||
}
|
||||
return DecompiledData {
|
||||
functions: functions,
|
||||
func_count: func_count,
|
||||
};
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use std::{collections::HashMap, io::{BufReader, Read, Write}, net::{self, TcpListener, TcpStream}, process, thread::sleep, time::Duration, vec};
|
||||
|
||||
use crate::{errors::{create_error, print_error, ErrorSubType, ErrorType}, virtualmachine::{DecompiledOperation, Machine, TableValue, VMMemory, VMMemoryBoolean, VMMemoryNativeFunction, VMMemoryNull, VMMemoryNumber, VMMemoryString, VMMemoryTable}};
|
||||
use crate::{decompiler::DecompiledOperation, errors::{create_error, print_error, ErrorSubType, ErrorType}, virtualmachine::{Machine, TableValue, VMMemory, VMMemoryBoolean, VMMemoryNativeFunction, VMMemoryNull, VMMemoryNumber, VMMemoryString, VMMemoryTable}};
|
||||
|
||||
fn get_string_from_vmmem(mem: &VMMemory) -> String {
|
||||
let mut out = String::new();
|
||||
|
|
|
@ -70,6 +70,17 @@ fn convert_types_to_short(typ: &ErrorType) -> String {
|
|||
ErrorType::IOError => String::from("IO:"),
|
||||
}
|
||||
}
|
||||
pub fn reverse_type_short(str: String) -> ErrorType {
|
||||
match str.as_str() {
|
||||
"ST" => ErrorType::SyntaxError,
|
||||
"TP" => ErrorType::TypeError,
|
||||
"MT" => ErrorType::MathError,
|
||||
"SM" => ErrorType::SemanticError,
|
||||
"MC" => ErrorType::MachineError,
|
||||
"IO" => ErrorType::IOError,
|
||||
_ => panic!("Unknown error type short: {}", str),
|
||||
}
|
||||
}
|
||||
fn convert_subtypes_to_string(stype: &ErrorSubType) -> String {
|
||||
match stype {
|
||||
ErrorSubType::UnexpectedEnd => String::from("Unexpected end"),
|
||||
|
@ -126,6 +137,35 @@ fn convert_subtypes_to_short(stype: &ErrorSubType) -> String {
|
|||
ErrorSubType::RuntimeError => String::from("RE:"),
|
||||
}
|
||||
}
|
||||
pub fn reverse_subtype_short(str: String) -> ErrorSubType {
|
||||
match str.as_str() {
|
||||
"UE" => ErrorSubType::UnexpectedEnd,
|
||||
"UP" => ErrorSubType::UnexpectedOperation,
|
||||
"EX" => ErrorSubType::Expected,
|
||||
"UN" => ErrorSubType::Unexpected,
|
||||
"IK" => ErrorSubType::InvalidTableKeys,
|
||||
"UC" => ErrorSubType::Unclosed,
|
||||
"EW" => ErrorSubType::ElseWithoutIf,
|
||||
"BC" => ErrorSubType::BreakContinueWithoutLoop,
|
||||
"UO" => ErrorSubType::UnknownOperation,
|
||||
"VN" => ErrorSubType::VariableNotFound,
|
||||
"VE" => ErrorSubType::VariableAlreadyExists,
|
||||
"AD" => ErrorSubType::ArgumentDuplication,
|
||||
"RF" => ErrorSubType::RegisterNotFound,
|
||||
"MO" => ErrorSubType::MemoryOutOfBounds,
|
||||
"OC" => ErrorSubType::UnknownOPCode,
|
||||
"UF" => ErrorSubType::UnknownFunction,
|
||||
"US" => ErrorSubType::UnknownString,
|
||||
"UM" => ErrorSubType::UnknownMemoryLocation,
|
||||
"NF" => ErrorSubType::NonFunctionCall,
|
||||
"DZ" => ErrorSubType::DivisionByZero,
|
||||
"WT" => ErrorSubType::WrongType,
|
||||
"TA" => ErrorSubType::TooManyArguments,
|
||||
"FE" => ErrorSubType::FileError,
|
||||
"RE" => ErrorSubType::RuntimeError,
|
||||
_ => panic!("Unknown error subtype short: {}", str),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_error(message: &str, position: usize, typ: ErrorType, stype: ErrorSubType) -> ASLError {
|
||||
let mut code = convert_types_to_short(&typ);
|
||||
|
|
298
src/main.rs
298
src/main.rs
|
@ -1,7 +1,6 @@
|
|||
use std::fs;
|
||||
|
||||
use parser::ASTPart;
|
||||
use std::{env, fs, time::Instant};
|
||||
use virtualmachine::Machine;
|
||||
use crate::{decompiler::process, errors::{create_error, print_error, reverse_subtype_short, reverse_type_short}};
|
||||
|
||||
mod lexer;
|
||||
mod parser;
|
||||
|
@ -9,133 +8,9 @@ mod enviroment;
|
|||
mod compiler;
|
||||
mod virtualmachine;
|
||||
mod errors;
|
||||
mod decompiler;
|
||||
|
||||
fn log_ast_part(part: &ASTPart, prefix: String) {
|
||||
match part {
|
||||
ASTPart::Number(number) => println!("{}{}: Number: {}", prefix, number.pos, number.value),
|
||||
ASTPart::String(str) => println!("{}{}: String: {}", prefix, str.pos, str.value),
|
||||
ASTPart::Boolean(bool) => println!("{}{}: Boolean: {}", prefix, bool.pos, bool.value),
|
||||
ASTPart::Null(null) => println!("{}{}: Null", prefix, null.pos),
|
||||
ASTPart::Assigment(assigment) => {
|
||||
println!("{}{}: Assigment: {}", prefix, assigment.pos, assigment.variable);
|
||||
println!("{} Value:", prefix);
|
||||
log_ast_part(&assigment.value, format!("{} ", prefix));
|
||||
},
|
||||
ASTPart::VarUpdate(v_update) => {
|
||||
println!("{}{}: Update: {}", prefix, v_update.pos, v_update.variable);
|
||||
println!("{} Value:", prefix);
|
||||
log_ast_part(&v_update.value, format!("{} ", prefix));
|
||||
},
|
||||
ASTPart::Operation(operation) => {
|
||||
println!("{}{}: Operation: {}", prefix, operation.pos, operation.operator);
|
||||
println!("{} Left:", prefix);
|
||||
log_ast_part(&operation.left, format!("{} ", prefix));
|
||||
println!("{} Right:", prefix);
|
||||
log_ast_part(&operation.right, format!("{} ", prefix));
|
||||
},
|
||||
ASTPart::VarRead(var_read) => println!("{}{}: Variable Read: {}", prefix, var_read.pos, var_read.variable),
|
||||
ASTPart::Call(call) => {
|
||||
println!("{}{}: Call:", prefix, call.pos);
|
||||
println!("{} Function:", prefix);
|
||||
log_ast_part(&call.function, format!("{} ", prefix));
|
||||
println!("{} Args:", prefix);
|
||||
for arg in &call.args {
|
||||
log_ast_part(&arg, format!("{} ", prefix));
|
||||
}
|
||||
},
|
||||
ASTPart::Function(func) => {
|
||||
println!("{}{}: Function:", prefix, func.pos);
|
||||
println!("{} Args: {}", prefix, func.args.join(", "));
|
||||
println!("{} Body:", prefix);
|
||||
for part in &func.body {
|
||||
log_ast_part(part, format!("{} ", prefix));
|
||||
}
|
||||
},
|
||||
ASTPart::If(if_part) => {
|
||||
println!("{}{}: If:", prefix, if_part.pos);
|
||||
println!("{} Condition:", prefix);
|
||||
log_ast_part(&if_part.condition, format!("{} ", prefix));
|
||||
println!("{} Body:", prefix);
|
||||
for part in &if_part.body {
|
||||
log_ast_part(part, format!("{} ", prefix));
|
||||
}
|
||||
},
|
||||
ASTPart::ElseIf(elif) => {
|
||||
println!("{}{}: Else If:", prefix, elif.pos);
|
||||
println!("{} Condition:", prefix);
|
||||
log_ast_part(&elif.condition, format!("{} ", prefix));
|
||||
println!("{} Body:", prefix);
|
||||
for part in &elif.body {
|
||||
log_ast_part(part, format!("{} ", prefix));
|
||||
}
|
||||
},
|
||||
ASTPart::Else(els) => {
|
||||
println!("{}{}: Else:", prefix, els.pos);
|
||||
println!("{} Body:", prefix);
|
||||
for part in &els.body {
|
||||
log_ast_part(part, format!("{} ", prefix));
|
||||
}
|
||||
},
|
||||
ASTPart::While(while_part) => {
|
||||
println!("{}{}: While:", prefix, while_part.pos);
|
||||
println!("{} Condition:", prefix);
|
||||
log_ast_part(&while_part.condition, format!("{} ", prefix));
|
||||
println!("{} Body:", prefix);
|
||||
for part in &while_part.body {
|
||||
log_ast_part(part, format!("{} ", prefix));
|
||||
}
|
||||
},
|
||||
ASTPart::Break(brk) => println!("{}{}: Break", prefix, brk.pos),
|
||||
ASTPart::For(fr) => {
|
||||
println!("{}{}: For:", prefix, fr.pos);
|
||||
println!("{} Init:", prefix);
|
||||
log_ast_part(&fr.init, format!("{} ", prefix));
|
||||
println!("{} Condition:", prefix);
|
||||
log_ast_part(&fr.condition, format!("{} ", prefix));
|
||||
println!("{} Update:", prefix);
|
||||
log_ast_part(&fr.update, format!("{} ", prefix));
|
||||
println!("{} Body:", prefix);
|
||||
for part in &fr.body {
|
||||
log_ast_part(part, format!("{} ", prefix));
|
||||
}
|
||||
},
|
||||
ASTPart::Continue(cnt) => println!("{}{}: Continue", prefix, cnt.pos),
|
||||
ASTPart::Return(ret) => {
|
||||
println!("{}{}: Return", prefix, ret.pos);
|
||||
println!("{} Value:", prefix);
|
||||
log_ast_part(&ret.value, format!("{} ", prefix));
|
||||
},
|
||||
ASTPart::Table(tbl) => {
|
||||
println!("{}{}: Table:", prefix, tbl.pos);
|
||||
for table_value in &tbl.values {
|
||||
println!("{} Key:", prefix);
|
||||
log_ast_part(&table_value.key, format!("{} ", prefix));
|
||||
println!("{} Value:", prefix);
|
||||
log_ast_part(&table_value.value, format!("{} ", prefix));
|
||||
}
|
||||
},
|
||||
ASTPart::TableGet(tbl_get) => {
|
||||
println!("{}{}: Table Get:", prefix, tbl_get.pos);
|
||||
println!("{} Table:", prefix);
|
||||
log_ast_part(&tbl_get.table, format!("{} ", prefix));
|
||||
println!("{} Key:", prefix);
|
||||
log_ast_part(&tbl_get.key, format!("{} ", prefix));
|
||||
},
|
||||
ASTPart::TableSet(tbl_set) => {
|
||||
println!("{}{}: Table Set:", prefix, tbl_set.pos);
|
||||
println!("{} Table:", prefix);
|
||||
log_ast_part(&tbl_set.table, format!("{} ", prefix));
|
||||
println!("{} Key:", prefix);
|
||||
log_ast_part(&tbl_set.key, format!("{} ", prefix));
|
||||
println!("{} Value:", prefix);
|
||||
log_ast_part(&tbl_set.value, format!("{} ", prefix));
|
||||
},
|
||||
ASTPart::Import(imp) => {
|
||||
println!("{}{}: Import: {}", prefix, imp.pos, imp.path);
|
||||
},
|
||||
ASTPart::NOOP => println!("{}NOOP", prefix)
|
||||
}
|
||||
}
|
||||
const CLIVER: [u8; 3] = [0, 1, 0];
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Context {
|
||||
|
@ -145,37 +20,146 @@ struct Context {
|
|||
known: bool
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
println!("Usage: ASL <command> <file> [<error code>]");
|
||||
println!("Commands:");
|
||||
println!(" run - Run an ASL/ASX file");
|
||||
println!(" compile - Compile the ASL file to bytecode");
|
||||
println!(" traceback - Traceback an error based on an error code");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let inp = fs::read_to_string("./test.asl");
|
||||
match inp {
|
||||
Result::Ok(data) => {
|
||||
let ctx = Context {
|
||||
file: String::from("./test.asl"),
|
||||
raw_file: data.clone(),
|
||||
c_funcid: 0,
|
||||
known: true
|
||||
};
|
||||
let lexed = lexer::lex(data, &ctx);
|
||||
println!("Lexer output: ");
|
||||
for token in &lexed {
|
||||
println!(" {}: {:?}: {}", token.pos, token.typ, token.value);
|
||||
let msg = format!("Astro Lang (ASL) CLI v{}.{}.{}", CLIVER[0], CLIVER[1], CLIVER[2]);
|
||||
println!("{}", msg);
|
||||
println!("{}", "=".repeat(msg.len()));
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() < 3 {
|
||||
println!("Not enough arguments provided.");
|
||||
print_help();
|
||||
return;
|
||||
}
|
||||
if args[1] == "run" {
|
||||
let file = &args[2];
|
||||
if file.ends_with(".asl") {
|
||||
let inp = fs::read_to_string(file);
|
||||
let time = Instant::now();
|
||||
match inp {
|
||||
Result::Ok(data) => {
|
||||
let ctx = Context {
|
||||
file: String::from(file),
|
||||
raw_file: data.clone(),
|
||||
c_funcid: 0,
|
||||
known: true
|
||||
};
|
||||
let lexed = lexer::lex(data, &ctx);
|
||||
let ast = parser::parse(lexed, &ctx);
|
||||
let (compiled, contexts) = compiler::compile(ast, &ctx);
|
||||
let ntime = time.elapsed();
|
||||
println!("Build successful. Took: {}ms", ntime.as_millis());
|
||||
let mut vm = Machine::new(contexts);
|
||||
vm.load(&compiled);
|
||||
vm.run();
|
||||
},
|
||||
Result::Err(err) => {
|
||||
panic!("Can't read file: {}", err);
|
||||
}
|
||||
}
|
||||
let ast = parser::parse(lexed, &ctx);
|
||||
println!("AST: ");
|
||||
for part in &ast {
|
||||
log_ast_part(part, String::from(" "));
|
||||
} else if file.ends_with(".asx") {
|
||||
let inp = fs::read(file);
|
||||
match inp {
|
||||
Result::Ok(data) => {
|
||||
let decompiled = process(&data);
|
||||
let mut contexts: Vec<Context> = Vec::new();
|
||||
for i in 0..decompiled.func_count {
|
||||
contexts.push(Context { file: String::from(file), raw_file: String::from("Unknown"), c_funcid: i, known: false });
|
||||
}
|
||||
let mut vm = Machine::new(contexts);
|
||||
vm.load(&data);
|
||||
vm.run();
|
||||
},
|
||||
Result::Err(err) => {
|
||||
panic!("Can't read file: {}", err);
|
||||
}
|
||||
}
|
||||
let (compiled, contexts) = compiler::compile(ast, &ctx);
|
||||
println!("Compiled output: {:?}", compiled);
|
||||
let mut vm = Machine::new(contexts);
|
||||
vm.load(compiled);
|
||||
vm.run();
|
||||
println!("Registers: {:?}", vm.registers);
|
||||
println!("Stack: {:?}", vm.stack);
|
||||
println!("Memory: {:?}", vm.memory);
|
||||
},
|
||||
Result::Err(err) => {
|
||||
panic!("Error while reading file: {}", err)
|
||||
} else {
|
||||
println!("Unknown file type. Please use .asl or .asx files.");
|
||||
}
|
||||
} else if args[1] == "compile" {
|
||||
let file = &args[2];
|
||||
if file.ends_with(".asl") {
|
||||
let inp = fs::read_to_string(file);
|
||||
let time = Instant::now();
|
||||
match inp {
|
||||
Result::Ok(data) => {
|
||||
let ctx = Context {
|
||||
file: String::from(file),
|
||||
raw_file: data.clone(),
|
||||
c_funcid: 0,
|
||||
known: true
|
||||
};
|
||||
let lexed = lexer::lex(data, &ctx);
|
||||
let ast = parser::parse(lexed, &ctx);
|
||||
let (compiled, _contexts) = compiler::compile(ast, &ctx);
|
||||
match fs::write("compiled.asx", compiled) {
|
||||
Ok(_) => {
|
||||
let ntime = time.elapsed();
|
||||
println!("Build successful. Took: {}ms", ntime.as_millis());
|
||||
},
|
||||
Err(err) => {
|
||||
panic!("Can't write compiled file: {}", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
Result::Err(err) => {
|
||||
panic!("Can't read file: {}", err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("Unknown file type. Please use .asl files.");
|
||||
}
|
||||
} else if args[1] == "traceback" {
|
||||
if args.len() < 4 {
|
||||
println!("Not enough arguments provided for traceback.");
|
||||
print_help();
|
||||
return;
|
||||
}
|
||||
let file = &args[2];
|
||||
if file.ends_with(".asl") {
|
||||
let inp = fs::read_to_string(file);
|
||||
let time = Instant::now();
|
||||
match inp {
|
||||
Result::Ok(data) => {
|
||||
let ctx = Context {
|
||||
file: String::from(file),
|
||||
raw_file: data.clone(),
|
||||
c_funcid: 0,
|
||||
known: true
|
||||
};
|
||||
let lexed = lexer::lex(data, &ctx);
|
||||
let ast = parser::parse(lexed, &ctx);
|
||||
let (compiled, contexts) = compiler::compile(ast, &ctx);
|
||||
let ntime = time.elapsed();
|
||||
println!("Build successful. Took: {}ms", ntime.as_millis());
|
||||
let decompiled = process(&compiled);
|
||||
let errcode_data: Vec<&str> = args[3].split(":").collect();
|
||||
if decompiled.func_count <= errcode_data[3].parse().unwrap() {
|
||||
println!("Error code {} is invalid for this file.", args[3]);
|
||||
return;
|
||||
}
|
||||
let error = create_error("", errcode_data[2].parse().unwrap(), reverse_type_short(errcode_data[0].to_string()), reverse_subtype_short(errcode_data[1].to_string()));
|
||||
let func_ctx = contexts[errcode_data[3].parse::<usize>().unwrap()].clone();
|
||||
print_error(&error, &func_ctx);
|
||||
return;
|
||||
},
|
||||
Result::Err(err) => {
|
||||
panic!("Can't read file: {}", err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("Unknown file type. Please use .asl files.");
|
||||
}
|
||||
} else {
|
||||
println!("Invalid command.");
|
||||
print_help();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
use std::{any::Any, collections::HashMap, process, vec};
|
||||
use crate::{enviroment, errors::{create_error, print_error, ErrorSubType, ErrorType}, Context};
|
||||
|
||||
const ASXVERSION: [u8; 3] = [0,1,0];
|
||||
use crate::{decompiler::{operation_to_name, process, DecompiledFunction, DecompiledOperation}, enviroment, errors::{create_error, print_error, ErrorSubType, ErrorType}, Context};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VMMemory {
|
||||
|
@ -54,36 +52,12 @@ pub struct VMMemoryNativeFunction {
|
|||
pub variable_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DecompiledFunction {
|
||||
body: Vec<DecompiledOperation>,
|
||||
variables: Vec<Variable>,
|
||||
strings: HashMap<u32, String>,
|
||||
functions: HashMap<u32, u32>
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DecompiledOperation {
|
||||
pub opcode: u8,
|
||||
pub arg1: u8,
|
||||
pub arg2: i64,
|
||||
pub arg3: u8,
|
||||
pub pos: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Register {
|
||||
id: u8,
|
||||
pointer: usize
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Variable {
|
||||
name: String,
|
||||
id: u32,
|
||||
start: usize,
|
||||
end: usize
|
||||
}
|
||||
|
||||
pub struct CallStack {
|
||||
pub func: usize,
|
||||
pub return_reg: usize,
|
||||
|
@ -101,55 +75,6 @@ pub struct Machine {
|
|||
pub storage: Vec<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
fn read_be_num(input: &[u8]) -> usize {
|
||||
let mut result: usize = 0;
|
||||
for i in 0..input.len() {
|
||||
result <<= 8;
|
||||
result |= input[i] as usize;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn read_str(input: &[u8]) -> String {
|
||||
String::from_utf8(input.to_vec()).expect("Invalid UTF-8 sequence")
|
||||
}
|
||||
|
||||
fn read_bin(input: &[u8]) -> String {
|
||||
let mut result = String::new();
|
||||
for &byte in input {
|
||||
result.push_str(&format!("{:08b}", byte));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn bin_to_num(bin: String) -> usize {
|
||||
let mut result: usize = 0;
|
||||
for (i, c) in bin.chars().rev().enumerate() {
|
||||
if c == '1' {
|
||||
result += 1 << i;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn bin_to_snum(bin: String) -> i64 {
|
||||
let mut result: i64 = 0;
|
||||
let mut is_negative = false;
|
||||
for (i, c) in bin.chars().rev().enumerate() {
|
||||
if c == '1' {
|
||||
if i == bin.len() - 1 {
|
||||
is_negative = true;
|
||||
} else {
|
||||
result += 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if is_negative {
|
||||
result = -result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn get_register_by_id(registers: &Vec<Register>, id: u8) -> Option<&Register> {
|
||||
for reg in registers {
|
||||
if reg.id == id {
|
||||
|
@ -226,44 +151,6 @@ fn get_mem_tbl_val(tbl: &VMMemoryTable, key: VMMemory) -> Option<&VMMemory> {
|
|||
None
|
||||
}
|
||||
|
||||
fn operation_to_name(opcode: u8) -> String {
|
||||
match opcode {
|
||||
0 => "HLT".to_string(),
|
||||
1 => "LDS".to_string(),
|
||||
2 => "LDM".to_string(),
|
||||
3 => "LDI".to_string(),
|
||||
4 => "LDB".to_string(),
|
||||
5 => "LDF".to_string(),
|
||||
6 => "LDN".to_string(),
|
||||
7 => "ASS".to_string(),
|
||||
8 => "UNB".to_string(),
|
||||
9 => "MOV".to_string(),
|
||||
10 => "ADD".to_string(),
|
||||
11 => "SUB".to_string(),
|
||||
12 => "MUL".to_string(),
|
||||
13 => "DIV".to_string(),
|
||||
14 => "POW".to_string(),
|
||||
15 => "MOD".to_string(),
|
||||
16 => "AND".to_string(),
|
||||
17 => "OR".to_string(),
|
||||
18 => "EQ".to_string(),
|
||||
19 => "NEQ".to_string(),
|
||||
20 => "GRE".to_string(),
|
||||
21 => "GRQ".to_string(),
|
||||
22 => "LES".to_string(),
|
||||
23 => "LEQ".to_string(),
|
||||
24 => "NOT".to_string(),
|
||||
25 => "JMP".to_string(),
|
||||
26 => "CJP".to_string(),
|
||||
27 => "CAL".to_string(),
|
||||
28 => "PSH".to_string(),
|
||||
29 => "RET".to_string(),
|
||||
30 => "GET".to_string(),
|
||||
31 => "SET".to_string(),
|
||||
_ => panic!("Unknown operation code: {}", opcode),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_var_id(var: &VMMemory) -> u32 {
|
||||
match var {
|
||||
VMMemory::String(str) => str.variable_id,
|
||||
|
@ -428,76 +315,6 @@ fn do_operation_operation(registers: &mut Vec<Register>, memory: &mut Vec<VMMemo
|
|||
memory[reg3_pointer] = result;
|
||||
}
|
||||
|
||||
fn load_func(data: Vec<u8>, offset: &mut usize) -> DecompiledFunction {
|
||||
let var_id_len = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
let mut variables: Vec<Variable> = vec![];
|
||||
for _ in 0..var_id_len {
|
||||
let name_len = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
let name = read_str(&data[*offset..*offset + name_len]);
|
||||
*offset += name_len;
|
||||
let var_id = read_be_num(&data[*offset..*offset + 3]) as u32;
|
||||
*offset += 3;
|
||||
let start = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
let end = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
variables.push(Variable { name: name, id: var_id, start: start, end: end });
|
||||
}
|
||||
let mut strings: HashMap<u32, String> = HashMap::new();
|
||||
let string_count = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
for _ in 0..string_count {
|
||||
let str_id = read_be_num(&data[*offset..*offset + 3]) as u32;
|
||||
*offset += 3;
|
||||
let str_len = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
let str_value = read_str(&data[*offset..*offset + str_len]);
|
||||
*offset += str_len;
|
||||
strings.insert(str_id, str_value);
|
||||
}
|
||||
let mut functions: HashMap<u32, u32> = HashMap::new();
|
||||
let function_count = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
for _ in 0..function_count {
|
||||
let func_id = read_be_num(&data[*offset..*offset + 3]) as u32;
|
||||
*offset += 3;
|
||||
let func_pos = read_be_num(&data[*offset..*offset + 4]) as u32;
|
||||
*offset += 4;
|
||||
functions.insert(func_id, func_pos);
|
||||
}
|
||||
let mut body: Vec<DecompiledOperation> = Vec::new();
|
||||
let instr_len: usize = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
for _ in 0..instr_len {
|
||||
let op = read_bin(&data[*offset..*offset + 10]);
|
||||
*offset += 10;
|
||||
let op_code = bin_to_num(op[0..5].to_string());
|
||||
let arg1 = bin_to_num(op[5..9].to_string());
|
||||
let arg2 = bin_to_snum(op[9..73].to_string());
|
||||
let arg3 = bin_to_num(op[73..77].to_string());
|
||||
let pos = read_be_num(&data[*offset..*offset + 4]);
|
||||
*offset += 4;
|
||||
body.push(DecompiledOperation {
|
||||
opcode: op_code as u8,
|
||||
arg1: arg1 as u8,
|
||||
arg2: arg2,
|
||||
arg3: arg3 as u8,
|
||||
pos: pos,
|
||||
});
|
||||
}
|
||||
println!("Variables: {:?}", variables);
|
||||
println!("Strings: {:?}", strings);
|
||||
println!("Functions: {:?}", functions);
|
||||
return DecompiledFunction {
|
||||
body: body,
|
||||
variables: variables,
|
||||
strings: strings,
|
||||
functions: functions,
|
||||
};
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn new(ctx: Vec<Context>) -> Self {
|
||||
let mut registers = Vec::new();
|
||||
|
@ -525,19 +342,9 @@ impl Machine {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn load(&mut self, data: Vec<u8>) {
|
||||
if data[0..3] != *"ASX".as_bytes() {
|
||||
panic!("Invalid ASX file header");
|
||||
}
|
||||
if data[3..6] != ASXVERSION {
|
||||
panic!("Unsupported ASX version");
|
||||
}
|
||||
let _func_count = read_be_num(&data[6..10]);
|
||||
let mut offset = 10;
|
||||
while offset < data.len() {
|
||||
let func = load_func(data.clone(), &mut offset);
|
||||
self.functions.push(func);
|
||||
}
|
||||
pub fn load(&mut self, data: &Vec<u8>) {
|
||||
let dec = process(data);
|
||||
self.functions = dec.functions;
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
|
|
1
test.asl
1
test.asl
|
@ -4,3 +4,4 @@ ugass(a)
|
|||
a = tábla.töröl(a, 0)
|
||||
ugass(a)
|
||||
ugass(tábla.kulcsok(a))
|
||||
ugass(a+1)
|
Loading…
Add table
Add a link
Reference in a new issue