1
0

Add loading binaries as functions

This commit is contained in:
Jeff 2024-01-25 04:19:45 -05:00
parent 784e4b309d
commit 8f3d36fc8d
6 changed files with 97 additions and 19 deletions

View File

@ -103,8 +103,8 @@ impl BuiltInValue {
BuiltInFunction::RandomInteger, BuiltInFunction::RandomInteger,
] { ] {
let key = built_in_function.name().to_string(); 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(); let r#type = built_in_function.r#type();
let value = Value::Function(Function::BuiltIn(built_in_function));
random_context.insert(key, (value, r#type)); random_context.insert(key, (value, r#type));
} }

View File

@ -68,21 +68,38 @@ impl AbstractTree for FunctionCall {
} }
}; };
if self.arguments.len() != parameter_types.len() { let required_argument_count =
return Err(Error::ExpectedFunctionArgumentAmount { parameter_types.iter().fold(
expected: parameter_types.len(), 0,
actual: self.arguments.len(), |acc, r#type| {
if r#type.is_option() {
acc
} else {
acc + 1
} }
.at_source_position(source, self.syntax_position)); },
);
if self.arguments.len() < required_argument_count {
return Err(Error::ExpectedFunctionArgumentMinimum {
minumum: required_argument_count,
actual: self.arguments.len(),
});
} }
for (index, expression) in self.arguments.iter().enumerate() { for (index, expression) in self.arguments.iter().enumerate() {
if let Some(r#type) = parameter_types.get(index) { if let Some(r#type) = parameter_types.get(index) {
let expected_type = expression.expected_type(context)?;
if let Type::Option(optional_type) = r#type {
optional_type.check(&expected_type)?;
} else {
r#type r#type
.check(&expression.expected_type(context)?) .check(&expression.expected_type(context)?)
.map_err(|error| error.at_source_position(source, self.syntax_position))?; .map_err(|error| error.at_source_position(source, self.syntax_position))?;
} }
} }
}
Ok(()) Ok(())
} }

View File

@ -150,6 +150,10 @@ impl Type {
pub fn is_map(&self) -> bool { pub fn is_map(&self) -> bool {
matches!(self, Type::Map(_)) matches!(self, Type::Map(_))
} }
pub fn is_option(&self) -> bool {
matches!(self, Type::Option(_))
}
} }
impl AbstractTree for Type { impl AbstractTree for Type {

View File

@ -3,6 +3,7 @@ mod string;
use std::{ use std::{
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
fs::read_to_string, fs::read_to_string,
process::Command,
}; };
use rand::{random, thread_rng, Rng}; use rand::{random, thread_rng, Rng};
@ -12,9 +13,10 @@ use crate::{Error, Format, Map, Result, Type, Value};
pub use string::{string_functions, StringFunction}; pub use string::{string_functions, StringFunction};
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum BuiltInFunction { pub enum BuiltInFunction {
AssertEqual, AssertEqual,
Binary(String),
FsRead, FsRead,
JsonParse, JsonParse,
Length, Length,
@ -30,6 +32,7 @@ impl BuiltInFunction {
pub fn name(&self) -> &'static str { pub fn name(&self) -> &'static str {
match self { match self {
BuiltInFunction::AssertEqual => "assert_equal", BuiltInFunction::AssertEqual => "assert_equal",
BuiltInFunction::Binary(_) => "binary",
BuiltInFunction::FsRead => "read", BuiltInFunction::FsRead => "read",
BuiltInFunction::JsonParse => "parse", BuiltInFunction::JsonParse => "parse",
BuiltInFunction::Length => "length", BuiltInFunction::Length => "length",
@ -45,6 +48,10 @@ impl BuiltInFunction {
pub fn r#type(&self) -> Type { pub fn r#type(&self) -> Type {
match self { match self {
BuiltInFunction::AssertEqual => Type::function(vec![Type::Any, Type::Any], Type::None), BuiltInFunction::AssertEqual => Type::function(vec![Type::Any, Type::Any], Type::None),
BuiltInFunction::Binary(_) => Type::function(
vec![Type::Option(Box::new(Type::String))],
Type::Option(Box::new(Type::Integer)),
),
BuiltInFunction::FsRead => Type::function(vec![Type::String], Type::String), BuiltInFunction::FsRead => Type::function(vec![Type::String], Type::String),
BuiltInFunction::JsonParse => Type::function(vec![Type::String], Type::Any), BuiltInFunction::JsonParse => Type::function(vec![Type::String], Type::Any),
BuiltInFunction::Length => Type::function(vec![Type::Collection], Type::Integer), BuiltInFunction::Length => Type::function(vec![Type::Collection], Type::Integer),
@ -67,6 +74,36 @@ impl BuiltInFunction {
Ok(Value::Boolean(left == right)) Ok(Value::Boolean(left == right))
} }
BuiltInFunction::Binary(binary_name) => {
let input = if let Some(value) = arguments.first() {
value.clone()
} else {
Value::none()
};
let mut command = Command::new(binary_name);
if let Ok(Some(value)) = input.as_option() {
let input_string = value.as_string()?;
if !input_string.is_empty() {
command.args(input_string.split_whitespace());
}
}
if let Ok(input_string) = input.as_string() {
if !input_string.is_empty() {
command.args(input_string.split_whitespace());
}
}
let output = command.spawn()?.wait()?;
let status_code = match output.code() {
Some(code) => Value::some(Value::Integer(code as i64)),
None => Value::none(),
};
Ok(status_code)
}
BuiltInFunction::FsRead => { BuiltInFunction::FsRead => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; Error::expect_argument_amount(self.name(), 1, arguments.len())?;

View File

@ -83,8 +83,7 @@ pub enum Error {
/// A function was called with the wrong amount of arguments. /// A function was called with the wrong amount of arguments.
ExpectedFunctionArgumentMinimum { ExpectedFunctionArgumentMinimum {
source: String, minumum: usize,
minumum_expected: usize,
actual: usize, actual: usize,
}, },
@ -338,14 +337,10 @@ impl fmt::Display for Error {
ExpectedFunctionArgumentAmount { expected, actual } => { ExpectedFunctionArgumentAmount { expected, actual } => {
write!(f, "Expected {expected} arguments, but got {actual}.",) write!(f, "Expected {expected} arguments, but got {actual}.",)
} }
ExpectedFunctionArgumentMinimum { ExpectedFunctionArgumentMinimum { minumum, actual } => {
source,
minumum_expected,
actual,
} => {
write!( write!(
f, f,
"{source} expected at least {minumum_expected} arguments, but got {actual}." "Expected at least {minumum} arguments, but got {actual}."
) )
} }
ExpectedString { actual } => { ExpectedString { actual } => {

View File

@ -10,7 +10,7 @@ use reedline::{
use std::{fs::read_to_string, path::PathBuf}; use std::{fs::read_to_string, path::PathBuf};
use dust_lang::{built_in_values, Interpreter, Map, Result, Value}; use dust_lang::{built_in_values, Function, Interpreter, Map, Result, Value};
/// Command-line arguments to be parsed. /// Command-line arguments to be parsed.
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -171,6 +171,31 @@ impl Highlighter for DustHighlighter {
} }
fn run_shell(context: Map) -> Result<()> { fn run_shell(context: Map) -> Result<()> {
for (key, value) in std::env::vars() {
if key == "PATH" {
for path in value.split([' ', ':']) {
let path_dir = if let Ok(path_dir) = PathBuf::from(path).read_dir() {
path_dir
} else {
continue;
};
for entry in path_dir {
let entry = entry?;
if entry.file_type()?.is_file() {
context.set(
entry.file_name().to_string_lossy().to_string(),
Value::Function(Function::BuiltIn(dust_lang::BuiltInFunction::Binary(
entry.file_name().to_string_lossy().to_string(),
))),
)?;
}
}
}
}
}
let mut interpreter = Interpreter::new(context.clone()); let mut interpreter = Interpreter::new(context.clone());
let prompt = DefaultPrompt::default(); let prompt = DefaultPrompt::default();
let mut keybindings = default_emacs_keybindings(); let mut keybindings = default_emacs_keybindings();