Compare commits
3 Commits
a0b8cda00d
...
3811b2ddb2
Author | SHA1 | Date |
---|---|---|
bog | 3811b2ddb2 | |
bog | 4e7e7ad07b | |
bog | 35e8cc813e |
|
@ -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
|
||||||
|
|
158
src/ast/lexer.rs
158
src/ast/lexer.rs
|
@ -19,7 +19,8 @@ impl Lexer {
|
||||||
line: 1,
|
line: 1,
|
||||||
text: String::from(""),
|
text: String::from(""),
|
||||||
separators: vec![
|
separators: vec![
|
||||||
';'
|
';', '[', ']',
|
||||||
|
'=', '?', '+'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,9 +39,22 @@ impl Lexer {
|
||||||
self.line = 1;
|
self.line = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_sep(&self, pos: usize) -> bool {
|
pub fn peek(&mut self, lookahead: i32) -> Option<Node> {
|
||||||
if let Some(c) = self.text.chars().nth(pos) {
|
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);
|
let el = self.separators.iter().find(|&&x| x == c);
|
||||||
|
|
||||||
if el.is_some() {
|
if el.is_some() {
|
||||||
|
@ -50,9 +64,46 @@ impl Lexer {
|
||||||
return c.is_whitespace();
|
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
|
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;
|
||||||
|
@ -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]
|
||||||
|
@ -388,4 +484,48 @@ mod test {
|
||||||
|
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,50 +45,142 @@ impl Parser {
|
||||||
Ok(Some(n))
|
Ok(Some(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_instr(&mut self) -> ParseResult {
|
fn ensure_semicolon(&mut self, result: ParseResult)
|
||||||
match self.parse_expr() {
|
-> ParseResult {
|
||||||
Ok(Some(expr)) => {
|
|
||||||
match self.lexer.next() {
|
match self.lexer.next() {
|
||||||
Some(Node::Semicolon) => Ok(Some(expr)),
|
Some(Node::Semicolon) => result,
|
||||||
None => Ok(None),
|
|
||||||
_ => Err(error::AstError {
|
_ => Err(error::AstError {
|
||||||
msg: String::from("missing semicolon"),
|
msg: String::from("missing semicolon"),
|
||||||
line: self.lexer.line()})
|
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) },
|
Err(err) => { Err(err) },
|
||||||
Ok(None) => Ok(None)
|
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 {
|
if let Some(Node::Ident(value)) = self.lexer.next() {
|
||||||
match self.lexer.next() {
|
let _assign = self.lexer.next();
|
||||||
Some(Node::Int(value))
|
|
||||||
=> Ok(Some(Node::Int(value))),
|
|
||||||
|
|
||||||
Some(Node::Float(value))
|
loop {
|
||||||
=> Ok(Some(Node::Float(value))),
|
match self.lexer.peek(0) {
|
||||||
|
Some(Node::Semicolon) | None => {
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
|
||||||
Some(Node::Bool(value))
|
Some(v) => { self.lexer.next(); rhs.push(v); }
|
||||||
=> Ok(Some(Node::Bool(value))),
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Some(Node::String(value))
|
Ok((Box::new(Node::Ident(value)), rhs))
|
||||||
=> Ok(Some(Node::String(value))),
|
|
||||||
|
|
||||||
Some(other) => {
|
} else {
|
||||||
Err(error::AstError {
|
Err(error::AstError {
|
||||||
msg: String::from(format!("unexpected {:?}", other)),
|
msg: String::from("cannot assign value to node"),
|
||||||
line: self.lexer.line()
|
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(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,14 +32,157 @@ 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; ")?;
|
||||||
|
@ -103,4 +266,93 @@ mod test {
|
||||||
|
|
||||||
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in New Issue