FEATURE: Add a TRACE pragma for assitance debugging ucg files.

This commit is contained in:
Jeremy Wall 2019-04-26 19:08:41 -05:00
parent c8e48bd9f4
commit ec756636b0
7 changed files with 82 additions and 2 deletions

View File

@ -660,4 +660,25 @@ fail "Oh No This was not what we wanted!";
fail "Expected foo but got @" % ("bar"); fail "Expected foo but got @" % ("bar");
``` ```
Trace Expression
----------------
UCG has a debugging expression that can be helpful to trace values while developing called the trace expression.
Trace expression are any valid expression preceded by the `TRACE` keyword. Trace
expression return the result of the expression unchanged but they also output a
trace statement to stderr printing the result of the expression as well as the
file, line and column where the expression was.
```
let mk_list = func(a, b) => TRACE [a, b];
mk_list(1, 2);
```
This will output a line to stderr something like the below:
TRACE: [1, 2] at file: <file name> line: 1 column: 29
This is helpful when developing shared modules or ucg libraries.
Next: <a href="/reference/statements">Statements</a> Next: <a href="/reference/statements">Statements</a>

View File

@ -57,6 +57,7 @@ mod_keyword: "mod" ;
out_keyword: "out" ; out_keyword: "out" ;
assert_keyword: "assert" ; assert_keyword: "assert" ;
fail_keyword: "fail" ; fail_keyword: "fail" ;
trace_keyword: "TRACE" ;
null_keyword: "NULL" ; null_keyword: "NULL" ;
in_keyword: "in" ; in_keyword: "in" ;
is_keyword: "in" ; is_keyword: "in" ;
@ -176,6 +177,12 @@ fail_expr: fail_keyword, (str | format_expr) ;
not_expr: not_keyword, expr ; not_expr: not_keyword, expr ;
``` ```
#### Not Expression
```
trace_expr: trace_keyword, expr ;
```
#### Non Operator Expression #### Non Operator Expression
``` ```
@ -185,6 +192,8 @@ non_operator_expr: literal
| funcdef | funcdef
| module_def | module_def
| fail_expr | fail_expr
| not_expr
| trace_expr
| format_expr | format_expr
| range_expr | range_expr
| include_expr | include_expr

View File

@ -84,6 +84,15 @@ impl<'a> From<&'a Position> for Position {
} }
} }
impl std::fmt::Display for Position {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
if let Some(ref file) = self.file {
write!(f, "file: {}", file.to_string_lossy().to_string())?;
}
write!(f, "line: {} column: {}", self.line, self.column)
}
}
/// Defines the types of tokens in UCG syntax. /// Defines the types of tokens in UCG syntax.
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] #[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
pub enum TokenType { pub enum TokenType {
@ -626,6 +635,12 @@ pub struct NotDef {
pub expr: Box<Expression>, pub expr: Box<Expression>,
} }
#[derive(Debug, PartialEq, Clone)]
pub struct DebugDef {
pub pos: Position,
pub expr: 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 {
@ -651,6 +666,8 @@ pub enum Expression {
// Declarative failure expressions // Declarative failure expressions
Fail(FailDef), Fail(FailDef),
// Debugging assistance
Debug(DebugDef),
} }
impl Expression { impl Expression {
@ -672,6 +689,7 @@ impl Expression {
&Expression::Import(ref def) => &def.pos, &Expression::Import(ref def) => &def.pos,
&Expression::Fail(ref def) => &def.pos, &Expression::Fail(ref def) => &def.pos,
&Expression::Not(ref def) => &def.pos, &Expression::Not(ref def) => &def.pos,
&Expression::Debug(ref def) => &def.pos,
} }
} }
} }
@ -724,6 +742,9 @@ impl fmt::Display for Expression {
&Expression::Not(ref def) => { &Expression::Not(ref def) => {
write!(w, "!{}", def.expr)?; write!(w, "!{}", def.expr)?;
} }
&Expression::Debug(ref def) => {
write!(w, "!{}", def.expr)?;
}
} }
Ok(()) Ok(())
} }

View File

@ -129,6 +129,9 @@ impl<'a> AstWalker<'a> {
Expression::Not(ref mut def) => { Expression::Not(ref mut def) => {
self.walk_expression(def.expr.as_mut()); self.walk_expression(def.expr.as_mut());
} }
Expression::Debug(ref mut def) => {
self.walk_expression(&mut def.expr);
}
} }
} }

View File

@ -1944,8 +1944,8 @@ impl<'a> FileBuilder<'a> {
&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) => { &Expression::Fail(ref def) => {
let err = self.eval_expr(&def.message, scope)?; let val = self.eval_expr(&def.message, scope)?;
return if let Val::Str(ref s) = err.as_ref() { return if let Val::Str(ref s) = val.as_ref() {
Err(error::BuildError::with_pos( Err(error::BuildError::with_pos(
s.clone(), s.clone(),
error::ErrorType::UserDefined, error::ErrorType::UserDefined,
@ -1964,6 +1964,13 @@ impl<'a> FileBuilder<'a> {
.to_boxed()) .to_boxed())
}; };
} }
&Expression::Debug(ref def) => {
let val = self.eval_expr(&def.expr, scope);
if let Ok(ref val) = val {
eprintln!("TRACE: {} at {}", val, def.pos);
}
val
}
&Expression::Not(ref def) => { &Expression::Not(ref def) => {
let val = self.eval_expr(&def.expr, scope)?; let val = self.eval_expr(&def.expr, scope)?;
return if let Val::Boolean(b) = val.as_ref() { return if let Val::Boolean(b) = val.as_ref() {

View File

@ -714,6 +714,19 @@ make_fn!(
) )
); );
make_fn!(
trace_expression<SliceIter<Token>, Expression>,
do_each!(
pos => pos,
_ => word!("TRACE"),
expr => must!(wrap_err!(expression, "Expected failure message")),
(Expression::Debug(DebugDef{
pos: pos,
expr: Box::new(expr),
}))
)
);
make_fn!( make_fn!(
not_expression<SliceIter<Token>, Expression>, not_expression<SliceIter<Token>, Expression>,
do_each!( do_each!(
@ -745,6 +758,7 @@ make_fn!(
trace_parse!(func_op_expression), trace_parse!(func_op_expression),
trace_parse!(func_expression), trace_parse!(func_expression),
trace_parse!(import_expression), trace_parse!(import_expression),
trace_parse!(trace_expression),
trace_parse!(not_expression), trace_parse!(not_expression),
trace_parse!(fail_expression), trace_parse!(fail_expression),
trace_parse!(module_expression), trace_parse!(module_expression),

View File

@ -286,6 +286,10 @@ make_fn!(nottok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::BAREWORD, "not", WS) do_text_token_tok!(TokenType::BAREWORD, "not", WS)
); );
make_fn!(tracetok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::BAREWORD, "TRACE", WS)
);
make_fn!(failtok<OffsetStrIter, Token>, make_fn!(failtok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::BAREWORD, "fail", WS) do_text_token_tok!(TokenType::BAREWORD, "fail", WS)
); );
@ -432,6 +436,7 @@ fn token<'a>(input: OffsetStrIter<'a>) -> Result<OffsetStrIter<'a>, Token> {
selecttok, selecttok,
asserttok, asserttok,
failtok, failtok,
tracetok,
functok, functok,
moduletok, moduletok,
importtok, importtok,