Fix a bunch of bugs

This commit is contained in:
Yehuda Katz 2019-06-23 18:55:31 -06:00
parent bed5ba52d3
commit 7957fc502f
10 changed files with 217 additions and 97 deletions

View File

@ -156,8 +156,8 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
LineResult::Error(mut line, err) => { LineResult::Error(mut line, err) => {
rl.add_history_entry(line.clone()); rl.add_history_entry(line.clone());
match err {
ShellError::Diagnostic(diag) => { let diag = err.to_diagnostic();
let host = context.host.lock().unwrap(); let host = context.host.lock().unwrap();
let writer = host.err_termcolor(); let writer = host.err_termcolor();
line.push_str(" "); line.push_str(" ");
@ -166,35 +166,50 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
language_reporting::emit( language_reporting::emit(
&mut writer.lock(), &mut writer.lock(),
&files, &files,
&diag.diagnostic, &diag,
&language_reporting::DefaultConfig, &language_reporting::DefaultConfig,
) )
.unwrap(); .unwrap();
}
ShellError::TypeError(desc) => context // match err {
.host // ShellError::Diagnostic(diag) => {
.lock() // let host = context.host.lock().unwrap();
.unwrap() // let writer = host.err_termcolor();
.stdout(&format!("TypeError: {}", desc)), // line.push_str(" ");
// let files = crate::parser::Files::new(line);
ShellError::MissingProperty { subpath, .. } => context // language_reporting::emit(
.host // &mut writer.lock(),
.lock() // &files,
.unwrap() // &diag.diagnostic,
.stdout(&format!("Missing property {}", subpath)), // &language_reporting::DefaultConfig,
// )
// .unwrap();
// }
ShellError::String(_) => { // ShellError::TypeError(desc) => context
context.host.lock().unwrap().stdout(&format!("{}", err)) // .host
} // .lock()
} // .unwrap()
// .stdout(&format!("TypeError: {}", desc)),
// ShellError::MissingProperty { subpath, .. } => context
// .host
// .lock()
// .unwrap()
// .stdout(&format!("Missing property {}", subpath)),
// ShellError::String(_) => {
// context.host.lock().unwrap().stdout(&format!("{}", err))
// }
// }
} }
LineResult::Break => { LineResult::Break => {
break; break;
} }
LineResult::FatalError(err) => { LineResult::FatalError(_, err) => {
context context
.host .host
.lock() .lock()
@ -216,24 +231,24 @@ enum LineResult {
Break, Break,
#[allow(unused)] #[allow(unused)]
FatalError(ShellError), FatalError(String, ShellError),
} }
impl std::ops::Try for LineResult { impl std::ops::Try for LineResult {
type Ok = Option<String>; type Ok = Option<String>;
type Error = ShellError; type Error = (String, ShellError);
fn into_result(self) -> Result<Option<String>, ShellError> { fn into_result(self) -> Result<Option<String>, (String, ShellError)> {
match self { match self {
LineResult::Success(s) => Ok(Some(s)), LineResult::Success(s) => Ok(Some(s)),
LineResult::Error(_, s) => Err(s), LineResult::Error(string, err) => Err((string, err)),
LineResult::Break => Ok(None), LineResult::Break => Ok(None),
LineResult::CtrlC => Ok(None), LineResult::CtrlC => Ok(None),
LineResult::FatalError(err) => Err(err), LineResult::FatalError(string, err) => Err((string, err)),
} }
} }
fn from_error(v: ShellError) -> Self { fn from_error(v: (String, ShellError)) -> Self {
LineResult::Error(String::new(), v) LineResult::Error(v.0, v.1)
} }
fn from_ok(v: Option<String>) -> Self { fn from_ok(v: Option<String>) -> Self {
@ -260,7 +275,8 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
debug!("=== Parsed ==="); debug!("=== Parsed ===");
debug!("{:#?}", result); debug!("{:#?}", result);
let mut pipeline = classify_pipeline(&result, ctx, &Text::from(line))?; let mut pipeline = classify_pipeline(&result, ctx, &Text::from(line))
.map_err(|err| (line.clone(), err))?;
match pipeline.commands.last() { match pipeline.commands.last() {
Some(ClassifiedCommand::Sink(_)) => {} Some(ClassifiedCommand::Sink(_)) => {}
@ -438,7 +454,13 @@ fn classify_command(
//Some(args) => args.iter().map(|i| i.as_external_arg(source)).collect(), //Some(args) => args.iter().map(|i| i.as_external_arg(source)).collect(),
Some(args) => args Some(args) => args
.iter() .iter()
.map(|i| Spanned::from_item(i.as_external_arg(source), i.span())) .filter_map(|i| match i {
TokenNode::Whitespace(_) => None,
other => Some(Spanned::from_item(
other.as_external_arg(source),
other.span(),
)),
})
.collect(), .collect(),
None => vec![], None => vec![],
}; };
@ -453,8 +475,12 @@ fn classify_command(
} }
} }
_ => Err(ShellError::unimplemented( call => Err(ShellError::diagnostic(
"classify_command on command whose head is not bare", language_reporting::Diagnostic::new(
language_reporting::Severity::Error,
"Invalid command",
)
.with_label(language_reporting::Label::new_primary(call.head().span())),
)), )),
} }
} }

View File

@ -1,17 +1,51 @@
#[allow(unused)] #[allow(unused)]
use crate::prelude::*; use crate::prelude::*;
use crate::parser::Span; use crate::parser::{Span, Spanned};
use derive_new::new; use derive_new::new;
use language_reporting::{Diagnostic, Label, Severity}; use language_reporting::{Diagnostic, Label, Severity};
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
pub enum Description {
Source(Spanned<String>),
Synthetic(String),
}
impl Description {
pub fn from(item: Spanned<impl Into<String>>) -> Description {
match item {
Spanned {
span: Span { start: 0, end: 0 },
item,
} => Description::Synthetic(item.into()),
Spanned { span, item } => Description::Source(Spanned::from_item(item.into(), span)),
}
}
}
impl Description {
fn into_label(self) -> Result<Label<Span>, String> {
match self {
Description::Source(s) => Ok(Label::new_primary(s.span).with_message(s.item)),
Description::Synthetic(s) => Err(s),
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
pub enum ShellError { pub enum ShellError {
String(StringError), String(StringError),
TypeError(String), TypeError(Spanned<String>),
MissingProperty { subpath: String, expr: String }, MissingProperty {
subpath: Description,
expr: Description,
},
Diagnostic(ShellDiagnostic), Diagnostic(ShellDiagnostic),
CoerceError {
left: Spanned<String>,
right: Spanned<String>,
},
} }
impl ShellError { impl ShellError {
@ -24,7 +58,7 @@ impl ShellError {
nom::Err::Incomplete(_) => unreachable!(), nom::Err::Incomplete(_) => unreachable!(),
nom::Err::Failure(span) | nom::Err::Error(span) => { nom::Err::Failure(span) | nom::Err::Error(span) => {
let diagnostic = let diagnostic =
Diagnostic::new(Severity::Error, format!("{:?}", span)) Diagnostic::new(Severity::Error, format!("Parse Error"))
.with_label(Label::new_primary(Span::from(span.0))); .with_label(Label::new_primary(Span::from(span.0)));
ShellError::diagnostic(diagnostic) ShellError::diagnostic(diagnostic)
@ -57,6 +91,41 @@ impl ShellError {
ShellError::Diagnostic(ShellDiagnostic { diagnostic }) ShellError::Diagnostic(ShellDiagnostic { diagnostic })
} }
crate fn to_diagnostic(self) -> Diagnostic<Span> {
match self {
ShellError::String(StringError { title, .. }) => {
Diagnostic::new(Severity::Error, title)
}
ShellError::TypeError(s) => Diagnostic::new(Severity::Error, "Type Error")
.with_label(Label::new_primary(s.span).with_message(s.item)),
ShellError::MissingProperty { subpath, expr } => {
let subpath = subpath.into_label();
let expr = expr.into_label();
let mut diag = Diagnostic::new(Severity::Error, "Missing property");
match subpath {
Ok(label) => diag = diag.with_label(label),
Err(ty) => diag.message = format!("Missing property (for {})", ty),
}
if let Ok(label) = expr {
diag = diag.with_label(label);
}
diag
}
ShellError::Diagnostic(diag) => diag.diagnostic,
ShellError::CoerceError { left, right } => {
Diagnostic::new(Severity::Error, "Coercion error")
.with_label(Label::new_primary(left.span).with_message(left.item))
.with_label(Label::new_secondary(right.span).with_message(right.item))
}
}
}
crate fn labeled_error( crate fn labeled_error(
msg: impl Into<String>, msg: impl Into<String>,
label: impl Into<String>, label: impl Into<String>,
@ -177,6 +246,7 @@ impl std::fmt::Display for ShellError {
ShellError::TypeError { .. } => write!(f, "TypeError"), ShellError::TypeError { .. } => write!(f, "TypeError"),
ShellError::MissingProperty { .. } => write!(f, "MissingProperty"), ShellError::MissingProperty { .. } => write!(f, "MissingProperty"),
ShellError::Diagnostic(_) => write!(f, "<diagnostic>"), ShellError::Diagnostic(_) => write!(f, "<diagnostic>"),
ShellError::CoerceError { .. } => write!(f, "CoerceError"),
} }
} }
} }

View File

@ -1,3 +1,4 @@
use crate::errors::Description;
use crate::object::base::Block; use crate::object::base::Block;
use crate::parser::{ use crate::parser::{
hir::{self, Expression, RawExpression}, hir::{self, Expression, RawExpression},
@ -37,11 +38,11 @@ crate fn evaluate_baseline_expr(
let right = evaluate_baseline_expr(binary.right(), registry, scope, source)?; let right = evaluate_baseline_expr(binary.right(), registry, scope, source)?;
match left.compare(binary.op(), &*right) { match left.compare(binary.op(), &*right) {
Some(result) => Ok(Spanned::from_item(Value::boolean(result), *expr.span())), Ok(result) => Ok(Spanned::from_item(Value::boolean(result), *expr.span())),
None => Err(ShellError::unimplemented(&format!( Err((left_type, right_type)) => Err(ShellError::CoerceError {
"Comparison failure {:?}", left: binary.left().copy_span(left_type),
binary right: binary.right().copy_span(right_type),
))), }),
} }
} }
RawExpression::Block(block) => Ok(Spanned::from_item( RawExpression::Block(block) => Ok(Spanned::from_item(
@ -50,18 +51,26 @@ crate fn evaluate_baseline_expr(
)), )),
RawExpression::Path(path) => { RawExpression::Path(path) => {
let value = evaluate_baseline_expr(path.head(), registry, scope, source)?; let value = evaluate_baseline_expr(path.head(), registry, scope, source)?;
let mut value = value.item(); let mut item = value;
for name in path.tail() { for name in path.tail() {
let next = value.get_data_by_key(name); let next = item.get_data_by_key(name);
match next { match next {
None => return Err(ShellError::unimplemented("Invalid property from path")), None => {
Some(next) => value = next, return Err(ShellError::MissingProperty {
subpath: Description::from(item.spanned_type_name()),
expr: Description::from(name.clone()),
})
}
Some(next) => {
item =
Spanned::from_item(next.clone(), (expr.span().start, name.span().end))
}
}; };
} }
Ok(Spanned::from_item(value.clone(), expr.span())) Ok(Spanned::from_item(item.item().clone(), expr.span()))
} }
RawExpression::Boolean(_boolean) => unimplemented!(), RawExpression::Boolean(_boolean) => unimplemented!(),
} }

View File

@ -165,7 +165,7 @@ impl Block {
} }
} }
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)]
pub enum Value { pub enum Value {
Primitive(Primitive), Primitive(Primitive),
Object(crate::object::Dictionary), Object(crate::object::Dictionary),
@ -211,6 +211,13 @@ impl fmt::Debug for ValueDebug<'a> {
} }
} }
impl Spanned<Value> {
crate fn spanned_type_name(&self) -> Spanned<String> {
let name = self.type_name();
Spanned::from_item(name, self.span)
}
}
impl Value { impl Value {
crate fn type_name(&self) -> String { crate fn type_name(&self) -> String {
match self { match self {
@ -301,7 +308,7 @@ impl Value {
} }
#[allow(unused)] #[allow(unused)]
crate fn compare(&self, operator: &Operator, other: &Value) -> Option<bool> { crate fn compare(&self, operator: &Operator, other: &Value) -> Result<bool, (String, String)> {
match operator { match operator {
_ => { _ => {
let coerced = coerce_compare(self, other)?; let coerced = coerce_compare(self, other)?;
@ -322,7 +329,7 @@ impl Value {
_ => false, _ => false,
}; };
Some(result) Ok(result)
} }
} }
} }
@ -383,18 +390,6 @@ impl Value {
} }
} }
#[allow(unused)]
crate fn as_bool(&self) -> Result<bool, ShellError> {
match self {
Value::Primitive(Primitive::Boolean(b)) => Ok(*b),
// TODO: this should definitely be more general with better errors
other => Err(ShellError::string(format!(
"Expected integer, got {:?}",
other
))),
}
}
crate fn is_true(&self) -> bool { crate fn is_true(&self) -> bool {
match self { match self {
Value::Primitive(Primitive::Boolean(true)) => true, Value::Primitive(Primitive::Boolean(true)) => true,
@ -591,24 +586,27 @@ impl CompareValues {
} }
} }
fn coerce_compare(left: &Value, right: &Value) -> Option<CompareValues> { fn coerce_compare(left: &Value, right: &Value) -> Result<CompareValues, (String, String)> {
match (left, right) { match (left, right) {
(Value::Primitive(left), Value::Primitive(right)) => coerce_compare_primitive(left, right), (Value::Primitive(left), Value::Primitive(right)) => coerce_compare_primitive(left, right),
_ => None, _ => Err((left.type_name(), right.type_name())),
} }
} }
fn coerce_compare_primitive(left: &Primitive, right: &Primitive) -> Option<CompareValues> { fn coerce_compare_primitive(
left: &Primitive,
right: &Primitive,
) -> Result<CompareValues, (String, String)> {
use Primitive::*; use Primitive::*;
match (left, right) { Ok(match (left, right) {
(Int(left), Int(right)) => Some(CompareValues::Ints(*left, *right)), (Int(left), Int(right)) => CompareValues::Ints(*left, *right),
(Float(left), Int(right)) => Some(CompareValues::Floats(*left, (*right as f64).into())), (Float(left), Int(right)) => CompareValues::Floats(*left, (*right as f64).into()),
(Int(left), Float(right)) => Some(CompareValues::Floats((*left as f64).into(), *right)), (Int(left), Float(right)) => CompareValues::Floats((*left as f64).into(), *right),
(Int(left), Bytes(right)) => Some(CompareValues::Bytes(*left as i128, *right as i128)), (Int(left), Bytes(right)) => CompareValues::Bytes(*left as i128, *right as i128),
(Bytes(left), Int(right)) => Some(CompareValues::Bytes(*left as i128, *right as i128)), (Bytes(left), Int(right)) => CompareValues::Bytes(*left as i128, *right as i128),
(String(left), String(right)) => Some(CompareValues::String(left.clone(), right.clone())), (String(left), String(right)) => CompareValues::String(left.clone(), right.clone()),
_ => None, _ => return Err((left.type_name(), right.type_name())),
} })
} }

View File

@ -39,7 +39,7 @@ impl DescriptorName {
} }
} }
#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, new)] #[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, new)]
pub struct DataDescriptor { pub struct DataDescriptor {
crate name: DescriptorName, crate name: DescriptorName,
crate readonly: bool, crate readonly: bool,

View File

@ -15,9 +15,18 @@ pub struct Dictionary {
} }
impl PartialOrd for Dictionary { impl PartialOrd for Dictionary {
// TODO: FIXME fn partial_cmp(&self, other: &Dictionary) -> Option<Ordering> {
fn partial_cmp(&self, _other: &Dictionary) -> Option<Ordering> { let this: Vec<&DataDescriptor> = self.entries.keys().collect();
Some(Ordering::Less) let that: Vec<&DataDescriptor> = other.entries.keys().collect();
if this != that {
return this.partial_cmp(&that);
}
let this: Vec<&Value> = self.entries.values().collect();
let that: Vec<&Value> = self.entries.values().collect();
this.partial_cmp(&that)
} }
} }
@ -56,9 +65,18 @@ impl From<IndexMap<String, Value>> for Dictionary {
} }
impl Ord for Dictionary { impl Ord for Dictionary {
// TODO: FIXME fn cmp(&self, other: &Dictionary) -> Ordering {
fn cmp(&self, _other: &Dictionary) -> Ordering { let this: Vec<&DataDescriptor> = self.entries.keys().collect();
Ordering::Less let that: Vec<&DataDescriptor> = other.entries.keys().collect();
if this != that {
return this.cmp(&that);
}
let this: Vec<&Value> = self.entries.values().collect();
let that: Vec<&Value> = self.entries.values().collect();
this.cmp(&that)
} }
} }
@ -69,7 +87,6 @@ impl PartialOrd<Value> for Dictionary {
} }
impl PartialEq<Value> for Dictionary { impl PartialEq<Value> for Dictionary {
// TODO: FIXME
fn eq(&self, other: &Value) -> bool { fn eq(&self, other: &Value) -> bool {
match other { match other {
Value::Object(d) => self == d, Value::Object(d) => self == d,

View File

@ -1,7 +1,7 @@
use derive_new::new; use derive_new::new;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash, new)] #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, new)]
pub enum Type { pub enum Type {
Any, Any,
} }

View File

@ -45,7 +45,7 @@ pub fn baseline_parse_next_expr(
let first = next_token(&mut tokens); let first = next_token(&mut tokens);
let first = match first { let first = match first {
None => return Err(ShellError::unimplemented("Expected token, found none")), None => return Err(ShellError::string("Expected token, found none")),
Some(token) => baseline_parse_semantic_token(token, source)?, Some(token) => baseline_parse_semantic_token(token, source)?,
}; };

View File

@ -1,8 +1,11 @@
use crate::Text; use crate::Text;
use derive_new::new; use derive_new::new;
use getset::Getters; use getset::Getters;
use serde_derive::{Deserialize, Serialize};
#[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Getters)] #[derive(
new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters,
)]
#[get = "crate"] #[get = "crate"]
pub struct Spanned<T> { pub struct Spanned<T> {
crate span: Span, crate span: Span,
@ -46,7 +49,7 @@ impl<T> Spanned<T> {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
pub struct Span { pub struct Span {
crate start: usize, crate start: usize,
crate end: usize, crate end: usize,

View File

@ -61,10 +61,7 @@ impl Highlighter for Helper {
let tokens = crate::parser::pipeline(nom_input(line)); let tokens = crate::parser::pipeline(nom_input(line));
match tokens { match tokens {
Err(e) => { Err(_) => Cow::Borrowed(line),
println!("error: {:?}", e);
Cow::Borrowed(line)
}
Ok((_rest, v)) => { Ok((_rest, v)) => {
let mut out = String::new(); let mut out = String::new();
let pipeline = match v.as_pipeline() { let pipeline = match v.as_pipeline() {