250 lines
No EOL
10 KiB
Rust
250 lines
No EOL
10 KiB
Rust
use std::{env, fs, time::Instant};
|
|
use virtualmachine::Machine;
|
|
use crate::{decompiler::{operation_to_name, process}, errors::{create_error, print_error, reverse_subtype_short, reverse_type_short}, virtualmachine::VMState};
|
|
|
|
mod lexer;
|
|
mod parser;
|
|
mod enviroment;
|
|
mod compiler;
|
|
mod virtualmachine;
|
|
mod errors;
|
|
mod decompiler;
|
|
|
|
const CLIVER: [u8; 3] = [1,0,2];
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct Context {
|
|
file: String,
|
|
raw_file: String,
|
|
c_funcid: usize,
|
|
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");
|
|
println!(" viewx- - View the contents of an ASX file");
|
|
}
|
|
|
|
fn main() {
|
|
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);
|
|
while let VMState::Paused = vm.state {
|
|
vm.resume();
|
|
}
|
|
match vm.state {
|
|
VMState::Finished => {
|
|
println!("Execution finished successfully.");
|
|
},
|
|
VMState::Error(err) => {
|
|
print_error(&err);
|
|
panic!("Execution failed with an error.");
|
|
},
|
|
_ => {}
|
|
}
|
|
},
|
|
Result::Err(err) => {
|
|
panic!("Can't read file: {}", err);
|
|
}
|
|
}
|
|
} 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);
|
|
while let VMState::Paused = vm.state {
|
|
vm.resume();
|
|
}
|
|
match vm.state {
|
|
VMState::Finished => {
|
|
println!("Execution finished successfully.");
|
|
},
|
|
VMState::Error(err) => {
|
|
print_error(&err);
|
|
panic!("Execution failed with an error.");
|
|
},
|
|
_ => {}
|
|
}
|
|
},
|
|
Result::Err(err) => {
|
|
panic!("Can't read 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[2].parse().unwrap() {
|
|
println!("Error code {} is invalid for this file.", args[3]);
|
|
return;
|
|
}
|
|
let func_ctx = contexts[errcode_data[2].parse::<usize>().unwrap()].clone();
|
|
let error = create_error("", errcode_data[3].parse().unwrap(), reverse_type_short(errcode_data[0].to_string()), reverse_subtype_short(errcode_data[1].to_string()), &func_ctx);
|
|
print_error(&error);
|
|
return;
|
|
},
|
|
Result::Err(err) => {
|
|
panic!("Can't read file: {}", err);
|
|
}
|
|
}
|
|
} else {
|
|
println!("Unknown file type. Please use .asl files.");
|
|
}
|
|
} else if args[1] == "viewx" {
|
|
let file = &args[2];
|
|
if file.ends_with(".asx") {
|
|
let inp = fs::read(file);
|
|
match inp {
|
|
Result::Ok(data) => {
|
|
let decompiled = process(&data);
|
|
println!("ASX Verion: {}", decompiled.version);
|
|
println!("Function Count: {}", decompiled.func_count);
|
|
for i in 0..decompiled.func_count {
|
|
println!("Function {}:", i);
|
|
let func = &decompiled.functions[i];
|
|
println!(" Variables:");
|
|
for var in &func.variables {
|
|
println!(" {}: {} ({}:{})", var.id, var.name, var.start, var.end);
|
|
}
|
|
println!(" Strings:");
|
|
for (id, string) in &func.strings {
|
|
println!(" {}: {}", id, string);
|
|
}
|
|
println!(" Functions:");
|
|
for (id, pos) in &func.functions {
|
|
println!(" {}: {}", id, pos);
|
|
}
|
|
println!(" Try/Catch: {}", func.try_catch);
|
|
println!(" Body:");
|
|
//Print these under each other like when you tabulate
|
|
let mut longest_cols: Vec<usize> = vec![0, 0, 0, 0];
|
|
for op in &func.body {
|
|
longest_cols[0] = longest_cols[0].max(operation_to_name(op.opcode).len());
|
|
longest_cols[1] = longest_cols[1].max(op.arg1.to_string().len());
|
|
longest_cols[2] = longest_cols[2].max(op.arg2.to_string().len());
|
|
longest_cols[3] = longest_cols[3].max(op.arg3.to_string().len());
|
|
}
|
|
let mut longest = 0;
|
|
for op in &func.body {
|
|
let op_name = operation_to_name(op.opcode);
|
|
let str = format!(" {}{} {}{} {}{} {}{} at {}",
|
|
" ".repeat(longest_cols[0] - op_name.len()),
|
|
op_name,
|
|
" ".repeat(longest_cols[1] - op.arg1.to_string().len()),
|
|
op.arg1,
|
|
" ".repeat(longest_cols[2] - op.arg2.to_string().len()),
|
|
op.arg2,
|
|
" ".repeat(longest_cols[3] - op.arg3.to_string().len()),
|
|
op.arg3,
|
|
op.pos);
|
|
longest = longest.max(str.len()+4);
|
|
println!("{}", str);
|
|
}
|
|
println!("{}", "=".repeat(longest));
|
|
}
|
|
},
|
|
Result::Err(err) => {
|
|
panic!("Can't read file: {}", err);
|
|
}
|
|
}
|
|
} else {
|
|
println!("Unknown file type. Please use .asx files.");
|
|
}
|
|
} else {
|
|
println!("Invalid command.");
|
|
print_help();
|
|
}
|
|
} |