Implement new built-in values

This commit is contained in:
Jeff 2024-01-01 04:59:27 -05:00
parent 2f0ec91c08
commit ae66e2a211
37 changed files with 17815 additions and 25119 deletions

View File

@ -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)

View File

@ -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),
}

View File

@ -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)?,
)),

View File

@ -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)
}
}

View File

@ -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, _)

View File

@ -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;

View File

@ -223,6 +223,5 @@ fn display_value(value: &Value, ui: &mut egui::Ui) {
ui.label("none");
}
},
Value::BuiltIn(_) => todo!(),
}
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}
}

View File

@ -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))),
}
}
}

View File

@ -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())
}
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}
}

View File

@ -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<()> {

View File

@ -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;

View File

@ -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, "(")?;

View File

@ -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,

View File

@ -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!(),
}
}
}

View File

@ -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)))
);
}
}

View File

@ -13,8 +13,8 @@ async { output ('Whaddup') }
(expression
(function_call
(function_expression
(identifier
(built_in_function)))
(value
(built_in_value)))
(expression
(value
(string)))))))))

View File

@ -15,8 +15,8 @@ Simple Block
(expression
(function_call
(function_expression
(identifier
(built_in_function)))
(value
(built_in_value)))
(expression
(value
(integer)))))))))

View File

@ -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))))))))))))

View File

@ -129,6 +129,7 @@ Anonymous Function Call
(expression
(function_call
(function_expression
(value
(function
(identifier)
(type_definition
@ -138,7 +139,7 @@ Anonymous Function Call
(block
(statement
(expression
(identifier))))))
(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)))))))))

View File

@ -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)

View File

@ -19,8 +19,8 @@ while true {
(expression
(function_call
(function_expression
(identifier
(built_in_function)))
(value
(built_in_value)))
(expression
(value
(string))))))))))

View File

@ -14,8 +14,8 @@ Simple Yield
(value
(integer)))
(function_expression
(identifier
(built_in_function)))))))
(value
(built_in_value)))))))
================================================================================
Yield Chain

View File

@ -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'),
},
});

View File

@ -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"
}
]
}

View File

@ -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