diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 050c1f2..e90fe34 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -595,6 +595,12 @@ pub struct IsDef { pub typ: Token, } +#[derive(Debug, PartialEq, Clone)] +pub struct FailDef { + pub pos: Position, + pub message: Box, +} + /// 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, "")?; } + &Expression::Fail(_) => { + write!(w, "")?; + } } Ok(()) } diff --git a/src/ast/walk.rs b/src/ast/walk.rs index 6a48e82..d59ef66 100644 --- a/src/ast/walk.rs +++ b/src/ast/walk.rs @@ -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 } } diff --git a/src/build/compile_test.rs b/src/build/compile_test.rs index 8aaa4b3..551872e 100644 --- a/src/build/compile_test.rs +++ b/src/build/compile_test.rs @@ -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);"); +} diff --git a/src/build/mod.rs b/src/build/mod.rs index e7c96bb..7182ddd 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -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(), + ))) + }; + } } } } diff --git a/src/error.rs b/src/error.rs index 78a593b..840e5c1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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) } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 1e03432..3febdbc 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -650,6 +650,27 @@ make_fn!( ) ); +make_fn!( + string_expression, Expression>, + do_each!( + val => trace_parse!(quoted_value), + (value_to_expression(val)) + ) +); + +make_fn!( + fail_expression, 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) -> ParseResult { 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), diff --git a/src/tokenizer/mod.rs b/src/tokenizer/mod.rs index 3efe5a0..7ea5329 100644 --- a/src/tokenizer/mod.rs +++ b/src/tokenizer/mod.rs @@ -278,6 +278,10 @@ make_fn!(istok, do_text_token_tok!(TokenType::BAREWORD, "is", WS) ); +make_fn!(failtok, + do_text_token_tok!(TokenType::BAREWORD, "fail", WS) +); + make_fn!(macrotok, do_text_token_tok!(TokenType::BAREWORD, "macro", WS) ); @@ -417,6 +421,7 @@ fn token<'a>(input: OffsetStrIter<'a>) -> Result, Token> { outtok, selecttok, asserttok, + failtok, macrotok, moduletok, importtok,