Add eval and argument implementation

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/3/head
trivernis 2 years ago
parent 247550ac4e
commit ca5057ea04
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -11,5 +11,5 @@ nu-engine = "0.69.1"
nu-parser = "0.69.1"
nu-protocol = "0.69.1"
paste = "1.0.9"
rusty-value = { version = "0.2.0", features = ["derive"] }
rusty-value = { version = "0.2.1", features = ["derive"] }
thiserror = "1.0.37"

@ -0,0 +1,35 @@
use nu_protocol::{ast::Expression, Span, Spanned};
use crate::{into_expression::IntoExpression, NewEmpty};
/// A struct representing the argument to a function
pub enum Argument {
Named((String, Option<Expression>)),
Positional(Expression),
}
impl Argument {
/// Creates a new named argument. No value means passing the argument as a flag (like --verbose)
pub fn named<S: ToString, E: IntoExpression>(name: S, value: Option<E>) -> Self {
Self::Named((name.to_string(), value.map(|v| v.into_expression())))
}
/// Creates a new positional argument
pub fn positional<E: IntoExpression>(value: E) -> Self {
Self::Positional(value.into_expression())
}
pub(crate) fn into_nu_argument(self) -> nu_protocol::ast::Argument {
match self {
Argument::Named((name, value)) => nu_protocol::ast::Argument::Named((
Spanned {
item: name,
span: Span::empty(),
},
None,
value,
)),
Argument::Positional(value) => nu_protocol::ast::Argument::Positional(value),
}
}
}

@ -4,14 +4,16 @@ mod command_group_config;
pub use builder::*;
pub use command_group_config::CommandGroupConfig;
use nu_protocol::{
ast::Block,
ast::{Block, Call},
engine::{EngineState, Stack},
PipelineData,
PipelineData, Span,
};
use crate::{
argument::Argument,
error::{CrateError, CrateResult},
utils::parse_nu_script,
NewEmpty,
};
/// Represents the evaluation context of nu scripts and commands
@ -52,6 +54,47 @@ impl Context {
self.eval_block(&block, input)
}
/// Returns if the given function exists in the context
pub fn has_fn<S: AsRef<str>>(&mut self, name: S) -> bool {
self.engine_state
.find_decl(name.as_ref().as_bytes(), &vec![])
.is_some()
}
/// Calls a function by the given name
/// Errs if the function doesn't exist
pub fn call_fn<S: AsRef<str>, I: IntoIterator<Item = Argument>>(
&mut self,
name: S,
args: I,
) -> CrateResult<PipelineData> {
let args = args
.into_iter()
.map(|a: Argument| a.into_nu_argument())
.collect::<Vec<_>>();
let decl_id = self
.engine_state
.find_decl(name.as_ref().as_bytes(), &vec![])
.ok_or_else(|| CrateError::FunctionNotFound(name.as_ref().to_string()))?;
let call = Call {
decl_id,
head: Span::empty(),
arguments: args,
redirect_stdout: true,
redirect_stderr: true,
};
let data = nu_engine::eval_call(
&self.engine_state,
&mut self.stack,
&call,
PipelineData::empty(),
)?;
Ok(data)
}
/// Prints the data of the given pipeline to stdout
pub fn print_pipeline(&mut self, pipeline: PipelineData) -> CrateResult<()> {
pipeline.print(&self.engine_state, &mut self.stack, false, false)?;

@ -11,4 +11,7 @@ pub enum CrateError {
#[error("Parse Error {0}")]
NuParseError(#[from] ParseError),
#[error("Could not find the function {0}")]
FunctionNotFound(String),
}

@ -0,0 +1,70 @@
use nu_protocol::{
ast::{Expr, Expression},
Span, Value,
};
use crate::{IntoValue, NewEmpty};
pub trait IntoExpression {
fn into_expression(self) -> Expression;
}
trait ValueIntoExpression {
fn into_expression(self) -> Expression;
fn into_expr(self) -> Expr;
}
impl<V: IntoValue> IntoExpression for V {
#[inline]
fn into_expression(self) -> Expression {
self.into_value().into_expression()
}
}
impl ValueIntoExpression for Value {
fn into_expression(self) -> Expression {
let ty = self.get_type();
Expression {
expr: self.into_expr(),
span: Span::empty(),
ty,
custom_completion: None,
}
}
fn into_expr(self) -> Expr {
match self {
Value::Bool { val, .. } => Expr::Bool(val),
Value::Int { val, .. } => Expr::Int(val),
Value::Float { val, .. } => Expr::Float(val),
Value::Filesize { val, .. } => Expr::Int(val),
Value::Duration { val, .. } => Expr::Int(val),
Value::Date { val, .. } => Expr::DateTime(val),
Value::String { val, .. } => Expr::String(val),
Value::Record {
mut cols, mut vals, ..
} => {
let mut entries = Vec::new();
for i in 0..cols.len() {
let col = cols.remove(i).into_expression();
let val = vals.remove(i).into_expression();
entries.push((col, val));
}
Expr::Record(entries)
}
Value::List { vals, .. } => {
let vals = vals.into_iter().map(|v| v.into_expression()).collect();
Expr::List(vals)
}
Value::Block { val, .. } => Expr::Block(val),
Value::Nothing { .. } => Expr::Nothing,
Value::Error { error } => Expr::String(error.to_string()),
Value::Binary { val, .. } => Expr::Binary(val),
Value::CellPath { val, .. } => Expr::CellPath(val),
_ => Expr::Nothing,
}
}
}

@ -1,9 +1,14 @@
pub mod context;
pub(crate) mod argument;
pub(crate) mod context;
pub(crate) mod error;
pub(crate) mod into_expression;
pub(crate) mod into_value;
pub(crate) mod utils;
pub use argument::Argument;
pub use context::{CommandGroupConfig, Context, ContextBuilder};
pub use into_value::*;
pub use nu_protocol::PipelineData;
pub use rusty_value;
pub use utils::NewEmpty;

@ -1,18 +1,41 @@
use embed_nu::{
context::{CommandGroupConfig, Context},
NewEmpty,
};
use embed_nu::{Argument, CommandGroupConfig, Context, NewEmpty};
use nu_protocol::PipelineData;
#[test]
fn it_evals_strings() {
let mut ctx = get_context();
let pipeline = ctx
.eval_raw(r#"echo "Hello World""#, PipelineData::empty())
.eval_raw(
r#"echo "Hello World from this eval""#,
PipelineData::empty(),
)
.unwrap();
ctx.print_pipeline(pipeline).unwrap()
}
#[test]
fn it_executes_functions() {
let mut ctx = get_context();
ctx.eval_raw(
r#"
def hello [] {
echo "Hello World from this script";
echo # dummy echo so I don't have to print the output
}
"#,
PipelineData::empty(),
)
.unwrap();
ctx.call_fn("hello", []).unwrap();
assert!(ctx.has_fn("world") == false);
let arg = Argument::positional("Hello from rust");
let pipeline = ctx.call_fn("echo", [arg]).unwrap();
ctx.print_pipeline(pipeline).unwrap();
}
fn get_context() -> Context {
Context::builder()
.with_command_groups(CommandGroupConfig::default().all_groups(true))

Loading…
Cancel
Save