diff --git a/.gitignore b/.gitignore index ea8c4bf..b34e77d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +*.izn diff --git a/src/ast.rs b/src/ast.rs new file mode 100644 index 0000000..899e156 --- /dev/null +++ b/src/ast.rs @@ -0,0 +1,19 @@ +use crate::token::{LiteralType, Token}; + +pub enum Expr { + Binary { + left: Box, + op: Token, + right: Box, + }, + Grouping { + expression: Box, + }, + Literal { + value: Option, + }, + Unary { + op: Token, + right: Box, + }, +} diff --git a/src/lib.rs b/src/lib.rs index 534e0ca..921b805 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,8 @@ use std::{ use scanner::Scanner; use token::Token; +mod ast; +mod printer; mod scanner; mod token; mod utils; diff --git a/src/printer.rs b/src/printer.rs new file mode 100644 index 0000000..b744079 --- /dev/null +++ b/src/printer.rs @@ -0,0 +1,84 @@ +use crate::{ast::Expr, token::LiteralType}; + +pub fn pretty_print(expr: &Expr) -> String { + match expr { + Expr::Binary { left, op, right } => parenthesize(&op.lexeme, &[left, right]), + Expr::Grouping { expression } => parenthesize("group", &[expression]), + Expr::Literal { value } => match value { + Some(LiteralType::String(v)) => v.to_string(), + Some(LiteralType::Number(v)) => v.to_string(), + None => "None".to_string(), + }, + Expr::Unary { op, right } => parenthesize(&op.lexeme, &[right]), + } +} + +fn parenthesize(name: &str, exprs: &[&Expr]) -> String { + let mut parenthesized = format!("({name}"); + + for expr in exprs { + parenthesized.push(' '); + parenthesized.push_str(&pretty_print(expr)); + } + + parenthesized.push(')'); + + parenthesized +} + +#[cfg(test)] +mod test { + use crate::token::{Token, TokenType}; + + use super::*; + use Expr::*; + + #[test] + fn equal_print_binary() { + use TokenType::*; + let expression = Binary { + left: Box::new(Literal { + value: Some(LiteralType::Number(10.2)), + }), + op: Token { + t_type: Plus, + lexeme: "+".to_string(), + literal: None, + line: 0, + }, + right: Box::new(Literal { + value: Some(LiteralType::Number(10.2)), + }), + }; + + let actual = pretty_print(&expression); + let expected = "(+ 10.2 10.2)"; + + assert_eq!(actual, expected); + } + + #[test] + // tests all cases + fn equal_test_whole() { + use TokenType::*; + let expression = Binary { + left: Box::new(Unary { + op: Token::new(Minus, "-", None, 0), + right: Box::new(Expr::Literal { + value: Some(LiteralType::number_literal(123.0)), + }), + }), + op: Token::new(Star, "*", None, 0), + right: Box::new(Grouping { + expression: Box::new(Literal { + value: Some(LiteralType::number_literal(45.67)), + }), + }), + }; + + let actual = pretty_print(&expression); + let expected = "(* (- 123) (group 45.67))"; + + assert_eq!(expected, actual); + } +} diff --git a/src/scanner.rs b/src/scanner.rs index a2e9062..cd4aec6 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -370,7 +370,7 @@ mod tests { let mut scanner = Scanner::new(value); - let expected_value = LiteralType::Number(123.456); + let expected_value = LiteralType::Number(123.0); let tokens = scanner.scan_tokens().expect("There shouldn't be an error"); diff --git a/src/token.rs b/src/token.rs index a610a89..48ad0cb 100644 --- a/src/token.rs +++ b/src/token.rs @@ -54,6 +54,16 @@ pub enum LiteralType { Number(f64), } +impl LiteralType { + pub fn string_literal(val: &str) -> LiteralType { + return LiteralType::String(val.to_string()); + } + + pub fn number_literal(val: f64) -> LiteralType { + return LiteralType::Number(val); + } +} + #[derive(Debug)] pub struct Token { pub t_type: TokenType, @@ -62,6 +72,18 @@ pub struct Token { pub line: usize, } +impl Token { + pub fn new(t_type: TokenType, lexeme: &str, literal: Option, line: usize) -> Self { + let lexeme = lexeme.to_string(); + Self { + t_type, + lexeme, + literal, + line, + } + } +} + impl Display for Token { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?} {} {:?}", self.t_type, self.lexeme, self.literal)