AstroLang/src/main.rs

225 lines
No EOL
9.3 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}};
mod lexer;
mod parser;
mod enviroment;
mod compiler;
mod virtualmachine;
mod errors;
mod decompiler;
const CLIVER: [u8; 3] = [0, 3, 0];
#[derive(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);
vm.run();
},
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);
vm.run();
},
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[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 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!(" 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();
}
}