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*
INSTR ::= EXPR semicolon
EXPR ::= BUILTIN
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
| ident
BUILTIN ::=
| bool
| float
| int
| string
ARRAY ::= osquare EXPR* csquare

View File

@ -19,7 +19,8 @@ impl Lexer {
line: 1,
text: String::from(""),
separators: vec![
';'
';', '[', ']',
'=', '?', '+'
]
}
}
@ -38,9 +39,22 @@ impl Lexer {
self.line = 1;
}
fn is_sep(&self, pos: usize) -> bool {
if let Some(c) = self.text.chars().nth(pos) {
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() {
@ -50,9 +64,46 @@ impl Lexer {
return c.is_whitespace();
}
fn is_sep(&self, pos: usize) -> bool {
if let Some(c) = self.text.chars().nth(pos) {
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;
@ -231,6 +282,21 @@ impl Lexer {
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 {
@ -238,6 +304,32 @@ impl Iterator for Lexer {
fn next(&mut self) -> Option<Self::Item> {
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") {
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
}
}
@ -324,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]
@ -388,4 +484,48 @@ mod test {
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(())
}
}

View File

@ -6,6 +6,16 @@ pub enum Node {
Int(i32),
Float(f64),
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
}

View File

@ -45,50 +45,142 @@ impl Parser {
Ok(Some(n))
}
fn parse_instr(&mut self) -> ParseResult {
match self.parse_expr() {
Ok(Some(expr)) => {
fn ensure_semicolon(&mut self, result: ParseResult)
-> ParseResult {
match self.lexer.next() {
Some(Node::Semicolon) => Ok(Some(expr)),
None => Ok(None),
Some(Node::Semicolon) => result,
_ => Err(error::AstError {
msg: String::from("missing semicolon"),
line: self.lexer.line()})
}
}
fn parse_instr(&mut self) -> ParseResult {
match self.lexer.peek(1) {
Some(Node::Assign) => {
let assign = self.parse_assign();
self.ensure_semicolon(assign)
},
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 parse_expr(&mut self) -> ParseResult {
self.parse_builtin()
}
fn assign_helper(&mut self)
-> Result<(Box<Node>, Vec<Node>), error::AstError> {
let mut rhs: Vec<Node> = vec![];
fn parse_builtin(&mut self) -> ParseResult {
match self.lexer.next() {
Some(Node::Int(value))
=> Ok(Some(Node::Int(value))),
if let Some(Node::Ident(value)) = self.lexer.next() {
let _assign = self.lexer.next();
Some(Node::Float(value))
=> Ok(Some(Node::Float(value))),
loop {
match self.lexer.peek(0) {
Some(Node::Semicolon) | None => {
break;
},
Some(Node::Bool(value))
=> Ok(Some(Node::Bool(value))),
Some(v) => { self.lexer.next(); rhs.push(v); }
}
}
Some(Node::String(value))
=> Ok(Some(Node::String(value))),
Ok((Box::new(Node::Ident(value)), rhs))
Some(other) => {
} else {
Err(error::AstError {
msg: String::from(format!("unexpected {:?}", other)),
msg: String::from("cannot assign value to node"),
line: self.lexer.line()
})
}
None => Ok(None)
}
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 {
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 {
let node = self.lexer.next().unwrap();
match node {
Node::Int(value)
=> Ok(Some(Node::Int(value))),
Node::Float(value)
=> Ok(Some(Node::Float(value))),
Node::Bool(value)
=> Ok(Some(Node::Bool(value))),
Node::String(value)
=> Ok(Some(Node::String(value))),
_ => {
Err(error::AstError {
msg: String::from(format!("unexpected {:?}", node)),
line: self.lexer.line()
})
}
}
}
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)))
}
}
@ -179,4 +271,59 @@ 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(
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,
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,14 +32,157 @@ 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))),
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)
}
}
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)]
@ -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]
fn simple_boolean() -> TestResult {
test_eval_value(Value::Bool(true), " true; false; true; ")?;
@ -103,4 +266,93 @@ mod test {
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 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,9 +1,11 @@
#[derive(PartialEq)]
#[derive(Debug)]
#[derive(Clone)]
pub enum Value {
Bool(bool),
Int(i32),
Float(f64),
String(String)
String(String),
Array(Vec<Value>)
}