From de6fc57a85edb3be78369ab21274d0c246851b6a Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Sat, 14 Oct 2023 01:54:31 +0100 Subject: [PATCH 1/5] PHP variable assignment fixes --- src/ast.rs | 41 ++++++++++++------- src/parser.rs | 109 ++++++++++++++++++++++++++++++-------------------- src/repl.rs | 20 +++++---- 3 files changed, 105 insertions(+), 65 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 74ba83d..7a96217 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,4 +1,4 @@ -use std::any::Any; +use std::{any::Any, fmt::Debug}; use crate::token::Token; @@ -17,6 +17,16 @@ impl std::fmt::Display for dyn Statement { } } +impl Debug for dyn Statement { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Statement: {}", self.token_literal()) + } +} + +impl Expression for dyn Statement { + fn expression_node(&self) {} +} + pub trait Expression: Node { fn expression_node(&self); } @@ -158,19 +168,18 @@ impl std::fmt::Display for ExpressionStatement { } } -pub struct LetStatement { +pub struct ReturnStatement { pub token: Token, - pub name: Identifier, - pub value: Option>, + pub return_value: Option> } -impl std::fmt::Display for LetStatement { +impl std::fmt::Display for ReturnStatement { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{} {};", self.token_literal(), self.name) + write!(f, "{};", self.token_literal()) } } -impl Node for LetStatement { +impl Node for ReturnStatement { fn as_any(&self) -> &dyn Any { self } @@ -180,22 +189,23 @@ impl Node for LetStatement { } } -impl Statement for LetStatement { +impl Statement for ReturnStatement { fn statement_node(&self) {} } -pub struct ReturnStatement { +pub struct LetStatement { pub token: Token, - pub return_value: Option> + pub name: String, + pub value: Box, } -impl std::fmt::Display for ReturnStatement { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{};", self.token_literal()) +impl std::fmt::Debug for LetStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} = {};", self.name, self.value.to_string()) } } -impl Node for ReturnStatement { +impl Node for LetStatement { fn as_any(&self) -> &dyn Any { self } @@ -205,7 +215,7 @@ impl Node for ReturnStatement { } } -impl Statement for ReturnStatement { +impl Statement for LetStatement { fn statement_node(&self) {} } @@ -218,6 +228,7 @@ impl std::fmt::Display for Program { let mut output = String::new(); for statement in &self.statements { + println!("statement: {}", statement); output.push_str(&format!("{}", statement)); } diff --git a/src/parser.rs b/src/parser.rs index 8d14810..9b3d44a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4,10 +4,10 @@ use anyhow::Error; use crate::{token::{Token, TokenType}, lexer::Lexer, ast::{Program, Statement, LetStatement, Identifier, ReturnStatement, Expression, ExpressionStatement, IntegerLiteral, PrefixExpression, InfixExpression}}; -type ParseExpressionResult = Option>; +type ParseResult = Option>; -type PrefixParseFn = fn(&mut Parser) -> ParseExpressionResult; -type InfixParseFn = fn(&mut Parser, Box) -> ParseExpressionResult; +type PrefixParseFn = fn(&mut Parser) -> ParseResult; +type InfixParseFn = fn(&mut Parser, Box) -> ParseResult; #[derive(Copy, Clone, PartialOrd, PartialEq)] enum Precedence { @@ -171,8 +171,8 @@ impl<'a> Parser<'a> { pub fn parse_statement(&mut self) -> Option> { if let Some(token) = &self.current_token { match token.token_type { - TokenType::Dollar => self.parse_dollar_statement(), TokenType::Return => self.parse_return_statement(), + TokenType::Dollar => self.parse_variable_assignment(), _ => self.parse_expression_statement(), } } else { @@ -180,51 +180,71 @@ impl<'a> Parser<'a> { } } - fn parse_dollar_statement(&mut self) -> Option> { - let current_token = self.current_token.clone().unwrap(); - - if !matches!(current_token.token_type, TokenType::Dollar) { - self.errors.push(format!("Expected '$', got {:?}", current_token)); + fn parse_variable_assignment(&mut self) -> Option> { + // Ensure the current token is a Dollar sign. + if let Some(token) = &self.current_token { + if token.token_type != TokenType::Dollar { + self.errors.push(format!("Expected $, got {:?}", token)); + return None; + } + } else { + self.errors.push(String::from("Expected $, got None")); return None; } - self.next_token(); // Move to identifier + // Move to the variable name. + self.next_token(); - let name_token = self.current_token.clone().unwrap(); - let name_value = match name_token.token_type { - TokenType::Ident => name_token.literal.clone(), - TokenType::Int => name_token.literal.clone(), - _ => { - self.errors.push(format!("Expected identifier, got {:?}", name_token)); + // Ensure the variable name is an identifier. + let name_token = if let Some(token) = &self.current_token { + if token.token_type == TokenType::Ident { + token.clone() + } else { + self.errors.push(format!("Expected identifier, got {:?}", token)); return None; } + } else { + self.errors.push(String::from("Expected identifier, got None")); + return None; }; - let mut value_expression: Option> = None; - - // Check if next token is an assignment - if self.peek_token_is(&TokenType::Assign) { - self.next_token(); - self.next_token(); - value_expression = self.parse_expression(Precedence::Lowest); // Parse the expression - } - - // Ensure the statement is terminated with a semicolon - if !self.expect_peek(TokenType::Semicolon) { + // Move to the expression after the equals sign. + self.next_token(); + + if let Some(token) = &self.current_token { + if token.token_type != TokenType::Assign { + self.errors.push(format!("Expected =, got {:?}", token)); + return None; + } + } else { + self.errors.push(String::from("Expected =, got None")); return None; } + + self.next_token(); - Some(Box::new(LetStatement { - name: Identifier { - token: name_token, - value: name_value.to_string(), - }, - token: current_token, - value: value_expression, - })) - } + let value_expression = self.parse_expression(Precedence::Lowest); + + if let Some(value_expression) = value_expression { + // Ensure the semicolon is present. + if !self.expect_peek(TokenType::Semicolon) { + return None; + } + + let variable_assignment = LetStatement { + token: name_token.clone(), + name: name_token.literal.clone(), + value: value_expression, + }; + + Some(Box::new(variable_assignment) as Box) + } else { + None + } + } - fn parse_expression(&mut self, precedence: Precedence) -> ParseExpressionResult { + fn parse_expression(&mut self, precedence: Precedence) -> Option> { + // Get prefix parse function (if it exists) let prefix_fn = self.prefix_parse_fns .get( &self.current_token @@ -239,6 +259,7 @@ impl<'a> Parser<'a> { return None; } + // Call the prefix parse function let mut left = prefix_fn.unwrap()(self); while !self.peek_token_is(&TokenType::Semicolon) && precedence < self.peek_precedence() { @@ -278,7 +299,7 @@ impl<'a> Parser<'a> { })) } - fn parse_identifier(&mut self) -> ParseExpressionResult { + fn parse_identifier(&mut self) -> ParseResult { let current_token = self.current_token.clone().unwrap(); let identifier = Identifier { @@ -289,7 +310,7 @@ impl<'a> Parser<'a> { Some(Box::new(identifier)) } - fn parse_integer_literal(&mut self) -> ParseExpressionResult { + fn parse_integer_literal(&mut self) -> ParseResult { let current_token = self.current_token.clone().unwrap(); let value = self.current_token.as_ref().unwrap().to_string().parse::().unwrap(); @@ -300,7 +321,7 @@ impl<'a> Parser<'a> { })) } - fn parse_infix_expression(&mut self, left: Box) -> ParseExpressionResult { + fn parse_infix_expression(&mut self, left: Box) -> Option> { let current_token = self.current_token.clone().unwrap(); let operator = self.current_token.as_ref().unwrap().to_string(); @@ -319,7 +340,9 @@ impl<'a> Parser<'a> { })) } - fn parse_prefix_expression(&mut self) -> ParseExpressionResult { + fn parse_prefix_expression(&mut self) -> Option> { + println!("Parsing prefix expression"); + let current_token = self.current_token.clone().unwrap(); let operator = self.current_token.as_ref().unwrap().to_string(); @@ -441,7 +464,7 @@ mod tests { let statement = program.statements[0].as_any().downcast_ref::().unwrap(); - assert_eq!("foobar", statement.name.value); + assert_eq!("foobar", statement.name); Ok(()) } @@ -563,7 +586,7 @@ mod tests { let let_statement = _let_statement.unwrap(); - assert_eq!(name, let_statement.name.value); + assert_eq!(name, let_statement.name); Ok(()) } diff --git a/src/repl.rs b/src/repl.rs index 398c8c2..955f632 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,8 +1,8 @@ -use std::io::{self, Write}; +use std::{io::{self, Write}, any::{TypeId, Any}}; use anyhow::{Result, Error}; -use crate::{lexer::Lexer, parser::Parser, ast::LetStatement}; +use crate::{lexer::Lexer, parser::Parser, ast::{LetStatement, Statement}}; const PROMPT: &str = ">> "; @@ -22,11 +22,17 @@ pub fn init_repl() -> Result<(), Error> { parser.check_errors()?; for statement in program.statements { - let let_statement = statement.as_any().downcast_ref::(); - - if let Some(let_statement) = let_statement { - println!("{}{} = {}", let_statement.token, let_statement.name.value, let_statement.value.as_ref().unwrap()); - } + print_statement(statement); } } +} + +fn print_statement(statement: Box) { + match statement.as_any().type_id() { + id if id == TypeId::of::() => { + let let_statement = statement.as_any().downcast_ref::().unwrap(); + println!("{:?}", let_statement); + }, + _ => println!("Unknown statement type: {:?}", statement.as_any().type_id()), + } } \ No newline at end of file From dc449c6a423419f1eed23caf68b6570d4fd1f371 Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Sat, 14 Oct 2023 12:24:15 +0100 Subject: [PATCH 2/5] Fixed identifier/variable parsing + implemented file parsing --- Cargo.lock | 384 ++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 + src/ast.rs | 68 +++++++-- src/main.rs | 38 ++++- src/parser.rs | 159 ++++++++++++++------- src/repl.rs | 60 ++++++-- 6 files changed, 629 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d8fc24..6551e8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,176 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clipboard-win" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +dependencies = [ + "error-code", + "str-buf", + "winapi", +] + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "error-code" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +dependencies = [ + "libc", + "str-buf", +] + +[[package]] +name = "fd-lock" +version = "3.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "linux-raw-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "phf" version = "0.11.2" @@ -55,7 +219,10 @@ name = "php-rs" version = "0.1.0" dependencies = [ "anyhow", + "env_logger", + "log", "phf", + "rustyline", ] [[package]] @@ -76,6 +243,16 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" @@ -91,12 +268,95 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "regex" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d119d7c7ca818f8a53c300863d4f87566aac09943aef5b355bb83969dae75d87" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56d84fdd47036b038fc80dd333d10b6aab10d5d31f4a366e20014def75328d33" + +[[package]] +name = "rustix" +version = "0.38.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustyline" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994eca4bca05c87e86e15d90fc7a91d1be64b4482b38cb2d27474568fe7c9db9" +dependencies = [ + "bitflags 2.4.0", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "scopeguard", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "siphasher" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "str-buf" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" + [[package]] name = "syn" version = "2.0.38" @@ -108,8 +368,132 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +dependencies = [ + "winapi-util", +] + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml index 5f086d6..1b432b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,7 @@ edition = "2021" [dependencies] anyhow = "1.0.75" +env_logger = "0.10.0" +log = "0.4.20" phf = { version = "0.11.2", features = ["macros"] } +rustyline = { version = "12.0.0", features = ["with-file-history"] } diff --git a/src/ast.rs b/src/ast.rs index 7a96217..d497674 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -31,7 +31,7 @@ pub trait Expression: Node { fn expression_node(&self); } -impl std::fmt::Display for dyn Expression { +impl std::fmt::Debug for dyn Expression { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.token_literal()) } @@ -94,9 +94,12 @@ pub struct InfixExpression { pub right: Box, } -impl std::fmt::Display for InfixExpression { +impl std::fmt::Debug for InfixExpression { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "({} {} {})", self.left, self.operator, self.right) + println!("left: {:?}", self.left); + println!("right: {:?}", self.right); + + write!(f, "({:?} {} {:?})", self.left, self.operator, self.right) } } @@ -120,9 +123,9 @@ pub struct PrefixExpression { pub right: Box, } -impl std::fmt::Display for PrefixExpression { +impl std::fmt::Debug for PrefixExpression { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "({}{})", self.operator, self.right) + write!(f, "({}{:?})", self.operator, self.right) } } @@ -159,10 +162,10 @@ impl Statement for ExpressionStatement { fn statement_node(&self) {} } -impl std::fmt::Display for ExpressionStatement { +impl std::fmt::Debug for ExpressionStatement { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match &self.expression { - Some(expr) => write!(f, "{}", expr), + Some(expr) => write!(f, "{:?}", expr), None => Ok(()) } } @@ -193,19 +196,19 @@ impl Statement for ReturnStatement { fn statement_node(&self) {} } -pub struct LetStatement { +pub struct VariableAssignment { pub token: Token, pub name: String, pub value: Box, } -impl std::fmt::Debug for LetStatement { +impl std::fmt::Debug for VariableAssignment { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{} = {};", self.name, self.value.to_string()) + write!(f, "${} = {:?};", self.name, self.value) } } -impl Node for LetStatement { +impl Node for VariableAssignment { fn as_any(&self) -> &dyn Any { self } @@ -215,7 +218,36 @@ impl Node for LetStatement { } } -impl Statement for LetStatement { +impl Statement for VariableAssignment { + fn statement_node(&self) {} +} + +pub struct VariableReference { + pub token: Token, + pub name: String, +} + +impl std::fmt::Debug for VariableReference { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "${}", self.name.to_string()) + } +} + +impl Node for VariableReference { + fn as_any(&self) -> &dyn Any { + self + } + + fn token_literal(&self) -> &str { + &self.token.literal + } +} + +impl Expression for VariableReference { + fn expression_node(&self) {} +} + +impl Statement for VariableReference { fn statement_node(&self) {} } @@ -224,6 +256,18 @@ pub struct Program { } impl std::fmt::Display for Program { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let mut output = String::new(); + + for statement in &self.statements { + output.push_str(&format!("{}", statement)); + } + + write!(f, "{}", output) + } +} + +impl std::fmt::Debug for Program { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let mut output = String::new(); diff --git a/src/main.rs b/src/main.rs index 8581d4b..a78c557 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use std::env; + use anyhow::{Result, Error}; use repl::init_repl; @@ -8,12 +10,40 @@ mod repl; mod string; mod token; -const NAME: &str = env!("CARGO_PKG_NAME"); -const VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const NAME: &str = env!("CARGO_PKG_NAME"); +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); fn main() -> Result<(), Error>{ - println!("{} interpreter {}", NAME, VERSION); - init_repl()?; + env_logger::init(); + + let args: Vec = env::args().collect(); + + let file_path = match args.len() { + 1 => None, + 2 => Some(args[1].clone()), + _ => { + println!("Usage: {} [FILE]", NAME); + std::process::exit(1); + } + }; + + if let Some(file_path) = file_path { + // TODO: Split this out into a separate function + println!("Running script: {}", file_path); + + let file = std::fs::read_to_string(file_path)?; + + let lexer = lexer::Lexer::new(&file); + let mut parser = parser::Parser::new(lexer); + + let program = parser.parse_program(); + + parser.check_errors()?; + + println!("{:?}", program); + } else { + init_repl()?; + } Ok(()) } diff --git a/src/parser.rs b/src/parser.rs index 9b3d44a..b2e3aa9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,8 +1,9 @@ use std::collections::HashMap; use anyhow::Error; +use log::trace; -use crate::{token::{Token, TokenType}, lexer::Lexer, ast::{Program, Statement, LetStatement, Identifier, ReturnStatement, Expression, ExpressionStatement, IntegerLiteral, PrefixExpression, InfixExpression}}; +use crate::{token::{Token, TokenType}, lexer::Lexer, ast::{Program, Statement, VariableAssignment, Identifier, ReturnStatement, Expression, ExpressionStatement, IntegerLiteral, PrefixExpression, InfixExpression, VariableReference}}; type ParseResult = Option>; @@ -103,6 +104,7 @@ impl<'a> Parser<'a> { parser.register_prefix(TokenType::Int, |p| Parser::parse_integer_literal(p)); parser.register_prefix(TokenType::Bang, |p| Parser::parse_prefix_expression(p)); parser.register_prefix(TokenType::Minus, |p| Parser::parse_prefix_expression(p)); + parser.register_prefix(TokenType::Dollar, |p| Parser::parse_variable_reference_expression(p)); parser.register_infix(TokenType::Plus, |p, left| Parser::parse_infix_expression(p, left)); parser.register_infix(TokenType::Minus, |p, left| Parser::parse_infix_expression(p, left)); @@ -172,7 +174,7 @@ impl<'a> Parser<'a> { if let Some(token) = &self.current_token { match token.token_type { TokenType::Return => self.parse_return_statement(), - TokenType::Dollar => self.parse_variable_assignment(), + TokenType::Dollar => self.parse_variable_or_assignment(), _ => self.parse_expression_statement(), } } else { @@ -180,8 +182,7 @@ impl<'a> Parser<'a> { } } - fn parse_variable_assignment(&mut self) -> Option> { - // Ensure the current token is a Dollar sign. + fn parse_variable_reference_expression(&mut self) -> Option> { if let Some(token) = &self.current_token { if token.token_type != TokenType::Dollar { self.errors.push(format!("Expected $, got {:?}", token)); @@ -191,10 +192,9 @@ impl<'a> Parser<'a> { self.errors.push(String::from("Expected $, got None")); return None; } - - // Move to the variable name. + self.next_token(); - + // Ensure the variable name is an identifier. let name_token = if let Some(token) = &self.current_token { if token.token_type == TokenType::Ident { @@ -207,39 +207,90 @@ impl<'a> Parser<'a> { self.errors.push(String::from("Expected identifier, got None")); return None; }; - - // Move to the expression after the equals sign. - self.next_token(); - + + // Parse as reference + let variable_reference = VariableReference { + token: name_token.clone(), + name: name_token.literal.clone(), + }; + + Some(Box::new(variable_reference) as Box) + } + + fn parse_variable_or_assignment(&mut self) -> Option> { + trace!("Current token: {:?}", self.current_token); + // Ensure the current token is a Dollar sign. if let Some(token) = &self.current_token { - if token.token_type != TokenType::Assign { - self.errors.push(format!("Expected =, got {:?}", token)); + if token.token_type != TokenType::Dollar { + self.errors.push(format!("Expected $, got {:?}", token)); return None; } } else { - self.errors.push(String::from("Expected =, got None")); + self.errors.push(String::from("Expected $, got None")); return None; } - + + // Move to the variable name. self.next_token(); - - let value_expression = self.parse_expression(Precedence::Lowest); - - if let Some(value_expression) = value_expression { - // Ensure the semicolon is present. - if !self.expect_peek(TokenType::Semicolon) { + + // Ensure the variable name is an identifier. + let name_token = if let Some(token) = &self.current_token { + if token.token_type == TokenType::Ident { + token.clone() + } else { + self.errors.push(format!("Expected identifier, got {:?}", token)); return None; } - - let variable_assignment = LetStatement { - token: name_token.clone(), - name: name_token.literal.clone(), - value: value_expression, - }; - - Some(Box::new(variable_assignment) as Box) } else { - None + self.errors.push(String::from("Expected identifier, got None")); + return None; + }; + + // Move to the next token and check if it's an assignment or a reference. + self.next_token(); + + match &self.current_token { + Some(token) if token.token_type == TokenType::Assign => { + // Parse as assignment + self.next_token(); + let value_expression = self.parse_expression(Precedence::Lowest); + + trace!("Expression: {:?}", value_expression); + + if let Some(value_expression) = value_expression { + // Ensure the semicolon is present. + if !self.expect_peek(TokenType::Semicolon) { + return None; + } + + let variable_assignment = VariableAssignment { + token: name_token.clone(), + name: name_token.literal.clone(), + value: value_expression, + }; + + Some(Box::new(variable_assignment) as Box) + } else { + None + } + }, + Some(token) if token.token_type == TokenType::Semicolon => { + // Parse as reference + let variable_reference = VariableReference { + token: name_token.clone(), + name: name_token.literal.clone(), + }; + + Some(Box::new(variable_reference) as Box) + }, + Some(token) => { + self.errors.push(format!("Expected = or ;, got {:?}", token)); + None + }, + None => { + self.errors.push(String::from("Expected = or ;, got None")); + None + } } } @@ -330,19 +381,23 @@ impl<'a> Parser<'a> { self.next_token(); - let right = self.parse_expression(precedence).unwrap(); - - Some(Box::new(InfixExpression { - token: current_token, - operator, - left, - right, - })) + match self.parse_expression(precedence) { + Some(right) => { + Some(Box::new(InfixExpression { + token: current_token, + operator, + left, + right, + })) + }, + None => { + self.errors.push("Expected an expression, but found none.".to_string()); + return None; + } + } } fn parse_prefix_expression(&mut self) -> Option> { - println!("Parsing prefix expression"); - let current_token = self.current_token.clone().unwrap(); let operator = self.current_token.as_ref().unwrap().to_string(); @@ -394,7 +449,7 @@ mod tests { use anyhow::{Result, Error}; - use crate::ast::{Statement, LetStatement, PrefixExpression, InfixExpression}; + use crate::ast::{Statement, VariableAssignment, PrefixExpression, InfixExpression}; #[test] fn test_assignment_statements() -> Result<(), Error> { @@ -453,19 +508,21 @@ mod tests { #[test] fn test_identifier_expression() -> Result<(), Error> { let input = "$foobar;"; - + let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); - + let program = parser.parse_program(); parser.check_errors()?; - + assert_eq!(1, program.statements.len()); - - let statement = program.statements[0].as_any().downcast_ref::().unwrap(); - - assert_eq!("foobar", statement.name); - + + let statement = program.statements[0].as_any().downcast_ref::().unwrap(); + + let identifier = statement.expression.as_ref().unwrap().as_any().downcast_ref::().unwrap(); + + assert_eq!("foobar", identifier.value); + Ok(()) } @@ -578,9 +635,7 @@ mod tests { } fn assert_let_statement(statement: &Box, name: &str) -> Result<(), Error> { - assert_eq!("$", statement.token_literal()); - - let _let_statement = statement.as_any().downcast_ref::(); + let _let_statement = statement.as_any().downcast_ref::(); assert!(_let_statement.is_some()); diff --git a/src/repl.rs b/src/repl.rs index 955f632..7555a8f 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,38 +1,68 @@ use std::{io::{self, Write}, any::{TypeId, Any}}; use anyhow::{Result, Error}; +use log::{info}; +use rustyline::error::ReadlineError; -use crate::{lexer::Lexer, parser::Parser, ast::{LetStatement, Statement}}; +use crate::{lexer::Lexer, parser::Parser, ast::{VariableAssignment, Statement, VariableReference}}; const PROMPT: &str = ">> "; pub fn init_repl() -> Result<(), Error> { - loop { - print!("{}", PROMPT); - io::stdout().flush().unwrap(); + let mut rl = rustyline::DefaultEditor::new()?; + + #[cfg(feature = "with-file-history")] + if rl.load_history("history.txt").is_err() { + info!("No previous history."); + } - let mut input = String::new(); + println!("{} interpreter v{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); - std::io::stdin().read_line(&mut input).unwrap(); + loop { + let readline = rl.readline(format!("{}", PROMPT).as_str()); - let lexer = Lexer::new(&input); - let mut parser = Parser::new(lexer); + match readline { + Ok(line) => { + rl.add_history_entry(line.as_str())?; - let program = parser.parse_program(); - parser.check_errors()?; + let lexer = Lexer::new(&line); + let mut parser = Parser::new(lexer); - for statement in program.statements { - print_statement(statement); + let program = parser.parse_program(); + parser.check_errors()?; + + for statement in program.statements { + print_statement(statement); + } + }, + Err(ReadlineError::Interrupted) => { + println!("CTRL-C"); + break; + }, + Err(ReadlineError::Eof) => { + println!("CTRL-D"); + break; + }, + Err(err) => { + println!("Error: {:?}", err); + break; + } } } + + Ok(()) } fn print_statement(statement: Box) { match statement.as_any().type_id() { - id if id == TypeId::of::() => { - let let_statement = statement.as_any().downcast_ref::().unwrap(); - println!("{:?}", let_statement); + id if id == TypeId::of::() => { + let variable_assignment = statement.as_any().downcast_ref::().unwrap(); + println!("{:?}", variable_assignment); }, + id if id == TypeId::of::() => { + let variable_reference = statement.as_any().downcast_ref::().unwrap(); + println!("Ref: {:?}", variable_reference); + } _ => println!("Unknown statement type: {:?}", statement.as_any().type_id()), } } \ No newline at end of file From 53c91cb50cd9951ed71ab6bd3e6d692940ca45c6 Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Sat, 14 Oct 2023 19:27:38 +0100 Subject: [PATCH 3/5] Fixed expression parsing --- src/ast.rs | 12 ++++++++++-- src/parser.rs | 21 --------------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index d497674..7b5ed84 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -33,7 +33,15 @@ pub trait Expression: Node { impl std::fmt::Debug for dyn Expression { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.token_literal()) + if self.as_any().type_id() == std::any::TypeId::of::() { + let infix_expression = self.as_any().downcast_ref::().unwrap(); + write!(f, "({:?} {} {:?})", infix_expression.left, infix_expression.operator, infix_expression.right) + } else if self.as_any().type_id() == std::any::TypeId::of::() { + let prefix_expression = self.as_any().downcast_ref::().unwrap(); + write!(f, "({}{:?})", prefix_expression.operator, prefix_expression.right) + } else { + write!(f, "{}", self.token_literal()) + } } } @@ -98,7 +106,7 @@ impl std::fmt::Debug for InfixExpression { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { println!("left: {:?}", self.left); println!("right: {:?}", self.right); - + write!(f, "({:?} {} {:?})", self.left, self.operator, self.right) } } diff --git a/src/parser.rs b/src/parser.rs index b2e3aa9..5ef5477 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -505,27 +505,6 @@ mod tests { Ok(()) } - #[test] - fn test_identifier_expression() -> Result<(), Error> { - let input = "$foobar;"; - - let lexer = Lexer::new(input); - let mut parser = Parser::new(lexer); - - let program = parser.parse_program(); - parser.check_errors()?; - - assert_eq!(1, program.statements.len()); - - let statement = program.statements[0].as_any().downcast_ref::().unwrap(); - - let identifier = statement.expression.as_ref().unwrap().as_any().downcast_ref::().unwrap(); - - assert_eq!("foobar", identifier.value); - - Ok(()) - } - #[test] fn test_integer_literal_expression() -> Result<(), Error> { let input = "5;"; From ad8b3a4ba381fef7e3acfa558c762a3fc0124fc2 Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Sat, 14 Oct 2023 19:44:46 +0100 Subject: [PATCH 4/5] Fixed string representation of expressions and tests --- src/ast.rs | 39 +++++++++++++++++++++++++++++++++++---- src/parser.rs | 6 +++++- src/repl.rs | 14 +++++++++----- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 7b5ed84..15df149 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -13,13 +13,45 @@ pub trait Statement: Node { impl std::fmt::Display for dyn Statement { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.token_literal()) + let statement_string = match self.as_any().type_id() { + id if id == std::any::TypeId::of::() => { + let expression_statement = self.as_any().downcast_ref::().unwrap(); + format!("{:?}", expression_statement) + }, + id if id == std::any::TypeId::of::() => { + let return_statement = self.as_any().downcast_ref::().unwrap(); + format!("{:?}", return_statement) + }, + id if id == std::any::TypeId::of::() => { + let variable_assignment = self.as_any().downcast_ref::().unwrap(); + format!("{:?}", variable_assignment) + }, + _ => "".to_string() + }; + + write!(f, "{}", statement_string) } } impl Debug for dyn Statement { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Statement: {}", self.token_literal()) + let statement_string = match self.as_any().type_id() { + id if id == std::any::TypeId::of::() => { + let expression_statement = self.as_any().downcast_ref::().unwrap(); + format!("Expression: {:?}", expression_statement) + }, + id if id == std::any::TypeId::of::() => { + let return_statement = self.as_any().downcast_ref::().unwrap(); + format!("Return: {:?}", return_statement) + }, + id if id == std::any::TypeId::of::() => { + let variable_assignment = self.as_any().downcast_ref::().unwrap(); + format!("Assignment: {:?}", variable_assignment) + }, + _ => "".to_string() + }; + + write!(f, "{}", statement_string) } } @@ -184,7 +216,7 @@ pub struct ReturnStatement { pub return_value: Option> } -impl std::fmt::Display for ReturnStatement { +impl std::fmt::Debug for ReturnStatement { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{};", self.token_literal()) } @@ -280,7 +312,6 @@ impl std::fmt::Debug for Program { let mut output = String::new(); for statement in &self.statements { - println!("statement: {}", statement); output.push_str(&format!("{}", statement)); } diff --git a/src/parser.rs b/src/parser.rs index 5ef5477..775812b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -449,7 +449,7 @@ mod tests { use anyhow::{Result, Error}; - use crate::ast::{Statement, VariableAssignment, PrefixExpression, InfixExpression}; + use crate::{ast::{Statement, VariableAssignment, PrefixExpression, InfixExpression}, repl::print_statement}; #[test] fn test_assignment_statements() -> Result<(), Error> { @@ -548,6 +548,10 @@ mod tests { let program = parser.parse_program(); parser.check_errors()?; + + for statement in &program.statements { + print_statement(statement); + } assert_eq!(*expected, program.to_string()); } diff --git a/src/repl.rs b/src/repl.rs index 7555a8f..7ec9852 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -4,7 +4,7 @@ use anyhow::{Result, Error}; use log::{info}; use rustyline::error::ReadlineError; -use crate::{lexer::Lexer, parser::Parser, ast::{VariableAssignment, Statement, VariableReference}}; +use crate::{lexer::Lexer, parser::Parser, ast::{VariableAssignment, Statement, VariableReference, ExpressionStatement}}; const PROMPT: &str = ">> "; @@ -32,7 +32,7 @@ pub fn init_repl() -> Result<(), Error> { parser.check_errors()?; for statement in program.statements { - print_statement(statement); + print_statement(&statement); } }, Err(ReadlineError::Interrupted) => { @@ -53,7 +53,7 @@ pub fn init_repl() -> Result<(), Error> { Ok(()) } -fn print_statement(statement: Box) { +pub fn print_statement(statement: &Box) { match statement.as_any().type_id() { id if id == TypeId::of::() => { let variable_assignment = statement.as_any().downcast_ref::().unwrap(); @@ -61,8 +61,12 @@ fn print_statement(statement: Box) { }, id if id == TypeId::of::() => { let variable_reference = statement.as_any().downcast_ref::().unwrap(); - println!("Ref: {:?}", variable_reference); - } + println!("{:?}", variable_reference); + }, + id if id == TypeId::of::() => { + let expression_statement = statement.as_any().downcast_ref::().unwrap(); + println!("{:?}", expression_statement); + }, _ => println!("Unknown statement type: {:?}", statement.as_any().type_id()), } } \ No newline at end of file From 183eb79866ea229d7d97cfba834cd7f16321a23a Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Sat, 14 Oct 2023 20:27:56 +0100 Subject: [PATCH 5/5] Variable reference string representation --- src/ast.rs | 4 ++++ src/parser.rs | 4 ++-- src/repl.rs | 22 ++-------------------- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 15df149..5fecd5a 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -26,6 +26,10 @@ impl std::fmt::Display for dyn Statement { let variable_assignment = self.as_any().downcast_ref::().unwrap(); format!("{:?}", variable_assignment) }, + id if id == std::any::TypeId::of::() => { + let variable_reference = self.as_any().downcast_ref::().unwrap(); + format!("${:?}", variable_reference) + }, _ => "".to_string() }; diff --git a/src/parser.rs b/src/parser.rs index 775812b..d0c5ed5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -449,7 +449,7 @@ mod tests { use anyhow::{Result, Error}; - use crate::{ast::{Statement, VariableAssignment, PrefixExpression, InfixExpression}, repl::print_statement}; + use crate::ast::{Statement, VariableAssignment, PrefixExpression, InfixExpression}; #[test] fn test_assignment_statements() -> Result<(), Error> { @@ -550,7 +550,7 @@ mod tests { parser.check_errors()?; for statement in &program.statements { - print_statement(statement); + println!("{}", statement); } assert_eq!(*expected, program.to_string()); diff --git a/src/repl.rs b/src/repl.rs index 7ec9852..93589e1 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -4,7 +4,7 @@ use anyhow::{Result, Error}; use log::{info}; use rustyline::error::ReadlineError; -use crate::{lexer::Lexer, parser::Parser, ast::{VariableAssignment, Statement, VariableReference, ExpressionStatement}}; +use crate::{lexer::Lexer, parser::Parser}; const PROMPT: &str = ">> "; @@ -32,7 +32,7 @@ pub fn init_repl() -> Result<(), Error> { parser.check_errors()?; for statement in program.statements { - print_statement(&statement); + println!("{}", &statement); } }, Err(ReadlineError::Interrupted) => { @@ -51,22 +51,4 @@ pub fn init_repl() -> Result<(), Error> { } Ok(()) -} - -pub fn print_statement(statement: &Box) { - match statement.as_any().type_id() { - id if id == TypeId::of::() => { - let variable_assignment = statement.as_any().downcast_ref::().unwrap(); - println!("{:?}", variable_assignment); - }, - id if id == TypeId::of::() => { - let variable_reference = statement.as_any().downcast_ref::().unwrap(); - println!("{:?}", variable_reference); - }, - id if id == TypeId::of::() => { - let expression_statement = statement.as_any().downcast_ref::().unwrap(); - println!("{:?}", expression_statement); - }, - _ => println!("Unknown statement type: {:?}", statement.as_any().type_id()), - } } \ No newline at end of file