Add loading binaries as functions
This commit is contained in:
parent
784e4b309d
commit
8f3d36fc8d
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -68,19 +68,36 @@ 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,
|
||||||
|
|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(),
|
actual: self.arguments.len(),
|
||||||
}
|
});
|
||||||
.at_source_position(source, self.syntax_position));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
r#type
|
let expected_type = expression.expected_type(context)?;
|
||||||
.check(&expression.expected_type(context)?)
|
|
||||||
.map_err(|error| error.at_source_position(source, self.syntax_position))?;
|
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 {
|
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 {
|
||||||
|
@ -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())?;
|
||||||
|
|
||||||
|
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.
|
/// 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 } => {
|
||||||
|
27
src/main.rs
27
src/main.rs
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user