Compare commits
No commits in common. "d2d2ea1c575b3c746548b0d650ba2bc5d18093c5" and "823bb3130591716771190e3123c5772250e00dc7" have entirely different histories.
d2d2ea1c57
...
823bb31305
105
src/interface.rs
105
src/interface.rs
@ -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 as TSTree, TreeCursor};
|
use tree_sitter::{Node, Parser, Tree, TreeCursor};
|
||||||
|
|
||||||
use crate::{language, Error, Result, Value, VariableMap};
|
use crate::{language, Error, Result, Value, VariableMap};
|
||||||
|
|
||||||
@ -55,40 +55,20 @@ 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
|
||||||
/// abstract trees called [Item][]s that can be run to execute the source code.
|
/// trees that can be run to execute the source code. Each of these trees is an
|
||||||
|
/// [Item][] in the evaluator.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Evaluator {
|
struct Evaluator {
|
||||||
items: Vec<Item>,
|
items: Vec<Item>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Evaluator {
|
impl Evaluator {
|
||||||
fn new(tree_sitter_tree: TSTree, source: &str) -> Result<Self> {
|
fn new(tree: Tree, source: &str) -> Result<Self> {
|
||||||
let root_node = tree_sitter_tree.root_node();
|
let root_node = tree.root_node();
|
||||||
let mut cursor = tree_sitter_tree.walk();
|
let mut cursor = 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() {
|
||||||
@ -96,7 +76,6 @@ 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;
|
||||||
}
|
}
|
||||||
@ -132,7 +111,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)]
|
||||||
pub enum Item {
|
enum Item {
|
||||||
Comment(String),
|
Comment(String),
|
||||||
Statement(Statement),
|
Statement(Statement),
|
||||||
}
|
}
|
||||||
@ -165,18 +144,13 @@ 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)]
|
||||||
pub enum Statement {
|
enum Statement {
|
||||||
Open(Expression),
|
Open(Expression),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Statement {
|
impl Statement {
|
||||||
pub fn new(node: Node, source: &str) -> Result<Self> {
|
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 {
|
||||||
@ -206,7 +180,7 @@ impl Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Expression {
|
enum Expression {
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
Value(Value),
|
Value(Value),
|
||||||
Operation(Box<Operation>),
|
Operation(Box<Operation>),
|
||||||
@ -214,7 +188,7 @@ pub enum Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Expression {
|
impl Expression {
|
||||||
pub fn new(node: Node, source: &str) -> Result<Self> {
|
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 {
|
||||||
@ -244,7 +218,7 @@ impl Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &mut VariableMap,
|
context: &mut VariableMap,
|
||||||
mut cursor: &mut TreeCursor,
|
mut cursor: &mut TreeCursor,
|
||||||
@ -268,7 +242,7 @@ impl Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Operation {
|
struct Operation {
|
||||||
left: Expression,
|
left: Expression,
|
||||||
operator: String,
|
operator: String,
|
||||||
right: Expression,
|
right: Expression,
|
||||||
@ -316,12 +290,8 @@ 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)]
|
||||||
pub struct ControlFlow {
|
struct ControlFlow {
|
||||||
if_expression: Expression,
|
if_expression: Expression,
|
||||||
then_statement: Statement,
|
then_statement: Statement,
|
||||||
else_statement: Option<Statement>,
|
else_statement: Option<Statement>,
|
||||||
@ -329,7 +299,6 @@ pub 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);
|
||||||
@ -376,8 +345,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("x = 9"), vec![Ok(Value::Empty)]);
|
assert_eq!(eval("1;"), vec![Ok(Value::Empty)]);
|
||||||
assert_eq!(eval("y = 'foobar'"), vec![Ok(Value::Empty)]);
|
assert_eq!(eval("'foobar';"), vec![Ok(Value::Empty)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -407,7 +376,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),
|
||||||
@ -418,13 +387,15 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn evaluate_function() {
|
fn evaluate_function() {
|
||||||
let function_str = "function <message, number> {
|
let function_str = "function <message, answer> {
|
||||||
output message
|
output message;
|
||||||
output number
|
output 'The answer is ' + answer;
|
||||||
}";
|
}";
|
||||||
|
|
||||||
todo!();
|
assert_eq!(
|
||||||
// assert_eq!("", vec![Ok(Value::Function(Function::new(function_str)))]);
|
eval("{ x = 1, foo = 'bar' }"),
|
||||||
|
vec![Ok(Value::Function(Function::new(function_str)))]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -435,7 +406,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("map { x = 1 foo = 'bar' }"), vec![Ok(Value::Map(map))]);
|
assert_eq!(eval("{ x = 1, foo = 'bar' }"), vec![Ok(Value::Map(map))]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -446,7 +417,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)])
|
||||||
@ -454,13 +425,21 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
eval(
|
eval(
|
||||||
"
|
"table <messages, numbers>
|
||||||
table <messages, numbers> {
|
row ('hiya', 42)
|
||||||
['hiya', 42]
|
row ('foo', -57)
|
||||||
['foo', 57]
|
row ('bar', 99.99)"
|
||||||
['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))]
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Types that represent runtime values.
|
//! Types that represent runtime values.
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
Expression, Function, Table, Time, ValueType, VariableMap,
|
Function, Table, Time, ValueType, VariableMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
use json::JsonValue;
|
use json::JsonValue;
|
||||||
@ -64,8 +64,9 @@ impl Value {
|
|||||||
Ok(Value::Integer(raw))
|
Ok(Value::Integer(raw))
|
||||||
}
|
}
|
||||||
"string" => {
|
"string" => {
|
||||||
let quote_str = &value_snippet.chars().nth(0).unwrap();
|
let without_quotes = &value_snippet[1..value_snippet.len() - 1];
|
||||||
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()))
|
||||||
}
|
}
|
||||||
@ -98,45 +99,9 @@ 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, float, list or empty",
|
expected: "integer, string, boolean or empty",
|
||||||
actual: node.kind(),
|
actual: node.kind(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user