Merge branch 'aiken-watch'

This commit is contained in:
KtorZ 2023-11-25 15:14:18 +01:00
commit c2725abcea
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
63 changed files with 1391 additions and 993 deletions

View File

@ -1,6 +1,20 @@
# Changelog # Changelog
## v1.0.20-alpha - 10/25/2023 ## v1.0.21-alpha - unreleased
### Added
- **aiken**: `--watch` flag on the `build`, `check` and `docs` commands to automatically watch and re-execute the command on file changes.
### Changed
N/A
### Fixed
N/A
## v1.0.20-alpha - 2023-10-25
### Added ### Added

111
Cargo.lock generated vendored
View File

@ -141,6 +141,7 @@ dependencies = [
"itertools", "itertools",
"miette", "miette",
"minicbor", "minicbor",
"notify",
"owo-colors", "owo-colors",
"pallas", "pallas",
"pallas-traverse", "pallas-traverse",
@ -360,6 +361,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.10.4" version = "0.10.4"
@ -505,7 +512,7 @@ checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
"bitflags", "bitflags 1.3.2",
"clap_lex", "clap_lex",
"strsim", "strsim",
"terminal_size 0.2.6", "terminal_size 0.2.6",
@ -662,7 +669,7 @@ version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"crossterm_winapi", "crossterm_winapi",
"libc", "libc",
"mio", "mio",
@ -866,6 +873,18 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "filetime"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.3.5",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "fixedbitset" name = "fixedbitset"
version = "0.4.2" version = "0.4.2"
@ -921,6 +940,15 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "fsevent-sys"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "fslock" name = "fslock"
version = "0.2.1" version = "0.2.1"
@ -1054,7 +1082,7 @@ version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b989d6a7ca95a362cf2cfc5ad688b3a467be1f87e480b8dad07fee8c79b0044" checksum = "7b989d6a7ca95a362cf2cfc5ad688b3a467be1f87e480b8dad07fee8c79b0044"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"libc", "libc",
"libgit2-sys", "libgit2-sys",
"log", "log",
@ -1293,13 +1321,33 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f2cb48b81b1dc9f39676bf99f5499babfec7cd8fe14307f7b3d747208fb5690" checksum = "9f2cb48b81b1dc9f39676bf99f5499babfec7cd8fe14307f7b3d747208fb5690"
[[package]]
name = "inotify"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
dependencies = [
"bitflags 1.3.2",
"inotify-sys",
"libc",
]
[[package]]
name = "inotify-sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "inquire" name = "inquire"
version = "0.6.2" version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33e7c1ddeb15c9abcbfef6029d8e29f69b52b6d6c891031b88ed91b5065803b" checksum = "c33e7c1ddeb15c9abcbfef6029d8e29f69b52b6d6c891031b88ed91b5065803b"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"crossterm", "crossterm",
"dyn-clone", "dyn-clone",
"lazy_static", "lazy_static",
@ -1414,6 +1462,26 @@ dependencies = [
"signature", "signature",
] ]
[[package]]
name = "kqueue"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
dependencies = [
"kqueue-sys",
"libc",
]
[[package]]
name = "kqueue-sys"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
dependencies = [
"bitflags 1.3.2",
"libc",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -1505,7 +1573,7 @@ version = "0.94.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237" checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"serde", "serde",
"serde_json", "serde_json",
"serde_repr", "serde_repr",
@ -1660,6 +1728,25 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "notify"
version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
"bitflags 2.4.1",
"crossbeam-channel",
"filetime",
"fsevent-sys",
"inotify",
"kqueue",
"libc",
"log",
"mio",
"walkdir",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "num-bigint" name = "num-bigint"
version = "0.4.3" version = "0.4.3"
@ -1728,7 +1815,7 @@ version = "0.10.55"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"cfg-if", "cfg-if",
"foreign-types", "foreign-types",
"libc", "libc",
@ -2069,7 +2156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65"
dependencies = [ dependencies = [
"bit-set", "bit-set",
"bitflags", "bitflags 1.3.2",
"byteorder", "byteorder",
"lazy_static", "lazy_static",
"num-traits", "num-traits",
@ -2097,7 +2184,7 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"memchr", "memchr",
"unicase", "unicase",
] ]
@ -2184,7 +2271,7 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
] ]
[[package]] [[package]]
@ -2193,7 +2280,7 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
] ]
[[package]] [[package]]
@ -2283,7 +2370,7 @@ version = "0.37.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"errno", "errno",
"io-lifetimes", "io-lifetimes",
"libc", "libc",
@ -2377,7 +2464,7 @@ version = "2.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"core-foundation", "core-foundation",
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",

View File

@ -516,7 +516,7 @@ impl<'comments> Formatter<'comments> {
.group(); .group();
// Format body // Format body
let body = self.expr(body); let body = self.expr(body, true);
// Add any trailing comments // Add any trailing comments
let body = match printed_comments(self.pop_comments(end_location), false) { let body = match printed_comments(self.pop_comments(end_location), false) {
@ -609,8 +609,10 @@ impl<'comments> Formatter<'comments> {
) -> Document<'a> { ) -> Document<'a> {
let args = wrap_args(args.iter().map(|e| (self.fn_arg(e), false))).group(); let args = wrap_args(args.iter().map(|e| (self.fn_arg(e), false))).group();
let body = match body { let body = match body {
UntypedExpr::Trace { .. } | UntypedExpr::When { .. } => self.expr(body).force_break(), UntypedExpr::Trace { .. } | UntypedExpr::When { .. } => {
_ => self.expr(body), self.expr(body, true).force_break()
}
_ => self.expr(body, true),
}; };
let header = "fn".to_doc().append(args); let header = "fn".to_doc().append(args);
@ -634,15 +636,19 @@ impl<'comments> Formatter<'comments> {
fn sequence<'a>(&mut self, expressions: &'a [UntypedExpr]) -> Document<'a> { fn sequence<'a>(&mut self, expressions: &'a [UntypedExpr]) -> Document<'a> {
let count = expressions.len(); let count = expressions.len();
let mut documents = Vec::with_capacity(count * 2); let mut documents = Vec::with_capacity(count * 2);
for (i, expression) in expressions.iter().enumerate() { for (i, expression) in expressions.iter().enumerate() {
let preceding_newline = self.pop_empty_lines(expression.start_byte_index()); let preceding_newline = self.pop_empty_lines(expression.start_byte_index());
if i != 0 && preceding_newline { if i != 0 && preceding_newline {
documents.push(lines(2)); documents.push(lines(2));
} else if i != 0 { } else if i != 0 {
documents.push(lines(1)); documents.push(lines(1));
} }
documents.push(self.expr(expression).group());
documents.push(self.expr(expression, false).group());
} }
documents.to_doc().force_break() documents.to_doc().force_break()
} }
@ -653,8 +659,6 @@ impl<'comments> Formatter<'comments> {
kind: AssignmentKind, kind: AssignmentKind,
annotation: &'a Option<Annotation>, annotation: &'a Option<Annotation>,
) -> Document<'a> { ) -> Document<'a> {
self.pop_empty_lines(pattern.location().end);
let keyword = match kind { let keyword = match kind {
AssignmentKind::Let => "let", AssignmentKind::Let => "let",
AssignmentKind::Expect => "expect", AssignmentKind::Expect => "expect",
@ -667,6 +671,8 @@ impl<'comments> Formatter<'comments> {
keyword.to_doc().append(self.case_clause_value(value)) keyword.to_doc().append(self.case_clause_value(value))
} }
_ => { _ => {
self.pop_empty_lines(pattern.location().end);
let pattern = self.pattern(pattern); let pattern = self.pattern(pattern);
let annotation = annotation let annotation = annotation
@ -782,7 +788,7 @@ impl<'comments> Formatter<'comments> {
} }
} }
pub fn expr<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> { pub fn expr<'a>(&mut self, expr: &'a UntypedExpr, is_top_level: bool) -> Document<'a> {
let comments = self.pop_comments(expr.start_byte_index()); let comments = self.pop_comments(expr.start_byte_index());
let document = match expr { let document = match expr {
@ -821,7 +827,18 @@ impl<'comments> Formatter<'comments> {
UntypedExpr::String { value, .. } => self.string(value), UntypedExpr::String { value, .. } => self.string(value),
UntypedExpr::Sequence { expressions, .. } => self.sequence(expressions), UntypedExpr::Sequence { expressions, .. } => {
let sequence = self.sequence(expressions);
if is_top_level {
sequence
} else {
"{".to_doc()
.append(line().append(sequence).nest(INDENT).group())
.append(line())
.append("}")
}
}
UntypedExpr::Var { name, .. } if name.contains(CAPTURE_VARIABLE) => "_".to_doc(), UntypedExpr::Var { name, .. } if name.contains(CAPTURE_VARIABLE) => "_".to_doc(),
@ -878,7 +895,10 @@ impl<'comments> Formatter<'comments> {
UntypedExpr::FieldAccess { UntypedExpr::FieldAccess {
label, container, .. label, container, ..
} => self.expr(container).append(".").append(label.as_str()), } => self
.expr(container, false)
.append(".")
.append(label.as_str()),
UntypedExpr::RecordUpdate { UntypedExpr::RecordUpdate {
constructor, constructor,
@ -893,7 +913,7 @@ impl<'comments> Formatter<'comments> {
UntypedExpr::TupleIndex { index, tuple, .. } => { UntypedExpr::TupleIndex { index, tuple, .. } => {
let suffix = Ordinal(*index + 1).suffix().to_doc(); let suffix = Ordinal(*index + 1).suffix().to_doc();
self.expr(tuple) self.expr(tuple, false)
.append(".".to_doc()) .append(".".to_doc())
.append((index + 1).to_doc()) .append((index + 1).to_doc())
.append(suffix) .append(suffix)
@ -953,7 +973,7 @@ impl<'comments> Formatter<'comments> {
} else { } else {
line() line()
}) })
.append(self.expr(then)), .append(self.expr(then, false)),
} }
} }
@ -1027,7 +1047,7 @@ impl<'comments> Formatter<'comments> {
false false
}; };
self.expr(fun) self.expr(fun, false)
.append(wrap_args( .append(wrap_args(
args.iter() args.iter()
.map(|a| (self.call_arg(a, needs_curly), needs_curly)), .map(|a| (self.call_arg(a, needs_curly), needs_curly)),
@ -1051,7 +1071,7 @@ impl<'comments> Formatter<'comments> {
let else_begin = line().append("} else {"); let else_begin = line().append("} else {");
let else_body = line().append(self.expr(final_else)).nest(INDENT); let else_body = line().append(self.expr(final_else, true)).nest(INDENT);
let else_end = line().append("}"); let else_end = line().append("}");
@ -1072,7 +1092,7 @@ impl<'comments> Formatter<'comments> {
.append(break_("{", " {")) .append(break_("{", " {"))
.group(); .group();
let if_body = line().append(self.expr(&branch.body)).nest(INDENT); let if_body = line().append(self.expr(&branch.body, true)).nest(INDENT);
if_begin.append(if_body) if_begin.append(if_body)
} }
@ -1110,8 +1130,8 @@ impl<'comments> Formatter<'comments> {
args: &'a [UntypedRecordUpdateArg], args: &'a [UntypedRecordUpdateArg],
) -> Document<'a> { ) -> Document<'a> {
use std::iter::once; use std::iter::once;
let constructor_doc = self.expr(constructor); let constructor_doc = self.expr(constructor, false);
let spread_doc = "..".to_doc().append(self.expr(&spread.base)); let spread_doc = "..".to_doc().append(self.expr(&spread.base, false));
let arg_docs = args.iter().map(|a| (self.record_update_arg(a), true)); let arg_docs = args.iter().map(|a| (self.record_update_arg(a), true));
let all_arg_docs = once((spread_doc, true)).chain(arg_docs); let all_arg_docs = once((spread_doc, true)).chain(arg_docs);
constructor_doc.append(wrap_args(all_arg_docs)).group() constructor_doc.append(wrap_args(all_arg_docs)).group()
@ -1128,14 +1148,14 @@ impl<'comments> Formatter<'comments> {
let left_precedence = left.binop_precedence(); let left_precedence = left.binop_precedence();
let right_precedence = right.binop_precedence(); let right_precedence = right.binop_precedence();
let left = self.expr(left); let left = self.expr(left, false);
let right = self.expr(right); let right = self.expr(right, false);
self.operator_side(left, precedence, left_precedence) self.operator_side(left, precedence, left_precedence)
.append(" ") .append(" ")
.append(name) .append(name)
.append(" ") .append(" ")
.append(self.operator_side(right, precedence, right_precedence - 1)) .append(self.operator_side(right, precedence, right_precedence.saturating_sub(1)))
} }
pub fn operator_side<'a>(&mut self, doc: Document<'a>, op: u8, side: u8) -> Document<'a> { pub fn operator_side<'a>(&mut self, doc: Document<'a>, op: u8, side: u8) -> Document<'a> {
@ -1161,7 +1181,9 @@ impl<'comments> Formatter<'comments> {
.append( .append(
line() line()
.append(join( .append(join(
expressions.iter().map(|expression| self.expr(expression)), expressions
.iter()
.map(|expression| self.expr(expression, false)),
",".to_doc().append(line()), ",".to_doc().append(line()),
)) ))
.nest(INDENT) .nest(INDENT)
@ -1241,10 +1263,10 @@ impl<'comments> Formatter<'comments> {
if hole_in_first_position && args.len() == 1 { if hole_in_first_position && args.len() == 1 {
// x |> fun(_) // x |> fun(_)
self.expr(fun) self.expr(fun, false)
} else if hole_in_first_position { } else if hole_in_first_position {
// x |> fun(_, 2, 3) // x |> fun(_, 2, 3)
self.expr(fun).append( self.expr(fun, false).append(
wrap_args( wrap_args(
args.iter() args.iter()
.skip(1) .skip(1)
@ -1254,7 +1276,7 @@ impl<'comments> Formatter<'comments> {
) )
} else { } else {
// x |> fun(1, _, 3) // x |> fun(1, _, 3)
self.expr(fun) self.expr(fun, false)
.append(wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group()) .append(wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group())
} }
} }
@ -1267,14 +1289,14 @@ impl<'comments> Formatter<'comments> {
.. ..
} => match args.as_slice() { } => match args.as_slice() {
[first, second] if is_breakable_expr(&second.value) && first.is_capture_hole() => { [first, second] if is_breakable_expr(&second.value) && first.is_capture_hole() => {
self.expr(fun) self.expr(fun, false)
.append("(_, ") .append("(_, ")
.append(self.call_arg(second, false)) .append(self.call_arg(second, false))
.append(")") .append(")")
.group() .group()
} }
_ => self.expr(fun).append( _ => self.expr(fun, false).append(
wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group(), wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group(),
), ),
}, },
@ -1556,12 +1578,12 @@ impl<'comments> Formatter<'comments> {
| UntypedExpr::Sequence { .. } | UntypedExpr::Sequence { .. }
| UntypedExpr::Assignment { .. } => "{" | UntypedExpr::Assignment { .. } => "{"
.to_doc() .to_doc()
.append(line().append(self.expr(expr)).nest(INDENT)) .append(line().append(self.expr(expr, true)).nest(INDENT))
.append(line()) .append(line())
.append("}") .append("}")
.force_break(), .force_break(),
_ => self.expr(expr), _ => self.expr(expr, false),
} }
} }
@ -1598,18 +1620,21 @@ impl<'comments> Formatter<'comments> {
| UntypedExpr::Sequence { .. } | UntypedExpr::Sequence { .. }
| UntypedExpr::Assignment { .. } => " {" | UntypedExpr::Assignment { .. } => " {"
.to_doc() .to_doc()
.append(line().append(self.expr(expr)).nest(INDENT).group()) .append(line().append(self.expr(expr, true)).nest(INDENT).group())
.append(line()) .append(line())
.append("}") .append("}")
.force_break(), .force_break(),
UntypedExpr::Fn { .. } | UntypedExpr::List { .. } => { UntypedExpr::Fn { .. } | UntypedExpr::List { .. } => {
line().append(self.expr(expr)).nest(INDENT).group() line().append(self.expr(expr, false)).nest(INDENT).group()
} }
UntypedExpr::When { .. } => line().append(self.expr(expr)).nest(INDENT).group(), UntypedExpr::When { .. } => line().append(self.expr(expr, false)).nest(INDENT).group(),
_ => break_("", " ").append(self.expr(expr)).nest(INDENT).group(), _ => break_("", " ")
.append(self.expr(expr, false))
.nest(INDENT)
.group(),
} }
} }
@ -1647,7 +1672,7 @@ impl<'comments> Formatter<'comments> {
|| break_(",", ", ") || break_(",", ", ")
}; };
let elements_document = join(elements.iter().map(|e| self.wrap_expr(e)), comma()); let elements_document = join(elements.iter().map(|e| self.wrap_expr(e)), comma());
let tail = tail.map(|e| self.expr(e)); let tail = tail.map(|e| self.expr(e, false));
list(elements_document, elements.len(), tail) list(elements_document, elements.len(), tail)
} }
@ -1720,7 +1745,7 @@ impl<'comments> Formatter<'comments> {
let right = self.clause_guard(right); let right = self.clause_guard(right);
self.operator_side(left, name_precedence, left_precedence) self.operator_side(left, name_precedence, left_precedence)
.append(name) .append(name)
.append(self.operator_side(right, name_precedence, right_precedence - 1)) .append(self.operator_side(right, name_precedence, right_precedence.saturating_sub(1)))
} }
fn clause_guard<'a>(&mut self, clause_guard: &'a UntypedClauseGuard) -> Document<'a> { fn clause_guard<'a>(&mut self, clause_guard: &'a UntypedClauseGuard) -> Document<'a> {
@ -1771,7 +1796,7 @@ impl<'comments> Formatter<'comments> {
fn wrap_unary_op<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> { fn wrap_unary_op<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> {
match expr { match expr {
UntypedExpr::BinOp { .. } => "(".to_doc().append(self.expr(expr)).append(")"), UntypedExpr::BinOp { .. } => "(".to_doc().append(self.expr(expr, false)).append(")"),
_ => self.wrap_expr(expr), _ => self.wrap_expr(expr),
} }
} }

View File

@ -4049,6 +4049,12 @@ impl<'a> CodeGenerator<'a> {
Term::equals_string() Term::equals_string()
} else if tipo.is_bytearray() { } else if tipo.is_bytearray() {
Term::equals_bytestring() Term::equals_bytestring()
} else if tipo.is_bls381_12_g1() {
Term::bls12_381_g1_equal()
} else if tipo.is_bls381_12_g2() {
Term::bls12_381_g2_equal()
} else if tipo.is_ml_result() {
panic!("ML Result equality is not supported")
} else { } else {
Term::equals_data() Term::equals_data()
}; };

View File

@ -579,6 +579,12 @@ pub fn get_variant_name(t: &Rc<Type>) -> String {
"_bool".to_string() "_bool".to_string()
} else if t.is_bytearray() { } else if t.is_bytearray() {
"_bytearray".to_string() "_bytearray".to_string()
} else if t.is_bls381_12_g1() {
"_bls381_12_g1".to_string()
} else if t.is_bls381_12_g2() {
"_bls381_12_g2".to_string()
} else if t.is_ml_result() {
"_ml_result".to_string()
} else if t.is_map() { } else if t.is_map() {
let mut full_type = vec!["_map".to_string()]; let mut full_type = vec!["_map".to_string()];
let pair_type = &t.get_inner_types()[0]; let pair_type = &t.get_inner_types()[0];
@ -1305,7 +1311,7 @@ pub fn convert_constants_to_data(constants: Vec<Rc<UplcConstant>>) -> Vec<UplcCo
UplcConstant::Bls12_381G2Element(b) => UplcConstant::Data(PlutusData::BoundedBytes( UplcConstant::Bls12_381G2Element(b) => UplcConstant::Data(PlutusData::BoundedBytes(
b.deref().clone().compress().into(), b.deref().clone().compress().into(),
)), )),
UplcConstant::Bls12_381MlResult(_) => unreachable!("Bls12_381MlResult not supported"), UplcConstant::Bls12_381MlResult(_) => panic!("Bls12_381MlResult not supported"),
}; };
new_constants.push(constant); new_constants.push(constant);
} }

View File

@ -66,6 +66,25 @@ fn validator_illegal_return_type() {
)) ))
} }
#[test]
fn validator_useless_pub() {
let source_code = r#"
type Datum {
thing: Int
}
validator {
pub fn foo(_d: Datum, _r, _c) {
True
}
}
"#;
let (warnings, _) = check_validator(parse(source_code)).unwrap();
assert!(matches!(warnings[0], Warning::PubInValidatorModule { .. }))
}
#[test] #[test]
fn validator_illegal_arity() { fn validator_illegal_arity() {
let source_code = r#" let source_code = r#"

View File

@ -62,6 +62,77 @@ fn format_if() {
); );
} }
#[test]
fn format_logic_op_with_code_block() {
assert_format!(
r#"
fn foo() {
True || {
let bar = 1
bar == bar
}
}
"#
);
}
#[test]
fn format_grouped_expression() {
assert_format!(
r#"
fn foo() {
y == { x |> f }
}
"#
);
}
#[test]
fn format_grouped_expression_2() {
assert_format!(
r#"
fn foo() {
( y == x ) |> f
}
"#
);
}
#[test]
fn format_grouped_expression_3() {
assert_format!(
r#"
fn foo() {
{ x |> f } == y
}
"#
);
}
#[test]
fn format_grouped_expression_4() {
assert_format!(
r#"
fn foo() {
x |> { f == y }
}
"#
);
}
#[test]
fn format_preserve_newline_after_bool_expect() {
assert_format!(
r#"
fn foo() {
expect 1 == 1
False
}
"#
);
}
#[test] #[test]
fn format_validator() { fn format_validator() {
assert_format!( assert_format!(

View File

@ -0,0 +1,8 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\nfn foo() {\n y == { x |> f }\n}\n"
---
fn foo() {
y == ( x |> f )
}

View File

@ -0,0 +1,8 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\nfn foo() {\n ( y == x ) |> f\n}\n"
---
fn foo() {
( y == x ) |> f
}

View File

@ -0,0 +1,8 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\nfn foo() {\n { x |> f } == y\n}\n"
---
fn foo() {
( x |> f ) == y
}

View File

@ -0,0 +1,8 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\nfn foo() {\n x |> { f == y }\n}\n"
---
fn foo() {
x |> f == y
}

View File

@ -0,0 +1,11 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\nfn foo() {\n True || {\n let bar = 1\n bar == bar\n }\n}\n"
---
fn foo() {
True || {
let bar = 1
bar == bar
}
}

View File

@ -0,0 +1,10 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\nfn foo() {\n expect 1 == 1\n\n False\n}\n"
---
fn foo() {
expect 1 == 1
False
}

View File

@ -1131,15 +1131,15 @@ fn suggest_constructor_pattern(
} }
fn suggest_unify( fn suggest_unify(
expected: &Rc<Type>, expected: &Type,
given: &Rc<Type>, given: &Type,
situation: &Option<UnifyErrorSituation>, situation: &Option<UnifyErrorSituation>,
rigid_type_names: &HashMap<u64, String>, rigid_type_names: &HashMap<u64, String>,
) -> String { ) -> String {
let expected_str = expected.to_pretty_with_names(rigid_type_names.clone(), 0); let expected_str = expected.to_pretty_with_names(rigid_type_names.clone(), 0);
let given_str = given.to_pretty_with_names(rigid_type_names.clone(), 0); let given_str = given.to_pretty_with_names(rigid_type_names.clone(), 0);
let (expected, given) = match (expected.as_ref(), given.as_ref()) { let (expected, given) = match (expected, given) {
( (
Type::App { Type::App {
module: expected_module, module: expected_module,
@ -1630,7 +1630,7 @@ pub enum UnknownRecordFieldSituation {
fn format_suggestion(sample: &UntypedExpr) -> String { fn format_suggestion(sample: &UntypedExpr) -> String {
Formatter::new() Formatter::new()
.expr(sample) .expr(sample, false)
.to_pretty_string(70) .to_pretty_string(70)
.lines() .lines()
.enumerate() .enumerate()

View File

@ -426,7 +426,9 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
tipo: string(), tipo: string(),
value: format!( value: format!(
"{} ? False", "{} ? False",
format::Formatter::new().expr(&value).to_pretty_string(999) format::Formatter::new()
.expr(&value, false)
.to_pretty_string(999)
), ),
}; };

View File

@ -150,9 +150,9 @@ impl FieldMap {
self.fields self.fields
.keys() .keys()
.cloned()
.filter(|f| !given.contains(f)) .filter(|f| !given.contains(f))
.sorted() .sorted()
.cloned()
.collect() .collect()
} }
} }

View File

@ -26,6 +26,7 @@ indexmap = "1.9.2"
itertools = "0.10.5" itertools = "0.10.5"
miette = { version = "5.9.0", features = ["fancy"] } miette = { version = "5.9.0", features = ["fancy"] }
minicbor = "0.19.1" minicbor = "0.19.1"
notify = "6.1.1"
owo-colors = { version = "3.5.0", features = ["supports-colors"] } owo-colors = { version = "3.5.0", features = ["supports-colors"] }
pallas = "0.18.0" pallas = "0.18.0"
pallas-traverse = "0.18.0" pallas-traverse = "0.18.0"

View File

@ -1,9 +1,6 @@
use crate::{ use crate::{blueprint::error as blueprint, deps::manifest::Package, package_name::PackageName};
blueprint::error as blueprint, deps::manifest::Package, package_name::PackageName, pretty,
script::EvalHint,
};
use aiken_lang::{ use aiken_lang::{
ast::{self, BinOp, Span}, ast::{self, Span},
error::ExtraData, error::ExtraData,
parser::error::ParseError, parser::error::ParseError,
tipo, tipo,
@ -18,7 +15,6 @@ use std::{
ops::Deref, ops::Deref,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use uplc::machine::cost_model::ExBudget;
use zip::result::ZipError; use zip::result::ZipError;
#[allow(dead_code)] #[allow(dead_code)]
@ -97,7 +93,7 @@ pub enum Error {
path: PathBuf, path: PathBuf,
verbose: bool, verbose: bool,
src: String, src: String,
evaluation_hint: Option<EvalHint>, evaluation_hint: Option<String>,
}, },
#[error( #[error(
@ -266,31 +262,45 @@ impl Diagnostic for Error {
} }
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
fn boxed<'a>(s: Box<dyn Display + 'a>) -> Box<dyn Display + 'a> {
Box::new(format!(
" {} {}",
"Error"
.if_supports_color(Stdout, |s| s.red())
.if_supports_color(Stdout, |s| s.bold()),
format!("{s}").if_supports_color(Stdout, |s| s.red())
))
}
match self { match self {
Error::DuplicateModule { .. } => Some(Box::new("aiken::module::duplicate")), Error::DuplicateModule { .. } => Some(boxed(Box::new("aiken::module::duplicate"))),
Error::FileIo { .. } => None, Error::FileIo { .. } => None,
Error::Blueprint(e) => e.code(), Error::Blueprint(e) => e.code().map(boxed),
Error::ImportCycle { .. } => Some(Box::new("aiken::module::cyclical")), Error::ImportCycle { .. } => Some(boxed(Box::new("aiken::module::cyclical"))),
Error::Parse { .. } => Some(Box::new("aiken::parser")), Error::Parse { .. } => Some(boxed(Box::new("aiken::parser"))),
Error::Type { error, .. } => Some(Box::new(format!( Error::Type { error, .. } => Some(boxed(Box::new(format!(
"aiken::check{}", "aiken::check{}",
error.code().map(|s| format!("::{s}")).unwrap_or_default() error.code().map(|s| format!("::{s}")).unwrap_or_default()
))), )))),
Error::StandardIo(_) => None, Error::StandardIo(_) => None,
Error::MissingManifest { .. } => None, Error::MissingManifest { .. } => None,
Error::TomlLoading { .. } => Some(Box::new("aiken::loading::toml")), Error::TomlLoading { .. } => Some(boxed(Box::new("aiken::loading::toml"))),
Error::Format { .. } => None, Error::Format { .. } => None,
Error::TestFailure { path, .. } => Some(Box::new(path.to_str().unwrap_or(""))), Error::TestFailure { path, .. } => Some(boxed(Box::new(path.to_str().unwrap_or("")))),
Error::Http(_) => Some(Box::new("aiken::packages::download")), Error::Http(_) => Some(Box::new("aiken::packages::download")),
Error::ZipExtract(_) => None, Error::ZipExtract(_) => None,
Error::JoinError(_) => None, Error::JoinError(_) => None,
Error::UnknownPackageVersion { .. } => Some(Box::new("aiken::packages::resolve")), Error::UnknownPackageVersion { .. } => {
Error::UnableToResolvePackage { .. } => Some(Box::new("aiken::package::download")), Some(boxed(Box::new("aiken::packages::resolve")))
}
Error::UnableToResolvePackage { .. } => {
Some(boxed(Box::new("aiken::package::download")))
}
Error::Json { .. } => None, Error::Json { .. } => None,
Error::MalformedStakeAddress { .. } => None, Error::MalformedStakeAddress { .. } => None,
Error::NoValidatorNotFound { .. } => None, Error::NoValidatorNotFound { .. } => None,
Error::MoreThanOneValidatorFound { .. } => None, Error::MoreThanOneValidatorFound { .. } => None,
Error::Module(e) => e.code(), Error::Module(e) => e.code().map(boxed),
} }
} }
@ -313,33 +323,9 @@ impl Diagnostic for Error {
Error::MissingManifest { .. } => Some(Box::new("Try running `aiken new <REPOSITORY/PROJECT>` to initialise a project with an example manifest.")), Error::MissingManifest { .. } => Some(Box::new("Try running `aiken new <REPOSITORY/PROJECT>` to initialise a project with an example manifest.")),
Error::TomlLoading { .. } => None, Error::TomlLoading { .. } => None,
Error::Format { .. } => None, Error::Format { .. } => None,
Error::TestFailure { evaluation_hint, .. } =>{ Error::TestFailure { evaluation_hint, .. } => match evaluation_hint {
match evaluation_hint { None => None,
None => None, Some(hint) => Some(Box::new(hint.to_string()))
Some(hint) => {
let budget = ExBudget { mem: i64::MAX, cpu: i64::MAX, };
let left = pretty::boxed("left", &match hint.left.clone().eval(budget).result() {
Ok(term) => format!("{term}"),
Err(err) => format!("{err}"),
});
let right = pretty::boxed("right", &match hint.right.clone().eval(budget).result() {
Ok(term) => format!("{term}"),
Err(err) => format!("{err}"),
});
let msg = match hint.bin_op {
BinOp::And => Some(format!("{left}\n\nand\n\n{right}\n\nshould both be true.")),
BinOp::Or => Some(format!("{left}\n\nor\n\n{right}\n\nshould be true.")),
BinOp::Eq => Some(format!("{left}\n\nshould be equal to\n\n{right}")),
BinOp::NotEq => Some(format!("{left}\n\nshould not be equal to\n\n{right}")),
BinOp::LtInt => Some(format!("{left}\n\nshould be lower than\n\n{right}")),
BinOp::LtEqInt => Some(format!("{left}\n\nshould be lower than or equal to\n\n{right}")),
BinOp::GtEqInt => Some(format!("{left}\n\nshould be greater than\n\n{right}")),
BinOp::GtInt => Some(format!("{left}\n\nshould be greater than or equal to\n\n{right}")),
_ => None
}?;
Some(Box::new(msg))
}
}
}, },
Error::Http(_) => None, Error::Http(_) => None,
Error::ZipExtract(_) => None, Error::ZipExtract(_) => None,
@ -560,14 +546,24 @@ impl Diagnostic for Warning {
} }
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
fn boxed<'a>(s: Box<dyn Display + 'a>) -> Box<dyn Display + 'a> {
Box::new(format!(
" {} {}",
"Warning"
.if_supports_color(Stdout, |s| s.yellow())
.if_supports_color(Stdout, |s| s.bold()),
format!("{s}").if_supports_color(Stdout, |s| s.yellow())
))
}
match self { match self {
Warning::Type { warning, .. } => Some(Box::new(format!( Warning::Type { warning, .. } => Some(boxed(Box::new(format!(
"aiken::check{}", "aiken::check{}",
warning.code().map(|s| format!("::{s}")).unwrap_or_default() warning.code().map(|s| format!("::{s}")).unwrap_or_default()
))), )))),
Warning::NoValidators => Some(Box::new("aiken::check")), Warning::NoValidators => Some(boxed(Box::new("aiken::check"))),
Warning::DependencyAlreadyExists { .. } => { Warning::DependencyAlreadyExists { .. } => {
Some(Box::new("aiken::packages::already_exists")) Some(boxed(Box::new("aiken::packages::already_exists")))
} }
} }
} }

View File

@ -14,6 +14,7 @@ pub mod script;
pub mod telemetry; pub mod telemetry;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub mod watch;
use crate::blueprint::{ use crate::blueprint::{
definitions::Definitions, definitions::Definitions,
@ -328,7 +329,11 @@ where
Some(Error::TestFailure { Some(Error::TestFailure {
name: e.script.name.clone(), name: e.script.name.clone(),
path: e.script.input_path.clone(), path: e.script.input_path.clone(),
evaluation_hint: e.script.evaluation_hint.clone(), evaluation_hint: e
.script
.evaluation_hint
.as_ref()
.map(|hint| hint.to_string()),
src: e.script.program.to_pretty(), src: e.script.program.to_pretty(),
verbose, verbose,
}) })

View File

@ -1,6 +1,9 @@
use crate::{ExBudget, Term}; use crate::{pretty, ExBudget, Term};
use aiken_lang::ast::BinOp; use aiken_lang::ast::BinOp;
use std::path::PathBuf; use std::{
fmt::{self, Display},
path::PathBuf,
};
use uplc::ast::{NamedDeBruijn, Program}; use uplc::ast::{NamedDeBruijn, Program};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -42,6 +45,48 @@ pub struct EvalHint {
pub right: Program<NamedDeBruijn>, pub right: Program<NamedDeBruijn>,
} }
impl Display for EvalHint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let unlimited_budget = ExBudget {
mem: i64::MAX,
cpu: i64::MAX,
};
let left = pretty::boxed(
"left",
&match self.left.clone().eval(unlimited_budget).result() {
Ok(term) => format!("{term}"),
Err(err) => format!("{err}"),
},
);
let right = pretty::boxed(
"right",
&match self.right.clone().eval(unlimited_budget).result() {
Ok(term) => format!("{term}"),
Err(err) => format!("{err}"),
},
);
let msg = match self.bin_op {
BinOp::And => Some(format!("{left}\n\nand\n\n{right}\n\nshould both be true.")),
BinOp::Or => Some(format!("{left}\n\nor\n\n{right}\n\nshould be true.")),
BinOp::Eq => Some(format!("{left}\n\nshould be equal to\n\n{right}")),
BinOp::NotEq => Some(format!("{left}\n\nshould not be equal to\n\n{right}")),
BinOp::LtInt => Some(format!("{left}\n\nshould be lower than\n\n{right}")),
BinOp::LtEqInt => Some(format!(
"{left}\n\nshould be lower than or equal to\n\n{right}"
)),
BinOp::GtEqInt => Some(format!("{left}\n\nshould be greater than\n\n{right}")),
BinOp::GtInt => Some(format!(
"{left}\n\nshould be greater than or equal to\n\n{right}"
)),
_ => None,
}
.ok_or(fmt::Error)?;
f.write_str(&msg)
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct EvalInfo { pub struct EvalInfo {
pub success: bool, pub success: bool,

View File

@ -1,5 +1,11 @@
use crate::pretty;
use crate::script::EvalInfo; use crate::script::EvalInfo;
use std::{fmt::Display, path::PathBuf}; use owo_colors::{
OwoColorize,
Stream::{self, Stderr},
};
use std::{collections::BTreeMap, fmt::Display, path::PathBuf};
use uplc::machine::cost_model::ExBudget;
pub trait EventListener { pub trait EventListener {
fn handle_event(&self, _event: Event) {} fn handle_event(&self, _event: Event) {}
@ -64,3 +70,339 @@ impl Display for DownloadSource {
} }
} }
} }
#[derive(Debug, Default, Clone, Copy)]
pub struct Terminal;
impl EventListener for Terminal {
fn handle_event(&self, event: Event) {
match event {
Event::StartingCompilation {
name,
version,
root,
} => {
eprintln!(
"{} {} {} ({})",
" Compiling"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
name.if_supports_color(Stderr, |s| s.bold()),
version,
root.display()
.if_supports_color(Stderr, |s| s.bright_blue())
);
}
Event::BuildingDocumentation {
name,
version,
root,
} => {
eprintln!(
"{} {} {} ({})",
" Generating documentation"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
name.if_supports_color(Stderr, |s| s.bold()),
version,
root.to_str()
.unwrap_or("")
.if_supports_color(Stderr, |s| s.bright_blue())
);
}
Event::WaitingForBuildDirLock => {
eprintln!(
"{}",
"Waiting for build directory lock ..."
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple())
);
}
Event::DumpingUPLC { path } => {
eprintln!(
"{} {} ({})",
" Exporting"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
"UPLC".if_supports_color(Stderr, |s| s.bold()),
path.display()
.if_supports_color(Stderr, |s| s.bright_blue())
);
}
Event::GeneratingBlueprint { path } => {
eprintln!(
"{} {} ({})",
" Generating"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
"project's blueprint".if_supports_color(Stderr, |s| s.bold()),
path.display()
.if_supports_color(Stderr, |s| s.bright_blue())
);
}
Event::GeneratingDocFiles { output_path } => {
eprintln!(
"{} in {}",
" Generating documentation files"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
output_path
.to_str()
.unwrap_or("")
.if_supports_color(Stderr, |s| s.bright_blue())
);
}
Event::GeneratingUPLCFor { name, path } => {
eprintln!(
"{} {}.{{{}}}",
" Generating UPLC for"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
path.to_str()
.unwrap_or("")
.if_supports_color(Stderr, |s| s.blue()),
name.if_supports_color(Stderr, |s| s.bright_blue()),
);
}
Event::EvaluatingFunction { results } => {
eprintln!(
"{}\n",
" Evaluating function ..."
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple())
);
let (max_mem, max_cpu) = find_max_execution_units(&results);
for eval_info in &results {
println!(" {}", fmt_eval(eval_info, max_mem, max_cpu, Stderr))
}
}
Event::RunningTests => {
eprintln!(
"{} {}\n",
" Testing"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
"...".if_supports_color(Stderr, |s| s.bold())
);
}
Event::FinishedTests { tests } => {
let (max_mem, max_cpu) = find_max_execution_units(&tests);
for (module, infos) in &group_by_module(&tests) {
let title = module
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.blue())
.to_string();
let tests = infos
.iter()
.map(|eval_info| fmt_test(eval_info, max_mem, max_cpu, true))
.collect::<Vec<String>>()
.join("\n");
let summary = fmt_test_summary(infos, true);
eprintln!(
"{}\n",
pretty::indent(
&pretty::open_box(&title, &tests, &summary, |border| border
.if_supports_color(Stderr, |s| s.bright_black())
.to_string()),
4
)
);
}
}
Event::ResolvingPackages { name } => {
eprintln!(
"{} {}",
" Resolving"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
name.if_supports_color(Stderr, |s| s.bold())
)
}
Event::PackageResolveFallback { name } => {
eprintln!(
"{} {}\n ↳ You're seeing this message because the package version is unpinned and the network is not accessible.",
" Using"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.yellow()),
format!("uncertain local version for {name}")
.if_supports_color(Stderr, |s| s.yellow())
)
}
Event::PackagesDownloaded {
start,
count,
source,
} => {
let elapsed = format!("{:.2}s", start.elapsed().as_millis() as f32 / 1000.);
let msg = match count {
1 => format!("1 package in {elapsed}"),
_ => format!("{count} packages in {elapsed}"),
};
eprintln!(
"{} {} from {source}",
match source {
DownloadSource::Network => " Downloaded",
DownloadSource::Cache => " Fetched",
}
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
msg.if_supports_color(Stderr, |s| s.bold())
)
}
Event::ResolvingVersions => {
eprintln!(
"{}",
" Resolving dependencies"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
)
}
}
}
}
fn fmt_test(eval_info: &EvalInfo, max_mem: usize, max_cpu: usize, styled: bool) -> String {
let EvalInfo {
success,
script,
spent_budget,
logs,
..
} = eval_info;
let ExBudget { mem, cpu } = spent_budget;
let mem_pad = pretty::pad_left(mem.to_string(), max_mem, " ");
let cpu_pad = pretty::pad_left(cpu.to_string(), max_cpu, " ");
let test = format!(
"{status} [mem: {mem_unit}, cpu: {cpu_unit}] {module}",
status = if *success {
pretty::style_if(styled, "PASS".to_string(), |s| {
s.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.green())
.to_string()
})
} else {
pretty::style_if(styled, "FAIL".to_string(), |s| {
s.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.red())
.to_string()
})
},
mem_unit = pretty::style_if(styled, mem_pad, |s| s
.if_supports_color(Stderr, |s| s.cyan())
.to_string()),
cpu_unit = pretty::style_if(styled, cpu_pad, |s| s
.if_supports_color(Stderr, |s| s.cyan())
.to_string()),
module = pretty::style_if(styled, script.name.clone(), |s| s
.if_supports_color(Stderr, |s| s.bright_blue())
.to_string()),
);
let logs = if logs.is_empty() {
String::new()
} else {
logs.iter()
.map(|line| {
format!(
"{arrow} {styled_line}",
arrow = "".if_supports_color(Stderr, |s| s.bright_yellow()),
styled_line = line.if_supports_color(Stderr, |s| s.bright_black())
)
})
.collect::<Vec<_>>()
.join("\n")
};
if logs.is_empty() {
test
} else {
[test, logs].join("\n")
}
}
fn fmt_test_summary(tests: &Vec<&EvalInfo>, styled: bool) -> String {
let (n_passed, n_failed) = tests
.iter()
.fold((0, 0), |(n_passed, n_failed), test_info| {
if test_info.success {
(n_passed + 1, n_failed)
} else {
(n_passed, n_failed + 1)
}
});
format!(
"{} | {} | {}",
pretty::style_if(styled, format!("{} tests", tests.len()), |s| s
.if_supports_color(Stderr, |s| s.bold())
.to_string()),
pretty::style_if(styled, format!("{n_passed} passed"), |s| s
.if_supports_color(Stderr, |s| s.bright_green())
.if_supports_color(Stderr, |s| s.bold())
.to_string()),
pretty::style_if(styled, format!("{n_failed} failed"), |s| s
.if_supports_color(Stderr, |s| s.bright_red())
.if_supports_color(Stderr, |s| s.bold())
.to_string()),
)
}
fn fmt_eval(eval_info: &EvalInfo, max_mem: usize, max_cpu: usize, stream: Stream) -> String {
let EvalInfo {
output,
script,
spent_budget,
..
} = eval_info;
let ExBudget { mem, cpu } = spent_budget;
format!(
" {}::{} [mem: {}, cpu: {}]\n\n ╰─▶ {}",
script.module.if_supports_color(stream, |s| s.blue()),
script.name.if_supports_color(stream, |s| s.bright_blue()),
pretty::pad_left(mem.to_string(), max_mem, " "),
pretty::pad_left(cpu.to_string(), max_cpu, " "),
output
.as_ref()
.map(|x| format!("{x}"))
.unwrap_or_else(|| "Error.".to_string()),
)
}
fn group_by_module(infos: &Vec<EvalInfo>) -> BTreeMap<String, Vec<&EvalInfo>> {
let mut modules = BTreeMap::new();
for eval_info in infos {
let xs: &mut Vec<&EvalInfo> = modules.entry(eval_info.script.module.clone()).or_default();
xs.push(eval_info);
}
modules
}
fn find_max_execution_units(xs: &[EvalInfo]) -> (usize, usize) {
let (max_mem, max_cpu) = xs.iter().fold(
(0, 0),
|(max_mem, max_cpu), EvalInfo { spent_budget, .. }| {
if spent_budget.mem >= max_mem && spent_budget.cpu >= max_cpu {
(spent_budget.mem, spent_budget.cpu)
} else if spent_budget.mem > max_mem {
(spent_budget.mem, max_cpu)
} else if spent_budget.cpu > max_cpu {
(max_mem, spent_budget.cpu)
} else {
(max_mem, max_cpu)
}
},
);
(max_mem.to_string().len(), max_cpu.to_string().len())
}

View File

@ -0,0 +1,225 @@
use crate::{telemetry::Terminal, Project};
use miette::{Diagnostic, IntoDiagnostic};
use notify::{Event, RecursiveMode, Watcher};
use owo_colors::{OwoColorize, Stream::Stderr};
use std::{
collections::VecDeque,
env,
ffi::OsStr,
fmt::{self, Display},
path::Path,
sync::{Arc, Mutex},
};
#[derive(Debug, Diagnostic, thiserror::Error)]
enum ExitFailure {
#[error("")]
ExitFailure,
}
impl ExitFailure {
fn into_report() -> miette::Report {
ExitFailure::ExitFailure.into()
}
}
struct Summary {
warning_count: usize,
error_count: usize,
}
impl Display for Summary {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&format!(
" {} {} {}, {} {}",
"Summary"
.if_supports_color(Stderr, |s| s.purple())
.if_supports_color(Stderr, |s| s.bold()),
self.error_count,
if self.error_count == 1 {
"error"
} else {
"errors"
}
.if_supports_color(Stderr, |s| s.red())
.if_supports_color(Stderr, |s| s.bold()),
self.warning_count,
if self.warning_count == 1 {
"warning"
} else {
"warnings"
}
.if_supports_color(Stderr, |s| s.yellow())
.if_supports_color(Stderr, |s| s.bold()),
))
}
}
/// A default filter for file events that catches the most relevant "source" changes
pub fn default_filter(evt: &Event) -> bool {
// Only watch for changes to .ak and aiken.toml files, and ignore the build directory
let source_file = evt
.paths
.iter()
.any(|p| p.extension() == Some(OsStr::new("ak")) || p.ends_with("aiken.toml"));
let build_dir = evt
.paths
.iter()
.all(|p| p.ancestors().any(|a| a.ends_with("build")));
match evt.kind {
notify::EventKind::Any => true,
notify::EventKind::Create(_)
| notify::EventKind::Modify(_)
| notify::EventKind::Remove(_) => source_file && !build_dir,
_ => false,
}
}
pub fn with_project<A>(directory: Option<&Path>, deny: bool, mut action: A) -> miette::Result<()>
where
A: FnMut(&mut Project<Terminal>) -> Result<(), Vec<crate::error::Error>>,
{
let project_path = if let Some(d) = directory {
d.to_path_buf()
} else {
env::current_dir().into_diagnostic()?
};
let mut project = match Project::new(project_path, Terminal) {
Ok(p) => Ok(p),
Err(e) => {
e.report();
Err(ExitFailure::into_report())
}
}?;
let build_result = action(&mut project);
let warnings = project.warnings();
let warning_count = warnings.len();
for warning in &warnings {
warning.report()
}
if let Err(errs) = build_result {
for err in &errs {
err.report()
}
eprintln!(
"{}",
Summary {
warning_count,
error_count: errs.len(),
}
);
return Err(ExitFailure::into_report());
} else {
eprintln!(
"{}",
Summary {
error_count: 0,
warning_count
}
);
}
if warning_count > 0 && deny {
Err(ExitFailure::into_report())
} else {
Ok(())
}
}
/// Run a function each time a file in the project changes
///
/// ```text
/// // Note: doctest disabled, because aiken_project doesn't have an implementation of EventListener I can use
/// use aiken_project::watch::{watch_project, default_filter};
/// use aiken_project::{Project};
/// watch_project(None, Terminal, default_filter, 500, |project| {
/// println!("Project changed!");
/// Ok(())
/// });
/// ```
pub fn watch_project<F, A>(
directory: Option<&Path>,
filter: F,
debounce: u32,
mut action: A,
) -> miette::Result<()>
where
F: Fn(&Event) -> bool,
A: FnMut(&mut Project<Terminal>) -> Result<(), Vec<crate::error::Error>>,
{
let project_path = directory
.map(|p| p.to_path_buf())
.unwrap_or(env::current_dir().into_diagnostic()?);
// Set up a queue for events, primarily so we can debounce on related events
let queue = Arc::new(Mutex::new(VecDeque::new()));
// Run the action once, to start
queue
.lock()
.expect("lock queue")
.push_back(Event::default());
// Spawn a file-watcher that will put each change event on the queue
let queue_write = queue.clone();
let mut watcher = notify::recommended_watcher(move |res: notify::Result<Event>| {
match res {
Ok(event) => queue_write
.lock()
.expect("lock queue")
.push_back(event.clone()),
Err(e) => {
// TODO: miette diagnostic?
println!(
"Encountered an error while monitoring for file changes: {:?}",
e
)
}
};
})
.into_diagnostic()?;
// Start watching for any changes in the project directory
let _ = watcher.watch(project_path.as_path(), RecursiveMode::Recursive);
// And then start reading from the queue
let queue_read = queue.clone();
loop {
// We sleep for the debounce interval, because notify will dump 12 related events into the queue all at once
std::thread::sleep(std::time::Duration::from_millis(debounce.into()));
// Grab the lock, and pop all events except the last one off the queue
let mut queue = queue_read.lock().expect("lock queue");
let mut latest = None;
// debounce the events, and ignore build/lock changes, because they come in in large batches
while let Some(evt) = queue.pop_back() {
// check if this event is meaningful to the caller
if !filter(&evt) {
continue;
}
latest = Some(evt);
}
// release the lock here, in case other events come in
drop(queue);
// If we have an event that survived the filter, then we can construct the project and invoke the action
if latest.is_some() {
print!("{esc}c", esc = 27 as char);
println!(
"{} ...",
" Watching"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
);
with_project(directory, false, &mut action).unwrap_or(())
}
}
}

View File

@ -1,5 +1,5 @@
use crate::with_project;
use aiken_lang::ast::Tracing; use aiken_lang::ast::Tracing;
use aiken_project::watch::with_project;
use std::path::PathBuf; use std::path::PathBuf;
/// Compute a validator's address. /// Compute a validator's address.
@ -34,7 +34,7 @@ pub fn exec(
rebuild, rebuild,
}: Args, }: Args,
) -> miette::Result<()> { ) -> miette::Result<()> {
with_project(directory, false, |p| { with_project(directory.as_deref(), false, |p| {
if rebuild { if rebuild {
p.build(false, Tracing::NoTraces)?; p.build(false, Tracing::NoTraces)?;
} }

View File

@ -1,4 +1,3 @@
use crate::with_project;
use aiken_project::{ use aiken_project::{
blueprint::{ blueprint::{
self, self,
@ -7,6 +6,7 @@ use aiken_project::{
}, },
error::Error, error::Error,
pretty::multiline, pretty::multiline,
watch::with_project,
}; };
use inquire; use inquire;
use num_bigint::BigInt; use num_bigint::BigInt;

View File

@ -1,5 +1,5 @@
use crate::with_project;
use aiken_lang::ast::Tracing; use aiken_lang::ast::Tracing;
use aiken_project::watch::with_project;
use std::path::PathBuf; use std::path::PathBuf;
/// Compute a validator's hash /// Compute a validator's hash
@ -29,7 +29,7 @@ pub fn exec(
rebuild, rebuild,
}: Args, }: Args,
) -> miette::Result<()> { ) -> miette::Result<()> {
with_project(directory, false, |p| { with_project(directory.as_deref(), false, |p| {
if rebuild { if rebuild {
p.build(false, Tracing::NoTraces)?; p.build(false, Tracing::NoTraces)?;
} }

View File

@ -1,5 +1,5 @@
use crate::with_project;
use aiken_lang::ast::Tracing; use aiken_lang::ast::Tracing;
use aiken_project::watch::with_project;
use std::path::PathBuf; use std::path::PathBuf;
/// Compute a minting scripts Policy ID /// Compute a minting scripts Policy ID
@ -29,7 +29,7 @@ pub fn exec(
rebuild, rebuild,
}: Args, }: Args,
) -> miette::Result<()> { ) -> miette::Result<()> {
with_project(directory, false, |p| { with_project(directory.as_deref(), false, |p| {
if rebuild { if rebuild {
p.build(false, Tracing::NoTraces)?; p.build(false, Tracing::NoTraces)?;
} }

View File

@ -1,3 +1,4 @@
use aiken_project::watch::{self, watch_project, with_project};
use std::path::PathBuf; use std::path::PathBuf;
#[derive(clap::Args)] #[derive(clap::Args)]
@ -10,6 +11,10 @@ pub struct Args {
#[clap(short = 'D', long)] #[clap(short = 'D', long)]
deny: bool, deny: bool,
/// When enabled, re-run the command on file changes instead of exiting
#[clap(short, long)]
watch: bool,
/// Also dump textual uplc /// Also dump textual uplc
#[clap(short, long)] #[clap(short, long)]
uplc: bool, uplc: bool,
@ -23,9 +28,18 @@ pub fn exec(
Args { Args {
directory, directory,
deny, deny,
watch,
uplc, uplc,
keep_traces, keep_traces,
}: Args, }: Args,
) -> miette::Result<()> { ) -> miette::Result<()> {
crate::with_project(directory, deny, |p| p.build(uplc, keep_traces.into())) if watch {
watch_project(directory.as_deref(), watch::default_filter, 500, |p| {
p.build(uplc, keep_traces.into())
})
} else {
with_project(directory.as_deref(), deny, |p| {
p.build(uplc, keep_traces.into())
})
}
} }

View File

@ -1,3 +1,4 @@
use aiken_project::watch::{self, watch_project, with_project};
use std::path::PathBuf; use std::path::PathBuf;
#[derive(clap::Args)] #[derive(clap::Args)]
@ -18,6 +19,10 @@ pub struct Args {
#[clap(long)] #[clap(long)]
debug: bool, debug: bool,
/// When enabled, re-run the command on file changes instead of exiting
#[clap(long)]
watch: bool,
/// Only run tests if they match any of these strings. /// Only run tests if they match any of these strings.
/// You can match a module with `-m aiken/list` or `-m list`. /// You can match a module with `-m aiken/list` or `-m list`.
/// You can match a test with `-m "aiken/list.{map}"` or `-m "aiken/option.{flatten_1}"` /// You can match a test with `-m "aiken/list.{map}"` or `-m "aiken/option.{flatten_1}"`
@ -43,15 +48,29 @@ pub fn exec(
match_tests, match_tests,
exact_match, exact_match,
no_traces, no_traces,
watch,
..
}: Args, }: Args,
) -> miette::Result<()> { ) -> miette::Result<()> {
crate::with_project(directory, deny, |p| { if watch {
p.check( watch_project(directory.as_deref(), watch::default_filter, 500, |p| {
skip_tests, p.check(
match_tests.clone(), skip_tests,
debug, match_tests.clone(),
exact_match, debug,
(!no_traces).into(), exact_match,
) (!no_traces).into(),
}) )
})
} else {
with_project(directory.as_deref(), deny, |p| {
p.check(
skip_tests,
match_tests.clone(),
debug,
exact_match,
(!no_traces).into(),
)
})
}
} }

View File

@ -1,3 +1,4 @@
use aiken_project::watch::{self, watch_project, with_project};
use std::path::PathBuf; use std::path::PathBuf;
#[derive(clap::Args)] #[derive(clap::Args)]
@ -10,6 +11,10 @@ pub struct Args {
#[clap(short = 'D', long)] #[clap(short = 'D', long)]
deny: bool, deny: bool,
/// When enabled, re-run the command on file changes instead of exiting
#[clap(short, long)]
watch: bool,
/// Output directory for the documentation /// Output directory for the documentation
#[clap(short = 'o', long)] #[clap(short = 'o', long)]
destination: Option<PathBuf>, destination: Option<PathBuf>,
@ -19,8 +24,15 @@ pub fn exec(
Args { Args {
directory, directory,
deny, deny,
watch,
destination, destination,
}: Args, }: Args,
) -> miette::Result<()> { ) -> miette::Result<()> {
crate::with_project(directory, deny, |p| p.docs(destination.clone())) if watch {
watch_project(directory.as_deref(), watch::default_filter, 500, |p| {
p.docs(destination.clone())
})
} else {
with_project(directory.as_deref(), deny, |p| p.docs(destination.clone()))
}
} }

View File

@ -1,431 +0,0 @@
use aiken_project::{
pretty,
script::EvalInfo,
telemetry::{self, DownloadSource},
Project,
};
use miette::IntoDiagnostic;
use owo_colors::{
OwoColorize,
Stream::{self, Stderr},
};
use std::{collections::BTreeMap, env, path::PathBuf, process};
use uplc::machine::cost_model::ExBudget;
pub mod cmd;
pub fn with_project<A>(directory: Option<PathBuf>, deny: bool, mut action: A) -> miette::Result<()>
where
A: FnMut(&mut Project<Terminal>) -> Result<(), Vec<aiken_project::error::Error>>,
{
let project_path = if let Some(d) = directory {
d
} else {
env::current_dir().into_diagnostic()?
};
let mut project = match Project::new(project_path, Terminal) {
Ok(p) => p,
Err(e) => {
e.report();
process::exit(1);
}
};
let build_result = action(&mut project);
let warnings = project.warnings();
let warning_count = warnings.len();
for warning in &warnings {
warning.report()
}
let plural = if warning_count == 1 { "" } else { "s" };
if let Err(errs) = build_result {
for err in &errs {
err.report()
}
eprintln!(
"\n{}",
"Summary"
.if_supports_color(Stderr, |s| s.purple())
.if_supports_color(Stderr, |s| s.bold())
);
let warning_text = format!("{warning_count} warning{plural}");
let plural = if errs.len() == 1 { "" } else { "s" };
let error_text = format!("{} error{}", errs.len(), plural);
let full_summary = format!(
" {}, {}",
error_text.if_supports_color(Stderr, |s| s.red()),
warning_text.if_supports_color(Stderr, |s| s.yellow())
);
eprintln!("{full_summary}");
process::exit(1);
} else {
eprintln!(
"\n{}",
"Summary"
.if_supports_color(Stderr, |s| s.purple())
.if_supports_color(Stderr, |s| s.bold())
);
let warning_text = format!("{warning_count} warning{plural}");
eprintln!(
" 0 errors, {}",
warning_text.if_supports_color(Stderr, |s| s.yellow()),
);
}
if warning_count > 0 && deny {
process::exit(1);
}
Ok(())
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Terminal;
impl telemetry::EventListener for Terminal {
fn handle_event(&self, event: telemetry::Event) {
match event {
telemetry::Event::StartingCompilation {
name,
version,
root,
} => {
eprintln!(
"{} {} {} ({})",
" Compiling"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
name.if_supports_color(Stderr, |s| s.bold()),
version,
root.display()
.if_supports_color(Stderr, |s| s.bright_blue())
);
}
telemetry::Event::BuildingDocumentation {
name,
version,
root,
} => {
eprintln!(
"{} {} {} ({})",
" Generating documentation"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
name.if_supports_color(Stderr, |s| s.bold()),
version,
root.to_str()
.unwrap_or("")
.if_supports_color(Stderr, |s| s.bright_blue())
);
}
telemetry::Event::WaitingForBuildDirLock => {
eprintln!(
"{}",
"Waiting for build directory lock ..."
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple())
);
}
telemetry::Event::DumpingUPLC { path } => {
eprintln!(
"{} {} ({})",
" Exporting"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
"UPLC".if_supports_color(Stderr, |s| s.bold()),
path.display()
.if_supports_color(Stderr, |s| s.bright_blue())
);
}
telemetry::Event::GeneratingBlueprint { path } => {
eprintln!(
"{} {} ({})",
" Generating"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
"project's blueprint".if_supports_color(Stderr, |s| s.bold()),
path.display()
.if_supports_color(Stderr, |s| s.bright_blue())
);
}
telemetry::Event::GeneratingDocFiles { output_path } => {
eprintln!(
"{} in {}",
" Generating documentation files"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
output_path
.to_str()
.unwrap_or("")
.if_supports_color(Stderr, |s| s.bright_blue())
);
}
telemetry::Event::GeneratingUPLCFor { name, path } => {
eprintln!(
"{} {}.{{{}}}",
" Generating UPLC for"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
path.to_str()
.unwrap_or("")
.if_supports_color(Stderr, |s| s.blue()),
name.if_supports_color(Stderr, |s| s.bright_blue()),
);
}
telemetry::Event::EvaluatingFunction { results } => {
eprintln!(
"{}\n",
" Evaluating function ..."
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple())
);
let (max_mem, max_cpu) = find_max_execution_units(&results);
for eval_info in &results {
println!(" {}", fmt_eval(eval_info, max_mem, max_cpu, Stderr))
}
}
telemetry::Event::RunningTests => {
eprintln!(
"{} {}\n",
" Testing"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
"...".if_supports_color(Stderr, |s| s.bold())
);
}
telemetry::Event::FinishedTests { tests } => {
let (max_mem, max_cpu) = find_max_execution_units(&tests);
for (module, infos) in &group_by_module(&tests) {
let title = module
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.blue())
.to_string();
let tests = infos
.iter()
.map(|eval_info| fmt_test(eval_info, max_mem, max_cpu, true))
.collect::<Vec<String>>()
.join("\n");
let summary = fmt_test_summary(infos, true);
eprintln!(
"{}\n",
pretty::indent(
&pretty::open_box(&title, &tests, &summary, |border| border
.if_supports_color(Stderr, |s| s.bright_black())
.to_string()),
4
)
);
}
}
telemetry::Event::ResolvingPackages { name } => {
eprintln!(
"{} {}",
" Resolving"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
name.if_supports_color(Stderr, |s| s.bold())
)
}
telemetry::Event::PackageResolveFallback { name } => {
eprintln!(
"{} {}\n ↳ You're seeing this message because the package version is unpinned and the network is not accessible.",
" Using"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.yellow()),
format!("uncertain local version for {name}")
.if_supports_color(Stderr, |s| s.yellow())
)
}
telemetry::Event::PackagesDownloaded {
start,
count,
source,
} => {
let elapsed = format!("{:.2}s", start.elapsed().as_millis() as f32 / 1000.);
let msg = match count {
1 => format!("1 package in {elapsed}"),
_ => format!("{count} packages in {elapsed}"),
};
eprintln!(
"{} {} from {source}",
match source {
DownloadSource::Network => " Downloaded",
DownloadSource::Cache => " Fetched",
}
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
msg.if_supports_color(Stderr, |s| s.bold())
)
}
telemetry::Event::ResolvingVersions => {
eprintln!(
"{}",
" Resolving dependencies"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.purple()),
)
}
}
}
}
fn fmt_test(eval_info: &EvalInfo, max_mem: usize, max_cpu: usize, styled: bool) -> String {
let EvalInfo {
success,
script,
spent_budget,
logs,
..
} = eval_info;
let ExBudget { mem, cpu } = spent_budget;
let mem_pad = pretty::pad_left(mem.to_string(), max_mem, " ");
let cpu_pad = pretty::pad_left(cpu.to_string(), max_cpu, " ");
let test = format!(
"{status} [mem: {mem_unit}, cpu: {cpu_unit}] {module}",
status = if *success {
pretty::style_if(styled, "PASS".to_string(), |s| {
s.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.green())
.to_string()
})
} else {
pretty::style_if(styled, "FAIL".to_string(), |s| {
s.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.red())
.to_string()
})
},
mem_unit = pretty::style_if(styled, mem_pad, |s| s
.if_supports_color(Stderr, |s| s.cyan())
.to_string()),
cpu_unit = pretty::style_if(styled, cpu_pad, |s| s
.if_supports_color(Stderr, |s| s.cyan())
.to_string()),
module = pretty::style_if(styled, script.name.clone(), |s| s
.if_supports_color(Stderr, |s| s.bright_blue())
.to_string()),
);
let logs = if logs.is_empty() {
String::new()
} else {
logs.iter()
.map(|line| {
format!(
"{arrow} {styled_line}",
arrow = "".if_supports_color(Stderr, |s| s.bright_yellow()),
styled_line = line.if_supports_color(Stderr, |s| s.bright_black())
)
})
.collect::<Vec<_>>()
.join("\n")
};
if logs.is_empty() {
test
} else {
[test, logs].join("\n")
}
}
fn fmt_test_summary(tests: &Vec<&EvalInfo>, styled: bool) -> String {
let (n_passed, n_failed) = tests
.iter()
.fold((0, 0), |(n_passed, n_failed), test_info| {
if test_info.success {
(n_passed + 1, n_failed)
} else {
(n_passed, n_failed + 1)
}
});
format!(
"{} | {} | {}",
pretty::style_if(styled, format!("{} tests", tests.len()), |s| s
.if_supports_color(Stderr, |s| s.bold())
.to_string()),
pretty::style_if(styled, format!("{n_passed} passed"), |s| s
.if_supports_color(Stderr, |s| s.bright_green())
.if_supports_color(Stderr, |s| s.bold())
.to_string()),
pretty::style_if(styled, format!("{n_failed} failed"), |s| s
.if_supports_color(Stderr, |s| s.bright_red())
.if_supports_color(Stderr, |s| s.bold())
.to_string()),
)
}
fn fmt_eval(eval_info: &EvalInfo, max_mem: usize, max_cpu: usize, stream: Stream) -> String {
let EvalInfo {
output,
script,
spent_budget,
..
} = eval_info;
let ExBudget { mem, cpu } = spent_budget;
format!(
" {}::{} [mem: {}, cpu: {}]\n\n ╰─▶ {}",
script.module.if_supports_color(stream, |s| s.blue()),
script.name.if_supports_color(stream, |s| s.bright_blue()),
pretty::pad_left(mem.to_string(), max_mem, " "),
pretty::pad_left(cpu.to_string(), max_cpu, " "),
output
.as_ref()
.map(|x| format!("{x}"))
.unwrap_or_else(|| "Error.".to_string()),
)
}
fn group_by_module(infos: &Vec<EvalInfo>) -> BTreeMap<String, Vec<&EvalInfo>> {
let mut modules = BTreeMap::new();
for eval_info in infos {
let xs: &mut Vec<&EvalInfo> = modules.entry(eval_info.script.module.clone()).or_default();
xs.push(eval_info);
}
modules
}
fn find_max_execution_units(xs: &[EvalInfo]) -> (usize, usize) {
let (max_mem, max_cpu) = xs.iter().fold(
(0, 0),
|(max_mem, max_cpu), EvalInfo { spent_budget, .. }| {
if spent_budget.mem >= max_mem && spent_budget.cpu >= max_cpu {
(spent_budget.mem, spent_budget.cpu)
} else if spent_budget.mem > max_mem {
(spent_budget.mem, max_cpu)
} else if spent_budget.cpu > max_cpu {
(max_mem, spent_budget.cpu)
} else {
(max_mem, max_cpu)
}
},
);
(max_mem.to_string().len(), max_cpu.to_string().len())
}

View File

@ -1,17 +1,19 @@
use aiken::cmd::{ use aiken_project::{config, pretty};
use cmd::{
blueprint::{self, address}, blueprint::{self, address},
build, check, completion, docs, fmt, lsp, new, build, check, completion, docs, fmt, lsp, new,
packages::{self, add}, packages::{self, add},
tx, uplc, Cmd, tx, uplc, Cmd,
}; };
use aiken_project::{config, pretty};
use owo_colors::OwoColorize; use owo_colors::OwoColorize;
use std::process;
fn main() -> miette::Result<()> { mod cmd;
fn main() {
panic_handler(); panic_handler();
match Cmd::default() { let result = match Cmd::default() {
Cmd::New(args) => new::exec(args), Cmd::New(args) => new::exec(args),
Cmd::Fmt(args) => fmt::exec(args), Cmd::Fmt(args) => fmt::exec(args),
Cmd::Build(args) => build::exec(args), Cmd::Build(args) => build::exec(args),
@ -25,6 +27,11 @@ fn main() -> miette::Result<()> {
Cmd::Tx(sub_cmd) => tx::exec(sub_cmd), Cmd::Tx(sub_cmd) => tx::exec(sub_cmd),
Cmd::Uplc(sub_cmd) => uplc::exec(sub_cmd), Cmd::Uplc(sub_cmd) => uplc::exec(sub_cmd),
Cmd::Completion(sub_cmd) => completion::exec(sub_cmd), Cmd::Completion(sub_cmd) => completion::exec(sub_cmd),
};
match result {
Ok(()) => (),
Err(_) => process::exit(1),
} }
} }

View File

@ -240,9 +240,12 @@ impl Machine {
env, env,
t.clone(), t.clone(),
)), )),
None => todo!(), None => Err(Error::MissingCaseBranch(
branches,
Value::Constr { tag, fields },
)),
}, },
_ => todo!("return a proper evaluation error"), v => Err(Error::NonConstrScrutinized(v)),
}, },
} }
} }

View File

@ -186,12 +186,12 @@ impl Default for MachineCosts {
}, },
// Placeholder values // Placeholder values
constr: ExBudget { constr: ExBudget {
mem: 30000000000, mem: 100,
cpu: 30000000000, cpu: 23000,
}, },
case: ExBudget { case: ExBudget {
mem: 30000000000, mem: 100,
cpu: 30000000000, cpu: 23000,
}, },
} }
} }

View File

@ -20,6 +20,10 @@ pub enum Error {
NonPolymorphicInstantiation(Value), NonPolymorphicInstantiation(Value),
#[error("Attempted to apply a non-function:\n\n{0:#?} to argument:\n\n{1:#?}")] #[error("Attempted to apply a non-function:\n\n{0:#?} to argument:\n\n{1:#?}")]
NonFunctionalApplication(Value, Value), NonFunctionalApplication(Value, Value),
#[error("Attempted to case a non-const:\n\n{0:#?}")]
NonConstrScrutinized(Value),
#[error("Cases: {0:#?}\n\n are missing branch for constr:\n\n{1:#?}")]
MissingCaseBranch(Vec<Term<NamedDeBruijn>>, Value),
#[error("Type mismatch expected '{0}' got '{1}'")] #[error("Type mismatch expected '{0}' got '{1}'")]
TypeMismatch(Type, Type), TypeMismatch(Type, Type),
#[error("Type mismatch expected '(list a)' got '{0}'")] #[error("Type mismatch expected '(list a)' got '{0}'")]
@ -36,6 +40,7 @@ pub enum Error {
NotAConstant(Value), NotAConstant(Value),
#[error("The evaluation never reached a final state")] #[error("The evaluation never reached a final state")]
MachineNeverReachedDone, MachineNeverReachedDone,
#[error("Decoding utf8")] #[error("Decoding utf8")]
Utf8(#[from] FromUtf8Error), Utf8(#[from] FromUtf8Error),
#[error("Out of Bounds\n\nindex: {}\nbytestring: {}\npossible: 0 - {}", .0, hex::encode(.1), .1.len() - 1)] #[error("Out of Bounds\n\nindex: {}\nbytestring: {}\npossible: 0 - {}", .0, hex::encode(.1), .1.len() - 1)]

View File

@ -89,8 +89,6 @@ impl BuiltinRuntime {
} }
pub fn push(&mut self, arg: Value) -> Result<(), Error> { pub fn push(&mut self, arg: Value) -> Result<(), Error> {
self.fun.check_type(&arg, &self.args)?;
self.args.push(arg); self.args.push(arg);
Ok(()) Ok(())
@ -264,171 +262,6 @@ impl DefaultFunction {
} }
} }
pub fn check_type(&self, arg: &Value, args: &[Value]) -> Result<(), Error> {
match self {
DefaultFunction::AddInteger => arg.expect_type(Type::Integer),
DefaultFunction::SubtractInteger => arg.expect_type(Type::Integer),
DefaultFunction::MultiplyInteger => arg.expect_type(Type::Integer),
DefaultFunction::DivideInteger => arg.expect_type(Type::Integer),
DefaultFunction::QuotientInteger => arg.expect_type(Type::Integer),
DefaultFunction::RemainderInteger => arg.expect_type(Type::Integer),
DefaultFunction::ModInteger => arg.expect_type(Type::Integer),
DefaultFunction::EqualsInteger => arg.expect_type(Type::Integer),
DefaultFunction::LessThanInteger => arg.expect_type(Type::Integer),
DefaultFunction::LessThanEqualsInteger => arg.expect_type(Type::Integer),
DefaultFunction::AppendByteString => arg.expect_type(Type::ByteString),
DefaultFunction::ConsByteString => {
if args.is_empty() {
arg.expect_type(Type::Integer)
} else {
arg.expect_type(Type::ByteString)
}
}
DefaultFunction::SliceByteString => {
if args.len() < 2 {
arg.expect_type(Type::Integer)
} else {
arg.expect_type(Type::ByteString)
}
}
DefaultFunction::LengthOfByteString => arg.expect_type(Type::ByteString),
DefaultFunction::IndexByteString => {
if args.is_empty() {
arg.expect_type(Type::ByteString)
} else {
arg.expect_type(Type::Integer)
}
}
DefaultFunction::EqualsByteString => arg.expect_type(Type::ByteString),
DefaultFunction::LessThanByteString => arg.expect_type(Type::ByteString),
DefaultFunction::LessThanEqualsByteString => arg.expect_type(Type::ByteString),
DefaultFunction::Sha2_256 => arg.expect_type(Type::ByteString),
DefaultFunction::Sha3_256 => arg.expect_type(Type::ByteString),
DefaultFunction::Blake2b_224 => arg.expect_type(Type::ByteString),
DefaultFunction::Blake2b_256 => arg.expect_type(Type::ByteString),
DefaultFunction::Keccak_256 => arg.expect_type(Type::ByteString),
DefaultFunction::VerifyEd25519Signature => arg.expect_type(Type::ByteString),
DefaultFunction::VerifyEcdsaSecp256k1Signature => arg.expect_type(Type::ByteString),
DefaultFunction::VerifySchnorrSecp256k1Signature => arg.expect_type(Type::ByteString),
DefaultFunction::AppendString => arg.expect_type(Type::String),
DefaultFunction::EqualsString => arg.expect_type(Type::String),
DefaultFunction::EncodeUtf8 => arg.expect_type(Type::String),
DefaultFunction::DecodeUtf8 => arg.expect_type(Type::ByteString),
DefaultFunction::IfThenElse => {
if args.is_empty() {
arg.expect_type(Type::Bool)
} else {
Ok(())
}
}
DefaultFunction::ChooseUnit => {
if args.is_empty() {
arg.expect_type(Type::Unit)
} else {
Ok(())
}
}
DefaultFunction::Trace => {
if args.is_empty() {
arg.expect_type(Type::String)
} else {
Ok(())
}
}
DefaultFunction::FstPair => arg.expect_pair(),
DefaultFunction::SndPair => arg.expect_pair(),
DefaultFunction::ChooseList => {
if args.is_empty() {
arg.expect_list()
} else {
Ok(())
}
}
DefaultFunction::MkCons => {
if args.is_empty() {
Ok(())
} else {
let first = &args[0];
arg.expect_type(Type::List(Rc::new(first.try_into()?)))
}
}
DefaultFunction::HeadList => arg.expect_list(),
DefaultFunction::TailList => arg.expect_list(),
DefaultFunction::NullList => arg.expect_list(),
DefaultFunction::ChooseData => {
if args.is_empty() {
arg.expect_type(Type::Data)
} else {
Ok(())
}
}
DefaultFunction::ConstrData => {
if args.is_empty() {
arg.expect_type(Type::Integer)
} else {
arg.expect_type(Type::List(Rc::new(Type::Data)))
}
}
DefaultFunction::MapData => arg.expect_type(Type::List(Rc::new(Type::Pair(
Rc::new(Type::Data),
Rc::new(Type::Data),
)))),
DefaultFunction::ListData => arg.expect_type(Type::List(Rc::new(Type::Data))),
DefaultFunction::IData => arg.expect_type(Type::Integer),
DefaultFunction::BData => arg.expect_type(Type::ByteString),
DefaultFunction::UnConstrData => arg.expect_type(Type::Data),
DefaultFunction::UnMapData => arg.expect_type(Type::Data),
DefaultFunction::UnListData => arg.expect_type(Type::Data),
DefaultFunction::UnIData => arg.expect_type(Type::Data),
DefaultFunction::UnBData => arg.expect_type(Type::Data),
DefaultFunction::EqualsData => arg.expect_type(Type::Data),
DefaultFunction::SerialiseData => arg.expect_type(Type::Data),
DefaultFunction::MkPairData => arg.expect_type(Type::Data),
DefaultFunction::MkNilData => arg.expect_type(Type::Unit),
DefaultFunction::MkNilPairData => arg.expect_type(Type::Unit),
DefaultFunction::Bls12_381_G1_Add => arg.expect_type(Type::Bls12_381G1Element),
DefaultFunction::Bls12_381_G1_Neg => arg.expect_type(Type::Bls12_381G1Element),
DefaultFunction::Bls12_381_G1_ScalarMul => {
if args.is_empty() {
arg.expect_type(Type::Integer)
} else {
arg.expect_type(Type::Bls12_381G1Element)
}
}
DefaultFunction::Bls12_381_G1_Equal => arg.expect_type(Type::Bls12_381G1Element),
DefaultFunction::Bls12_381_G1_Compress => arg.expect_type(Type::Bls12_381G1Element),
DefaultFunction::Bls12_381_G1_Uncompress => arg.expect_type(Type::ByteString),
DefaultFunction::Bls12_381_G1_HashToGroup => arg.expect_type(Type::ByteString),
DefaultFunction::Bls12_381_G2_Add => arg.expect_type(Type::Bls12_381G2Element),
DefaultFunction::Bls12_381_G2_Neg => arg.expect_type(Type::Bls12_381G2Element),
DefaultFunction::Bls12_381_G2_ScalarMul => {
if args.is_empty() {
arg.expect_type(Type::Integer)
} else {
arg.expect_type(Type::Bls12_381G2Element)
}
}
DefaultFunction::Bls12_381_G2_Equal => arg.expect_type(Type::Bls12_381G2Element),
DefaultFunction::Bls12_381_G2_Compress => arg.expect_type(Type::Bls12_381G2Element),
DefaultFunction::Bls12_381_G2_Uncompress => arg.expect_type(Type::ByteString),
DefaultFunction::Bls12_381_G2_HashToGroup => arg.expect_type(Type::ByteString),
DefaultFunction::Bls12_381_MillerLoop => {
if args.is_empty() {
arg.expect_type(Type::Bls12_381G1Element)
} else {
arg.expect_type(Type::Bls12_381G2Element)
}
}
DefaultFunction::Bls12_381_MulMlResult => arg.expect_type(Type::Bls12_381MlResult),
DefaultFunction::Bls12_381_FinalVerify => arg.expect_type(Type::Bls12_381MlResult),
}
}
// This should be safe because we've already checked
// the types of the args as they were pushed. Although
// the unreachables look ugly, it's the reality of the situation.
pub fn call( pub fn call(
&self, &self,
semantics: BuiltinSemantics, semantics: BuiltinSemantics,
@ -437,8 +270,8 @@ impl DefaultFunction {
) -> Result<Value, Error> { ) -> Result<Value, Error> {
match self { match self {
DefaultFunction::AddInteger => { DefaultFunction::AddInteger => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_integer(); let arg2 = args[1].unwrap_integer()?;
let result = arg1 + arg2; let result = arg1 + arg2;
@ -447,8 +280,8 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::SubtractInteger => { DefaultFunction::SubtractInteger => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_integer(); let arg2 = args[1].unwrap_integer()?;
let result = arg1 - arg2; let result = arg1 - arg2;
@ -457,8 +290,8 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::MultiplyInteger => { DefaultFunction::MultiplyInteger => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_integer(); let arg2 = args[1].unwrap_integer()?;
let result = arg1 * arg2; let result = arg1 * arg2;
@ -467,8 +300,8 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::DivideInteger => { DefaultFunction::DivideInteger => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_integer(); let arg2 = args[1].unwrap_integer()?;
if *arg2 != 0.into() { if *arg2 != 0.into() {
let (result, _) = arg1.div_mod_floor(arg2); let (result, _) = arg1.div_mod_floor(arg2);
@ -481,8 +314,8 @@ impl DefaultFunction {
} }
} }
DefaultFunction::QuotientInteger => { DefaultFunction::QuotientInteger => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_integer(); let arg2 = args[1].unwrap_integer()?;
if *arg2 != 0.into() { if *arg2 != 0.into() {
let (result, _) = arg1.div_rem(arg2); let (result, _) = arg1.div_rem(arg2);
@ -495,8 +328,8 @@ impl DefaultFunction {
} }
} }
DefaultFunction::RemainderInteger => { DefaultFunction::RemainderInteger => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_integer(); let arg2 = args[1].unwrap_integer()?;
if *arg2 != 0.into() { if *arg2 != 0.into() {
let (_, result) = arg1.div_rem(arg2); let (_, result) = arg1.div_rem(arg2);
@ -509,8 +342,8 @@ impl DefaultFunction {
} }
} }
DefaultFunction::ModInteger => { DefaultFunction::ModInteger => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_integer(); let arg2 = args[1].unwrap_integer()?;
if *arg2 != 0.into() { if *arg2 != 0.into() {
let (_, result) = arg1.div_mod_floor(arg2); let (_, result) = arg1.div_mod_floor(arg2);
@ -523,32 +356,32 @@ impl DefaultFunction {
} }
} }
DefaultFunction::EqualsInteger => { DefaultFunction::EqualsInteger => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_integer(); let arg2 = args[1].unwrap_integer()?;
let value = Value::bool(arg1 == arg2); let value = Value::bool(arg1 == arg2);
Ok(value) Ok(value)
} }
DefaultFunction::LessThanInteger => { DefaultFunction::LessThanInteger => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_integer(); let arg2 = args[1].unwrap_integer()?;
let value = Value::bool(arg1 < arg2); let value = Value::bool(arg1 < arg2);
Ok(value) Ok(value)
} }
DefaultFunction::LessThanEqualsInteger => { DefaultFunction::LessThanEqualsInteger => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_integer(); let arg2 = args[1].unwrap_integer()?;
let value = Value::bool(arg1 <= arg2); let value = Value::bool(arg1 <= arg2);
Ok(value) Ok(value)
} }
DefaultFunction::AppendByteString => { DefaultFunction::AppendByteString => {
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let arg2 = args[1].unwrap_byte_string(); let arg2 = args[1].unwrap_byte_string()?;
let result = arg1.iter().copied().chain(arg2.iter().copied()).collect(); let result = arg1.iter().copied().chain(arg2.iter().copied()).collect();
@ -557,8 +390,8 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::ConsByteString => { DefaultFunction::ConsByteString => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_byte_string(); let arg2 = args[1].unwrap_byte_string()?;
let byte: u8 = match semantics { let byte: u8 = match semantics {
BuiltinSemantics::V1 => { BuiltinSemantics::V1 => {
@ -584,9 +417,9 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::SliceByteString => { DefaultFunction::SliceByteString => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_integer(); let arg2 = args[1].unwrap_integer()?;
let arg3 = args[2].unwrap_byte_string(); let arg3 = args[2].unwrap_byte_string()?;
let skip: usize = if arg1.lt(&0.into()) { let skip: usize = if arg1.lt(&0.into()) {
0 0
@ -606,15 +439,15 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::LengthOfByteString => { DefaultFunction::LengthOfByteString => {
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let value = Value::integer(arg1.len().into()); let value = Value::integer(arg1.len().into());
Ok(value) Ok(value)
} }
DefaultFunction::IndexByteString => { DefaultFunction::IndexByteString => {
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let arg2 = args[1].unwrap_integer(); let arg2 = args[1].unwrap_integer()?;
let index: i128 = arg2.try_into().unwrap(); let index: i128 = arg2.try_into().unwrap();
@ -629,24 +462,24 @@ impl DefaultFunction {
} }
} }
DefaultFunction::EqualsByteString => { DefaultFunction::EqualsByteString => {
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let arg2 = args[1].unwrap_byte_string(); let arg2 = args[1].unwrap_byte_string()?;
let value = Value::bool(arg1 == arg2); let value = Value::bool(arg1 == arg2);
Ok(value) Ok(value)
} }
DefaultFunction::LessThanByteString => { DefaultFunction::LessThanByteString => {
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let arg2 = args[1].unwrap_byte_string(); let arg2 = args[1].unwrap_byte_string()?;
let value = Value::bool(arg1 < arg2); let value = Value::bool(arg1 < arg2);
Ok(value) Ok(value)
} }
DefaultFunction::LessThanEqualsByteString => { DefaultFunction::LessThanEqualsByteString => {
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let arg2 = args[1].unwrap_byte_string(); let arg2 = args[1].unwrap_byte_string()?;
let value = Value::bool(arg1 <= arg2); let value = Value::bool(arg1 <= arg2);
@ -655,7 +488,7 @@ impl DefaultFunction {
DefaultFunction::Sha2_256 => { DefaultFunction::Sha2_256 => {
use cryptoxide::{digest::Digest, sha2::Sha256}; use cryptoxide::{digest::Digest, sha2::Sha256};
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
@ -672,7 +505,7 @@ impl DefaultFunction {
DefaultFunction::Sha3_256 => { DefaultFunction::Sha3_256 => {
use cryptoxide::{digest::Digest, sha3::Sha3_256}; use cryptoxide::{digest::Digest, sha3::Sha3_256};
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let mut hasher = Sha3_256::new(); let mut hasher = Sha3_256::new();
@ -690,7 +523,7 @@ impl DefaultFunction {
DefaultFunction::Blake2b_224 => { DefaultFunction::Blake2b_224 => {
use cryptoxide::{blake2b::Blake2b, digest::Digest}; use cryptoxide::{blake2b::Blake2b, digest::Digest};
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let mut digest = [0u8; 28]; let mut digest = [0u8; 28];
let mut context = Blake2b::new(28); let mut context = Blake2b::new(28);
@ -705,7 +538,7 @@ impl DefaultFunction {
DefaultFunction::Blake2b_256 => { DefaultFunction::Blake2b_256 => {
use cryptoxide::{blake2b::Blake2b, digest::Digest}; use cryptoxide::{blake2b::Blake2b, digest::Digest};
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let mut digest = [0u8; 32]; let mut digest = [0u8; 32];
let mut context = Blake2b::new(32); let mut context = Blake2b::new(32);
@ -720,7 +553,7 @@ impl DefaultFunction {
DefaultFunction::Keccak_256 => { DefaultFunction::Keccak_256 => {
use cryptoxide::{digest::Digest, sha3::Keccak256}; use cryptoxide::{digest::Digest, sha3::Keccak256};
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let mut hasher = Keccak256::new(); let mut hasher = Keccak256::new();
@ -737,9 +570,9 @@ impl DefaultFunction {
DefaultFunction::VerifyEd25519Signature => { DefaultFunction::VerifyEd25519Signature => {
use cryptoxide::ed25519; use cryptoxide::ed25519;
let public_key = args[0].unwrap_byte_string(); let public_key = args[0].unwrap_byte_string()?;
let message = args[1].unwrap_byte_string(); let message = args[1].unwrap_byte_string()?;
let signature = args[2].unwrap_byte_string(); let signature = args[2].unwrap_byte_string()?;
let public_key: [u8; 32] = public_key let public_key: [u8; 32] = public_key
.clone() .clone()
@ -758,37 +591,37 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::VerifyEcdsaSecp256k1Signature => { DefaultFunction::VerifyEcdsaSecp256k1Signature => {
let public_key = args[0].unwrap_byte_string(); let public_key = args[0].unwrap_byte_string()?;
let message = args[1].unwrap_byte_string(); let message = args[1].unwrap_byte_string()?;
let signature = args[2].unwrap_byte_string(); let signature = args[2].unwrap_byte_string()?;
verify_ecdsa(public_key, message, signature) verify_ecdsa(public_key, message, signature)
} }
DefaultFunction::VerifySchnorrSecp256k1Signature => { DefaultFunction::VerifySchnorrSecp256k1Signature => {
let public_key = args[0].unwrap_byte_string(); let public_key = args[0].unwrap_byte_string()?;
let message = args[1].unwrap_byte_string(); let message = args[1].unwrap_byte_string()?;
let signature = args[2].unwrap_byte_string(); let signature = args[2].unwrap_byte_string()?;
verify_schnorr(public_key, message, signature) verify_schnorr(public_key, message, signature)
} }
DefaultFunction::AppendString => { DefaultFunction::AppendString => {
let arg1 = args[0].unwrap_string(); let arg1 = args[0].unwrap_string()?;
let arg2 = args[1].unwrap_string(); let arg2 = args[1].unwrap_string()?;
let value = Value::string(format!("{arg1}{arg2}")); let value = Value::string(format!("{arg1}{arg2}"));
Ok(value) Ok(value)
} }
DefaultFunction::EqualsString => { DefaultFunction::EqualsString => {
let arg1 = args[0].unwrap_string(); let arg1 = args[0].unwrap_string()?;
let arg2 = args[1].unwrap_string(); let arg2 = args[1].unwrap_string()?;
let value = Value::bool(arg1 == arg2); let value = Value::bool(arg1 == arg2);
Ok(value) Ok(value)
} }
DefaultFunction::EncodeUtf8 => { DefaultFunction::EncodeUtf8 => {
let arg1 = args[0].unwrap_string(); let arg1 = args[0].unwrap_string()?;
let bytes = arg1.as_bytes().to_vec(); let bytes = arg1.as_bytes().to_vec();
@ -797,7 +630,7 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::DecodeUtf8 => { DefaultFunction::DecodeUtf8 => {
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let string = String::from_utf8(arg1.clone())?; let string = String::from_utf8(arg1.clone())?;
@ -806,7 +639,7 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::IfThenElse => { DefaultFunction::IfThenElse => {
let condition = args[0].unwrap_bool(); let condition = args[0].unwrap_bool()?;
if *condition { if *condition {
Ok(args[1].clone()) Ok(args[1].clone())
@ -815,35 +648,33 @@ impl DefaultFunction {
} }
} }
DefaultFunction::ChooseUnit => { DefaultFunction::ChooseUnit => {
// We don't need to check it here because this is already done in args[0].unwrap_unit()?;
// expect_type
// let Value::Con(Constant::Unit) = args[0] else {unreachable!()};
Ok(args[1].clone()) Ok(args[1].clone())
} }
DefaultFunction::Trace => { DefaultFunction::Trace => {
let arg1 = args[0].unwrap_string(); let arg1 = args[0].unwrap_string()?;
logs.push(arg1.clone()); logs.push(arg1.clone());
Ok(args[1].clone()) Ok(args[1].clone())
} }
DefaultFunction::FstPair => { DefaultFunction::FstPair => {
let (_, _, first, _) = args[0].unwrap_pair(); let (_, _, first, _) = args[0].unwrap_pair()?;
let value = Value::Con(first.clone()); let value = Value::Con(first.clone());
Ok(value) Ok(value)
} }
DefaultFunction::SndPair => { DefaultFunction::SndPair => {
let (_, _, _, second) = args[0].unwrap_pair(); let (_, _, _, second) = args[0].unwrap_pair()?;
let value = Value::Con(second.clone()); let value = Value::Con(second.clone());
Ok(value) Ok(value)
} }
DefaultFunction::ChooseList => { DefaultFunction::ChooseList => {
let (_, list) = args[0].unwrap_list(); let (_, list) = args[0].unwrap_list()?;
if list.is_empty() { if list.is_empty() {
Ok(args[1].clone()) Ok(args[1].clone())
@ -852,8 +683,12 @@ impl DefaultFunction {
} }
} }
DefaultFunction::MkCons => { DefaultFunction::MkCons => {
let item = args[0].unwrap_constant(); let item = args[0].unwrap_constant()?;
let (r#type, list) = args[1].unwrap_list(); let (r#type, list) = args[1].unwrap_list()?;
if *r#type != Type::from(item) {
return Err(Error::TypeMismatch(Type::from(item), r#type.clone()));
}
let mut ret = vec![item.clone()]; let mut ret = vec![item.clone()];
@ -864,15 +699,10 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::HeadList => { DefaultFunction::HeadList => {
let c @ Value::Con(inner) = &args[0] else { let (_, list) = args[0].unwrap_list()?;
unreachable!()
};
let Constant::ProtoList(_, list) = inner.as_ref() else {
unreachable!()
};
if list.is_empty() { if list.is_empty() {
Err(Error::EmptyList(c.clone())) Err(Error::EmptyList(args[0].clone()))
} else { } else {
let value = Value::Con(list[0].clone().into()); let value = Value::Con(list[0].clone().into());
@ -880,15 +710,10 @@ impl DefaultFunction {
} }
} }
DefaultFunction::TailList => { DefaultFunction::TailList => {
let c @ Value::Con(inner) = &args[0] else { let (r#type, list) = args[0].unwrap_list()?;
unreachable!()
};
let Constant::ProtoList(r#type, list) = inner.as_ref() else {
unreachable!()
};
if list.is_empty() { if list.is_empty() {
Err(Error::EmptyList(c.clone())) Err(Error::EmptyList(args[0].clone()))
} else { } else {
let value = Value::list(r#type.clone(), list[1..].to_vec()); let value = Value::list(r#type.clone(), list[1..].to_vec());
@ -896,27 +721,26 @@ impl DefaultFunction {
} }
} }
DefaultFunction::NullList => { DefaultFunction::NullList => {
let (_, list) = args[0].unwrap_list(); let (_, list) = args[0].unwrap_list()?;
let value = Value::bool(list.is_empty()); let value = Value::bool(list.is_empty());
Ok(value) Ok(value)
} }
DefaultFunction::ChooseData => { DefaultFunction::ChooseData => {
let con = args[0].unwrap_constant(); let con = args[0].unwrap_data()?;
match con { match con {
Constant::Data(PlutusData::Constr(_)) => Ok(args[1].clone()), PlutusData::Constr(_) => Ok(args[1].clone()),
Constant::Data(PlutusData::Map(_)) => Ok(args[2].clone()), PlutusData::Map(_) => Ok(args[2].clone()),
Constant::Data(PlutusData::Array(_)) => Ok(args[3].clone()), PlutusData::Array(_) => Ok(args[3].clone()),
Constant::Data(PlutusData::BigInt(_)) => Ok(args[4].clone()), PlutusData::BigInt(_) => Ok(args[4].clone()),
Constant::Data(PlutusData::BoundedBytes(_)) => Ok(args[5].clone()), PlutusData::BoundedBytes(_) => Ok(args[5].clone()),
_ => unreachable!(),
} }
} }
DefaultFunction::ConstrData => { DefaultFunction::ConstrData => {
let i = args[0].unwrap_integer(); let i = args[0].unwrap_integer()?;
let l = args[1].unwrap_data_list(); let l = args[1].unwrap_data_list()?;
let data_list: Vec<PlutusData> = l let data_list: Vec<PlutusData> = l
.iter() .iter()
@ -939,7 +763,17 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::MapData => { DefaultFunction::MapData => {
let (_, list) = args[0].unwrap_list(); let (r#type, list) = args[0].unwrap_list()?;
if *r#type != Type::Pair(Rc::new(Type::Data), Rc::new(Type::Data)) {
return Err(Error::TypeMismatch(
Type::List(Rc::new(Type::Pair(
Rc::new(Type::Data),
Rc::new(Type::Data),
))),
r#type.clone(),
));
}
let mut map = Vec::new(); let mut map = Vec::new();
@ -948,12 +782,13 @@ impl DefaultFunction {
unreachable!() unreachable!()
}; };
match (left.as_ref(), right.as_ref()) { let (Constant::Data(key), Constant::Data(value)) =
(Constant::Data(key), Constant::Data(value)) => { (left.as_ref(), right.as_ref())
map.push((key.clone(), value.clone())); else {
} unreachable!()
_ => unreachable!(), };
}
map.push((key.clone(), value.clone()));
} }
let value = Value::data(PlutusData::Map(map.into())); let value = Value::data(PlutusData::Map(map.into()));
@ -961,7 +796,7 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::ListData => { DefaultFunction::ListData => {
let (_, list) = args[0].unwrap_list(); let list = args[0].unwrap_data_list()?;
let data_list: Vec<PlutusData> = list let data_list: Vec<PlutusData> = list
.iter() .iter()
@ -976,14 +811,14 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::IData => { DefaultFunction::IData => {
let i = args[0].unwrap_integer(); let i = args[0].unwrap_integer()?;
let value = Value::data(PlutusData::BigInt(to_pallas_bigint(i))); let value = Value::data(PlutusData::BigInt(to_pallas_bigint(i)));
Ok(value) Ok(value)
} }
DefaultFunction::BData => { DefaultFunction::BData => {
let b = args[0].unwrap_byte_string(); let b = args[0].unwrap_byte_string()?;
let value = Value::data(PlutusData::BoundedBytes(b.clone().try_into().unwrap())); let value = Value::data(PlutusData::BoundedBytes(b.clone().try_into().unwrap()));
@ -1022,10 +857,7 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
v => Err(Error::DeserialisationError( v => Err(Error::NotAConstant(v.clone())),
"UnConstrData".to_string(),
v.clone(),
)),
}, },
DefaultFunction::UnMapData => match &args[0] { DefaultFunction::UnMapData => match &args[0] {
v @ Value::Con(inner) => { v @ Value::Con(inner) => {
@ -1055,10 +887,7 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
v => Err(Error::DeserialisationError( v => Err(Error::NotAConstant(v.clone())),
"UnMapData".to_string(),
v.clone(),
)),
}, },
DefaultFunction::UnListData => match &args[0] { DefaultFunction::UnListData => match &args[0] {
v @ Value::Con(inner) => { v @ Value::Con(inner) => {
@ -1079,10 +908,7 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
v => Err(Error::DeserialisationError( v => Err(Error::NotAConstant(v.clone())),
"UnListData".to_string(),
v.clone(),
)),
}, },
DefaultFunction::UnIData => match &args[0] { DefaultFunction::UnIData => match &args[0] {
v @ Value::Con(inner) => { v @ Value::Con(inner) => {
@ -1097,10 +923,7 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
v => Err(Error::DeserialisationError( v => Err(Error::NotAConstant(v.clone())),
"UnIData".to_string(),
v.clone(),
)),
}, },
DefaultFunction::UnBData => match &args[0] { DefaultFunction::UnBData => match &args[0] {
v @ Value::Con(inner) => { v @ Value::Con(inner) => {
@ -1115,34 +938,18 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
v => Err(Error::DeserialisationError( v => Err(Error::NotAConstant(v.clone())),
"UnBData".to_string(),
v.clone(),
)),
}, },
DefaultFunction::EqualsData => { DefaultFunction::EqualsData => {
let (Value::Con(inner1), Value::Con(inner2)) = (&args[0], &args[1]) else { let d1 = args[0].unwrap_data()?;
unreachable!() let d2 = args[1].unwrap_data()?;
};
let Constant::Data(d1) = inner1.as_ref() else {
unreachable!()
};
let Constant::Data(d2) = inner2.as_ref() else {
unreachable!()
};
let value = Value::bool(d1.eq(d2)); let value = Value::bool(d1.eq(d2));
Ok(value) Ok(value)
} }
DefaultFunction::SerialiseData => { DefaultFunction::SerialiseData => {
let Value::Con(inner) = &args[0] else { let d = args[0].unwrap_data()?;
unreachable!()
};
let Constant::Data(d) = inner.as_ref() else {
unreachable!()
};
let serialized_data = plutus_data_to_bytes(d).unwrap(); let serialized_data = plutus_data_to_bytes(d).unwrap();
@ -1151,16 +958,8 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::MkPairData => { DefaultFunction::MkPairData => {
let (Value::Con(inner1), Value::Con(inner2)) = (&args[0], &args[1]) else { let d1 = args[0].unwrap_data()?;
unreachable!() let d2 = args[1].unwrap_data()?;
};
let Constant::Data(d1) = inner1.as_ref() else {
unreachable!()
};
let Constant::Data(d2) = inner2.as_ref() else {
unreachable!()
};
let constant = Constant::ProtoPair( let constant = Constant::ProtoPair(
Type::Data, Type::Data,
@ -1174,11 +973,15 @@ impl DefaultFunction {
Ok(value) Ok(value)
} }
DefaultFunction::MkNilData => { DefaultFunction::MkNilData => {
args[0].unwrap_unit()?;
let value = Value::list(Type::Data, vec![]); let value = Value::list(Type::Data, vec![]);
Ok(value) Ok(value)
} }
DefaultFunction::MkNilPairData => { DefaultFunction::MkNilPairData => {
args[0].unwrap_unit()?;
let constant = Constant::ProtoList( let constant = Constant::ProtoList(
Type::Pair(Rc::new(Type::Data), Rc::new(Type::Data)), Type::Pair(Rc::new(Type::Data), Rc::new(Type::Data)),
vec![], vec![],
@ -1190,8 +993,8 @@ impl DefaultFunction {
} }
DefaultFunction::Bls12_381_G1_Add => { DefaultFunction::Bls12_381_G1_Add => {
let arg1 = args[0].unwrap_bls12_381_g1_element(); let arg1 = args[0].unwrap_bls12_381_g1_element()?;
let arg2 = args[1].unwrap_bls12_381_g1_element(); let arg2 = args[1].unwrap_bls12_381_g1_element()?;
let mut out = blst::blst_p1::default(); let mut out = blst::blst_p1::default();
@ -1208,7 +1011,7 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G1_Neg => { DefaultFunction::Bls12_381_G1_Neg => {
let arg1 = args[0].unwrap_bls12_381_g1_element(); let arg1 = args[0].unwrap_bls12_381_g1_element()?;
let mut out = *arg1; let mut out = *arg1;
@ -1225,8 +1028,8 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G1_ScalarMul => { DefaultFunction::Bls12_381_G1_ScalarMul => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_bls12_381_g1_element(); let arg2 = args[1].unwrap_bls12_381_g1_element()?;
let size_scalar = size_of::<blst::blst_scalar>(); let size_scalar = size_of::<blst::blst_scalar>();
@ -1266,8 +1069,8 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G1_Equal => { DefaultFunction::Bls12_381_G1_Equal => {
let arg1 = args[0].unwrap_bls12_381_g1_element(); let arg1 = args[0].unwrap_bls12_381_g1_element()?;
let arg2 = args[1].unwrap_bls12_381_g1_element(); let arg2 = args[1].unwrap_bls12_381_g1_element()?;
let is_equal = unsafe { blst::blst_p1_is_equal(arg1, arg2) }; let is_equal = unsafe { blst::blst_p1_is_equal(arg1, arg2) };
@ -1276,7 +1079,7 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G1_Compress => { DefaultFunction::Bls12_381_G1_Compress => {
let arg1 = args[0].unwrap_bls12_381_g1_element(); let arg1 = args[0].unwrap_bls12_381_g1_element()?;
let out = arg1.compress(); let out = arg1.compress();
@ -1285,7 +1088,7 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G1_Uncompress => { DefaultFunction::Bls12_381_G1_Uncompress => {
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let out = blst::blst_p1::uncompress(arg1)?; let out = blst::blst_p1::uncompress(arg1)?;
@ -1294,8 +1097,8 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G1_HashToGroup => { DefaultFunction::Bls12_381_G1_HashToGroup => {
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let arg2 = args[1].unwrap_byte_string(); let arg2 = args[1].unwrap_byte_string()?;
if arg2.len() > 255 { if arg2.len() > 255 {
return Err(Error::HashToCurveDstTooBig); return Err(Error::HashToCurveDstTooBig);
@ -1321,8 +1124,8 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G2_Add => { DefaultFunction::Bls12_381_G2_Add => {
let arg1 = args[0].unwrap_bls12_381_g2_element(); let arg1 = args[0].unwrap_bls12_381_g2_element()?;
let arg2 = args[1].unwrap_bls12_381_g2_element(); let arg2 = args[1].unwrap_bls12_381_g2_element()?;
let mut out = blst::blst_p2::default(); let mut out = blst::blst_p2::default();
@ -1339,7 +1142,7 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G2_Neg => { DefaultFunction::Bls12_381_G2_Neg => {
let arg1 = args[0].unwrap_bls12_381_g2_element(); let arg1 = args[0].unwrap_bls12_381_g2_element()?;
let mut out = *arg1; let mut out = *arg1;
@ -1356,8 +1159,8 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G2_ScalarMul => { DefaultFunction::Bls12_381_G2_ScalarMul => {
let arg1 = args[0].unwrap_integer(); let arg1 = args[0].unwrap_integer()?;
let arg2 = args[1].unwrap_bls12_381_g2_element(); let arg2 = args[1].unwrap_bls12_381_g2_element()?;
let size_scalar = size_of::<blst::blst_scalar>(); let size_scalar = size_of::<blst::blst_scalar>();
@ -1397,8 +1200,8 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G2_Equal => { DefaultFunction::Bls12_381_G2_Equal => {
let arg1 = args[0].unwrap_bls12_381_g2_element(); let arg1 = args[0].unwrap_bls12_381_g2_element()?;
let arg2 = args[1].unwrap_bls12_381_g2_element(); let arg2 = args[1].unwrap_bls12_381_g2_element()?;
let is_equal = unsafe { blst::blst_p2_is_equal(arg1, arg2) }; let is_equal = unsafe { blst::blst_p2_is_equal(arg1, arg2) };
@ -1407,7 +1210,7 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G2_Compress => { DefaultFunction::Bls12_381_G2_Compress => {
let arg1 = args[0].unwrap_bls12_381_g2_element(); let arg1 = args[0].unwrap_bls12_381_g2_element()?;
let out = arg1.compress(); let out = arg1.compress();
@ -1416,7 +1219,7 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G2_Uncompress => { DefaultFunction::Bls12_381_G2_Uncompress => {
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let out = blst::blst_p2::uncompress(arg1)?; let out = blst::blst_p2::uncompress(arg1)?;
@ -1425,8 +1228,8 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_G2_HashToGroup => { DefaultFunction::Bls12_381_G2_HashToGroup => {
let arg1 = args[0].unwrap_byte_string(); let arg1 = args[0].unwrap_byte_string()?;
let arg2 = args[1].unwrap_byte_string(); let arg2 = args[1].unwrap_byte_string()?;
if arg2.len() > 255 { if arg2.len() > 255 {
return Err(Error::HashToCurveDstTooBig); return Err(Error::HashToCurveDstTooBig);
@ -1452,8 +1255,8 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_MillerLoop => { DefaultFunction::Bls12_381_MillerLoop => {
let arg1 = args[0].unwrap_bls12_381_g1_element(); let arg1 = args[0].unwrap_bls12_381_g1_element()?;
let arg2 = args[1].unwrap_bls12_381_g2_element(); let arg2 = args[1].unwrap_bls12_381_g2_element()?;
let mut out = blst::blst_fp12::default(); let mut out = blst::blst_fp12::default();
@ -1472,8 +1275,8 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_MulMlResult => { DefaultFunction::Bls12_381_MulMlResult => {
let arg1 = args[0].unwrap_bls12_381_ml_result(); let arg1 = args[0].unwrap_bls12_381_ml_result()?;
let arg2 = args[1].unwrap_bls12_381_ml_result(); let arg2 = args[1].unwrap_bls12_381_ml_result()?;
let mut out = blst::blst_fp12::default(); let mut out = blst::blst_fp12::default();
@ -1486,8 +1289,8 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::Bls12_381_FinalVerify => { DefaultFunction::Bls12_381_FinalVerify => {
let arg1 = args[0].unwrap_bls12_381_ml_result(); let arg1 = args[0].unwrap_bls12_381_ml_result()?;
let arg2 = args[1].unwrap_bls12_381_ml_result(); let arg2 = args[1].unwrap_bls12_381_ml_result()?;
let verified = unsafe { blst::blst_fp12_finalverify(arg1, arg2) }; let verified = unsafe { blst::blst_fp12_finalverify(arg1, arg2) };

View File

@ -69,125 +69,138 @@ impl Value {
Value::Con(constant.into()) Value::Con(constant.into())
} }
pub(super) fn unwrap_integer(&self) -> &BigInt { pub(super) fn unwrap_integer(&self) -> Result<&BigInt, Error> {
let Value::Con(inner) = self else { let inner = self.unwrap_constant()?;
unreachable!()
}; let Constant::Integer(integer) = inner else {
let Constant::Integer(integer) = inner.as_ref() else { return Err(Error::TypeMismatch(Type::Integer, inner.into()));
unreachable!()
}; };
integer Ok(integer)
} }
pub(super) fn unwrap_byte_string(&self) -> &Vec<u8> { pub(super) fn unwrap_byte_string(&self) -> Result<&Vec<u8>, Error> {
let Value::Con(inner) = self else { let inner = self.unwrap_constant()?;
unreachable!()
}; let Constant::ByteString(byte_string) = inner else {
let Constant::ByteString(byte_string) = inner.as_ref() else { return Err(Error::TypeMismatch(Type::ByteString, inner.into()));
unreachable!()
}; };
byte_string Ok(byte_string)
} }
pub(super) fn unwrap_string(&self) -> &String { pub(super) fn unwrap_string(&self) -> Result<&String, Error> {
let Value::Con(inner) = self else { let inner = self.unwrap_constant()?;
unreachable!()
}; let Constant::String(string) = inner else {
let Constant::String(string) = inner.as_ref() else { return Err(Error::TypeMismatch(Type::String, inner.into()));
unreachable!()
}; };
string Ok(string)
} }
pub(super) fn unwrap_bool(&self) -> &bool { pub(super) fn unwrap_bool(&self) -> Result<&bool, Error> {
let Value::Con(inner) = self else { let inner = self.unwrap_constant()?;
unreachable!()
}; let Constant::Bool(condition) = inner else {
let Constant::Bool(condition) = inner.as_ref() else { return Err(Error::TypeMismatch(Type::Bool, inner.into()));
unreachable!()
}; };
condition Ok(condition)
} }
pub(super) fn unwrap_pair(&self) -> (&Type, &Type, &Rc<Constant>, &Rc<Constant>) { #[allow(clippy::type_complexity)]
let Value::Con(inner) = self else { pub(super) fn unwrap_pair(
unreachable!() &self,
}; ) -> Result<(&Type, &Type, &Rc<Constant>, &Rc<Constant>), Error> {
let Constant::ProtoPair(t1, t2, first, second) = inner.as_ref() else { let inner = self.unwrap_constant()?;
unreachable!()
let Constant::ProtoPair(t1, t2, first, second) = inner else {
return Err(Error::PairTypeMismatch(inner.into()));
}; };
(t1, t2, first, second) Ok((t1, t2, first, second))
} }
pub(super) fn unwrap_list(&self) -> (&Type, &Vec<Constant>) { pub(super) fn unwrap_list(&self) -> Result<(&Type, &Vec<Constant>), Error> {
let Value::Con(inner) = self else { let inner = self.unwrap_constant()?;
unreachable!()
}; let Constant::ProtoList(t, list) = inner else {
let Constant::ProtoList(t, list) = inner.as_ref() else { return Err(Error::ListTypeMismatch(inner.into()));
unreachable!()
}; };
(t, list) Ok((t, list))
} }
pub(super) fn unwrap_constant(&self) -> &Constant { pub(super) fn unwrap_data(&self) -> Result<&PlutusData, Error> {
let inner = self.unwrap_constant()?;
let Constant::Data(data) = inner else {
return Err(Error::TypeMismatch(Type::Data, inner.into()));
};
Ok(data)
}
pub(super) fn unwrap_unit(&self) -> Result<(), Error> {
let inner = self.unwrap_constant()?;
let Constant::Unit = inner else {
return Err(Error::TypeMismatch(Type::Unit, inner.into()));
};
Ok(())
}
pub(super) fn unwrap_constant(&self) -> Result<&Constant, Error> {
let Value::Con(item) = self else { let Value::Con(item) = self else {
unreachable!() return Err(Error::NotAConstant(self.clone()));
}; };
item.as_ref() Ok(item.as_ref())
} }
pub(super) fn unwrap_data_list(&self) -> &Vec<Constant> { pub(super) fn unwrap_data_list(&self) -> Result<&Vec<Constant>, Error> {
let Value::Con(inner) = self else { let inner = self.unwrap_constant()?;
unreachable!()
}; let Constant::ProtoList(Type::Data, list) = inner else {
let Constant::ProtoList(Type::Data, list) = inner.as_ref() else { return Err(Error::TypeMismatch(
unreachable!() Type::List(Type::Data.into()),
inner.into(),
));
}; };
list Ok(list)
} }
pub(super) fn unwrap_bls12_381_g1_element(&self) -> &blst::blst_p1 { pub(super) fn unwrap_bls12_381_g1_element(&self) -> Result<&blst::blst_p1, Error> {
let Value::Con(inner) = self else { let inner = self.unwrap_constant()?;
unreachable!()
let Constant::Bls12_381G1Element(element) = inner else {
return Err(Error::TypeMismatch(Type::Bls12_381G1Element, inner.into()));
}; };
let Constant::Bls12_381G1Element(element) = inner.as_ref() else { Ok(element)
unreachable!()
};
element
} }
pub(super) fn unwrap_bls12_381_g2_element(&self) -> &blst::blst_p2 { pub(super) fn unwrap_bls12_381_g2_element(&self) -> Result<&blst::blst_p2, Error> {
let Value::Con(inner) = self else { let inner = self.unwrap_constant()?;
unreachable!()
let Constant::Bls12_381G2Element(element) = inner else {
return Err(Error::TypeMismatch(Type::Bls12_381G2Element, inner.into()));
}; };
let Constant::Bls12_381G2Element(element) = inner.as_ref() else { Ok(element)
unreachable!()
};
element
} }
pub(super) fn unwrap_bls12_381_ml_result(&self) -> &blst::blst_fp12 { pub(super) fn unwrap_bls12_381_ml_result(&self) -> Result<&blst::blst_fp12, Error> {
let Value::Con(inner) = self else { let inner = self.unwrap_constant()?;
unreachable!()
let Constant::Bls12_381MlResult(element) = inner else {
return Err(Error::TypeMismatch(Type::Bls12_381MlResult, inner.into()));
}; };
let Constant::Bls12_381MlResult(element) = inner.as_ref() else { Ok(element)
unreachable!()
};
element
} }
pub fn is_integer(&self) -> bool { pub fn is_integer(&self) -> bool {

View File

@ -145,7 +145,7 @@ peg::parser! {
} }
rule case(interner: &mut Interner) -> Term<Name> rule case(interner: &mut Interner) -> Term<Name>
= "(" _* "case" _+ constr:term(interner) _* branches:(t:term(interner) _* { t })+ _* ")" { = "(" _* "case" _+ constr:term(interner) _* branches:(t:term(interner) _* { t })* _* ")" {
Term::Case { constr: constr.into(), branches } Term::Case { constr: constr.into(), branches }
} }

View File

@ -0,0 +1,7 @@
(program
1.0.0
[
[ (force (builtin ifThenElse)) (con string "11 <= 22") ]
(con string "\172(11 <= 22)")
]
)

View File

@ -0,0 +1,4 @@
(program 1.0.0 [
[ (force (builtin ifThenElse)) (con string "11 <= 22") ]
(con string "\172(11 <= 22)")
])

View File

@ -0,0 +1,4 @@
-- select first branch
(program 1.1.0
(case (constr 0 (con integer 0)) (lam x (con integer 1)) (lam x (con integer 2)))
)

View File

@ -0,0 +1 @@
(program 1.1.0 (con integer 1))

View File

@ -0,0 +1,4 @@
-- select second branch
(program 1.1.0
(case (constr 1 (con integer 0)) (lam x (con integer 1)) (lam x (con integer 2)))
)

View File

@ -0,0 +1 @@
(program 1.1.0 (con integer 2))

View File

@ -0,0 +1,4 @@
-- select first branch and do computation with the args
(program 1.1.0
(case (constr 0 (con integer 3) (con integer 2)) (lam x (lam y [(builtin addInteger) x y])) (lam x (lam y [(builtin subtractInteger) x y])))
)

View File

@ -0,0 +1 @@
(program 1.1.0 (con integer 5))

View File

@ -0,0 +1,4 @@
-- select second branch and do computation with the args
(program 1.1.0
(case (constr 1 (con integer 3) (con integer 2)) (lam x (lam y [(builtin addInteger) x y])) (lam x (lam y [(builtin subtractInteger) x y])))
)

View File

@ -0,0 +1 @@
(program 1.1.0 (con integer 1))

View File

@ -0,0 +1,4 @@
-- case of non-constr
(program 1.1.0
(case (con integer 1) (lam x x) (lam x x))
)

View File

@ -0,0 +1 @@
evaluation failure

View File

@ -1,4 +0,0 @@
-- case can't be used before 1.1.0
(program 1.0.0
(case (con integer 1))
)

View File

@ -0,0 +1,4 @@
-- nullary case
(program 1.1.0
(case (constr 0) (con integer 1) (con integer 2))
)

View File

@ -0,0 +1 @@
(program 1.1.0 (con integer 1))

View File

@ -0,0 +1,4 @@
-- empty case, aka -XEmptyCase
(program 1.1.0
(case (constr 0))
)

View File

@ -0,0 +1 @@
evaluation failure

View File

@ -0,0 +1,4 @@
-- empty constr
(program 1.1.0
(constr 0 )
)

View File

@ -0,0 +1 @@
(program 1.1.0 (constr 0))

View File

@ -0,0 +1,4 @@
-- constr with an argument
(program 1.1.0
(constr 0 (con integer 1))
)

View File

@ -0,0 +1 @@
(program 1.1.0 (constr 0 (con integer 1)))

View File

@ -0,0 +1,2 @@
-- ill-typed but does not fail at runtime because the builtin application is not saturated.
(program 1.0.0 [(builtin addInteger) (con unit ())])

View File

@ -0,0 +1 @@
(program 1.0.0 [ (builtin addInteger) (con unit ()) ])

View File

@ -119,12 +119,13 @@ fn check_mint_and_outputs(
when minted_assets is { when minted_assets is {
[] -> True [] -> True
[(minted_asset_name, quantity), ..rest_assets] -> { [(minted_asset_name, quantity), ..rest_assets] -> {
expect True = expect
list.any( list.any(
expected_assets, expected_assets,
fn(expected_asset) { expected_asset == minted_asset_name }, fn(expected_asset) { expected_asset == minted_asset_name },
) )
expect True =
expect
list.any( list.any(
outputs, outputs,
fn(output) { fn(output) {
@ -132,6 +133,7 @@ fn check_mint_and_outputs(
datum == InlineDatum(minted_asset_name) && address.payment_credential == validator_cred datum == InlineDatum(minted_asset_name) && address.payment_credential == validator_cred
}, },
) )
quantity == 1 && check_mint_and_outputs( quantity == 1 && check_mint_and_outputs(
rest_assets, rest_assets,
outputs, outputs,