From ec756636b009206c0655cc1541b0dc40cdbd167c Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Fri, 26 Apr 2019 19:08:41 -0500 Subject: [PATCH] FEATURE: Add a TRACE pragma for assitance debugging ucg files. --- docsite/site/content/reference/expressions.md | 21 +++++++++++++++++++ docsite/site/content/reference/grammar.md | 9 ++++++++ src/ast/mod.rs | 21 +++++++++++++++++++ src/ast/walk.rs | 3 +++ src/build/mod.rs | 11 ++++++++-- src/parse/mod.rs | 14 +++++++++++++ src/tokenizer/mod.rs | 5 +++++ 7 files changed, 82 insertions(+), 2 deletions(-) diff --git a/docsite/site/content/reference/expressions.md b/docsite/site/content/reference/expressions.md index 08791cb..d713dca 100644 --- a/docsite/site/content/reference/expressions.md +++ b/docsite/site/content/reference/expressions.md @@ -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: line: 1 column: 29 + +This is helpful when developing shared modules or ucg libraries. + Next: Statements \ No newline at end of file diff --git a/docsite/site/content/reference/grammar.md b/docsite/site/content/reference/grammar.md index 4369d4e..8bcaf92 100644 --- a/docsite/site/content/reference/grammar.md +++ b/docsite/site/content/reference/grammar.md @@ -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 diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 7b1d7fc..041fbba 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -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, } +#[derive(Debug, PartialEq, Clone)] +pub struct DebugDef { + pub pos: Position, + pub expr: Box, +} + /// 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(()) } diff --git a/src/ast/walk.rs b/src/ast/walk.rs index 4758453..023de26 100644 --- a/src/ast/walk.rs +++ b/src/ast/walk.rs @@ -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); + } } } diff --git a/src/build/mod.rs b/src/build/mod.rs index 1dbeb37..f889769 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -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() { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index c942ff1..4578022 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -714,6 +714,19 @@ make_fn!( ) ); +make_fn!( + trace_expression, 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, 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), diff --git a/src/tokenizer/mod.rs b/src/tokenizer/mod.rs index b4b7f4e..b191fe7 100644 --- a/src/tokenizer/mod.rs +++ b/src/tokenizer/mod.rs @@ -286,6 +286,10 @@ make_fn!(nottok, do_text_token_tok!(TokenType::BAREWORD, "not", WS) ); +make_fn!(tracetok, + do_text_token_tok!(TokenType::BAREWORD, "TRACE", WS) +); + make_fn!(failtok, do_text_token_tok!(TokenType::BAREWORD, "fail", WS) ); @@ -432,6 +436,7 @@ fn token<'a>(input: OffsetStrIter<'a>) -> Result, Token> { selecttok, asserttok, failtok, + tracetok, functok, moduletok, importtok,