diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 63c9720..d9ede09 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -1,4 +1,4 @@ MODULE ::= INSTR* INSTR ::= EXPR semicolon EXPR ::= BUILTIN -BUILTIN ::= bool +BUILTIN ::= bool | float | int diff --git a/src/ast/lexer.rs b/src/ast/lexer.rs index e1e1886..f1e9bee 100644 --- a/src/ast/lexer.rs +++ b/src/ast/lexer.rs @@ -1,8 +1,8 @@ -use crate::ast::node; +use crate::ast::node::Node; struct Token { position: usize, - value: String, + value: String } pub struct Lexer { @@ -53,6 +53,79 @@ impl Lexer { false } + + fn scan_int(&self) -> Option { + let mut value = String::from(""); + + if let Some('-') = self.text.chars().nth(self.cursor) { + value.push('-'); + } + + for c in self.text.chars().skip(self.cursor + value.len()) { + if c.is_digit(10) { + value.push(c); + } else { + break; + } + } + + if value.len() > 0 { + Some(Token { + position: self.cursor + value.len(), + value + }) + } else { + None + } + } + + fn scan_float(&self) -> Option { + let mut value = String::from(""); + + if let Some('-') = self.text.chars().nth(self.cursor) { + value.push('-'); + } + + for c in self.text.chars().skip(self.cursor + value.len()) { + if c.is_digit(10) { + value.push(c); + } else { + break; + } + } + + if let Some('.') = self.text.chars().nth(self.cursor + value.len()) { + value.push('.'); + } else { + return None; + } + + for c in self.text.chars().skip(self.cursor + value.len()) { + if c.is_digit(10) { + value.push(c); + } else { + break; + } + } + + if value.len() > 0 && value != "." { + if value.chars().nth(0).unwrap() == '.' { + value = format!("0{}", value); + } + + if value.chars().last().unwrap() == '.' { + value = format!("{}0", value); + } + + Some(Token { + position: self.cursor + value.len(), + value + }) + } else { + None + } + } + fn scan_text(&self, text: &str) -> Option { let c = self.cursor; @@ -112,24 +185,38 @@ impl Lexer { } impl Iterator for Lexer { - type Item = node::Node; + type Item = Node; fn next(&mut self) -> Option { self.skip_spaces(); if let Some(token) = self.scan_keyword("true") { self.cursor = token.position; - return Some(node::Node::Bool(token.value == "true")); + return Some(Node::Bool(token.value == "true")); } if let Some(token) = self.scan_keyword("false") { self.cursor = token.position; - return Some(node::Node::Bool(token.value == "true")); + return Some(Node::Bool(token.value == "true")); } if let Some(token) = self.scan_text(";") { self.cursor = token.position; - return Some(node::Node::Semicolon); + return Some(Node::Semicolon); + } + + if let Some(token) = self.scan_float() { + if let Ok(val) = token.value.parse::() { + self.cursor = token.position; + return Some(Node::Float(val)); + } + } + + if let Some(token) = self.scan_int() { + if let Ok(val) = token.value.parse::() { + self.cursor = token.position; + return Some(Node::Int(val)); + } } None @@ -140,8 +227,9 @@ impl Iterator for Lexer { mod test { use super::*; use crate::ast::error; + use crate::ast::node::Node; - fn lexer_run(lexer: &mut Lexer, text: &str) -> Result, error::AstError> { + fn lexer_run(lexer: &mut Lexer, text: &str) -> Result, error::AstError> { lexer.prepare(text); let result = lexer.collect(); @@ -168,8 +256,8 @@ mod test { let mut lex = Lexer::new(); let res = lexer_run(&mut lex, "true false").unwrap(); assert_eq!(2, res.len()); - assert_eq!("Bool(true)", format!("{:?}", res.get(0).unwrap())); - assert_eq!("Bool(false)", format!("{:?}", res.get(1).unwrap())); + assert_eq!(Node::Bool(true), *res.get(0).unwrap()); + assert_eq!(Node::Bool(false), *res.get(1).unwrap()); } #[test] @@ -177,8 +265,8 @@ mod test { let mut lex = Lexer::new(); let res = lexer_run(&mut lex, "true;").unwrap(); assert_eq!(2, res.len()); - assert_eq!("Bool(true)", format!("{:?}", res.get(0).unwrap())); - assert_eq!("Semicolon", format!("{:?}", res.get(1).unwrap())); + assert_eq!(Node::Bool(true), *res.get(0).unwrap()); + assert_eq!(Node::Semicolon, *res.get(1).unwrap()); } #[test] @@ -203,8 +291,33 @@ mod test { let mut lex = Lexer::new(); let res = lexer_run(&mut lex, " ;; ")?; assert_eq!(2, res.len()); - assert_eq!("Semicolon", format!("{:?}", res.get(0).unwrap())); - assert_eq!("Semicolon", format!("{:?}", res.get(1).unwrap())); + assert_eq!(Node::Semicolon, *res.get(0).unwrap()); + assert_eq!(Node::Semicolon, *res.get(1).unwrap()); + Ok(()) + } + + #[test] + fn test_integers() -> Result<(), error::AstError> { + let mut lex = Lexer::new(); + let res = lexer_run(&mut lex, " 4 -2 328")?; + assert_eq!(3, res.len()); + assert_eq!(Node::Int(4), *res.get(0).unwrap()); + assert_eq!(Node::Int(-2), *res.get(1).unwrap()); + assert_eq!(Node::Int(328), *res.get(2).unwrap()); + + Ok(()) + } + + #[test] + fn test_floats() -> Result<(), error::AstError> { + let mut lex = Lexer::new(); + let res = lexer_run(&mut lex, " 2.0 .7 5. -3.14 ")?; + assert_eq!(4, res.len()); + assert_eq!(Node::Float(2.0), *res.get(0).unwrap()); + assert_eq!(Node::Float(0.7), *res.get(1).unwrap()); + assert_eq!(Node::Float(5.0), *res.get(2).unwrap()); + assert_eq!(Node::Float(-3.14), *res.get(3).unwrap()); + Ok(()) } } diff --git a/src/ast/node.rs b/src/ast/node.rs index f3a43ec..b1ae045 100644 --- a/src/ast/node.rs +++ b/src/ast/node.rs @@ -3,6 +3,8 @@ pub enum Node { Module(String, Vec), Bool(bool), + Int(i32), + Float(f64), Semicolon } diff --git a/src/ast/parser.rs b/src/ast/parser.rs index faab2eb..28ef6a8 100644 --- a/src/ast/parser.rs +++ b/src/ast/parser.rs @@ -1,5 +1,5 @@ use crate::ast::{ - node, + node::Node, lexer, error }; @@ -9,7 +9,7 @@ pub struct Parser { lexer: lexer::Lexer } -type ParseResult = Result, error::AstError>; +type ParseResult = Result, error::AstError>; impl Parser { pub fn new(module_name: &str, lex: lexer::Lexer) -> Self { @@ -24,7 +24,7 @@ impl Parser { } fn parse_module(&mut self) -> ParseResult { - let mut children: Vec = vec![]; + let mut children: Vec = vec![]; loop { match self.parse_instr() { @@ -41,7 +41,7 @@ impl Parser { }); } - let n = node::Node::Module(self.module_name.clone(), children); + let n = Node::Module(self.module_name.clone(), children); Ok(Some(n)) } @@ -49,7 +49,7 @@ impl Parser { match self.parse_expr() { Ok(Some(expr)) => { match self.lexer.next() { - Some(node::Node::Semicolon) => Ok(Some(expr)), + Some(Node::Semicolon) => Ok(Some(expr)), None => Ok(None), _ => Err(error::AstError { msg: String::from("missing semicolon"), @@ -68,8 +68,14 @@ impl Parser { fn parse_builtin(&mut self) -> ParseResult { match self.lexer.next() { - Some(node::Node::Bool(value)) - => Ok(Some(node::Node::Bool(value))), + Some(Node::Int(value)) + => Ok(Some(Node::Int(value))), + + Some(Node::Float(value)) + => Ok(Some(Node::Float(value))), + + Some(Node::Bool(value)) + => Ok(Some(Node::Bool(value))), Some(other) => { Err(error::AstError { @@ -86,11 +92,10 @@ impl Parser { #[cfg(test)] mod test { use super::*; - use crate::ast::node::Node; type TestResult = Result<(), String>; - fn test_parser(oracle: node::Node, source: &str) -> TestResult { + fn test_parser(oracle: Node, source: &str) -> TestResult { let mut lex = lexer::Lexer::new(); lex.prepare(source); let mut parser = Parser::new("mod", lex); @@ -140,4 +145,23 @@ mod test { Ok(()) } + + #[test] + fn numbers() -> TestResult { + test_parser( + Node::Module("mod".to_owned(), vec![ + Node::Float(27.3) + ]), + " 27.3; " + )?; + + test_parser( + Node::Module("mod".to_owned(), vec![ + Node::Int(-27) + ]), + " -27; " + )?; + + Ok(()) + } } diff --git a/src/eval/evaluator.rs b/src/eval/evaluator.rs index eb94a49..b5ee2fe 100644 --- a/src/eval/evaluator.rs +++ b/src/eval/evaluator.rs @@ -29,7 +29,11 @@ impl Evaluator { Ok(res) }, + Node::Bool(value) => Ok(Some(Value::Bool(*value))), + Node::Int(value) => Ok(Some(Value::Int(*value))), + Node::Float(value) => Ok(Some(Value::Float(*value))), + _ => Ok(None) } } @@ -65,7 +69,10 @@ mod test { Ok(()) } } else { - Ok(()) + Err(Box::new(EvalError { + msg: format!("unexpected: {:?}", value), + line: 0 + })) } }, None => Ok(()) @@ -79,4 +86,12 @@ mod test { Ok(()) } + + #[test] + fn numbers() -> TestResult { + test_eval_value(Value::Float(-271.4), " -271.4; ")?; + test_eval_value(Value::Int(333), " 333; ")?; + + Ok(()) + } } diff --git a/src/eval/value.rs b/src/eval/value.rs index 8d74361..53559c9 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -1,6 +1,8 @@ #[derive(PartialEq)] #[derive(Debug)] pub enum Value { - Bool(bool) + Bool(bool), + Int(i32), + Float(f64) }