Compare commits

...

2 Commits

Author SHA1 Message Date
d2d2ea1c57 Clean up and fix tests 2023-09-29 17:57:55 -04:00
dd939db924 Implement list 2023-09-29 16:33:46 -04:00
2 changed files with 103 additions and 47 deletions

View File

@ -1,6 +1,6 @@
//! The top level of Dust's API with functions to interpret Dust code. //! The top level of Dust's API with functions to interpret Dust code.
use tree_sitter::{Node, Parser, Tree, TreeCursor}; use tree_sitter::{Node, Parser, Tree as TSTree, TreeCursor};
use crate::{language, Error, Result, Value, VariableMap}; use crate::{language, Error, Result, Value, VariableMap};
@ -55,20 +55,40 @@ pub fn eval_with_context(source: &str, context: &mut VariableMap) -> Vec<Result<
results results
} }
pub trait EvaluatorTree: Sized {
/// Interpret the syntax tree at the given node and return the abstraction.
///
/// This function is used to convert nodes in the Tree Sitter concrete
/// syntax tree into executable nodes in an abstract tree. This function is
/// where the tree should be traversed by accessing sibling and child nodes.
/// Each node in the CST should be traversed only once.
///
/// If necessary, the source code can be accessed directly by getting the
/// node's byte range.
fn new(node: Node, source: &str) -> Result<Self>;
/// Execute dust code by traversing the tree
fn run(
&self,
context: &mut VariableMap,
cursor: &mut TreeCursor,
source: &str,
) -> Vec<Result<Value>>;
}
/// A collection of statements and comments interpreted from a syntax tree. /// A collection of statements and comments interpreted from a syntax tree.
/// ///
/// The Evaluator turns a tree sitter concrete syntax tree into a vector of /// The Evaluator turns a tree sitter concrete syntax tree into a vector of
/// trees that can be run to execute the source code. Each of these trees is an /// abstract trees called [Item][]s that can be run to execute the source code.
/// [Item][] in the evaluator.
#[derive(Debug)] #[derive(Debug)]
struct Evaluator { pub struct Evaluator {
items: Vec<Item>, items: Vec<Item>,
} }
impl Evaluator { impl Evaluator {
fn new(tree: Tree, source: &str) -> Result<Self> { fn new(tree_sitter_tree: TSTree, source: &str) -> Result<Self> {
let root_node = tree.root_node(); let root_node = tree_sitter_tree.root_node();
let mut cursor = tree.walk(); let mut cursor = tree_sitter_tree.walk();
let mut items = Vec::new(); let mut items = Vec::new();
for (index, node) in root_node.children(&mut cursor).enumerate() { for (index, node) in root_node.children(&mut cursor).enumerate() {
@ -76,6 +96,7 @@ impl Evaluator {
items.push(item); items.push(item);
// This iterator will run forever without this check.
if index == root_node.child_count() - 1 { if index == root_node.child_count() - 1 {
break; break;
} }
@ -111,7 +132,7 @@ impl Evaluator {
/// to produce a single value or interact with a context by creating or /// to produce a single value or interact with a context by creating or
/// referencing variables. /// referencing variables.
#[derive(Debug)] #[derive(Debug)]
enum Item { pub enum Item {
Comment(String), Comment(String),
Statement(Statement), Statement(Statement),
} }
@ -144,13 +165,18 @@ impl Item {
} }
} }
/// Representation of a statement in the .
///
/// Items are either comments, which do nothing, or statements, which can be run
/// to produce a single value or interact with a context by creating or
/// referencing variables.
#[derive(Debug)] #[derive(Debug)]
enum Statement { pub enum Statement {
Open(Expression), Open(Expression),
} }
impl Statement { impl Statement {
fn new(node: Node, source: &str) -> Result<Self> { pub fn new(node: Node, source: &str) -> Result<Self> {
let node = if node.kind() == "statement" { let node = if node.kind() == "statement" {
node.child(0).unwrap() node.child(0).unwrap()
} else { } else {
@ -180,7 +206,7 @@ impl Statement {
} }
#[derive(Debug)] #[derive(Debug)]
enum Expression { pub enum Expression {
Identifier(String), Identifier(String),
Value(Value), Value(Value),
Operation(Box<Operation>), Operation(Box<Operation>),
@ -188,7 +214,7 @@ enum Expression {
} }
impl Expression { impl Expression {
fn new(node: Node, source: &str) -> Result<Self> { pub fn new(node: Node, source: &str) -> Result<Self> {
let node = if node.kind() == "expression" { let node = if node.kind() == "expression" {
node.child(0).unwrap() node.child(0).unwrap()
} else { } else {
@ -218,7 +244,7 @@ impl Expression {
} }
} }
fn run( pub fn run(
&self, &self,
context: &mut VariableMap, context: &mut VariableMap,
mut cursor: &mut TreeCursor, mut cursor: &mut TreeCursor,
@ -242,7 +268,7 @@ impl Expression {
} }
#[derive(Debug)] #[derive(Debug)]
struct Operation { pub struct Operation {
left: Expression, left: Expression,
operator: String, operator: String,
right: Expression, right: Expression,
@ -290,8 +316,12 @@ impl Operation {
} }
} }
/// Respresentation of an if-then-else logic gate.
///
/// A ControlFlow instance represents work to be done when the "run" method is
/// called.
#[derive(Debug)] #[derive(Debug)]
struct ControlFlow { pub struct ControlFlow {
if_expression: Expression, if_expression: Expression,
then_statement: Statement, then_statement: Statement,
else_statement: Option<Statement>, else_statement: Option<Statement>,
@ -299,6 +329,7 @@ struct ControlFlow {
impl ControlFlow { impl ControlFlow {
fn new(node: Node, source: &str) -> Result<Self> { fn new(node: Node, source: &str) -> Result<Self> {
// Skip the child nodes for the keywords "if", "then" and "else".
let second_child = node.child(1).unwrap(); let second_child = node.child(1).unwrap();
let fourth_child = node.child(3).unwrap(); let fourth_child = node.child(3).unwrap();
let sixth_child = node.child(5); let sixth_child = node.child(5);
@ -345,8 +376,8 @@ mod tests {
#[test] #[test]
fn evaluate_empty() { fn evaluate_empty() {
assert_eq!(eval("()"), vec![Ok(Value::Empty)]); assert_eq!(eval("()"), vec![Ok(Value::Empty)]);
assert_eq!(eval("1;"), vec![Ok(Value::Empty)]); assert_eq!(eval("x = 9"), vec![Ok(Value::Empty)]);
assert_eq!(eval("'foobar';"), vec![Ok(Value::Empty)]); assert_eq!(eval("y = 'foobar'"), vec![Ok(Value::Empty)]);
} }
#[test] #[test]
@ -376,7 +407,7 @@ mod tests {
#[test] #[test]
fn evaluate_list() { fn evaluate_list() {
assert_eq!( assert_eq!(
eval("(1, 2, 'foobar')"), eval("[1, 2, 'foobar']"),
vec![Ok(Value::List(vec![ vec![Ok(Value::List(vec![
Value::Integer(1), Value::Integer(1),
Value::Integer(2), Value::Integer(2),
@ -387,15 +418,13 @@ mod tests {
#[test] #[test]
fn evaluate_function() { fn evaluate_function() {
let function_str = "function <message, answer> { let function_str = "function <message, number> {
output message; output message
output 'The answer is ' + answer; output number
}"; }";
assert_eq!( todo!();
eval("{ x = 1, foo = 'bar' }"), // assert_eq!("", vec![Ok(Value::Function(Function::new(function_str)))]);
vec![Ok(Value::Function(Function::new(function_str)))]
);
} }
#[test] #[test]
@ -406,7 +435,7 @@ mod tests {
map.set_value("foo", Value::String("bar".to_string())) map.set_value("foo", Value::String("bar".to_string()))
.unwrap(); .unwrap();
assert_eq!(eval("{ x = 1, foo = 'bar' }"), vec![Ok(Value::Map(map))]); assert_eq!(eval("map { x = 1 foo = 'bar' }"), vec![Ok(Value::Map(map))]);
} }
#[test] #[test]
@ -417,7 +446,7 @@ mod tests {
.insert(vec![Value::String("hiya".to_string()), Value::Integer(42)]) .insert(vec![Value::String("hiya".to_string()), Value::Integer(42)])
.unwrap(); .unwrap();
table table
.insert(vec![Value::String("foo".to_string()), Value::Integer(-57)]) .insert(vec![Value::String("foo".to_string()), Value::Integer(57)])
.unwrap(); .unwrap();
table table
.insert(vec![Value::String("bar".to_string()), Value::Float(99.99)]) .insert(vec![Value::String("bar".to_string()), Value::Float(99.99)])
@ -425,21 +454,13 @@ mod tests {
assert_eq!( assert_eq!(
eval( eval(
"table <messages, numbers> "
row ('hiya', 42) table <messages, numbers> {
row ('foo', -57) ['hiya', 42]
row ('bar', 99.99)" ['foo', 57]
), ['bar', 99.99]
vec![Ok(Value::Table(table.clone()))] }
); "
assert_eq!(
eval(
"my_table = table <messages, numbers>
row ('hiya', 42);
my_table += ('foo', -57);
my_table += ('bar', 99.99);
my_table"
), ),
vec![Ok(Value::Table(table))] vec![Ok(Value::Table(table))]
); );

View File

@ -1,7 +1,7 @@
//! Types that represent runtime values. //! Types that represent runtime values.
use crate::{ use crate::{
error::{Error, Result}, error::{Error, Result},
Function, Table, Time, ValueType, VariableMap, Expression, Function, Table, Time, ValueType, VariableMap,
}; };
use json::JsonValue; use json::JsonValue;
@ -64,9 +64,8 @@ impl Value {
Ok(Value::Integer(raw)) Ok(Value::Integer(raw))
} }
"string" => { "string" => {
let without_quotes = &value_snippet[1..value_snippet.len() - 1]; let quote_str = &value_snippet.chars().nth(0).unwrap();
let without_quotes = value_snippet.trim_matches(*quote_str);
println!("{without_quotes}");
Ok(Value::String(without_quotes.to_string())) Ok(Value::String(without_quotes.to_string()))
} }
@ -99,9 +98,45 @@ impl Value {
Ok(Value::List(values)) Ok(Value::List(values))
} }
"table" => {
let child_count = node.child_count();
let mut column_names = Vec::new();
let mut rows = Vec::new();
// Skip the first and last nodes because they are pointy braces.
for index in 1..child_count - 1 {
let child = node.child(index).unwrap();
if child.kind() == "identifier" {
let child_expression = Expression::new(child, source)?;
if let Expression::Identifier(column_name) = child_expression {
column_names.push(column_name)
}
}
if child.kind() == "list" {
let child_value = Value::new(child, source)?;
if let Value::List(row) = child_value {
rows.push(row);
}
}
}
let mut table = Table::new(column_names);
table.reserve(rows.len());
for row in rows {
table.insert(row)?;
}
Ok(Value::Table(table))
}
"map" => todo!(),
"empty" => Ok(Value::Empty), "empty" => Ok(Value::Empty),
_ => Err(Error::UnexpectedSourceNode { _ => Err(Error::UnexpectedSourceNode {
expected: "integer, string, boolean or empty", expected: "integer, string, boolean, float, list or empty",
actual: node.kind(), actual: node.kind(),
}), }),
} }