Span all the things

Also set up builder infra for more consistent AST creation.
This commit is contained in:
Yehuda Katz
2019-06-05 23:34:59 -07:00
parent f3bb4a03c2
commit 324f7915be
11 changed files with 4006 additions and 1760 deletions

View File

@ -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)))))
],
)
}
}