mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-09-11 03:45:29 +03:00
[sc-692] Add simple readback of tuples
This commit is contained in:
parent
163ea65a1b
commit
100cc90aee
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -178,9 +178,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.11.0"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
|
||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
@ -278,9 +278,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.154"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
@ -423,9 +423,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.63"
|
||||
version = "2.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
|
||||
checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -69,6 +69,7 @@ pub struct Reader<'a> {
|
||||
dup_paths: Option<HashMap<u16, Vec<SlotId>>>,
|
||||
/// Store for floating/unscoped terms, like dups and let tups.
|
||||
scope: Scope,
|
||||
// To avoid reinserting things in the scope.
|
||||
seen_fans: Scope,
|
||||
seen: HashSet<Port>,
|
||||
errors: Vec<ReadbackError>,
|
||||
@ -79,7 +80,7 @@ impl Reader<'_> {
|
||||
use CtrKind::*;
|
||||
|
||||
maybe_grow(|| {
|
||||
if self.dup_paths.is_none() && !self.seen.insert(next) {
|
||||
if !self.seen.insert(next) && self.dup_paths.is_none() {
|
||||
self.error(ReadbackError::Cyclic);
|
||||
return Term::Var { nam: Name::new("...") };
|
||||
}
|
||||
@ -93,14 +94,22 @@ impl Reader<'_> {
|
||||
}
|
||||
// If we're visiting a con node...
|
||||
NodeKind::Ctr(CtrKind::Con(lab)) => match next.slot() {
|
||||
// If we're visiting a port 0, then it is a lambda.
|
||||
// If we're visiting a port 0, then it is a tuple or a lambda.
|
||||
0 => {
|
||||
let nam = self.namegen.decl_name(self.net, Port(node, 1));
|
||||
let bod = self.read_term(self.net.enter_port(Port(node, 2)));
|
||||
Term::Lam {
|
||||
tag: self.labels.con.to_tag(*lab),
|
||||
pat: Box::new(Pattern::Var(nam)),
|
||||
bod: Box::new(bod),
|
||||
if self.is_tup(node) {
|
||||
// A tuple
|
||||
let lft = self.read_term(self.net.enter_port(Port(node, 1)));
|
||||
let rgt = self.read_term(self.net.enter_port(Port(node, 2)));
|
||||
Term::Fan { fan: FanKind::Tup, tag: self.labels.con.to_tag(*lab), els: vec![lft, rgt] }
|
||||
} else {
|
||||
// A lambda
|
||||
let nam = self.namegen.decl_name(self.net, Port(node, 1));
|
||||
let bod = self.read_term(self.net.enter_port(Port(node, 2)));
|
||||
Term::Lam {
|
||||
tag: self.labels.con.to_tag(*lab),
|
||||
pat: Box::new(Pattern::Var(nam)),
|
||||
bod: Box::new(bod),
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we're visiting a port 1, then it is a variable.
|
||||
@ -332,8 +341,8 @@ impl Reader<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enters both ports 1 and 2 of a node,
|
||||
/// Returning a Term if is possible to simplify the net, or the Terms on the two ports of the node.
|
||||
/// Enters both ports 1 and 2 of a node. Returns a Term if it is
|
||||
/// possible to simplify the net, or the Terms on the two ports of the node.
|
||||
/// The two possible outcomes are always equivalent.
|
||||
///
|
||||
/// If:
|
||||
@ -404,6 +413,42 @@ impl Reader<'_> {
|
||||
diagnostics.add_diagnostic(msg.as_str(), Severity::Warning, DiagnosticOrigin::Readback);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether the given port represents a tuple or some other
|
||||
/// term (usually a lambda).
|
||||
///
|
||||
/// Used heuristic: a con node is a tuple if port 1 is a closed net and not an ERA.
|
||||
fn is_tup(&self, node: NodeId) -> bool {
|
||||
if !matches!(self.net.node(node).kind, NodeKind::Ctr(CtrKind::Con(_))) {
|
||||
return false;
|
||||
}
|
||||
if self.net.node(self.net.enter_port(Port(node, 1)).node()).kind == NodeKind::Era {
|
||||
return false;
|
||||
}
|
||||
let mut wires = HashSet::new();
|
||||
let mut to_check = vec![self.net.enter_port(Port(node, 1))];
|
||||
while let Some(port) = to_check.pop() {
|
||||
match port.slot() {
|
||||
0 => {
|
||||
let node = port.node();
|
||||
let lft = self.net.enter_port(Port(node, 1));
|
||||
let rgt = self.net.enter_port(Port(node, 2));
|
||||
to_check.push(lft);
|
||||
to_check.push(rgt);
|
||||
}
|
||||
1 | 2 => {
|
||||
// Mark as a wire. If already present, mark as visited by removing it.
|
||||
if !(wires.insert(port) && wires.insert(self.net.enter_port(port))) {
|
||||
wires.remove(&port);
|
||||
wires.remove(&self.net.enter_port(port));
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
// No hanging wires = a combinator = a tuple
|
||||
wires.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// Argument for a Opr node
|
||||
|
4
tests/golden_tests/cli/tuple_readback.args
Normal file
4
tests/golden_tests/cli/tuple_readback.args
Normal file
@ -0,0 +1,4 @@
|
||||
run
|
||||
tests/golden_tests/cli/tuple_readback.bend
|
||||
-Ono-float-combinators
|
||||
-Aall
|
9
tests/golden_tests/cli/tuple_readback.bend
Normal file
9
tests/golden_tests/cli/tuple_readback.bend
Normal file
@ -0,0 +1,9 @@
|
||||
# We probably will not be able to handle all these
|
||||
main = (
|
||||
@a (1, a),
|
||||
(1, 2),
|
||||
(*, 2),
|
||||
($x, @$x *),
|
||||
((1, @$y $z), (2, @$z $y)),
|
||||
((@$a $b, @$b $c), (@$c $d, @$d $a))
|
||||
)
|
@ -7,6 +7,9 @@ String/from_list (List/Cons x xs) = (String/Cons x (String/from_list xs))
|
||||
(Join List/Nil) = ""
|
||||
(Join (List/Cons x xs)) = (Concat x (Join xs))
|
||||
|
||||
(Expand xs) = fold xs { String/Nil: xs; String/Cons: xs }
|
||||
|
||||
main =
|
||||
((String/from_list ['\n', '\r', '\t', '\0', '\"', '\'', '\u{AFE}', '\\'])
|
||||
, (Join ["\n", "\r", "\t", "\0", "\"", "\'", "\u{AFE}", "\\"]))
|
||||
let a = (String/from_list ['\n', '\r', '\t', '\0', '\"', '\'', '\u{AFE}', '\\'])
|
||||
let b = (Join ["\n", "\r", "\t", "\0", "\"", "\'", "\u{AFE}", "\\"])
|
||||
(Expand (Concat a b))
|
||||
|
6
tests/snapshots/cli__tuple_readback.bend.snap
Normal file
6
tests/snapshots/cli__tuple_readback.bend.snap
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/cli/tuple_readback.bend
|
||||
---
|
||||
Warning: Running in strict mode without enabling the float_combinators pass can lead to some functions expanding infinitely.
|
||||
Result: (λa (1, a), ((1, 2), (λ* 2, (λb λc *, (λd (2, λe $f), λg λh λi $j)))))
|
@ -2,4 +2,4 @@
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/escape_sequences.bend
|
||||
---
|
||||
λa (Join ["\n", "\r", "\t", "\0", "\"", "'", "\u{afe}", "\\"])
|
||||
"\n\r\t\0\"'\u{afe}\\\n\r\t\0\"'\u{afe}\\"
|
||||
|
@ -2,4 +2,4 @@
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/nested_str.bend
|
||||
---
|
||||
λa λb λc λd λ* (d "ab" λg λ* (g "cd" ""))
|
||||
((String/Cons "a" ""), (λa λ* (a 97 λb λ* (b "bc" "")), (λe λ* (e "ab" "c"), λi λ* (i "ab" λl λ* (l "cd" "")))))
|
||||
|
Loading…
Reference in New Issue
Block a user