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");
```
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>

View File

@ -57,6 +57,7 @@ mod_keyword: "mod" ;
out_keyword: "out" ;
assert_keyword: "assert" ;
fail_keyword: "fail" ;
trace_keyword: "TRACE" ;
null_keyword: "NULL" ;
in_keyword: "in" ;
is_keyword: "in" ;
@ -176,6 +177,12 @@ fail_expr: fail_keyword, (str | format_expr) ;
not_expr: not_keyword, expr ;
```
#### Not Expression
```
trace_expr: trace_keyword, expr ;
```
#### Non Operator Expression
```
@ -185,6 +192,8 @@ non_operator_expr: literal
| funcdef
| module_def
| fail_expr
| not_expr
| trace_expr
| format_expr
| range_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.
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
pub enum TokenType {
@ -626,6 +635,12 @@ pub struct NotDef {
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.
#[derive(Debug, PartialEq, Clone)]
pub enum Expression {
@ -651,6 +666,8 @@ pub enum Expression {
// Declarative failure expressions
Fail(FailDef),
// Debugging assistance
Debug(DebugDef),
}
impl Expression {
@ -672,6 +689,7 @@ impl Expression {
&Expression::Import(ref def) => &def.pos,
&Expression::Fail(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) => {
write!(w, "!{}", def.expr)?;
}
&Expression::Debug(ref def) => {
write!(w, "!{}", def.expr)?;
}
}
Ok(())
}

View File

@ -129,6 +129,9 @@ impl<'a> AstWalker<'a> {
Expression::Not(ref mut def) => {
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::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() {
let val = self.eval_expr(&def.message, scope)?;
return if let Val::Str(ref s) = val.as_ref() {
Err(error::BuildError::with_pos(
s.clone(),
error::ErrorType::UserDefined,
@ -1964,6 +1964,13 @@ impl<'a> FileBuilder<'a> {
.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) => {
let val = self.eval_expr(&def.expr, scope)?;
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!(
not_expression<SliceIter<Token>, Expression>,
do_each!(
@ -745,6 +758,7 @@ make_fn!(
trace_parse!(func_op_expression),
trace_parse!(func_expression),
trace_parse!(import_expression),
trace_parse!(trace_expression),
trace_parse!(not_expression),
trace_parse!(fail_expression),
trace_parse!(module_expression),

View File

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