Merge branch 'main' into gui
This commit is contained in:
commit
318825d1b1
@ -4,8 +4,8 @@ new_data = []
|
|||||||
|
|
||||||
for commit_data in data {
|
for commit_data in data {
|
||||||
new_data += {
|
new_data += {
|
||||||
message = commit_data.commit.message
|
message = commit_data:commit:message
|
||||||
name = commit_data.commit.committer.name
|
name = commit_data:commit:committer:name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,10 +58,6 @@ impl AbstractTree for Block {
|
|||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find_map_first(|(index, statement)| {
|
.find_map_first(|(index, statement)| {
|
||||||
if let Statement::Return(expression) = statement {
|
|
||||||
return Some(expression.run(source, context));
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = statement.run(source, context);
|
let result = statement.run(source, context);
|
||||||
|
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
@ -79,10 +75,6 @@ impl AbstractTree for Block {
|
|||||||
let mut prev_result = None;
|
let mut prev_result = None;
|
||||||
|
|
||||||
for statement in &self.statements {
|
for statement in &self.statements {
|
||||||
if let Statement::Return(expression) = statement {
|
|
||||||
return expression.run(source, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
prev_result = Some(statement.run(source, context));
|
prev_result = Some(statement.run(source, context));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,10 +83,6 @@ impl AbstractTree for Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||||
if self.is_async {
|
self.statements.last().unwrap().expected_type(context)
|
||||||
Ok(Type::Any)
|
|
||||||
} else {
|
|
||||||
self.statements.last().unwrap().expected_type(context)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,12 @@ impl AbstractTree for Identifier {
|
|||||||
|
|
||||||
let text = &source[node.byte_range()];
|
let text = &source[node.byte_range()];
|
||||||
|
|
||||||
|
if text.is_empty() {
|
||||||
|
println!("{node:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(!text.is_empty());
|
||||||
|
|
||||||
Ok(Identifier(text.to_string()))
|
Ok(Identifier(text.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ pub mod r#match;
|
|||||||
pub mod math;
|
pub mod math;
|
||||||
pub mod statement;
|
pub mod statement;
|
||||||
pub mod type_definition;
|
pub mod type_definition;
|
||||||
pub mod r#use;
|
|
||||||
pub mod value_node;
|
pub mod value_node;
|
||||||
pub mod r#while;
|
pub mod r#while;
|
||||||
pub mod r#yield;
|
pub mod r#yield;
|
||||||
@ -29,8 +28,7 @@ pub mod r#yield;
|
|||||||
pub use {
|
pub use {
|
||||||
assignment::*, block::*, expression::*, function_call::*, function_expression::*,
|
assignment::*, block::*, expression::*, function_call::*, function_expression::*,
|
||||||
identifier::*, if_else::*, index::*, index_assignment::IndexAssignment, logic::*, math::*,
|
identifier::*, if_else::*, index::*, index_assignment::IndexAssignment, logic::*, math::*,
|
||||||
r#for::*, r#match::*, r#use::*, r#while::*, r#yield::*, statement::*, type_definition::*,
|
r#for::*, r#match::*, r#while::*, r#yield::*, statement::*, type_definition::*, value_node::*,
|
||||||
value_node::*,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
@ -3,21 +3,19 @@ use tree_sitter::Node;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AbstractTree, Assignment, Block, Error, Expression, For, IfElse, IndexAssignment, Map, Match,
|
AbstractTree, Assignment, Block, Error, Expression, For, IfElse, IndexAssignment, Map, Match,
|
||||||
Result, Type, Use, Value, While,
|
Result, Type, Value, While,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Abstract representation of a statement.
|
/// Abstract representation of a statement.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Assignment(Box<Assignment>),
|
Assignment(Box<Assignment>),
|
||||||
Return(Expression),
|
|
||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
IfElse(Box<IfElse>),
|
IfElse(Box<IfElse>),
|
||||||
Match(Match),
|
Match(Match),
|
||||||
While(Box<While>),
|
While(Box<While>),
|
||||||
Block(Box<Block>),
|
Block(Box<Block>),
|
||||||
For(Box<For>),
|
For(Box<For>),
|
||||||
Use(Use),
|
|
||||||
IndexAssignment(Box<IndexAssignment>),
|
IndexAssignment(Box<IndexAssignment>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,15 +29,6 @@ impl AbstractTree for Statement {
|
|||||||
"assignment" => Ok(Statement::Assignment(Box::new(
|
"assignment" => Ok(Statement::Assignment(Box::new(
|
||||||
Assignment::from_syntax_node(source, child, context)?,
|
Assignment::from_syntax_node(source, child, context)?,
|
||||||
))),
|
))),
|
||||||
"return" => {
|
|
||||||
let expression_node = child.child(1).unwrap();
|
|
||||||
|
|
||||||
Ok(Statement::Return(Expression::from_syntax_node(
|
|
||||||
source,
|
|
||||||
expression_node,
|
|
||||||
context,
|
|
||||||
)?))
|
|
||||||
}
|
|
||||||
"expression" => Ok(Statement::Expression(Expression::from_syntax_node(
|
"expression" => Ok(Statement::Expression(Expression::from_syntax_node(
|
||||||
source, child, context,
|
source, child, context,
|
||||||
)?)),
|
)?)),
|
||||||
@ -55,9 +44,6 @@ impl AbstractTree for Statement {
|
|||||||
"for" => Ok(Statement::For(Box::new(For::from_syntax_node(
|
"for" => Ok(Statement::For(Box::new(For::from_syntax_node(
|
||||||
source, child, context,
|
source, child, context,
|
||||||
)?))),
|
)?))),
|
||||||
"use" => Ok(Statement::Use(Use::from_syntax_node(
|
|
||||||
source, child, context,
|
|
||||||
)?)),
|
|
||||||
"index_assignment" => Ok(Statement::IndexAssignment(Box::new(
|
"index_assignment" => Ok(Statement::IndexAssignment(Box::new(
|
||||||
IndexAssignment::from_syntax_node(source, child, context)?,
|
IndexAssignment::from_syntax_node(source, child, context)?,
|
||||||
))),
|
))),
|
||||||
@ -77,14 +63,12 @@ impl AbstractTree for Statement {
|
|||||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||||
match self {
|
match self {
|
||||||
Statement::Assignment(assignment) => assignment.run(source, context),
|
Statement::Assignment(assignment) => assignment.run(source, context),
|
||||||
Statement::Return(expression) => expression.run(source, context),
|
|
||||||
Statement::Expression(expression) => expression.run(source, context),
|
Statement::Expression(expression) => expression.run(source, context),
|
||||||
Statement::IfElse(if_else) => if_else.run(source, context),
|
Statement::IfElse(if_else) => if_else.run(source, context),
|
||||||
Statement::Match(r#match) => r#match.run(source, context),
|
Statement::Match(r#match) => r#match.run(source, context),
|
||||||
Statement::While(r#while) => r#while.run(source, context),
|
Statement::While(r#while) => r#while.run(source, context),
|
||||||
Statement::Block(block) => block.run(source, context),
|
Statement::Block(block) => block.run(source, context),
|
||||||
Statement::For(r#for) => r#for.run(source, context),
|
Statement::For(r#for) => r#for.run(source, context),
|
||||||
Statement::Use(run) => run.run(source, context),
|
|
||||||
Statement::IndexAssignment(index_assignment) => index_assignment.run(source, context),
|
Statement::IndexAssignment(index_assignment) => index_assignment.run(source, context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,14 +76,12 @@ impl AbstractTree for Statement {
|
|||||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||||
match self {
|
match self {
|
||||||
Statement::Assignment(assignment) => assignment.expected_type(context),
|
Statement::Assignment(assignment) => assignment.expected_type(context),
|
||||||
Statement::Return(expression) => expression.expected_type(context),
|
|
||||||
Statement::Expression(expression) => expression.expected_type(context),
|
Statement::Expression(expression) => expression.expected_type(context),
|
||||||
Statement::IfElse(if_else) => if_else.expected_type(context),
|
Statement::IfElse(if_else) => if_else.expected_type(context),
|
||||||
Statement::Match(r#match) => r#match.expected_type(context),
|
Statement::Match(r#match) => r#match.expected_type(context),
|
||||||
Statement::While(r#while) => r#while.expected_type(context),
|
Statement::While(r#while) => r#while.expected_type(context),
|
||||||
Statement::Block(block) => block.expected_type(context),
|
Statement::Block(block) => block.expected_type(context),
|
||||||
Statement::For(r#for) => r#for.expected_type(context),
|
Statement::For(r#for) => r#for.expected_type(context),
|
||||||
Statement::Use(r#use) => r#use.expected_type(context),
|
|
||||||
Statement::IndexAssignment(index_assignment) => index_assignment.expected_type(context),
|
Statement::IndexAssignment(index_assignment) => index_assignment.expected_type(context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
use std::fs::read_to_string;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use tree_sitter::Node;
|
|
||||||
|
|
||||||
use crate::{interpret_with_context, AbstractTree, Error, Map, Result, Type, Value};
|
|
||||||
|
|
||||||
/// Abstract representation of a use statement.
|
|
||||||
///
|
|
||||||
/// Use will evaluate the Dust file at the given path. It will create an empty
|
|
||||||
/// context to do so, then apply every value from that context to the current
|
|
||||||
/// context.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
|
||||||
pub struct Use {
|
|
||||||
path: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractTree for Use {
|
|
||||||
fn from_syntax_node(source: &str, node: Node, _context: &Map) -> Result<Self> {
|
|
||||||
Error::expect_syntax_node(source, "use", node)?;
|
|
||||||
|
|
||||||
let string_node = node.child(1).unwrap();
|
|
||||||
let path = source[string_node.start_byte() + 1..string_node.end_byte() - 1].to_string();
|
|
||||||
|
|
||||||
Ok(Use { path })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(&self, _source: &str, context: &Map) -> Result<Value> {
|
|
||||||
let file_contents = read_to_string(&self.path)?;
|
|
||||||
let file_context = Map::new();
|
|
||||||
|
|
||||||
interpret_with_context(&file_contents, file_context.clone())?;
|
|
||||||
|
|
||||||
for (key, (value, r#type)) in file_context.variables()?.iter() {
|
|
||||||
context.set(key.clone(), value.clone(), Some(r#type.clone()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Value::Map(file_context))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _context: &Map) -> Result<Type> {
|
|
||||||
Ok(Type::Map)
|
|
||||||
}
|
|
||||||
}
|
|
@ -84,14 +84,8 @@ impl BuiltInFunction for Append {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||||
let file_content = arguments
|
let file_content = arguments.get(0).unwrap_or_default().as_string()?;
|
||||||
.first()
|
let path = arguments.get(1).unwrap_or_default().as_string()?;
|
||||||
.unwrap_or(&Value::none())
|
|
||||||
.as_string()?;
|
|
||||||
let path = arguments
|
|
||||||
.get(1)
|
|
||||||
.unwrap_or(&Value::none())
|
|
||||||
.as_string()?;
|
|
||||||
|
|
||||||
File::options()
|
File::options()
|
||||||
.append(true)
|
.append(true)
|
||||||
|
26
src/error.rs
26
src/error.rs
@ -401,10 +401,14 @@ impl fmt::Display for Error {
|
|||||||
actual,
|
actual,
|
||||||
location,
|
location,
|
||||||
relevant_source,
|
relevant_source,
|
||||||
} => write!(
|
} => {
|
||||||
f,
|
let location = get_position(location);
|
||||||
"Expected {expected}, but got {actual} at {location}. Code: {relevant_source} ",
|
|
||||||
),
|
write!(
|
||||||
|
f,
|
||||||
|
"Expected {expected}, but got {actual} at {location}. Code: {relevant_source} ",
|
||||||
|
)
|
||||||
|
}
|
||||||
WrongColumnAmount { expected, actual } => write!(
|
WrongColumnAmount { expected, actual } => write!(
|
||||||
f,
|
f,
|
||||||
"Wrong column amount. Expected {expected} but got {actual}."
|
"Wrong column amount. Expected {expected} but got {actual}."
|
||||||
@ -412,7 +416,9 @@ impl fmt::Display for Error {
|
|||||||
External(message) => write!(f, "External error: {message}"),
|
External(message) => write!(f, "External error: {message}"),
|
||||||
CustomMessage(message) => write!(f, "{message}"),
|
CustomMessage(message) => write!(f, "{message}"),
|
||||||
Syntax { source, location } => {
|
Syntax { source, location } => {
|
||||||
write!(f, "Syntax error at {location}, this is not valid: {source}")
|
let location = get_position(location);
|
||||||
|
|
||||||
|
write!(f, "Syntax error at {location}: {source}")
|
||||||
}
|
}
|
||||||
TypeCheck { expected, actual } => write!(
|
TypeCheck { expected, actual } => write!(
|
||||||
f,
|
f,
|
||||||
@ -422,7 +428,11 @@ impl fmt::Display for Error {
|
|||||||
error,
|
error,
|
||||||
location,
|
location,
|
||||||
source,
|
source,
|
||||||
} => write!(f, "{error} Occured at {location}: \"{source}\""),
|
} => {
|
||||||
|
let location = get_position(location);
|
||||||
|
|
||||||
|
write!(f, "{error} Occured at {location}: \"{source}\"")
|
||||||
|
}
|
||||||
SerdeJson(message) => write!(f, "JSON processing error: {message}"),
|
SerdeJson(message) => write!(f, "JSON processing error: {message}"),
|
||||||
ParserCancelled => write!(
|
ParserCancelled => write!(
|
||||||
f,
|
f,
|
||||||
@ -431,3 +441,7 @@ impl fmt::Display for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_position(position: &Point) -> String {
|
||||||
|
format!("column {}, row {}", position.row + 1, position.column)
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! You can use this library externally by calling either of the "eval"
|
//! You can use this library externally by calling either of the "eval"
|
||||||
//! functions or by constructing your own Evaluator.
|
//! functions or by constructing your own Evaluator.
|
||||||
use tree_sitter::{Parser, Tree as TSTree};
|
use tree_sitter::{Node, Parser, Tree as TSTree, TreeCursor};
|
||||||
|
|
||||||
use crate::{language, AbstractTree, Error, Map, Result, Root, Value};
|
use crate::{language, AbstractTree, Error, Map, Result, Root, Value};
|
||||||
|
|
||||||
@ -71,12 +71,39 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_only(&mut self, source: &str) {
|
pub fn parse(&mut self, source: &str) -> Result<()> {
|
||||||
self.syntax_tree = self.parser.parse(source, None);
|
fn check_for_error(source: &str, node: Node, cursor: &mut TreeCursor) -> Result<()> {
|
||||||
|
if node.is_error() {
|
||||||
|
Err(Error::Syntax {
|
||||||
|
source: source[node.byte_range()].to_string(),
|
||||||
|
location: node.start_position(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
for child in node.children(&mut cursor.clone()) {
|
||||||
|
check_for_error(source, child, cursor)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let syntax_tree = self.parser.parse(source, None);
|
||||||
|
|
||||||
|
if let Some(tree) = &syntax_tree {
|
||||||
|
let root = tree.root_node();
|
||||||
|
let mut cursor = root.walk();
|
||||||
|
|
||||||
|
check_for_error(source, root, &mut cursor)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.syntax_tree = syntax_tree;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, source: &str) -> Result<Value> {
|
pub fn run(&mut self, source: &str) -> Result<Value> {
|
||||||
self.syntax_tree = self.parser.parse(source, None);
|
self.parse(source)?;
|
||||||
|
|
||||||
self.abstract_tree = if let Some(syntax_tree) = &self.syntax_tree {
|
self.abstract_tree = if let Some(syntax_tree) = &self.syntax_tree {
|
||||||
Some(Root::from_syntax_node(
|
Some(Root::from_syntax_node(
|
||||||
source,
|
source,
|
||||||
|
@ -75,7 +75,7 @@ fn main() {
|
|||||||
let mut interpreter = Interpreter::new(context);
|
let mut interpreter = Interpreter::new(context);
|
||||||
|
|
||||||
if args.show_syntax_tree {
|
if args.show_syntax_tree {
|
||||||
interpreter.parse_only(&source);
|
interpreter.parse(&source).unwrap();
|
||||||
|
|
||||||
println!("{}", interpreter.syntax_tree().unwrap());
|
println!("{}", interpreter.syntax_tree().unwrap());
|
||||||
}
|
}
|
||||||
|
@ -76,19 +76,6 @@ mod for_loop {
|
|||||||
result
|
result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn modify_value_async() {
|
|
||||||
let result = interpret(
|
|
||||||
"
|
|
||||||
fn = (x <int>) <none> {}
|
|
||||||
|
|
||||||
fn(1)
|
|
||||||
",
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(Ok(Value::none()), result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod logic {
|
mod logic {
|
||||||
@ -107,27 +94,27 @@ mod value {
|
|||||||
use dust_lang::*;
|
use dust_lang::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_empty() {
|
fn empty() {
|
||||||
assert_eq!(interpret("x = 9"), Ok(Value::Option(None)));
|
assert_eq!(interpret("x = 9"), Ok(Value::Option(None)));
|
||||||
assert_eq!(interpret("x = 1 + 1"), Ok(Value::Option(None)));
|
assert_eq!(interpret("x = 1 + 1"), Ok(Value::Option(None)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_integer() {
|
fn integer() {
|
||||||
assert_eq!(interpret("1"), Ok(Value::Integer(1)));
|
assert_eq!(interpret("1"), Ok(Value::Integer(1)));
|
||||||
assert_eq!(interpret("123"), Ok(Value::Integer(123)));
|
assert_eq!(interpret("123"), Ok(Value::Integer(123)));
|
||||||
assert_eq!(interpret("-666"), Ok(Value::Integer(-666)));
|
assert_eq!(interpret("-666"), Ok(Value::Integer(-666)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_float() {
|
fn float() {
|
||||||
assert_eq!(interpret("0.1"), Ok(Value::Float(0.1)));
|
assert_eq!(interpret("0.1"), Ok(Value::Float(0.1)));
|
||||||
assert_eq!(interpret("12.3"), Ok(Value::Float(12.3)));
|
assert_eq!(interpret("12.3"), Ok(Value::Float(12.3)));
|
||||||
assert_eq!(interpret("-6.66"), Ok(Value::Float(-6.66)));
|
assert_eq!(interpret("-6.66"), Ok(Value::Float(-6.66)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_string() {
|
fn string() {
|
||||||
assert_eq!(interpret("\"one\""), Ok(Value::String("one".to_string())));
|
assert_eq!(interpret("\"one\""), Ok(Value::String("one".to_string())));
|
||||||
assert_eq!(interpret("'one'"), Ok(Value::String("one".to_string())));
|
assert_eq!(interpret("'one'"), Ok(Value::String("one".to_string())));
|
||||||
assert_eq!(interpret("`one`"), Ok(Value::String("one".to_string())));
|
assert_eq!(interpret("`one`"), Ok(Value::String("one".to_string())));
|
||||||
@ -140,7 +127,7 @@ mod value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_list() {
|
fn list() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpret("[1, 2, 'foobar']"),
|
interpret("[1, 2, 'foobar']"),
|
||||||
Ok(Value::List(List::with_items(vec![
|
Ok(Value::List(List::with_items(vec![
|
||||||
@ -152,7 +139,7 @@ mod value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_map() {
|
fn map() {
|
||||||
let map = Map::new();
|
let map = Map::new();
|
||||||
|
|
||||||
map.set("x".to_string(), Value::Integer(1), None).unwrap();
|
map.set("x".to_string(), Value::Integer(1), None).unwrap();
|
||||||
@ -163,7 +150,7 @@ mod value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_map_types() {
|
fn map_types() {
|
||||||
let map = Map::new();
|
let map = Map::new();
|
||||||
|
|
||||||
map.set("x".to_string(), Value::Integer(1), Some(Type::Integer))
|
map.set("x".to_string(), Value::Integer(1), Some(Type::Integer))
|
||||||
@ -182,7 +169,7 @@ mod value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_map_type_errors() {
|
fn map_type_errors() {
|
||||||
assert!(interpret("{ foo <bool> = 'bar' }")
|
assert!(interpret("{ foo <bool> = 'bar' }")
|
||||||
.unwrap_err()
|
.unwrap_err()
|
||||||
.is_type_check_error(&Error::TypeCheck {
|
.is_type_check_error(&Error::TypeCheck {
|
||||||
@ -192,15 +179,15 @@ mod value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_function() {
|
fn function() {
|
||||||
let result = interpret("() -> <int> { 1 }");
|
let result = interpret("() <int> { 1 }");
|
||||||
let value = result.unwrap();
|
let value = result.unwrap();
|
||||||
let function = value.as_function().unwrap();
|
let function = value.as_function().unwrap();
|
||||||
|
|
||||||
assert_eq!(&Vec::<Identifier>::with_capacity(0), function.parameters());
|
assert_eq!(&Vec::<Identifier>::with_capacity(0), function.parameters());
|
||||||
assert_eq!(&Type::Integer, function.return_type());
|
assert_eq!(&Type::Integer, function.return_type());
|
||||||
|
|
||||||
let result = interpret("(x <bool>) -> <bool> {true}");
|
let result = interpret("(x <bool>) <bool> { true }");
|
||||||
let value = result.unwrap();
|
let value = result.unwrap();
|
||||||
let function = value.as_function().unwrap();
|
let function = value.as_function().unwrap();
|
||||||
|
|
||||||
@ -212,7 +199,7 @@ mod value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_option() {
|
fn option() {
|
||||||
let result = interpret("x <option(int)> = some(1); x").unwrap();
|
let result = interpret("x <option(int)> = some(1); x").unwrap();
|
||||||
|
|
||||||
assert_eq!(Value::Option(Some(Box::new(Value::Integer(1)))), result);
|
assert_eq!(Value::Option(Some(Box::new(Value::Integer(1)))), result);
|
||||||
@ -223,11 +210,11 @@ mod function_call {
|
|||||||
use dust_lang::*;
|
use dust_lang::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_function_call() {
|
fn function_call() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpret(
|
interpret(
|
||||||
"
|
"
|
||||||
foobar = (message <str>) -> <str> { message }
|
foobar = (message <str>) <str> { message }
|
||||||
foobar('Hiya')
|
foobar('Hiya')
|
||||||
",
|
",
|
||||||
),
|
),
|
||||||
@ -236,7 +223,20 @@ mod function_call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_callback() {
|
fn call_empty_function() {
|
||||||
|
assert_eq!(
|
||||||
|
interpret(
|
||||||
|
"
|
||||||
|
foobar = (message <str>) <none> {}
|
||||||
|
foobar('Hiya')
|
||||||
|
",
|
||||||
|
),
|
||||||
|
Ok(Value::none())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn callback() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpret(
|
interpret(
|
||||||
"
|
"
|
||||||
@ -251,7 +251,7 @@ mod function_call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_built_in_function_call() {
|
fn built_in_function_call() {
|
||||||
assert_eq!(interpret("output('Hiya')"), Ok(Value::Option(None)));
|
assert_eq!(interpret("output('Hiya')"), Ok(Value::Option(None)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,7 +260,7 @@ mod if_else {
|
|||||||
use dust_lang::*;
|
use dust_lang::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_if() {
|
fn r#if() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpret("if true { 'true' }"),
|
interpret("if true { 'true' }"),
|
||||||
Ok(Value::String("true".to_string()))
|
Ok(Value::String("true".to_string()))
|
||||||
@ -268,7 +268,7 @@ mod if_else {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_if_else() {
|
fn if_else() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpret("if false { 1 } else { 2 }"),
|
interpret("if false { 1 } else { 2 }"),
|
||||||
Ok(Value::Integer(2))
|
Ok(Value::Integer(2))
|
||||||
@ -280,7 +280,7 @@ mod if_else {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_if_else_else_if_else() {
|
fn if_else_else_if_else() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpret(
|
interpret(
|
||||||
"
|
"
|
||||||
@ -298,7 +298,7 @@ mod if_else {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_if_else_if_else_if_else_if_else() {
|
fn if_else_if_else_if_else_if_else() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpret(
|
interpret(
|
||||||
"
|
"
|
||||||
@ -342,13 +342,13 @@ mod index {
|
|||||||
let test = interpret(
|
let test = interpret(
|
||||||
"
|
"
|
||||||
x = [1 2 3]
|
x = [1 2 3]
|
||||||
y = () -> <int> { 0 }
|
y = () <int> { 2 }
|
||||||
x:y()
|
x:y()
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(Value::Integer(1), test);
|
assert_eq!(Value::Integer(3), test);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +356,7 @@ mod r#match {
|
|||||||
use dust_lang::*;
|
use dust_lang::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_match() {
|
fn r#match() {
|
||||||
let test = interpret(
|
let test = interpret(
|
||||||
"
|
"
|
||||||
match 1 {
|
match 1 {
|
||||||
@ -372,7 +372,7 @@ mod r#match {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_match_assignment() {
|
fn match_assignment() {
|
||||||
let test = interpret(
|
let test = interpret(
|
||||||
"
|
"
|
||||||
x = match 1 {
|
x = match 1 {
|
||||||
@ -393,7 +393,7 @@ mod r#while {
|
|||||||
use dust_lang::*;
|
use dust_lang::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_while_loop() {
|
fn while_loop() {
|
||||||
assert_eq!(interpret("while false { 'foo' }"), Ok(Value::Option(None)))
|
assert_eq!(interpret("while false { 'foo' }"), Ok(Value::Option(None)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
Anonymous Function
|
Anonymous Function
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
() <str> { "Hiya" }
|
() -> <str> { "Hiya" }
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ Simple Identifiers
|
|||||||
x
|
x
|
||||||
_y
|
_y
|
||||||
__xyz__
|
__xyz__
|
||||||
|
x123x
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -15,6 +16,9 @@ __xyz__
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(identifier)))
|
(identifier)))
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(identifier)))
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(identifier))))
|
(identifier))))
|
||||||
|
@ -11,14 +11,6 @@ module.exports = grammar({
|
|||||||
|
|
||||||
_comment: $ => /[#][^#\n]*[#|\n]/,
|
_comment: $ => /[#][^#\n]*[#|\n]/,
|
||||||
|
|
||||||
block: $ =>
|
|
||||||
seq(
|
|
||||||
optional('async'),
|
|
||||||
'{',
|
|
||||||
repeat1($.statement),
|
|
||||||
'}',
|
|
||||||
),
|
|
||||||
|
|
||||||
statement: $ =>
|
statement: $ =>
|
||||||
prec.left(
|
prec.left(
|
||||||
seq(
|
seq(
|
||||||
@ -70,6 +62,14 @@ module.exports = grammar({
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
block: $ =>
|
||||||
|
seq(
|
||||||
|
optional('async'),
|
||||||
|
'{',
|
||||||
|
repeat($.statement),
|
||||||
|
'}',
|
||||||
|
),
|
||||||
|
|
||||||
identifier: $ =>
|
identifier: $ =>
|
||||||
choice(
|
choice(
|
||||||
$._identifier_pattern,
|
$._identifier_pattern,
|
||||||
@ -77,7 +77,7 @@ module.exports = grammar({
|
|||||||
),
|
),
|
||||||
|
|
||||||
_identifier_pattern: $ =>
|
_identifier_pattern: $ =>
|
||||||
/[_a-zA-Z]+[_a-zA-Z0-9]?/,
|
/[_a-zA-Z]+[_a-zA-Z0-9]*[_a-zA-Z]?/,
|
||||||
|
|
||||||
value: $ =>
|
value: $ =>
|
||||||
choice(
|
choice(
|
||||||
@ -175,7 +175,7 @@ module.exports = grammar({
|
|||||||
map: $ =>
|
map: $ =>
|
||||||
seq(
|
seq(
|
||||||
'{',
|
'{',
|
||||||
repeat(
|
repeat1(
|
||||||
seq(
|
seq(
|
||||||
$.identifier,
|
$.identifier,
|
||||||
optional($.type_definition),
|
optional($.type_definition),
|
||||||
|
@ -17,38 +17,6 @@
|
|||||||
"type": "PATTERN",
|
"type": "PATTERN",
|
||||||
"value": "[#][^#\\n]*[#|\\n]"
|
"value": "[#][^#\\n]*[#|\\n]"
|
||||||
},
|
},
|
||||||
"block": {
|
|
||||||
"type": "SEQ",
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "CHOICE",
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "STRING",
|
|
||||||
"value": "async"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "BLANK"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "STRING",
|
|
||||||
"value": "{"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "REPEAT1",
|
|
||||||
"content": {
|
|
||||||
"type": "SYMBOL",
|
|
||||||
"name": "statement"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "STRING",
|
|
||||||
"value": "}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"statement": {
|
"statement": {
|
||||||
"type": "PREC_LEFT",
|
"type": "PREC_LEFT",
|
||||||
"value": 0,
|
"value": 0,
|
||||||
@ -202,6 +170,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"block": {
|
||||||
|
"type": "SEQ",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "CHOICE",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "async"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "BLANK"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "{"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "REPEAT",
|
||||||
|
"content": {
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "statement"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"identifier": {
|
"identifier": {
|
||||||
"type": "CHOICE",
|
"type": "CHOICE",
|
||||||
"members": [
|
"members": [
|
||||||
@ -217,7 +217,7 @@
|
|||||||
},
|
},
|
||||||
"_identifier_pattern": {
|
"_identifier_pattern": {
|
||||||
"type": "PATTERN",
|
"type": "PATTERN",
|
||||||
"value": "[_a-zA-Z]+[_a-zA-Z0-9]?"
|
"value": "[_a-zA-Z]+[_a-zA-Z0-9]*[_a-zA-Z]?"
|
||||||
},
|
},
|
||||||
"value": {
|
"value": {
|
||||||
"type": "CHOICE",
|
"type": "CHOICE",
|
||||||
@ -518,7 +518,7 @@
|
|||||||
"value": "{"
|
"value": "{"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "REPEAT",
|
"type": "REPEAT1",
|
||||||
"content": {
|
"content": {
|
||||||
"type": "SEQ",
|
"type": "SEQ",
|
||||||
"members": [
|
"members": [
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
"fields": {},
|
"fields": {},
|
||||||
"children": {
|
"children": {
|
||||||
"multiple": true,
|
"multiple": true,
|
||||||
"required": true,
|
"required": false,
|
||||||
"types": [
|
"types": [
|
||||||
{
|
{
|
||||||
"type": "statement",
|
"type": "statement",
|
||||||
@ -365,7 +365,7 @@
|
|||||||
"fields": {},
|
"fields": {},
|
||||||
"children": {
|
"children": {
|
||||||
"multiple": true,
|
"multiple": true,
|
||||||
"required": false,
|
"required": true,
|
||||||
"types": [
|
"types": [
|
||||||
{
|
{
|
||||||
"type": "identifier",
|
"type": "identifier",
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user