diff --git a/README.md b/README.md new file mode 100644 index 0000000..42b08f1 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Raoul + +Imperative language done as compilers class final project + +## How to run it? + +``` +cargo run -- examples/filename.ra +``` + +### Flags + +- `-d` or `--debug`. Shows debugging message for the developer of the language diff --git a/examples/complete.ra b/examples/complete.ra index c6969b0..c18dad1 100644 --- a/examples/complete.ra +++ b/examples/complete.ra @@ -4,16 +4,28 @@ func helper(a: int): int { func main(): void { global globalVariable = 2; + global globalArray = [3, 2, 1]; a = 1; b = "abc"; c = 2.0; d = true; - e = [1, 2, 3]; + arr = [1, 2, 3]; + matrix = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]; f = helper(a); g = input(); - print(e[0]); + print(arr[0]); + arr[0] = 3; + print(arr[0]); + + print(matrix[0][a - 1]); + matrix[0][a - 1] = 3; + print(matrix[0][a - 1]); + + print(globalArray[0]); + global globalArray[0] = 1; + print(globalArray[0]); if (a < c) { print(a); @@ -34,13 +46,13 @@ func main(): void { dataframe = read_csv('file.csv', rows, cols); - print(average(dataframe)); - print(std(dataframe)); - print(mode(dataframe)); - print(variance(dataframe)); - print(average(dataframe, 'column')); print(std(dataframe, 'column')); print(mode(dataframe, 'column')); print(variance(dataframe, 'column')); + + print(correlation(dataframe, 'column1', 'column2')); + + plot(dataframe, 'column1', 'column2'); + histogram(dataframe, 'column1', 10); } diff --git a/propuesta/images/Raoul-1.png b/propuesta/images/Raoul-1.png index a990cba..244263f 100644 Binary files a/propuesta/images/Raoul-1.png and b/propuesta/images/Raoul-1.png differ diff --git a/propuesta/images/Raoul-2.png b/propuesta/images/Raoul-2.png index ec06de6..d9e1eef 100644 Binary files a/propuesta/images/Raoul-2.png and b/propuesta/images/Raoul-2.png differ diff --git a/propuesta/images/Raoul-3.png b/propuesta/images/Raoul-3.png index 2dc4116..472b23f 100644 Binary files a/propuesta/images/Raoul-3.png and b/propuesta/images/Raoul-3.png differ diff --git a/propuesta/images/Raoul-4.png b/propuesta/images/Raoul-4.png index 630a57b..f61cf68 100644 Binary files a/propuesta/images/Raoul-4.png and b/propuesta/images/Raoul-4.png differ diff --git a/propuesta/images/Raoul-5.png b/propuesta/images/Raoul-5.png index c964d33..9d7f3ba 100644 Binary files a/propuesta/images/Raoul-5.png and b/propuesta/images/Raoul-5.png differ diff --git a/propuesta/images/Raoul-6.png b/propuesta/images/Raoul-6.png index 292e553..fd50285 100644 Binary files a/propuesta/images/Raoul-6.png and b/propuesta/images/Raoul-6.png differ diff --git a/propuesta/images/Raoul-7.png b/propuesta/images/Raoul-7.png deleted file mode 100644 index 15d42a0..0000000 Binary files a/propuesta/images/Raoul-7.png and /dev/null differ diff --git a/src/args/mod.rs b/src/args/mod.rs index 5ed3e33..5e5a0fb 100644 --- a/src/args/mod.rs +++ b/src/args/mod.rs @@ -1,21 +1,25 @@ -use clap::{Arg, Command, ArgMatches}; +use clap::{Arg, ArgMatches, Command}; pub fn parse_args() -> ArgMatches { Command::new("raoul") .version("1.0") .author("ricglz") .about("My cool programming language") - .arg(Arg::new("file") - .value_name("FILE") - .help("Sets a file to parse") - .required(true)) - .arg(Arg::new("verbose") - .short('v') - .long("verbose") - .value_name("VERBOSE") - .help("Makes it verbose") - .default_value("false") - .takes_value(false) - .required(false)) + .arg( + Arg::new("file") + .value_name("FILE") + .help("Sets a file to parse") + .required(true), + ) + .arg( + Arg::new("verbose") + .short('d') + .long("debug") + .value_name("DEBUG") + .help("Displays debugging prints throughout the process") + .default_value("false") + .takes_value(false) + .required(false), + ) .get_matches() } diff --git a/src/main.rs b/src/main.rs index 69ef642..7c16725 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,26 @@ mod args; mod enums; -mod parser; +mod test_parser; +#[macro_use] +extern crate pest_derive; use std::process::exit; -use crate::parser::parse; -use crate::args::parse_args; +use args::parse_args; +use test_parser::parse_file; fn main() { let matches = parse_args(); let filename = matches.value_of("file").expect("required"); - let verbose = matches.is_present("verbose"); - if verbose { + let debug = matches.is_present("debug"); + if debug { println!("Starting parsing"); } - if let Err(error) = parse(filename, verbose) { + if let Err(error) = parse_file(filename, debug) { println!("Parsing error {}", error.to_string()); exit(1); } - if verbose { + if debug { println!("Parsing ended sucessfully"); } } diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 6388aa6..d23f9a1 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -32,11 +32,12 @@ AND = {"AND"} OR = {"OR"} not = {"not"} -BOOL = {"bool"} -FLOAT = {"float"} -INT = {"int"} -STRING = {"string"} -void = {"void"} +BOOL = {"bool"} +FLOAT = {"float"} +INT = {"int"} +STRING = {"string"} +DATAFRAME = {"Dataframe"} +void = {"void"} FUNC = _{"func"} MAIN = _{"main"} @@ -57,6 +58,9 @@ AVERAGE_KEY = _{"average"} STD_KEY = _{"std"} MODE_KEY = _{"mode"} VARIANCE_KEY = _{"variance"} +CORRELATION_KEY = {"correlation"} +PLOT_KEY = {"plot"} +HISTOGRAM_KEY = {"histogram"} RETURN_KEY = _{"return"} @@ -69,31 +73,34 @@ COMP_OP = _{ EQ | NE } REL_OP = _{ GT | GTE | LT | LTE } KEYWORD_TYPE = _{ - AND | - OR | - not | - BOOL | - FLOAT | - INT | - STRING | - void | - FUNC | - MAIN | - IF | - ELSE | - PRINT | - WHILE | - FOR | - TO | - GLOBAL | - INPUT | - TRUE | - FALSE | - READ_CSV_KEY | - AVERAGE_KEY | - STD_KEY | - MODE_KEY | - VARIANCE_KEY | + AND | + OR | + not | + BOOL | + FLOAT | + INT | + STRING | + void | + FUNC | + MAIN | + IF | + ELSE | + PRINT | + WHILE | + FOR | + TO | + GLOBAL | + INPUT | + TRUE | + FALSE | + READ_CSV_KEY | + AVERAGE_KEY | + STD_KEY | + MODE_KEY | + VARIANCE_KEY | + CORRELATION_KEY | + PLOT_KEY | + HISTOGRAM_KEY | RETURN_KEY } KEYWORD = _{ KEYWORD_TYPE ~ !ID_SUFFIX } @@ -111,7 +118,7 @@ ARR_CTE = { L_SQUARE ~ exprs? ~ R_SQUARE } CTE = _{ ARR_CTE | ATOM_CTE } ARR_INDEX = { L_SQUARE ~ expr ~ R_SQUARE } ARR_VAL = { id ~ ARR_INDEX{1,2} } -NON_CTE = _{ DATAFRAME_OPS | FUNC_CALL | ARR_VAL | id } +NON_CTE = _{ DATAFRAME_VALUE_OPS | FUNC_CALL | ARR_VAL | id } VAR_VAL = _{ CTE | NON_CTE } expr = { and_term ~ (OR ~ and_term)* } @@ -124,19 +131,20 @@ operand = { not? ~ operand_value } operand_value = { L_PAREN ~ expr ~ R_PAREN | VAR_VAL } exprs = { expr ~ (COMMA ~ expr)* } -ATOMIC_TYPES = _{ BOOL | FLOAT | INT | STRING } -ARR_TYPES = { L_SQUARE ~ (ATOMIC_TYPES | ARR_TYPES) ~ R_SQUARE } -VAR_TYPES = _{ ARR_TYPES | ATOMIC_TYPES } -TYPES = _{ VAR_TYPES | void} +ATOMIC_TYPES = _{ BOOL | FLOAT | INT | STRING } +ARR_TYPES = { L_SQUARE ~ (ATOMIC_TYPES | ARR_TYPES) ~ R_SQUARE } +SIMPLE_TYPES = _{ ARR_TYPES | ATOMIC_TYPES } +COMPOSED_TYPES = _{ SIMPLE_TYPES | DATAFRAME } +TYPES = _{ SIMPLE_TYPES | void} READ = { INPUT ~ L_PAREN ~ R_PAREN } -ASSIGNMENT_EXP = _{ READ | expr } -assignment = { GLOBAL? ~ id ~ ASGN ~ ASSIGNMENT_EXP } +ASSIGNMENT_EXP = _{ READ | READ_CSV | expr } +assignment = { GLOBAL? ~ (ARR_VAL | id) ~ ASGN ~ ASSIGNMENT_EXP } block = { L_BRACKET ~ statement* ~ R_BRACKET } -FUNC_ARG = { id ~ COLON ~ VAR_TYPES } +FUNC_ARG = { id ~ COLON ~ SIMPLE_TYPES } FUNC_ARGS = { FUNC_ARG ~ (COMMA ~ FUNC_ARG)* } FUNC_HEADER = { FUNC ~ id ~ L_PAREN ~ FUNC_ARGS? ~ R_PAREN ~ COLON ~ TYPES } function = { FUNC_HEADER ~ block } @@ -154,20 +162,25 @@ WHILE_LOOP = {WHILE ~ COND_EXPR ~ block} FOR_LOOP = {FOR ~ L_PAREN ~ assignment ~ TO ~ expr ~ R_PAREN ~ block} -POSSIBLE_STR = {STRING_CTE | NON_CTE} -READ_CSV_EXTRA = {(COMMA ~ id){2}} -READ_CSV = {READ_CSV_KEY ~ L_PAREN ~ POSSIBLE_STR ~ READ_CSV_EXTRA? ~ R_PAREN} -OPTIONAL_COLUMN_FUNC = {L_PAREN ~ id ~ (COMMA ~ POSSIBLE_STR)? ~ R_PAREN} -AVERAGE = {AVERAGE_KEY ~ OPTIONAL_COLUMN_FUNC} -STD = {STD_KEY ~ OPTIONAL_COLUMN_FUNC} -MODE = {MODE_KEY ~ OPTIONAL_COLUMN_FUNC} -VARIANCE = {VARIANCE_KEY ~ OPTIONAL_COLUMN_FUNC} -DATAFRAME_OPS = _{READ_CSV | AVERAGE | STD | MODE | VARIANCE} +POSSIBLE_STR = {STRING_CTE | NON_CTE} +READ_CSV_EXTRA = {(COMMA ~ id){2}} +READ_CSV = {READ_CSV_KEY ~ L_PAREN ~ POSSIBLE_STR ~ READ_CSV_EXTRA? ~ R_PAREN} +ONE_COLUMN_FUNC = {L_PAREN ~ id ~ COMMA ~ POSSIBLE_STR ~ R_PAREN} +AVERAGE = {AVERAGE_KEY ~ ONE_COLUMN_FUNC} +STD = {STD_KEY ~ ONE_COLUMN_FUNC} +MODE = {MODE_KEY ~ ONE_COLUMN_FUNC} +VARIANCE = {VARIANCE_KEY ~ ONE_COLUMN_FUNC} +TWO_COLUMNS_FUNC = {L_PAREN ~ id ~ (COMMA ~ POSSIBLE_STR){2} ~ R_PAREN} +CORRELATION = {CORRELATION_KEY ~ TWO_COLUMNS_FUNC} +PLOT = {PLOT_KEY ~ TWO_COLUMNS_FUNC} +HISTOGRAM = {HISTOGRAM_KEY ~ L_PAREN ~ id ~ COMMA ~ POSSIBLE_STR ~ COMMA ~ expr ~ R_PAREN} +DATAFRAME_VALUE_OPS = _{AVERAGE | STD | MODE | VARIANCE | CORRELATION} +DATAFRAME_VOID_OPS = _{PLOT | HISTOGRAM} RETURN = { RETURN_KEY ~ expr } BLOCK_STATEMENT = _{ DECISION | WHILE_LOOP | FOR_LOOP } -INLINE_STATEMENT = _{ DATAFRAME_OPS | assignment | write | RETURN | FUNC_CALL } +INLINE_STATEMENT = _{ DATAFRAME_VOID_OPS | assignment | write | RETURN | FUNC_CALL } statement = { INLINE_STATEMENT ~ SEMI_COLON | BLOCK_STATEMENT } program = { SOI ~ function* ~ MAIN_FUNCTION ~ EOI } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 14a9b08..4c45c84 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,5 +1,5 @@ -use pest_consume::Parser; use pest_consume::match_nodes; +use pest_consume::Parser; use crate::enums::{Operations, Types}; @@ -153,10 +153,14 @@ impl LanguageParser { } } -pub fn parse(filename: &str, verbose: bool) -> Result<()> { - let file = std::fs::read_to_string(filename).expect(filename); - let inputs = LanguageParser::parse_with_userdata(Rule::program, &file, verbose)?; +fn parse(source: &str, debug: bool) -> Result<()> { + let inputs = LanguageParser::parse_with_userdata(Rule::program, &source, debug)?; // There should be a single root node in the parsed tree let input = inputs.single()?; LanguageParser::program(input) } + +pub fn parse_file(filename: &str, debug: bool) -> Result<()> { + let file = std::fs::read_to_string(filename).expect(filename); + parse(file, debug) +} diff --git a/src/test_parser/mod.rs b/src/test_parser/mod.rs new file mode 100644 index 0000000..56b4687 --- /dev/null +++ b/src/test_parser/mod.rs @@ -0,0 +1,39 @@ +use pest::error::Error; +use pest::Parser; + +#[derive(Parser)] +#[grammar = "parser/grammar.pest"] // relative to src +struct MyParser; + +fn parse(source: &str) -> Result<(), Error> { + if let Err(err) = MyParser::parse(Rule::program, &source) { + Err(err) + } else { + Ok(()) + } +} + +pub fn parse_file(filename: &str, debug: bool) -> Result<(), Error> { + let program = std::fs::read_to_string(filename).expect(filename); + if debug { + println!("Testing {:?}", filename); + } + parse(&program) +} + +#[cfg(test)] +mod tests { + use std::fs::read_dir; + + use super::*; + + #[test] + fn example_files() { + let paths = read_dir("examples").unwrap(); + for path in paths { + let file_path = path.expect("File must exist").path(); + let file = file_path.to_str().unwrap(); + assert!(parse_file(file, true).is_ok()); + } + } +}