From f262a5d92b986af5e461b4a1527056a23bfe137c Mon Sep 17 00:00:00 2001 From: Servostar Date: Mon, 14 Nov 2022 17:50:34 +0100 Subject: [PATCH] improved error message composition --- Cargo.toml | 3 +- README.md | 4 +- src/inter/mod.rs | 39 +++++++++ src/main.rs | 24 ++++-- src/parser/data.rs | 23 +++++- src/parser/mod.rs | 157 ++++++++++++++++++------------------ src/parser/msg.rs | 50 ++++++++++++ src/token/mod.rs | 197 +++++++++++++++++++++++++++++++-------------- test.erpn | 34 ++++++++ 9 files changed, 381 insertions(+), 150 deletions(-) create mode 100644 src/inter/mod.rs create mode 100644 src/parser/msg.rs create mode 100644 test.erpn diff --git a/Cargo.toml b/Cargo.toml index 3414dd8..5ee8a6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,5 @@ edition = "2021" regex = "*" lazy_static = "1.4.0" rand = "0.8.5" -colored = "*" \ No newline at end of file +colored = "*" + diff --git a/README.md b/README.md index d6f9e92..fc4f6d3 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,9 @@ It contains features such as: // code block }contShort form for “continue”. Jumps to the next iteration of a loop breakExits a loop immediately yieldReturns from a function with an argument as return value
foo() = int {
     yield 4
-}
retShort form for “return”. This keyword takes no argument and returns from a function. NOTE: if ret is used in a function with a return type, an error occurs.  +}retShort form for “return”. This keyword takes no argument and returns from a function. NOTE: if ret is used in a function with a return type, an error occurs. pleaseDecides on runtime wether a block should be executed or not.
please {
+	// code
+}
### Example code diff --git a/src/inter/mod.rs b/src/inter/mod.rs new file mode 100644 index 0000000..021a9f5 --- /dev/null +++ b/src/inter/mod.rs @@ -0,0 +1,39 @@ +use std::io::Write; + +use crate::data::*; + +fn write_expr(file: &mut std::fs::File, indent: &mut String, expr: &Expr) { + match expr { + Expr::Block(block) => write_block(file, indent, block), + Expr::Term(term) => write_term(file, indent, term) + } +} + +fn write_term(file: &mut std::fs::File, indent: &mut String, term: &std::collections::VecDeque) { + for token in term.iter() { + write!(file, "{}{}\n", indent, token).unwrap(); + } +} + +fn write_block(file: &mut std::fs::File, indent: &mut String, block: &std::collections::VecDeque) { + indent.push('\t'); + + block.iter().for_each(|expr| write_expr(file, indent, expr)); + + indent.pop(); +} + +pub fn convert_to_erpn<'a>(funcs: &mut Vec>, declrs: &Vec>) { + + let mut file = std::fs::OpenOptions::new().write(true).create(true).truncate(true).open("test.erpn").unwrap(); + + let mut indent = String::new(); + + for (x, func) in funcs.iter().enumerate() { + // write function name + write!(&mut file, "{}:\n", declrs[x].name.unwrap()).unwrap(); + + // write down function body + write_expr(&mut file, &mut indent, func.expr.as_ref().unwrap()); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 53a8e7e..2e933d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,10 +5,11 @@ mod token; mod parser; // translate a tree of functions and expressions to pseudo assembly // designed for a virtual stack machiene +mod inter; use token::*; use parser::*; -use colored::{Colorize}; +use colored::Colorize; pub fn message(typ: MessageType, msg: String) { println!("{}: {}", typ.to_colored(), msg.bold().bright_white()); @@ -19,24 +20,33 @@ fn main() { let source = r" # this is pi -pi = rat 3.1415926535 +pi = rat 5.1415926535 -foo(x :int, y: rat) = bool { +foo(x:int, y:rat) = bool { x:int = 5 + 6 - unless(x < 6) { + unless (x < 6) { yield true + please { + + } + unless(x < 6) { yield true } } - + + -- comment yield false } -main { - +main() = int { + a = 4 + b = 5.5 + c = true + r = foo(3, 3.4) + 0 } "; diff --git a/src/parser/data.rs b/src/parser/data.rs index a5e6b5b..9a69b52 100644 --- a/src/parser/data.rs +++ b/src/parser/data.rs @@ -1,8 +1,16 @@ use core::panic; use std::collections::{VecDeque}; -use crate::token::{Token}; +use crate::token::{Token, DebugErr}; use crate::Prim; +pub struct Diagnostics<'a> { + /// terminating factor on error + err: Option>, + /// additional warning and informations + /// all non critical + hints: Vec> +} + #[derive(Debug)] pub struct Func<'a> { /// raw tokens @@ -155,4 +163,15 @@ impl<'a> Scope<'a> { } None } -} \ No newline at end of file + + pub fn new<'b>() -> Scope<'b> { + Scope { + args: None, + vars: vec![], + func_return_typ: None, + expr_yield: false, + yields: false, + cond_count: 0 + } + } +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 0e4b046..a2a4b18 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -3,6 +3,7 @@ use std::{collections::{VecDeque}, vec}; use crate::token::{Token, Operator, Assoc, Prim, MessageType, Keyword}; pub mod data; +pub mod msg; use data::*; @@ -24,11 +25,12 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) macro_rules! finish_func { ($dbginf:expr) => { if declrs.contains(&declr) { - panic!("Function defined multiple times: {declr}") + println!("{}", $dbginf.make_msg_w_ext(crate::msg::ERR10, format!("Multiple definitions: {declr}"))); + panic!() } if declr.results && declr.result_typ.is_none() { - $dbginf.print(MessageType::Error, format!("Function is missing return type: {}", declr).as_str(), source); + println!("{}", $dbginf.make_msg_w_ext(crate::msg::ERR11, format!("for function {declr}"))); panic!(); } @@ -48,14 +50,16 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) Token::Delemiter(char, dbginf) => match char { '{' => { brace_cnt += 1; + + // we have an found open function body if brace_cnt == 1 { if declr.name.is_none() { - dbginf.print(MessageType::Error, "Anonymous function not permitted", source); + println!("{}", dbginf.make_msg(crate::msg::ERR12)); panic!(); } if paren_cnt > 0 { - dbginf.print(MessageType::Error, "Unclosed parameter list", source); + println!("{}", dbginf.make_msg(crate::msg::ERR13)); panic!(); } @@ -66,6 +70,14 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) }, '}' => { brace_cnt -= 1; + + // we closed one to much + if brace_cnt < 0 { + println!("{}", dbginf.make_msg(crate::msg::ERR14)); + panic!(); + } + + // end of function body if brace_cnt == 0 { finish_func!(dbginf); continue; @@ -74,17 +86,12 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) _ => () } - Token::Type(typ, dbginf) => { + Token::Type(typ, dbginf) => if declr.result_typ.is_none() { if declr.results { - if declr.result_typ.is_some() { - dbginf.print(MessageType::Error, "Function must return either nothing or a single type", source); - panic!(); - } - declr.result_typ = Some(*typ); continue; } else { - dbginf.print(MessageType::Error, "Missing equal sign", source); + println!("{}", dbginf.make_msg(crate::msg::ERR16)); panic!(); } }, @@ -104,11 +111,11 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) Token::Operator(op, dbginf) => match op { Operator::Assign => { if declr.results { - dbginf.print(MessageType::Error, "double function assignment", source); + println!("{}", dbginf.make_msg(crate::msg::ERR17)); panic!(); } if declr.name.is_none() { - dbginf.print(MessageType::Error, "Anonymous function", source); + println!("{}", dbginf.make_msg(crate::msg::ERR12)); panic!(); } @@ -120,17 +127,16 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) } Token::Word(text, dbginf) => { - + if declr.name.is_some() { if declr.args.is_none() { - dbginf.print(MessageType::Error, "multiple function names", source); + println!("{}", dbginf.make_msg(crate::msg::ERR18)); panic!(); } } else if brace_cnt > 0 { - dbginf.print(MessageType::Error, "brace count missmatch", source); + println!("{}", dbginf.make_msg(crate::msg::ERR13)); panic!(); - } - else { + } else { declr.name = Some(text); continue; } @@ -138,11 +144,11 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) Token::Assign(name, _, dbginf) => { if declr.results { - dbginf.print(MessageType::Error, "double function assignment", source); + println!("{}", dbginf.make_msg(crate::msg::ERR17)); panic!(); } if declr.name.is_some() { - dbginf.print(MessageType::Error, "multiple function names", source); + println!("{}", dbginf.make_msg(crate::msg::ERR18)); panic!(); } @@ -160,7 +166,7 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) if paren_cnt == 1 { if declr.args.is_some() { - dbginf.print(MessageType::Error, "double parameter list", source); + println!("{}", dbginf.make_msg(crate::msg::ERR19)); panic!(); } @@ -187,18 +193,18 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) else if let Some(args) = &mut declr.args { if paren_cnt == 0 { - top.print(MessageType::Error, "token is no parameter", source); + println!("{}", top.create_msg(crate::msg::ERR20)); panic!(); } match &top { Token::Decl(name, typ, _dbginf) => args.push((name, *typ)), Token::Word(_, dbginf) => { - dbginf.print(MessageType::Error, "type declaration missing", source); + println!("{}", dbginf.make_msg(crate::msg::ERR21)); panic!() }, _ => { - top.print(MessageType::Error, "argument must be declaration", source); + println!("{}", top.create_msg(crate::msg::ERR23)); panic!() } } @@ -209,7 +215,7 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) match &top { Token::LineBreak(_) | Token::Terminator(_) => (), // valid whitespace _ => { - top.print(MessageType::Error, "unresolvable token", source); + println!("{}", top.create_msg(crate::msg::ERR22)); panic!() } } @@ -217,7 +223,7 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) if let Some(raw) = func.raw { if let Some(front) = raw.front() { - front.print(MessageType::Error, "Open function body", source); + println!("{}", front.create_msg(crate::msg::ERR15)); panic!(); } } @@ -240,7 +246,7 @@ fn discover_exprs<'a>(functions: &mut Vec>, _: &Vec>, source: match &top { Token::LineBreak(dbginf) | Token::Terminator(dbginf) => if !expr.is_empty() { blocks.last_mut().unwrap_or_else(|| { - dbginf.print(MessageType::Error, "curly brace missmatch", source); + println!("{}", dbginf.make_msg(crate::msg::ERR41)); panic!() }).push_back(Expr::Term(expr)); expr = VecDeque::new(); @@ -249,7 +255,7 @@ fn discover_exprs<'a>(functions: &mut Vec>, _: &Vec>, source: Token::Delemiter(char, dbginf) => match char { '{' => { blocks.last_mut().unwrap_or_else(|| { - dbginf.print(MessageType::Error, "curly brace missmatch", source); + println!("{}", dbginf.make_msg(crate::msg::ERR41)); panic!() }).push_back(Expr::Term(expr)); expr = VecDeque::new(); @@ -260,11 +266,12 @@ fn discover_exprs<'a>(functions: &mut Vec>, _: &Vec>, source: // pop topmost block of the stack, storing it in the next lower block if let Some(block) = blocks.pop() { blocks.last_mut().unwrap_or_else(|| { - dbginf.print(MessageType::Error, "curly brace missmatch", source); + println!("{}", dbginf.make_msg(crate::msg::ERR41)); panic!() }).push_back(Expr::Block(block)); } else { - panic!("Curly brace missmatch") + println!("{}", dbginf.make_msg(crate::msg::ERR40)); + panic!() } continue; }, @@ -278,7 +285,7 @@ fn discover_exprs<'a>(functions: &mut Vec>, _: &Vec>, source: if !expr.is_empty() { blocks.last_mut().unwrap_or_else(|| { - expr.back().unwrap().print(MessageType::Error, "curly brace missmatch", source); + println!("{}", expr.back().unwrap().create_msg(crate::msg::ERR40)); panic!() }).push_back(Expr::Term(expr)); } @@ -292,26 +299,26 @@ fn discover_exprs<'a>(functions: &mut Vec>, _: &Vec>, source: } fn check_var_typ(typ: &mut Option, operands: &mut Vec, dbginf: &crate::token::DebugInfo, source: &str) { + if let Some(value) = operands.pop() { if !operands.is_empty() { - dbginf.print(MessageType::Error, format!("Expr does't resolve to a single value but multiple").as_str(), source); + println!("{}", dbginf.make_msg(crate::msg::ERR50)); panic!(); } if let Some(typ) = typ { if !typ.is_equal(value) { - dbginf.print(MessageType::Error, format!("Variable has type {:?} but {:?} was given", typ, value).as_str(), source); + println!("{}", dbginf.make_msg_w_ext(crate::msg::ERR51, format!("needed {:?} got {:?}", typ, value) )); panic!(); } } else { // assign a type to untyped variable - dbginf.print(MessageType::Info, format!("Variable has no fixed type, guessing type: {:?}", value).as_str(), source); - + println!("{}", dbginf.make_msg_w_ext(crate::msg::INF51, format!("gessed type: {:?}", value) )); *typ = Some(value); } } else { - dbginf.print(MessageType::Error, "No result to bind variable to", source); + println!("{}", dbginf.make_msg(crate::msg::ERR52)); panic!(); } } @@ -320,7 +327,7 @@ fn process_keyword(keyword: Keyword, _: &Vec, scope: &mut Scope, operands match keyword { Keyword::If | Keyword::While => { if operands.len() != 1 { - dbginf.print(MessageType::Error, format!("Expected single boolean got {} values", operands.len()).as_str(), source); + println!("{}", dbginf.make_msg_w_ext(crate::msg::ERR53, format!("got {:?} values", operands.len()) )); panic!(); } @@ -328,7 +335,7 @@ fn process_keyword(keyword: Keyword, _: &Vec, scope: &mut Scope, operands match operand { Prim::Bool => (), _ => { - dbginf.print(MessageType::Error, format!("Expected boolean, got {:?}", operand).as_str(), source); + println!("{}", dbginf.make_msg_w_ext(crate::msg::ERR53, format!("got {:?}", operand) )); panic!(); } } @@ -336,30 +343,30 @@ fn process_keyword(keyword: Keyword, _: &Vec, scope: &mut Scope, operands }, Keyword::Return => { if scope.func_return_typ.is_some() { - dbginf.print(MessageType::Error, "cannot return function, did u mean to use `yield`?", source); + println!("{}", dbginf.make_msg_w_ext(crate::msg::ERR54, "perhaps use `yield`" )); panic!(); } } Keyword::Yield => { if operands.len() != 1 { - dbginf.print(MessageType::Error, format!("Expected single value but got {} values", operands.len()).as_str(), source); + println!("{}", dbginf.make_msg_w_ext(crate::msg::ERR55, format!("got {:?} instead of 1", operands.len()) )); panic!(); } if let Some(operand) = operands.pop() { if let Some(typ) = scope.func_return_typ { if typ != operand { - dbginf.print(MessageType::Error, format!("Expected {:?} but got {:?}", typ, operand).as_str(), source); + println!("{}", dbginf.make_msg_w_ext(crate::msg::ERR59, format!("expected {:?} got {:?}", typ, operand) )); panic!(); } println!("{}", scope.cond_count); scope.yields = scope.cond_count == 1; } else { - dbginf.print(MessageType::Error, format!("Function does not return anything").as_str(), source); + println!("{}", dbginf.make_msg(crate::msg::ERR57)); panic!(); } } else { - dbginf.print(MessageType::Error, format!("Yield must return something").as_str(), source); + println!("{}", dbginf.make_msg(crate::msg::ERR58)); panic!(); } } @@ -367,12 +374,12 @@ fn process_keyword(keyword: Keyword, _: &Vec, scope: &mut Scope, operands } } -fn collapse_operation(operation: &Token, declrs: &Vec, scope: &mut Scope, operands: &mut Vec, source: &str) { +fn collapse_operation(operation: &mut Token, declrs: &Vec, scope: &mut Scope, operands: &mut Vec, source: &str) { match operation { Token::Operator(op, dbginf) => op.operate(operands, &dbginf, source), Token::Assign(name, mut typ, dbginf) => { check_var_typ(&mut typ, operands, &dbginf, source); - scope.decl_var((*name).to_owned(), typ.to_owned()); + scope.decl_var((*name).to_owned(), typ); }, Token::Func(name, dbginf) => call_func(name, declrs, scope, operands, &dbginf, source), Token::Keyword(keyword, dbginf) => process_keyword(*keyword, declrs, scope, operands, &dbginf, source), @@ -387,7 +394,7 @@ fn call_func(name: &str, declrs: &Vec, _: &mut Scope, operands: &mut Vec< if let Some(args) = &declr.args { if args.len() > operands.len() { - dbginf.print(MessageType::Error, format!("Expected {} parameters but got {}", args.len(), operands.len()).as_str(), source); + println!("{}", dbginf.make_msg_w_ext(crate::msg::ERR60, format!("expected {:?} got {:?}", args.len(), operands.len()) )); panic!() } @@ -395,7 +402,7 @@ fn call_func(name: &str, declrs: &Vec, _: &mut Scope, operands: &mut Vec< let operand = operands.first().unwrap(); if !operand.is_equal(arg.1) { - dbginf.print(MessageType::Error, format!("Expected {:?} as parameter {x}, but got {:?}", arg, operand).as_str(), source); + println!("{}", dbginf.make_msg_w_ext(crate::msg::ERR61, format!("expected {:?} got {:?}", arg, operand) )); panic!() } @@ -434,7 +441,7 @@ fn parse_term<'a>(term: &mut VecDeque>, declrs: &Vec>, scope output.push_back(Token::Var(text, *dbginf)); continue; } - dbginf.print(MessageType::Error, "Unknown word", source); + println!("{}", dbginf.make_msg(crate::msg::ERR62)); panic!() } Token::Bool(_, _) => { @@ -442,7 +449,7 @@ fn parse_term<'a>(term: &mut VecDeque>, declrs: &Vec>, scope value_stack.push(Prim::Bool) }, Token::Number(_, hint, _) => { - output.push_back(token); + output.push_back(token.clone()); value_stack.push(Prim::UntypedNum(*hint)) }, Token::Assign(_, _, _) => { @@ -450,18 +457,18 @@ fn parse_term<'a>(term: &mut VecDeque>, declrs: &Vec>, scope }, Token::Keyword(_, _) => op_stack.push(token), - Token::Delemiter(char, _) => { + Token::Delemiter(char, dbginf) => { match char { '(' => op_stack.push(token), ')' => { - while let Some(token) = op_stack.pop() { + while let Some(mut token) = op_stack.pop() { match &token { Token::Delemiter(char, _) => if *char == '(' { if let Some(next) = op_stack.last() { match &next { Token::Func(_, _) => { - let token = op_stack.pop().unwrap(); - collapse_operation(&token, declrs, scope, &mut value_stack, source); + let mut token = op_stack.pop().unwrap(); + collapse_operation(&mut token, declrs, scope, &mut value_stack, source); output.push_back(token); }, _ => () @@ -470,26 +477,30 @@ fn parse_term<'a>(term: &mut VecDeque>, declrs: &Vec>, scope continue 'outer; }, _ => { - collapse_operation(&token, declrs, scope, &mut value_stack, source); + collapse_operation(&mut token, declrs, scope, &mut value_stack, source); output.push_back(token) } } } - panic!("Mismatched right parenthesis") + println!("{}", dbginf.make_msg(crate::msg::ERR64)); + panic!(); }, - _ => panic!("Misplaced character: '{char}'") + _ => { + println!("{}", dbginf.make_msg(crate::msg::ERR65)); + panic!() + } } } Token::Operator(op, _) => { let prec0 = op.prec(); - while let Some(top) = op_stack.last(){ + while let Some(mut top) = op_stack.last_mut(){ match &top { Token::Operator(op1, _) => { let prec1 = op1.prec(); if prec1 > prec0 || prec0 == prec1 && op.assoc() == Assoc::Left { - collapse_operation(top, declrs, scope, &mut value_stack, source); + collapse_operation(&mut top, declrs, scope, &mut value_stack, source); output.push_back(op_stack.pop().unwrap()); continue } @@ -504,20 +515,20 @@ fn parse_term<'a>(term: &mut VecDeque>, declrs: &Vec>, scope } } - while let Some(token) = op_stack.pop() { + while let Some(mut token) = op_stack.pop() { match &token { - Token::Delemiter(char, _) => if *char == '(' { - panic!("Mismatched parenthesis") + Token::Delemiter(char, dbginf) => if *char == '(' { + println!("{}", dbginf.make_msg(crate::msg::ERR63)); }, _ => { - collapse_operation(&token, declrs, scope, &mut value_stack, source); + collapse_operation(&mut token, declrs, scope, &mut value_stack, source); output.push_back(token) } } } if value_stack.len() > 1 { - output[0].print(MessageType::Error, "expression resolves to multiple results", source); + println!("{}", output[0].create_msg(crate::msg::ERR50)); panic!(); } @@ -525,7 +536,7 @@ fn parse_term<'a>(term: &mut VecDeque>, declrs: &Vec>, scope if scope.expr_yield { let yielded = value_stack.pop().unwrap(); if !yielded.is_equal(scope.func_return_typ.unwrap()) { - output[0].print(MessageType::Error, format!("expected {:?} but got {:?}", scope.func_return_typ.unwrap(), yielded).as_str(), source); + println!("{}", output[0].create_msg_w_ext(crate::msg::ERR59, format!("expected: {:?} got {:?}", scope.func_return_typ.unwrap(), yielded) )); panic!(); } } @@ -556,14 +567,7 @@ fn parse_block<'a>(block: &mut Block<'a>, declrs: &Vec>, scope: &mut S } fn parse_exprs<'a>(funcs: &mut Vec>, declrs: &Vec>, source: &'a str) { - let mut scope = Scope { - args: None, - vars: vec![], - func_return_typ: None, - expr_yield: false, - yields: false, - cond_count: 0 - }; + let mut scope = Scope::new(); for (x, func) in funcs.iter_mut().enumerate() { match func.expr.as_mut().expect("Function has no body") { @@ -576,7 +580,7 @@ fn parse_exprs<'a>(funcs: &mut Vec>, declrs: &Vec>, source: & parse_block(block, declrs, &mut scope, source); if scope.func_return_typ.is_some() && !scope.yields && !scope.expr_yield { - crate::message(MessageType::Error, format!("Function {} missing return value at some point", declrs[x])); + //token.create_msg_w_ext(crate::msg::ERR56, format!("at function {}", declrs[x])); panic!(); } }, @@ -590,13 +594,12 @@ fn parse_exprs<'a>(funcs: &mut Vec>, declrs: &Vec>, source: & /// A function has a name followed by an optional parameter list, followed by an optional equal sign and block. pub fn parse<'a>(tokens: &mut VecDeque>, source: &'a str) -> Vec> { let (mut funcs, declrs) = discover_functions(tokens, source); - + + // make all of this shit return a mutable diagnostics struct. discover_exprs(&mut funcs, &declrs, source); parse_exprs(&mut funcs, &declrs, source); - for (x, f) in funcs.iter().enumerate() { - println!("{:#?}{:#?}", declrs[x], f); - } + crate::inter::convert_to_erpn(&mut funcs, &declrs); funcs } diff --git a/src/parser/msg.rs b/src/parser/msg.rs new file mode 100644 index 0000000..9f85354 --- /dev/null +++ b/src/parser/msg.rs @@ -0,0 +1,50 @@ + +use crate::token::DebugMsg; +use crate::token::MessageType::Error; +use crate::token::MessageType::Warning; +use crate::token::MessageType::Info; + +pub const ERR10: &DebugMsg = &DebugMsg { typ: Error, code: 10, msg: "Function defined multiple times" }; +pub const ERR11: &DebugMsg = &DebugMsg { typ: Error, code: 11, msg: "Function missing return type" }; +pub const ERR12: &DebugMsg = &DebugMsg { typ: Error, code: 12, msg: "Function missing name" }; +pub const ERR13: &DebugMsg = &DebugMsg { typ: Error, code: 13, msg: "Open parameter list" }; +pub const ERR14: &DebugMsg = &DebugMsg { typ: Error, code: 14, msg: "Closed non existant function body" }; +pub const ERR15: &DebugMsg = &DebugMsg { typ: Error, code: 15, msg: "Open function body" }; +pub const ERR16: &DebugMsg = &DebugMsg { typ: Error, code: 16, msg: "Missing assign for return type" }; +pub const ERR17: &DebugMsg = &DebugMsg { typ: Error, code: 17, msg: "Double function assign" }; +pub const ERR18: &DebugMsg = &DebugMsg { typ: Error, code: 18, msg: "Function has multiple names" }; +pub const ERR19: &DebugMsg = &DebugMsg { typ: Error, code: 19, msg: "Multiple parameter lists" }; +pub const ERR20: &DebugMsg = &DebugMsg { typ: Error, code: 20, msg: "not a valid parameter" }; +pub const ERR21: &DebugMsg = &DebugMsg { typ: Error, code: 21, msg: "type declaration missing for parameter" }; +pub const ERR22: &DebugMsg = &DebugMsg { typ: Error, code: 22, msg: "token outside of function definition" }; +pub const ERR23: &DebugMsg = &DebugMsg { typ: Error, code: 23, msg: "token must be declaration" }; + +pub const ERR40: &DebugMsg = &DebugMsg { typ: Error, code: 40, msg: "Open block" }; +pub const ERR41: &DebugMsg = &DebugMsg { typ: Error, code: 41, msg: "Closed non existant block" }; + +pub const INF51: &DebugMsg = &DebugMsg { typ: Info, code: 51, msg: "No variable type provided" }; + +pub const ERR50: &DebugMsg = &DebugMsg { typ: Error, code: 50, msg: "Expression has multiple values" }; +pub const ERR51: &DebugMsg = &DebugMsg { typ: Error, code: 51, msg: "Missmatched variable types" }; +pub const ERR52: &DebugMsg = &DebugMsg { typ: Error, code: 52, msg: "Unable to assign variable type" }; +pub const ERR53: &DebugMsg = &DebugMsg { typ: Error, code: 53, msg: "Expected single boolean value" }; +pub const ERR54: &DebugMsg = &DebugMsg { typ: Error, code: 54, msg: "Cannot return from function without result" }; +pub const ERR55: &DebugMsg = &DebugMsg { typ: Error, code: 55, msg: "Expected single value" }; +pub const ERR56: &DebugMsg = &DebugMsg { typ: Error, code: 56, msg: "Function missing return value" }; +pub const ERR57: &DebugMsg = &DebugMsg { typ: Error, code: 57, msg: "Function does not return anything" }; +pub const ERR58: &DebugMsg = &DebugMsg { typ: Error, code: 58, msg: "Yield must return a value" }; +pub const ERR59: &DebugMsg = &DebugMsg { typ: Error, code: 59, msg: "Missmatched function return type" }; + +pub const ERR60: &DebugMsg = &DebugMsg { typ: Error, code: 60, msg: "Missmatched parameter count" }; +pub const ERR61: &DebugMsg = &DebugMsg { typ: Error, code: 61, msg: "Missmatched parameter type" }; +pub const ERR62: &DebugMsg = &DebugMsg { typ: Error, code: 62, msg: "Unknown symbol" }; +pub const ERR63: &DebugMsg = &DebugMsg { typ: Error, code: 63, msg: "Missing left parenthesis" }; +pub const ERR64: &DebugMsg = &DebugMsg { typ: Error, code: 64, msg: "Missing right parenthesis" }; +pub const ERR65: &DebugMsg = &DebugMsg { typ: Error, code: 65, msg: "Missplaced character" }; + +pub const ERR70: &DebugMsg = &DebugMsg { typ: Error, code: 70, msg: "Unknown type declaration"}; +pub const ERR71: &DebugMsg = &DebugMsg { typ: Error, code: 71, msg: "Unable to identify characters as token"}; +pub const ERR72: &DebugMsg = &DebugMsg { typ: Error, code: 72, msg: "Unknown operator"}; +pub const ERR73: &DebugMsg = &DebugMsg { typ: Error, code: 73, msg: "Missmatched types for operation"}; +pub const ERR74: &DebugMsg = &DebugMsg { typ: Error, code: 74, msg: "Missing operands"}; + diff --git a/src/token/mod.rs b/src/token/mod.rs index 6cfb8b8..fc8bc4f 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -1,4 +1,4 @@ -use std::{collections::{VecDeque}}; +use std::collections::VecDeque; use colored::{Colorize, ColoredString}; #[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] @@ -82,7 +82,7 @@ impl Operator { fn present_types(operands: &[Prim], types: &[Prim], r#yield: Prim, dbginf: &DebugInfo, source: &str) -> Option { if operands.len() < types.len() { - dbginf.print(MessageType::Error, format!("Missing {} operands", types.len() - operands.len()).as_str(), source); + println!("{}", dbginf.make_msg_w_ext(crate::msg::ERR74, format!("required {} got {}", types.len() - operands.len(), operands.len()))); panic!() } @@ -130,7 +130,7 @@ impl Operator { operands.pop(); operands.push(result); } else { - dbginf.print(MessageType::Error, format!("Missmatched types for {:?}, expected either two integer or rationals", self).as_str(), source); + println!("{}", dbginf.make_msg_w_ext(crate::msg::ERR73, "expected two numbers")); panic!() } }, @@ -144,7 +144,7 @@ impl Operator { operands.pop(); operands.push(result); } else { - dbginf.print(MessageType::Error, format!("Missmatched types for {:?}, expected two booleans", self).as_str(), source); + println!("{}", dbginf.make_msg_w_ext(crate::msg::ERR73, "expected two booleans")); panic!() } }, @@ -169,11 +169,13 @@ impl Operator { operands.pop(); operands.push(result); } else { - dbginf.print(MessageType::Error, format!("Missmatched types for {:?}, expected two numbers", self).as_str(), source); + println!("{}", dbginf.make_msg_w_ext(crate::msg::ERR73, "expected two numbers")); panic!() } }, - _ => panic!("Unknown operator: {:?}", self) + _ => { + panic!("Unknown operator"); + } } } } @@ -187,7 +189,8 @@ pub enum Keyword { Break, Continue, Return, - Yield + Yield, + Please, } impl Keyword { @@ -200,6 +203,7 @@ impl Keyword { "cont" => Keyword::Continue, "ret" => Keyword::Return, "yield" => Keyword::Yield, + "please" => Keyword::Please, _ => panic!("Text not a known keyword {text}") } } @@ -239,7 +243,7 @@ impl Prim { "bool" => Prim::Bool, _ => { - dbginf.print(MessageType::Error, "Unknown type declaration", source); + println!("{}", dbginf.make_msg(&crate::msg::ERR70)); panic!() } } @@ -269,14 +273,64 @@ impl Prim { } } +pub struct DebugMsg { + pub code: i32, + pub typ: MessageType, + pub msg: &'static str, +} + +pub struct DebugErr<'a> { + info: DebugInfo<'a>, + /// generic error description + msg: &'static DebugMsg, + /// extra message which is case specific + ext: Option, +} + +impl<'a> std::fmt::Display for DebugErr<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // write header as: + // `Error (56) some syntax error message in line 5:` + f.write_fmt(format_args!("{} ({}) {} in line {}: {}\n", + self.msg.typ.to_colored(), + self.msg.code, + self.msg.msg.bold().bright_white(), + self.info.line, + self.ext.as_ref().unwrap_or(&String::new()) + )); + // write additional information + f.write_fmt(format_args!("{}", self.info)) + } +} + #[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub struct DebugInfo { +pub struct DebugInfo<'a> { /// index in source string where the token begins in the current line start: usize, /// index in source string where the token ends in the current line end: usize, /// line number where the line in which the token is begins - line: usize + line: usize, + + source: &'a str +} + +impl<'a> DebugInfo<'a> { + pub fn make_msg(&self, msg: &'static DebugMsg) -> DebugErr { + DebugErr { + info: self.clone(), + msg, + ext: None + } + } + + pub fn make_msg_w_ext(&self, msg: &'static DebugMsg, ext: T) -> DebugErr where T: Into { + DebugErr { + info: self.clone(), + msg, + ext: Some(ext.into()) + } + } } #[derive(Debug)] @@ -301,79 +355,97 @@ impl MessageType { } } -impl DebugInfo { - /// print message in the form of: - /// ```text - /// Error (message) in line 7: token `code` - /// somewhere in here: - /// --> `code line` - /// ^^^^ - /// ``` - pub fn print<'a>(&self, typ: MessageType, msg: &str, source: &'a str) { - println!("{} ({}) in line {}: token `{}`", typ.to_colored(), msg.bold().bright_white(), self.line, &source[self.start..self.end].bold()); - println!(" somewhere in here:\n --> `{}`", source.lines().nth(self.line).unwrap().trim().bold().bright_white()); +impl<'a> std::fmt::Display for DebugInfo<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!(" somewhere in here:\n --> `{}`\n", + self.source.lines().nth(self.line).unwrap().trim().bold().bright_white())).unwrap(); - for _ in 0..self.start + 6 { - print!(" "); - } - for _ in self.start..self.end { - print!("^"); - } - println!("\n"); + (0..self.start + 6).into_iter().for_each(|_| f.write_str(" ").unwrap()); + (self.start..self.end).into_iter().for_each(|_| f.write_str("^").unwrap()); + + Ok(()) } } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] /// A token represents a basic building block for source code. /// They give a meaning to patterns of chars allowing to interpret them. pub enum Token<'a> { // base tokens that can simply be split to from raw source code - Word(&'a str, DebugInfo), + Word(&'a str, DebugInfo<'a>), /// Single symbol delemiter like ```(```,```}``` - Delemiter(char, DebugInfo), - Operator(Operator, DebugInfo), - Number(&'a str, NumberClassHint, DebugInfo), - LineBreak(DebugInfo), - Func(&'a str, DebugInfo), + Delemiter(char, DebugInfo<'a>), + Operator(Operator, DebugInfo<'a>), + Number(&'a str, NumberClassHint, DebugInfo<'a>), + LineBreak(DebugInfo<'a>), + Func(&'a str, DebugInfo<'a>), /// Variable - Var(&'a str, DebugInfo), + Var(&'a str, DebugInfo<'a>), /// Function argument - Arg(&'a str, DebugInfo), + Arg(&'a str, DebugInfo<'a>), /// Variable assignment in the form of ```name = ``` - Assign(&'a str, Option, DebugInfo), + Assign(&'a str, Option, DebugInfo<'a>), /// Variable type declaration in the form of ```name:type``` - Decl(&'a str, Prim, DebugInfo), - Bool(bool, DebugInfo), + Decl(&'a str, Prim, DebugInfo<'a>), + Bool(bool, DebugInfo<'a>), /// Keywords like ```if```,```break```,```while``` - Keyword(Keyword, DebugInfo), - Type(Prim, DebugInfo), + Keyword(Keyword, DebugInfo<'a>), + Type(Prim, DebugInfo<'a>), /// Semicolon - Terminator(DebugInfo) + Terminator(DebugInfo<'a>) } -impl<'a> Token<'a> { - /// redirect for ```DebugInfo.print()``` - pub fn print(&self, error: MessageType, arg: &str, source: &str) { +impl<'a> std::fmt::Display for Token<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Token::Type(_, dbginf) => dbginf.print(error, arg, source), - Token::Word(_, dbginf) => dbginf.print(error, arg, source), - Token::Delemiter(_, dbginf) => dbginf.print(error, arg, source), - Token::Operator(_, dbginf) => dbginf.print(error, arg, source), - Token::Number(_, _, dbginf) => dbginf.print(error, arg, source), - Token::LineBreak(dbginf) => dbginf.print(error, arg, source), - Token::Func(_, dbginf) => dbginf.print(error, arg, source), - Token::Var(_, dbginf) => dbginf.print(error, arg, source), - Token::Arg(_, dbginf) => dbginf.print(error, arg, source), - Token::Assign(_, _, dbginf) => dbginf.print(error, arg, source), - Token::Decl(_, _, dbginf) => dbginf.print(error, arg, source), - Token::Bool(_, dbginf) => dbginf.print(error, arg, source), - Token::Keyword(_, dbginf) => dbginf.print(error, arg, source), - Token::Terminator(dbginf) => dbginf.print(error, arg, source), + Token::Type(t, _) => f.write_fmt(format_args!("__Type {:?}", t)), + Token::Word(w, _) => f.write_fmt(format_args!("__Word {:?}", w)), + Token::Delemiter(d, _) => f.write_fmt(format_args!("__Delemiter {:?}", d)), + Token::Operator(o, _) => f.write_fmt(format_args!("{:?}", o)), + Token::Number(n, hint, _) => f.write_fmt(format_args!("Load {:?} {}", hint, n)), + Token::LineBreak(_) => f.write_str("__Line-break"), + Token::Func(name, _) => f.write_fmt(format_args!("Call {}", name)), + Token::Var(v, _) => f.write_fmt(format_args!("Load Var {}", v)), + Token::Arg(a, _) => f.write_fmt(format_args!("Load Arg {}", a)), + Token::Assign(a, typ, _) => f.write_fmt(format_args!("Store {:?} {}", typ, a)), + Token::Decl(d, typ, _) => f.write_fmt(format_args!("__Decl {:?} as {}", typ, d)), + Token::Bool(b, _) => f.write_fmt(format_args!("Load Bool {}", b)), + Token::Keyword(k, _) => f.write_fmt(format_args!("{:?}", k)), + Token::Terminator(_) => f.write_str("__Terminator"), } } } -const TOKEN_REGEX_SRC: &'static str = r"(#.*)|(unless|while|loop|break|cont|ret|yield)|(int|rat|bool)|(true|false|ye|no|maybe)|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))?\s*=|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))|([A-Za-z_]+)|(\d*\.?\d+)|(!=|==|<=|<=|[&|+\-*/<>=])|([(){}])|(\n)|(;)"; +impl<'a> Token<'a> { + fn debug_info(&self) -> &DebugInfo { + match self { + Token::Type(_, d) => d, + Token::Word(_, d) => d, + Token::Delemiter(_, d) => d, + Token::Operator(_, d) => d, + Token::Number(_, _, d) => d, + Token::LineBreak(d) => d, + Token::Func(_, d) => d, + Token::Var(_, d) => d, + Token::Arg(_, d) => d, + Token::Assign(_, _, d) => d, + Token::Decl(_, _, d) => d, + Token::Bool(_, d) => d, + Token::Keyword(_, d) => d, + Token::Terminator(d) => d, + } + } + + pub fn create_msg(&self, msg: &'static DebugMsg) -> DebugErr { + DebugErr { info: self.debug_info().clone(), msg, ext: None } + } + + pub fn create_msg_w_ext(&self, msg: &'static DebugMsg, ext: T) -> DebugErr where T: Into { + DebugErr { info: self.debug_info().clone(), msg, ext: Some(ext.into()) } + } +} + +const TOKEN_REGEX_SRC: &'static str = r"(#.*|--.*)|(unless|while|loop|break|cont|ret|yield|please)|(int|rat|bool)|(true|false|ye|no|maybe)|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))?\s*=|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))|([A-Za-z_]+)|(\d*\.?\d+)|(!=|==|<=|<=|[&|+\-*/<>=])|([(){}])|(\n)|(;)"; lazy_static::lazy_static! { static ref TOKEN_REGEX: regex::Regex = regex::Regex::new(TOKEN_REGEX_SRC).unwrap(); @@ -405,6 +477,7 @@ pub fn tokenize<'a>(source: &'a str) -> VecDeque> { // if we have a match, save it as token if let Some(mat) = group { let debug_info = DebugInfo { + source, start: mat.start() - line_start, end: mat.end() - line_start, line: line_count @@ -440,7 +513,7 @@ pub fn tokenize<'a>(source: &'a str) -> VecDeque> { 14 => Token::Terminator(debug_info), _ => { - debug_info.print(MessageType::Error, "Unable to identify sequence as token", source); + println!("{}", debug_info.make_msg(crate::msg::ERR71)); panic!() } }); diff --git a/test.erpn b/test.erpn new file mode 100644 index 0000000..fc12ed5 --- /dev/null +++ b/test.erpn @@ -0,0 +1,34 @@ +pi: + Load Rat 5.1415926535 +foo: + Load Int 5 + Load Int 6 + Add + Store Some(Int) x + Load Arg x + Load Int 6 + Lt + If + Load Bool true + Yield + Please + Load Arg x + Load Int 6 + Lt + If + Load Bool true + Yield + Load Bool false + Yield +main: + Load Int 4 + Store None a + Load Rat 5.5 + Store None b + Load Bool true + Store None c + Load Int 3 + Load Rat 3.4 + Call foo + Store None r + Load Int 0