diff --git a/Cargo.lock b/Cargo.lock index 42119e4..a21e498 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,7 +112,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "fplc" -version = "0.9.512" +version = "0.9.513" dependencies = [ "chrono", "chrono-tz", diff --git a/Cargo.toml b/Cargo.toml index 92a92cf..7ce2266 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "fplc" -version = "0.9.512" +version = "0.9.513" edition = "2021" description = "A pseudolang interpreter written in Rust" repository = "https://github.com/PseudoLang-Software-Foundation/Pseudolang" diff --git a/installer/pseudolang.nsi b/installer/pseudolang.nsi index 89d4e01..8799935 100644 --- a/installer/pseudolang.nsi +++ b/installer/pseudolang.nsi @@ -4,7 +4,7 @@ !define MUI_ICON "Pseudolang-Logo.ico" -Name "PseudoLang Installer v0.9.512" +Name "PseudoLang Installer v0.9.513" InstallDir "$PROGRAMFILES\PseudoLang\" OutFile "../release/installer/pseudolang-setup-x64.exe" BrandingText "(c) 2024 PseudoLang Software Foundation" @@ -33,7 +33,7 @@ Section "" WriteRegStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path" "$INSTDIR;$R0" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pseudolang" "DisplayName" "Pseudolang" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pseudolang" "DisplayVersion" "0.9.512" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pseudolang" "DisplayVersion" "0.9.513" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pseudolang" "Publisher" "Pseudolang Software Foundation" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pseudolang" "DisplayIcon" "$INSTDIR\Pseudolang-Logo.ico" diff --git a/readme.md b/readme.md index e906aa0..44c4858 100644 --- a/readme.md +++ b/readme.md @@ -9,7 +9,7 @@

Build and Test Pseudolang - Version + Version Nightly Releases

@@ -60,7 +60,6 @@ The file `src/tests/mod.rs` also contains various unit tests (examples of code) Functionality - [ ] Dictionaries -- [ ] Evaluation - [ ] Better error handling (line, column) - [ ] Networking - [ ] File IO diff --git a/src/interpreter.rs b/src/interpreter.rs index bfc1fac..d9f49ad 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1339,6 +1339,19 @@ fn evaluate_node( } } } + AstNode::Eval(expr) => { + let expr_val = evaluate_node(expr, Rc::clone(&env), debug)?; + if let Value::String(s) = expr_val { + let mut lexer = crate::lexer::Lexer::new(&s); + let tokens = lexer.tokenize(); + let mut parser = crate::parser::Parser::new(tokens); + let ast = parser.parse_expression(debug)?; + + evaluate_node(&ast, Rc::clone(&env), debug) + } else { + Err("EVAL requires a string argument".to_string()) + } + } _ => Err(format!("Unimplemented node type: {:?}", node)), } } diff --git a/src/lexer.rs b/src/lexer.rs index ffabf1a..4587498 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -86,6 +86,7 @@ pub enum Token { Null, NaN, + Eval, } pub struct Lexer<'a> { @@ -432,6 +433,7 @@ impl<'a> Lexer<'a> { "SORT" => Some(Token::Sort), "TRY" => Some(Token::Try), "CATCH" => Some(Token::Catch), + "EVAL" => Some(Token::Eval), _ => Some(Token::Identifier(identifier)), } } diff --git a/src/parser.rs b/src/parser.rs index f65ede6..3e9e4e6 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -60,6 +60,7 @@ pub enum AstNode { error_var: Option, catch_block: Box, }, + Eval(Box), } #[derive(Debug, Clone)] @@ -393,7 +394,7 @@ impl Parser { } } - fn parse_expression(&mut self, debug: bool) -> Result { + pub fn parse_expression(&mut self, debug: bool) -> Result { if self.match_token(&Token::Sort) { if !self.match_token(&Token::OpenParen) { return Err("Expected '(' after SORT".to_string()); @@ -684,6 +685,17 @@ impl Parser { } Ok(AstNode::Input(prompt)) } + Some(Token::Eval) => { + self.advance(); + if !self.match_token(&Token::OpenParen) { + return Err("Expected '(' after EVAL".to_string()); + } + let expr = self.parse_expression(debug)?; + if !self.match_token(&Token::CloseParen) { + return Err("Expected ')' after EVAL expression".to_string()); + } + Ok(AstNode::Eval(Box::new(expr))) + } _ => match self.advance() { Some(Token::Integer(n)) => Ok(AstNode::Integer(n)), Some(Token::Float(f)) => Ok(AstNode::Float(f)), diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 069f750..08a1b80 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -2436,4 +2436,47 @@ DISPLAY(arr)"#, "NAN", ); } + + #[test] + fn test_eval() { + assert_output(r#"DISPLAY(EVAL("1 + 2"))"#, "3"); + assert_output(r#"DISPLAY(EVAL("2 * (3 + 4)"))"#, "14"); + assert_output(r#"DISPLAY(EVAL("10 / 2"))"#, "5"); + assert_output(r#"DISPLAY(EVAL("7 MOD 3"))"#, "1"); + + assert_output( + r#" + x <- 5 + DISPLAY(EVAL("x * 2")) + DISPLAY(EVAL("x * (x + 1)")) + "#, + "10\n30", + ); + + assert_output( + r#" + nums <- [1, 3, 5] + DISPLAY(EVAL("nums[1] + nums[2]")) + "#, + "4", + ); + + assert_output( + r#" + x <- 10 + y <- 20 + DISPLAY(EVAL("x < y")) + DISPLAY(EVAL("x = 10")) + DISPLAY(EVAL("(x > 5) AND (y < 30)")) + "#, + "true\ntrue\ntrue", + ); + + assert_output(r#"DISPLAY(EVAL("1.5 + 2.3"))"#, "3.8"); + assert_output(r#"DISPLAY(EVAL("3.0 * (4.5 - 2.5)"))"#, "6"); + + assert!(run_test(r#"DISPLAY(EVAL("1 + "))"#).is_err()); + assert!(run_test(r#"DISPLAY(EVAL("1 / 0"))"#).is_err()); + assert!(run_test(r#"DISPLAY(EVAL("invalid"))"#).is_err()); + } } diff --git a/wapm.toml b/wapm.toml index d0d788f..a58acd1 100644 --- a/wapm.toml +++ b/wapm.toml @@ -1,7 +1,7 @@ [package] name = "pseudolang/fplc" -version = "0.9.512" +version = "0.9.513" description = "A pseudolang interpreter written in Rust" license = "MIT" readme = "readme.md"