FEATURE: Declarative failures.

FIXES: #30
This commit is contained in:
Jeremy Wall 2019-01-19 11:18:24 -06:00
parent eaa3e84179
commit 6d5d4c79de
7 changed files with 81 additions and 1 deletions

View File

@ -595,6 +595,12 @@ pub struct IsDef {
pub typ: Token,
}
#[derive(Debug, PartialEq, Clone)]
pub struct FailDef {
pub pos: Position,
pub message: Box<Expression>,
}
/// Encodes a ucg expression. Expressions compute a value from.
#[derive(Debug, PartialEq, Clone)]
pub enum Expression {
@ -617,6 +623,9 @@ pub enum Expression {
Select(SelectDef),
FuncOp(FuncOpDef),
Module(ModuleDef),
// Declarative failure expressions
Fail(FailDef),
}
impl Expression {
@ -636,6 +645,7 @@ impl Expression {
&Expression::FuncOp(ref def) => def.pos(),
&Expression::Include(ref def) => &def.pos,
&Expression::Import(ref def) => &def.pos,
&Expression::Fail(ref def) => &def.pos,
}
}
}
@ -682,6 +692,9 @@ impl fmt::Display for Expression {
&Expression::Import(_) => {
write!(w, "<Include>")?;
}
&Expression::Fail(_) => {
write!(w, "<Fail>")?;
}
}
Ok(())
}

View File

@ -111,7 +111,7 @@ impl<'a> AstWalker<'a> {
Expression::Simple(ref mut val) => {
self.visit_value(val);
}
Expression::Import(_) | Expression::Include(_) => {
Expression::Import(_) | Expression::Include(_) | Expression::Fail(_) => {
//noop
}
}

View File

@ -101,3 +101,21 @@ fn test_format() {
fn test_type_checks() {
assert_build(include_str!("../../integration_tests/types_test.ucg"));
}
#[test]
#[should_panic(expected = "UserDefined")]
fn test_declarative_failures_are_user_defined() {
assert_build("fail \"I am a failure!\";");
}
#[test]
#[should_panic(expected = "Caused By:\n\tI am a failure!")]
fn test_declarative_failures_are_caused_by_msg() {
assert_build("fail \"I am a failure!\";");
}
#[test]
#[should_panic(expected = "1 is a failure!")]
fn test_declarative_failures_can_with_format_expr() {
assert_build("fail \"@ is a failure!\" % (1);");
}

View File

@ -1766,6 +1766,25 @@ impl<'a> FileBuilder<'a> {
&Expression::FuncOp(ref def) => self.eval_func_op(def, scope),
&Expression::Include(ref def) => self.eval_include(def),
&Expression::Import(ref def) => self.eval_import(def),
&Expression::Fail(ref def) => {
let err = self.eval_expr(&def.message, scope)?;
return if let Val::Str(ref s) = err.as_ref() {
Err(Box::new(error::BuildError::new(
s.clone(),
error::ErrorType::UserDefined,
def.pos.clone(),
)))
} else {
Err(Box::new(error::BuildError::new(
format!(
"Expected string form message but got {}",
def.message.as_ref()
),
error::ErrorType::TypeFail,
def.message.pos().clone(),
)))
};
}
}
}
}

View File

@ -35,6 +35,8 @@ pub enum ErrorType {
// Parsing Errors
ParseError,
AssertError,
// User Defined Declarative Errors
UserDefined,
}
impl fmt::Display for ErrorType {
@ -49,6 +51,7 @@ impl fmt::Display for ErrorType {
&ErrorType::ReservedWordError => "ReservedWordError",
&ErrorType::ParseError => "ParseError",
&ErrorType::AssertError => "AssertError",
&ErrorType::UserDefined => "UserDefined",
};
w.write_str(name)
}

View File

@ -650,6 +650,27 @@ make_fn!(
)
);
make_fn!(
string_expression<SliceIter<Token>, Expression>,
do_each!(
val => trace_parse!(quoted_value),
(value_to_expression(val))
)
);
make_fn!(
fail_expression<SliceIter<Token>, Expression>,
do_each!(
pos => pos,
_ => word!("fail"),
msg => must!(wrap_err!(either!(format_expression, string_expression), "Expected failure message")),
(Expression::Fail(FailDef{
pos: pos,
message: Box::new(msg),
}))
)
);
fn unprefixed_expression(input: SliceIter<Token>) -> ParseResult<Expression> {
let _input = input.clone();
either!(
@ -668,6 +689,7 @@ make_fn!(
trace_parse!(func_op_expression),
trace_parse!(macro_expression),
trace_parse!(import_expression),
trace_parse!(fail_expression),
trace_parse!(module_expression),
trace_parse!(select_expression),
trace_parse!(grouped_expression),

View File

@ -278,6 +278,10 @@ make_fn!(istok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::BAREWORD, "is", WS)
);
make_fn!(failtok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::BAREWORD, "fail", WS)
);
make_fn!(macrotok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::BAREWORD, "macro", WS)
);
@ -417,6 +421,7 @@ fn token<'a>(input: OffsetStrIter<'a>) -> Result<OffsetStrIter<'a>, Token> {
outtok,
selecttok,
asserttok,
failtok,
macrotok,
moduletok,
importtok,