Compare commits

..

3 Commits

Author SHA1 Message Date
bog 3811b2ddb2 variable declaration. 2024-03-02 07:36:52 +01:00
bog 4e7e7ad07b array values. 2024-03-01 18:44:16 +01:00
bog 35e8cc813e add comments. 2024-03-01 18:11:50 +01:00
8 changed files with 666 additions and 59 deletions

View File

@ -1,8 +1,21 @@
MODULE ::= INSTR* MODULE ::= INSTR*
INSTR ::= EXPR semicolon INSTR ::=
EXPR ::= BUILTIN | 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
| ident
BUILTIN ::= BUILTIN ::=
| bool | bool
| float | float
| int | int
| string | string
ARRAY ::= osquare EXPR* csquare

View File

@ -19,7 +19,8 @@ impl Lexer {
line: 1, line: 1,
text: String::from(""), text: String::from(""),
separators: vec![ separators: vec![
';' ';', '[', ']',
'=', '?', '+'
] ]
} }
} }
@ -38,21 +39,71 @@ impl Lexer {
self.line = 1; self.line = 1;
} }
pub fn peek(&mut self, lookahead: i32) -> Option<Node> {
let cursor = self.cursor;
let line = self.line;
let mut node: Option<Node> = 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 { fn is_sep(&self, pos: usize) -> bool {
if let Some(c) = self.text.chars().nth(pos) { if let Some(c) = self.text.chars().nth(pos) {
return self.is_char_sep(c);
let el = self.separators.iter().find(|&&x| x == c);
if el.is_some() {
return true;
}
return c.is_whitespace();
} }
false false
} }
fn scan_identifier(&mut self) -> Option<Token> {
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<Token> { fn scan_string(&mut self) -> Option<Token> {
let mut value : String; let mut value : String;
let mut length = 1; let mut length = 1;
@ -62,7 +113,7 @@ impl Lexer {
} else { } else {
return None; return None;
} }
let mut escape = false; let mut escape = false;
let mut closed = false; let mut closed = false;
@ -71,7 +122,7 @@ impl Lexer {
escape = true; escape = true;
length += 1; length += 1;
continue; continue;
} }
if escape { if escape {
match c { match c {
@ -80,7 +131,7 @@ impl Lexer {
'n' => value.push('\n'), 'n' => value.push('\n'),
't' => value.push('\t'), 't' => value.push('\t'),
'r' => value.push('\r'), 'r' => value.push('\r'),
_ => {} _ => {}
} }
} else if c == '"' { } else if c == '"' {
closed = true; closed = true;
@ -105,7 +156,7 @@ impl Lexer {
fn scan_int(&self) -> Option<Token> { fn scan_int(&self) -> Option<Token> {
let mut value = String::from(""); let mut value = String::from("");
if let Some('-') = self.text.chars().nth(self.cursor) { if let Some('-') = self.text.chars().nth(self.cursor) {
value.push('-'); value.push('-');
} }
@ -117,7 +168,7 @@ impl Lexer {
break; break;
} }
} }
if value.len() > 0 { if value.len() > 0 {
Some(Token { Some(Token {
position: self.cursor + value.len(), position: self.cursor + value.len(),
@ -125,12 +176,12 @@ impl Lexer {
}) })
} else { } else {
None None
} }
} }
fn scan_float(&self) -> Option<Token> { fn scan_float(&self) -> Option<Token> {
let mut value = String::from(""); let mut value = String::from("");
if let Some('-') = self.text.chars().nth(self.cursor) { if let Some('-') = self.text.chars().nth(self.cursor) {
value.push('-'); value.push('-');
} }
@ -142,7 +193,7 @@ impl Lexer {
break; break;
} }
} }
if let Some('.') = self.text.chars().nth(self.cursor + value.len()) { if let Some('.') = self.text.chars().nth(self.cursor + value.len()) {
value.push('.'); value.push('.');
} else { } else {
@ -156,7 +207,7 @@ impl Lexer {
break; break;
} }
} }
if value.len() > 0 && value != "." { if value.len() > 0 && value != "." {
if value.chars().nth(0).unwrap() == '.' { if value.chars().nth(0).unwrap() == '.' {
value = format!("0{}", value); value = format!("0{}", value);
@ -172,7 +223,7 @@ impl Lexer {
}) })
} else { } else {
None None
} }
} }
fn scan_text(&self, text: &str) -> Option<Token> { fn scan_text(&self, text: &str) -> Option<Token> {
@ -231,6 +282,21 @@ impl Lexer {
self.cursor += 1; self.cursor += 1;
} }
} }
fn skip_comments(&mut self) {
while let Some('#') = self.text.chars().nth(self.cursor) {
loop {
if let Some('\n')
= self.text.chars().nth(self.cursor) {
break;
}
self.cursor += 1;
}
self.skip_spaces();
}
}
} }
impl Iterator for Lexer { impl Iterator for Lexer {
@ -238,6 +304,32 @@ impl Iterator for Lexer {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.skip_spaces(); 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);
}
if let Some(token) = self.scan_text("]") {
self.cursor = token.position;
return Some(Node::CSquare);
}
if let Some(token) = self.scan_keyword("true") { if let Some(token) = self.scan_keyword("true") {
self.cursor = token.position; self.cursor = token.position;
@ -273,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 None
} }
} }
@ -324,13 +421,12 @@ mod test {
} }
#[test] #[test]
fn test_wrong_ident() { fn test_identifier() {
let mut lex = Lexer::new(); let mut lex = Lexer::new();
let res = lexer_run(&mut lex, " truea "); let res = lexer_run(&mut lex, " truea atrue ").unwrap();
assert!(res.is_err()); assert_eq!(2, res.len());
let mut lex = Lexer::new(); assert_eq!(Node::Ident(String::from("truea")), *res.get(0).unwrap());
let res = lexer_run(&mut lex, " atrue "); assert_eq!(Node::Ident(String::from("atrue")), *res.get(1).unwrap());
assert!(res.is_err());
} }
#[test] #[test]
@ -349,7 +445,7 @@ mod test {
assert_eq!(Node::Semicolon, *res.get(1).unwrap()); assert_eq!(Node::Semicolon, *res.get(1).unwrap());
Ok(()) Ok(())
} }
#[test] #[test]
fn test_integers() -> Result<(), error::AstError> { fn test_integers() -> Result<(), error::AstError> {
let mut lex = Lexer::new(); let mut lex = Lexer::new();
@ -378,14 +474,58 @@ mod test {
#[test] #[test]
fn test_string() -> Result<(), error::AstError> { fn test_string() -> Result<(), error::AstError> {
let mut lex = Lexer::new(); let mut lex = Lexer::new();
let res = lexer_run(&mut lex, let res = lexer_run(&mut lex,
" \"s\\ta\\\"lut\\n\" ")?; " \"s\\ta\\\"lut\\n\" ")?;
assert_eq!(1, res.len()); 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()); *res.get(0).unwrap());
Ok(())
}
#[test]
fn test_comments() -> Result<(), error::AstError> {
let mut lex = Lexer::new();
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,
" [ ] ")?;
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(()) Ok(())
} }
} }

View File

@ -6,6 +6,16 @@ pub enum Node {
Int(i32), Int(i32),
Float(f64), Float(f64),
String(String), String(String),
OSquare,
CSquare,
Array(Vec<Node>),
Assign,
AssignIf,
AssignAdd,
AssignInstr(Box<Node>, Vec<Node>),
AssignIfInstr(Box<Node>, Vec<Node>),
AssignAddInstr(Box<Node>, Vec<Node>),
Ident(String),
Semicolon Semicolon
} }

View File

@ -45,51 +45,143 @@ impl Parser {
Ok(Some(n)) 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 { fn parse_instr(&mut self) -> ParseResult {
match self.parse_expr() { match self.lexer.peek(1) {
Ok(Some(expr)) => { Some(Node::Assign) => {
match self.lexer.next() { let assign = self.parse_assign();
Some(Node::Semicolon) => Ok(Some(expr)), self.ensure_semicolon(assign)
None => Ok(None),
_ => Err(error::AstError {
msg: String::from("missing semicolon"),
line: self.lexer.line()})
}
}, },
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<Node>, Vec<Node>), error::AstError> {
let mut rhs: Vec<Node> = 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)
} }
} }
fn parse_expr(&mut self) -> ParseResult { fn parse_expr(&mut self) -> ParseResult {
self.parse_builtin() self.parse_literal()
} }
fn parse_literal(&mut self) -> ParseResult {
match self.lexer.peek(0) {
Some(Node::Ident(id))
=> { self.lexer.next(); Ok(Some(Node::Ident(id))) },
Some(Node::OSquare) => self.parse_array(),
Some(_) => self.parse_builtin(),
None => { self.lexer.next(); Ok(None) }
}
}
fn parse_builtin(&mut self) -> ParseResult { fn parse_builtin(&mut self) -> ParseResult {
match self.lexer.next() { let node = self.lexer.next().unwrap();
Some(Node::Int(value)) match node {
Node::Int(value)
=> Ok(Some(Node::Int(value))), => Ok(Some(Node::Int(value))),
Some(Node::Float(value)) Node::Float(value)
=> Ok(Some(Node::Float(value))), => Ok(Some(Node::Float(value))),
Some(Node::Bool(value)) Node::Bool(value)
=> Ok(Some(Node::Bool(value))), => Ok(Some(Node::Bool(value))),
Some(Node::String(value)) Node::String(value)
=> Ok(Some(Node::String(value))), => Ok(Some(Node::String(value))),
Some(other) => { _ => {
Err(error::AstError { Err(error::AstError {
msg: String::from(format!("unexpected {:?}", other)), msg: String::from(format!("unexpected {:?}", node)),
line: self.lexer.line() line: self.lexer.line()
}) })
} }
None => Ok(None)
} }
} }
fn parse_array(&mut self) -> ParseResult {
let mut elements: Vec<Node> = vec![];
let _osquare = self.lexer.next();
while let Ok(Some(expr)) = self.parse_expr() {
elements.push(expr);
}
Ok(Some(Node::Array(elements)))
}
} }
#[cfg(test)] #[cfg(test)]
@ -179,4 +271,59 @@ mod test {
Ok(()) 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(
Node::Module("mod".to_owned(), vec![
Node::Array(vec![
Node::Int(2),
Node::Float(3.2),
Node::Bool(true)
])
]),
" [2 3.2 true]; "
)?;
Ok(())
}
} }

View File

@ -2,12 +2,13 @@ use crate::{
ast::node::Node, ast::node::Node,
eval::{ eval::{
value::Value, value::Value,
error::EvalError error::EvalError,
symtable::SymTable
} }
}; };
pub struct Evaluator { pub struct Evaluator {
table: SymTable
} }
type EvalResult = Result<Option<Value>, EvalError>; type EvalResult = Result<Option<Value>, EvalError>;
@ -15,6 +16,7 @@ type EvalResult = Result<Option<Value>, EvalError>;
impl Evaluator { impl Evaluator {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
table: SymTable::new()
} }
} }
@ -29,15 +31,158 @@ impl Evaluator {
Ok(res) 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<Value> = 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::Bool(value) => Ok(Some(Value::Bool(*value))),
Node::Int(value) => Ok(Some(Value::Int(*value))), Node::Int(value) => Ok(Some(Value::Int(*value))),
Node::Float(value) => Ok(Some(Value::Float(*value))), Node::Float(value) => Ok(Some(Value::Float(*value))),
Node::String(value) => Ok(Some(Value::String(value.clone()))), Node::String(value)
=> Ok(Some(Value::String(value.clone()))),
Node::Array(nodes) => {
let mut values: Vec<Value> = vec![];
for node in nodes {
match self.run(node) {
Ok(Some(value)) => {
values.push(value);
},
_ => {
return Err(EvalError {
msg: String::from("invalid array element"),
line: 0
});
}
}
}
Ok(Some(Value::Array(values)))
}
_ => Ok(None) _ => Ok(None)
} }
} }
fn nodes_to_value(&mut self, nodes: &Vec<Node>) -> EvalResult {
let mut val_array: Vec<Value> = 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)] #[cfg(test)]
@ -80,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] #[test]
fn simple_boolean() -> TestResult { fn simple_boolean() -> TestResult {
test_eval_value(Value::Bool(true), " true; false; true; ")?; test_eval_value(Value::Bool(true), " true; false; true; ")?;
@ -98,9 +261,98 @@ mod test {
#[test] #[test]
fn strings() -> TestResult { fn strings() -> TestResult {
test_eval_value(Value::String(String::from(" pizzza!!!")), test_eval_value(Value::String(String::from(" pizzza!!!")),
" \" pizzza!!!\"; ")?; " \" pizzza!!!\"; ")?;
Ok(()) Ok(())
} }
#[test]
fn arrays() -> TestResult {
test_eval_value(
Value::Array(
vec![
Value::Int(2),
Value::Int(4),
Value::Float(6.1)
]
),
" [2 4 6.1]; ")?;
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(())
}
} }

View File

@ -1,3 +1,4 @@
pub mod value; pub mod value;
pub mod evaluator; pub mod evaluator;
pub mod error; pub mod error;
pub mod symtable;

42
src/eval/symtable.rs Normal file
View File

@ -0,0 +1,42 @@
use crate::eval::value::Value;
use std::collections::HashMap;
struct Sym {
value: Value
}
pub struct SymTable {
table: HashMap<String, Sym>
}
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<Value> {
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());
}
}

View File

@ -1,9 +1,11 @@
#[derive(PartialEq)] #[derive(PartialEq)]
#[derive(Debug)] #[derive(Debug)]
#[derive(Clone)]
pub enum Value { pub enum Value {
Bool(bool), Bool(bool),
Int(i32), Int(i32),
Float(f64), Float(f64),
String(String) String(String),
Array(Vec<Value>)
} }