variable declaration.

main
bog 2024-03-02 07:36:52 +01:00
parent 4e7e7ad07b
commit 3811b2ddb2
8 changed files with 538 additions and 59 deletions

View File

@ -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

View File

@ -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<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 {
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<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> {
let mut value : String;
let mut length = 1;
@ -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]
@ -442,4 +512,20 @@ mod test {
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(())
}
}

View File

@ -9,6 +9,13 @@ pub enum Node {
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
}

View File

@ -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<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)
}
}
@ -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,6 +174,8 @@ impl Parser {
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);
}
@ -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(

View File

@ -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<Option<Value>, EvalError>;
@ -15,6 +16,7 @@ type EvalResult = Result<Option<Value>, 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<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::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<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)]
@ -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(())
}
}

View File

@ -1,3 +1,4 @@
pub mod value;
pub mod evaluator;
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,5 +1,6 @@
#[derive(PartialEq)]
#[derive(Debug)]
#[derive(Clone)]
pub enum Value {
Bool(bool),
Int(i32),