From 828585a31206d64ca8be83d5160cc3bf743f9ef5 Mon Sep 17 00:00:00 2001 From: Jonathan Turner <jonathan.d.turner@gmail.com> Date: Tue, 10 Aug 2021 17:55:25 +1200 Subject: [PATCH] add more type helpers and span fixes --- src/errors.rs | 22 ++++++++++++----- src/eval.rs | 59 ++++++++++++++++++++++++++++++++++++--------- src/parser.rs | 2 +- src/parser_state.rs | 25 ++++++++++++++++++- 4 files changed, 89 insertions(+), 19 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 2af1c7a16b..2b0e213e5f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -78,7 +78,7 @@ impl<'a> codespan_reporting::files::Files<'a> for ParserWorkingSet<'a> { if count > line_index { break; } else if count == line_index { - start = Some(byte.0); + start = Some(byte.0 + 1); } } } @@ -286,12 +286,15 @@ pub fn report_shell_error( let config = codespan_reporting::term::Config::default(); let diagnostic = match error { - ShellError::Mismatch(missing, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; + ShellError::OperatorMismatch(operator, ty1, span1, ty2, span2) => { + let (diag_file_id1, diag_range1) = convert_span_to_diag(working_set, span1)?; + let (diag_file_id2, diag_range2) = convert_span_to_diag(working_set, span2)?; Diagnostic::error() - .with_message("Type mismatch during operation") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("expected {}", missing))]) + .with_message(format!("Type mismatch during operation '{}'", operator)) + .with_labels(vec![ + Label::primary(diag_file_id1, diag_range1).with_message(ty1.to_string()), + Label::secondary(diag_file_id2, diag_range2).with_message(ty2.to_string()), + ]) } ShellError::Unsupported(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; @@ -312,6 +315,13 @@ pub fn report_shell_error( Label::primary(diag_file_id, diag_range).with_message("variable not found") ]) } + ShellError::CantConvert(s, span) => { + let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; + Diagnostic::error() + .with_message(format!("Can't convert to {}", s)) + .with_labels(vec![Label::primary(diag_file_id, diag_range) + .with_message(format!("can't convert to {}", s))]) + } }; // println!("DIAG"); diff --git a/src/eval.rs b/src/eval.rs index 3ad4820c69..b30c0e30e4 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,15 +1,17 @@ use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc, time::Instant}; use crate::{ - parser::Operator, Block, BlockId, Call, Expr, Expression, ParserState, Span, Statement, VarId, + parser::Operator, parser_state::Type, Block, BlockId, Call, Expr, Expression, ParserState, + Span, Statement, VarId, }; #[derive(Debug)] pub enum ShellError { - Mismatch(String, Span), + OperatorMismatch(String, Type, Span, Type, Span), Unsupported(Span), InternalError(String), VariableNotFound(Span), + CantConvert(String, Span), } #[derive(Debug, Clone)] @@ -27,7 +29,7 @@ impl Value { pub fn as_string(&self) -> Result<String, ShellError> { match self { Value::String { val, .. } => Ok(val.to_string()), - _ => Err(ShellError::Mismatch("string".into(), self.span())), + _ => Err(ShellError::CantConvert("string".into(), self.span())), } } @@ -42,6 +44,32 @@ impl Value { Value::Nothing { span, .. } => *span, } } + + pub fn with_span(mut self, new_span: Span) -> Value { + match &mut self { + Value::Bool { span, .. } => *span = new_span, + Value::Int { span, .. } => *span = new_span, + Value::Float { span, .. } => *span = new_span, + Value::String { span, .. } => *span = new_span, + Value::List { span, .. } => *span = new_span, + Value::Block { span, .. } => *span = new_span, + Value::Nothing { span, .. } => *span = new_span, + } + + self + } + + pub fn get_type(&self) -> Type { + match self { + Value::Bool { .. } => Type::Bool, + Value::Int { .. } => Type::Int, + Value::Float { .. } => Type::Float, + Value::String { .. } => Type::String, + Value::List { .. } => Type::List(Box::new(Type::Unknown)), // FIXME + Value::Nothing { .. } => Type::Nothing, + Value::Block { .. } => Type::Block, + } + } } impl PartialEq for Value { @@ -80,29 +108,37 @@ impl Display for Value { impl Value { pub fn add(&self, rhs: &Value) -> Result<Value, ShellError> { + let span = crate::parser::span(&[self.span(), rhs.span()]); + match (self, rhs) { (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Int { val: lhs + rhs, - span: Span::unknown(), + span, }), (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float { val: *lhs as f64 + *rhs, - span: Span::unknown(), + span, }), (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Float { val: *lhs + *rhs as f64, - span: Span::unknown(), + span, }), (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float { val: lhs + rhs, - span: Span::unknown(), + span, }), (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => Ok(Value::String { val: lhs.to_string() + rhs, - span: Span::unknown(), + span, }), - _ => Err(ShellError::Mismatch("addition".into(), self.span())), + _ => Err(ShellError::OperatorMismatch( + "+".into(), + self.get_type(), + self.span(), + rhs.get_type(), + rhs.span(), + )), } } } @@ -193,7 +229,7 @@ pub fn eval_operator( expr: Expr::Operator(operator), .. } => Ok(operator.clone()), - Expression { span, .. } => Err(ShellError::Mismatch("operator".to_string(), *span)), + Expression { span, .. } => Err(ShellError::Unsupported(*span)), } } @@ -280,7 +316,7 @@ fn eval_call(state: &State, stack: Stack, call: &Call) -> Result<Value, ShellErr Ok(Value::Nothing { span }) } } - _ => Err(ShellError::Mismatch("bool".into(), Span::unknown())), + _ => Err(ShellError::CantConvert("bool".into(), result.span())), } } else if decl.signature.name == "build-string" { let mut output = vec![]; @@ -385,6 +421,7 @@ pub fn eval_expression( }), Expr::Var(var_id) => stack .get_var(*var_id) + .map(|x| x.with_span(expr.span)) .map_err(move |_| ShellError::VariableNotFound(expr.span)), Expr::Call(call) => eval_call(state, stack, call), Expr::ExternalCall(_, _) => Err(ShellError::Unsupported(expr.span)), diff --git a/src/parser.rs b/src/parser.rs index 91ce8fc8c6..ca27f2ce60 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -375,7 +375,7 @@ fn check_call(command: Span, sig: &Signature, call: &Call) -> Option<ParseError> } } -fn span(spans: &[Span]) -> Span { +pub(crate) fn span(spans: &[Span]) -> Span { let length = spans.len(); if length == 0 { diff --git a/src/parser_state.rs b/src/parser_state.rs index 50e12b8e92..6dafdb6336 100644 --- a/src/parser_state.rs +++ b/src/parser_state.rs @@ -1,6 +1,6 @@ use crate::{parser::Block, Declaration, Span}; use core::panic; -use std::{collections::HashMap, slice::Iter}; +use std::{collections::HashMap, fmt::Display, slice::Iter}; #[derive(Debug)] pub struct ParserState { @@ -15,6 +15,7 @@ pub struct ParserState { #[derive(Clone, Debug, PartialEq, Eq)] pub enum Type { Int, + Float, Bool, String, Block, @@ -24,10 +25,32 @@ pub enum Type { Filesize, List(Box<Type>), Number, + Nothing, Table, Unknown, } +impl Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Type::Block => write!(f, "block"), + Type::Bool => write!(f, "bool"), + Type::ColumnPath => write!(f, "column path"), + Type::Duration => write!(f, "duration"), + Type::FilePath => write!(f, "filepath"), + Type::Filesize => write!(f, "filesize"), + Type::Float => write!(f, "float"), + Type::Int => write!(f, "int"), + Type::List(l) => write!(f, "list<{}>", l), + Type::Nothing => write!(f, "nothing"), + Type::Number => write!(f, "number"), + Type::String => write!(f, "string"), + Type::Table => write!(f, "table"), + Type::Unknown => write!(f, "unknown"), + } + } +} + pub type VarId = usize; pub type DeclId = usize; pub type BlockId = usize;