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 []"); 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 = 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 = 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::().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 = 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(); } }