From 2eeceae6133b067f85668148532e66a853bf3b7e Mon Sep 17 00:00:00 2001 From: JT Date: Fri, 30 Jul 2021 10:56:51 +1200 Subject: [PATCH] fix clippy, add strings and concat --- Cargo.toml | 2 +- src/declaration.rs | 2 +- src/eval.rs | 92 +++++++-------- src/flatten.rs | 9 +- src/lex.rs | 8 +- src/lib.rs | 1 + src/main.rs | 2 +- src/parser.rs | 279 +++++++++++++++++++------------------------- src/parser_state.rs | 7 +- src/signature.rs | 33 +++--- src/type_check.rs | 49 ++++++++ 11 files changed, 253 insertions(+), 231 deletions(-) create mode 100644 src/type_check.rs diff --git a/Cargo.toml b/Cargo.toml index 7a3ea1ac34..a0a1c6310f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -reedline = {path = "../reedline"} +reedline = {git = "https://github.com/jntrnr/reedline"} nu-ansi-term = "0.32.0" \ No newline at end of file diff --git a/src/declaration.rs b/src/declaration.rs index d555bffc3a..585a1980ed 100644 --- a/src/declaration.rs +++ b/src/declaration.rs @@ -2,6 +2,6 @@ use crate::{BlockId, Signature}; #[derive(Clone, Debug)] pub struct Declaration { - pub signature: Signature, + pub signature: Box, pub body: Option, } diff --git a/src/eval.rs b/src/eval.rs index 29d62824ab..ff03a08121 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -27,6 +27,11 @@ impl Value { val: lhs + rhs, span: Span::unknown(), }), + (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => Ok(Value::String { + val: lhs.to_string() + rhs, + span: Span::unknown(), + }), + _ => Ok(Value::Unknown), } } @@ -87,56 +92,54 @@ fn eval_call(state: &State, stack: &mut Stack, call: &Call) -> Result { - if val { - let block = state.parser_state.get_block(then_block); - eval_block(state, stack, block) - } else if let Some(else_case) = else_case { - println!("{:?}", else_case); - if let Some(else_expr) = else_case.as_keyword() { - if let Some(block_id) = else_expr.as_block() { - let block = state.parser_state.get_block(block_id); - eval_block(state, stack, block) - } else { - eval_expression(state, stack, else_expr) - } + let result = eval_expression(state, stack, cond)?; + match result { + Value::Bool { val, .. } => { + if val { + let block = state.parser_state.get_block(then_block); + eval_block(state, stack, block) + } else if let Some(else_case) = else_case { + println!("{:?}", else_case); + if let Some(else_expr) = else_case.as_keyword() { + if let Some(block_id) = else_expr.as_block() { + let block = state.parser_state.get_block(block_id); + eval_block(state, stack, block) } else { - eval_expression(state, stack, else_case) + eval_expression(state, stack, else_expr) } } else { - Ok(Value::Unknown) + eval_expression(state, stack, else_case) } + } else { + Ok(Value::Unknown) } - _ => Err(ShellError::Mismatch("bool".into(), Span::unknown())), } - } else { - Ok(Value::Unknown) + _ => Err(ShellError::Mismatch("bool".into(), Span::unknown())), } + } else { + Ok(Value::Unknown) } } @@ -159,9 +162,9 @@ pub fn eval_expression( Expr::ExternalCall(_, _) => Err(ShellError::Unsupported(expr.span)), Expr::Operator(_) => Ok(Value::Unknown), Expr::BinaryOp(lhs, op, rhs) => { - let lhs = eval_expression(state, stack, &lhs)?; - let op = eval_operator(state, stack, &op)?; - let rhs = eval_expression(state, stack, &rhs)?; + let lhs = eval_expression(state, stack, lhs)?; + let op = eval_operator(state, stack, op)?; + let rhs = eval_expression(state, stack, rhs)?; match op { Operator::Plus => lhs.add(&rhs), @@ -197,11 +200,8 @@ pub fn eval_block(state: &State, stack: &mut Stack, block: &Block) -> Result { - last = Ok(eval_expression(state, stack, expression)?); - } - _ => {} + if let Statement::Expression(expression) = stmt { + last = Ok(eval_expression(state, stack, expression)?); } } diff --git a/src/flatten.rs b/src/flatten.rs index 86b97f2233..66e8f29688 100644 --- a/src/flatten.rs +++ b/src/flatten.rs @@ -35,15 +35,14 @@ impl<'a> ParserWorkingSet<'a> { 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.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![]; - output.push((call.head, FlatShape::InternalCall)); + let mut output = vec![(call.head, FlatShape::InternalCall)]; for positional in &call.positional { output.extend(self.flatten_expression(positional)); } diff --git a/src/lex.rs b/src/lex.rs index 767e917d3e..70d5995bc0 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -163,7 +163,13 @@ pub fn lex_item( // synthetic closing characters to the accumulated token. if let Some(block) = block_level.last() { let delim = block.closing(); - let cause = ParseError::UnexpectedEof((delim as char).to_string(), span); + let cause = ParseError::UnexpectedEof( + (delim as char).to_string(), + Span { + start: span.end - 1, + end: span.end, + }, + ); return (span, Some(cause)); } diff --git a/src/lib.rs b/src/lib.rs index dd6fa086e1..47915ab09d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ mod span; mod syntax_highlight; #[cfg(test)] mod tests; +mod type_check; pub use declaration::Declaration; pub use eval::{eval_block, eval_expression, Stack, State}; diff --git a/src/main.rs b/src/main.rs index 04a4a07128..c2ddd36986 100644 --- a/src/main.rs +++ b/src/main.rs @@ -163,7 +163,7 @@ fn main() -> std::io::Result<()> { println!("Error: {:?}", err); continue; } - // println!("Error: {:?}", err); + println!("Error: {:?}", err); (output, working_set.render()) }; diff --git a/src/parser.rs b/src/parser.rs index d0bf685134..d9d81b7ee9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -173,7 +173,7 @@ pub enum Expr { Table(Vec, Vec>), Keyword(Vec, Box), String(String), // FIXME: improve this in the future? - Signature(Signature), + Signature(Box), Garbage, } @@ -225,7 +225,7 @@ impl Expression { } } - pub fn as_signature(&self) -> Option { + pub fn as_signature(&self) -> Option> { match &self.expr { Expr::Signature(sig) => Some(sig.clone()), _ => None, @@ -502,10 +502,7 @@ impl<'a> ParserWorkingSet<'a> { if positional.shape == SyntaxShape::Int || positional.shape == SyntaxShape::Number { - if String::from_utf8_lossy(&arg_contents) - .parse::() - .is_ok() - { + if String::from_utf8_lossy(arg_contents).parse::().is_ok() { return (None, None); } else if let Some(first) = unmatched_short_flags.first() { error = error.or(Some(ParseError::UnknownFlag(*first))); @@ -547,21 +544,20 @@ impl<'a> ParserWorkingSet<'a> { let mut next_keyword_idx = spans.len(); for idx in (positional_idx + 1)..decl.signature.num_positionals() { - match decl.signature.get_positional(idx) { - Some(PositionalArg { - shape: SyntaxShape::Keyword(kw, ..), - .. - }) => { - for span_idx in spans_idx..spans.len() { - let contents = self.get_span_contents(spans[span_idx]); + if let Some(PositionalArg { + shape: SyntaxShape::Keyword(kw, ..), + .. + }) = decl.signature.get_positional(idx) + { + #[allow(clippy::needless_range_loop)] + for span_idx in spans_idx..spans.len() { + let contents = self.get_span_contents(spans[span_idx]); - if contents == kw { - next_keyword_idx = span_idx - (idx - (positional_idx + 1)); - break; - } + if contents == kw { + next_keyword_idx = span_idx - (idx - (positional_idx + 1)); + break; } } - _ => {} } } @@ -575,8 +571,8 @@ impl<'a> ParserWorkingSet<'a> { let end = [next_keyword_idx, remainder_idx, spans.len()] .iter() .min() - .expect("internal error: can't find min") - .clone(); + .copied() + .expect("internal error: can't find min"); // println!( // "{:?}", @@ -633,17 +629,19 @@ impl<'a> ParserWorkingSet<'a> { // go ahead and override the current error and tell the user about the missing // keyword/literal. error = Some(ParseError::Mismatch( - String::from_utf8_lossy(&keyword).into(), + String::from_utf8_lossy(keyword).into(), arg_span, )) } *spans_idx += 1; if *spans_idx >= spans.len() { - error = error.or(Some(ParseError::MissingPositional( - String::from_utf8_lossy(&keyword).into(), - spans[*spans_idx - 1], - ))); + error = error.or_else(|| { + Some(ParseError::MissingPositional( + String::from_utf8_lossy(keyword).into(), + spans[*spans_idx - 1], + )) + }); return ( Expression { expr: Expr::Keyword( @@ -656,7 +654,7 @@ impl<'a> ParserWorkingSet<'a> { error, ); } - let (expr, err) = self.parse_multispan_value(&spans, spans_idx, arg); + let (expr, err) = self.parse_multispan_value(spans, spans_idx, arg); error = error.or(err); let ty = expr.ty.clone(); @@ -669,11 +667,11 @@ impl<'a> ParserWorkingSet<'a> { error, ) } - x => { + _ => { // All other cases are single-span values let arg_span = spans[*spans_idx]; - let (arg, err) = self.parse_value(arg_span, &shape); + let (arg, err) = self.parse_value(arg_span, shape); error = error.or(err); (arg, error) @@ -756,10 +754,9 @@ impl<'a> ParserWorkingSet<'a> { && arg.ty != positional.shape.to_type() { let span = span(&spans[orig_idx..spans_idx]); - error = error.or(Some(ParseError::TypeMismatch( - positional.shape.to_type(), - span, - ))); + error = error.or_else(|| { + Some(ParseError::TypeMismatch(positional.shape.to_type(), span)) + }); Expression::garbage(span) } else { arg @@ -940,8 +937,12 @@ impl<'a> ParserWorkingSet<'a> { // this seems okay to set it to unknown here, but we should double-check let id = self.add_variable(name, Type::Unknown); ( - Expression::garbage(span), - Some(ParseError::VariableNotFound(span)), + Expression { + expr: Expr::Var(id), + span, + ty: Type::Unknown, + }, + None, ) } } else { @@ -978,7 +979,7 @@ impl<'a> ParserWorkingSet<'a> { let source = self.get_span_contents(span); - let (output, err) = lex(&source, start, &[], &[]); + let (output, err) = lex(source, start, &[], &[]); error = error.or(err); let (output, err) = lite_parse(&output); @@ -1001,6 +1002,13 @@ impl<'a> ParserWorkingSet<'a> { pub fn parse_string(&mut self, span: Span) -> (Expression, Option) { let bytes = self.get_span_contents(span); + let bytes = if (bytes.starts_with(b"\"") && bytes.ends_with(b"\"") && bytes.len() > 1) + || (bytes.starts_with(b"\'") && bytes.ends_with(b"\'") && bytes.len() > 1) + { + &bytes[1..(bytes.len() - 1)] + } else { + bytes + }; if let Ok(token) = String::from_utf8(bytes.into()) { ( @@ -1144,7 +1152,7 @@ impl<'a> ParserWorkingSet<'a> { let span = Span { start, end }; let source = self.get_span_contents(span); - let (output, err) = lex(&source, span.start, &[b'\n', b','], &[b':']); + let (output, err) = lex(source, span.start, &[b'\n', b','], &[b':']); error = error.or(err); let mut args: Vec = vec![]; @@ -1166,7 +1174,8 @@ impl<'a> ParserWorkingSet<'a> { } ParseMode::TypeMode => { // We're seeing two types for the same thing for some reason, error - error = error.or(Some(ParseError::Mismatch("type".into(), span))); + error = error + .or_else(|| Some(ParseError::Mismatch("type".into(), span))); } } } else { @@ -1197,17 +1206,19 @@ impl<'a> ParserWorkingSet<'a> { let short_flag = if !short_flag.starts_with(b"-") || !short_flag.ends_with(b")") { - error = error.or(Some(ParseError::Mismatch( - "short flag".into(), - span, - ))); + error = error.or_else(|| { + Some(ParseError::Mismatch( + "short flag".into(), + span, + )) + }); short_flag } else { &short_flag[1..(short_flag.len() - 1)] }; let short_flag = - String::from_utf8_lossy(&short_flag).to_string(); + String::from_utf8_lossy(short_flag).to_string(); let chars: Vec = short_flag.chars().collect(); let long = String::from_utf8_lossy(&flags[0]).to_string(); let variable_name = flags[0][2..].to_vec(); @@ -1224,10 +1235,12 @@ impl<'a> ParserWorkingSet<'a> { var_id: Some(var_id), })); } else { - error = error.or(Some(ParseError::Mismatch( - "short flag".into(), - span, - ))); + error = error.or_else(|| { + Some(ParseError::Mismatch( + "short flag".into(), + span, + )) + }); } } } else if contents.starts_with(b"-") && contents.len() > 1 { @@ -1239,10 +1252,9 @@ impl<'a> ParserWorkingSet<'a> { let chars: Vec = short_flag.chars().collect(); if chars.len() > 1 { - error = error.or(Some(ParseError::Mismatch( - "short flag".into(), - span, - ))); + error = error.or_else(|| { + Some(ParseError::Mismatch("short flag".into(), span)) + }); args.push(Arg::Flag(Flag { arg: None, @@ -1272,10 +1284,9 @@ impl<'a> ParserWorkingSet<'a> { let short_flag = &contents[2..]; let short_flag = if !short_flag.ends_with(b")") { - error = error.or(Some(ParseError::Mismatch( - "short flag".into(), - span, - ))); + error = error.or_else(|| { + Some(ParseError::Mismatch("short flag".into(), span)) + }); short_flag } else { &short_flag[..(short_flag.len() - 1)] @@ -1289,62 +1300,62 @@ impl<'a> ParserWorkingSet<'a> { match args.last_mut() { Some(Arg::Flag(flag)) => { if flag.short.is_some() { - error = error.or(Some(ParseError::Mismatch( - "one short flag".into(), - span, - ))); + error = error.or_else(|| { + Some(ParseError::Mismatch( + "one short flag".into(), + span, + )) + }); } else { flag.short = Some(chars[0]); } } _ => { - error = error.or(Some(ParseError::Mismatch( - "unknown flag".into(), - span, - ))); + error = error.or_else(|| { + Some(ParseError::Mismatch( + "unknown flag".into(), + span, + )) + }); } } } else { - error = error.or(Some(ParseError::Mismatch( - "short flag".into(), - span, - ))); + error = error.or_else(|| { + Some(ParseError::Mismatch("short flag".into(), span)) + }); } + } else if contents.ends_with(b"?") { + let contents: Vec<_> = contents[..(contents.len() - 1)].into(); + let name = String::from_utf8_lossy(&contents).to_string(); + + let var_id = self.add_variable(contents, Type::Unknown); + + // Positional arg, optional + args.push(Arg::Positional( + PositionalArg { + desc: String::new(), + name, + shape: SyntaxShape::Any, + var_id: Some(var_id), + }, + false, + )) } else { - if contents.ends_with(b"?") { - let contents: Vec<_> = - contents[..(contents.len() - 1)].into(); - let name = String::from_utf8_lossy(&contents).to_string(); + let name = String::from_utf8_lossy(contents).to_string(); + let contents_vec = contents.to_vec(); - let var_id = - self.add_variable(contents.into(), Type::Unknown); + let var_id = self.add_variable(contents_vec, Type::Unknown); - // Positional arg, optional - args.push(Arg::Positional( - PositionalArg { - desc: String::new(), - name, - shape: SyntaxShape::Any, - var_id: Some(var_id), - }, - false, - )) - } else { - let name = String::from_utf8_lossy(contents).to_string(); - let contents_vec = contents.to_vec(); - let var_id = self.add_variable(contents_vec, Type::Unknown); - - // Positional arg, required - args.push(Arg::Positional( - PositionalArg { - desc: String::new(), - name, - shape: SyntaxShape::Any, - var_id: Some(var_id), - }, - true, - )) - } + // Positional arg, required + args.push(Arg::Positional( + PositionalArg { + desc: String::new(), + name, + shape: SyntaxShape::Any, + var_id: Some(var_id), + }, + true, + )) } } ParseMode::TypeMode => { @@ -1387,13 +1398,13 @@ impl<'a> ParserWorkingSet<'a> { match last { Arg::Flag(flag) => { if !flag.desc.is_empty() { - flag.desc.push_str("\n"); + flag.desc.push('\n'); } flag.desc.push_str(&contents); } Arg::Positional(positional, ..) => { if !positional.desc.is_empty() { - positional.desc.push_str("\n"); + positional.desc.push('\n'); } positional.desc.push_str(&contents); } @@ -1431,7 +1442,7 @@ impl<'a> ParserWorkingSet<'a> { ( Expression { - expr: Expr::Signature(sig), + expr: Expr::Signature(Box::new(sig)), span, ty: Type::Unknown, }, @@ -1471,7 +1482,7 @@ impl<'a> ParserWorkingSet<'a> { let span = Span { start, end }; let source = self.get_span_contents(span); - let (output, err) = lex(&source, span.start, &[b'\n', b','], &[]); + let (output, err) = lex(source, span.start, &[b'\n', b','], &[]); error = error.or(err); let (output, err) = lite_parse(&output); @@ -1533,7 +1544,7 @@ impl<'a> ParserWorkingSet<'a> { let source = self.get_span_contents(span); - let (output, err) = lex(&source, start, &[b'\n', b','], &[]); + let (output, err) = lex(source, start, &[b'\n', b','], &[]); error = error.or(err); let (output, err) = lite_parse(&output); @@ -1625,7 +1636,7 @@ impl<'a> ParserWorkingSet<'a> { let source = self.get_span_contents(span); - let (output, err) = lex(&source, start, &[], &[]); + let (output, err) = lex(source, start, &[], &[]); error = error.or(err); let (output, err) = lite_parse(&output); @@ -1726,7 +1737,7 @@ impl<'a> ParserWorkingSet<'a> { } SyntaxShape::List(elem) => { if bytes.starts_with(b"[") { - self.parse_list_expression(span, &elem) + self.parse_list_expression(span, elem) } else { ( Expression::garbage(span), @@ -1919,64 +1930,12 @@ impl<'a> ParserWorkingSet<'a> { (output, error) } - pub fn math_result_type( - &self, - lhs: &mut Expression, - op: &mut Expression, - rhs: &mut Expression, - ) -> (Type, Option) { - match &op.expr { - Expr::Operator(operator) => match operator { - Operator::Plus => match (&lhs.ty, &rhs.ty) { - (Type::Int, Type::Int) => (Type::Int, None), - (Type::Unknown, _) => (Type::Unknown, None), - (_, Type::Unknown) => (Type::Unknown, None), - (Type::Int, _) => { - *rhs = Expression::garbage(rhs.span); - ( - Type::Unknown, - Some(ParseError::Mismatch("int".into(), rhs.span)), - ) - } - (_, Type::Int) => { - *lhs = Expression::garbage(lhs.span); - ( - Type::Unknown, - Some(ParseError::Mismatch("int".into(), lhs.span)), - ) - } - _ => { - *op = Expression::garbage(op.span); - ( - Type::Unknown, - Some(ParseError::Mismatch("math".into(), op.span)), - ) - } - }, - _ => { - *op = Expression::garbage(op.span); - ( - Type::Unknown, - Some(ParseError::Mismatch("math".into(), op.span)), - ) - } - }, - _ => { - *op = Expression::garbage(op.span); - ( - Type::Unknown, - Some(ParseError::Mismatch("operator".into(), op.span)), - ) - } - } - } - pub fn parse_expression(&mut self, spans: &[Span]) -> (Expression, Option) { let bytes = self.get_span_contents(spans[0]); match bytes[0] { b'0' | b'1' | b'2' | b'3' | b'4' | b'5' | b'6' | b'7' | b'8' | b'9' | b'(' | b'{' - | b'[' | b'$' => self.parse_math_expression(spans), + | b'[' | b'$' | b'"' | b'\'' => self.parse_math_expression(spans), _ => self.parse_call(spans), } } @@ -2166,7 +2125,7 @@ impl<'a> ParserWorkingSet<'a> { self.add_file(fname.into(), contents); - let (output, err) = lex(&contents, span_offset, &[], &[]); + let (output, err) = lex(contents, span_offset, &[], &[]); error = error.or(err); let (output, err) = lite_parse(&output); @@ -2183,7 +2142,7 @@ impl<'a> ParserWorkingSet<'a> { let span_offset = self.next_span_start(); - self.add_file("source".into(), source.into()); + self.add_file("source".into(), source); let (output, err) = lex(source, span_offset, &[], &[]); error = error.or(err); diff --git a/src/parser_state.rs b/src/parser_state.rs index d634f28c6f..72c19cb754 100644 --- a/src/parser_state.rs +++ b/src/parser_state.rs @@ -347,9 +347,14 @@ impl<'a> ParserWorkingSet<'a> { None } - pub fn add_variable(&mut self, name: Vec, ty: Type) -> VarId { + pub fn add_variable(&mut self, mut name: Vec, ty: Type) -> VarId { let next_id = self.next_var_id(); + // correct name if necessary + if !name.starts_with(b"$") { + name.insert(0, b'$'); + } + let last = self .delta .scope diff --git a/src/signature.rs b/src/signature.rs index 8d1ce73287..8e12543a0c 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -203,21 +203,15 @@ impl Signature { let mut total = self.required_positional.len() + self.optional_positional.len(); for positional in &self.required_positional { - match positional.shape { - SyntaxShape::Keyword(..) => { - // Keywords have a required argument, so account for that - total += 1; - } - _ => {} + if let SyntaxShape::Keyword(..) = positional.shape { + // Keywords have a required argument, so account for that + total += 1; } } for positional in &self.optional_positional { - match positional.shape { - SyntaxShape::Keyword(..) => { - // Keywords have a required argument, so account for that - total += 1; - } - _ => {} + if let SyntaxShape::Keyword(..) = positional.shape { + // Keywords have a required argument, so account for that + total += 1; } } total @@ -285,10 +279,19 @@ impl Signature { } } -impl Into for Signature { - fn into(self) -> Declaration { +impl From> for Declaration { + fn from(val: Box) -> Self { Declaration { - signature: self, + signature: val, + body: None, + } + } +} + +impl From for Declaration { + fn from(val: Signature) -> Self { + Declaration { + signature: Box::new(val), body: None, } } diff --git a/src/type_check.rs b/src/type_check.rs new file mode 100644 index 0000000000..c01ad01710 --- /dev/null +++ b/src/type_check.rs @@ -0,0 +1,49 @@ +use crate::{parser::Operator, parser_state::Type, Expr, Expression, ParseError, ParserWorkingSet}; + +impl<'a> ParserWorkingSet<'a> { + pub fn math_result_type( + &self, + lhs: &mut Expression, + op: &mut Expression, + rhs: &mut Expression, + ) -> (Type, Option) { + match &op.expr { + Expr::Operator(operator) => match operator { + Operator::Plus => match (&lhs.ty, &rhs.ty) { + (Type::Int, Type::Int) => (Type::Int, 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::Mismatch("int".into(), rhs.span)), + ) + } + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::Mismatch("math".into(), op.span)), + ) + } + }, + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::Mismatch("math".into(), op.span)), + ) + } + }, + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::Mismatch("operator".into(), op.span)), + ) + } + } + } +}