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, 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. /// Encodes a ucg expression. Expressions compute a value from.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Expression { pub enum Expression {
@ -617,6 +623,9 @@ pub enum Expression {
Select(SelectDef), Select(SelectDef),
FuncOp(FuncOpDef), FuncOp(FuncOpDef),
Module(ModuleDef), Module(ModuleDef),
// Declarative failure expressions
Fail(FailDef),
} }
impl Expression { impl Expression {
@ -636,6 +645,7 @@ impl Expression {
&Expression::FuncOp(ref def) => def.pos(), &Expression::FuncOp(ref def) => def.pos(),
&Expression::Include(ref def) => &def.pos, &Expression::Include(ref def) => &def.pos,
&Expression::Import(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(_) => { &Expression::Import(_) => {
write!(w, "<Include>")?; write!(w, "<Include>")?;
} }
&Expression::Fail(_) => {
write!(w, "<Fail>")?;
}
} }
Ok(()) Ok(())
} }

View File

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

View File

@ -101,3 +101,21 @@ fn test_format() {
fn test_type_checks() { fn test_type_checks() {
assert_build(include_str!("../../integration_tests/types_test.ucg")); 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::FuncOp(ref def) => self.eval_func_op(def, scope),
&Expression::Include(ref def) => self.eval_include(def), &Expression::Include(ref def) => self.eval_include(def),
&Expression::Import(ref def) => self.eval_import(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 // Parsing Errors
ParseError, ParseError,
AssertError, AssertError,
// User Defined Declarative Errors
UserDefined,
} }
impl fmt::Display for ErrorType { impl fmt::Display for ErrorType {
@ -49,6 +51,7 @@ impl fmt::Display for ErrorType {
&ErrorType::ReservedWordError => "ReservedWordError", &ErrorType::ReservedWordError => "ReservedWordError",
&ErrorType::ParseError => "ParseError", &ErrorType::ParseError => "ParseError",
&ErrorType::AssertError => "AssertError", &ErrorType::AssertError => "AssertError",
&ErrorType::UserDefined => "UserDefined",
}; };
w.write_str(name) 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> { fn unprefixed_expression(input: SliceIter<Token>) -> ParseResult<Expression> {
let _input = input.clone(); let _input = input.clone();
either!( either!(
@ -668,6 +689,7 @@ make_fn!(
trace_parse!(func_op_expression), trace_parse!(func_op_expression),
trace_parse!(macro_expression), trace_parse!(macro_expression),
trace_parse!(import_expression), trace_parse!(import_expression),
trace_parse!(fail_expression),
trace_parse!(module_expression), trace_parse!(module_expression),
trace_parse!(select_expression), trace_parse!(select_expression),
trace_parse!(grouped_expression), trace_parse!(grouped_expression),

View File

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