Add loading binaries as functions
This commit is contained in:
parent
784e4b309d
commit
8f3d36fc8d
@ -103,8 +103,8 @@ impl BuiltInValue {
|
||||
BuiltInFunction::RandomInteger,
|
||||
] {
|
||||
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 value = Value::Function(Function::BuiltIn(built_in_function));
|
||||
|
||||
random_context.insert(key, (value, r#type));
|
||||
}
|
||||
|
@ -68,19 +68,36 @@ impl AbstractTree for FunctionCall {
|
||||
}
|
||||
};
|
||||
|
||||
if self.arguments.len() != parameter_types.len() {
|
||||
return Err(Error::ExpectedFunctionArgumentAmount {
|
||||
expected: parameter_types.len(),
|
||||
let required_argument_count =
|
||||
parameter_types.iter().fold(
|
||||
0,
|
||||
|acc, r#type| {
|
||||
if r#type.is_option() {
|
||||
acc
|
||||
} else {
|
||||
acc + 1
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if self.arguments.len() < required_argument_count {
|
||||
return Err(Error::ExpectedFunctionArgumentMinimum {
|
||||
minumum: required_argument_count,
|
||||
actual: self.arguments.len(),
|
||||
}
|
||||
.at_source_position(source, self.syntax_position));
|
||||
});
|
||||
}
|
||||
|
||||
for (index, expression) in self.arguments.iter().enumerate() {
|
||||
if let Some(r#type) = parameter_types.get(index) {
|
||||
r#type
|
||||
.check(&expression.expected_type(context)?)
|
||||
.map_err(|error| error.at_source_position(source, self.syntax_position))?;
|
||||
let expected_type = expression.expected_type(context)?;
|
||||
|
||||
if let Type::Option(optional_type) = r#type {
|
||||
optional_type.check(&expected_type)?;
|
||||
} else {
|
||||
r#type
|
||||
.check(&expression.expected_type(context)?)
|
||||
.map_err(|error| error.at_source_position(source, self.syntax_position))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,6 +150,10 @@ impl Type {
|
||||
pub fn is_map(&self) -> bool {
|
||||
matches!(self, Type::Map(_))
|
||||
}
|
||||
|
||||
pub fn is_option(&self) -> bool {
|
||||
matches!(self, Type::Option(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl AbstractTree for Type {
|
||||
|
@ -3,6 +3,7 @@ mod string;
|
||||
use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
fs::read_to_string,
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use rand::{random, thread_rng, Rng};
|
||||
@ -12,9 +13,10 @@ use crate::{Error, Format, Map, Result, Type, Value};
|
||||
|
||||
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 {
|
||||
AssertEqual,
|
||||
Binary(String),
|
||||
FsRead,
|
||||
JsonParse,
|
||||
Length,
|
||||
@ -30,6 +32,7 @@ impl BuiltInFunction {
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
BuiltInFunction::AssertEqual => "assert_equal",
|
||||
BuiltInFunction::Binary(_) => "binary",
|
||||
BuiltInFunction::FsRead => "read",
|
||||
BuiltInFunction::JsonParse => "parse",
|
||||
BuiltInFunction::Length => "length",
|
||||
@ -45,6 +48,10 @@ impl BuiltInFunction {
|
||||
pub fn r#type(&self) -> Type {
|
||||
match self {
|
||||
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::JsonParse => Type::function(vec![Type::String], Type::Any),
|
||||
BuiltInFunction::Length => Type::function(vec![Type::Collection], Type::Integer),
|
||||
@ -67,6 +74,36 @@ impl BuiltInFunction {
|
||||
|
||||
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 => {
|
||||
Error::expect_argument_amount(self.name(), 1, arguments.len())?;
|
||||
|
||||
|
11
src/error.rs
11
src/error.rs
@ -83,8 +83,7 @@ pub enum Error {
|
||||
|
||||
/// A function was called with the wrong amount of arguments.
|
||||
ExpectedFunctionArgumentMinimum {
|
||||
source: String,
|
||||
minumum_expected: usize,
|
||||
minumum: usize,
|
||||
actual: usize,
|
||||
},
|
||||
|
||||
@ -338,14 +337,10 @@ impl fmt::Display for Error {
|
||||
ExpectedFunctionArgumentAmount { expected, actual } => {
|
||||
write!(f, "Expected {expected} arguments, but got {actual}.",)
|
||||
}
|
||||
ExpectedFunctionArgumentMinimum {
|
||||
source,
|
||||
minumum_expected,
|
||||
actual,
|
||||
} => {
|
||||
ExpectedFunctionArgumentMinimum { minumum, actual } => {
|
||||
write!(
|
||||
f,
|
||||
"{source} expected at least {minumum_expected} arguments, but got {actual}."
|
||||
"Expected at least {minumum} arguments, but got {actual}."
|
||||
)
|
||||
}
|
||||
ExpectedString { actual } => {
|
||||
|
27
src/main.rs
27
src/main.rs
@ -10,7 +10,7 @@ use reedline::{
|
||||
|
||||
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.
|
||||
#[derive(Parser, Debug)]
|
||||
@ -171,6 +171,31 @@ impl Highlighter for DustHighlighter {
|
||||
}
|
||||
|
||||
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 prompt = DefaultPrompt::default();
|
||||
let mut keybindings = default_emacs_keybindings();
|
||||
|
Loading…
Reference in New Issue
Block a user