forked from extern/nushell
429 lines
13 KiB
Rust
429 lines
13 KiB
Rust
use crate::prelude::*;
|
|
|
|
use crate::parser::parse::flag::{Flag, FlagKind};
|
|
use crate::parser::parse::operator::Operator;
|
|
use crate::parser::parse::pipeline::{Pipeline, PipelineElement};
|
|
use crate::parser::parse::token_tree::{DelimitedNode, Delimiter, PathNode, TokenNode};
|
|
use crate::parser::parse::tokens::{RawNumber, RawToken};
|
|
use crate::parser::parse::unit::Unit;
|
|
use crate::parser::CallNode;
|
|
use crate::Span;
|
|
use derive_new::new;
|
|
|
|
#[derive(new)]
|
|
pub struct TokenTreeBuilder {
|
|
#[new(default)]
|
|
pos: usize,
|
|
}
|
|
|
|
pub type CurriedToken = Box<dyn FnOnce(&mut TokenTreeBuilder) -> TokenNode + 'static>;
|
|
pub type CurriedCall = Box<dyn FnOnce(&mut TokenTreeBuilder) -> Tagged<CallNode> + 'static>;
|
|
|
|
impl TokenTreeBuilder {
|
|
pub fn build(block: impl FnOnce(&mut Self) -> TokenNode) -> TokenNode {
|
|
let mut builder = TokenTreeBuilder::new();
|
|
block(&mut builder)
|
|
}
|
|
|
|
pub fn pipeline(input: Vec<(Option<&str>, CurriedCall, Option<&str>)>) -> CurriedToken {
|
|
let input: Vec<(Option<String>, CurriedCall, Option<String>)> = input
|
|
.into_iter()
|
|
.map(|(pre, call, post)| {
|
|
(
|
|
pre.map(|s| s.to_string()),
|
|
call,
|
|
post.map(|s| s.to_string()),
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
Box::new(move |b| {
|
|
let start = b.pos;
|
|
|
|
let mut out: Vec<PipelineElement> = vec![];
|
|
|
|
let mut input = input.into_iter().peekable();
|
|
let (pre, call, post) = input
|
|
.next()
|
|
.expect("A pipeline must contain at least one element");
|
|
|
|
let pre_span = pre.map(|pre| b.consume(&pre));
|
|
let call = call(b);
|
|
let post_span = post.map(|post| b.consume(&post));
|
|
let pipe = input.peek().map(|_| Span::from(b.consume("|")));
|
|
out.push(PipelineElement::new(
|
|
pre_span.map(Span::from),
|
|
call,
|
|
post_span.map(Span::from),
|
|
pipe,
|
|
));
|
|
|
|
loop {
|
|
match input.next() {
|
|
None => break,
|
|
Some((pre, call, post)) => {
|
|
let pre_span = pre.map(|pre| b.consume(&pre));
|
|
let call = call(b);
|
|
let post_span = post.map(|post| b.consume(&post));
|
|
|
|
let pipe = input.peek().map(|_| Span::from(b.consume("|")));
|
|
|
|
out.push(PipelineElement::new(
|
|
pre_span.map(Span::from),
|
|
call,
|
|
post_span.map(Span::from),
|
|
pipe,
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
let end = b.pos;
|
|
|
|
TokenTreeBuilder::spanned_pipeline((out, None), (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_pipeline(
|
|
input: (Vec<PipelineElement>, Option<Span>),
|
|
span: impl Into<Span>,
|
|
) -> TokenNode {
|
|
TokenNode::Pipeline(Tagged::from_simple_spanned_item(
|
|
Pipeline::new(input.0, input.1.into()),
|
|
span,
|
|
))
|
|
}
|
|
|
|
pub fn op(input: impl Into<Operator>) -> CurriedToken {
|
|
let input = input.into();
|
|
|
|
Box::new(move |b| {
|
|
let (start, end) = b.consume(input.as_str());
|
|
|
|
b.pos = end;
|
|
|
|
TokenTreeBuilder::spanned_op(input, (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_op(input: impl Into<Operator>, span: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Operator(Tagged::from_simple_spanned_item(input.into(), span.into()))
|
|
}
|
|
|
|
pub fn string(input: impl Into<String>) -> CurriedToken {
|
|
let input = input.into();
|
|
|
|
Box::new(move |b| {
|
|
let (start, _) = b.consume("\"");
|
|
let (inner_start, inner_end) = b.consume(&input);
|
|
let (_, end) = b.consume("\"");
|
|
b.pos = end;
|
|
|
|
TokenTreeBuilder::spanned_string((inner_start, inner_end), (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_string(input: impl Into<Span>, span: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Token(Tagged::from_simple_spanned_item(
|
|
RawToken::String(input.into()),
|
|
span.into(),
|
|
))
|
|
}
|
|
|
|
pub fn bare(input: impl Into<String>) -> CurriedToken {
|
|
let input = input.into();
|
|
|
|
Box::new(move |b| {
|
|
let (start, end) = b.consume(&input);
|
|
b.pos = end;
|
|
|
|
TokenTreeBuilder::spanned_bare((start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_bare(input: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Token(Tagged::from_simple_spanned_item(
|
|
RawToken::Bare,
|
|
input.into(),
|
|
))
|
|
}
|
|
|
|
pub fn spanned_external(input: impl Into<Span>, span: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Token(Tagged::from_simple_spanned_item(
|
|
RawToken::External(input.into()),
|
|
span.into(),
|
|
))
|
|
}
|
|
|
|
pub fn int(input: impl Into<BigInt>) -> CurriedToken {
|
|
let int = input.into();
|
|
|
|
Box::new(move |b| {
|
|
let (start, end) = b.consume(&int.to_string());
|
|
b.pos = end;
|
|
|
|
TokenTreeBuilder::spanned_number(RawNumber::Int((start, end).into()), (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn decimal(input: impl Into<BigDecimal>) -> CurriedToken {
|
|
let decimal = input.into();
|
|
|
|
Box::new(move |b| {
|
|
let (start, end) = b.consume(&decimal.to_string());
|
|
b.pos = end;
|
|
|
|
TokenTreeBuilder::spanned_number(RawNumber::Decimal((start, end).into()), (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_number(input: impl Into<RawNumber>, span: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Token(Tagged::from_simple_spanned_item(
|
|
RawToken::Number(input.into()),
|
|
span.into(),
|
|
))
|
|
}
|
|
|
|
pub fn size(int: impl Into<i64>, unit: impl Into<Unit>) -> CurriedToken {
|
|
let int = int.into();
|
|
let unit = unit.into();
|
|
|
|
Box::new(move |b| {
|
|
let (start_int, end_int) = b.consume(&int.to_string());
|
|
let (_, end_unit) = b.consume(unit.as_str());
|
|
b.pos = end_unit;
|
|
|
|
TokenTreeBuilder::spanned_size(
|
|
(RawNumber::Int((start_int, end_int).into()), unit),
|
|
(start_int, end_unit),
|
|
)
|
|
})
|
|
}
|
|
|
|
pub fn spanned_size(
|
|
input: (impl Into<RawNumber>, impl Into<Unit>),
|
|
span: impl Into<Span>,
|
|
) -> TokenNode {
|
|
let (int, unit) = (input.0.into(), input.1.into());
|
|
|
|
TokenNode::Token(Tagged::from_simple_spanned_item(
|
|
RawToken::Size(int, unit),
|
|
span,
|
|
))
|
|
}
|
|
|
|
pub fn path(head: CurriedToken, tail: Vec<CurriedToken>) -> CurriedToken {
|
|
Box::new(move |b| {
|
|
let start = b.pos;
|
|
let head = head(b);
|
|
|
|
let mut output = vec![];
|
|
|
|
for item in tail {
|
|
b.consume(".");
|
|
|
|
output.push(item(b));
|
|
}
|
|
|
|
let end = b.pos;
|
|
|
|
TokenTreeBuilder::spanned_path((head, output), (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_path(input: (TokenNode, Vec<TokenNode>), span: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Path(Tagged::from_simple_spanned_item(
|
|
PathNode::new(Box::new(input.0), input.1),
|
|
span,
|
|
))
|
|
}
|
|
|
|
pub fn var(input: impl Into<String>) -> CurriedToken {
|
|
let input = input.into();
|
|
|
|
Box::new(move |b| {
|
|
let (start, _) = b.consume("$");
|
|
let (inner_start, end) = b.consume(&input);
|
|
|
|
TokenTreeBuilder::spanned_var((inner_start, end), (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_var(input: impl Into<Span>, span: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Token(Tagged::from_simple_spanned_item(
|
|
RawToken::Variable(input.into()),
|
|
span.into(),
|
|
))
|
|
}
|
|
|
|
pub fn flag(input: impl Into<String>) -> CurriedToken {
|
|
let input = input.into();
|
|
|
|
Box::new(move |b| {
|
|
let (start, _) = b.consume("--");
|
|
let (inner_start, end) = b.consume(&input);
|
|
|
|
TokenTreeBuilder::spanned_flag((inner_start, end), (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_flag(input: impl Into<Span>, span: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Flag(Tagged::from_simple_spanned_item(
|
|
Flag::new(FlagKind::Longhand, input.into()),
|
|
span.into(),
|
|
))
|
|
}
|
|
|
|
pub fn shorthand(input: impl Into<String>) -> CurriedToken {
|
|
let input = input.into();
|
|
|
|
Box::new(move |b| {
|
|
let (start, _) = b.consume("-");
|
|
let (inner_start, end) = b.consume(&input);
|
|
|
|
TokenTreeBuilder::spanned_shorthand((inner_start, end), (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_shorthand(input: impl Into<Span>, span: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Flag(Tagged::from_simple_spanned_item(
|
|
Flag::new(FlagKind::Shorthand, input.into()),
|
|
span.into(),
|
|
))
|
|
}
|
|
|
|
pub fn member(input: impl Into<String>) -> CurriedToken {
|
|
let input = input.into();
|
|
|
|
Box::new(move |b| {
|
|
let (start, end) = b.consume(&input);
|
|
TokenTreeBuilder::spanned_member((start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_member(span: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Member(span.into())
|
|
}
|
|
|
|
pub fn call(head: CurriedToken, input: Vec<CurriedToken>) -> CurriedCall {
|
|
Box::new(move |b| {
|
|
let start = b.pos;
|
|
|
|
let head_node = head(b);
|
|
|
|
let mut nodes = vec![head_node];
|
|
for item in input {
|
|
nodes.push(item(b));
|
|
}
|
|
|
|
let end = b.pos;
|
|
|
|
TokenTreeBuilder::spanned_call(nodes, (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_call(input: Vec<TokenNode>, span: impl Into<Span>) -> Tagged<CallNode> {
|
|
if input.len() == 0 {
|
|
panic!("BUG: spanned call (TODO)")
|
|
}
|
|
|
|
let mut input = input.into_iter();
|
|
|
|
let head = input.next().unwrap();
|
|
let tail = input.collect();
|
|
|
|
Tagged::from_simple_spanned_item(CallNode::new(Box::new(head), tail), span)
|
|
}
|
|
|
|
pub fn parens(input: Vec<CurriedToken>) -> CurriedToken {
|
|
Box::new(move |b| {
|
|
let (start, _) = b.consume("(");
|
|
let mut output = vec![];
|
|
for item in input {
|
|
output.push(item(b));
|
|
}
|
|
|
|
let (_, end) = b.consume(")");
|
|
|
|
TokenTreeBuilder::spanned_parens(output, (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_parens(input: impl Into<Vec<TokenNode>>, span: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Delimited(Tagged::from_simple_spanned_item(
|
|
DelimitedNode::new(Delimiter::Paren, input.into()),
|
|
span,
|
|
))
|
|
}
|
|
|
|
pub fn square(input: Vec<CurriedToken>) -> CurriedToken {
|
|
Box::new(move |b| {
|
|
let (start, _) = b.consume("[");
|
|
let mut output = vec![];
|
|
for item in input {
|
|
output.push(item(b));
|
|
}
|
|
|
|
let (_, end) = b.consume("]");
|
|
|
|
TokenTreeBuilder::spanned_square(output, (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_square(input: impl Into<Vec<TokenNode>>, span: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Delimited(Tagged::from_simple_spanned_item(
|
|
DelimitedNode::new(Delimiter::Square, input.into()),
|
|
span,
|
|
))
|
|
}
|
|
|
|
pub fn braced(input: Vec<CurriedToken>) -> CurriedToken {
|
|
Box::new(move |b| {
|
|
let (start, _) = b.consume("{ ");
|
|
let mut output = vec![];
|
|
for item in input {
|
|
output.push(item(b));
|
|
}
|
|
|
|
let (_, end) = b.consume(" }");
|
|
|
|
TokenTreeBuilder::spanned_brace(output, (start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_brace(input: impl Into<Vec<TokenNode>>, span: impl Into<Span>) -> TokenNode {
|
|
TokenNode::Delimited(Tagged::from_simple_spanned_item(
|
|
DelimitedNode::new(Delimiter::Brace, input.into()),
|
|
span,
|
|
))
|
|
}
|
|
|
|
pub fn sp() -> CurriedToken {
|
|
Box::new(|b| {
|
|
let (start, end) = b.consume(" ");
|
|
TokenNode::Whitespace(Span::from((start, end)))
|
|
})
|
|
}
|
|
|
|
pub fn ws(input: impl Into<String>) -> CurriedToken {
|
|
let input = input.into();
|
|
|
|
Box::new(move |b| {
|
|
let (start, end) = b.consume(&input);
|
|
TokenTreeBuilder::spanned_ws((start, end))
|
|
})
|
|
}
|
|
|
|
pub fn spanned_ws(span: impl Into<Span>) -> TokenNode {
|
|
let span = span.into();
|
|
|
|
TokenNode::Whitespace(span.into())
|
|
}
|
|
|
|
fn consume(&mut self, input: &str) -> (usize, usize) {
|
|
let start = self.pos;
|
|
self.pos += input.len();
|
|
(start, self.pos)
|
|
}
|
|
}
|