Implement new built-in values
This commit is contained in:
parent
2f0ec91c08
commit
ae66e2a211
@ -1,13 +1,13 @@
|
||||
stuff = [
|
||||
random_integer()
|
||||
random_integer()
|
||||
random_integer()
|
||||
random_float()
|
||||
random_float()
|
||||
random_float()
|
||||
random_boolean()
|
||||
random_boolean()
|
||||
random_boolean()
|
||||
random:integer()
|
||||
random:integer()
|
||||
random:integer()
|
||||
random:float()
|
||||
random:float()
|
||||
random:float()
|
||||
random:boolean()
|
||||
random:boolean()
|
||||
random:boolean()
|
||||
]
|
||||
|
||||
random(stuff)
|
||||
random:from(stuff)
|
||||
|
@ -1,10 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node;
|
||||
|
||||
use crate::{
|
||||
AbstractTree, Error, Expression, FunctionExpression, Map, Result, Type, Value,
|
||||
BUILT_IN_FUNCTIONS,
|
||||
};
|
||||
use crate::{AbstractTree, Error, Expression, FunctionExpression, Map, Result, Type, Value};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct FunctionCall {
|
||||
@ -84,21 +81,6 @@ impl AbstractTree for FunctionCall {
|
||||
let value = match &self.function_expression {
|
||||
FunctionExpression::Identifier(identifier) => {
|
||||
let key = identifier.inner();
|
||||
|
||||
for built_in_function in BUILT_IN_FUNCTIONS {
|
||||
if key == built_in_function.name() {
|
||||
let mut arguments = Vec::with_capacity(self.arguments.len());
|
||||
|
||||
for expression in &self.arguments {
|
||||
let value = expression.run(source, context)?;
|
||||
|
||||
arguments.push(value);
|
||||
}
|
||||
|
||||
return built_in_function.run(&arguments, context);
|
||||
}
|
||||
}
|
||||
|
||||
let variables = context.variables()?;
|
||||
|
||||
if let Some((value, _)) = variables.get(key) {
|
||||
@ -131,18 +113,6 @@ impl AbstractTree for FunctionCall {
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
match &self.function_expression {
|
||||
FunctionExpression::Identifier(identifier) => {
|
||||
for built_in_function in BUILT_IN_FUNCTIONS {
|
||||
if identifier.inner() == built_in_function.name() {
|
||||
if let Type::Function {
|
||||
parameter_types: _,
|
||||
return_type,
|
||||
} = built_in_function.r#type()
|
||||
{
|
||||
return Ok(*return_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let identifier_type = identifier.expected_type(context)?;
|
||||
|
||||
if let Type::Function {
|
||||
@ -156,7 +126,15 @@ impl AbstractTree for FunctionCall {
|
||||
}
|
||||
}
|
||||
FunctionExpression::FunctionCall(function_call) => function_call.expected_type(context),
|
||||
FunctionExpression::Value(value_node) => value_node.expected_type(context),
|
||||
FunctionExpression::Value(value_node) => {
|
||||
let value_type = value_node.expected_type(context)?;
|
||||
|
||||
if let Type::Function { return_type, .. } = value_type {
|
||||
Ok(*return_type)
|
||||
} else {
|
||||
Ok(value_type)
|
||||
}
|
||||
}
|
||||
FunctionExpression::Index(index) => index.expected_type(context),
|
||||
FunctionExpression::Yield(r#yield) => r#yield.expected_type(context),
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ impl AbstractTree for FunctionExpression {
|
||||
"identifier" => FunctionExpression::Identifier(Identifier::from_syntax_node(
|
||||
source, child, context,
|
||||
)?),
|
||||
|
||||
"function_call" => FunctionExpression::FunctionCall(Box::new(
|
||||
FunctionCall::from_syntax_node(source, child, context)?,
|
||||
)),
|
||||
|
@ -1,7 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node;
|
||||
|
||||
use crate::{AbstractTree, Error, Map, Result, Type, Value, BUILT_IN_FUNCTIONS};
|
||||
use crate::{AbstractTree, Error, Map, Result, Type, Value};
|
||||
|
||||
/// A string by which a variable is known to a context.
|
||||
///
|
||||
@ -47,12 +47,6 @@ impl AbstractTree for Identifier {
|
||||
if let Some((_value, r#type)) = context.variables()?.get(&self.0) {
|
||||
Ok(r#type.clone())
|
||||
} else {
|
||||
for built_in_function in BUILT_IN_FUNCTIONS {
|
||||
if self.0 == built_in_function.name() {
|
||||
return Ok(built_in_function.r#type());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Type::None)
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,17 @@ pub enum Type {
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn list_of(item_type: Type) -> Self {
|
||||
Type::List(Box::new(item_type))
|
||||
}
|
||||
|
||||
pub fn function(parameter_types: Vec<Type>, return_type: Type) -> Self {
|
||||
Type::Function {
|
||||
parameter_types,
|
||||
return_type: Box::new(return_type),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check(&self, other: &Type) -> Result<()> {
|
||||
match (self, other) {
|
||||
(Type::Any, _)
|
||||
|
@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node;
|
||||
|
||||
use crate::{
|
||||
value::BuiltInValue, AbstractTree, Block, Error, Expression, Function, Identifier, List, Map,
|
||||
Result, Statement, Type, TypeDefinition, Value,
|
||||
AbstractTree, BuiltInValue, Error, Expression, Function, Identifier, List, Map, Result,
|
||||
Statement, Type, TypeDefinition, Value,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||
@ -29,58 +29,7 @@ impl AbstractTree for ValueNode {
|
||||
let value_node = match child.kind() {
|
||||
"boolean" => ValueNode::Boolean(source[child.byte_range()].to_string()),
|
||||
"float" => ValueNode::Float(source[child.byte_range()].to_string()),
|
||||
"function" => {
|
||||
let child_count = child.child_count();
|
||||
let mut parameters = Vec::new();
|
||||
let mut parameter_types = Vec::new();
|
||||
|
||||
for index in 1..child_count - 3 {
|
||||
let child = child.child(index).unwrap();
|
||||
|
||||
if child.kind() == "identifier" {
|
||||
let identifier = Identifier::from_syntax_node(source, child, context)?;
|
||||
|
||||
parameters.push(identifier);
|
||||
}
|
||||
|
||||
if child.kind() == "type_definition" {
|
||||
let type_definition =
|
||||
TypeDefinition::from_syntax_node(source, child, context)?;
|
||||
|
||||
parameter_types.push(type_definition.take_inner());
|
||||
}
|
||||
}
|
||||
|
||||
let function_context = Map::clone_from(context)?;
|
||||
|
||||
for (parameter_name, parameter_type) in
|
||||
parameters.iter().zip(parameter_types.iter())
|
||||
{
|
||||
function_context.set(
|
||||
parameter_name.inner().clone(),
|
||||
Value::none(),
|
||||
Some(parameter_type.clone()),
|
||||
)?;
|
||||
}
|
||||
|
||||
let return_type_node = child.child(child_count - 2).unwrap();
|
||||
let return_type =
|
||||
TypeDefinition::from_syntax_node(source, return_type_node, context)?;
|
||||
|
||||
let body_node = child.child(child_count - 1).unwrap();
|
||||
let body = Block::from_syntax_node(source, body_node, &function_context)?;
|
||||
|
||||
return_type
|
||||
.inner()
|
||||
.check(&body.expected_type(&function_context)?)?;
|
||||
|
||||
let r#type = Type::Function {
|
||||
parameter_types,
|
||||
return_type: Box::new(return_type.take_inner()),
|
||||
};
|
||||
|
||||
ValueNode::Function(Function::new(parameters, body, Some(r#type)))
|
||||
}
|
||||
"function" => ValueNode::Function(Function::from_syntax_node(source, child, context)?),
|
||||
"integer" => ValueNode::Integer(source[child.byte_range()].to_string()),
|
||||
"string" => {
|
||||
let without_quotes = child.start_byte() + 1..child.end_byte() - 1;
|
||||
|
@ -223,6 +223,5 @@ fn display_value(value: &Value, ui: &mut egui::Ui) {
|
||||
ui.label("none");
|
||||
}
|
||||
},
|
||||
Value::BuiltIn(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +0,0 @@
|
||||
use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
|
||||
|
||||
pub struct Assert;
|
||||
|
||||
impl BuiltInFunction for Assert {
|
||||
fn name(&self) -> &'static str {
|
||||
"assert"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
for argument in arguments {
|
||||
if !argument.as_boolean()? {
|
||||
return Err(Error::AssertFailed);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::none())
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::Any],
|
||||
return_type: Box::new(Type::None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AssertEqual;
|
||||
|
||||
impl BuiltInFunction for AssertEqual {
|
||||
fn name(&self) -> &'static str {
|
||||
"assert_equal"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 2, arguments.len())?;
|
||||
|
||||
let left = arguments.get(0).unwrap();
|
||||
let right = arguments.get(1).unwrap();
|
||||
|
||||
if left == right {
|
||||
Ok(Value::none())
|
||||
} else {
|
||||
Err(Error::AssertEqualFailed {
|
||||
expected: left.clone(),
|
||||
actual: right.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::Any, Type::Any],
|
||||
return_type: Box::new(Type::Boolean),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
|
||||
|
||||
pub struct Length;
|
||||
|
||||
impl BuiltInFunction for Length {
|
||||
fn name(&self) -> &'static str {
|
||||
"length"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 1, arguments.len())?;
|
||||
|
||||
let length = arguments.first().unwrap().as_list()?.items().len();
|
||||
|
||||
Ok(Value::Integer(length as i64))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::List(Box::new(Type::Any))],
|
||||
return_type: Box::new(Type::Integer),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
use std::process::Command;
|
||||
|
||||
use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
|
||||
|
||||
pub struct Raw;
|
||||
|
||||
impl BuiltInFunction for Raw {
|
||||
fn name(&self) -> &'static str {
|
||||
"raw"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 2, arguments.len())?;
|
||||
|
||||
let program = arguments.first().unwrap().as_string()?;
|
||||
let command_arguments = arguments.get(1).unwrap().as_list()?;
|
||||
let mut command = Command::new(program);
|
||||
|
||||
for argument in command_arguments.items().iter() {
|
||||
command.arg(argument.as_string()?);
|
||||
}
|
||||
|
||||
let output = command.spawn()?.wait_with_output()?.stdout;
|
||||
|
||||
Ok(Value::String(String::from_utf8(output)?))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> crate::Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::String, Type::List(Box::new(Type::String))],
|
||||
return_type: Box::new(Type::String),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Sh;
|
||||
|
||||
impl BuiltInFunction for Sh {
|
||||
fn name(&self) -> &'static str {
|
||||
"sh"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
let command_text = arguments.first().unwrap().as_string()?;
|
||||
let mut command = Command::new("sh");
|
||||
|
||||
command.arg("-c");
|
||||
command.arg(command_text);
|
||||
|
||||
let extra_command_text = arguments.get(1).unwrap_or_default().as_option()?;
|
||||
|
||||
if let Some(text) = extra_command_text {
|
||||
command.args(["--", text.as_string()?]);
|
||||
}
|
||||
|
||||
let output = command.spawn()?.wait_with_output()?.stdout;
|
||||
|
||||
Ok(Value::String(String::from_utf8(output)?))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> crate::Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::String, Type::Option(Box::new(Type::String))],
|
||||
return_type: Box::new(Type::String),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
|
||||
|
||||
pub struct FromJson;
|
||||
|
||||
impl BuiltInFunction for FromJson {
|
||||
fn name(&self) -> &'static str {
|
||||
"from_json"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 1, arguments.len())?;
|
||||
|
||||
let json_string = arguments.first().unwrap().as_string()?;
|
||||
let value = serde_json::from_str(json_string)?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::String],
|
||||
return_type: Box::new(Type::Any),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ToJson;
|
||||
|
||||
impl BuiltInFunction for ToJson {
|
||||
fn name(&self) -> &'static str {
|
||||
"to_json"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 1, arguments.len())?;
|
||||
|
||||
let value = arguments.first().unwrap();
|
||||
let json_string = serde_json::to_string(&value)?;
|
||||
|
||||
Ok(Value::String(json_string))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::Any],
|
||||
return_type: Box::new(Type::String),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
use std::{
|
||||
fs::{read_dir, read_to_string, write, File},
|
||||
io::Write as IoWrite,
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use crate::{BuiltInFunction, List, Map, Result, Type, Value};
|
||||
|
||||
pub struct Read;
|
||||
|
||||
impl BuiltInFunction for Read {
|
||||
fn name(&self) -> &'static str {
|
||||
"read"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
let path_string = arguments.first().unwrap_or_default().as_string()?;
|
||||
let path = PathBuf::from(path_string);
|
||||
|
||||
if path.is_dir() {
|
||||
let files = List::new();
|
||||
|
||||
for entry in read_dir(&path)? {
|
||||
let entry = entry?;
|
||||
let file_data = Map::new();
|
||||
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
let metadata = entry.metadata()?;
|
||||
let created = metadata.created()?.elapsed()?.as_secs() as i64;
|
||||
let modified = metadata.modified()?.elapsed()?.as_secs() as i64;
|
||||
|
||||
file_data.set("name".to_string(), Value::String(name), None)?;
|
||||
file_data.set("created".to_string(), Value::Integer(created), None)?;
|
||||
file_data.set("modified".to_string(), Value::Integer(modified), None)?;
|
||||
|
||||
files.items_mut().push(Value::Map(file_data));
|
||||
}
|
||||
|
||||
return Ok(Value::List(files));
|
||||
}
|
||||
|
||||
let file_content = read_to_string(path)?;
|
||||
|
||||
Ok(Value::String(file_content))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::String],
|
||||
return_type: Box::new(Type::String),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Write;
|
||||
|
||||
impl BuiltInFunction for Write {
|
||||
fn name(&self) -> &'static str {
|
||||
"write"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
let file_content = arguments.first().unwrap_or_default().as_string()?;
|
||||
let path = arguments.get(1).unwrap_or_default().as_string()?;
|
||||
|
||||
write(path, file_content)?;
|
||||
|
||||
Ok(Value::none())
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::String],
|
||||
return_type: Box::new(Type::None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Append;
|
||||
|
||||
impl BuiltInFunction for Append {
|
||||
fn name(&self) -> &'static str {
|
||||
"append"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
let file_content = arguments.get(0).unwrap_or_default().as_string()?;
|
||||
let path = arguments.get(1).unwrap_or_default().as_string()?;
|
||||
|
||||
File::options()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(path)?
|
||||
.write_all(file_content.as_bytes())?;
|
||||
|
||||
Ok(Value::none())
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::String, Type::String],
|
||||
return_type: Box::new(Type::None),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
//! Functions related to [Map][crate::Map] values.
|
||||
|
||||
use crate::{BuiltInFunction, List, Map, Result, Type, Value};
|
||||
|
||||
pub struct Keys;
|
||||
|
||||
impl BuiltInFunction for Keys {
|
||||
fn name(&self) -> &'static str {
|
||||
"keys"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
let map = arguments.first().unwrap_or_default().as_map()?;
|
||||
let variables = map.variables()?;
|
||||
let mut keys = Vec::with_capacity(variables.len());
|
||||
|
||||
for (key, _) in variables.iter() {
|
||||
keys.push(Value::String(key.clone()))
|
||||
}
|
||||
|
||||
Ok(Value::List(List::with_items(keys)))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::Map],
|
||||
return_type: Box::new(Type::List(Box::new(Type::String))),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +1,90 @@
|
||||
/// Built-in functions that are available to all Dust programs.
|
||||
use crate::{Map, Result, Type, Value};
|
||||
use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
fs::read_to_string,
|
||||
};
|
||||
|
||||
mod assert;
|
||||
mod collections;
|
||||
mod commands;
|
||||
mod data_formats;
|
||||
mod fs;
|
||||
mod network;
|
||||
mod option;
|
||||
mod output;
|
||||
mod packages;
|
||||
mod random;
|
||||
mod r#type;
|
||||
use rand::random;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// All built-in functions recognized by the interpreter.
|
||||
///
|
||||
/// This is the public interface to access built-in functions by iterating over
|
||||
/// the references it holds.
|
||||
pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 21] = [
|
||||
&assert::Assert,
|
||||
&assert::AssertEqual,
|
||||
&collections::Length,
|
||||
&commands::Raw,
|
||||
&commands::Sh,
|
||||
&data_formats::FromJson,
|
||||
&data_formats::ToJson,
|
||||
&fs::Read,
|
||||
&fs::Write,
|
||||
&fs::Append,
|
||||
&network::Download,
|
||||
&option::EitherOr,
|
||||
&option::IsNone,
|
||||
&option::IsSome,
|
||||
&output::Output,
|
||||
&packages::InstallPackages,
|
||||
&random::Random,
|
||||
&random::RandomBoolean,
|
||||
&random::RandomFloat,
|
||||
&random::RandomInteger,
|
||||
&r#type::TypeFunction,
|
||||
];
|
||||
use crate::{Error, Map, Result, Type, Value};
|
||||
|
||||
pub trait BuiltInFunction {
|
||||
fn name(&self) -> &'static str;
|
||||
fn run(&self, arguments: &[Value], context: &Map) -> Result<Value>;
|
||||
fn r#type(&self) -> Type;
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum BuiltInFunction {
|
||||
AssertEqual,
|
||||
FsRead,
|
||||
Output,
|
||||
RandomBoolean,
|
||||
Length,
|
||||
}
|
||||
|
||||
impl BuiltInFunction {
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
BuiltInFunction::AssertEqual => "assert_equal",
|
||||
BuiltInFunction::FsRead => "fs_read",
|
||||
BuiltInFunction::Output => "output",
|
||||
BuiltInFunction::RandomBoolean => "boolean",
|
||||
BuiltInFunction::Length => "length",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> Type {
|
||||
match self {
|
||||
BuiltInFunction::AssertEqual => Type::function(vec![Type::Any, Type::Any], Type::None),
|
||||
BuiltInFunction::FsRead => Type::function(vec![Type::String], Type::String),
|
||||
BuiltInFunction::Output => Type::function(vec![Type::Any], Type::None),
|
||||
BuiltInFunction::RandomBoolean => Type::function(vec![], Type::Boolean),
|
||||
BuiltInFunction::Length => {
|
||||
Type::function(vec![Type::list_of(Type::Any)], Type::Integer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call(&self, arguments: &[Value], _source: &str, _outer_context: &Map) -> Result<Value> {
|
||||
match self {
|
||||
BuiltInFunction::AssertEqual => {
|
||||
Error::expect_argument_amount(self, 2, arguments.len())?;
|
||||
|
||||
let left = arguments.get(0).unwrap();
|
||||
let right = arguments.get(1).unwrap();
|
||||
|
||||
Ok(Value::Boolean(left == right))
|
||||
}
|
||||
BuiltInFunction::FsRead => {
|
||||
Error::expect_argument_amount(self, 1, arguments.len())?;
|
||||
|
||||
let path = arguments.first().unwrap().as_string()?;
|
||||
let file_content = read_to_string(path)?;
|
||||
|
||||
Ok(Value::String(file_content))
|
||||
}
|
||||
BuiltInFunction::Output => {
|
||||
Error::expect_argument_amount(self, 1, arguments.len())?;
|
||||
|
||||
let value = arguments.first().unwrap();
|
||||
|
||||
println!("{value}");
|
||||
|
||||
Ok(Value::none())
|
||||
}
|
||||
BuiltInFunction::RandomBoolean => {
|
||||
Error::expect_argument_amount(self, 0, arguments.len())?;
|
||||
|
||||
Ok(Value::Boolean(random()))
|
||||
}
|
||||
BuiltInFunction::Length => {
|
||||
Error::expect_argument_amount(self, 1, arguments.len())?;
|
||||
|
||||
let list_len = arguments.first().unwrap().as_list()?.items().len() as i64;
|
||||
|
||||
Ok(Value::Integer(list_len))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for BuiltInFunction {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.r#type())
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
use reqwest::blocking::get;
|
||||
|
||||
use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
|
||||
|
||||
pub struct Download;
|
||||
|
||||
impl BuiltInFunction for Download {
|
||||
fn name(&self) -> &'static str {
|
||||
"download"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 1, arguments.len())?;
|
||||
|
||||
let url = arguments.first().unwrap().as_string()?;
|
||||
let response = get(url)?;
|
||||
|
||||
Ok(Value::String(response.text()?))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::String],
|
||||
return_type: Box::new(Type::String),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
use crate::{BuiltInFunction, Map, Result, Type, Value};
|
||||
|
||||
pub struct EitherOr;
|
||||
|
||||
impl BuiltInFunction for EitherOr {
|
||||
fn name(&self) -> &'static str {
|
||||
"either_or"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
let option = arguments.first().unwrap_or_default().as_option()?;
|
||||
let value = if let Some(value) = option {
|
||||
*value.clone()
|
||||
} else {
|
||||
arguments.get(1).unwrap_or_default().clone()
|
||||
};
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::Option(Box::new(Type::Any)), Type::Any],
|
||||
return_type: Box::new(Type::Boolean),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IsNone;
|
||||
|
||||
impl BuiltInFunction for IsNone {
|
||||
fn name(&self) -> &'static str {
|
||||
"is_none"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
let option = arguments.first().unwrap_or_default().as_option()?;
|
||||
|
||||
Ok(Value::Boolean(option.is_none()))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::Option(Box::new(Type::Any))],
|
||||
return_type: Box::new(Type::Boolean),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IsSome;
|
||||
|
||||
impl BuiltInFunction for IsSome {
|
||||
fn name(&self) -> &'static str {
|
||||
"is_some"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
let option = arguments.first().unwrap_or_default().as_option()?;
|
||||
|
||||
Ok(Value::Boolean(option.is_some()))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::Option(Box::new(Type::Any))],
|
||||
return_type: Box::new(Type::Boolean),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
|
||||
|
||||
pub struct Output;
|
||||
|
||||
impl BuiltInFunction for Output {
|
||||
fn name(&self) -> &'static str {
|
||||
"output"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 1, arguments.len())?;
|
||||
|
||||
let value = arguments.first().unwrap();
|
||||
|
||||
println!("{value}");
|
||||
|
||||
Ok(Value::default())
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::Any],
|
||||
return_type: Box::new(Type::None),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
use std::process::Command;
|
||||
|
||||
use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
|
||||
|
||||
pub struct InstallPackages;
|
||||
|
||||
impl BuiltInFunction for InstallPackages {
|
||||
fn name(&self) -> &'static str {
|
||||
"install_packages"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 1, arguments.len())?;
|
||||
|
||||
let mut command = Command::new("sudo");
|
||||
let argument_list = arguments.first().unwrap().as_list()?;
|
||||
|
||||
command.args(["dnf", "-y", "install"]);
|
||||
|
||||
for argument in argument_list.items().iter() {
|
||||
command.arg(argument.as_string()?);
|
||||
}
|
||||
|
||||
command.spawn()?.wait()?;
|
||||
|
||||
Ok(Value::none())
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::List(Box::new(Type::String))],
|
||||
return_type: Box::new(Type::None),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
use rand::{random, thread_rng, Rng};
|
||||
|
||||
use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
|
||||
|
||||
pub struct Random;
|
||||
|
||||
impl BuiltInFunction for Random {
|
||||
fn name(&self) -> &'static str {
|
||||
"random"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 1, arguments.len())?;
|
||||
|
||||
let list = arguments.first().unwrap().as_list()?;
|
||||
let items = list.items();
|
||||
let random_index = thread_rng().gen_range(0..items.len());
|
||||
let random_argument = items.get(random_index).unwrap();
|
||||
|
||||
Ok(random_argument.clone())
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::List(Box::new(Type::Any))],
|
||||
return_type: Box::new(Type::Any),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RandomInteger;
|
||||
|
||||
impl BuiltInFunction for RandomInteger {
|
||||
fn name(&self) -> &'static str {
|
||||
"random_integer"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 0, arguments.len())?;
|
||||
|
||||
Ok(Value::Integer(random()))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: Vec::with_capacity(0),
|
||||
return_type: Box::new(Type::Integer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RandomFloat;
|
||||
|
||||
impl BuiltInFunction for RandomFloat {
|
||||
fn name(&self) -> &'static str {
|
||||
"random_float"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 0, arguments.len())?;
|
||||
|
||||
Ok(Value::Float(random()))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: Vec::with_capacity(0),
|
||||
return_type: Box::new(Type::Float),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RandomBoolean;
|
||||
|
||||
impl BuiltInFunction for RandomBoolean {
|
||||
fn name(&self) -> &'static str {
|
||||
"random_boolean"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 0, arguments.len())?;
|
||||
|
||||
Ok(Value::Boolean(random()))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: Vec::with_capacity(0),
|
||||
return_type: Box::new(Type::Boolean),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
|
||||
|
||||
pub struct TypeFunction;
|
||||
|
||||
impl BuiltInFunction for TypeFunction {
|
||||
fn name(&self) -> &'static str {
|
||||
"type"
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 1, arguments.len())?;
|
||||
|
||||
let type_text = arguments.first().unwrap().r#type().to_string();
|
||||
|
||||
Ok(Value::String(type_text))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::Any],
|
||||
return_type: Box::new(Type::String),
|
||||
}
|
||||
}
|
||||
}
|
@ -199,8 +199,8 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_argument_amount<F: BuiltInFunction>(
|
||||
function: &F,
|
||||
pub fn expect_argument_amount(
|
||||
function: &BuiltInFunction,
|
||||
expected: usize,
|
||||
actual: usize,
|
||||
) -> Result<()> {
|
||||
|
@ -5,10 +5,15 @@
|
||||
//! interpreting Dust code. Most of the language's features are implemented in the [tools] module.
|
||||
pub use crate::{
|
||||
abstract_tree::*,
|
||||
built_in_functions::{BuiltInFunction, BUILT_IN_FUNCTIONS},
|
||||
built_in_functions::BuiltInFunction,
|
||||
error::*,
|
||||
interpret::*,
|
||||
value::{function::Function, list::List, map::Map, Value},
|
||||
value::{
|
||||
function::{ContextDefinedFunction, Function},
|
||||
list::List,
|
||||
map::Map,
|
||||
BuiltInValue, Value,
|
||||
},
|
||||
};
|
||||
|
||||
mod abstract_tree;
|
||||
|
@ -1,17 +1,127 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node;
|
||||
|
||||
use crate::{AbstractTree, Block, Identifier, Map, Result, Type, Value};
|
||||
use crate::{
|
||||
AbstractTree, Block, BuiltInFunction, Error, Identifier, Map, Result, Type, TypeDefinition,
|
||||
Value,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Function {
|
||||
pub enum Function {
|
||||
BuiltIn(BuiltInFunction),
|
||||
ContextDefined(ContextDefinedFunction),
|
||||
}
|
||||
|
||||
impl Display for Function {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Function::BuiltIn(inner) => write!(f, "{inner}"),
|
||||
Function::ContextDefined(inner) => write!(f, "{inner}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn call(&self, arguments: &[Value], source: &str, outer_context: &Map) -> Result<Value> {
|
||||
match self {
|
||||
Function::BuiltIn(built_in_function) => {
|
||||
built_in_function.call(arguments, source, outer_context)
|
||||
}
|
||||
Function::ContextDefined(context_defined_function) => {
|
||||
context_defined_function.call(arguments, source, outer_context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> Type {
|
||||
match self {
|
||||
Function::BuiltIn(built_in_function) => built_in_function.r#type(),
|
||||
Function::ContextDefined(context_defined_function) => {
|
||||
context_defined_function.r#type().clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AbstractTree for Function {
|
||||
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self> {
|
||||
Error::expect_syntax_node(source, "function", node)?;
|
||||
|
||||
let child_count = node.child_count();
|
||||
let mut parameters = Vec::new();
|
||||
let mut parameter_types = Vec::new();
|
||||
|
||||
for index in 1..child_count - 3 {
|
||||
let child = node.child(index).unwrap();
|
||||
|
||||
if child.kind() == "identifier" {
|
||||
let identifier = Identifier::from_syntax_node(source, child, context)?;
|
||||
|
||||
parameters.push(identifier);
|
||||
}
|
||||
|
||||
if child.kind() == "type_definition" {
|
||||
let type_definition = TypeDefinition::from_syntax_node(source, child, context)?;
|
||||
|
||||
parameter_types.push(type_definition.take_inner());
|
||||
}
|
||||
}
|
||||
|
||||
let function_context = Map::clone_from(context)?;
|
||||
|
||||
for (parameter_name, parameter_type) in parameters.iter().zip(parameter_types.iter()) {
|
||||
function_context.set(
|
||||
parameter_name.inner().clone(),
|
||||
Value::none(),
|
||||
Some(parameter_type.clone()),
|
||||
)?;
|
||||
}
|
||||
|
||||
let return_type_node = node.child(child_count - 2).unwrap();
|
||||
let return_type = TypeDefinition::from_syntax_node(source, return_type_node, context)?;
|
||||
|
||||
let body_node = node.child(child_count - 1).unwrap();
|
||||
let body = Block::from_syntax_node(source, body_node, &function_context)?;
|
||||
|
||||
return_type
|
||||
.inner()
|
||||
.check(&body.expected_type(&function_context)?)
|
||||
.map_err(|error| error.at_node(body_node, source))?;
|
||||
|
||||
let r#type = Type::Function {
|
||||
parameter_types,
|
||||
return_type: Box::new(return_type.take_inner()),
|
||||
};
|
||||
|
||||
Ok(Self::ContextDefined(ContextDefinedFunction::new(
|
||||
parameters,
|
||||
body,
|
||||
Some(r#type),
|
||||
)))
|
||||
}
|
||||
|
||||
fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
|
||||
Ok(Value::Function(self.clone()))
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Map) -> Result<Type> {
|
||||
match self {
|
||||
Function::BuiltIn(built_in) => Ok(built_in.r#type()),
|
||||
Function::ContextDefined(context_defined) => Ok(context_defined.r#type().clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ContextDefinedFunction {
|
||||
parameters: Vec<Identifier>,
|
||||
body: Block,
|
||||
r#type: Type,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
impl ContextDefinedFunction {
|
||||
pub fn new(parameters: Vec<Identifier>, body: Block, r#type: Option<Type>) -> Self {
|
||||
let r#type = r#type.unwrap_or(Type::Function {
|
||||
parameter_types: vec![Type::Any; parameters.len()],
|
||||
@ -63,7 +173,7 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Function {
|
||||
impl Display for ContextDefinedFunction {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "(")?;
|
||||
|
||||
|
@ -8,7 +8,7 @@ use std::{
|
||||
collections::BTreeMap,
|
||||
fmt::{self, Display, Formatter},
|
||||
marker::PhantomData,
|
||||
sync::{Arc, RwLock, RwLockReadGuard},
|
||||
sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
|
||||
};
|
||||
|
||||
use crate::{value::Value, Result, Type};
|
||||
@ -46,6 +46,10 @@ impl Map {
|
||||
Ok(self.variables.read()?)
|
||||
}
|
||||
|
||||
pub fn variables_mut(&self) -> Result<RwLockWriteGuard<BTreeMap<String, (Value, Type)>>> {
|
||||
Ok(self.variables.write()?)
|
||||
}
|
||||
|
||||
pub fn set(
|
||||
&self,
|
||||
key: String,
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Types that represent runtime values.
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
interpret_with_context, AbstractTree, Function, List, Map, Type,
|
||||
AbstractTree, BuiltInFunction, Function, List, Map, Type,
|
||||
};
|
||||
|
||||
use serde::{
|
||||
@ -14,6 +14,7 @@ use tree_sitter::Node;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
convert::TryFrom,
|
||||
env::args,
|
||||
fmt::{self, Display, Formatter},
|
||||
marker::PhantomData,
|
||||
ops::{Add, AddAssign, Div, Mul, Rem, Sub, SubAssign},
|
||||
@ -24,36 +25,70 @@ pub mod function;
|
||||
pub mod list;
|
||||
pub mod map;
|
||||
|
||||
static STD: OnceLock<Value> = OnceLock::new();
|
||||
static ARGS: OnceLock<Value> = OnceLock::new();
|
||||
static RANDOM: OnceLock<Value> = OnceLock::new();
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum BuiltInValue {
|
||||
Std,
|
||||
Args,
|
||||
AssertEqual,
|
||||
Length,
|
||||
Output,
|
||||
Random,
|
||||
}
|
||||
|
||||
impl BuiltInValue {
|
||||
fn r#type(&self) -> Type {
|
||||
match self {
|
||||
BuiltInValue::Std => Type::Map,
|
||||
BuiltInValue::Args => Type::list_of(Type::String),
|
||||
BuiltInValue::AssertEqual => BuiltInFunction::AssertEqual.r#type(),
|
||||
BuiltInValue::Length => BuiltInFunction::Length.r#type(),
|
||||
BuiltInValue::Output => BuiltInFunction::Output.r#type(),
|
||||
BuiltInValue::Random => Type::Map,
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self) -> &Value {
|
||||
STD.get_or_init(|| {
|
||||
let std_source = "foobar = () <str> { 'foobar' }";
|
||||
let std_context = Map::new();
|
||||
match self {
|
||||
BuiltInValue::Args => ARGS.get_or_init(|| {
|
||||
let args = args().map(|arg| Value::String(arg.to_string())).collect();
|
||||
|
||||
interpret_with_context(std_source, std_context.clone()).unwrap();
|
||||
Value::List(List::with_items(args))
|
||||
}),
|
||||
BuiltInValue::AssertEqual => {
|
||||
&Value::Function(Function::BuiltIn(BuiltInFunction::AssertEqual))
|
||||
}
|
||||
BuiltInValue::Length => &Value::Function(Function::BuiltIn(BuiltInFunction::Length)),
|
||||
BuiltInValue::Output => &Value::Function(Function::BuiltIn(BuiltInFunction::Output)),
|
||||
BuiltInValue::Random => RANDOM.get_or_init(|| {
|
||||
let random_context = Map::new();
|
||||
|
||||
Value::Map(std_context)
|
||||
})
|
||||
{
|
||||
let mut variables = random_context.variables_mut().unwrap();
|
||||
|
||||
for built_in_function in [BuiltInFunction::RandomBoolean] {
|
||||
let key = built_in_function.name().to_string();
|
||||
let value = Value::Function(Function::BuiltIn(built_in_function));
|
||||
let r#type = built_in_function.r#type();
|
||||
|
||||
variables.insert(key, (value, r#type));
|
||||
}
|
||||
}
|
||||
|
||||
Value::Map(random_context)
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AbstractTree for BuiltInValue {
|
||||
fn from_syntax_node(_source: &str, node: Node, _context: &Map) -> Result<Self> {
|
||||
let built_in_value = match node.kind() {
|
||||
"std" => BuiltInValue::Std,
|
||||
"args" => BuiltInValue::Args,
|
||||
"assert_equal" => BuiltInValue::AssertEqual,
|
||||
"length" => BuiltInValue::Length,
|
||||
"output" => BuiltInValue::Output,
|
||||
"random" => BuiltInValue::Random,
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
@ -65,9 +100,7 @@ impl AbstractTree for BuiltInValue {
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Map) -> Result<Type> {
|
||||
match self {
|
||||
BuiltInValue::Std => Ok(Type::Map),
|
||||
}
|
||||
Ok(self.r#type())
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +119,6 @@ pub enum Value {
|
||||
Integer(i64),
|
||||
Boolean(bool),
|
||||
Option(Option<Box<Value>>),
|
||||
BuiltIn(BuiltInValue),
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
@ -132,7 +164,6 @@ impl Value {
|
||||
Type::None
|
||||
}
|
||||
}
|
||||
Value::BuiltIn(built_in_value) => built_in_value.r#type(),
|
||||
};
|
||||
|
||||
r#type
|
||||
@ -471,8 +502,6 @@ impl PartialOrd for Value {
|
||||
impl Ord for Value {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match (self, other) {
|
||||
(Value::BuiltIn(left), Value::BuiltIn(right)) => left.cmp(right),
|
||||
(Value::BuiltIn(_), _) => Ordering::Greater,
|
||||
(Value::String(left), Value::String(right)) => left.cmp(right),
|
||||
(Value::String(_), _) => Ordering::Greater,
|
||||
(Value::Float(left), Value::Float(right)) => left.total_cmp(right),
|
||||
@ -524,7 +553,6 @@ impl Serialize for Value {
|
||||
Value::Option(inner) => inner.serialize(serializer),
|
||||
Value::Map(inner) => inner.serialize(serializer),
|
||||
Value::Function(inner) => inner.serialize(serializer),
|
||||
Value::BuiltIn(inner) => inner.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -552,7 +580,6 @@ impl Display for Value {
|
||||
}
|
||||
Value::Map(map) => write!(f, "{map}"),
|
||||
Value::Function(function) => write!(f, "{function}"),
|
||||
Value::BuiltIn(_) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -183,6 +183,11 @@ mod value {
|
||||
let result = interpret("() <int> { 1 }");
|
||||
let value = result.unwrap();
|
||||
let function = value.as_function().unwrap();
|
||||
let function = if let Function::ContextDefined(function) = function {
|
||||
function
|
||||
} else {
|
||||
panic!("Something is wrong with this test...");
|
||||
};
|
||||
|
||||
assert_eq!(&Vec::<Identifier>::with_capacity(0), function.parameters());
|
||||
assert_eq!(&Type::Integer, function.return_type());
|
||||
@ -190,6 +195,11 @@ mod value {
|
||||
let result = interpret("(x <bool>) <bool> { true }");
|
||||
let value = result.unwrap();
|
||||
let function = value.as_function().unwrap();
|
||||
let function = if let Function::ContextDefined(function) = function {
|
||||
function
|
||||
} else {
|
||||
panic!("Something is wrong with this test...");
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
&vec![Identifier::new("x".to_string())],
|
||||
@ -503,3 +513,15 @@ mod blocks {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod std {
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn load_std() {
|
||||
assert_eq!(
|
||||
interpret("std:read"),
|
||||
Ok(Value::Function(Function::BuiltIn(BuiltInFunction::FsRead)))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ async { output ('Whaddup') }
|
||||
(expression
|
||||
(function_call
|
||||
(function_expression
|
||||
(identifier
|
||||
(built_in_function)))
|
||||
(value
|
||||
(built_in_value)))
|
||||
(expression
|
||||
(value
|
||||
(string)))))))))
|
||||
|
@ -15,8 +15,8 @@ Simple Block
|
||||
(expression
|
||||
(function_call
|
||||
(function_expression
|
||||
(identifier
|
||||
(built_in_function)))
|
||||
(value
|
||||
(built_in_value)))
|
||||
(expression
|
||||
(value
|
||||
(integer)))))))))
|
||||
|
@ -29,8 +29,8 @@ for i in [1, 2, 3] {
|
||||
(expression
|
||||
(function_call
|
||||
(function_expression
|
||||
(identifier
|
||||
(built_in_function)))
|
||||
(value
|
||||
(built_in_value)))
|
||||
(expression
|
||||
(identifier)))))))))
|
||||
|
||||
@ -63,7 +63,7 @@ for list in list_of_lists {
|
||||
(expression
|
||||
(function_call
|
||||
(function_expression
|
||||
(identifier
|
||||
(built_in_function)))
|
||||
(value
|
||||
(built_in_value)))
|
||||
(expression
|
||||
(identifier))))))))))))
|
||||
|
@ -129,16 +129,17 @@ Anonymous Function Call
|
||||
(expression
|
||||
(function_call
|
||||
(function_expression
|
||||
(function
|
||||
(identifier)
|
||||
(type_definition
|
||||
(type))
|
||||
(type_definition
|
||||
(type))
|
||||
(block
|
||||
(statement
|
||||
(expression
|
||||
(identifier))))))
|
||||
(value
|
||||
(function
|
||||
(identifier)
|
||||
(type_definition
|
||||
(type))
|
||||
(type_definition
|
||||
(type))
|
||||
(block
|
||||
(statement
|
||||
(expression
|
||||
(identifier)))))))
|
||||
(expression
|
||||
(value
|
||||
(string)))))))
|
||||
@ -222,13 +223,11 @@ from_json(read('file.json'))
|
||||
(expression
|
||||
(function_call
|
||||
(function_expression
|
||||
(identifier
|
||||
(built_in_function)))
|
||||
(identifier))
|
||||
(expression
|
||||
(function_call
|
||||
(function_expression
|
||||
(identifier
|
||||
(built_in_function)))
|
||||
(identifier))
|
||||
(expression
|
||||
(value
|
||||
(string)))))))))
|
||||
|
@ -65,8 +65,8 @@ Complex Logic Sequence
|
||||
(expression
|
||||
(function_call
|
||||
(function_expression
|
||||
(identifier
|
||||
(built_in_function)))
|
||||
(value
|
||||
(built_in_value)))
|
||||
(expression
|
||||
(identifier))))
|
||||
(logic_operator)
|
||||
@ -79,8 +79,8 @@ Complex Logic Sequence
|
||||
(expression
|
||||
(function_call
|
||||
(function_expression
|
||||
(identifier
|
||||
(built_in_function)))
|
||||
(value
|
||||
(built_in_value)))
|
||||
(expression
|
||||
(identifier))))
|
||||
(logic_operator)
|
||||
@ -93,8 +93,8 @@ Complex Logic Sequence
|
||||
(expression
|
||||
(function_call
|
||||
(function_expression
|
||||
(identifier
|
||||
(built_in_function)))
|
||||
(value
|
||||
(built_in_value)))
|
||||
(expression
|
||||
(identifier))))
|
||||
(logic_operator)
|
||||
|
@ -19,8 +19,8 @@ while true {
|
||||
(expression
|
||||
(function_call
|
||||
(function_expression
|
||||
(identifier
|
||||
(built_in_function)))
|
||||
(value
|
||||
(built_in_value)))
|
||||
(expression
|
||||
(value
|
||||
(string))))))))))
|
||||
|
@ -14,8 +14,8 @@ Simple Yield
|
||||
(value
|
||||
(integer)))
|
||||
(function_expression
|
||||
(identifier
|
||||
(built_in_function)))))))
|
||||
(value
|
||||
(built_in_value)))))))
|
||||
|
||||
================================================================================
|
||||
Yield Chain
|
||||
|
@ -1,7 +1,7 @@
|
||||
module.exports = grammar({
|
||||
name: 'dust',
|
||||
|
||||
word: $ => $._identifier_pattern,
|
||||
word: $ => $.identifier,
|
||||
|
||||
extras: $ => [/\s/, $._comment],
|
||||
|
||||
@ -71,12 +71,6 @@ module.exports = grammar({
|
||||
),
|
||||
|
||||
identifier: $ =>
|
||||
choice(
|
||||
$._identifier_pattern,
|
||||
$.built_in_function,
|
||||
),
|
||||
|
||||
_identifier_pattern: $ =>
|
||||
/[_a-zA-Z]+[_a-zA-Z0-9]*[_a-zA-Z]?/,
|
||||
|
||||
value: $ =>
|
||||
@ -406,11 +400,10 @@ module.exports = grammar({
|
||||
prec(
|
||||
2,
|
||||
choice(
|
||||
$.built_in_value,
|
||||
$.function,
|
||||
$.function_call,
|
||||
$.identifier,
|
||||
$.index,
|
||||
$.value,
|
||||
$.yield,
|
||||
),
|
||||
),
|
||||
@ -442,30 +435,14 @@ module.exports = grammar({
|
||||
),
|
||||
),
|
||||
|
||||
built_in_function: $ =>
|
||||
built_in_value: $ =>
|
||||
choice(
|
||||
'assert',
|
||||
'args',
|
||||
'assert_equal',
|
||||
'bash',
|
||||
'download',
|
||||
'either_or',
|
||||
'fish',
|
||||
'from_json',
|
||||
'is_none',
|
||||
'is_some',
|
||||
'env',
|
||||
'length',
|
||||
'metadata',
|
||||
'output',
|
||||
'output_error',
|
||||
'random',
|
||||
'random_boolean',
|
||||
'random_float',
|
||||
'random_integer',
|
||||
'read',
|
||||
'to_json',
|
||||
'write',
|
||||
),
|
||||
|
||||
built_in_value: $ => choice('std'),
|
||||
},
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dust",
|
||||
"word": "_identifier_pattern",
|
||||
"word": "identifier",
|
||||
"rules": {
|
||||
"root": {
|
||||
"type": "PREC",
|
||||
@ -203,19 +203,6 @@
|
||||
]
|
||||
},
|
||||
"identifier": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_identifier_pattern"
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "built_in_function"
|
||||
}
|
||||
]
|
||||
},
|
||||
"_identifier_pattern": {
|
||||
"type": "PATTERN",
|
||||
"value": "[_a-zA-Z]+[_a-zA-Z0-9]*[_a-zA-Z]?"
|
||||
},
|
||||
@ -1304,14 +1291,6 @@
|
||||
"content": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "built_in_value"
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "function"
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "function_call"
|
||||
@ -1324,6 +1303,10 @@
|
||||
"type": "SYMBOL",
|
||||
"name": "index"
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "value"
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "yield"
|
||||
@ -1410,12 +1393,12 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"built_in_function": {
|
||||
"built_in_value": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "assert"
|
||||
"value": "args"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
@ -1423,84 +1406,19 @@
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "bash"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "download"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "either_or"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "fish"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "from_json"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "is_none"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "is_some"
|
||||
"value": "env"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "length"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "metadata"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "output"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "output_error"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "random"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "random_boolean"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "random_float"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "random_integer"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "read"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "to_json"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "write"
|
||||
}
|
||||
]
|
||||
},
|
||||
"built_in_value": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "std"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -51,11 +51,6 @@
|
||||
"named": true,
|
||||
"fields": {}
|
||||
},
|
||||
{
|
||||
"type": "built_in_function",
|
||||
"named": true,
|
||||
"fields": {}
|
||||
},
|
||||
{
|
||||
"type": "built_in_value",
|
||||
"named": true,
|
||||
@ -207,14 +202,6 @@
|
||||
"multiple": false,
|
||||
"required": true,
|
||||
"types": [
|
||||
{
|
||||
"type": "built_in_value",
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "function_call",
|
||||
"named": true
|
||||
@ -228,22 +215,11 @@
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "yield",
|
||||
"type": "value",
|
||||
"named": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "identifier",
|
||||
"named": true,
|
||||
"fields": {},
|
||||
"children": {
|
||||
"multiple": false,
|
||||
"required": false,
|
||||
"types": [
|
||||
},
|
||||
{
|
||||
"type": "built_in_function",
|
||||
"type": "yield",
|
||||
"named": true
|
||||
}
|
||||
]
|
||||
@ -777,7 +753,7 @@
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "assert",
|
||||
"type": "args",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
@ -792,22 +768,10 @@
|
||||
"type": "async for",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "bash",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "bool",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "download",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "either_or",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "else",
|
||||
"named": false
|
||||
@ -817,11 +781,11 @@
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "false",
|
||||
"type": "env",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "fish",
|
||||
"type": "false",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
@ -837,8 +801,8 @@
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "from_json",
|
||||
"named": false
|
||||
"type": "identifier",
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "if",
|
||||
@ -856,14 +820,6 @@
|
||||
"type": "integer",
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "is_none",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "is_some",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "length",
|
||||
"named": false
|
||||
@ -876,10 +832,6 @@
|
||||
"type": "match",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "none",
|
||||
"named": false
|
||||
@ -896,30 +848,10 @@
|
||||
"type": "output",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "output_error",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "random",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "random_boolean",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "random_float",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "random_integer",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "read",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "return",
|
||||
"named": false
|
||||
@ -928,10 +860,6 @@
|
||||
"type": "some",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "std",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "str",
|
||||
"named": false
|
||||
@ -940,10 +868,6 @@
|
||||
"type": "string",
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "to_json",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "true",
|
||||
"named": false
|
||||
@ -952,10 +876,6 @@
|
||||
"type": "while",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "write",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "{",
|
||||
"named": false
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user