From 3811b2ddb2cfe4b30ba60ffdd6be9e65ae8841ea Mon Sep 17 00:00:00 2001 From: bog Date: Sat, 2 Mar 2024 07:36:52 +0100 Subject: [PATCH] :sparkles: variable declaration. --- doc/grammar.bnf | 14 ++- src/ast/lexer.rs | 164 +++++++++++++++++++++++-------- src/ast/node.rs | 7 ++ src/ast/parser.rs | 148 +++++++++++++++++++++++++--- src/eval/evaluator.rs | 220 +++++++++++++++++++++++++++++++++++++++++- src/eval/mod.rs | 1 + src/eval/symtable.rs | 42 ++++++++ src/eval/value.rs | 1 + 8 files changed, 538 insertions(+), 59 deletions(-) create mode 100644 src/eval/symtable.rs diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 312c5a2..5239a3a 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -1,7 +1,17 @@ MODULE ::= INSTR* -INSTR ::= EXPR semicolon +INSTR ::= +| EXPR semicolon +| ASSIGN semicolon +| ASSIGN_IF semicolon +| ASSIGN_ADD semicolon +ASSIGN ::= ident assign EXPR +ASSIGN_IF ::= ident assign_if EXPR +ASSIGN_ADD ::= ident assign_add EXPR EXPR ::= LITERAL -LITERAL ::= BUILTIN | ARRAY +LITERAL ::= +| BUILTIN +| ARRAY +| ident BUILTIN ::= | bool | float diff --git a/src/ast/lexer.rs b/src/ast/lexer.rs index efa88bb..096631f 100644 --- a/src/ast/lexer.rs +++ b/src/ast/lexer.rs @@ -19,7 +19,8 @@ impl Lexer { line: 1, text: String::from(""), separators: vec![ - ';', '[', ']' + ';', '[', ']', + '=', '?', '+' ] } } @@ -38,21 +39,71 @@ impl Lexer { self.line = 1; } + pub fn peek(&mut self, lookahead: i32) -> Option { + let cursor = self.cursor; + let line = self.line; + + let mut node: Option = None; + + for _ in 0..=lookahead { + node = self.next(); + } + + self.cursor = cursor; + self.line = line; + node + } + + fn is_char_sep(&self, c: char) -> bool { + let el = self.separators.iter().find(|&&x| x == c); + + if el.is_some() { + return true; + } + + return c.is_whitespace(); + } + fn is_sep(&self, pos: usize) -> bool { if let Some(c) = self.text.chars().nth(pos) { - - let el = self.separators.iter().find(|&&x| x == c); - - if el.is_some() { - return true; - } - - return c.is_whitespace(); + return self.is_char_sep(c); } false } + fn scan_identifier(&mut self) -> Option { + let mut value = String::from(""); + let mut first = true; + + for c in self.text.chars().skip(self.cursor) { + if self.is_char_sep(c) { + break; + } + + if first && c.is_digit(10) { + break; + } + + if !c.is_ascii_alphabetic() && c != '_' + && !c.is_digit(10) { + break; + } + + value.push(c); + first = false; + } + + if value.len() > 0 { + return Some(Token { + position: self.cursor + value.len(), + value + }); + } + + None + } + fn scan_string(&mut self) -> Option { let mut value : String; let mut length = 1; @@ -62,7 +113,7 @@ impl Lexer { } else { return None; } - + let mut escape = false; let mut closed = false; @@ -71,7 +122,7 @@ impl Lexer { escape = true; length += 1; continue; - } + } if escape { match c { @@ -80,7 +131,7 @@ impl Lexer { 'n' => value.push('\n'), 't' => value.push('\t'), 'r' => value.push('\r'), - _ => {} + _ => {} } } else if c == '"' { closed = true; @@ -105,7 +156,7 @@ impl Lexer { fn scan_int(&self) -> Option { let mut value = String::from(""); - + if let Some('-') = self.text.chars().nth(self.cursor) { value.push('-'); } @@ -117,7 +168,7 @@ impl Lexer { break; } } - + if value.len() > 0 { Some(Token { position: self.cursor + value.len(), @@ -125,12 +176,12 @@ impl Lexer { }) } else { None - } + } } fn scan_float(&self) -> Option { let mut value = String::from(""); - + if let Some('-') = self.text.chars().nth(self.cursor) { value.push('-'); } @@ -142,7 +193,7 @@ impl Lexer { break; } } - + if let Some('.') = self.text.chars().nth(self.cursor + value.len()) { value.push('.'); } else { @@ -156,7 +207,7 @@ impl Lexer { break; } } - + if value.len() > 0 && value != "." { if value.chars().nth(0).unwrap() == '.' { value = format!("0{}", value); @@ -172,7 +223,7 @@ impl Lexer { }) } else { None - } + } } fn scan_text(&self, text: &str) -> Option { @@ -235,16 +286,16 @@ impl Lexer { fn skip_comments(&mut self) { while let Some('#') = self.text.chars().nth(self.cursor) { loop { - if let Some('\n') + if let Some('\n') = self.text.chars().nth(self.cursor) { - break; + break; } self.cursor += 1; } self.skip_spaces(); - } + } } } @@ -255,6 +306,21 @@ impl Iterator for Lexer { self.skip_spaces(); self.skip_comments(); + if let Some(token) = self.scan_text("+=") { + self.cursor = token.position; + return Some(Node::AssignAdd); + } + + if let Some(token) = self.scan_text("?=") { + self.cursor = token.position; + return Some(Node::AssignIf); + } + + if let Some(token) = self.scan_text("=") { + self.cursor = token.position; + return Some(Node::Assign); + } + if let Some(token) = self.scan_text("[") { self.cursor = token.position; return Some(Node::OSquare); @@ -299,6 +365,11 @@ impl Iterator for Lexer { } } + if let Some(token) = self.scan_identifier() { + self.cursor = token.position; + return Some(Node::Ident(token.value.clone())); + } + None } } @@ -350,13 +421,12 @@ mod test { } #[test] - fn test_wrong_ident() { + fn test_identifier() { let mut lex = Lexer::new(); - let res = lexer_run(&mut lex, " truea "); - assert!(res.is_err()); - let mut lex = Lexer::new(); - let res = lexer_run(&mut lex, " atrue "); - assert!(res.is_err()); + let res = lexer_run(&mut lex, " truea atrue ").unwrap(); + assert_eq!(2, res.len()); + assert_eq!(Node::Ident(String::from("truea")), *res.get(0).unwrap()); + assert_eq!(Node::Ident(String::from("atrue")), *res.get(1).unwrap()); } #[test] @@ -375,7 +445,7 @@ mod test { assert_eq!(Node::Semicolon, *res.get(1).unwrap()); Ok(()) } - + #[test] fn test_integers() -> Result<(), error::AstError> { let mut lex = Lexer::new(); @@ -404,42 +474,58 @@ mod test { #[test] fn test_string() -> Result<(), error::AstError> { let mut lex = Lexer::new(); - - let res = lexer_run(&mut lex, + + 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")), + assert_eq!(Node::String(String::from("s\ta\"lut\n")), *res.get(0).unwrap()); - + Ok(()) } #[test] fn test_comments() -> Result<(), error::AstError> { let mut lex = Lexer::new(); - - let res = lexer_run(&mut lex, + + let res = lexer_run(&mut lex, " # aze \n 4 ")?; assert_eq!(1, res.len()); assert_eq!(Node::Int(4), *res.get(0).unwrap()); - + Ok(()) } #[test] fn test_arrays() -> Result<(), error::AstError> { let mut lex = Lexer::new(); - - let res = lexer_run(&mut lex, + + let res = lexer_run(&mut lex, " [ ] ")?; assert_eq!(2, res.len()); assert_eq!(Node::OSquare, *res.get(0).unwrap()); assert_eq!(Node::CSquare, *res.get(1).unwrap()); - + + Ok(()) + } + + #[test] + fn test_variables() -> Result<(), error::AstError> { + let mut lex = Lexer::new(); + + let res = lexer_run(&mut lex, + " = ?= += ananas ")?; + + assert_eq!(4, res.len()); + assert_eq!(Node::Assign, *res.get(0).unwrap()); + assert_eq!(Node::AssignIf, *res.get(1).unwrap()); + assert_eq!(Node::AssignAdd, *res.get(2).unwrap()); + assert_eq!(Node::Ident(String::from("ananas")), *res.get(3).unwrap()); + Ok(()) } } diff --git a/src/ast/node.rs b/src/ast/node.rs index 1b87916..10d707c 100644 --- a/src/ast/node.rs +++ b/src/ast/node.rs @@ -9,6 +9,13 @@ pub enum Node { OSquare, CSquare, Array(Vec), + Assign, + AssignIf, + AssignAdd, + AssignInstr(Box, Vec), + AssignIfInstr(Box, Vec), + AssignAddInstr(Box, Vec), + Ident(String), Semicolon } diff --git a/src/ast/parser.rs b/src/ast/parser.rs index 8bbdd84..1c106e1 100644 --- a/src/ast/parser.rs +++ b/src/ast/parser.rs @@ -45,19 +45,91 @@ impl Parser { Ok(Some(n)) } + fn ensure_semicolon(&mut self, result: ParseResult) + -> ParseResult { + match self.lexer.next() { + Some(Node::Semicolon) => result, + _ => Err(error::AstError { + msg: String::from("missing semicolon"), + line: self.lexer.line()}) + } + } + fn parse_instr(&mut self) -> ParseResult { - match self.parse_expr() { - Ok(Some(expr)) => { - match self.lexer.next() { - Some(Node::Semicolon) => Ok(Some(expr)), - None => Ok(None), - _ => Err(error::AstError { - msg: String::from("missing semicolon"), - line: self.lexer.line()}) - } + match self.lexer.peek(1) { + Some(Node::Assign) => { + let assign = self.parse_assign(); + self.ensure_semicolon(assign) }, - Err(err) => { Err(err) }, - Ok(None) => Ok(None) + + Some(Node::AssignIf) => { + let assign = self.parse_assign_if(); + self.ensure_semicolon(assign) + }, + + Some(Node::AssignAdd) => { + let assign = self.parse_assign_add(); + self.ensure_semicolon(assign) + }, + + _ => + match self.parse_expr() { + Ok(Some(expr)) => + self.ensure_semicolon(Ok(Some(expr))), + Err(err) => { Err(err) }, + Ok(None) => Ok(None) + } + } + } + + fn assign_helper(&mut self) + -> Result<(Box, Vec), error::AstError> { + let mut rhs: Vec = vec![]; + + if let Some(Node::Ident(value)) = self.lexer.next() { + let _assign = self.lexer.next(); + + loop { + match self.lexer.peek(0) { + Some(Node::Semicolon) | None => { + break; + }, + + Some(v) => { self.lexer.next(); rhs.push(v); } + } + } + + Ok((Box::new(Node::Ident(value)), rhs)) + + } else { + Err(error::AstError { + msg: String::from("cannot assign value to node"), + line: self.lexer.line() + }) + } + } + + fn parse_assign(&mut self) -> ParseResult { + match self.assign_helper() { + Ok((lhs, rhs)) + => Ok(Some(Node::AssignInstr(lhs, rhs))), + Err(err) => Err(err) + } + } + + fn parse_assign_if(&mut self) -> ParseResult { + match self.assign_helper() { + Ok((lhs, rhs)) + => Ok(Some(Node::AssignIfInstr(lhs, rhs))), + Err(err) => Err(err) + } + } + + fn parse_assign_add(&mut self) -> ParseResult { + match self.assign_helper() { + Ok((lhs, rhs)) + => Ok(Some(Node::AssignAddInstr(lhs, rhs))), + Err(err) => Err(err) } } @@ -66,14 +138,17 @@ impl Parser { } fn parse_literal(&mut self) -> ParseResult { - match self.lexer.next() { + match self.lexer.peek(0) { + Some(Node::Ident(id)) + => { self.lexer.next(); Ok(Some(Node::Ident(id))) }, Some(Node::OSquare) => self.parse_array(), - Some(node) => self.parse_builtin(node), - None => Ok(None) + Some(_) => self.parse_builtin(), + None => { self.lexer.next(); Ok(None) } } } - fn parse_builtin(&mut self, node: Node) -> ParseResult { + fn parse_builtin(&mut self) -> ParseResult { + let node = self.lexer.next().unwrap(); match node { Node::Int(value) => Ok(Some(Node::Int(value))), @@ -99,8 +174,10 @@ impl Parser { fn parse_array(&mut self) -> ParseResult { let mut elements: Vec = vec![]; + let _osquare = self.lexer.next(); + while let Ok(Some(expr)) = self.parse_expr() { - elements.push(expr); + elements.push(expr); } Ok(Some(Node::Array(elements))) @@ -195,6 +272,45 @@ mod test { Ok(()) } + #[test] + fn assign_variables() -> TestResult { + test_parser( + Node::Module("mod".to_owned(), vec![ + Node::AssignInstr( + Box::new(Node::Ident(String::from("HELLO"))), + vec![Node::Int(4), Node::Bool(true)] + ) + ]), + " HELLO = 4 true; " + )?; + + test_parser( + Node::Module("mod".to_owned(), vec![ + Node::AssignIfInstr( + Box::new(Node::Ident(String::from("HELLO_2"))), + vec![Node::Int(4)] + ) + ]), + " HELLO_2 ?= 4; " + )?; + + test_parser( + Node::Module("mod".to_owned(), vec![ + Node::AssignAddInstr( + Box::new(Node::Ident( + String::from("HELLO_WORLD")) + ), + vec![ + Node::Int(4), + Node::Int(1), + Node::Int(2) + ] + ) + ]), + " HELLO_WORLD += 4 1 2; " + )?; + Ok(()) + } #[test] fn arrays() -> TestResult { test_parser( diff --git a/src/eval/evaluator.rs b/src/eval/evaluator.rs index e3309ec..6fe5532 100644 --- a/src/eval/evaluator.rs +++ b/src/eval/evaluator.rs @@ -2,12 +2,13 @@ use crate::{ ast::node::Node, eval::{ value::Value, - error::EvalError + error::EvalError, + symtable::SymTable } }; pub struct Evaluator { - + table: SymTable } type EvalResult = Result, EvalError>; @@ -15,6 +16,7 @@ type EvalResult = Result, EvalError>; impl Evaluator { pub fn new() -> Self { Self { + table: SymTable::new() } } @@ -30,6 +32,107 @@ impl Evaluator { Ok(res) }, + Node::Ident(name) => { + match self.table.get(name) { + Some(val) => { + Ok(Some(val)) + }, + + None => Err(EvalError { + msg: format!("cannot find <{}>", name), + line: 0 + }) + } + }, + + Node::AssignAddInstr(target, values) => { + match &**target { + Node::Ident(name) => { + let mut val_array: Vec = vec![]; + + match self.table.get(name) { + Some(Value::Array(existing_arr)) => { + val_array = existing_arr; + }, + Some(val) => { + val_array.push(val); + }, + None => { + } + } + + for node_val in values { + let val = self.run(node_val)?; + if let Some(v) = val { + val_array.push(v); + } + } + + let value = Value::Array(val_array); + self.table.set(name, value); + }, + _ => { + return Err(EvalError { + msg: format!( + "cannot assign to target <{:?}>", + target + ), + line: 0 + }); + } + } + + Ok(None) + }, + + Node::AssignIfInstr(target, values) => { + match &**target { + Node::Ident(name) => { + match self.table.get(name) { + Some(_) => {}, + None => { + if let Some(value) + = self.nodes_to_value(values)? { + self.table.set(name, value); + } + } + } + }, + _ => { + return Err(EvalError { + msg: format!( + "cannot assign to target <{:?}>", + target + ), + line: 0 + }); + } + } + + Ok(None) + }, + + Node::AssignInstr(target, values) => { + match &**target { + Node::Ident(name) => { + if let Some(value) = self.nodes_to_value(values)? { + self.table.set(name, value); + } + }, + _ => { + return Err(EvalError { + msg: format!( + "cannot assign to target <{:?}>", + target + ), + line: 0 + }); + } + } + + Ok(None) + }, + Node::Bool(value) => Ok(Some(Value::Bool(*value))), Node::Int(value) => Ok(Some(Value::Int(*value))), Node::Float(value) => Ok(Some(Value::Float(*value))), @@ -59,6 +162,27 @@ impl Evaluator { _ => Ok(None) } } + + fn nodes_to_value(&mut self, nodes: &Vec) -> EvalResult { + let mut val_array: Vec = vec![]; + + for node in nodes { + match self.run(node) { + Ok(Some(val)) => val_array.push(val), + Ok(None) => {}, + Err(err) => { return Err(err); } + } + } + + if val_array.len() == 0 { + Ok(None) + } else if val_array.len() == 1 { + let val = val_array.get(0).unwrap(); + Ok(Some(val.clone())) + } else { + Ok(Some(Value::Array(val_array))) + } + } } #[cfg(test)] @@ -101,6 +225,24 @@ mod test { } } + fn test_eval_fail(source: &str) -> TestResult { + let mut lex = Lexer::new(); + lex.prepare(source); + + let mut parser = Parser::new("mod", lex); + let root = parser.run()?.unwrap(); + + let mut evaluator = Evaluator::new(); + + match evaluator.run(&root) { + Err(_) => Ok(()), + _ => Err(Box::new(EvalError { + msg: format!("\"{}\" should fail", source), + line: 0 + })) + } + } + #[test] fn simple_boolean() -> TestResult { test_eval_value(Value::Bool(true), " true; false; true; ")?; @@ -139,4 +281,78 @@ mod test { Ok(()) } + + #[test] + fn simple_variable() -> TestResult { + test_eval_fail("X = V;")?; + + test_eval_value( + Value::Int(4), + " X = 4; X; ")?; + + test_eval_value( + Value::Float(7.2), + " X = 4; X = 7.2; X; ")?; + + test_eval_value( + Value::Array( + vec![ + Value::Int(4), + Value::Int(9), + Value::Float(5.2), + ] + ), + " X = 4 9 5.2; X; ")?; + + Ok(()) + } + + #[test] + fn variable_assign_if() -> TestResult { + test_eval_fail(" X ?= V; ")?; + + test_eval_value( + Value::Int(4), + " X ?= 4; X; ")?; + + test_eval_value( + Value::Int(4), + " X ?= 4; X ?= 9; X; ")?; + + Ok(()) + } + + #[test] + fn variable_assign_add() -> TestResult { + test_eval_fail("X += V; ")?; + + test_eval_value( + Value::Array( + vec![ + Value::Int(4), + ] + ), + " X += 4; X; ")?; + + test_eval_value( + Value::Array( + vec![ + Value::Int(4), + Value::Float(3.3) + ] + ), + " X = 4; X += 3.3; X; ")?; + + test_eval_value( + Value::Array( + vec![ + Value::Int(4), + Value::Int(1), + Value::Float(3.3) + ] + ), + " X = 4 1; X += 3.3; X; ")?; + + Ok(()) + } } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 489ede0..72340d0 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -1,3 +1,4 @@ pub mod value; pub mod evaluator; pub mod error; +pub mod symtable; diff --git a/src/eval/symtable.rs b/src/eval/symtable.rs new file mode 100644 index 0000000..815bd1b --- /dev/null +++ b/src/eval/symtable.rs @@ -0,0 +1,42 @@ +use crate::eval::value::Value; +use std::collections::HashMap; + +struct Sym { + value: Value +} + +pub struct SymTable { + table: HashMap +} + +impl SymTable { + pub fn new() -> Self { + Self { + table: HashMap::new() + } + } + + pub fn set(&mut self, name: &str, value: Value) { + self.table.insert(name.to_owned(), Sym { value }); + } + + pub fn get(&mut self, name: &str) -> Option { + match self.table.get(name) { + Some(sym) => Some(sym.value.clone()), + None => None + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn declare_new_value() { + let mut sym = SymTable::new(); + assert!(sym.get("hello").is_none()); + sym.set("hello", Value::Int(34)); + assert_eq!(Value::Int(34), sym.get("hello").unwrap()); + } +} diff --git a/src/eval/value.rs b/src/eval/value.rs index 8577d58..986d7e5 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -1,5 +1,6 @@ #[derive(PartialEq)] #[derive(Debug)] +#[derive(Clone)] pub enum Value { Bool(bool), Int(i32),