diff --git a/doc/grammar.bnf b/doc/grammar.bnf index d9ede09..bfb5024 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -1,4 +1,8 @@ MODULE ::= INSTR* INSTR ::= EXPR semicolon EXPR ::= BUILTIN -BUILTIN ::= bool | float | int +BUILTIN ::= +| bool +| float +| int +| string diff --git a/src/ast/lexer.rs b/src/ast/lexer.rs index f1e9bee..e662266 100644 --- a/src/ast/lexer.rs +++ b/src/ast/lexer.rs @@ -53,6 +53,55 @@ impl Lexer { false } + fn scan_string(&mut self) -> Option { + let mut value : String; + let mut length = 1; + + if let Some('"') = self.text.chars().nth(self.cursor) { + value = String::from(""); + } else { + return None; + } + + let mut escape = false; + let mut closed = false; + + for c in self.text.chars().skip(length + self.cursor) { + if !escape && c == '\\' { + escape = true; + length += 1; + continue; + } + + if escape { + match c { + '"' => value.push('"'), + '\\' => value.push('\\'), + 'n' => value.push('\n'), + 't' => value.push('\t'), + 'r' => value.push('\r'), + _ => {} + } + } else if c == '"' { + closed = true; + break; + } else { + value.push(c); + } + + escape = false; + length += 1; + } + + if length >= 2 && closed { + return Some(Token { + position: 1 + self.cursor + length, + value + }); + } + + None + } fn scan_int(&self) -> Option { let mut value = String::from(""); @@ -205,6 +254,11 @@ impl Iterator for Lexer { return Some(Node::Semicolon); } + if let Some(token) = self.scan_string() { + self.cursor = token.position; + return Some(Node::String(token.value.clone())); + } + if let Some(token) = self.scan_float() { if let Ok(val) = token.value.parse::() { self.cursor = token.position; @@ -320,4 +374,18 @@ mod test { Ok(()) } + + #[test] + fn test_string() -> Result<(), error::AstError> { + let mut lex = Lexer::new(); + + let res = lexer_run(&mut lex, + " \"s\\ta\\\"lut\\n\" ")?; + + assert_eq!(1, res.len()); + assert_eq!(Node::String(String::from("s\ta\"lut\n")), + *res.get(0).unwrap()); + + Ok(()) + } } diff --git a/src/ast/node.rs b/src/ast/node.rs index b1ae045..437fb96 100644 --- a/src/ast/node.rs +++ b/src/ast/node.rs @@ -5,6 +5,7 @@ pub enum Node { Bool(bool), Int(i32), Float(f64), + String(String), Semicolon } diff --git a/src/ast/parser.rs b/src/ast/parser.rs index 28ef6a8..8313f73 100644 --- a/src/ast/parser.rs +++ b/src/ast/parser.rs @@ -77,6 +77,9 @@ impl Parser { Some(Node::Bool(value)) => Ok(Some(Node::Bool(value))), + Some(Node::String(value)) + => Ok(Some(Node::String(value))), + Some(other) => { Err(error::AstError { msg: String::from(format!("unexpected {:?}", other)), @@ -164,4 +167,16 @@ mod test { Ok(()) } + + #[test] + fn strings() -> TestResult { + test_parser( + Node::Module("mod".to_owned(), vec![ + Node::String(String::from("pizza! ")) + ]), + " \"pizza! \"; " + )?; + + Ok(()) + } } diff --git a/src/eval/evaluator.rs b/src/eval/evaluator.rs index b5ee2fe..2bc0995 100644 --- a/src/eval/evaluator.rs +++ b/src/eval/evaluator.rs @@ -33,6 +33,7 @@ impl Evaluator { Node::Bool(value) => Ok(Some(Value::Bool(*value))), Node::Int(value) => Ok(Some(Value::Int(*value))), Node::Float(value) => Ok(Some(Value::Float(*value))), + Node::String(value) => Ok(Some(Value::String(value.clone()))), _ => Ok(None) } @@ -94,4 +95,12 @@ mod test { Ok(()) } + + #[test] + fn strings() -> TestResult { + test_eval_value(Value::String(String::from(" pizzza!!!")), + " \" pizzza!!!\"; ")?; + + Ok(()) + } } diff --git a/src/eval/value.rs b/src/eval/value.rs index 53559c9..a6af228 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -3,6 +3,7 @@ pub enum Value { Bool(bool), Int(i32), - Float(f64) + Float(f64), + String(String) }