diff --git a/crates/lang/src/air.rs b/crates/lang/src/air.rs index 2a0b473f..10189f40 100644 --- a/crates/lang/src/air.rs +++ b/crates/lang/src/air.rs @@ -203,6 +203,12 @@ pub enum Air { tipo: Arc, }, + Trace { + scope: Vec, + text: Option, + tipo: Arc, + }, + Record { scope: Vec, }, @@ -261,6 +267,7 @@ impl Air { | Air::Record { scope, .. } | Air::RecordUpdate { scope, .. } | Air::Negate { scope, .. } + | Air::Trace { scope, .. } | Air::TupleAccessor { scope, .. } => scope.to_vec(), } } diff --git a/crates/lang/src/builtins.rs b/crates/lang/src/builtins.rs index ab587b78..8c2030d2 100644 --- a/crates/lang/src/builtins.rs +++ b/crates/lang/src/builtins.rs @@ -344,13 +344,7 @@ pub fn from_default_function( Some((tipo, 3)) } DefaultFunction::ChooseUnit => None, - DefaultFunction::Trace => { - let ret = generic_var(id_gen.next()); - - let tipo = function(vec![string(), ret.clone()], ret); - - Some((tipo, 2)) - } + DefaultFunction::Trace => None, DefaultFunction::FstPair => None, DefaultFunction::SndPair => None, DefaultFunction::ChooseList => None, diff --git a/crates/lang/src/expr.rs b/crates/lang/src/expr.rs index 51487e74..d57c8e46 100644 --- a/crates/lang/src/expr.rs +++ b/crates/lang/src/expr.rs @@ -91,6 +91,13 @@ pub enum TypedExpr { kind: AssignmentKind, }, + Trace { + location: Span, + tipo: Arc, + then: Box, + text: Option, + }, + When { location: Span, tipo: Arc, @@ -158,6 +165,7 @@ impl TypedExpr { match self { Self::Negate { .. } => bool(), Self::Var { constructor, .. } => constructor.tipo.clone(), + Self::Trace {then, ..} => then.tipo(), Self::Fn { tipo, .. } | Self::Int { tipo, .. } | Self::Todo { tipo, .. } @@ -200,6 +208,7 @@ impl TypedExpr { match self { TypedExpr::Fn { .. } | TypedExpr::Int { .. } + | TypedExpr::Trace { .. } | TypedExpr::List { .. } | TypedExpr::Call { .. } | TypedExpr::When { .. } @@ -240,6 +249,7 @@ impl TypedExpr { Self::Fn { location, .. } | Self::Int { location, .. } | Self::Var { location, .. } + | Self::Trace { location, .. } | Self::Todo { location, .. } | Self::When { location, .. } | Self::Call { location, .. } @@ -276,6 +286,7 @@ impl TypedExpr { match self { Self::Fn { location, .. } | Self::Int { location, .. } + | Self::Trace { location, .. } | Self::Var { location, .. } | Self::Todo { location, .. } | Self::When { location, .. } @@ -363,7 +374,11 @@ pub enum UntypedExpr { kind: AssignmentKind, annotation: Option, }, - + Trace { + location: Span, + then: Box, + text: Option, + }, When { location: Span, subjects: Vec, @@ -462,6 +477,7 @@ impl UntypedExpr { pub fn location(&self) -> Span { match self { Self::PipeLine { expressions, .. } => expressions.last().location(), + Self::Trace { then, .. } => then.location(), Self::Fn { location, .. } | Self::Var { location, .. } | Self::Int { location, .. } @@ -498,7 +514,7 @@ impl UntypedExpr { .map(|e| e.start_byte_index()) .unwrap_or(location.start), Self::PipeLine { expressions, .. } => expressions.first().start_byte_index(), - Self::Assignment { location, .. } => location.start, + Self::Trace { location, .. } | Self::Assignment { location, .. } => location.start, _ => self.location().start, } } diff --git a/crates/lang/src/format.rs b/crates/lang/src/format.rs index 8267129a..e51b277c 100644 --- a/crates/lang/src/format.rs +++ b/crates/lang/src/format.rs @@ -751,6 +751,29 @@ impl<'comments> Formatter<'comments> { .. } => self.assignment(pattern, value, None, Some(*kind), annotation), + UntypedExpr::Trace { + text: None, then, .. + } => "trace" + .to_doc() + .append(if self.pop_empty_lines(then.start_byte_index()) { + lines(2) + } else { + line() + }) + .append(self.expr(then)), + + UntypedExpr::Trace { + text: Some(l), + then, + .. + } => docvec!["trace(\"", l, "\")"] + .append(if self.pop_empty_lines(then.start_byte_index()) { + lines(2) + } else { + line() + }) + .append(self.expr(then)), + UntypedExpr::When { subjects, clauses, .. } => self.when(subjects, clauses), @@ -1319,7 +1342,9 @@ impl<'comments> Formatter<'comments> { fn wrap_expr<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> { match expr { - UntypedExpr::Sequence { .. } | UntypedExpr::Assignment { .. } => "{" + UntypedExpr::Trace { .. } + | UntypedExpr::Sequence { .. } + | UntypedExpr::Assignment { .. } => "{" .to_doc() .append(line().append(self.expr(expr)).nest(INDENT)) .append(line()) @@ -1356,7 +1381,9 @@ impl<'comments> Formatter<'comments> { fn case_clause_value<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> { match expr { - UntypedExpr::Sequence { .. } | UntypedExpr::Assignment { .. } => " {" + UntypedExpr::Trace { .. } + | UntypedExpr::Sequence { .. } + | UntypedExpr::Assignment { .. } => " {" .to_doc() .append(line().append(self.expr(expr)).nest(INDENT).group()) .append(line()) diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 03ae4473..0a4253ba 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -553,9 +553,23 @@ pub fn anon_fn_param_parser() -> impl Parser impl Parser { recursive(|r| { - expr_parser(r.clone()) - .then(r.repeated()) - .foldl(|current, next| current.append_in_sequence(next)) + choice(( + just(Token::Trace) + .ignore_then( + select! {Token::String {value} => value} + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .or_not(), + ) + .then(r.clone()) + .map_with_span(|(text, then_), span| expr::UntypedExpr::Trace { + location: span, + then: Box::new(then_), + text, + }), + expr_parser(r.clone()) + .then(r.repeated()) + .foldl(|current, next| current.append_in_sequence(next)), + )) }) } diff --git a/crates/lang/src/parser/lexer.rs b/crates/lang/src/parser/lexer.rs index 537c7cff..fd7eedbf 100644 --- a/crates/lang/src/parser/lexer.rs +++ b/crates/lang/src/parser/lexer.rs @@ -73,6 +73,7 @@ pub fn lexer() -> impl Parser, Error = ParseError> { .labelled("string"); let keyword = text::ident().map(|s: String| match s.as_str() { + "trace" => Token::Trace, "as" => Token::As, "assert" => Token::Assert, "check" => Token::Assert, diff --git a/crates/lang/src/parser/token.rs b/crates/lang/src/parser/token.rs index 000076f6..76e9a6ac 100644 --- a/crates/lang/src/parser/token.rs +++ b/crates/lang/src/parser/token.rs @@ -73,6 +73,7 @@ pub enum Token { Todo, Type, When, + Trace, } impl fmt::Display for Token { @@ -144,6 +145,7 @@ impl fmt::Display for Token { Token::Opaque => "opaque", Token::Pub => "pub", Token::Todo => "todo", + Token::Trace => "trace", Token::Type => "type", Token::Test => "test", }; diff --git a/crates/lang/src/tipo/expr.rs b/crates/lang/src/tipo/expr.rs index 7fb986ff..488cb004 100644 --- a/crates/lang/src/tipo/expr.rs +++ b/crates/lang/src/tipo/expr.rs @@ -279,6 +279,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> { .. } => self.infer_assignment(pattern, *value, kind, &annotation, location), + UntypedExpr::Trace { + location, + then, + text, + } => self.infer_trace(*then, location, text), + UntypedExpr::When { location, subjects, @@ -1711,6 +1717,24 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } } + fn infer_trace( + &mut self, + then: UntypedExpr, + location: Span, + text: Option, + ) -> Result { + let then = self.infer(then)?; + + let tipo = then.tipo(); + + Ok(TypedExpr::Trace { + location, + tipo, + then: Box::new(then), + text, + }) + } + fn infer_value_constructor( &mut self, module: &Option, diff --git a/crates/lang/src/tipo/infer.rs b/crates/lang/src/tipo/infer.rs index 2e5be297..83f8f470 100644 --- a/crates/lang/src/tipo/infer.rs +++ b/crates/lang/src/tipo/infer.rs @@ -475,6 +475,7 @@ fn str_to_keyword(word: &str) -> Option { "pub" => Some(Token::Pub), "todo" => Some(Token::Todo), "type" => Some(Token::Type), + "trace" => Some(Token::Trace), _ => None, } } diff --git a/crates/lang/src/uplc.rs b/crates/lang/src/uplc.rs index 27b45b5d..bb095575 100644 --- a/crates/lang/src/uplc.rs +++ b/crates/lang/src/uplc.rs @@ -517,6 +517,21 @@ impl<'a> CodeGenerator<'a> { ir_stack.append(&mut elems_air); } + TypedExpr::Trace { + tipo, then, text, .. + } => { + let mut scope = scope; + + ir_stack.push(Air::Trace { + text: text.clone(), + tipo: tipo.clone(), + scope: scope.clone(), + }); + + scope.push(self.id_gen.next()); + + self.build_ir(then, ir_stack, scope); + } } } @@ -3517,8 +3532,20 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } } - Air::Todo { .. } => { - arg_stack.push(Term::Error); + Air::Todo { label, .. } => { + let term = Term::Apply { + function: Term::Apply { + function: Term::Builtin(DefaultFunction::Trace).force_wrap().into(), + argument: Term::Constant(uplc::ast::Constant::String( + label.unwrap_or_else(|| "aiken::todo".to_string()), + )) + .into(), + } + .into(), + argument: Term::Error.into(), + }; + + arg_stack.push(term); } Air::Record { .. } => todo!(), Air::RecordUpdate { .. } => todo!(), @@ -3677,6 +3704,23 @@ impl<'a> CodeGenerator<'a> { }; } + arg_stack.push(term); + } + Air::Trace { text, .. } => { + let term = arg_stack.pop().unwrap(); + + let term = Term::Apply { + function: Term::Apply { + function: Term::Builtin(DefaultFunction::Trace).force_wrap().into(), + argument: Term::Constant(uplc::ast::Constant::String( + text.unwrap_or_else(|| "aike::trace".to_string()), + )) + .into(), + } + .into(), + argument: term.into(), + }; + arg_stack.push(term); } } diff --git a/examples/acceptance_tests/.gitignore b/examples/acceptance_tests/.gitignore new file mode 100644 index 00000000..d1638636 --- /dev/null +++ b/examples/acceptance_tests/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/examples/acceptance_tests/001/aiken.lock b/examples/acceptance_tests/001/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/001/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/002/aiken.lock b/examples/acceptance_tests/002/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/002/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/003/aiken.lock b/examples/acceptance_tests/003/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/003/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/004/aiken.lock b/examples/acceptance_tests/004/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/004/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/005/aiken.lock b/examples/acceptance_tests/005/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/005/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/006/aiken.lock b/examples/acceptance_tests/006/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/006/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/007/aiken.lock b/examples/acceptance_tests/007/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/007/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/008/aiken.lock b/examples/acceptance_tests/008/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/008/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/009/aiken.lock b/examples/acceptance_tests/009/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/009/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/010/aiken.lock b/examples/acceptance_tests/010/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/010/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/011/aiken.lock b/examples/acceptance_tests/011/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/011/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/012/aiken.lock b/examples/acceptance_tests/012/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/012/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/013/aiken.lock b/examples/acceptance_tests/013/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/013/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/014/aiken.lock b/examples/acceptance_tests/014/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/014/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/015/aiken.lock b/examples/acceptance_tests/015/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/015/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/016/aiken.lock b/examples/acceptance_tests/016/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/016/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/017/aiken.lock b/examples/acceptance_tests/017/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/017/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/018/aiken.lock b/examples/acceptance_tests/018/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/018/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/019/aiken.lock b/examples/acceptance_tests/019/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/019/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/020/aiken.lock b/examples/acceptance_tests/020/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/020/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/021/aiken.lock b/examples/acceptance_tests/021/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/021/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/022/aiken.lock b/examples/acceptance_tests/022/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/022/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/023/aiken.lock b/examples/acceptance_tests/023/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/023/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/024/aiken.lock b/examples/acceptance_tests/024/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/024/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/025/aiken.lock b/examples/acceptance_tests/025/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/025/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/026/aiken.lock b/examples/acceptance_tests/026/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/026/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/027/aiken.lock b/examples/acceptance_tests/027/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/027/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/028/aiken.lock b/examples/acceptance_tests/028/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/028/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/029/aiken.lock b/examples/acceptance_tests/029/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/029/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/030/aiken.lock b/examples/acceptance_tests/030/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/030/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/031/aiken.lock b/examples/acceptance_tests/031/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/031/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/032/aiken.lock b/examples/acceptance_tests/032/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/032/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/032/aiken.toml b/examples/acceptance_tests/032/aiken.toml index d8cd0c44..60cec46b 100644 --- a/examples/acceptance_tests/032/aiken.toml +++ b/examples/acceptance_tests/032/aiken.toml @@ -1,2 +1,2 @@ -name = "acceptance_test_032" +name = "aiken-lang/acceptance_test_032" version = "0.0.0" diff --git a/examples/acceptance_tests/032/lib/test.ak b/examples/acceptance_tests/032/lib/test.ak index 0300d140..ab4226f1 100644 --- a/examples/acceptance_tests/032/lib/test.ak +++ b/examples/acceptance_tests/032/lib/test.ak @@ -1,14 +1,15 @@ -use aiken/builtin - -fn is_negative(i : Int) -> Bool { +fn is_negative(i: Int) -> Bool { if i < 0 { - builtin.trace("is negative", True) + trace("is negative") + + True } else { - builtin.trace("is non-negative", False) + trace("is non-negative") + + False } } - test trace_1() { is_negative(-14) && !is_negative(42) }