From adc110c30615e17605c28704430023991660df13 Mon Sep 17 00:00:00 2001 From: Xavier2p Date: Mon, 25 Sep 2023 19:56:51 +0200 Subject: [PATCH] new error managment, new debug capacities and misc improvements --- Cargo.toml | 4 +- src/ast.rs | 187 ++---------------------------------------- src/builtins/mod.rs | 1 + src/errors.rs | 119 --------------------------- src/helpers/errors.rs | 72 ++++++++++++++++ src/helpers/file.rs | 9 +- src/helpers/mod.rs | 1 + src/lexer.rs | 45 ++++------ src/main.rs | 38 +++++++-- src/old/ast-old.rs | 186 +++++++++++++++++++++++++++++++++++++++++ src/parser.rs | 15 +--- src/print.rs | 16 ++-- src/program.rs | 43 +++++----- src/tokens.rs | 2 +- src/variables.rs | 2 +- src/verbose.rs | 1 + 16 files changed, 355 insertions(+), 386 deletions(-) create mode 100644 src/builtins/mod.rs delete mode 100644 src/errors.rs create mode 100644 src/helpers/errors.rs create mode 100644 src/old/ast-old.rs create mode 100644 src/verbose.rs diff --git a/Cargo.toml b/Cargo.toml index 2d4b4fd..0f38621 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fortran-rs" version = "0.1.0" -edition = "2021" +edition = "2021" authors = ["Xavier2p "] description = "An open-source Fortran interpreter.\nWritten in Rust." license = "MIT" @@ -16,4 +16,4 @@ documentation = "https://xavier2p.github.io/fortran.rs" test-case = "3.1.0" colored = "2.0.0" # clap = { version = "3.2.25", features = ["derive"] } -clap = {version = "4.4.4", features = ["derive"] } \ No newline at end of file +clap = { version = "4.4.4", features = ["derive"] } diff --git a/src/ast.rs b/src/ast.rs index efbafed..069f99b 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,186 +1,13 @@ -use crate::{ - // errors::{Error, ErrorKind}, - tokens::Token, - variables::Variable, -}; +use crate::tokens::Token; -pub struct Node { - pub token: Token, - pub left: Box, - pub right: Box, +struct Node { + token: Token, + left: Option>, + right: Option>, } -trait NodeTrait { - fn new(token: Token, left: Box, right: Box) -> Node; - fn eval(&self) -> T; -} - -impl NodeTrait for Node { - fn new(token: Token, left: Box, right: Box) -> Node { - Node { token, left, right } - } - - fn eval(&self) -> i32 { - match self.token { - Token::Operator(_) => match self.token.get_value().as_str() { - "+" => { - let left = self.left.eval(); - let right = self.right.eval(); - left + right - } - "-" => { - let left = self.left.eval(); - let right = self.right.eval(); - left - right - } - "*" => { - let left = self.left.eval(); - let right = self.right.eval(); - left * right - } - "/" => { - let left = self.left.eval(); - let right = self.right.eval(); - left / right - } - _ => 0, - }, - Token::Number(n) => n, - Token::Variable(v) => { - let var = Variable::new(v); - var.get_value() // parse value for i32 - } - _ => 0, - } - } -} - -impl NodeTrait for Node { - fn new(token: Token, left: Box, right: Box) -> Node { - Node { token, left, right } - } - - fn eval(&self) -> f64 { - match self.token { - Token::Operator(_) => match self.token.get_value().as_str() { - "+" => { - let left = self.left.eval(); - let right = self.right.eval(); - left + right - } - "-" => { - let left = self.left.eval(); - let right = self.right.eval(); - left - right - } - "*" => { - let left = self.left.eval(); - let right = self.right.eval(); - left * right - } - "/" => { - let left = self.left.eval(); - let right = self.right.eval(); - left / right - } - _ => 0.0, - }, - Token::Number(n) => n, - Token::Variable(v) => { - let var = Variable::new(v); - var.get_value() // parse value for f64 - } - _ => 0.0, - } - } -} - -impl NodeTrait for Node { - fn new(token: Token, left: Box, right: Box) -> Node { +impl Node { + fn new(token: Token, left: Option>, right: Option>) -> Node { Node { token, left, right } } - - fn eval(&self) -> String { - match self.token { - Token::Operator(_) => match self.token.get_value().as_str() { - "+" => { - let left: String = self.left.eval(); - let right: String = self.right.eval(); - left + &right - } - "-" => { - let left: String = self.left.eval(); - let right: String = self.right.eval(); - left - right - } - "*" => { - let left = self.left.eval(); - let right = self.right.eval(); - left * right - } - "/" => { - let left = self.left.eval(); - let right = self.right.eval(); - left / right - } - _ => "0".to_string(), - }, - Token::Number(n) => n.to_string(), - Token::Variable(v) => { - let var = Variable::new(v); - var.get_value() - } - _ => "0".to_string(), - } - } } - -// impl Node { -// pub fn new(token: Token, left: Box, right: Box) -> Node { -// Node { token, left, right } -// } - -// pub fn eval(&self) { -// match self.token { -// Token::Operator(_) => match self.token.get_value().as_str() { -// "+" => { -// let left = self.left.eval(); -// let right = self.right.eval(); -// left + right -// } -// "-" => { -// let left = self.left.eval(); -// let right = self.right.eval(); -// left - right -// } -// "*" => { -// let left = self.left.eval(); -// let right = self.right.eval(); -// left * right -// } -// "/" => { -// let left = self.left.eval(); -// let right = self.right.eval(); -// left / right -// } -// _ => { -// let error = Error::new( -// "test".to_string(), -// "module".to_string(), -// 0, -// 12, -// "Unknown operator".to_string(), -// ErrorKind::Syntax, -// ); -// error.raise(); -// } -// }, -// Token::Number(n) => n, -// Token::Variable(v) => { -// let var = Variable::new(v); -// var.get_value() -// } -// _ => 0, -// } -// } -// } diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/builtins/mod.rs @@ -0,0 +1 @@ + diff --git a/src/errors.rs b/src/errors.rs deleted file mode 100644 index c347616..0000000 --- a/src/errors.rs +++ /dev/null @@ -1,119 +0,0 @@ -//! # Errors -//! -//! This module contains the error handling system. -use colored::Colorize; - -/// This enum contains the different types of errors. -#[allow(dead_code)] -#[derive(Clone, Copy)] -pub enum ErrorKind { - Syntax, - NotImplemented, - FileNotFound, - Type, - UnknownToken, - UnexpectedToken, - Critical, -} - -/// This struct contains the errors. -#[allow(dead_code)] -pub struct Error { - filename: String, - function: String, - line: usize, - column: usize, - value: String, - kind: ErrorKind, - code: i32, -} - -/// This struct contains the errors. -impl Error { - /// This function returns the code of the error. - fn get_code_number(kind: ErrorKind) -> i32 { - match kind { - ErrorKind::Syntax => 1, - ErrorKind::NotImplemented => 2, - ErrorKind::FileNotFound => 1, - ErrorKind::Type => 1, - ErrorKind::UnknownToken => 1, - ErrorKind::UnexpectedToken => 1, - ErrorKind::Critical => 2, - } - } - - /// This function returns the error's level. - fn get_error(&self, level: &str) -> String { - match self.kind { - ErrorKind::Syntax => "Syntax", - ErrorKind::NotImplemented => "NotImplemented", - ErrorKind::FileNotFound => "FileNotFound", - ErrorKind::Type => "Type", - ErrorKind::UnknownToken => "UnknownToken", - ErrorKind::UnexpectedToken => "UnexpectedToken", - ErrorKind::Critical => "Critical", - } - .to_string() - + level - } - - /// This function returns a new error. - pub fn new( - filename: String, - function: String, - line: usize, - column: usize, - value: String, - kind: ErrorKind, - ) -> Error { - Error { - filename, - function, - line, - column, - value, - kind, - code: Error::get_code_number(kind), - } - } - - /// This function returns a new warning. - #[allow(dead_code)] - pub fn warn(&self) { - println!( - "Warning handled on {}, in `{}` at line {}:{}.", - self.filename.blue(), - self.function.yellow(), - self.line, - self.column - ); - - let kind: String = self.get_error("Warning"); - - println!("Warning: {}", kind.yellow()); - println!(" > {}", self.value.cyan()); - } - - /// This function returns a new error. - #[allow(dead_code)] - pub fn raise(&self) { - eprintln!( - "Error handled on {}, in `{}` at line {}:{}.", - self.filename.blue(), - self.function.yellow(), - self.line, - self.column - ); - - let kind: String = self.get_error("Error"); - - eprintln!("Error: {}", kind.red()); - eprintln!(" > {}", self.value.magenta()); - eprintln!( - "Exiting with code {}...", - self.code.to_string().red().dimmed() - ); - std::process::exit(self.code); - } -} diff --git a/src/helpers/errors.rs b/src/helpers/errors.rs new file mode 100644 index 0000000..e50ef7b --- /dev/null +++ b/src/helpers/errors.rs @@ -0,0 +1,72 @@ +use crate::program::Program; +use colored::Colorize; + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub enum Error { + Syntax, + NotImplemented, + FileNotFound, + Type, + UnknownToken, + UnexpectedToken, + Critical, +} + +fn error_to_string(error: Error) -> String { + match error { + Error::Syntax => "Syntax", + Error::NotImplemented => "NotImplemented", + Error::FileNotFound => "FileNotFound", + Error::Type => "Type", + Error::UnknownToken => "UnknownToken", + Error::UnexpectedToken => "UnexpectedToken", + Error::Critical => "Critical", + } + .to_string() +} + +fn get_code_number(kind: Error) -> i32 { + match kind { + Error::Syntax => 1, + Error::NotImplemented => 2, + Error::FileNotFound => 1, + Error::Type => 1, + Error::UnknownToken => 1, + Error::UnexpectedToken => 1, + Error::Critical => 2, + } +} + +fn header(program: Program, kind: Error, is_warning: bool) -> String { + let kind_colored = if is_warning { + error_to_string(kind).yellow() + } else { + error_to_string(kind).red() + }; + let location = format!( + "In the file `{}`,\n at block `{}`,\n at line {}", + program.get_filename(), + program.get_name(), + program.get_line() + ); + format!( + "{}\n {} {}", + location, + kind_colored, + if is_warning { "warning" } else { "error" } + ) +} + +pub fn raise(program: &Program, kind: Error, message: String) { + let header: String = header(program.clone(), kind.clone(), false); + let code = get_code_number(kind); + eprintln!("{} [Code {}]\nDetails: {}", header, code, message); + // std::process::exit(); +} + +pub fn warn(program: &Program, kind: Error, message: String) { + let header: String = header(program.clone(), kind.clone(), true); + let code = get_code_number(kind); + println!("{} [Code {}]\nDetails: {}", header, code, message); +} diff --git a/src/helpers/file.rs b/src/helpers/file.rs index 8abd505..672034d 100644 --- a/src/helpers/file.rs +++ b/src/helpers/file.rs @@ -15,16 +15,15 @@ pub struct File { /// This struct contains the file's path, name, content, version and arguments. impl File { - /// This function returns the file's name. - pub fn get_name(&self) -> &String { - &self.name - } - /// This function returns the file's content. pub fn get_content(&self) -> &String { &self.content } + pub fn get_path(&self) -> &String { + &self.path + } + pub fn debug(&self) { println!("File {{"); println!(" name: {}", self.name); diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs index 38c584b..f9fef7b 100644 --- a/src/helpers/mod.rs +++ b/src/helpers/mod.rs @@ -1,2 +1,3 @@ pub mod cli; +pub mod errors; pub mod file; diff --git a/src/lexer.rs b/src/lexer.rs index 432da44..a0b6f65 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -2,12 +2,11 @@ //! //! The lexer is the first step of the compilation process. It takes the source code and converts it into tokens. use crate::{ - errors::{Error, ErrorKind}, - preprocess, + helpers::errors::{self, Error}, print::print_to_stdout, program::Program, tokens::Token, - variables, + variables, VERBOSE, }; use colored::Colorize; @@ -22,12 +21,12 @@ pub fn lexer(program: &mut Program) { let token: &Token = line.get(index).unwrap(); match token { Token::Comment(_) => { - if preprocess::get_verbose(program.get_args()) { + if VERBOSE { println!("{} {}", "|".dimmed(), token.get_value().dimmed()); } } Token::Print => { - print_to_stdout(line.to_vec(), index, pc, program.clone()); + print_to_stdout(line.to_vec(), index, program); break; } Token::Program => { @@ -37,11 +36,9 @@ pub fn lexer(program: &mut Program) { if stack.last().unwrap() == line.get(index + 1).unwrap() { stack.pop(); } else { - let error: Error = Error::new( - program.get_filename().to_string(), - program.get_name().to_string(), - pc, - index, + errors::raise( + program, + Error::UnexpectedToken, format!( "Expected `END {}`, got `END {}`", stack.last().unwrap().get_name().to_ascii_uppercase(), @@ -50,9 +47,7 @@ pub fn lexer(program: &mut Program) { .get_value() .to_ascii_uppercase() ), - ErrorKind::UnexpectedToken, - ); - error.raise(); + ) } } Token::Identifier(_) | Token::Assign(_) | Token::Other(_) => {} @@ -60,21 +55,15 @@ pub fn lexer(program: &mut Program) { Token::Variable(_) => { *program = variables::assign(line.to_vec(), index, program, token); } - _ => { - let error: Error = Error::new( - program.get_filename().to_string(), - program.get_name().to_string(), - pc, - index, - format!( - "Unexpected token {} `{}`", - token.get_name(), - token.get_value() - ), - ErrorKind::UnexpectedToken, - ); - error.warn(); - } + _ => errors::warn( + program, + Error::UnexpectedToken, + format!( + "Unexpected token {} `{}`", + token.get_name(), + token.get_value() + ), + ), } } } diff --git a/src/main.rs b/src/main.rs index 9f563eb..49db048 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,21 +18,49 @@ //! * `-v`, `--verbose` - Print the verbose output. //! * `-V`, `--version` - Print the version. //! * `--werror` - Treat all warnings as errors. -mod errors; -mod file_traitement; +mod ast; +mod helpers; mod lexer; mod parser; -mod preprocess; mod print; mod program; mod tokens; mod variables; +mod verbose; + +use crate::helpers::cli; +use clap::Parser; + +static VERBOSE: bool = true; fn main() { - let args = preprocess::process_args(); - let file = file_traitement::File::new(args); + let args: cli::Cli = cli::Cli::parse(); + + if VERBOSE { + args.debug(); + } + + match args.get_command() { + cli::Commands::Run { .. } => { + println!("Running: `{}`...", args.get_path()); + } + cli::Commands::Check { .. } => { + println!("Checking: `{}`...", args.get_path()); + } + } + + let file = helpers::file::new(args); + + if VERBOSE { + file.debug(); + } + let mut program = parser::parser(file); + if VERBOSE { + program.debug(); + } + program = variables::parse(program); lexer::lexer(&mut program); diff --git a/src/old/ast-old.rs b/src/old/ast-old.rs new file mode 100644 index 0000000..efbafed --- /dev/null +++ b/src/old/ast-old.rs @@ -0,0 +1,186 @@ +use crate::{ + // errors::{Error, ErrorKind}, + tokens::Token, + variables::Variable, +}; + +pub struct Node { + pub token: Token, + pub left: Box, + pub right: Box, +} + +trait NodeTrait { + fn new(token: Token, left: Box, right: Box) -> Node; + fn eval(&self) -> T; +} + +impl NodeTrait for Node { + fn new(token: Token, left: Box, right: Box) -> Node { + Node { token, left, right } + } + + fn eval(&self) -> i32 { + match self.token { + Token::Operator(_) => match self.token.get_value().as_str() { + "+" => { + let left = self.left.eval(); + let right = self.right.eval(); + left + right + } + "-" => { + let left = self.left.eval(); + let right = self.right.eval(); + left - right + } + "*" => { + let left = self.left.eval(); + let right = self.right.eval(); + left * right + } + "/" => { + let left = self.left.eval(); + let right = self.right.eval(); + left / right + } + _ => 0, + }, + Token::Number(n) => n, + Token::Variable(v) => { + let var = Variable::new(v); + var.get_value() // parse value for i32 + } + _ => 0, + } + } +} + +impl NodeTrait for Node { + fn new(token: Token, left: Box, right: Box) -> Node { + Node { token, left, right } + } + + fn eval(&self) -> f64 { + match self.token { + Token::Operator(_) => match self.token.get_value().as_str() { + "+" => { + let left = self.left.eval(); + let right = self.right.eval(); + left + right + } + "-" => { + let left = self.left.eval(); + let right = self.right.eval(); + left - right + } + "*" => { + let left = self.left.eval(); + let right = self.right.eval(); + left * right + } + "/" => { + let left = self.left.eval(); + let right = self.right.eval(); + left / right + } + _ => 0.0, + }, + Token::Number(n) => n, + Token::Variable(v) => { + let var = Variable::new(v); + var.get_value() // parse value for f64 + } + _ => 0.0, + } + } +} + +impl NodeTrait for Node { + fn new(token: Token, left: Box, right: Box) -> Node { + Node { token, left, right } + } + + fn eval(&self) -> String { + match self.token { + Token::Operator(_) => match self.token.get_value().as_str() { + "+" => { + let left: String = self.left.eval(); + let right: String = self.right.eval(); + left + &right + } + "-" => { + let left: String = self.left.eval(); + let right: String = self.right.eval(); + left - right + } + "*" => { + let left = self.left.eval(); + let right = self.right.eval(); + left * right + } + "/" => { + let left = self.left.eval(); + let right = self.right.eval(); + left / right + } + _ => "0".to_string(), + }, + Token::Number(n) => n.to_string(), + Token::Variable(v) => { + let var = Variable::new(v); + var.get_value() + } + _ => "0".to_string(), + } + } +} + +// impl Node { +// pub fn new(token: Token, left: Box, right: Box) -> Node { +// Node { token, left, right } +// } + +// pub fn eval(&self) { +// match self.token { +// Token::Operator(_) => match self.token.get_value().as_str() { +// "+" => { +// let left = self.left.eval(); +// let right = self.right.eval(); +// left + right +// } +// "-" => { +// let left = self.left.eval(); +// let right = self.right.eval(); +// left - right +// } +// "*" => { +// let left = self.left.eval(); +// let right = self.right.eval(); +// left * right +// } +// "/" => { +// let left = self.left.eval(); +// let right = self.right.eval(); +// left / right +// } +// _ => { +// let error = Error::new( +// "test".to_string(), +// "module".to_string(), +// 0, +// 12, +// "Unknown operator".to_string(), +// ErrorKind::Syntax, +// ); +// error.raise(); +// } +// }, +// Token::Number(n) => n, +// Token::Variable(v) => { +// let var = Variable::new(v); +// var.get_value() +// } +// _ => 0, +// } +// } +// } diff --git a/src/parser.rs b/src/parser.rs index 385a9d1..ac66c6d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,14 +1,11 @@ //! # Parser //! //! The parser is the second step of the compilation process. It takes the output of the preprocessor and transforms it into a program. -#[allow(unused_imports)] use crate::{ - errors::{Error, ErrorKind}, - file_traitement::File, - preprocess::Cli, + helpers::file::File, program::Program, tokens::Token, - variables::Variable, + // variables::Variable, }; use std::collections::HashMap; @@ -125,11 +122,5 @@ pub fn parser(file: File) -> Program { let name: String = lines[0][1].get_value(); - Program::new( - name, - lines, - HashMap::new(), - file.get_args().clone(), - file.get_name().to_string(), - ) + Program::new(name, lines, HashMap::new(), file.get_path().to_string()) } diff --git a/src/print.rs b/src/print.rs index b31c058..513db30 100644 --- a/src/print.rs +++ b/src/print.rs @@ -2,13 +2,13 @@ //! //! This module contains the `print` function. use crate::{ - errors::{Error, ErrorKind}, + helpers::errors::{self, Error}, program::Program, tokens::Token, }; /// This function prints the value of the variable to the standard output. -pub fn print_to_stdout(line: Vec, index: usize, pc: usize, prog: Program) { +pub fn print_to_stdout(line: Vec, index: usize, prog: &Program) { if line.get(index + 1).unwrap().get_value() == "*" { let mut to_print: String = String::new(); for index in index + 1..line.len() { @@ -27,17 +27,13 @@ pub fn print_to_stdout(line: Vec, index: usize, pc: usize, prog: Program) } println!("{}", to_print); } else { - let error: Error = Error::new( - prog.get_filename().to_string(), - prog.get_name().to_string(), - pc, - index, + errors::raise( + prog, + Error::Syntax, format!( "Expected `PRINT *,`, got `PRINT {}`", line.get(index + 1).unwrap().get_value() ), - ErrorKind::Syntax, - ); - error.raise(); + ) } } diff --git a/src/program.rs b/src/program.rs index b2b89f3..48afc93 100644 --- a/src/program.rs +++ b/src/program.rs @@ -1,69 +1,53 @@ //! # Program //! //! This module contains the `Program` struct. -use crate::{preprocess, tokens::Token, variables::Variable}; +use crate::{tokens::Token, variables::Variable}; use std::collections::HashMap; -/// This struct contains the program's name, lines, variables, arguments and program counter. +/// This struct contains the program's name, lines, variables, arguments and program counter +#[derive(Clone)] pub struct Program { filename: String, - path: String, name: String, variables: HashMap, lines: Vec>, pc: u8, - args: preprocess::Cli, } /// This struct contains the program's name, lines, variables, arguments and program counter. impl Program { - /// This function returns the program counter. pub fn get_name(&self) -> &String { &self.name } - /// This function returns the program counter. pub fn get_lines(&self) -> &Vec> { &self.lines } - /// This function returns the program counter. - pub fn get_args(&self) -> &preprocess::Cli { - &self.args - } - - /// This function returns the program counter. pub fn get_variables(&self) -> &HashMap { &self.variables } - /// This function returns the program counter. pub fn get_filename(&self) -> &String { &self.filename } - /// This function returns the program counter. - #[allow(dead_code)] - pub fn get_path(&self) -> &String { - &self.path + pub fn get_line(&self) -> u8 { + self.pc } - /// This function returns the program counter. pub fn new( name: String, lines: Vec>, variables: HashMap, - args: preprocess::Cli, filename: String, ) -> Program { Program { filename, - path: preprocess::get_path(&args), name, variables, lines, pc: 0, - args, } } @@ -77,12 +61,25 @@ impl Program { pub fn clone(&self) -> Program { Program { filename: self.filename.clone(), - path: self.path.clone(), name: self.name.clone(), variables: self.variables.clone(), lines: self.lines.clone(), pc: self.pc, - args: self.args.clone(), } } + + pub fn debug(&self) { + println!("Program {{"); + println!(" name: {}", self.name); + println!(" filename: {}", self.filename); + println!(" variables: {:?}", self.variables); + println!(" pc: {}", self.pc); + println!(" lines: ["); + for line in self.lines.iter() { + println!(" {:?}", line); + } + println!(" ]"); + println!("}}"); + println!("---- ---- ---- ---- ----"); + } } diff --git a/src/tokens.rs b/src/tokens.rs index 64464ca..0b507ea 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -3,7 +3,7 @@ //! This module contains the Token enum, which is used to represent the tokens /// This enum contains the different types of tokens. -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] #[allow(dead_code)] pub enum Token { Null, diff --git a/src/variables.rs b/src/variables.rs index 3d50b74..0075e01 100644 --- a/src/variables.rs +++ b/src/variables.rs @@ -97,7 +97,7 @@ pub fn parse(program: Program) -> Program { program.get_name().to_string(), lines, variables, - program.get_args().clone(), + // program.get_args().clone(), program.get_filename().to_string(), ); } diff --git a/src/verbose.rs b/src/verbose.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/verbose.rs @@ -0,0 +1 @@ +