Merge branch 'main' of https://github.com/jonathandturner/engine-q into similar-name

This commit is contained in:
Fernando Herrera 2021-09-04 08:45:55 +01:00
commit ca8d311c78
62 changed files with 4284 additions and 3374 deletions

20
Cargo.lock generated
View File

@ -162,8 +162,10 @@ dependencies = [
"assert_cmd",
"codespan-reporting",
"nu-cli",
"nu-command",
"nu-engine",
"nu-parser",
"nu-protocol",
"pretty_assertions",
"reedline",
"tempfile",
@ -292,14 +294,24 @@ dependencies = [
"nu-ansi-term",
"nu-engine",
"nu-parser",
"nu-protocol",
"reedline",
]
[[package]]
name = "nu-command"
version = "0.1.0"
dependencies = [
"nu-engine",
"nu-protocol",
]
[[package]]
name = "nu-engine"
version = "0.1.0"
dependencies = [
"nu-parser",
"nu-protocol",
]
[[package]]
@ -318,6 +330,14 @@ dependencies = [
[[package]]
name = "nu-parser"
version = "0.1.0"
dependencies = [
"codespan-reporting",
"nu-protocol",
]
[[package]]
name = "nu-protocol"
version = "0.1.0"
dependencies = [
"codespan-reporting",
]

View File

@ -6,14 +6,16 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace]
members = ["crates/nu-cli", "crates/nu-engine", "crates/nu-parser"]
members = ["crates/nu-cli", "crates/nu-engine", "crates/nu-parser", "crates/nu-command", "crates/nu-protocol"]
[dependencies]
reedline = { git = "https://github.com/jntrnr/reedline", branch = "main" }
codespan-reporting = "0.11.1"
nu-cli = { path="./crates/nu-cli" }
nu-command = { path="./crates/nu-command" }
nu-engine = { path="./crates/nu-engine" }
nu-parser = { path="./crates/nu-parser" }
nu-protocol = { path = "./crates/nu-protocol" }
# mimalloc = { version = "*", default-features = false }

View File

@ -12,10 +12,10 @@
- [x] subcommand alias
- [x] type inference from successful parse (eg not `List<unknown>` but `List<int>`)
- [x] parsing tables
- [ ] Column path
- [ ] ...rest without calling it rest
- [ ] operator overflow
- [ ] finish operator type-checking
- [ ] Column path
- [ ] Ranges
- [ ] Source
- [ ] Autoenv

View File

@ -6,6 +6,7 @@ edition = "2018"
[dependencies]
nu-engine = { path = "../nu-engine" }
nu-parser = { path = "../nu-parser" }
nu-protocol = { path = "../nu-protocol" }
codespan-reporting = "0.11.1"
nu-ansi-term = "0.32.0"
reedline = { git = "https://github.com/jntrnr/reedline", branch = "main" }

View File

@ -1,115 +0,0 @@
use std::{cell::RefCell, rc::Rc};
use nu_parser::{ParserState, ParserWorkingSet, Signature, SyntaxShape};
pub fn create_default_context() -> Rc<RefCell<ParserState>> {
let parser_state = Rc::new(RefCell::new(ParserState::new()));
let delta = {
let parser_state = parser_state.borrow();
let mut working_set = ParserWorkingSet::new(&*parser_state);
let sig =
Signature::build("where").required("cond", SyntaxShape::RowCondition, "condition");
working_set.add_decl(sig.into());
let sig = Signature::build("if")
.required("cond", SyntaxShape::Expression, "condition")
.required("then_block", SyntaxShape::Block, "then block")
.optional(
"else",
SyntaxShape::Keyword(b"else".to_vec(), Box::new(SyntaxShape::Expression)),
"optional else followed by else block",
);
working_set.add_decl(sig.into());
let sig = Signature::build("let")
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
);
working_set.add_decl(sig.into());
let sig = Signature::build("let-env")
.required("var_name", SyntaxShape::String, "variable name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::String)),
"equals sign followed by value",
);
working_set.add_decl(sig.into());
let sig = Signature::build("alias")
.required("name", SyntaxShape::String, "name of the alias")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
);
working_set.add_decl(sig.into());
let sig = Signature::build("build-string").rest(SyntaxShape::String, "list of string");
working_set.add_decl(sig.into());
let sig = Signature::build("def")
.required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.required("block", SyntaxShape::Block, "body of the definition");
working_set.add_decl(sig.into());
let sig = Signature::build("for")
.required(
"var_name",
SyntaxShape::Variable,
"name of the looping variable",
)
.required(
"range",
SyntaxShape::Keyword(b"in".to_vec(), Box::new(SyntaxShape::Int)),
"range of the loop",
)
.required("block", SyntaxShape::Block, "the block to run");
working_set.add_decl(sig.into());
let sig =
Signature::build("benchmark").required("block", SyntaxShape::Block, "the block to run");
working_set.add_decl(sig.into());
// let sig = Signature::build("foo").named("--jazz", SyntaxShape::Int, "jazz!!", Some('j'));
// working_set.add_decl(sig.into());
// let sig = Signature::build("bar")
// .named("--jazz", SyntaxShape::Int, "jazz!!", Some('j'))
// .switch("--rock", "rock!!", Some('r'));
// working_set.add_decl(sig.into());
let sig = Signature::build("exit");
working_set.add_decl(sig.into());
let sig = Signature::build("vars");
working_set.add_decl(sig.into());
let sig = Signature::build("decls");
working_set.add_decl(sig.into());
let sig = Signature::build("blocks");
working_set.add_decl(sig.into());
let sig = Signature::build("stack");
working_set.add_decl(sig.into());
let sig = Signature::build("add");
working_set.add_decl(sig.into());
let sig = Signature::build("add it");
working_set.add_decl(sig.into());
let sig = Signature::build("add it together")
.required("x", SyntaxShape::Int, "x value")
.required("y", SyntaxShape::Int, "y value");
working_set.add_decl(sig.into());
working_set.render()
};
{
ParserState::merge_delta(&mut *parser_state.borrow_mut(), delta);
}
parser_state
}

View File

@ -2,11 +2,11 @@ use core::ops::Range;
use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use nu_engine::ShellError;
use nu_parser::{ParseError, ParserWorkingSet, Span};
use nu_parser::ParseError;
use nu_protocol::{engine::StateWorkingSet, ShellError, Span};
fn convert_span_to_diag(
working_set: &ParserWorkingSet,
working_set: &StateWorkingSet,
span: &Span,
) -> Result<(usize, Range<usize>), Box<dyn std::error::Error>> {
for (file_id, (_, start, end)) in working_set.files().enumerate() {
@ -22,7 +22,7 @@ fn convert_span_to_diag(
}
pub fn report_parsing_error(
working_set: &ParserWorkingSet,
working_set: &StateWorkingSet,
error: &ParseError,
) -> Result<(), Box<dyn std::error::Error>> {
let writer = StandardStream::stderr(ColorChoice::Always);
@ -236,7 +236,7 @@ pub fn report_parsing_error(
}
pub fn report_shell_error(
working_set: &ParserWorkingSet,
working_set: &StateWorkingSet,
error: &ShellError,
) -> Result<(), Box<dyn std::error::Error>> {
let writer = StandardStream::stderr(ColorChoice::Always);

View File

@ -1,7 +1,5 @@
mod default_context;
mod errors;
mod syntax_highlight;
pub use default_context::create_default_context;
pub use errors::{report_parsing_error, report_shell_error};
pub use syntax_highlight::NuHighlighter;

View File

@ -1,21 +1,22 @@
use nu_ansi_term::Style;
use nu_parser::{FlatShape, ParserState, ParserWorkingSet};
use nu_parser::{flatten_block, parse_source, FlatShape};
use nu_protocol::engine::{EngineState, StateWorkingSet};
use reedline::{Highlighter, StyledText};
use std::{cell::RefCell, rc::Rc};
pub struct NuHighlighter {
pub parser_state: Rc<RefCell<ParserState>>,
pub engine_state: Rc<RefCell<EngineState>>,
}
impl Highlighter for NuHighlighter {
fn highlight(&self, line: &str) -> StyledText {
let (shapes, global_span_offset) = {
let parser_state = self.parser_state.borrow();
let mut working_set = ParserWorkingSet::new(&*parser_state);
let (block, _) = working_set.parse_source(line.as_bytes(), false);
let engine_state = self.engine_state.borrow();
let mut working_set = StateWorkingSet::new(&*engine_state);
let (block, _) = parse_source(&mut working_set, line.as_bytes(), false);
let shapes = working_set.flatten_block(&block);
(shapes, parser_state.next_span_start())
let shapes = flatten_block(&working_set, &block);
(shapes, engine_state.next_span_start())
};
let mut output = StyledText::default();

View File

@ -0,0 +1,10 @@
[package]
name = "nu-command"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-protocol = { path = "../nu-protocol" }
nu-engine = { path = "../nu-engine" }

View File

@ -0,0 +1,34 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{Signature, SyntaxShape, Value};
pub struct Alias;
impl Command for Alias {
fn name(&self) -> &str {
"alias"
}
fn usage(&self) -> &str {
"Alias a command (with optional flags) to a new name"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("alias")
.required("name", SyntaxShape::String, "name of the alias")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
)
}
fn run(
&self,
_context: &EvaluationContext,
call: &Call,
_input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
Ok(Value::Nothing { span: call.head })
}
}

View File

@ -0,0 +1,44 @@
use std::time::Instant;
use nu_engine::eval_block;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{Signature, SyntaxShape, Value};
pub struct Benchmark;
impl Command for Benchmark {
fn name(&self) -> &str {
"benchmark"
}
fn usage(&self) -> &str {
"Time the running time of a block"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("benchmark").required("block", SyntaxShape::Block, "the block to run")
}
fn run(
&self,
context: &EvaluationContext,
call: &Call,
_input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let block = call.positional[0]
.as_block()
.expect("internal error: expected block");
let engine_state = context.engine_state.borrow();
let block = engine_state.get_block(block);
let state = context.enter_scope();
let start_time = Instant::now();
eval_block(&state, block, Value::nothing())?;
let end_time = Instant::now();
println!("{} ms", (end_time - start_time).as_millis());
Ok(Value::Nothing {
span: call.positional[0].span,
})
}
}

View File

@ -0,0 +1,39 @@
use nu_engine::eval_expression;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{Signature, SyntaxShape, Value};
pub struct BuildString;
impl Command for BuildString {
fn name(&self) -> &str {
"build-string"
}
fn usage(&self) -> &str {
"Create a string from the arguments."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("build-string").rest(SyntaxShape::String, "list of string")
}
fn run(
&self,
context: &EvaluationContext,
call: &Call,
_input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let mut output = vec![];
for expr in &call.positional {
let val = eval_expression(context, expr)?;
output.push(val.into_string());
}
Ok(Value::String {
val: output.join(""),
span: call.head,
})
}
}

View File

@ -0,0 +1,31 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{Signature, SyntaxShape, Value};
pub struct Def;
impl Command for Def {
fn name(&self) -> &str {
"def"
}
fn usage(&self) -> &str {
"Define a custom command"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("def")
.required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.required("block", SyntaxShape::Block, "body of the definition")
}
fn run(
&self,
_context: &EvaluationContext,
call: &Call,
_input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
Ok(Value::Nothing { span: call.head })
}
}

View File

@ -0,0 +1,61 @@
use std::{cell::RefCell, rc::Rc};
use nu_protocol::{
engine::{EngineState, StateWorkingSet},
Signature, SyntaxShape,
};
use crate::{Alias, Benchmark, BuildString, Def, Do, Each, For, If, Length, Let, LetEnv};
pub fn create_default_context() -> Rc<RefCell<EngineState>> {
let engine_state = Rc::new(RefCell::new(EngineState::new()));
let delta = {
let engine_state = engine_state.borrow();
let mut working_set = StateWorkingSet::new(&*engine_state);
let sig =
Signature::build("where").required("cond", SyntaxShape::RowCondition, "condition");
working_set.add_decl(sig.predeclare());
working_set.add_decl(Box::new(If));
working_set.add_decl(Box::new(Let));
working_set.add_decl(Box::new(LetEnv));
working_set.add_decl(Box::new(Alias));
working_set.add_decl(Box::new(BuildString));
working_set.add_decl(Box::new(Def));
working_set.add_decl(Box::new(For));
working_set.add_decl(Box::new(Each));
working_set.add_decl(Box::new(Do));
working_set.add_decl(Box::new(Benchmark));
working_set.add_decl(Box::new(Length));
let sig = Signature::build("exit");
working_set.add_decl(sig.predeclare());
let sig = Signature::build("vars");
working_set.add_decl(sig.predeclare());
let sig = Signature::build("decls");
working_set.add_decl(sig.predeclare());
let sig = Signature::build("blocks");
working_set.add_decl(sig.predeclare());
let sig = Signature::build("stack");
working_set.add_decl(sig.predeclare());
working_set.render()
};
{
EngineState::merge_delta(&mut *engine_state.borrow_mut(), delta);
}
engine_state
}

View File

@ -0,0 +1,40 @@
use nu_engine::{eval_block, eval_expression};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{Signature, SyntaxShape, Value};
pub struct Do;
impl Command for Do {
fn name(&self) -> &str {
"do"
}
fn usage(&self) -> &str {
"Run a block"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("do").required("block", SyntaxShape::Block, "the block to run")
}
fn run(
&self,
context: &EvaluationContext,
call: &Call,
input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let block = &call.positional[0];
let out = eval_expression(context, &block)?;
match out {
Value::Block { val: block_id, .. } => {
let engine_state = context.engine_state.borrow();
let block = engine_state.get_block(block_id);
eval_block(context, block, input)
}
_ => Ok(Value::nothing()),
}
}
}

View File

@ -0,0 +1,77 @@
use nu_engine::eval_block;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{IntoValueStream, Signature, SyntaxShape, Value};
pub struct Each;
impl Command for Each {
fn name(&self) -> &str {
"each"
}
fn usage(&self) -> &str {
"Run a block on each element of input"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("each")
.required(
"var_name",
SyntaxShape::Variable,
"name of the looping variable",
)
.required("block", SyntaxShape::Block, "the block to run")
}
fn run(
&self,
context: &EvaluationContext,
call: &Call,
input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let var_id = call.positional[0]
.as_var()
.expect("internal error: missing variable");
let block = call.positional[1]
.as_block()
.expect("internal error: expected block");
let context = context.clone();
match input {
Value::List { val, .. } => Ok(Value::List {
val: val
.into_iter()
.map(move |x| {
let engine_state = context.engine_state.borrow();
let block = engine_state.get_block(block);
let state = context.enter_scope();
state.add_var(var_id, x.clone());
//FIXME: DON'T UNWRAP
eval_block(&state, block, Value::nothing()).unwrap()
})
.collect(),
span: call.head,
}),
Value::ValueStream { stream, .. } => Ok(Value::ValueStream {
stream: stream
.map(move |x| {
let engine_state = context.engine_state.borrow();
let block = engine_state.get_block(block);
let state = context.enter_scope();
state.add_var(var_id, x.clone());
//FIXME: DON'T UNWRAP
eval_block(&state, block, Value::nothing()).unwrap()
})
.into_value_stream(),
span: call.head,
}),
_ => Ok(Value::nothing()),
}
}
}

View File

@ -0,0 +1,90 @@
use nu_engine::{eval_block, eval_expression};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{IntoValueStream, Signature, SyntaxShape, Value};
pub struct For;
impl Command for For {
fn name(&self) -> &str {
"for"
}
fn usage(&self) -> &str {
"Loop over a range"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("for")
.required(
"var_name",
SyntaxShape::Variable,
"name of the looping variable",
)
.required(
"range",
SyntaxShape::Keyword(
b"in".to_vec(),
Box::new(SyntaxShape::List(Box::new(SyntaxShape::Int))),
),
"range of the loop",
)
.required("block", SyntaxShape::Block, "the block to run")
}
fn run(
&self,
context: &EvaluationContext,
call: &Call,
_input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let var_id = call.positional[0]
.as_var()
.expect("internal error: missing variable");
let keyword_expr = call.positional[1]
.as_keyword()
.expect("internal error: missing keyword");
let values = eval_expression(context, keyword_expr)?;
let block = call.positional[2]
.as_block()
.expect("internal error: expected block");
let context = context.clone();
match values {
Value::ValueStream { stream, .. } => Ok(Value::ValueStream {
stream: stream
.map(move |x| {
let engine_state = context.engine_state.borrow();
let block = engine_state.get_block(block);
let state = context.enter_scope();
state.add_var(var_id, x.clone());
//FIXME: DON'T UNWRAP
eval_block(&state, block, Value::nothing()).unwrap()
})
.into_value_stream(),
span: call.head,
}),
Value::List { val, .. } => Ok(Value::List {
val: val
.into_iter()
.map(move |x| {
let engine_state = context.engine_state.borrow();
let block = engine_state.get_block(block);
let state = context.enter_scope();
state.add_var(var_id, x.clone());
//FIXME: DON'T UNWRAP
eval_block(&state, block, Value::nothing()).unwrap()
})
.collect(),
span: call.head,
}),
_ => Ok(Value::nothing()),
}
}
}

View File

@ -0,0 +1,67 @@
use nu_engine::{eval_block, eval_expression};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{ShellError, Signature, SyntaxShape, Value};
pub struct If;
impl Command for If {
fn name(&self) -> &str {
"if"
}
fn usage(&self) -> &str {
"Create a variable and give it a value."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("if")
.required("cond", SyntaxShape::Expression, "condition")
.required("then_block", SyntaxShape::Block, "then block")
.optional(
"else",
SyntaxShape::Keyword(b"else".to_vec(), Box::new(SyntaxShape::Expression)),
"optional else followed by else block",
)
}
fn run(
&self,
context: &EvaluationContext,
call: &Call,
input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let cond = &call.positional[0];
let then_block = call.positional[1]
.as_block()
.expect("internal error: expected block");
let else_case = call.positional.get(2);
let result = eval_expression(context, cond)?;
match result {
Value::Bool { val, span } => {
let engine_state = context.engine_state.borrow();
if val {
let block = engine_state.get_block(then_block);
let state = context.enter_scope();
eval_block(&state, block, input)
} else if let Some(else_case) = else_case {
if let Some(else_expr) = else_case.as_keyword() {
if let Some(block_id) = else_expr.as_block() {
let block = engine_state.get_block(block_id);
let state = context.enter_scope();
eval_block(&state, block, input)
} else {
eval_expression(context, else_expr)
}
} else {
eval_expression(context, else_case)
}
} else {
Ok(Value::Nothing { span })
}
}
_ => Err(ShellError::CantConvert("bool".into(), result.span())),
}
}
}

View File

@ -0,0 +1,69 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{Signature, Value};
pub struct Length;
impl Command for Length {
fn name(&self) -> &str {
"length"
}
fn usage(&self) -> &str {
"Count the number of elements in the input."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("length")
}
fn run(
&self,
_context: &EvaluationContext,
call: &Call,
input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
match input {
Value::List { val, .. } => {
let length = val.len();
Ok(Value::Int {
val: length as i64,
span: call.head,
})
}
Value::Table { val, .. } => {
let length = val.len();
Ok(Value::Int {
val: length as i64,
span: call.head,
})
}
Value::ValueStream { stream, .. } => {
let length = stream.count();
Ok(Value::Int {
val: length as i64,
span: call.head,
})
}
Value::RowStream { stream, .. } => {
let length = stream.count();
Ok(Value::Int {
val: length as i64,
span: call.head,
})
}
Value::Nothing { .. } => Ok(Value::Int {
val: 0,
span: call.head,
}),
_ => Ok(Value::Int {
val: 1,
span: call.head,
}),
}
}
}

View File

@ -0,0 +1,50 @@
use nu_engine::eval_expression;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{Signature, SyntaxShape, Value};
pub struct Let;
impl Command for Let {
fn name(&self) -> &str {
"let"
}
fn usage(&self) -> &str {
"Create a variable and give it a value."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("let")
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
)
}
fn run(
&self,
context: &EvaluationContext,
call: &Call,
_input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let var_id = call.positional[0]
.as_var()
.expect("internal error: missing variable");
let keyword_expr = call.positional[1]
.as_keyword()
.expect("internal error: missing keyword");
let rhs = eval_expression(context, keyword_expr)?;
//println!("Adding: {:?} to {}", rhs, var_id);
context.add_var(var_id, rhs);
Ok(Value::Nothing {
span: call.positional[0].span,
})
}
}

View File

@ -0,0 +1,51 @@
use nu_engine::eval_expression;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{Signature, SyntaxShape, Value};
pub struct LetEnv;
impl Command for LetEnv {
fn name(&self) -> &str {
"let-env"
}
fn usage(&self) -> &str {
"Create an environment variable and give it a value."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("let-env")
.required("var_name", SyntaxShape::String, "variable name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::String)),
"equals sign followed by value",
)
}
fn run(
&self,
context: &EvaluationContext,
call: &Call,
_input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let env_var = call.positional[0]
.as_string()
.expect("internal error: missing variable");
let keyword_expr = call.positional[1]
.as_keyword()
.expect("internal error: missing keyword");
let rhs = eval_expression(context, keyword_expr)?;
let rhs = rhs.as_string()?;
//println!("Adding: {:?} to {}", rhs, var_id);
context.add_env_var(env_var, rhs);
Ok(Value::Nothing {
span: call.positional[0].span,
})
}
}

View File

@ -0,0 +1,25 @@
mod alias;
mod benchmark;
mod build_string;
mod def;
mod default_context;
mod do_;
mod each;
mod for_;
mod if_;
mod length;
mod let_;
mod let_env;
pub use alias::Alias;
pub use benchmark::Benchmark;
pub use build_string::BuildString;
pub use def::Def;
pub use default_context::create_default_context;
pub use do_::Do;
pub use each::Each;
pub use for_::For;
pub use if_::If;
pub use length::Length;
pub use let_::Let;
pub use let_env::LetEnv;

View File

@ -4,4 +4,5 @@ version = "0.1.0"
edition = "2018"
[dependencies]
nu-parser = { path = "../nu-parser" }
nu-parser = { path = "../nu-parser" }
nu-protocol = { path = "../nu-protocol" }

View File

@ -1,23 +1,6 @@
use std::time::Instant;
use crate::{state::State, value::Value};
use nu_parser::{Block, Call, Expr, Expression, Operator, Span, Statement, Type};
#[derive(Debug)]
pub enum ShellError {
OperatorMismatch {
op_span: Span,
lhs_ty: Type,
lhs_span: Span,
rhs_ty: Type,
rhs_span: Span,
},
Unsupported(Span),
InternalError(String),
VariableNotFound(Span),
CantConvert(String, Span),
DivisionByZero(Span),
}
use nu_protocol::ast::{Block, Call, Expr, Expression, Operator, Statement};
use nu_protocol::engine::EvaluationContext;
use nu_protocol::{ShellError, Value};
pub fn eval_operator(op: &Expression) -> Result<Operator, ShellError> {
match op {
@ -29,16 +12,16 @@ pub fn eval_operator(op: &Expression) -> Result<Operator, ShellError> {
}
}
fn eval_call(state: &State, call: &Call) -> Result<Value, ShellError> {
let parser_state = state.parser_state.borrow();
let decl = parser_state.get_decl(call.decl_id);
if let Some(block_id) = decl.body {
let state = state.enter_scope();
fn eval_call(context: &EvaluationContext, call: &Call, input: Value) -> Result<Value, ShellError> {
let engine_state = context.engine_state.borrow();
let decl = engine_state.get_decl(call.decl_id);
if let Some(block_id) = decl.get_custom_command() {
let state = context.enter_scope();
for (arg, param) in call.positional.iter().zip(
decl.signature
decl.signature()
.required_positional
.iter()
.chain(decl.signature.optional_positional.iter()),
.chain(decl.signature().optional_positional.iter()),
) {
let result = eval_expression(&state, arg)?;
let var_id = param
@ -47,161 +30,18 @@ fn eval_call(state: &State, call: &Call) -> Result<Value, ShellError> {
state.add_var(var_id, result);
}
let parser_state = state.parser_state.borrow();
let block = parser_state.get_block(block_id);
eval_block(&state, block)
} else if decl.signature.name == "let" {
let var_id = call.positional[0]
.as_var()
.expect("internal error: missing variable");
let keyword_expr = call.positional[1]
.as_keyword()
.expect("internal error: missing keyword");
let rhs = eval_expression(state, keyword_expr)?;
//println!("Adding: {:?} to {}", rhs, var_id);
state.add_var(var_id, rhs);
Ok(Value::Nothing {
span: call.positional[0].span,
})
} else if decl.signature.name == "let-env" {
let env_var = call.positional[0]
.as_string()
.expect("internal error: missing variable");
let keyword_expr = call.positional[1]
.as_keyword()
.expect("internal error: missing keyword");
let rhs = eval_expression(state, keyword_expr)?;
let rhs = rhs.as_string()?;
//println!("Adding: {:?} to {}", rhs, var_id);
state.add_env_var(env_var, rhs);
Ok(Value::Nothing {
span: call.positional[0].span,
})
} else if decl.signature.name == "if" {
let cond = &call.positional[0];
let then_block = call.positional[1]
.as_block()
.expect("internal error: expected block");
let else_case = call.positional.get(2);
let result = eval_expression(state, cond)?;
match result {
Value::Bool { val, span } => {
let parser_state = state.parser_state.borrow();
if val {
let block = parser_state.get_block(then_block);
let state = state.enter_scope();
eval_block(&state, block)
} else if let Some(else_case) = else_case {
if let Some(else_expr) = else_case.as_keyword() {
if let Some(block_id) = else_expr.as_block() {
let block = parser_state.get_block(block_id);
let state = state.enter_scope();
eval_block(&state, block)
} else {
eval_expression(state, else_expr)
}
} else {
eval_expression(state, else_case)
}
} else {
Ok(Value::Nothing { span })
}
}
_ => Err(ShellError::CantConvert("bool".into(), result.span())),
}
} else if decl.signature.name == "build-string" {
let mut output = vec![];
for expr in &call.positional {
let val = eval_expression(state, expr)?;
output.push(val.to_string());
}
Ok(Value::String {
val: output.join(""),
span: call.head,
})
} else if decl.signature.name == "benchmark" {
let block = call.positional[0]
.as_block()
.expect("internal error: expected block");
let parser_state = state.parser_state.borrow();
let block = parser_state.get_block(block);
let state = state.enter_scope();
let start_time = Instant::now();
eval_block(&state, block)?;
let end_time = Instant::now();
println!("{} ms", (end_time - start_time).as_millis());
Ok(Value::Nothing {
span: call.positional[0].span,
})
} else if decl.signature.name == "for" {
let var_id = call.positional[0]
.as_var()
.expect("internal error: missing variable");
let keyword_expr = call.positional[1]
.as_keyword()
.expect("internal error: missing keyword");
let end_val = eval_expression(state, keyword_expr)?;
let block = call.positional[2]
.as_block()
.expect("internal error: expected block");
let parser_state = state.parser_state.borrow();
let block = parser_state.get_block(block);
let state = state.enter_scope();
let mut x = Value::Int {
val: 0,
span: Span::unknown(),
};
loop {
if x == end_val {
break;
} else {
state.add_var(var_id, x.clone());
eval_block(&state, block)?;
}
if let Value::Int { ref mut val, .. } = x {
*val += 1
}
}
Ok(Value::Nothing {
span: call.positional[0].span,
})
} else if decl.signature.name == "vars" {
state.parser_state.borrow().print_vars();
Ok(Value::Nothing { span: call.head })
} else if decl.signature.name == "decls" {
state.parser_state.borrow().print_decls();
Ok(Value::Nothing { span: call.head })
} else if decl.signature.name == "blocks" {
state.parser_state.borrow().print_blocks();
Ok(Value::Nothing { span: call.head })
} else if decl.signature.name == "stack" {
state.print_stack();
Ok(Value::Nothing { span: call.head })
} else if decl.signature.name == "def" || decl.signature.name == "alias" {
Ok(Value::Nothing { span: call.head })
let engine_state = state.engine_state.borrow();
let block = engine_state.get_block(block_id);
eval_block(&state, block, input)
} else {
Err(ShellError::Unsupported(call.head))
decl.run(context, call, input)
}
}
pub fn eval_expression(state: &State, expr: &Expression) -> Result<Value, ShellError> {
pub fn eval_expression(
context: &EvaluationContext,
expr: &Expression,
) -> Result<Value, ShellError> {
match &expr.expr {
Expr::Bool(b) => Ok(Value::Bool {
val: *b,
@ -215,17 +55,17 @@ pub fn eval_expression(state: &State, expr: &Expression) -> Result<Value, ShellE
val: *f,
span: expr.span,
}),
Expr::Var(var_id) => state
Expr::Var(var_id) => context
.get_var(*var_id)
.map_err(move |_| ShellError::VariableNotFound(expr.span)),
Expr::Call(call) => eval_call(state, call),
Expr::Call(call) => eval_call(context, call, Value::nothing()),
Expr::ExternalCall(_, _) => Err(ShellError::Unsupported(expr.span)),
Expr::Operator(_) => Ok(Value::Nothing { span: expr.span }),
Expr::BinaryOp(lhs, op, rhs) => {
let op_span = op.span;
let lhs = eval_expression(state, lhs)?;
let lhs = eval_expression(context, lhs)?;
let op = eval_operator(op)?;
let rhs = eval_expression(state, rhs)?;
let rhs = eval_expression(context, rhs)?;
match op {
Operator::Plus => lhs.add(op_span, &rhs),
@ -243,11 +83,11 @@ pub fn eval_expression(state: &State, expr: &Expression) -> Result<Value, ShellE
}
Expr::Subexpression(block_id) => {
let parser_state = state.parser_state.borrow();
let block = parser_state.get_block(*block_id);
let engine_state = context.engine_state.borrow();
let block = engine_state.get_block(*block_id);
let state = state.enter_scope();
eval_block(&state, block)
let state = context.enter_scope();
eval_block(&state, block, Value::nothing())
}
Expr::Block(block_id) => Ok(Value::Block {
val: *block_id,
@ -256,7 +96,7 @@ pub fn eval_expression(state: &State, expr: &Expression) -> Result<Value, ShellE
Expr::List(x) => {
let mut output = vec![];
for expr in x {
output.push(eval_expression(state, expr)?);
output.push(eval_expression(context, expr)?);
}
Ok(Value::List {
val: output,
@ -266,14 +106,14 @@ pub fn eval_expression(state: &State, expr: &Expression) -> Result<Value, ShellE
Expr::Table(headers, vals) => {
let mut output_headers = vec![];
for expr in headers {
output_headers.push(eval_expression(state, expr)?.as_string()?);
output_headers.push(eval_expression(context, expr)?.as_string()?);
}
let mut output_rows = vec![];
for val in vals {
let mut row = vec![];
for expr in val {
row.push(eval_expression(state, expr)?);
row.push(eval_expression(context, expr)?);
}
output_rows.push(row);
}
@ -283,7 +123,7 @@ pub fn eval_expression(state: &State, expr: &Expression) -> Result<Value, ShellE
span: expr.span,
})
}
Expr::Keyword(_, _, expr) => eval_expression(state, expr),
Expr::Keyword(_, _, expr) => eval_expression(context, expr),
Expr::String(s) => Ok(Value::String {
val: s.clone(),
span: expr.span,
@ -293,16 +133,29 @@ pub fn eval_expression(state: &State, expr: &Expression) -> Result<Value, ShellE
}
}
pub fn eval_block(state: &State, block: &Block) -> Result<Value, ShellError> {
let mut last = Ok(Value::Nothing {
span: Span { start: 0, end: 0 },
});
pub fn eval_block(
context: &EvaluationContext,
block: &Block,
mut input: Value,
) -> Result<Value, ShellError> {
for stmt in &block.stmts {
if let Statement::Expression(expression) = stmt {
last = Ok(eval_expression(state, expression)?);
if let Statement::Pipeline(pipeline) = stmt {
for elem in &pipeline.expressions {
match elem {
Expression {
expr: Expr::Call(call),
..
} => {
input = eval_call(context, call, input)?;
}
elem => {
input = eval_expression(context, elem)?;
}
}
}
}
}
last
Ok(input)
}

View File

@ -1,7 +1,3 @@
mod eval;
mod state;
mod value;
pub use eval::{eval_block, eval_expression, eval_operator, ShellError};
pub use state::{Stack, State};
pub use value::Value;
pub use eval::{eval_block, eval_expression, eval_operator};

View File

@ -4,4 +4,5 @@ version = "0.1.0"
edition = "2018"
[dependencies]
codespan-reporting = "0.11.1"
codespan-reporting = "0.11.1"
nu-protocol = { path = "../nu-protocol"}

View File

@ -1,7 +0,0 @@
use crate::{BlockId, Signature};
#[derive(Clone, Debug)]
pub struct Declaration {
pub signature: Box<Signature>,
pub body: Option<BlockId>,
}

View File

@ -1,8 +1,4 @@
use crate::parser_state::Type;
use crate::ParserWorkingSet;
use std::ops::Range;
pub use crate::Span;
use nu_protocol::{Span, Type};
#[derive(Debug)]
pub enum ParseError {
@ -33,95 +29,3 @@ pub enum ParseError {
IncompleteParser(Span),
RestNeedsName(Span),
}
impl<'a> codespan_reporting::files::Files<'a> for ParserWorkingSet<'a> {
type FileId = usize;
type Name = String;
type Source = String;
fn name(&'a self, id: Self::FileId) -> Result<Self::Name, codespan_reporting::files::Error> {
Ok(self.get_filename(id))
}
fn source(
&'a self,
id: Self::FileId,
) -> Result<Self::Source, codespan_reporting::files::Error> {
Ok(self.get_file_source(id))
}
fn line_index(
&'a self,
id: Self::FileId,
byte_index: usize,
) -> Result<usize, codespan_reporting::files::Error> {
let source = self.get_file_source(id);
let mut count = 0;
for byte in source.bytes().enumerate() {
if byte.0 == byte_index {
// println!("count: {} for file: {} index: {}", count, id, byte_index);
return Ok(count);
}
if byte.1 == b'\n' {
count += 1;
}
}
// println!("count: {} for file: {} index: {}", count, id, byte_index);
Ok(count)
}
fn line_range(
&'a self,
id: Self::FileId,
line_index: usize,
) -> Result<Range<usize>, codespan_reporting::files::Error> {
let source = self.get_file_source(id);
let mut count = 0;
let mut start = Some(0);
let mut end = None;
for byte in source.bytes().enumerate() {
#[allow(clippy::comparison_chain)]
if count > line_index {
let start = start.expect("internal error: couldn't find line");
let end = end.expect("internal error: couldn't find line");
// println!(
// "Span: {}..{} for fileid: {} index: {}",
// start, end, id, line_index
// );
return Ok(start..end);
} else if count == line_index {
end = Some(byte.0 + 1);
}
#[allow(clippy::comparison_chain)]
if byte.1 == b'\n' {
count += 1;
if count > line_index {
break;
} else if count == line_index {
start = Some(byte.0 + 1);
}
}
}
match (start, end) {
(Some(start), Some(end)) => {
// println!(
// "Span: {}..{} for fileid: {} index: {}",
// start, end, id, line_index
// );
Ok(start..end)
}
_ => Err(codespan_reporting::files::Error::FileMissing),
}
}
}

View File

@ -1,4 +1,5 @@
use crate::{Block, Expr, Expression, ParserWorkingSet, Pipeline, Span, Statement};
use nu_protocol::ast::{Block, Expr, Expression, Pipeline, Statement};
use nu_protocol::{engine::StateWorkingSet, Span};
#[derive(Debug)]
pub enum FlatShape {
@ -15,101 +16,109 @@ pub enum FlatShape {
Variable,
}
impl<'a> ParserWorkingSet<'a> {
pub fn flatten_block(&self, block: &Block) -> Vec<(Span, FlatShape)> {
let mut output = vec![];
for stmt in &block.stmts {
output.extend(self.flatten_statement(stmt));
}
output
pub fn flatten_block(working_set: &StateWorkingSet, block: &Block) -> Vec<(Span, FlatShape)> {
let mut output = vec![];
for stmt in &block.stmts {
output.extend(flatten_statement(working_set, stmt));
}
output
}
pub fn flatten_statement(&self, stmt: &Statement) -> Vec<(Span, FlatShape)> {
match stmt {
Statement::Expression(expr) => self.flatten_expression(expr),
Statement::Pipeline(pipeline) => self.flatten_pipeline(pipeline),
_ => vec![],
}
}
pub fn flatten_expression(&self, expr: &Expression) -> Vec<(Span, FlatShape)> {
match &expr.expr {
Expr::BinaryOp(lhs, op, rhs) => {
let mut output = vec![];
output.extend(self.flatten_expression(lhs));
output.extend(self.flatten_expression(op));
output.extend(self.flatten_expression(rhs));
output
}
Expr::Block(block_id) => self.flatten_block(self.get_block(*block_id)),
Expr::Call(call) => {
let mut output = vec![(call.head, FlatShape::InternalCall)];
for positional in &call.positional {
output.extend(self.flatten_expression(positional));
}
output
}
Expr::ExternalCall(..) => {
vec![(expr.span, FlatShape::External)]
}
Expr::Garbage => {
vec![(expr.span, FlatShape::Garbage)]
}
Expr::Int(_) => {
vec![(expr.span, FlatShape::Int)]
}
Expr::Float(_) => {
vec![(expr.span, FlatShape::Float)]
}
Expr::Bool(_) => {
vec![(expr.span, FlatShape::Bool)]
}
Expr::List(list) => {
let mut output = vec![];
for l in list {
output.extend(self.flatten_expression(l));
}
output
}
Expr::Keyword(_, span, expr) => {
let mut output = vec![(*span, FlatShape::Operator)];
output.extend(self.flatten_expression(expr));
output
}
Expr::Operator(_) => {
vec![(expr.span, FlatShape::Operator)]
}
Expr::Signature(_) => {
vec![(expr.span, FlatShape::Signature)]
}
Expr::String(_) => {
vec![(expr.span, FlatShape::String)]
}
Expr::Subexpression(block_id) => self.flatten_block(self.get_block(*block_id)),
Expr::Table(headers, cells) => {
let mut output = vec![];
for e in headers {
output.extend(self.flatten_expression(e));
}
for row in cells {
for expr in row {
output.extend(self.flatten_expression(expr));
}
}
output
}
Expr::Var(_) => {
vec![(expr.span, FlatShape::Variable)]
}
}
}
pub fn flatten_pipeline(&self, pipeline: &Pipeline) -> Vec<(Span, FlatShape)> {
let mut output = vec![];
for expr in &pipeline.expressions {
output.extend(self.flatten_expression(expr))
}
output
pub fn flatten_statement(
working_set: &StateWorkingSet,
stmt: &Statement,
) -> Vec<(Span, FlatShape)> {
match stmt {
Statement::Pipeline(pipeline) => flatten_pipeline(working_set, pipeline),
_ => vec![],
}
}
pub fn flatten_expression(
working_set: &StateWorkingSet,
expr: &Expression,
) -> Vec<(Span, FlatShape)> {
match &expr.expr {
Expr::BinaryOp(lhs, op, rhs) => {
let mut output = vec![];
output.extend(flatten_expression(working_set, lhs));
output.extend(flatten_expression(working_set, op));
output.extend(flatten_expression(working_set, rhs));
output
}
Expr::Block(block_id) => flatten_block(working_set, working_set.get_block(*block_id)),
Expr::Call(call) => {
let mut output = vec![(call.head, FlatShape::InternalCall)];
for positional in &call.positional {
output.extend(flatten_expression(working_set, positional));
}
output
}
Expr::ExternalCall(..) => {
vec![(expr.span, FlatShape::External)]
}
Expr::Garbage => {
vec![(expr.span, FlatShape::Garbage)]
}
Expr::Int(_) => {
vec![(expr.span, FlatShape::Int)]
}
Expr::Float(_) => {
vec![(expr.span, FlatShape::Float)]
}
Expr::Bool(_) => {
vec![(expr.span, FlatShape::Bool)]
}
Expr::List(list) => {
let mut output = vec![];
for l in list {
output.extend(flatten_expression(working_set, l));
}
output
}
Expr::Keyword(_, span, expr) => {
let mut output = vec![(*span, FlatShape::Operator)];
output.extend(flatten_expression(working_set, expr));
output
}
Expr::Operator(_) => {
vec![(expr.span, FlatShape::Operator)]
}
Expr::Signature(_) => {
vec![(expr.span, FlatShape::Signature)]
}
Expr::String(_) => {
vec![(expr.span, FlatShape::String)]
}
Expr::Subexpression(block_id) => {
flatten_block(working_set, working_set.get_block(*block_id))
}
Expr::Table(headers, cells) => {
let mut output = vec![];
for e in headers {
output.extend(flatten_expression(working_set, e));
}
for row in cells {
for expr in row {
output.extend(flatten_expression(working_set, expr));
}
}
output
}
Expr::Var(_) => {
vec![(expr.span, FlatShape::Variable)]
}
}
}
pub fn flatten_pipeline(
working_set: &StateWorkingSet,
pipeline: &Pipeline,
) -> Vec<(Span, FlatShape)> {
let mut output = vec![];
for expr in &pipeline.expressions {
output.extend(flatten_expression(working_set, expr))
}
output
}

View File

@ -1,4 +1,5 @@
use crate::{ParseError, Span};
use crate::ParseError;
use nu_protocol::Span;
#[derive(Debug, PartialEq, Eq)]
pub enum TokenContents {

View File

@ -1,23 +1,12 @@
mod declaration;
mod errors;
mod flatten;
mod lex;
mod lite_parse;
mod parser;
mod parser_state;
mod signature;
mod span;
mod type_check;
pub use declaration::Declaration;
pub use errors::ParseError;
pub use flatten::FlatShape;
pub use flatten::{flatten_block, FlatShape};
pub use lex::{lex, Token, TokenContents};
pub use lite_parse::{lite_parse, LiteBlock};
pub use parser::{
span, Block, Call, Expr, Expression, Import, Operator, Pipeline, Statement, SyntaxShape,
VarDecl,
};
pub use parser_state::{BlockId, DeclId, ParserDelta, ParserState, ParserWorkingSet, Type, VarId};
pub use signature::{Flag, PositionalArg, Signature};
pub use span::Span;
pub use parser::{parse_file, parse_source, Import, VarDecl};

View File

@ -1,4 +1,5 @@
use crate::{ParseError, Span, Token, TokenContents};
use crate::{ParseError, Token, TokenContents};
use nu_protocol::Span;
#[derive(Debug)]
pub struct LiteCommand {

File diff suppressed because it is too large Load Diff

View File

@ -1,250 +1,50 @@
use crate::{parser::Operator, parser_state::Type, Expr, Expression, ParseError, ParserWorkingSet};
use crate::ParseError;
use nu_protocol::{
ast::{Expr, Expression, Operator},
engine::StateWorkingSet,
Type,
};
impl<'a> ParserWorkingSet<'a> {
pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool {
match (lhs, rhs) {
(Type::List(c), Type::List(d)) => ParserWorkingSet::type_compatible(c, d),
(Type::Unknown, _) => true,
(_, Type::Unknown) => true,
(lhs, rhs) => lhs == rhs,
}
pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool {
match (lhs, rhs) {
(Type::List(c), Type::List(d)) => type_compatible(c, d),
(Type::Unknown, _) => true,
(_, Type::Unknown) => true,
(lhs, rhs) => lhs == rhs,
}
}
pub fn math_result_type(
&self,
lhs: &mut Expression,
op: &mut Expression,
rhs: &mut Expression,
) -> (Type, Option<ParseError>) {
match &op.expr {
Expr::Operator(operator) => match operator {
Operator::Plus => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Int, None),
(Type::Float, Type::Int) => (Type::Float, None),
(Type::Int, Type::Float) => (Type::Float, None),
(Type::Float, Type::Float) => (Type::Float, None),
(Type::String, Type::String) => (Type::String, None),
(Type::Unknown, _) => (Type::Unknown, None),
(_, Type::Unknown) => (Type::Unknown, None),
(Type::Int, _) => {
*rhs = Expression::garbage(rhs.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::Minus => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Int, None),
(Type::Float, Type::Int) => (Type::Float, None),
(Type::Int, Type::Float) => (Type::Float, None),
(Type::Float, Type::Float) => (Type::Float, None),
(Type::Unknown, _) => (Type::Unknown, None),
(_, Type::Unknown) => (Type::Unknown, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::Multiply => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Int, None),
(Type::Float, Type::Int) => (Type::Float, None),
(Type::Int, Type::Float) => (Type::Float, None),
(Type::Float, Type::Float) => (Type::Float, None),
(Type::Unknown, _) => (Type::Unknown, None),
(_, Type::Unknown) => (Type::Unknown, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::Divide => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Int, None),
(Type::Float, Type::Int) => (Type::Float, None),
(Type::Int, Type::Float) => (Type::Float, None),
(Type::Float, Type::Float) => (Type::Float, None),
(Type::Unknown, _) => (Type::Unknown, None),
(_, Type::Unknown) => (Type::Unknown, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::LessThan => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Bool, None),
(Type::Float, Type::Int) => (Type::Bool, None),
(Type::Int, Type::Float) => (Type::Bool, None),
(Type::Float, Type::Float) => (Type::Bool, None),
(Type::Unknown, _) => (Type::Bool, None),
(_, Type::Unknown) => (Type::Bool, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::LessThanOrEqual => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Bool, None),
(Type::Float, Type::Int) => (Type::Bool, None),
(Type::Int, Type::Float) => (Type::Bool, None),
(Type::Float, Type::Float) => (Type::Bool, None),
(Type::Unknown, _) => (Type::Bool, None),
(_, Type::Unknown) => (Type::Bool, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::GreaterThan => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Bool, None),
(Type::Float, Type::Int) => (Type::Bool, None),
(Type::Int, Type::Float) => (Type::Bool, None),
(Type::Float, Type::Float) => (Type::Bool, None),
(Type::Unknown, _) => (Type::Bool, None),
(_, Type::Unknown) => (Type::Bool, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::GreaterThanOrEqual => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Bool, None),
(Type::Float, Type::Int) => (Type::Bool, None),
(Type::Int, Type::Float) => (Type::Bool, None),
(Type::Float, Type::Float) => (Type::Bool, None),
(Type::Unknown, _) => (Type::Bool, None),
(_, Type::Unknown) => (Type::Bool, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::Equal => match (&lhs.ty, &rhs.ty) {
(Type::Float, Type::Int) => (Type::Bool, None),
(Type::Int, Type::Float) => (Type::Bool, None),
(x, y) if x == y => (Type::Bool, None),
(Type::Unknown, _) => (Type::Bool, None),
(_, Type::Unknown) => (Type::Bool, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::NotEqual => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Bool, None),
(Type::Float, Type::Int) => (Type::Bool, None),
(Type::Int, Type::Float) => (Type::Bool, None),
(Type::Float, Type::Float) => (Type::Bool, None),
(Type::Unknown, _) => (Type::Bool, None),
(_, Type::Unknown) => (Type::Bool, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
pub fn math_result_type(
working_set: &StateWorkingSet,
lhs: &mut Expression,
op: &mut Expression,
rhs: &mut Expression,
) -> (Type, Option<ParseError>) {
match &op.expr {
Expr::Operator(operator) => match operator {
Operator::Plus => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Int, None),
(Type::Float, Type::Int) => (Type::Float, None),
(Type::Int, Type::Float) => (Type::Float, None),
(Type::Float, Type::Float) => (Type::Float, None),
(Type::String, Type::String) => (Type::String, None),
(Type::Unknown, _) => (Type::Unknown, None),
(_, Type::Unknown) => (Type::Unknown, None),
(Type::Int, _) => {
*rhs = Expression::garbage(rhs.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
@ -257,14 +57,217 @@ impl<'a> ParserWorkingSet<'a> {
)
}
},
Operator::Minus => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Int, None),
(Type::Float, Type::Int) => (Type::Float, None),
(Type::Int, Type::Float) => (Type::Float, None),
(Type::Float, Type::Float) => (Type::Float, None),
(Type::Unknown, _) => (Type::Unknown, None),
(_, Type::Unknown) => (Type::Unknown, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::Multiply => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Int, None),
(Type::Float, Type::Int) => (Type::Float, None),
(Type::Int, Type::Float) => (Type::Float, None),
(Type::Float, Type::Float) => (Type::Float, None),
(Type::Unknown, _) => (Type::Unknown, None),
(_, Type::Unknown) => (Type::Unknown, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::Divide => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Int, None),
(Type::Float, Type::Int) => (Type::Float, None),
(Type::Int, Type::Float) => (Type::Float, None),
(Type::Float, Type::Float) => (Type::Float, None),
(Type::Unknown, _) => (Type::Unknown, None),
(_, Type::Unknown) => (Type::Unknown, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::LessThan => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Bool, None),
(Type::Float, Type::Int) => (Type::Bool, None),
(Type::Int, Type::Float) => (Type::Bool, None),
(Type::Float, Type::Float) => (Type::Bool, None),
(Type::Unknown, _) => (Type::Bool, None),
(_, Type::Unknown) => (Type::Bool, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::LessThanOrEqual => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Bool, None),
(Type::Float, Type::Int) => (Type::Bool, None),
(Type::Int, Type::Float) => (Type::Bool, None),
(Type::Float, Type::Float) => (Type::Bool, None),
(Type::Unknown, _) => (Type::Bool, None),
(_, Type::Unknown) => (Type::Bool, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::GreaterThan => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Bool, None),
(Type::Float, Type::Int) => (Type::Bool, None),
(Type::Int, Type::Float) => (Type::Bool, None),
(Type::Float, Type::Float) => (Type::Bool, None),
(Type::Unknown, _) => (Type::Bool, None),
(_, Type::Unknown) => (Type::Bool, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::GreaterThanOrEqual => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Bool, None),
(Type::Float, Type::Int) => (Type::Bool, None),
(Type::Int, Type::Float) => (Type::Bool, None),
(Type::Float, Type::Float) => (Type::Bool, None),
(Type::Unknown, _) => (Type::Bool, None),
(_, Type::Unknown) => (Type::Bool, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::Equal => match (&lhs.ty, &rhs.ty) {
(Type::Float, Type::Int) => (Type::Bool, None),
(Type::Int, Type::Float) => (Type::Bool, None),
(x, y) if x == y => (Type::Bool, None),
(Type::Unknown, _) => (Type::Bool, None),
(_, Type::Unknown) => (Type::Bool, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
Operator::NotEqual => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Bool, None),
(Type::Float, Type::Int) => (Type::Bool, None),
(Type::Int, Type::Float) => (Type::Bool, None),
(Type::Float, Type::Float) => (Type::Bool, None),
(Type::Unknown, _) => (Type::Bool, None),
(_, Type::Unknown) => (Type::Bool, None),
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::IncompleteMathExpression(op.span)),
Some(ParseError::UnsupportedOperation(
op.span,
lhs.span,
lhs.ty.clone(),
rhs.span,
rhs.ty.clone(),
)),
)
}
},
_ => {
*op = Expression::garbage(op.span);
(
Type::Unknown,
Some(ParseError::IncompleteMathExpression(op.span)),
)
}
}
}

View File

@ -1,4 +1,5 @@
use nu_parser::{lex, ParseError, Span, Token, TokenContents};
use nu_parser::{lex, ParseError, Token, TokenContents};
use nu_protocol::Span;
#[test]
fn lex_basic() {

View File

@ -1,4 +1,5 @@
use nu_parser::{lex, lite_parse, LiteBlock, ParseError, Span};
use nu_parser::{lex, lite_parse, LiteBlock, ParseError};
use nu_protocol::Span;
fn lite_parse_helper(input: &[u8]) -> Result<LiteBlock, ParseError> {
let (output, err) = lex(input, 0, &[], &[]);

View File

@ -1,43 +1,59 @@
use nu_parser::ParseError;
use nu_parser::*;
use nu_parser::{ParseError, ParserState, Signature};
use nu_protocol::{
ast::{Expr, Expression, Pipeline, Statement},
engine::{EngineState, StateWorkingSet},
Signature, SyntaxShape,
};
#[test]
pub fn parse_int() {
let parser_state = ParserState::new();
let mut working_set = ParserWorkingSet::new(&parser_state);
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let (block, err) = working_set.parse_source(b"3", true);
let (block, err) = parse_source(&mut working_set, b"3", true);
assert!(err.is_none());
assert!(block.len() == 1);
assert!(matches!(
block[0],
Statement::Expression(Expression {
expr: Expr::Int(3),
..
})
));
match &block[0] {
Statement::Pipeline(Pipeline { expressions }) => {
assert!(expressions.len() == 1);
assert!(matches!(
expressions[0],
Expression {
expr: Expr::Int(3),
..
}
))
}
_ => panic!("No match"),
}
}
#[test]
pub fn parse_call() {
let parser_state = ParserState::new();
let mut working_set = ParserWorkingSet::new(&parser_state);
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let sig = Signature::build("foo").named("--jazz", SyntaxShape::Int, "jazz!!", Some('j'));
working_set.add_decl(sig.into());
working_set.add_decl(sig.predeclare());
let (block, err) = working_set.parse_source(b"foo", true);
let (block, err) = parse_source(&mut working_set, b"foo", true);
assert!(err.is_none());
assert!(block.len() == 1);
match &block[0] {
Statement::Expression(Expression {
expr: Expr::Call(call),
..
}) => {
assert_eq!(call.decl_id, 0);
Statement::Pipeline(Pipeline { expressions }) => {
assert_eq!(expressions.len(), 1);
if let Expression {
expr: Expr::Call(call),
..
} = &expressions[0]
{
assert_eq!(call.decl_id, 0);
}
}
_ => panic!("not a call"),
}
@ -45,38 +61,38 @@ pub fn parse_call() {
#[test]
pub fn parse_call_missing_flag_arg() {
let parser_state = ParserState::new();
let mut working_set = ParserWorkingSet::new(&parser_state);
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let sig = Signature::build("foo").named("--jazz", SyntaxShape::Int, "jazz!!", Some('j'));
working_set.add_decl(sig.into());
working_set.add_decl(sig.predeclare());
let (_, err) = working_set.parse_source(b"foo --jazz", true);
let (_, err) = parse_source(&mut working_set, b"foo --jazz", true);
assert!(matches!(err, Some(ParseError::MissingFlagParam(..))));
}
#[test]
pub fn parse_call_missing_short_flag_arg() {
let parser_state = ParserState::new();
let mut working_set = ParserWorkingSet::new(&parser_state);
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let sig = Signature::build("foo").named("--jazz", SyntaxShape::Int, "jazz!!", Some('j'));
working_set.add_decl(sig.into());
working_set.add_decl(sig.predeclare());
let (_, err) = working_set.parse_source(b"foo -j", true);
let (_, err) = parse_source(&mut working_set, b"foo -j", true);
assert!(matches!(err, Some(ParseError::MissingFlagParam(..))));
}
#[test]
pub fn parse_call_too_many_shortflag_args() {
let parser_state = ParserState::new();
let mut working_set = ParserWorkingSet::new(&parser_state);
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let sig = Signature::build("foo")
.named("--jazz", SyntaxShape::Int, "jazz!!", Some('j'))
.named("--math", SyntaxShape::Int, "math!!", Some('m'));
working_set.add_decl(sig.into());
let (_, err) = working_set.parse_source(b"foo -mj", true);
working_set.add_decl(sig.predeclare());
let (_, err) = parse_source(&mut working_set, b"foo -mj", true);
assert!(matches!(
err,
Some(ParseError::ShortFlagBatchCantTakeArg(..))
@ -85,44 +101,44 @@ pub fn parse_call_too_many_shortflag_args() {
#[test]
pub fn parse_call_unknown_shorthand() {
let parser_state = ParserState::new();
let mut working_set = ParserWorkingSet::new(&parser_state);
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let sig = Signature::build("foo").switch("--jazz", "jazz!!", Some('j'));
working_set.add_decl(sig.into());
let (_, err) = working_set.parse_source(b"foo -mj", true);
working_set.add_decl(sig.predeclare());
let (_, err) = parse_source(&mut working_set, b"foo -mj", true);
assert!(matches!(err, Some(ParseError::UnknownFlag(..))));
}
#[test]
pub fn parse_call_extra_positional() {
let parser_state = ParserState::new();
let mut working_set = ParserWorkingSet::new(&parser_state);
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let sig = Signature::build("foo").switch("--jazz", "jazz!!", Some('j'));
working_set.add_decl(sig.into());
let (_, err) = working_set.parse_source(b"foo -j 100", true);
working_set.add_decl(sig.predeclare());
let (_, err) = parse_source(&mut working_set, b"foo -j 100", true);
assert!(matches!(err, Some(ParseError::ExtraPositional(..))));
}
#[test]
pub fn parse_call_missing_req_positional() {
let parser_state = ParserState::new();
let mut working_set = ParserWorkingSet::new(&parser_state);
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let sig = Signature::build("foo").required("jazz", SyntaxShape::Int, "jazz!!");
working_set.add_decl(sig.into());
let (_, err) = working_set.parse_source(b"foo", true);
working_set.add_decl(sig.predeclare());
let (_, err) = parse_source(&mut working_set, b"foo", true);
assert!(matches!(err, Some(ParseError::MissingPositional(..))));
}
#[test]
pub fn parse_call_missing_req_flag() {
let parser_state = ParserState::new();
let mut working_set = ParserWorkingSet::new(&parser_state);
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let sig = Signature::build("foo").required_named("--jazz", SyntaxShape::Int, "jazz!!", None);
working_set.add_decl(sig.into());
let (_, err) = working_set.parse_source(b"foo", true);
working_set.add_decl(sig.predeclare());
let (_, err) = parse_source(&mut working_set, b"foo", true);
assert!(matches!(err, Some(ParseError::MissingRequiredFlag(..))));
}

View File

@ -0,0 +1,9 @@
[package]
name = "nu-protocol"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
codespan-reporting = "0.11.1"

View File

@ -0,0 +1,3 @@
# nu-protocol
The nu-protocol crate holds the definitions of structs/traits that are used throughout Nushell. This gives us one way to expose them to many other crates, as well as make these definitions available to each other, without causing mutually recursive dependencies.

View File

@ -0,0 +1,44 @@
use std::ops::{Index, IndexMut};
use super::Statement;
#[derive(Debug, Clone)]
pub struct Block {
pub stmts: Vec<Statement>,
}
impl Block {
pub fn len(&self) -> usize {
self.stmts.len()
}
pub fn is_empty(&self) -> bool {
self.stmts.is_empty()
}
}
impl Index<usize> for Block {
type Output = Statement;
fn index(&self, index: usize) -> &Self::Output {
&self.stmts[index]
}
}
impl IndexMut<usize> for Block {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.stmts[index]
}
}
impl Default for Block {
fn default() -> Self {
Self::new()
}
}
impl Block {
pub fn new() -> Self {
Self { stmts: vec![] }
}
}

View File

@ -0,0 +1,28 @@
use super::Expression;
use crate::{DeclId, Span};
#[derive(Debug, Clone)]
pub struct Call {
/// identifier of the declaration to call
pub decl_id: DeclId,
pub head: Span,
pub positional: Vec<Expression>,
pub named: Vec<(String, Option<Expression>)>,
}
impl Default for Call {
fn default() -> Self {
Self::new()
}
}
impl Call {
pub fn new() -> Call {
Self {
decl_id: 0,
head: Span::unknown(),
positional: vec![],
named: vec![],
}
}
}

View File

@ -0,0 +1,22 @@
use super::{Call, Expression, Operator};
use crate::{BlockId, Signature, Span, VarId};
#[derive(Debug, Clone)]
pub enum Expr {
Bool(bool),
Int(i64),
Float(f64),
Var(VarId),
Call(Box<Call>),
ExternalCall(Vec<u8>, Vec<Vec<u8>>),
Operator(Operator),
BinaryOp(Box<Expression>, Box<Expression>, Box<Expression>), //lhs, op, rhs
Subexpression(BlockId),
Block(BlockId),
List(Vec<Expression>),
Table(Vec<Expression>, Vec<Vec<Expression>>),
Keyword(Vec<u8>, Span, Box<Expression>),
String(String), // FIXME: improve this in the future?
Signature(Box<Signature>),
Garbage,
}

View File

@ -0,0 +1,86 @@
use super::{Expr, Operator};
use crate::{BlockId, Signature, Span, Type, VarId};
#[derive(Debug, Clone)]
pub struct Expression {
pub expr: Expr,
pub span: Span,
pub ty: Type,
}
impl Expression {
pub fn garbage(span: Span) -> Expression {
Expression {
expr: Expr::Garbage,
span,
ty: Type::Unknown,
}
}
pub fn precedence(&self) -> usize {
match &self.expr {
Expr::Operator(operator) => {
// Higher precedence binds tighter
match operator {
Operator::Pow => 100,
Operator::Multiply | Operator::Divide | Operator::Modulo => 95,
Operator::Plus | Operator::Minus => 90,
Operator::NotContains
| Operator::Contains
| Operator::LessThan
| Operator::LessThanOrEqual
| Operator::GreaterThan
| Operator::GreaterThanOrEqual
| Operator::Equal
| Operator::NotEqual
| Operator::In
| Operator::NotIn => 80,
Operator::And => 50,
Operator::Or => 40, // TODO: should we have And and Or be different precedence?
}
}
_ => 0,
}
}
pub fn as_block(&self) -> Option<BlockId> {
match self.expr {
Expr::Block(block_id) => Some(block_id),
_ => None,
}
}
pub fn as_signature(&self) -> Option<Box<Signature>> {
match &self.expr {
Expr::Signature(sig) => Some(sig.clone()),
_ => None,
}
}
pub fn as_list(&self) -> Option<Vec<Expression>> {
match &self.expr {
Expr::List(list) => Some(list.clone()),
_ => None,
}
}
pub fn as_keyword(&self) -> Option<&Expression> {
match &self.expr {
Expr::Keyword(_, _, expr) => Some(expr),
_ => None,
}
}
pub fn as_var(&self) -> Option<VarId> {
match self.expr {
Expr::Var(var_id) => Some(var_id),
_ => None,
}
}
pub fn as_string(&self) -> Option<String> {
match &self.expr {
Expr::String(string) => Some(string.clone()),
_ => None,
}
}
}

View File

@ -0,0 +1,15 @@
mod block;
mod call;
mod expr;
mod expression;
mod operator;
mod pipeline;
mod statement;
pub use block::*;
pub use call::*;
pub use expr::*;
pub use expression::*;
pub use operator::*;
pub use pipeline::*;
pub use statement::*;

View File

@ -0,0 +1,48 @@
use std::fmt::Display;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Operator {
Equal,
NotEqual,
LessThan,
GreaterThan,
LessThanOrEqual,
GreaterThanOrEqual,
Contains,
NotContains,
Plus,
Minus,
Multiply,
Divide,
In,
NotIn,
Modulo,
And,
Or,
Pow,
}
impl Display for Operator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Operator::Equal => write!(f, "=="),
Operator::NotEqual => write!(f, "!="),
Operator::LessThan => write!(f, "<"),
Operator::GreaterThan => write!(f, ">"),
Operator::Contains => write!(f, "=~"),
Operator::NotContains => write!(f, "!~"),
Operator::Plus => write!(f, "+"),
Operator::Minus => write!(f, "-"),
Operator::Multiply => write!(f, "*"),
Operator::Divide => write!(f, "/"),
Operator::In => write!(f, "in"),
Operator::NotIn => write!(f, "not-in"),
Operator::Modulo => write!(f, "mod"),
Operator::And => write!(f, "&&"),
Operator::Or => write!(f, "||"),
Operator::Pow => write!(f, "**"),
Operator::LessThanOrEqual => write!(f, "<="),
Operator::GreaterThanOrEqual => write!(f, ">="),
}
}
}

View File

@ -0,0 +1,24 @@
use crate::ast::Expression;
#[derive(Debug, Clone)]
pub struct Pipeline {
pub expressions: Vec<Expression>,
}
impl Default for Pipeline {
fn default() -> Self {
Self::new()
}
}
impl Pipeline {
pub fn new() -> Self {
Self {
expressions: vec![],
}
}
pub fn from_vec(expressions: Vec<Expression>) -> Pipeline {
Self { expressions }
}
}

View File

@ -0,0 +1,8 @@
use super::Pipeline;
use crate::DeclId;
#[derive(Debug, Clone)]
pub enum Statement {
Declaration(DeclId),
Pipeline(Pipeline),
}

View File

@ -0,0 +1,8 @@
use crate::ast::Call;
use crate::Span;
#[derive(Debug, Clone)]
pub struct UnevaluatedCallInfo {
pub args: Call,
pub name_span: Span,
}

View File

@ -0,0 +1,57 @@
use crate::{ast::Call, BlockId, Example, ShellError, Signature, Value};
use super::EvaluationContext;
pub trait Command {
fn name(&self) -> &str;
fn signature(&self) -> Signature {
Signature::new(self.name()).desc(self.usage()).filter()
}
fn usage(&self) -> &str;
fn extra_usage(&self) -> &str {
""
}
fn run(
&self,
context: &EvaluationContext,
call: &Call,
input: Value,
) -> Result<Value, ShellError>;
fn is_binary(&self) -> bool {
false
}
// Commands that are not meant to be run by users
fn is_private(&self) -> bool {
false
}
fn examples(&self) -> Vec<Example> {
Vec::new()
}
// This is a built-in command
fn is_builtin(&self) -> bool {
true
}
// Is a sub command
fn is_sub(&self) -> bool {
self.name().contains(' ')
}
// Is a plugin command
fn is_plugin(&self) -> bool {
false
}
// If command is a custom command i.e. def blah [] { }, get the block id
fn get_custom_command(&self) -> Option<BlockId> {
None
}
}

View File

@ -1,60 +1,17 @@
use crate::{parser::Block, Declaration, Span};
use super::Command;
use crate::{ast::Block, BlockId, DeclId, Span, Type, VarId};
use core::panic;
use std::{collections::HashMap, fmt::Display, slice::Iter};
use std::{collections::HashMap, ops::Range, slice::Iter};
#[derive(Debug)]
pub struct ParserState {
pub struct EngineState {
files: Vec<(String, usize, usize)>,
file_contents: Vec<u8>,
vars: Vec<Type>,
decls: Vec<Declaration>,
decls: Vec<Box<dyn Command>>,
blocks: Vec<Block>,
scope: Vec<ScopeFrame>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Type {
Int,
Float,
Bool,
String,
Block,
ColumnPath,
Duration,
FilePath,
Filesize,
List(Box<Type>),
Number,
Nothing,
Table,
Unknown,
}
impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Block => write!(f, "block"),
Type::Bool => write!(f, "bool"),
Type::ColumnPath => write!(f, "column path"),
Type::Duration => write!(f, "duration"),
Type::FilePath => write!(f, "filepath"),
Type::Filesize => write!(f, "filesize"),
Type::Float => write!(f, "float"),
Type::Int => write!(f, "int"),
Type::List(l) => write!(f, "list<{}>", l),
Type::Nothing => write!(f, "nothing"),
Type::Number => write!(f, "number"),
Type::String => write!(f, "string"),
Type::Table => write!(f, "table"),
Type::Unknown => write!(f, "unknown"),
}
}
}
pub type VarId = usize;
pub type DeclId = usize;
pub type BlockId = usize;
#[derive(Debug)]
struct ScopeFrame {
vars: HashMap<Vec<u8>, VarId>,
@ -72,13 +29,13 @@ impl ScopeFrame {
}
}
impl Default for ParserState {
impl Default for EngineState {
fn default() -> Self {
Self::new()
}
}
impl ParserState {
impl EngineState {
pub fn new() -> Self {
Self {
files: vec![],
@ -90,7 +47,7 @@ impl ParserState {
}
}
pub fn merge_delta(this: &mut ParserState, mut delta: ParserDelta) {
pub fn merge_delta(this: &mut EngineState, mut delta: StateDelta) {
// Take the mutable reference and extend the permanent state from the working set
this.files.extend(delta.files);
this.file_contents.extend(delta.file_contents);
@ -136,7 +93,7 @@ impl ParserState {
pub fn print_decls(&self) {
for decl in self.decls.iter().enumerate() {
println!("decl{}: {:?}", decl.0, decl.1);
println!("decl{}: {:?}", decl.0, decl.1.signature());
}
}
@ -162,7 +119,7 @@ impl ParserState {
.expect("internal error: missing variable")
}
pub fn get_decl(&self, decl_id: DeclId) -> &Declaration {
pub fn get_decl(&self, decl_id: DeclId) -> &Box<dyn Command> {
self.decls
.get(decl_id)
.expect("internal error: missing declaration")
@ -219,23 +176,21 @@ impl ParserState {
}
}
#[derive(Debug)]
pub struct ParserWorkingSet<'a> {
permanent_state: &'a ParserState,
pub delta: ParserDelta,
pub struct StateWorkingSet<'a> {
pub permanent_state: &'a EngineState,
pub delta: StateDelta,
}
#[derive(Debug)]
pub struct ParserDelta {
pub struct StateDelta {
files: Vec<(String, usize, usize)>,
pub(crate) file_contents: Vec<u8>,
vars: Vec<Type>, // indexed by VarId
decls: Vec<Declaration>, // indexed by DeclId
blocks: Vec<Block>, // indexed by BlockId
vars: Vec<Type>, // indexed by VarId
decls: Vec<Box<dyn Command>>, // indexed by DeclId
blocks: Vec<Block>, // indexed by BlockId
scope: Vec<ScopeFrame>,
}
impl ParserDelta {
impl StateDelta {
pub fn num_files(&self) -> usize {
self.files.len()
}
@ -257,10 +212,10 @@ impl ParserDelta {
}
}
impl<'a> ParserWorkingSet<'a> {
pub fn new(permanent_state: &'a ParserState) -> Self {
impl<'a> StateWorkingSet<'a> {
pub fn new(permanent_state: &'a EngineState) -> Self {
Self {
delta: ParserDelta {
delta: StateDelta {
files: vec![],
file_contents: vec![],
vars: vec![],
@ -284,8 +239,8 @@ impl<'a> ParserWorkingSet<'a> {
self.delta.num_blocks() + self.permanent_state.num_blocks()
}
pub fn add_decl(&mut self, decl: Declaration) -> DeclId {
let name = decl.signature.name.as_bytes().to_vec();
pub fn add_decl(&mut self, decl: Box<dyn Command>) -> DeclId {
let name = decl.name().as_bytes().to_vec();
self.delta.decls.push(decl);
let decl_id = self.num_decls() - 1;
@ -391,10 +346,10 @@ impl<'a> ParserWorkingSet<'a> {
None
}
pub fn update_decl(&mut self, decl_id: usize, block: Option<BlockId>) {
let decl = self.get_decl_mut(decl_id);
decl.body = block;
}
// pub fn update_decl(&mut self, decl_id: usize, block: Option<BlockId>) {
// let decl = self.get_decl_mut(decl_id);
// decl.body = block;
// }
pub fn contains_decl_partial_match(&self, name: &[u8]) -> bool {
for scope in self.delta.scope.iter().rev() {
@ -505,7 +460,7 @@ impl<'a> ParserWorkingSet<'a> {
}
}
pub fn get_decl(&self, decl_id: DeclId) -> &Declaration {
pub fn get_decl(&self, decl_id: DeclId) -> &Box<dyn Command> {
let num_permanent_decls = self.permanent_state.num_decls();
if decl_id < num_permanent_decls {
self.permanent_state.get_decl(decl_id)
@ -517,7 +472,7 @@ impl<'a> ParserWorkingSet<'a> {
}
}
pub fn get_decl_mut(&mut self, decl_id: DeclId) -> &mut Declaration {
pub fn get_decl_mut(&mut self, decl_id: DeclId) -> &mut Box<dyn Command> {
let num_permanent_decls = self.permanent_state.num_decls();
if decl_id < num_permanent_decls {
panic!("internal error: can only mutate declarations in working set")
@ -541,30 +496,122 @@ impl<'a> ParserWorkingSet<'a> {
}
}
pub fn render(self) -> ParserDelta {
pub fn render(self) -> StateDelta {
self.delta
}
}
impl<'a> codespan_reporting::files::Files<'a> for StateWorkingSet<'a> {
type FileId = usize;
type Name = String;
type Source = String;
fn name(&'a self, id: Self::FileId) -> Result<Self::Name, codespan_reporting::files::Error> {
Ok(self.get_filename(id))
}
fn source(
&'a self,
id: Self::FileId,
) -> Result<Self::Source, codespan_reporting::files::Error> {
Ok(self.get_file_source(id))
}
fn line_index(
&'a self,
id: Self::FileId,
byte_index: usize,
) -> Result<usize, codespan_reporting::files::Error> {
let source = self.get_file_source(id);
let mut count = 0;
for byte in source.bytes().enumerate() {
if byte.0 == byte_index {
// println!("count: {} for file: {} index: {}", count, id, byte_index);
return Ok(count);
}
if byte.1 == b'\n' {
count += 1;
}
}
// println!("count: {} for file: {} index: {}", count, id, byte_index);
Ok(count)
}
fn line_range(
&'a self,
id: Self::FileId,
line_index: usize,
) -> Result<Range<usize>, codespan_reporting::files::Error> {
let source = self.get_file_source(id);
let mut count = 0;
let mut start = Some(0);
let mut end = None;
for byte in source.bytes().enumerate() {
#[allow(clippy::comparison_chain)]
if count > line_index {
let start = start.expect("internal error: couldn't find line");
let end = end.expect("internal error: couldn't find line");
// println!(
// "Span: {}..{} for fileid: {} index: {}",
// start, end, id, line_index
// );
return Ok(start..end);
} else if count == line_index {
end = Some(byte.0 + 1);
}
#[allow(clippy::comparison_chain)]
if byte.1 == b'\n' {
count += 1;
if count > line_index {
break;
} else if count == line_index {
start = Some(byte.0 + 1);
}
}
}
match (start, end) {
(Some(start), Some(end)) => {
// println!(
// "Span: {}..{} for fileid: {} index: {}",
// start, end, id, line_index
// );
Ok(start..end)
}
_ => Err(codespan_reporting::files::Error::FileMissing),
}
}
}
#[cfg(test)]
mod parser_state_tests {
mod engine_state_tests {
use super::*;
#[test]
fn add_file_gives_id() {
let parser_state = ParserState::new();
let mut parser_state = ParserWorkingSet::new(&parser_state);
let id = parser_state.add_file("test.nu".into(), &[]);
let engine_state = EngineState::new();
let mut engine_state = StateWorkingSet::new(&engine_state);
let id = engine_state.add_file("test.nu".into(), &[]);
assert_eq!(id, 0);
}
#[test]
fn add_file_gives_id_including_parent() {
let mut parser_state = ParserState::new();
let parent_id = parser_state.add_file("test.nu".into(), vec![]);
let mut engine_state = EngineState::new();
let parent_id = engine_state.add_file("test.nu".into(), vec![]);
let mut working_set = ParserWorkingSet::new(&parser_state);
let mut working_set = StateWorkingSet::new(&engine_state);
let working_set_id = working_set.add_file("child.nu".into(), &[]);
assert_eq!(parent_id, 0);
@ -573,19 +620,19 @@ mod parser_state_tests {
#[test]
fn merge_states() {
let mut parser_state = ParserState::new();
parser_state.add_file("test.nu".into(), vec![]);
let mut engine_state = EngineState::new();
engine_state.add_file("test.nu".into(), vec![]);
let delta = {
let mut working_set = ParserWorkingSet::new(&parser_state);
let mut working_set = StateWorkingSet::new(&engine_state);
working_set.add_file("child.nu".into(), &[]);
working_set.render()
};
ParserState::merge_delta(&mut parser_state, delta);
EngineState::merge_delta(&mut engine_state, delta);
assert_eq!(parser_state.num_files(), 2);
assert_eq!(&parser_state.files[0].0, "test.nu");
assert_eq!(&parser_state.files[1].0, "child.nu");
assert_eq!(engine_state.num_files(), 2);
assert_eq!(&engine_state.files[0].0, "test.nu");
assert_eq!(&engine_state.files[1].0, "child.nu");
}
}

View File

@ -1,21 +1,22 @@
use nu_parser::{ParserState, VarId};
use super::EngineState;
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use crate::{value::Value, ShellError};
use crate::{ShellError, Value, VarId};
pub struct State {
pub parser_state: Rc<RefCell<ParserState>>,
#[derive(Clone)]
pub struct EvaluationContext {
pub engine_state: Rc<RefCell<EngineState>>,
pub stack: Stack,
}
impl State {
impl EvaluationContext {
pub fn get_var(&self, var_id: VarId) -> Result<Value, ShellError> {
self.stack.get_var(var_id)
}
pub fn enter_scope(&self) -> State {
pub fn enter_scope(&self) -> EvaluationContext {
Self {
parser_state: self.parser_state.clone(),
engine_state: self.engine_state.clone(),
stack: self.stack.clone().enter_scope(),
}
}

View File

@ -0,0 +1,9 @@
mod call_info;
mod command;
mod engine_state;
mod evaluation_context;
pub use call_info::*;
pub use command::*;
pub use engine_state::*;
pub use evaluation_context::*;

View File

@ -0,0 +1,7 @@
use crate::Value;
pub struct Example {
pub example: &'static str,
pub description: &'static str,
pub result: Option<Vec<Value>>,
}

View File

@ -0,0 +1,3 @@
pub type VarId = usize;
pub type DeclId = usize;
pub type BlockId = usize;

View File

@ -0,0 +1,19 @@
pub mod ast;
pub mod engine;
mod example;
mod id;
mod shell_error;
mod signature;
mod span;
mod syntax_shape;
mod ty;
mod value;
pub use example::*;
pub use id::*;
pub use shell_error::*;
pub use signature::*;
pub use span::*;
pub use syntax_shape::*;
pub use ty::*;
pub use value::*;

View File

@ -0,0 +1,17 @@
use crate::{Span, Type};
#[derive(Debug)]
pub enum ShellError {
OperatorMismatch {
op_span: Span,
lhs_ty: Type,
lhs_span: Span,
rhs_ty: Type,
rhs_span: Span,
},
Unsupported(Span),
InternalError(String),
VariableNotFound(Span),
CantConvert(String, Span),
DivisionByZero(Span),
}

View File

@ -1,4 +1,10 @@
use crate::{parser::SyntaxShape, Declaration, VarId};
use crate::ast::Call;
use crate::engine::Command;
use crate::engine::EvaluationContext;
use crate::BlockId;
use crate::SyntaxShape;
use crate::Value;
use crate::VarId;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Flag {
@ -285,22 +291,84 @@ impl Signature {
}
None
}
}
impl From<Box<Signature>> for Declaration {
fn from(val: Box<Signature>) -> Self {
Declaration {
signature: val,
body: None,
}
/// Set the filter flag for the signature
pub fn filter(mut self) -> Signature {
self.is_filter = true;
self
}
/// Create a placeholder implementation of Command as a way to predeclare a definition's
/// signature so other definitions can see it. This placeholder is later replaced with the
/// full definition in a second pass of the parser.
pub fn predeclare(self) -> Box<dyn Command> {
Box::new(Predeclaration { signature: self })
}
/// Combines a signature and a block into a runnable block
pub fn into_block_command(self, block_id: BlockId) -> Box<dyn Command> {
Box::new(BlockCommand {
signature: self,
block_id,
})
}
}
impl From<Signature> for Declaration {
fn from(val: Signature) -> Self {
Declaration {
signature: Box::new(val),
body: None,
}
struct Predeclaration {
signature: Signature,
}
impl Command for Predeclaration {
fn name(&self) -> &str {
&self.signature.name
}
fn signature(&self) -> Signature {
self.signature.clone()
}
fn usage(&self) -> &str {
&self.signature.usage
}
fn run(
&self,
_context: &EvaluationContext,
_call: &Call,
_input: Value,
) -> Result<crate::Value, crate::ShellError> {
panic!("Internal error: can't run a predeclaration without a body")
}
}
struct BlockCommand {
signature: Signature,
block_id: BlockId,
}
impl Command for BlockCommand {
fn name(&self) -> &str {
&self.signature.name
}
fn signature(&self) -> Signature {
self.signature.clone()
}
fn usage(&self) -> &str {
&self.signature.usage
}
fn run(
&self,
_context: &EvaluationContext,
_call: &Call,
_input: Value,
) -> Result<crate::Value, crate::ShellError> {
panic!("Internal error: can't run custom command with 'run', use block_id");
}
fn get_custom_command(&self) -> Option<BlockId> {
Some(self.block_id)
}
}

View File

@ -20,3 +20,18 @@ impl Span {
}
}
}
pub fn span(spans: &[Span]) -> Span {
let length = spans.len();
if length == 0 {
Span::unknown()
} else if length == 1 {
spans[0]
} else {
Span {
start: spans[0].start,
end: spans[length - 1].end,
}
}
}

View File

@ -0,0 +1,104 @@
use crate::Type;
/// The syntactic shapes that values must match to be passed into a command. You can think of this as the type-checking that occurs when you call a function.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxShape {
/// A specific match to a word or symbol
Keyword(Vec<u8>, Box<SyntaxShape>),
/// Any syntactic form is allowed
Any,
/// Strings and string-like bare words are allowed
String,
/// A dotted path to navigate the table
ColumnPath,
/// A dotted path to navigate the table (including variable)
FullColumnPath,
/// Only a numeric (integer or decimal) value is allowed
Number,
/// A range is allowed (eg, `1..3`)
Range,
/// Only an integer value is allowed
Int,
/// A filepath is allowed
FilePath,
/// A glob pattern is allowed, eg `foo*`
GlobPattern,
/// A block is allowed, eg `{start this thing}`
Block,
/// A table is allowed, eg `[[first, second]; [1, 2]]`
Table,
/// A table is allowed, eg `[first second]`
List(Box<SyntaxShape>),
/// A filesize value is allowed, eg `10kb`
Filesize,
/// A duration value is allowed, eg `19day`
Duration,
/// An operator
Operator,
/// A math expression which expands shorthand forms on the lefthand side, eg `foo > 1`
/// The shorthand allows us to more easily reach columns inside of the row being passed in
RowCondition,
/// A general math expression, eg `1 + 2`
MathExpression,
/// A variable name
Variable,
/// A variable with optional type, `x` or `x: int`
VarWithOptType,
/// A signature for a definition, `[x:int, --foo]`
Signature,
/// A general expression, eg `1 + 2` or `foo --bar`
Expression,
}
impl SyntaxShape {
pub fn to_type(&self) -> Type {
match self {
SyntaxShape::Any => Type::Unknown,
SyntaxShape::Block => Type::Block,
SyntaxShape::ColumnPath => Type::Unknown,
SyntaxShape::Duration => Type::Duration,
SyntaxShape::Expression => Type::Unknown,
SyntaxShape::FilePath => Type::FilePath,
SyntaxShape::Filesize => Type::Filesize,
SyntaxShape::FullColumnPath => Type::Unknown,
SyntaxShape::GlobPattern => Type::String,
SyntaxShape::Int => Type::Int,
SyntaxShape::List(x) => {
let contents = x.to_type();
Type::List(Box::new(contents))
}
SyntaxShape::Keyword(_, expr) => expr.to_type(),
SyntaxShape::MathExpression => Type::Unknown,
SyntaxShape::Number => Type::Number,
SyntaxShape::Operator => Type::Unknown,
SyntaxShape::Range => Type::Unknown,
SyntaxShape::RowCondition => Type::Bool,
SyntaxShape::Signature => Type::Unknown,
SyntaxShape::String => Type::String,
SyntaxShape::Table => Type::Table,
SyntaxShape::VarWithOptType => Type::Unknown,
SyntaxShape::Variable => Type::Unknown,
}
}
}

View File

@ -0,0 +1,44 @@
use std::fmt::Display;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Type {
Int,
Float,
Bool,
String,
Block,
ColumnPath,
Duration,
FilePath,
Filesize,
List(Box<Type>),
Number,
Nothing,
Table,
RowStream,
ValueStream,
Unknown,
}
impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Block => write!(f, "block"),
Type::Bool => write!(f, "bool"),
Type::ColumnPath => write!(f, "column path"),
Type::Duration => write!(f, "duration"),
Type::FilePath => write!(f, "filepath"),
Type::Filesize => write!(f, "filesize"),
Type::Float => write!(f, "float"),
Type::Int => write!(f, "int"),
Type::List(l) => write!(f, "list<{}>", l),
Type::Nothing => write!(f, "nothing"),
Type::Number => write!(f, "number"),
Type::String => write!(f, "string"),
Type::Table => write!(f, "table"),
Type::ValueStream => write!(f, "value stream"),
Type::RowStream => write!(f, "row stream"),
Type::Unknown => write!(f, "unknown"),
}
}
}

View File

@ -1,9 +1,112 @@
use std::fmt::Display;
use std::{cell::RefCell, fmt::Debug, rc::Rc};
use nu_parser::{BlockId, Span, Type};
use crate::{span, BlockId, Span, Type};
use crate::ShellError;
#[derive(Clone)]
pub struct ValueStream(pub Rc<RefCell<dyn Iterator<Item = Value>>>);
impl ValueStream {
pub fn into_string(self) -> String {
let val: Vec<Value> = self.collect();
format!(
"[{}]",
val.into_iter()
.map(|x| x.into_string())
.collect::<Vec<String>>()
.join(", ".into())
)
}
pub fn from_stream(input: impl Iterator<Item = Value> + 'static) -> ValueStream {
ValueStream(Rc::new(RefCell::new(input)))
}
}
impl Debug for ValueStream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ValueStream").finish()
}
}
impl Iterator for ValueStream {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
{
let mut iter = self.0.borrow_mut();
iter.next()
}
}
}
pub trait IntoValueStream {
fn into_value_stream(self) -> ValueStream;
}
impl<T> IntoValueStream for T
where
T: Iterator<Item = Value> + 'static,
{
fn into_value_stream(self) -> ValueStream {
ValueStream::from_stream(self)
}
}
#[derive(Clone)]
pub struct RowStream(Rc<RefCell<dyn Iterator<Item = Vec<Value>>>>);
impl RowStream {
pub fn into_string(self, headers: Vec<String>) -> String {
let val: Vec<Vec<Value>> = self.collect();
format!(
"[{}]\n[{}]",
headers
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(", ".into()),
val.into_iter()
.map(|x| {
x.into_iter()
.map(|x| x.into_string())
.collect::<Vec<String>>()
.join(", ".into())
})
.collect::<Vec<String>>()
.join("\n")
)
}
}
impl Debug for RowStream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ValueStream").finish()
}
}
impl Iterator for RowStream {
type Item = Vec<Value>;
fn next(&mut self) -> Option<Self::Item> {
{
let mut iter = self.0.borrow_mut();
iter.next()
}
}
}
pub trait IntoRowStream {
fn into_row_stream(self) -> RowStream;
}
impl IntoRowStream for Vec<Vec<Value>> {
fn into_row_stream(self) -> RowStream {
RowStream(Rc::new(RefCell::new(self.into_iter())))
}
}
#[derive(Debug, Clone)]
pub enum Value {
Bool {
@ -22,6 +125,15 @@ pub enum Value {
val: String,
span: Span,
},
ValueStream {
stream: ValueStream,
span: Span,
},
RowStream {
headers: Vec<String>,
stream: RowStream,
span: Span,
},
List {
val: Vec<Value>,
span: Span,
@ -57,6 +169,8 @@ impl Value {
Value::List { span, .. } => *span,
Value::Table { span, .. } => *span,
Value::Block { span, .. } => *span,
Value::RowStream { span, .. } => *span,
Value::ValueStream { span, .. } => *span,
Value::Nothing { span, .. } => *span,
}
}
@ -67,6 +181,8 @@ impl Value {
Value::Int { span, .. } => *span = new_span,
Value::Float { span, .. } => *span = new_span,
Value::String { span, .. } => *span = new_span,
Value::RowStream { span, .. } => *span = new_span,
Value::ValueStream { span, .. } => *span = new_span,
Value::List { span, .. } => *span = new_span,
Value::Table { span, .. } => *span = new_span,
Value::Block { span, .. } => *span = new_span,
@ -86,6 +202,44 @@ impl Value {
Value::Table { .. } => Type::Table, // FIXME
Value::Nothing { .. } => Type::Nothing,
Value::Block { .. } => Type::Block,
Value::ValueStream { .. } => Type::ValueStream,
Value::RowStream { .. } => Type::RowStream,
}
}
pub fn into_string(self) -> String {
match self {
Value::Bool { val, .. } => val.to_string(),
Value::Int { val, .. } => val.to_string(),
Value::Float { val, .. } => val.to_string(),
Value::String { val, .. } => val,
Value::ValueStream { stream, .. } => stream.into_string(),
Value::List { val, .. } => val
.into_iter()
.map(|x| x.into_string())
.collect::<Vec<_>>()
.join(", "),
Value::Table { val, .. } => val
.into_iter()
.map(|x| {
x.into_iter()
.map(|x| x.into_string())
.collect::<Vec<_>>()
.join(", ")
})
.collect::<Vec<_>>()
.join("\n"),
Value::RowStream {
headers, stream, ..
} => stream.into_string(headers),
Value::Block { val, .. } => format!("<Block {}>", val),
Value::Nothing { .. } => String::new(),
}
}
pub fn nothing() -> Value {
Value::Nothing {
span: Span::unknown(),
}
}
}
@ -97,65 +251,15 @@ impl PartialEq for Value {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => lhs == rhs,
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => lhs == rhs,
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => lhs == rhs,
(Value::List { val: l1, .. }, Value::List { val: l2, .. }) => l1 == l2,
(Value::Block { val: b1, .. }, Value::Block { val: b2, .. }) => b1 == b2,
_ => false,
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Bool { val, .. } => {
write!(f, "{}", val)
}
Value::Int { val, .. } => {
write!(f, "{}", val)
}
Value::Float { val, .. } => {
write!(f, "{}", val)
}
Value::String { val, .. } => write!(f, "{}", val),
Value::List { val, .. } => {
write!(
f,
"[{}]",
val.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(", ".into())
)
}
Value::Table { headers, val, .. } => {
write!(
f,
"[{}]\n[{}]",
headers
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(", ".into()),
val.iter()
.map(|x| {
x.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(", ".into())
})
.collect::<Vec<String>>()
.join("\n")
)
}
Value::Block { .. } => write!(f, "<block>"),
Value::Nothing { .. } => write!(f, ""),
}
}
}
impl Value {
pub fn add(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = nu_parser::span(&[self.span(), rhs.span()]);
let span = span(&[self.span(), rhs.span()]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Int {
@ -189,7 +293,7 @@ impl Value {
}
}
pub fn sub(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = nu_parser::span(&[self.span(), rhs.span()]);
let span = span(&[self.span(), rhs.span()]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Int {
@ -219,7 +323,7 @@ impl Value {
}
}
pub fn mul(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = nu_parser::span(&[self.span(), rhs.span()]);
let span = span(&[self.span(), rhs.span()]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Int {
@ -249,7 +353,7 @@ impl Value {
}
}
pub fn div(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = nu_parser::span(&[self.span(), rhs.span()]);
let span = span(&[self.span(), rhs.span()]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
@ -303,7 +407,7 @@ impl Value {
}
}
pub fn lt(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = nu_parser::span(&[self.span(), rhs.span()]);
let span = span(&[self.span(), rhs.span()]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool {
@ -332,7 +436,7 @@ impl Value {
}
}
pub fn lte(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = nu_parser::span(&[self.span(), rhs.span()]);
let span = span(&[self.span(), rhs.span()]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool {
@ -361,7 +465,7 @@ impl Value {
}
}
pub fn gt(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = nu_parser::span(&[self.span(), rhs.span()]);
let span = span(&[self.span(), rhs.span()]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool {
@ -390,7 +494,7 @@ impl Value {
}
}
pub fn gte(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = nu_parser::span(&[self.span(), rhs.span()]);
let span = span(&[self.span(), rhs.span()]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool {
@ -419,7 +523,7 @@ impl Value {
}
}
pub fn eq(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = nu_parser::span(&[self.span(), rhs.span()]);
let span = span(&[self.span(), rhs.span()]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool {
@ -445,6 +549,25 @@ impl Value {
val: lhs == rhs,
span,
}),
(Value::List { val: lhs, .. }, Value::List { val: rhs, .. }) => Ok(Value::Bool {
val: lhs == rhs,
span,
}),
(
Value::Table {
val: lhs,
headers: lhs_headers,
..
},
Value::Table {
val: rhs,
headers: rhs_headers,
..
},
) => Ok(Value::Bool {
val: lhs_headers == rhs_headers && lhs == rhs,
span,
}),
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
@ -455,7 +578,7 @@ impl Value {
}
}
pub fn ne(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = nu_parser::span(&[self.span(), rhs.span()]);
let span = span(&[self.span(), rhs.span()]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool {
@ -481,6 +604,26 @@ impl Value {
val: lhs != rhs,
span,
}),
(Value::List { val: lhs, .. }, Value::List { val: rhs, .. }) => Ok(Value::Bool {
val: lhs != rhs,
span,
}),
(
Value::Table {
val: lhs,
headers: lhs_headers,
..
},
Value::Table {
val: rhs,
headers: rhs_headers,
..
},
) => Ok(Value::Bool {
val: lhs_headers != rhs_headers || lhs != rhs,
span,
}),
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),

View File

@ -1,20 +1,25 @@
use nu_cli::{create_default_context, report_parsing_error, report_shell_error, NuHighlighter};
use nu_cli::{report_parsing_error, report_shell_error, NuHighlighter};
use nu_command::create_default_context;
use nu_engine::eval_block;
use nu_parser::{ParserState, ParserWorkingSet};
use nu_parser::parse_file;
use nu_protocol::{
engine::{EngineState, EvaluationContext, StateWorkingSet},
Value,
};
#[cfg(test)]
mod tests;
fn main() -> std::io::Result<()> {
let parser_state = create_default_context();
let engine_state = create_default_context();
if let Some(path) = std::env::args().nth(1) {
let file = std::fs::read(&path)?;
let (block, delta) = {
let parser_state = parser_state.borrow();
let mut working_set = ParserWorkingSet::new(&*parser_state);
let (output, err) = working_set.parse_file(&path, &file, false);
let engine_state = engine_state.borrow();
let mut working_set = StateWorkingSet::new(&*engine_state);
let (output, err) = parse_file(&mut working_set, &path, &file, false);
if let Some(err) = err {
let _ = report_parsing_error(&working_set, &err);
@ -23,20 +28,20 @@ fn main() -> std::io::Result<()> {
(output, working_set.render())
};
ParserState::merge_delta(&mut *parser_state.borrow_mut(), delta);
EngineState::merge_delta(&mut *engine_state.borrow_mut(), delta);
let state = nu_engine::State {
parser_state: parser_state.clone(),
stack: nu_engine::Stack::new(),
let state = EvaluationContext {
engine_state: engine_state.clone(),
stack: nu_protocol::engine::Stack::new(),
};
match eval_block(&state, &block) {
match eval_block(&state, &block, Value::nothing()) {
Ok(value) => {
println!("{}", value);
println!("{}", value.into_string());
}
Err(err) => {
let parser_state = parser_state.borrow();
let working_set = ParserWorkingSet::new(&*parser_state);
let engine_state = engine_state.borrow();
let working_set = StateWorkingSet::new(&*engine_state);
let _ = report_shell_error(&working_set, &err);
@ -54,12 +59,12 @@ fn main() -> std::io::Result<()> {
"history.txt".into(),
)?))?
.with_highlighter(Box::new(NuHighlighter {
parser_state: parser_state.clone(),
engine_state: engine_state.clone(),
}));
let prompt = DefaultPrompt::new(1);
let mut current_line = 1;
let stack = nu_engine::Stack::new();
let stack = nu_protocol::engine::Stack::new();
loop {
let input = line_editor.read_line(&prompt);
@ -67,13 +72,25 @@ fn main() -> std::io::Result<()> {
Ok(Signal::Success(s)) => {
if s.trim() == "exit" {
break;
} else if s.trim() == "vars" {
engine_state.borrow().print_vars();
continue;
} else if s.trim() == "decls" {
engine_state.borrow().print_decls();
continue;
} else if s.trim() == "blocks" {
engine_state.borrow().print_blocks();
continue;
} else if s.trim() == "stack" {
stack.print_stack();
continue;
}
// println!("input: '{}'", s);
let (block, delta) = {
let parser_state = parser_state.borrow();
let mut working_set = ParserWorkingSet::new(&*parser_state);
let (output, err) = working_set.parse_file(
let engine_state = engine_state.borrow();
let mut working_set = StateWorkingSet::new(&*engine_state);
let (output, err) = parse_file(
&mut working_set,
&format!("line_{}", current_line),
s.as_bytes(),
false,
@ -85,20 +102,20 @@ fn main() -> std::io::Result<()> {
(output, working_set.render())
};
ParserState::merge_delta(&mut *parser_state.borrow_mut(), delta);
EngineState::merge_delta(&mut *engine_state.borrow_mut(), delta);
let state = nu_engine::State {
parser_state: parser_state.clone(),
let state = nu_protocol::engine::EvaluationContext {
engine_state: engine_state.clone(),
stack: stack.clone(),
};
match eval_block(&state, &block) {
match eval_block(&state, &block, Value::nothing()) {
Ok(value) => {
println!("{}", value);
println!("{}", value.into_string());
}
Err(err) => {
let parser_state = parser_state.borrow();
let working_set = ParserWorkingSet::new(&*parser_state);
let engine_state = engine_state.borrow();
let working_set = StateWorkingSet::new(&*engine_state);
let _ = report_shell_error(&working_set, &err);
}