Allow to trace expressions (and not only string literals)
This however enforces that the argument unifies to a `String`. So this is more flexible than the previous form, but does fundamentally the same thing. Fixes #378.
This commit is contained in:
parent
4e51e49fe6
commit
7abd76b6ad
|
@ -249,7 +249,6 @@ pub enum Air {
|
||||||
|
|
||||||
Trace {
|
Trace {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
text: Option<String>,
|
|
||||||
tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1685,12 +1685,12 @@ pub fn monomorphize(
|
||||||
needs_variant = true;
|
needs_variant = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Air::Trace { scope, text, tipo } => {
|
Air::Trace { scope, tipo } => {
|
||||||
if tipo.is_generic() {
|
if tipo.is_generic() {
|
||||||
let mut tipo = tipo.clone();
|
let mut tipo = tipo.clone();
|
||||||
find_generics_to_replace(&mut tipo, &generic_types);
|
find_generics_to_replace(&mut tipo, &generic_types);
|
||||||
|
|
||||||
new_air[index] = Air::Trace { scope, tipo, text };
|
new_air[index] = Air::Trace { scope, tipo };
|
||||||
needs_variant = true;
|
needs_variant = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ pub enum TypedExpr {
|
||||||
location: Span,
|
location: Span,
|
||||||
tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
then: Box<Self>,
|
then: Box<Self>,
|
||||||
text: Option<String>,
|
text: Box<Self>,
|
||||||
},
|
},
|
||||||
|
|
||||||
When {
|
When {
|
||||||
|
@ -389,7 +389,7 @@ pub enum UntypedExpr {
|
||||||
Trace {
|
Trace {
|
||||||
location: Span,
|
location: Span,
|
||||||
then: Box<Self>,
|
then: Box<Self>,
|
||||||
text: Option<String>,
|
text: Box<Self>,
|
||||||
},
|
},
|
||||||
|
|
||||||
When {
|
When {
|
||||||
|
|
|
@ -705,22 +705,10 @@ impl<'comments> Formatter<'comments> {
|
||||||
..
|
..
|
||||||
} => self.assignment(pattern, value, None, Some(*kind), annotation),
|
} => self.assignment(pattern, value, None, Some(*kind), annotation),
|
||||||
|
|
||||||
UntypedExpr::Trace {
|
UntypedExpr::Trace { text, then, .. } => "trace"
|
||||||
text: None, then, ..
|
|
||||||
} => "trace"
|
|
||||||
.to_doc()
|
.to_doc()
|
||||||
.append(if self.pop_empty_lines(then.start_byte_index()) {
|
.append(wrap_args([(self.wrap_expr(text), false)]))
|
||||||
lines(2)
|
.group()
|
||||||
} 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()) {
|
.append(if self.pop_empty_lines(then.start_byte_index()) {
|
||||||
lines(2)
|
lines(2)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -592,15 +592,14 @@ pub fn expr_seq_parser() -> impl Parser<Token, expr::UntypedExpr, Error = ParseE
|
||||||
choice((
|
choice((
|
||||||
just(Token::Trace)
|
just(Token::Trace)
|
||||||
.ignore_then(
|
.ignore_then(
|
||||||
select! {Token::String {value} => value}
|
expr_parser(r.clone())
|
||||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
.delimited_by(just(Token::LeftParen), just(Token::RightParen)),
|
||||||
.or_not(),
|
|
||||||
)
|
)
|
||||||
.then(r.clone())
|
.then(r.clone())
|
||||||
.map_with_span(|(text, then_), span| expr::UntypedExpr::Trace {
|
.map_with_span(|(text, then_), span| expr::UntypedExpr::Trace {
|
||||||
location: span,
|
location: span,
|
||||||
then: Box::new(then_),
|
then: Box::new(then_),
|
||||||
text,
|
text: Box::new(text),
|
||||||
}),
|
}),
|
||||||
expr_parser(r.clone())
|
expr_parser(r.clone())
|
||||||
.then(r.repeated())
|
.then(r.repeated())
|
||||||
|
|
|
@ -126,3 +126,35 @@ fn list_pattern_6() {
|
||||||
"#;
|
"#;
|
||||||
assert!(check(parse(source_code)).is_ok())
|
assert!(check(parse(source_code)).is_ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trace_strings() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn bar() {
|
||||||
|
"BAR"
|
||||||
|
}
|
||||||
|
|
||||||
|
test foo() {
|
||||||
|
let msg1 = "FOO"
|
||||||
|
trace("INLINE")
|
||||||
|
trace(msg1)
|
||||||
|
trace(bar())
|
||||||
|
True
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
assert!(check(parse(source_code)).is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trace_non_strings() {
|
||||||
|
let source_code = r#"
|
||||||
|
test foo() {
|
||||||
|
trace(14 + 42)
|
||||||
|
True
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
assert!(matches!(
|
||||||
|
check(parse(source_code)),
|
||||||
|
Err((_, Error::CouldNotUnify { .. }))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
|
@ -2586,3 +2586,121 @@ fn scope_logical_expression() {
|
||||||
})],
|
})],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trace_expressions() {
|
||||||
|
let code = indoc! {r#"
|
||||||
|
fn foo() {
|
||||||
|
let msg1 = "FOO"
|
||||||
|
trace "INLINE"
|
||||||
|
trace msg1
|
||||||
|
trace string.concat(msg1, "BAR")
|
||||||
|
trace ( 14 + 42 * 1337 )
|
||||||
|
Void
|
||||||
|
}
|
||||||
|
"#};
|
||||||
|
assert_definitions(
|
||||||
|
code,
|
||||||
|
vec![ast::Definition::Fn(Function {
|
||||||
|
arguments: vec![],
|
||||||
|
body: expr::UntypedExpr::Sequence {
|
||||||
|
location: Span::new((), 13..128),
|
||||||
|
expressions: vec![
|
||||||
|
expr::UntypedExpr::Assignment {
|
||||||
|
location: Span::new((), 13..29),
|
||||||
|
value: Box::new(expr::UntypedExpr::String {
|
||||||
|
location: Span::new((), 24..29),
|
||||||
|
value: "FOO".to_string(),
|
||||||
|
}),
|
||||||
|
pattern: ast::Pattern::Var {
|
||||||
|
location: Span::new((), 17..21),
|
||||||
|
name: "msg1".to_string(),
|
||||||
|
},
|
||||||
|
kind: ast::AssignmentKind::Let,
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
expr::UntypedExpr::Trace {
|
||||||
|
location: Span::new((), 32..128),
|
||||||
|
then: Box::new(expr::UntypedExpr::Trace {
|
||||||
|
location: Span::new((), 49..128),
|
||||||
|
then: Box::new(expr::UntypedExpr::Trace {
|
||||||
|
location: Span::new((), 62..128),
|
||||||
|
then: Box::new(expr::UntypedExpr::Trace {
|
||||||
|
location: Span::new((), 97..128),
|
||||||
|
then: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 124..128),
|
||||||
|
name: "Void".to_string(),
|
||||||
|
}),
|
||||||
|
text: Box::new(expr::UntypedExpr::BinOp {
|
||||||
|
location: Span::new((), 105..119),
|
||||||
|
name: ast::BinOp::AddInt,
|
||||||
|
left: Box::new(expr::UntypedExpr::Int {
|
||||||
|
location: Span::new((), 105..107),
|
||||||
|
value: "14".to_string(),
|
||||||
|
}),
|
||||||
|
right: Box::new(expr::UntypedExpr::BinOp {
|
||||||
|
location: Span::new((), 110..119),
|
||||||
|
name: ast::BinOp::MultInt,
|
||||||
|
left: Box::new(expr::UntypedExpr::Int {
|
||||||
|
location: Span::new((), 110..112),
|
||||||
|
value: "42".to_string(),
|
||||||
|
}),
|
||||||
|
right: Box::new(expr::UntypedExpr::Int {
|
||||||
|
location: Span::new((), 115..119),
|
||||||
|
value: "1337".to_string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
text: Box::new(expr::UntypedExpr::Call {
|
||||||
|
arguments: vec![
|
||||||
|
ast::CallArg {
|
||||||
|
label: None,
|
||||||
|
location: Span::new((), 82..86),
|
||||||
|
value: expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 82..86),
|
||||||
|
name: "msg1".to_string(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ast::CallArg {
|
||||||
|
label: None,
|
||||||
|
location: Span::new((), 88..93),
|
||||||
|
value: expr::UntypedExpr::String {
|
||||||
|
location: Span::new((), 88..93),
|
||||||
|
value: "BAR".to_string(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fun: Box::new(expr::UntypedExpr::FieldAccess {
|
||||||
|
location: Span::new((), 68..81),
|
||||||
|
label: "concat".to_string(),
|
||||||
|
container: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 68..74),
|
||||||
|
name: "string".to_string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
location: Span::new((), 68..94),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
text: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 55..59),
|
||||||
|
name: "msg1".to_string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
text: Box::new(expr::UntypedExpr::String {
|
||||||
|
location: Span::new((), 38..46),
|
||||||
|
value: "INLINE".to_string(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
doc: None,
|
||||||
|
location: Span::new((), 0..8),
|
||||||
|
name: "foo".to_string(),
|
||||||
|
public: false,
|
||||||
|
return_annotation: None,
|
||||||
|
return_type: (),
|
||||||
|
end_position: 129,
|
||||||
|
})],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -309,7 +309,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
location,
|
location,
|
||||||
then,
|
then,
|
||||||
text,
|
text,
|
||||||
} => self.infer_trace(*then, location, text),
|
} => self.infer_trace(*then, location, *text),
|
||||||
|
|
||||||
UntypedExpr::When {
|
UntypedExpr::When {
|
||||||
location,
|
location,
|
||||||
|
@ -1887,17 +1887,19 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
&mut self,
|
&mut self,
|
||||||
then: UntypedExpr,
|
then: UntypedExpr,
|
||||||
location: Span,
|
location: Span,
|
||||||
text: Option<String>,
|
text: UntypedExpr,
|
||||||
) -> Result<TypedExpr, Error> {
|
) -> Result<TypedExpr, Error> {
|
||||||
let then = self.infer(then)?;
|
let text = self.infer(text)?;
|
||||||
|
self.unify(text.tipo(), string(), text.location(), false)?;
|
||||||
|
|
||||||
|
let then = self.infer(then)?;
|
||||||
let tipo = then.tipo();
|
let tipo = then.tipo();
|
||||||
|
|
||||||
Ok(TypedExpr::Trace {
|
Ok(TypedExpr::Trace {
|
||||||
location,
|
location,
|
||||||
tipo,
|
tipo,
|
||||||
then: Box::new(then),
|
then: Box::new(then),
|
||||||
text,
|
text: Box::new(text),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -651,19 +651,21 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
ir_stack.append(&mut elems_air);
|
ir_stack.append(&mut elems_air);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedExpr::Trace {
|
TypedExpr::Trace {
|
||||||
tipo, then, text, ..
|
tipo, then, text, ..
|
||||||
} => {
|
} => {
|
||||||
let mut scope = scope;
|
let mut scope = scope;
|
||||||
|
|
||||||
ir_stack.push(Air::Trace {
|
ir_stack.push(Air::Trace {
|
||||||
text: text.clone(),
|
|
||||||
tipo: tipo.clone(),
|
tipo: tipo.clone(),
|
||||||
scope: scope.clone(),
|
scope: scope.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.push(self.id_gen.next());
|
scope.push(self.id_gen.next());
|
||||||
|
self.build_ir(text, ir_stack, scope.clone());
|
||||||
|
|
||||||
|
scope.push(self.id_gen.next());
|
||||||
self.build_ir(then, ir_stack, scope);
|
self.build_ir(then, ir_stack, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3504,13 +3506,12 @@ impl<'a> CodeGenerator<'a> {
|
||||||
label,
|
label,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Air::Trace { tipo, scope, text } => {
|
Air::Trace { tipo, scope } => {
|
||||||
let mut replaced_type = tipo.clone();
|
let mut replaced_type = tipo.clone();
|
||||||
replace_opaque_type(&mut replaced_type, self.data_types.clone());
|
replace_opaque_type(&mut replaced_type, self.data_types.clone());
|
||||||
|
|
||||||
ir_stack[index] = Air::Trace {
|
ir_stack[index] = Air::Trace {
|
||||||
scope,
|
scope,
|
||||||
text,
|
|
||||||
tipo: replaced_type,
|
tipo: replaced_type,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -5631,19 +5632,13 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
}
|
}
|
||||||
Air::Trace { text, .. } => {
|
Air::Trace { .. } => {
|
||||||
|
let text = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
let term = arg_stack.pop().unwrap();
|
let term = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
let term = apply_wrap(
|
let term = apply_wrap(
|
||||||
apply_wrap(
|
apply_wrap(Term::Builtin(DefaultFunction::Trace).force_wrap(), text),
|
||||||
Term::Builtin(DefaultFunction::Trace).force_wrap(),
|
|
||||||
Term::Constant(
|
|
||||||
UplcConstant::String(
|
|
||||||
text.unwrap_or_else(|| "aiken::trace".to_string()),
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Term::Delay(term.into()),
|
Term::Delay(term.into()),
|
||||||
)
|
)
|
||||||
.force_wrap();
|
.force_wrap();
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
|
use aiken/builtin
|
||||||
|
|
||||||
|
fn concat(left: String, right: String) -> String {
|
||||||
|
builtin.append_bytearray(
|
||||||
|
builtin.encode_utf8(left),
|
||||||
|
builtin.encode_utf8(right),
|
||||||
|
)
|
||||||
|
|> builtin.decode_utf8
|
||||||
|
}
|
||||||
|
|
||||||
fn is_negative(i: Int) -> Bool {
|
fn is_negative(i: Int) -> Bool {
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
trace("is negative")
|
trace("is negative")
|
||||||
True
|
True
|
||||||
} else {
|
} else {
|
||||||
trace("is non-negative")
|
trace(concat("is", concat(" ", "non-negative")))
|
||||||
False
|
False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue