Compare commits
3 Commits
a0b8cda00d
...
3811b2ddb2
Author | SHA1 | Date |
---|---|---|
bog | 3811b2ddb2 | |
bog | 4e7e7ad07b | |
bog | 35e8cc813e |
|
@ -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
|
||||
|
|
158
src/ast/lexer.rs
158
src/ast/lexer.rs
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod value;
|
||||
pub mod evaluator;
|
||||
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(Debug)]
|
||||
#[derive(Clone)]
|
||||
pub enum Value {
|
||||
Bool(bool),
|
||||
Int(i32),
|
||||
Float(f64),
|
||||
String(String)
|
||||
String(String),
|
||||
Array(Vec<Value>)
|
||||
}
|
||||
|
||||
|
|
Reference in New Issue