Improve CLI
- Remove unused flags - Show more helpful error messages
This commit is contained in:
parent
c1bee69fa6
commit
4e92a416ed
@ -5,6 +5,7 @@ use crate::{
|
||||
ast::{Ast, BinOpType, BlockScope, Expression, FunDecl, If, Statement, UnOpType},
|
||||
astoptimizer::{AstOptimizer, SimpleAstOptimizer},
|
||||
lexer::lex,
|
||||
nice_panic,
|
||||
parser::parse,
|
||||
stringstore::{Sid, StringStore},
|
||||
};
|
||||
@ -13,18 +14,25 @@ use crate::{
|
||||
pub enum RuntimeError {
|
||||
#[error("Invalid error Index: {}", 0.to_string())]
|
||||
InvalidArrayIndex(Value),
|
||||
|
||||
#[error("Variable used but not declared: {0}")]
|
||||
VarUsedNotDeclared(String),
|
||||
|
||||
#[error("Can't index into non-array variable: {0}")]
|
||||
TryingToIndexNonArray(String),
|
||||
|
||||
#[error("Invalid value type for unary operation: {}", 0.to_string())]
|
||||
UnOpInvalidType(Value),
|
||||
|
||||
#[error("Incompatible binary operations. Operands don't match: {} {}", 0.to_string(), 1.to_string())]
|
||||
BinOpIncompatibleTypes(Value, Value),
|
||||
|
||||
#[error("Array access out of bounds: Accessed {0}, size is {1}")]
|
||||
ArrayOutOfBounds(usize, usize),
|
||||
|
||||
#[error("Division by zero")]
|
||||
DivideByZero,
|
||||
|
||||
#[error("Invalid number of arguments for function {0}. Expected {1}, got {2}")]
|
||||
InvalidNumberOfArgs(String, usize, usize),
|
||||
}
|
||||
@ -83,14 +91,24 @@ impl Interpreter {
|
||||
}
|
||||
|
||||
pub fn run_str(&mut self, code: &str) {
|
||||
let tokens = lex(code).unwrap();
|
||||
let tokens = match lex(code) {
|
||||
Ok(tokens) => tokens,
|
||||
Err(e) => nice_panic!("Lexing error: {}", e),
|
||||
};
|
||||
|
||||
if self.print_tokens {
|
||||
println!("Tokens: {:?}", tokens);
|
||||
}
|
||||
|
||||
let ast = parse(tokens).unwrap();
|
||||
let ast = match parse(tokens) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => nice_panic!("Parsing error: {}", e),
|
||||
};
|
||||
|
||||
self.run_ast(ast).unwrap();
|
||||
match self.run_ast(ast) {
|
||||
Ok(_) => (),
|
||||
Err(e) => nice_panic!("Runtime error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_ast(&mut self, mut ast: Ast) -> Result<(), RuntimeError> {
|
||||
@ -137,15 +155,13 @@ impl Interpreter {
|
||||
self.vartable.push(rhs);
|
||||
}
|
||||
|
||||
Statement::Block(block) => {
|
||||
match self.run_block(block)? {
|
||||
BlockExit::Return(val) => {
|
||||
self.vartable.truncate(framepointer);
|
||||
return Ok(BlockExit::Return(val));
|
||||
}
|
||||
_ => ()
|
||||
Statement::Block(block) => match self.run_block(block)? {
|
||||
BlockExit::Return(val) => {
|
||||
self.vartable.truncate(framepointer);
|
||||
return Ok(BlockExit::Return(val));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
|
||||
Statement::Loop(looop) => {
|
||||
// loop runs as long condition != 0
|
||||
@ -225,7 +241,10 @@ impl Interpreter {
|
||||
|
||||
// All of the arg expressions must be resolved before pushing the vars on the stack,
|
||||
// otherwise the stack positions are incorrect while resolving
|
||||
let args = args.iter().map(|arg| self.resolve_expr(arg)).collect::<Vec<_>>();
|
||||
let args = args
|
||||
.iter()
|
||||
.map(|arg| self.resolve_expr(arg))
|
||||
.collect::<Vec<_>>();
|
||||
for arg in args {
|
||||
self.vartable.push(arg?);
|
||||
}
|
||||
@ -234,8 +253,16 @@ impl Interpreter {
|
||||
let expected_num_args = self.funtable.get(*fun_stackpos).unwrap().argnames.len();
|
||||
|
||||
if expected_num_args != args_len {
|
||||
let fun_name = self.stringstore.lookup(*fun_name).cloned().unwrap_or("<unknown>".to_string());
|
||||
return Err(RuntimeError::InvalidNumberOfArgs(fun_name, expected_num_args, args_len));
|
||||
let fun_name = self
|
||||
.stringstore
|
||||
.lookup(*fun_name)
|
||||
.cloned()
|
||||
.unwrap_or("<unknown>".to_string());
|
||||
return Err(RuntimeError::InvalidNumberOfArgs(
|
||||
fun_name,
|
||||
expected_num_args,
|
||||
args_len,
|
||||
));
|
||||
}
|
||||
|
||||
match self.run_block_fp_offset(
|
||||
@ -414,7 +441,12 @@ impl Interpreter {
|
||||
match val {
|
||||
Value::I64(val) => format!("{}", val),
|
||||
Value::Array(val) => format!("{:?}", val.borrow()),
|
||||
Value::String(text) => format!("{}", self.stringstore.lookup(*text).unwrap_or(&"<invalid string>".to_string())),
|
||||
Value::String(text) => format!(
|
||||
"{}",
|
||||
self.stringstore
|
||||
.lookup(*text)
|
||||
.unwrap_or(&"<invalid string>".to_string())
|
||||
),
|
||||
Value::Void => format!("void"),
|
||||
}
|
||||
}
|
||||
|
||||
49
src/main.rs
49
src/main.rs
@ -1,18 +1,12 @@
|
||||
use std::{
|
||||
env::args,
|
||||
fs,
|
||||
// io::{stdin, stdout, Write},
|
||||
process::exit,
|
||||
};
|
||||
use std::{env::args, fs, process::exit};
|
||||
|
||||
use nek_lang::interpreter::Interpreter;
|
||||
use nek_lang::{interpreter::Interpreter, nice_panic};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct CliConfig {
|
||||
print_tokens: bool,
|
||||
print_ast: bool,
|
||||
no_optimizations: bool,
|
||||
// interactive: bool,
|
||||
file: Option<String>,
|
||||
}
|
||||
|
||||
@ -25,10 +19,11 @@ fn main() {
|
||||
"--token" | "-t" => conf.print_tokens = true,
|
||||
"--ast" | "-a" => conf.print_ast = true,
|
||||
"--no-opt" | "-n" => conf.no_optimizations = true,
|
||||
// "--interactive" | "-i" => conf.interactive = true,
|
||||
"--help" | "-h" => print_help(),
|
||||
file if conf.file.is_none() => conf.file = Some(file.to_string()),
|
||||
_ => panic!("Invalid argument: '{}'", arg),
|
||||
file if !arg.starts_with("-") && conf.file.is_none() => {
|
||||
conf.file = Some(file.to_string())
|
||||
}
|
||||
_ => nice_panic!("Error: Invalid argument '{}'", arg),
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,32 +34,15 @@ fn main() {
|
||||
interpreter.optimize_ast = !conf.no_optimizations;
|
||||
|
||||
if let Some(file) = &conf.file {
|
||||
let code = fs::read_to_string(file).expect(&format!("File not found: '{}'", file));
|
||||
let code = match fs::read_to_string(file) {
|
||||
Ok(code) => code,
|
||||
Err(_) => nice_panic!("Error: Could not read file '{}'", file),
|
||||
};
|
||||
interpreter.run_str(&code);
|
||||
} else {
|
||||
println!("Error: No file given\n");
|
||||
print_help();
|
||||
}
|
||||
|
||||
// TODO: The interactive prompt is currently broken due to the precalculated stack positions.
|
||||
// For this to still work, there needs to be a way to keep the stack in the interpreter after
|
||||
// runing once. Also somehow the stringstore and var stack from the last parsing stages would
|
||||
// need to be reused for the parser to work correctly
|
||||
|
||||
// if conf.interactive || conf.file.is_none() {
|
||||
// let mut code = String::new();
|
||||
|
||||
// loop {
|
||||
// print!(">> ");
|
||||
// stdout().flush().unwrap();
|
||||
|
||||
// code.clear();
|
||||
// stdin().read_line(&mut code).unwrap();
|
||||
|
||||
// if code.trim() == "exit" {
|
||||
// break;
|
||||
// }
|
||||
|
||||
// interpreter.run_str(&code);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
@ -73,7 +51,6 @@ fn print_help() {
|
||||
println!("-t, --token Print the lexed tokens");
|
||||
println!("-a, --ast Print the abstract syntax tree");
|
||||
println!("-n, --no-opt Disable the AST optimizations");
|
||||
// println!("-i, --interactive Interactive mode");
|
||||
println!("-h, --help Show this help screen");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
20
src/util.rs
20
src/util.rs
@ -1,3 +1,23 @@
|
||||
/// Exit the program with error code 1 and format-print the given text on stderr. This pretty much
|
||||
/// works like panic, but doesn't show the additional information that panic adds. Those can be
|
||||
/// interesting for debugging, but don't look that great when building a release executable for an
|
||||
/// end user.
|
||||
#[macro_export]
|
||||
macro_rules! nice_panic {
|
||||
($fmt:expr) => {
|
||||
{
|
||||
eprintln!($fmt);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
($fmt:expr, $($arg:tt)*) => {
|
||||
{
|
||||
eprintln!($fmt, $($arg)*);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The PutBackIter allows for items to be put back back and to be peeked. Putting an item back
|
||||
/// will cause it to be the next item returned by `next`. Peeking an item will get a reference to
|
||||
/// the next item in the iterator without removing it.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user