nushell/crates/nu-parser/src/hir/syntax_shape/expression/variable_path.rs
Jonathan Turner 0f626dd076
Another batch of un-unwrapping (#1148)
Another batch of un-unwrappings
2020-01-02 17:02:46 +13:00

981 lines
28 KiB
Rust

use crate::hir::syntax_shape::{
color_fallible_syntax, color_fallible_syntax_with, expand_atom, expand_expr, expand_syntax,
parse_single_node, AnyExpressionShape, BareShape, ExpandContext, ExpandExpression,
ExpandSyntax, ExpansionRule, FallibleColorSyntax, FlatShape, ParseError, Peeked, SkipSyntax,
StringShape, TestSyntax, UnspannedAtomicToken, WhitespaceShape,
};
use crate::parse::tokens::{RawNumber, UnspannedToken};
use crate::{hir, hir::Expression, hir::TokensIterator, CompareOperator, EvaluationOperator};
use nu_errors::ShellError;
use nu_protocol::{PathMember, ShellTypeName};
use nu_source::{
b, DebugDocBuilder, HasSpan, PrettyDebug, PrettyDebugWithSource, Span, Spanned, SpannedItem,
Tag, Tagged, TaggedItem, Text,
};
use num_bigint::BigInt;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
#[derive(Debug, Copy, Clone)]
pub struct VariablePathShape;
impl ExpandExpression for VariablePathShape {
fn name(&self) -> &'static str {
"variable path"
}
fn expand_expr<'a, 'b>(
&self,
token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext,
) -> Result<hir::Expression, ParseError> {
// 1. let the head be the first token, expecting a variable
// 2. let the tail be an empty list of members
// 2. while the next token (excluding ws) is a dot:
// 1. consume the dot
// 2. consume the next token as a member and push it onto tail
let head = expand_expr(&VariableShape, token_nodes, context)?;
let start = head.span;
let mut end = start;
let mut tail: Vec<PathMember> = vec![];
loop {
if DotShape.skip(token_nodes, context).is_err() {
break;
}
let member = expand_syntax(&MemberShape, token_nodes, context)?;
let member = member.to_path_member(context.source);
end = member.span;
tail.push(member);
}
Ok(hir::Expression::path(head, tail, start.until(end)))
}
}
impl FallibleColorSyntax for VariablePathShape {
type Info = ();
type Input = ();
fn name(&self) -> &'static str {
"VariablePathShape"
}
fn color_syntax<'a, 'b>(
&self,
_input: &(),
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<(), ShellError> {
token_nodes.atomic(|token_nodes| {
// If the head of the token stream is not a variable, fail
color_fallible_syntax(&VariableShape, token_nodes, context)?;
loop {
// look for a dot at the head of a stream
if color_fallible_syntax_with(
&ColorableDotShape,
&FlatShape::Dot,
token_nodes,
context,
)
.is_err()
{
// if there's no dot, we're done
break;
}
// otherwise, look for a member, and if you don't find one, fail
color_fallible_syntax(&MemberShape, token_nodes, context)?;
}
Ok(())
})
}
}
#[derive(Debug, Copy, Clone)]
pub struct PathTailShape;
/// The failure mode of `PathTailShape` is a dot followed by a non-member
impl FallibleColorSyntax for PathTailShape {
type Info = ();
type Input = ();
fn name(&self) -> &'static str {
"PathTailShape"
}
fn color_syntax<'a, 'b>(
&self,
_input: &(),
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<(), ShellError> {
token_nodes.atomic(|token_nodes| loop {
let result = color_fallible_syntax_with(
&ColorableDotShape,
&FlatShape::Dot,
token_nodes,
context,
);
if result.is_err() {
return Ok(());
}
// If we've seen a dot but not a member, fail
color_fallible_syntax(&MemberShape, token_nodes, context)?;
})
}
}
#[derive(Debug, Clone)]
pub struct PathTailSyntax {
pub tail: Vec<PathMember>,
pub span: Span,
}
impl HasSpan for PathTailSyntax {
fn span(&self) -> Span {
self.span
}
}
impl PrettyDebug for PathTailSyntax {
fn pretty(&self) -> DebugDocBuilder {
b::typed("tail", b::intersperse(self.tail.iter(), b::space()))
}
}
impl ExpandSyntax for PathTailShape {
type Output = PathTailSyntax;
fn name(&self) -> &'static str {
"path continuation"
}
fn expand_syntax<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<Self::Output, ParseError> {
let mut end: Option<Span> = None;
let mut tail: Vec<PathMember> = vec![];
loop {
if DotShape.skip(token_nodes, context).is_err() {
break;
}
let member = expand_syntax(&MemberShape, token_nodes, context)?;
let member = member.to_path_member(context.source);
end = Some(member.span);
tail.push(member);
}
match end {
None => Err(ParseError::mismatch(
"path tail",
token_nodes.typed_span_at_cursor(),
)),
Some(end) => Ok(PathTailSyntax { tail, span: end }),
}
}
}
#[derive(Debug, Clone)]
pub enum ExpressionContinuation {
DotSuffix(Span, PathMember),
InfixSuffix(Spanned<CompareOperator>, Expression),
}
impl PrettyDebugWithSource for ExpressionContinuation {
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
match self {
ExpressionContinuation::DotSuffix(_, suffix) => {
b::operator(".") + suffix.pretty_debug(source)
}
ExpressionContinuation::InfixSuffix(op, expr) => {
op.pretty_debug(source) + b::space() + expr.pretty_debug(source)
}
}
}
}
impl HasSpan for ExpressionContinuation {
fn span(&self) -> Span {
match self {
ExpressionContinuation::DotSuffix(dot, column) => dot.until(column.span),
ExpressionContinuation::InfixSuffix(operator, expression) => {
operator.span.until(expression.span)
}
}
}
}
/// An expression continuation
#[derive(Debug, Copy, Clone)]
pub struct ExpressionContinuationShape;
impl ExpandSyntax for ExpressionContinuationShape {
type Output = ExpressionContinuation;
fn name(&self) -> &'static str {
"expression continuation"
}
fn expand_syntax<'a, 'b>(
&self,
token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext,
) -> Result<ExpressionContinuation, ParseError> {
// Try to expand a `.`
let dot = expand_syntax(&DotShape, token_nodes, context);
match dot {
// If a `.` was matched, it's a `Path`, and we expect a `Member` next
Ok(dot) => {
let syntax = expand_syntax(&MemberShape, token_nodes, context)?;
let member = syntax.to_path_member(context.source);
Ok(ExpressionContinuation::DotSuffix(dot, member))
}
// Otherwise, we expect an infix operator and an expression next
Err(_) => {
let (_, op, _) = expand_syntax(&InfixShape, token_nodes, context)?.infix.item;
let next = expand_expr(&AnyExpressionShape, token_nodes, context)?;
Ok(ExpressionContinuation::InfixSuffix(op.operator, next))
}
}
}
}
pub enum ContinuationInfo {
Dot,
Infix,
}
impl FallibleColorSyntax for ExpressionContinuationShape {
type Info = ContinuationInfo;
type Input = ();
fn name(&self) -> &'static str {
"ExpressionContinuationShape"
}
fn color_syntax<'a, 'b>(
&self,
_input: &(),
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<ContinuationInfo, ShellError> {
token_nodes.atomic(|token_nodes| {
// Try to expand a `.`
let dot = color_fallible_syntax_with(
&ColorableDotShape,
&FlatShape::Dot,
token_nodes,
context,
);
match dot {
Ok(_) => {
// we found a dot, so let's keep looking for a member; if no member was found, fail
color_fallible_syntax(&MemberShape, token_nodes, context)?;
Ok(ContinuationInfo::Dot)
}
Err(_) => {
let result = token_nodes.atomic(|token_nodes| {
// we didn't find a dot, so let's see if we're looking at an infix. If not found, fail
color_fallible_syntax(&InfixShape, token_nodes, context)?;
// now that we've seen an infix shape, look for any expression. If not found, fail
color_fallible_syntax(&AnyExpressionShape, token_nodes, context)?;
Ok(ContinuationInfo::Infix)
})?;
Ok(result)
}
}
})
}
}
#[derive(Debug, Copy, Clone)]
pub struct VariableShape;
impl ExpandExpression for VariableShape {
fn name(&self) -> &'static str {
"variable"
}
fn expand_expr<'a, 'b>(
&self,
token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext,
) -> Result<hir::Expression, ParseError> {
parse_single_node(token_nodes, "variable", |token, token_tag, err| {
Ok(match token {
UnspannedToken::Variable(tag) => {
if tag.slice(context.source) == "it" {
hir::Expression::it_variable(tag, token_tag)
} else {
hir::Expression::variable(tag, token_tag)
}
}
_ => return Err(err.error()),
})
})
}
}
impl FallibleColorSyntax for VariableShape {
type Info = ();
type Input = ();
fn name(&self) -> &'static str {
"VariableShape"
}
fn color_syntax<'a, 'b>(
&self,
_input: &(),
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<(), ShellError> {
let atom = expand_atom(
token_nodes,
"variable",
context,
ExpansionRule::permissive(),
);
let atom = match atom {
Err(err) => return Err(err.into()),
Ok(atom) => atom,
};
match &atom.unspanned {
UnspannedAtomicToken::Variable { .. } => {
token_nodes.color_shape(FlatShape::Variable.spanned(atom.span));
Ok(())
}
UnspannedAtomicToken::ItVariable { .. } => {
token_nodes.color_shape(FlatShape::ItVariable.spanned(atom.span));
Ok(())
}
_ => Err(ParseError::mismatch("variable", atom.type_name().spanned(atom.span)).into()),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Member {
String(/* outer */ Span, /* inner */ Span),
Int(BigInt, Span),
Bare(Span),
}
impl ShellTypeName for Member {
fn type_name(&self) -> &'static str {
match self {
Member::String(_, _) => "string",
Member::Int(_, _) => "integer",
Member::Bare(_) => "word",
}
}
}
impl Member {
pub fn to_path_member(&self, source: &Text) -> PathMember {
match self {
Member::String(outer, inner) => PathMember::string(inner.slice(source), *outer),
Member::Int(int, span) => PathMember::int(int.clone(), *span),
Member::Bare(span) => PathMember::string(span.slice(source), *span),
}
}
}
impl PrettyDebugWithSource for Member {
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
match self {
Member::String(outer, _) => b::value(outer.slice(source)),
Member::Int(int, _) => b::value(format!("{}", int)),
Member::Bare(span) => b::value(span.slice(source)),
}
}
}
impl HasSpan for Member {
fn span(&self) -> Span {
match self {
Member::String(outer, ..) => *outer,
Member::Int(_, int) => *int,
Member::Bare(name) => *name,
}
}
}
impl Member {
pub fn to_expr(&self) -> hir::Expression {
match self {
Member::String(outer, inner) => hir::Expression::string(*inner, *outer),
Member::Int(number, span) => hir::Expression::number(number.clone(), *span),
Member::Bare(span) => hir::Expression::string(*span, *span),
}
}
pub(crate) fn span(&self) -> Span {
match self {
Member::String(outer, _inner) => *outer,
Member::Int(_, span) => *span,
Member::Bare(span) => *span,
}
}
}
enum ColumnPathState {
Initial,
LeadingDot(Span),
Dot(Span, Vec<Member>, Span),
Member(Span, Vec<Member>),
Error(ParseError),
}
impl ColumnPathState {
pub fn dot(self, dot: Span) -> ColumnPathState {
match self {
ColumnPathState::Initial => ColumnPathState::LeadingDot(dot),
ColumnPathState::LeadingDot(_) => {
ColumnPathState::Error(ParseError::mismatch("column", "dot".spanned(dot)))
}
ColumnPathState::Dot(..) => {
ColumnPathState::Error(ParseError::mismatch("column", "dot".spanned(dot)))
}
ColumnPathState::Member(tag, members) => ColumnPathState::Dot(tag, members, dot),
ColumnPathState::Error(err) => ColumnPathState::Error(err),
}
}
pub fn member(self, member: Member) -> ColumnPathState {
match self {
ColumnPathState::Initial => ColumnPathState::Member(member.span(), vec![member]),
ColumnPathState::LeadingDot(tag) => {
ColumnPathState::Member(tag.until(member.span()), vec![member])
}
ColumnPathState::Dot(tag, mut tags, _) => {
ColumnPathState::Member(tag.until(member.span()), {
tags.push(member);
tags
})
}
ColumnPathState::Member(..) => ColumnPathState::Error(ParseError::mismatch(
"column",
member.type_name().spanned(member.span()),
)),
ColumnPathState::Error(err) => ColumnPathState::Error(err),
}
}
pub fn into_path(self, next: Peeked) -> Result<Tagged<Vec<Member>>, ParseError> {
match self {
ColumnPathState::Initial => Err(next.type_error("column path")),
ColumnPathState::LeadingDot(dot) => {
Err(ParseError::mismatch("column", "dot".spanned(dot)))
}
ColumnPathState::Dot(_tag, _members, dot) => {
Err(ParseError::mismatch("column", "dot".spanned(dot)))
}
ColumnPathState::Member(tag, tags) => Ok(tags.tagged(tag)),
ColumnPathState::Error(err) => Err(err),
}
}
}
pub fn expand_column_path<'a, 'b>(
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<ColumnPathSyntax, ParseError> {
let mut state = ColumnPathState::Initial;
loop {
let member = expand_syntax(&MemberShape, token_nodes, context);
match member {
Err(_) => break,
Ok(member) => state = state.member(member),
}
let dot = expand_syntax(&DotShape, token_nodes, context);
match dot {
Err(_) => break,
Ok(dot) => state = state.dot(dot),
}
}
let path = state.into_path(token_nodes.peek_non_ws())?;
Ok(ColumnPathSyntax {
path: path.item,
tag: path.tag,
})
}
#[derive(Debug, Copy, Clone)]
pub struct ColumnPathShape;
impl FallibleColorSyntax for ColumnPathShape {
type Info = ();
type Input = ();
fn name(&self) -> &'static str {
"ColumnPathShape"
}
fn color_syntax<'a, 'b>(
&self,
_input: &(),
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<(), ShellError> {
// If there's not even one member shape, fail
color_fallible_syntax(&MemberShape, token_nodes, context)?;
loop {
let checkpoint = token_nodes.checkpoint();
match color_fallible_syntax_with(
&ColorableDotShape,
&FlatShape::Dot,
checkpoint.iterator,
context,
) {
Err(_) => {
// we already saw at least one member shape, so return successfully
return Ok(());
}
Ok(_) => {
match color_fallible_syntax(&MemberShape, checkpoint.iterator, context) {
Err(_) => {
// we saw a dot but not a member (but we saw at least one member),
// so don't commit the dot but return successfully
return Ok(());
}
Ok(_) => {
// we saw a dot and a member, so commit it and continue on
checkpoint.commit();
}
}
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct ColumnPathSyntax {
pub path: Vec<Member>,
pub tag: Tag,
}
impl HasSpan for ColumnPathSyntax {
fn span(&self) -> Span {
self.tag.span
}
}
impl PrettyDebugWithSource for ColumnPathSyntax {
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
b::typed(
"column path",
b::intersperse(
self.path.iter().map(|member| member.pretty_debug(source)),
b::space(),
),
)
}
}
impl ExpandSyntax for ColumnPathShape {
type Output = ColumnPathSyntax;
fn name(&self) -> &'static str {
"column path"
}
fn expand_syntax<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<Self::Output, ParseError> {
expand_column_path(token_nodes, context)
}
}
#[derive(Debug, Copy, Clone)]
pub struct MemberShape;
impl FallibleColorSyntax for MemberShape {
type Info = ();
type Input = ();
fn name(&self) -> &'static str {
"MemberShape"
}
fn color_syntax<'a, 'b>(
&self,
_input: &(),
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<(), ShellError> {
let bare =
color_fallible_syntax_with(&BareShape, &FlatShape::BareMember, token_nodes, context);
if bare.is_ok() {
return Ok(());
}
// If we don't have a bare word, we'll look for a string
// Look for a string token. If we don't find one, fail
color_fallible_syntax_with(&StringShape, &FlatShape::StringMember, token_nodes, context)
}
}
#[derive(Debug, Copy, Clone)]
struct IntMemberShape;
impl ExpandSyntax for IntMemberShape {
type Output = Member;
fn name(&self) -> &'static str {
"integer member"
}
fn expand_syntax<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<Self::Output, ParseError> {
token_nodes.atomic_parse(|token_nodes| {
let next = expand_atom(
token_nodes,
"integer member",
context,
ExpansionRule::new().separate_members(),
)?;
match next.unspanned {
UnspannedAtomicToken::Number {
number: RawNumber::Int(int),
} => Ok(Member::Int(
BigInt::from_str(int.slice(context.source)).map_err(|_| {
ParseError::internal_error(
"can't convert from string to big int".spanned(int),
)
})?,
int,
)),
UnspannedAtomicToken::Word { text } => {
let int = BigInt::from_str(text.slice(context.source));
match int {
Ok(int) => Ok(Member::Int(int, text)),
Err(_) => Err(ParseError::mismatch("integer member", "word".spanned(text))),
}
}
other => Err(ParseError::mismatch(
"integer member",
other.type_name().spanned(next.span),
)),
}
})
}
}
impl ExpandSyntax for MemberShape {
type Output = Member;
fn name(&self) -> &'static str {
"column"
}
fn expand_syntax<'a, 'b>(
&self,
token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext,
) -> Result<Member, ParseError> {
if let Ok(int) = expand_syntax(&IntMemberShape, token_nodes, context) {
return Ok(int);
}
let bare = BareShape.test(token_nodes, context);
if let Some(peeked) = bare {
let node = peeked.not_eof("column")?.commit();
return Ok(Member::Bare(node.span()));
}
/* KATZ */
/* let number = NumberShape.test(token_nodes, context);
if let Some(peeked) = number {
let node = peeked.not_eof("column")?.commit();
let (n, span) = node.as_number().ok_or_else(|| {
ParseError::internal_error("can't convert node to number".spanned(node.span()))
})?;
return Ok(Member::Number(n, span))
}*/
let string = StringShape.test(token_nodes, context);
if let Some(peeked) = string {
let node = peeked.not_eof("column")?.commit();
let (outer, inner) = node.as_string().ok_or_else(|| {
ParseError::internal_error("can't convert node to string".spanned(node.span()))
})?;
return Ok(Member::String(outer, inner));
}
Err(token_nodes.peek_any().type_error("column"))
}
}
#[derive(Debug, Copy, Clone)]
pub struct DotShape;
#[derive(Debug, Copy, Clone)]
pub struct ColorableDotShape;
impl FallibleColorSyntax for ColorableDotShape {
type Info = ();
type Input = FlatShape;
fn name(&self) -> &'static str {
"ColorableDotShape"
}
fn color_syntax<'a, 'b>(
&self,
input: &FlatShape,
token_nodes: &'b mut TokensIterator<'a>,
_context: &ExpandContext,
) -> Result<(), ShellError> {
let peeked = token_nodes.peek_any().not_eof("dot")?;
match peeked.node {
node if node.is_dot() => {
peeked.commit();
token_nodes.color_shape((*input).spanned(node.span()));
Ok(())
}
other => Err(ShellError::type_error(
"dot",
other.type_name().spanned(other.span()),
)),
}
}
}
impl SkipSyntax for DotShape {
fn skip<'a, 'b>(
&self,
token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext,
) -> Result<(), ShellError> {
expand_syntax(self, token_nodes, context)?;
Ok(())
}
}
impl ExpandSyntax for DotShape {
type Output = Span;
fn name(&self) -> &'static str {
"dot"
}
fn expand_syntax<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
_context: &ExpandContext,
) -> Result<Self::Output, ParseError> {
parse_single_node(token_nodes, "dot", |token, token_span, _| {
Ok(match token {
UnspannedToken::EvaluationOperator(EvaluationOperator::Dot) => token_span,
_ => {
return Err(ParseError::mismatch(
"dot",
token.type_name().spanned(token_span),
))
}
})
})
}
}
#[derive(Debug, Copy, Clone)]
pub struct InfixShape;
impl FallibleColorSyntax for InfixShape {
type Info = ();
type Input = ();
fn name(&self) -> &'static str {
"InfixShape"
}
fn color_syntax<'a, 'b>(
&self,
_input: &(),
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<(), ShellError> {
let checkpoint = token_nodes.checkpoint();
// An infix operator must be prefixed by whitespace. If no whitespace was found, fail
color_fallible_syntax(&WhitespaceShape, checkpoint.iterator, context)?;
// Parse the next TokenNode after the whitespace
let operator_span = parse_single_node(
checkpoint.iterator,
"infix operator",
|token, token_span, _| {
match token {
// If it's an operator (and not `.`), it's a match
UnspannedToken::CompareOperator(_operator) => Ok(token_span),
// Otherwise, it's not a match
_ => Err(ParseError::mismatch(
"infix operator",
token.type_name().spanned(token_span),
)),
}
},
)?;
checkpoint
.iterator
.color_shape(FlatShape::CompareOperator.spanned(operator_span));
// An infix operator must be followed by whitespace. If no whitespace was found, fail
color_fallible_syntax(&WhitespaceShape, checkpoint.iterator, context)?;
checkpoint.commit();
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct InfixSyntax {
infix: Spanned<(Span, InfixInnerSyntax, Span)>,
}
impl HasSpan for InfixSyntax {
fn span(&self) -> Span {
self.infix.span
}
}
impl PrettyDebugWithSource for InfixSyntax {
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
self.infix.1.pretty_debug(source)
}
}
impl ExpandSyntax for InfixShape {
type Output = InfixSyntax;
fn name(&self) -> &'static str {
"infix operator"
}
fn expand_syntax<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<Self::Output, ParseError> {
let mut checkpoint = token_nodes.checkpoint();
// An infix operator must be prefixed by whitespace
let start = expand_syntax(&WhitespaceShape, checkpoint.iterator, context)?;
// Parse the next TokenNode after the whitespace
let operator = expand_syntax(&InfixInnerShape, &mut checkpoint.iterator, context)?;
// An infix operator must be followed by whitespace
let end = expand_syntax(&WhitespaceShape, checkpoint.iterator, context)?;
checkpoint.commit();
Ok(InfixSyntax {
infix: (start, operator, end).spanned(start.until(end)),
})
}
}
#[derive(Debug, Clone)]
pub struct InfixInnerSyntax {
pub operator: Spanned<CompareOperator>,
}
impl HasSpan for InfixInnerSyntax {
fn span(&self) -> Span {
self.operator.span
}
}
impl PrettyDebug for InfixInnerSyntax {
fn pretty(&self) -> DebugDocBuilder {
self.operator.pretty()
}
}
#[derive(Debug, Copy, Clone)]
pub struct InfixInnerShape;
impl ExpandSyntax for InfixInnerShape {
type Output = InfixInnerSyntax;
fn name(&self) -> &'static str {
"infix inner"
}
fn expand_syntax<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
_context: &ExpandContext,
) -> Result<Self::Output, ParseError> {
parse_single_node(token_nodes, "infix operator", |token, token_span, err| {
Ok(match token {
// If it's a comparison operator, it's a match
UnspannedToken::CompareOperator(operator) => InfixInnerSyntax {
operator: operator.spanned(token_span),
},
// Otherwise, it's not a match
_ => return Err(err.error()),
})
})
}
}