WIP improve error infrastructure

Also simplify commands and reduce papercuts
This commit is contained in:
Yehuda Katz
2019-07-03 13:31:15 -07:00
parent 5d73c73fe8
commit 34033afce4
46 changed files with 1087 additions and 520 deletions

View File

@ -23,7 +23,7 @@ impl language_reporting::ReportingFiles for Files {
0
}
fn file_name(&self, _file: Self::FileId) -> FileName {
FileName::Verbatim(format!("<eval>"))
FileName::Verbatim(format!("shell"))
}
fn byte_index(&self, _file: Self::FileId, _line: usize, _column: usize) -> Option<usize> {
unimplemented!("byte_index")

View File

@ -1,5 +1,5 @@
use crate::errors::{ArgumentError, ShellError};
use crate::parser::registry::{CommandConfig, CommandRegistry, NamedType};
use crate::parser::registry::{CommandConfig, CommandRegistry, NamedType, PositionalType};
use crate::parser::{baseline_parse_tokens, CallNode, Span, Spanned};
use crate::parser::{
hir::{self, NamedArguments},
@ -86,30 +86,32 @@ fn parse_command_tail(
named.insert_switch(name, flag);
}
NamedType::Mandatory(kind) => match extract_mandatory(name, tail, source, command_span)
{
Err(err) => return Err(err), // produce a correct diagnostic
Ok((pos, flag)) => {
tail.move_to(pos);
NamedType::Mandatory(kind) => {
match extract_mandatory(config, name, tail, source, command_span) {
Err(err) => return Err(err), // produce a correct diagnostic
Ok((pos, flag)) => {
tail.move_to(pos);
if tail.at_end() {
return Err(ShellError::ArgumentError {
error: ArgumentError::MissingValueForName(name.to_string()),
span: flag.span,
});
if tail.at_end() {
return Err(ShellError::ArgumentError {
command: config.name().clone(),
error: ArgumentError::MissingValueForName(name.to_string()),
span: flag.span,
});
}
let expr = hir::baseline_parse_next_expr(
tail,
registry,
source,
kind.to_coerce_hint(),
)?;
tail.restart();
named.insert_mandatory(name, expr);
}
let expr = hir::baseline_parse_next_expr(
tail,
registry,
source,
kind.to_coerce_hint(),
)?;
tail.restart();
named.insert_mandatory(name, expr);
}
},
}
NamedType::Optional(kind) => match extract_optional(name, tail, source) {
Err(err) => return Err(err), // produce a correct diagnostic
Ok(Some((pos, flag))) => {
@ -117,6 +119,7 @@ fn parse_command_tail(
if tail.at_end() {
return Err(ShellError::ArgumentError {
command: config.name().clone(),
error: ArgumentError::MissingValueForName(name.to_string()),
span: flag.span,
});
@ -144,16 +147,26 @@ fn parse_command_tail(
trace_remaining("after named", tail.clone(), source);
let mut positional = vec![];
let mandatory = config.mandatory_positional();
for arg in mandatory {
trace!("Processing mandatory {:?}", arg);
for arg in config.positional() {
trace!("Processing positional {:?}", arg);
if tail.len() == 0 {
return Err(ShellError::ArgumentError {
error: ArgumentError::MissingMandatoryPositional(arg.name().to_string()),
span: command_span,
});
match arg {
PositionalType::Mandatory(..) => {
if tail.len() == 0 {
return Err(ShellError::ArgumentError {
command: config.name().clone(),
error: ArgumentError::MissingMandatoryPositional(arg.name().to_string()),
span: command_span,
});
}
}
PositionalType::Optional(..) => {
if tail.len() == 0 {
break;
}
}
}
let result = hir::baseline_parse_next_expr(tail, registry, source, arg.to_coerce_hint())?;
@ -161,21 +174,7 @@ fn parse_command_tail(
positional.push(result);
}
trace_remaining("after mandatory", tail.clone(), source);
let optional = config.optional_positional();
for arg in optional {
if tail.len() == 0 {
break;
}
let result = hir::baseline_parse_next_expr(tail, registry, source, arg.to_coerce_hint())?;
positional.push(result);
}
trace_remaining("after optional", tail.clone(), source);
trace_remaining("after positional", tail.clone(), source);
// TODO: Only do this if rest params are specified
let remainder = baseline_parse_tokens(tail, registry, source)?;
@ -207,6 +206,7 @@ fn extract_switch(name: &str, tokens: &mut hir::TokensIterator<'_>, source: &Tex
}
fn extract_mandatory(
config: &CommandConfig,
name: &str,
tokens: &mut hir::TokensIterator<'a>,
source: &Text,
@ -216,6 +216,7 @@ fn extract_mandatory(
match flag {
None => Err(ShellError::ArgumentError {
command: config.name().clone(),
error: ArgumentError::MissingMandatoryFlag(name.to_string()),
span,
}),

View File

@ -36,22 +36,38 @@ impl NamedValue {
#[allow(unused)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PositionalType {
Value(String),
Block(String),
Mandatory(String, PositionalValue),
Optional(String, PositionalValue),
}
#[derive(Debug, Clone)]
pub enum PositionalValue {
Value,
Block,
}
impl PositionalType {
crate fn mandatory(name: &str, kind: &str) -> PositionalType {
match kind {
"Block" => PositionalType::Mandatory(name.to_string(), PositionalValue::Block),
_ => PositionalType::Mandatory(name.to_string(), PositionalValue::Value),
}
}
crate fn to_coerce_hint(&self) -> Option<ExpressionKindHint> {
match self {
PositionalType::Value(_) => None,
PositionalType::Block(_) => Some(ExpressionKindHint::Block),
PositionalType::Mandatory(_, PositionalValue::Block)
| PositionalType::Optional(_, PositionalValue::Block) => {
Some(ExpressionKindHint::Block)
}
_ => None,
}
}
crate fn name(&self) -> &str {
match self {
PositionalType::Value(s) => s,
PositionalType::Block(s) => s,
PositionalType::Mandatory(s, _) => s,
PositionalType::Optional(s, _) => s,
}
}
}
@ -60,8 +76,7 @@ impl PositionalType {
#[get = "crate"]
pub struct CommandConfig {
pub name: String,
pub mandatory_positional: Vec<PositionalType>,
pub optional_positional: Vec<PositionalType>,
crate positional: Vec<PositionalType>,
pub rest_positional: bool,
pub named: IndexMap<String, NamedType>,
pub is_filter: bool,