diff --git a/.gitignore b/.gitignore index f0e3bcacb9..e0f63a0998 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target -**/*.rs.bk \ No newline at end of file +**/*.rs.bk +**/*.un~ +**/*.toml~ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 7111148ff6..d42147a967 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,27 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ctor" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "logos" version = "0.9.7" @@ -20,6 +42,25 @@ dependencies = [ "utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "output_vt100" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pretty_assertions" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "0.4.30" @@ -28,6 +69,14 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proc-macro2" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "0.6.13" @@ -36,6 +85,14 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex-syntax" version = "0.6.12" @@ -46,6 +103,7 @@ name = "rusty" version = "0.1.0" dependencies = [ "logos 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", + "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -58,22 +116,68 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syn" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "utf8-ranges" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [metadata] +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" +"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum logos 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f494e22d293fa05db60b3fd95fb30e9409feb5672b56ce6f250f99d9fbae6b93" "checksum logos-derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "13ff1b1068db09ee21d12baf55eccc0900a781a735273e0a606f6f4fbb32a322" +"checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" +"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 07fbe07d17..2404f7df2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,6 @@ edition = "2018" [dependencies] logos = "0.9.7" + +[dev-dependencies] +pretty_assertions = "0.6.1" diff --git a/src/ast.rs b/src/ast.rs index 9f39c094ee..2df56037f2 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -46,7 +46,12 @@ pub enum Operator { Minus, Multiplication, Division, - OperatorEqual, - OperatorNotEqual, + Equal, + NotEqual, Modulo, + Less, + Greater, + LessOrEqual, + GreaterOrEqual, + } diff --git a/src/lexer.rs b/src/lexer.rs index 33d9e877ac..9e4867d4d8 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -57,6 +57,18 @@ pub enum Token { #[token = "<>"] OperatorNotEqual, + #[token = "<"] + OperatorLess, + + #[token = ">"] + OperatorGreater, + + #[token = "<="] + OperatorLessOrEqual, + + #[token = ">="] + OperatorGreaterOrEqual, + #[token = "MOD"] OperatorModulo, @@ -133,7 +145,7 @@ mod tests { #[test] fn operator_test() { - let mut lexer = super::lex("+ - * / MOD"); + let mut lexer = super::lex("+ - * / MOD = <> < > <= >="); assert_eq!(lexer.token, super::Token::OperatorPlus); lexer.advance(); assert_eq!(lexer.token, super::Token::OperatorMinus); @@ -143,6 +155,18 @@ mod tests { assert_eq!(lexer.token, super::Token::OperatorDivision); lexer.advance(); assert_eq!(lexer.token, super::Token::OperatorModulo); + lexer.advance(); + assert_eq!(lexer.token, super::Token::OperatorEqual); + lexer.advance(); + assert_eq!(lexer.token, super::Token::OperatorNotEqual); + lexer.advance(); + assert_eq!(lexer.token, super::Token::OperatorLess); + lexer.advance(); + assert_eq!(lexer.token, super::Token::OperatorGreater); + lexer.advance(); + assert_eq!(lexer.token, super::Token::OperatorLessOrEqual); + lexer.advance(); + assert_eq!(lexer.token, super::Token::OperatorGreaterOrEqual); } #[test] diff --git a/src/parser.rs b/src/parser.rs index ded89c1360..163d67b808 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -114,18 +114,18 @@ fn parse_statement(lexer: &mut RustyLexer) -> Result { } fn parse_primary_expression(lexer: &mut RustyLexer) -> Result { - parse_additive_expression(lexer) + parse_equality_expression(lexer) } fn parse_equality_expression(lexer: &mut RustyLexer) -> Result { - let left = parse_compare_expression(lexer); + let left = parse_compare_expression(lexer)?; let operator = match lexer.token { OperatorEqual => Operator::Equal, OperatorNotEqual => Operator::NotEqual, _ => return Ok(left), }; lexer.advance(); - let right = parse_primary_expression(lexer)?; + let right = parse_equality_expression(lexer)?; Ok(Statement::BinaryExpression { operator, left: Box::new(left), @@ -134,7 +134,21 @@ fn parse_equality_expression(lexer: &mut RustyLexer) -> Result Result { - + let left = parse_additive_expression(lexer)?; + let operator = match lexer.token { + OperatorLess => Operator::Less, + OperatorGreater => Operator::Greater, + OperatorLessOrEqual => Operator::LessOrEqual, + OperatorGreaterOrEqual => Operator::GreaterOrEqual, + _ => return Ok(left), + }; + lexer.advance(); + let right = parse_compare_expression(lexer)?; + Ok(Statement::BinaryExpression { + operator, + left: Box::new(left), + right: Box::new(right), + }) } fn parse_additive_expression(lexer: &mut RustyLexer) -> Result { @@ -145,7 +159,7 @@ fn parse_additive_expression(lexer: &mut RustyLexer) -> Result return Ok(left), }; lexer.advance(); - let right = parse_primary_expression(lexer)?; + let right = parse_additive_expression(lexer)?; Ok(Statement::BinaryExpression { operator, left: Box::new(left), @@ -183,16 +197,14 @@ fn parse_parenthesized_expression(lexer: &mut RustyLexer) -> Result Result { - let current = - match lexer.token { - Identifier => parse_reference(lexer), - LiteralNumber => parse_literal_number(lexer), - _ => Err(unexpected_token(lexer)), - }; - + let current = match lexer.token { + Identifier => parse_reference(lexer), + LiteralNumber => parse_literal_number(lexer), + _ => Err(unexpected_token(lexer)), + }; if current.is_ok() && lexer.token == KeywordAssignment { lexer.advance(); - return Ok(Statement::Assignment{ + return Ok(Statement::Assignment { left: Box::new(current?), right: Box::new(parse_primary_expression(lexer)?), }); @@ -250,6 +262,7 @@ fn parse_variable( mod tests { use super::super::lexer; use super::Statement; + use pretty_assertions::assert_eq; #[test] fn empty_returns_empty_compilation_unit() { @@ -655,4 +668,150 @@ mod tests { } } + #[test] + fn equality_expression_test() { + let lexer = lexer::lex("PROGRAM exp x = 3; x - 0 <> 1 + 2; END_PROGRAM"); + let result = super::parse(lexer).unwrap(); + + let prg = &result.units[0]; + { + let statement = &prg.statements[0]; + let ast_string = format!("{:#?}", statement); + let expected_ast = r#"BinaryExpression { + operator: Equal, + left: Reference { + name: "x", + }, + right: LiteralNumber { + value: "3", + }, +}"#; + assert_eq!(ast_string, expected_ast); + } + + { + let statement = &prg.statements[1]; + let ast_string = format!("{:#?}", statement); + let expected_ast = r#"BinaryExpression { + operator: NotEqual, + left: BinaryExpression { + operator: Minus, + left: Reference { + name: "x", + }, + right: LiteralNumber { + value: "0", + }, + }, + right: BinaryExpression { + operator: Plus, + left: LiteralNumber { + value: "1", + }, + right: LiteralNumber { + value: "2", + }, + }, +}"#; + assert_eq!(ast_string, expected_ast); + } + } + #[test] + fn comparison_expression_test() { + let lexer = lexer::lex( + "PROGRAM exp + a < 3; + b > 0; + c <= 7; + d >= 4; + e := 2 + 1 > 3 + 1; + END_PROGRAM", + ); + let result = super::parse(lexer).unwrap(); + + let prg = &result.units[0]; + { + let statement = &prg.statements[0]; + let expected_ast = r#"BinaryExpression { + operator: Less, + left: Reference { + name: "a", + }, + right: LiteralNumber { + value: "3", + }, +}"#; + assert_eq!(format!("{:#?}", statement), expected_ast); + } + { + let statement = &prg.statements[1]; // b > 0 + let expected_ast = r#"BinaryExpression { + operator: Greater, + left: Reference { + name: "b", + }, + right: LiteralNumber { + value: "0", + }, +}"#; + assert_eq!(format!("{:#?}", statement), expected_ast); + } + { + let statement = &prg.statements[2]; // c <= 7 + let expected_ast = r#"BinaryExpression { + operator: LessOrEqual, + left: Reference { + name: "c", + }, + right: LiteralNumber { + value: "7", + }, +}"#; + assert_eq!(format!("{:#?}", statement), expected_ast); + } + { + let statement = &prg.statements[3]; // d >= 4 + let expected_ast = r#"BinaryExpression { + operator: GreaterOrEqual, + left: Reference { + name: "d", + }, + right: LiteralNumber { + value: "4", + }, +}"#; + assert_eq!(format!("{:#?}", statement), expected_ast); + } + { + //e := 2 + 1 > 3 + 1; + let statement = &prg.statements[4]; + let expected_ast = r#"Assignment { + left: Reference { + name: "e", + }, + right: BinaryExpression { + operator: Greater, + left: BinaryExpression { + operator: Plus, + left: LiteralNumber { + value: "2", + }, + right: LiteralNumber { + value: "1", + }, + }, + right: BinaryExpression { + operator: Plus, + left: LiteralNumber { + value: "3", + }, + right: LiteralNumber { + value: "1", + }, + }, + }, +}"#; + assert_eq!(format!("{:#?}", statement), expected_ast); + } + } }