mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 20:47:44 +02:00
Span all the things
Also set up builder infra for more consistent AST creation.
This commit is contained in:
@ -5,7 +5,7 @@ crate mod parser;
|
||||
crate mod registry;
|
||||
crate mod span;
|
||||
|
||||
crate use ast::{ParsedCommand, Pipeline};
|
||||
crate use ast::Pipeline;
|
||||
crate use registry::{Args, CommandConfig};
|
||||
|
||||
use crate::errors::ShellError;
|
||||
@ -33,7 +33,7 @@ pub fn parse(input: &str) -> Result<Pipeline, ShellError> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::parser::ast::{bare, flag, short, unit, var, Expression, Operator, Pipeline};
|
||||
use crate::parser::ast::Pipeline;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
fn assert_parse(source: &str, expected: Pipeline) {
|
||||
@ -61,30 +61,44 @@ mod tests {
|
||||
let printed = parsed.print();
|
||||
|
||||
assert_eq!(parsed, expected);
|
||||
assert_eq!(source, printed);
|
||||
assert_eq!(printed, source);
|
||||
}
|
||||
|
||||
macro_rules! commands {
|
||||
( $( ( $name:tt $( $command:expr)* ) )|* ) => {
|
||||
Pipeline::new(vec![
|
||||
( $( ( $name:tt $( $command:ident ( $arg:expr ) )* ) )|* ) => {{
|
||||
use $crate::parser::ast::{Expression, ExpressionBuilder};
|
||||
let mut builder = crate::parser::ast::ExpressionBuilder::new();
|
||||
|
||||
builder.pipeline(vec![
|
||||
$(
|
||||
command!($name $($command)*)
|
||||
(command!($name $($command($arg))*) as (&dyn Fn(&mut ExpressionBuilder) -> Expression))
|
||||
),*
|
||||
])
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
macro_rules! command {
|
||||
($name:ident $( $command:expr )*) => {
|
||||
Expression::call(Expression::bare(stringify!($name)), vec![ $($command.into()),* ])
|
||||
($name:ident) => {
|
||||
&|b: &mut $crate::parser::ast::ExpressionBuilder| b.call((
|
||||
&|b: &mut $crate::parser::ast::ExpressionBuilder| b.bare(stringify!($name)),
|
||||
vec![]
|
||||
))
|
||||
};
|
||||
|
||||
($name:ident $( $command:expr )*) => {
|
||||
Expression::call(Expression::bare(stringify!($name)), vec![ $($command.into()),* ])
|
||||
($name:ident $( $command:ident ( $body:expr ) )*) => {{
|
||||
use $crate::parser::ast::{Expression, ExpressionBuilder};
|
||||
&|b: &mut ExpressionBuilder| b.call((
|
||||
(&|b: &mut ExpressionBuilder| b.bare(stringify!($name))) as (&dyn Fn(&mut ExpressionBuilder) -> Expression),
|
||||
vec![$( (&|b: &mut ExpressionBuilder| b.$command($body)) as &dyn Fn(&mut ExpressionBuilder) -> Expression ),* ]))
|
||||
|
||||
}};
|
||||
|
||||
($name:ident $( $command:ident ( $body:expr ) )*) => {
|
||||
&|b: &mut $crate::parser::ast::ExpressionBuilder| b.call(|b| b.bare(stringify!($name)), vec![ $( |b| b.$command($body) ),* ])
|
||||
};
|
||||
|
||||
($name:tt $( $command:expr )*) => {
|
||||
Expression::call(Expression::bare($name), vec![ $($command.into()),* ])
|
||||
($name:tt $( $command:ident ( $body:expr ) )*) => {
|
||||
&|b: &mut $crate::parser::ast::ExpressionBuilder| b.call((&|b| b.bare($name), vec![ $( &|b| b.$command($body) ),* ]))
|
||||
};
|
||||
}
|
||||
|
||||
@ -100,7 +114,7 @@ mod tests {
|
||||
commands![
|
||||
(open bare("Cargo.toml"))
|
||||
| (select bare("package.authors"))
|
||||
| ("split-row" " ")
|
||||
| ("split-row" string(" "))
|
||||
],
|
||||
);
|
||||
|
||||
@ -122,7 +136,7 @@ mod tests {
|
||||
|
||||
assert_parse(
|
||||
"open Cargo.toml -r",
|
||||
commands![(open bare("Cargo.toml") short("r"))],
|
||||
commands![(open bare("Cargo.toml") shorthand("r"))],
|
||||
);
|
||||
|
||||
assert_parse(
|
||||
@ -132,7 +146,7 @@ mod tests {
|
||||
|
||||
assert_parse(
|
||||
r#"config --get "ignore dups" | format-list"#,
|
||||
commands![(config flag("get") "ignore dups") | ("format-list")],
|
||||
commands![(config flag("get") string("ignore dups")) | ("format-list")],
|
||||
);
|
||||
|
||||
assert_parse(
|
||||
@ -147,16 +161,16 @@ mod tests {
|
||||
|
||||
assert_parse(
|
||||
"config --set tabs 2",
|
||||
commands![(config flag("set") bare("tabs") 2)],
|
||||
commands![(config flag("set") bare("tabs") int(2))],
|
||||
);
|
||||
|
||||
assert_parse(
|
||||
r#"ls | skip 1 | first 2 | select "file name" | rm $it"#,
|
||||
commands![
|
||||
(ls)
|
||||
| (skip 1)
|
||||
| (first 2)
|
||||
| (select "file name")
|
||||
| (skip int(1))
|
||||
| (first int(2))
|
||||
| (select string("file name"))
|
||||
| (rm var("it"))
|
||||
],
|
||||
);
|
||||
@ -165,7 +179,7 @@ mod tests {
|
||||
r#"git branch --merged | split-row "`n" | where $it != "* master""#,
|
||||
commands![
|
||||
// TODO: Handle escapes correctly. Should we do ` escape because of paths?
|
||||
(git bare("branch") flag("merged")) | ("split-row" "`n") | (where binary(var("it"), "!=", "* master"))
|
||||
(git bare("branch") flag("merged")) | ("split-row" string("`n")) | (where binary((&|b| b.var("it"), &|b| b.op("!="), &|b| b.string("* master"))))
|
||||
],
|
||||
);
|
||||
|
||||
@ -175,7 +189,7 @@ mod tests {
|
||||
(open bare("input2.json"))
|
||||
| ("from-json")
|
||||
| (select bare("glossary.GlossDiv.GlossList.GlossEntry.GlossDef.GlossSeeAlso"))
|
||||
| (where binary(var("it"), ">", "GML"))
|
||||
| (where binary((&|b| b.var("it"), &|b| b.op(">"), &|b| b.string("GML"))))
|
||||
]
|
||||
);
|
||||
|
||||
@ -189,16 +203,15 @@ mod tests {
|
||||
assert_parse(
|
||||
"ls | where size < 1KB",
|
||||
commands![
|
||||
(ls) | (where binary(bare("size"), "<", unit(1, "KB")))
|
||||
(ls) | (where binary((&|b| b.bare("size"), &|b| b.op("<"), &|b| b.unit((1, "KB")))))
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
fn binary(
|
||||
left: impl Into<Expression>,
|
||||
op: impl Into<Operator>,
|
||||
right: impl Into<Expression>,
|
||||
) -> Expression {
|
||||
Expression::binary(left, op, right)
|
||||
assert_parse(
|
||||
"ls | where { $it.size > 100 }",
|
||||
commands![
|
||||
(ls) | (where block(&|b| b.binary((&|b| b.path((&|b| b.var("it"), vec!["size"])), &|b| b.op(">"), &|b| b.int(100)))))
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user