mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-09-11 11:56:54 +03:00
Merge pull request #563 from HigherOrderCO/467-readback-of-numeric-operations-and-switches-is-broken
#467 Fix readback of numeric operations
This commit is contained in:
commit
8b31fad7a3
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project does not adhere to a particular versioning scheme at this point.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed readback of numeric operations. ([#467][gh-467])
|
||||
|
||||
## [0.2.35] - 2024-06-06
|
||||
|
||||
### Changed
|
||||
@ -84,6 +90,7 @@ and this project does not adhere to a particular versioning scheme at this point
|
||||
[gh-451]: https://github.com/HigherOrderCO/Bend/issues/451
|
||||
[gh-465]: https://github.com/HigherOrderCO/Bend/issues/465
|
||||
[gh-466]: https://github.com/HigherOrderCO/Bend/issues/466
|
||||
[gh-467]: https://github.com/HigherOrderCO/Bend/issues/467
|
||||
[gh-479]: https://github.com/HigherOrderCO/Bend/issues/479
|
||||
[gh-502]: https://github.com/HigherOrderCO/Bend/issues/502
|
||||
[gh-526]: https://github.com/HigherOrderCO/Bend/issues/526
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
diagnostics::{DiagnosticOrigin, Diagnostics, Severity},
|
||||
fun::{term_to_net::Labels, Book, FanKind, Name, Num, Op, Pattern, Tag, Term},
|
||||
maybe_grow,
|
||||
net::{CtrKind, INet, NodeId, NodeKind, Port, SlotId, ROOT},
|
||||
net::{BendLab, CtrKind, INet, NodeId, NodeKind, Port, SlotId, ROOT},
|
||||
};
|
||||
use hvm::hvm::Numb;
|
||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
@ -86,28 +86,41 @@ impl Reader<'_> {
|
||||
return Term::Var { nam: Name::new("...") };
|
||||
}
|
||||
|
||||
let node = next.node();
|
||||
let node = next.node_id();
|
||||
match &self.net.node(node).kind {
|
||||
NodeKind::Era => {
|
||||
// Only the main port actually exists in an ERA, the aux ports are just an artifact of this representation.
|
||||
debug_assert!(next.slot() == 0);
|
||||
Term::Era
|
||||
NodeKind::Era => Term::Era,
|
||||
NodeKind::Ctr(CtrKind::Con(lab)) => self.read_con(next, *lab),
|
||||
NodeKind::Swi => self.read_swi(next),
|
||||
NodeKind::Ref { def_name } => Term::Ref { nam: def_name.clone() },
|
||||
NodeKind::Ctr(kind @ (Dup(_) | Tup(_))) => self.read_fan(next, *kind),
|
||||
NodeKind::Num { val } => num_from_bits_with_type(*val, *val),
|
||||
NodeKind::Opr => self.read_opr(next),
|
||||
NodeKind::Rot => {
|
||||
self.error(ReadbackError::ReachedRoot);
|
||||
Term::Err
|
||||
}
|
||||
// If we're visiting a con node...
|
||||
NodeKind::Ctr(CtrKind::Con(lab)) => match next.slot() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads a term from a CON node.
|
||||
/// Could be a lambda, an application, a CON tuple or a CON tuple elimination.
|
||||
fn read_con(&mut self, next: Port, label: Option<BendLab>) -> Term {
|
||||
let node = next.node_id();
|
||||
match next.slot() {
|
||||
// If we're visiting a port 0, then it is a tuple or a lambda.
|
||||
0 => {
|
||||
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] }
|
||||
Term::Fan { fan: FanKind::Tup, tag: self.labels.con.to_tag(label), 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),
|
||||
tag: self.labels.con.to_tag(label),
|
||||
pat: Box::new(Pattern::Var(nam)),
|
||||
bod: Box::new(bod),
|
||||
}
|
||||
@ -119,72 +132,19 @@ impl Reader<'_> {
|
||||
2 => {
|
||||
let fun = self.read_term(self.net.enter_port(Port(node, 0)));
|
||||
let arg = self.read_term(self.net.enter_port(Port(node, 1)));
|
||||
Term::App { tag: self.labels.con.to_tag(*lab), fun: Box::new(fun), arg: Box::new(arg) }
|
||||
Term::App { tag: self.labels.con.to_tag(label), fun: Box::new(fun), arg: Box::new(arg) }
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
NodeKind::Mat => match next.slot() {
|
||||
2 => {
|
||||
// Read the matched expression
|
||||
let arg = self.read_term(self.net.enter_port(Port(node, 0)));
|
||||
let bnd = if let Term::Var { nam } = &arg { nam.clone() } else { self.namegen.unique() };
|
||||
|
||||
// Read the pattern matching node
|
||||
let sel_node = self.net.enter_port(Port(node, 1)).node();
|
||||
|
||||
// We expect the pattern matching node to be a CON
|
||||
let sel_kind = &self.net.node(sel_node).kind;
|
||||
if sel_kind != &NodeKind::Ctr(CtrKind::Con(None)) {
|
||||
// TODO: Is there any case where we expect a different node type here on readback?
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
return Term::Err;
|
||||
}
|
||||
}
|
||||
|
||||
let zero_term = self.read_term(self.net.enter_port(Port(sel_node, 1)));
|
||||
let mut succ_term = self.read_term(self.net.enter_port(Port(sel_node, 2)));
|
||||
// Call expand_generated in case of succ_term be a lifted term
|
||||
succ_term.expand_generated(self.book, self.recursive_defs);
|
||||
|
||||
// Succ term should be a lambda
|
||||
let (zero, succ) = match &mut succ_term {
|
||||
Term::Lam { pat, bod, .. } => {
|
||||
if let Pattern::Var(nam) = pat.as_ref() {
|
||||
let mut bod = std::mem::take(bod.as_mut());
|
||||
if let Some(nam) = nam {
|
||||
bod.subst(nam, &Term::Var { nam: Name::new(format!("{bnd}-1")) });
|
||||
}
|
||||
(zero_term, bod)
|
||||
} else {
|
||||
// Readback should never generate non-var patterns for lambdas.
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
(zero_term, succ_term)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
(zero_term, succ_term)
|
||||
}
|
||||
};
|
||||
Term::Swt {
|
||||
arg: Box::new(arg),
|
||||
bnd: Some(bnd),
|
||||
with_arg: vec![],
|
||||
with_bnd: vec![],
|
||||
pred: None,
|
||||
arms: vec![zero, succ],
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
Term::Err
|
||||
}
|
||||
},
|
||||
NodeKind::Ref { def_name } => Term::Ref { nam: def_name.clone() },
|
||||
// If we're visiting a fan node...
|
||||
NodeKind::Ctr(kind @ (Dup(_) | Tup(_))) => {
|
||||
let (fan, lab) = match *kind {
|
||||
Tup(lab) => (FanKind::Tup, lab),
|
||||
Dup(lab) => (FanKind::Dup, Some(lab)),
|
||||
/// Reads a fan term from a DUP node.
|
||||
/// Could be a superposition, a duplication, a DUP tuple or a DUP tuple elimination.
|
||||
fn read_fan(&mut self, next: Port, kind: CtrKind) -> Term {
|
||||
let node = next.node_id();
|
||||
let (fan, lab) = match kind {
|
||||
CtrKind::Tup(lab) => (FanKind::Tup, lab),
|
||||
CtrKind::Dup(lab) => (FanKind::Dup, Some(lab)),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
match next.slot() {
|
||||
@ -246,52 +206,279 @@ impl Reader<'_> {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
NodeKind::Num { val } => num_from_bits_with_type(*val, *val),
|
||||
NodeKind::Opr => match next.slot() {
|
||||
2 => {
|
||||
let port0_node = self.net.enter_port(Port(node, 0)).node();
|
||||
let port0_kind = self.net.node(port0_node).kind.clone();
|
||||
// two oper in a row
|
||||
if port0_kind == NodeKind::Opr {
|
||||
// TODO: allow for nested oper
|
||||
let opr_node = self.net.enter_port(Port(port0_node, 0)).node();
|
||||
let opr_kind = self.net.node(opr_node).kind.clone();
|
||||
let opr = if let NodeKind::Num { val } = opr_kind {
|
||||
let typ = hvm::hvm::Numb::get_typ(&Numb(val));
|
||||
if typ != hvm::hvm::TY_SYM {
|
||||
self.error(ReadbackError::InvalidNumericOp);
|
||||
return Term::Err;
|
||||
|
||||
/// Reads an Opr term from an OPR node.
|
||||
fn read_opr(&mut self, next: Port) -> Term {
|
||||
/// Read one of the argument ports of an operation.
|
||||
fn add_arg(
|
||||
reader: &mut Reader,
|
||||
port: Port,
|
||||
args: &mut Vec<Result<hvm::hvm::Val, Term>>,
|
||||
types: &mut Vec<hvm::hvm::Tag>,
|
||||
ops: &mut Vec<hvm::hvm::Tag>,
|
||||
) {
|
||||
if let NodeKind::Num { val } = reader.net.node(port.node_id()).kind {
|
||||
match hvm::hvm::Numb::get_typ(&Numb(val)) {
|
||||
// Contains an operation
|
||||
hvm::hvm::TY_SYM => {
|
||||
ops.push(hvm::hvm::Numb(val).get_sym());
|
||||
}
|
||||
// Contains a number with a type
|
||||
typ @ hvm::hvm::TY_U24..=hvm::hvm::TY_F24 => {
|
||||
types.push(typ);
|
||||
args.push(Ok(val));
|
||||
}
|
||||
// Contains a partially applied number with operation and no type
|
||||
op @ hvm::hvm::OP_ADD.. => {
|
||||
ops.push(op);
|
||||
args.push(Ok(val));
|
||||
}
|
||||
if let Some(op) = Op::from_native_tag(typ, NumType::U24) {
|
||||
op
|
||||
} else {
|
||||
self.error(ReadbackError::InvalidNumericOp);
|
||||
return Term::Err;
|
||||
}
|
||||
} else {
|
||||
self.error(ReadbackError::InvalidNumericOp);
|
||||
// Some other non-number argument
|
||||
let term = reader.read_term(port);
|
||||
args.push(Err(term));
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an Opr term from the arguments of the subnet of an OPR node.
|
||||
fn opr_term_from_hvm_args(
|
||||
args: &mut Vec<Result<hvm::hvm::Val, Term>>,
|
||||
types: &mut Vec<hvm::hvm::Tag>,
|
||||
ops: &mut Vec<hvm::hvm::Tag>,
|
||||
is_flipped: bool,
|
||||
) -> Term {
|
||||
let typ = match types.as_slice() {
|
||||
[typ] => *typ,
|
||||
// Use U24 as default number type
|
||||
[] => hvm::hvm::TY_U24,
|
||||
_ => {
|
||||
// Too many types
|
||||
return Term::Err;
|
||||
}
|
||||
};
|
||||
match (args.as_slice(), ops.as_slice()) {
|
||||
([arg1, arg2], [op]) => {
|
||||
// Correct number of arguments
|
||||
let arg1 = match arg1 {
|
||||
Ok(val) => num_from_bits_with_type(*val, typ as u32),
|
||||
Err(val) => val.clone(),
|
||||
};
|
||||
let arg2 = match arg2 {
|
||||
Ok(val) => num_from_bits_with_type(*val, typ as u32),
|
||||
Err(val) => val.clone(),
|
||||
};
|
||||
let (arg1, arg2) = if is_flipped ^ op_is_flipped(*op) { (arg2, arg1) } else { (arg1, arg2) };
|
||||
let Some(op) = op_from_native_tag(*op, typ) else {
|
||||
// Invalid operator
|
||||
return Term::Err;
|
||||
};
|
||||
let fst = self.read_term(self.net.enter_port(Port(port0_node, 1)));
|
||||
let snd = self.read_term(self.net.enter_port(Port(node, 1)));
|
||||
Term::Oper { opr, fst: Box::new(fst), snd: Box::new(snd) }
|
||||
Term::Oper { opr: op, fst: Box::new(arg1), snd: Box::new(arg2) }
|
||||
}
|
||||
_ => {
|
||||
// Invalid number of arguments/types/operators
|
||||
Term::Err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn op_is_flipped(op: hvm::hvm::Tag) -> bool {
|
||||
[hvm::hvm::FP_DIV, hvm::hvm::FP_REM, hvm::hvm::FP_SHL, hvm::hvm::FP_SHR, hvm::hvm::FP_SUB].contains(&op)
|
||||
}
|
||||
|
||||
fn op_from_native_tag(val: hvm::hvm::Tag, typ: hvm::hvm::Tag) -> Option<Op> {
|
||||
let op = match val {
|
||||
hvm::hvm::OP_ADD => Op::ADD,
|
||||
hvm::hvm::OP_SUB => Op::SUB,
|
||||
hvm::hvm::FP_SUB => Op::SUB,
|
||||
hvm::hvm::OP_MUL => Op::MUL,
|
||||
hvm::hvm::OP_DIV => Op::DIV,
|
||||
hvm::hvm::FP_DIV => Op::DIV,
|
||||
hvm::hvm::OP_REM => Op::REM,
|
||||
hvm::hvm::FP_REM => Op::REM,
|
||||
hvm::hvm::OP_EQ => Op::EQ,
|
||||
hvm::hvm::OP_NEQ => Op::NEQ,
|
||||
hvm::hvm::OP_LT => Op::LT,
|
||||
hvm::hvm::OP_GT => Op::GT,
|
||||
hvm::hvm::OP_AND => {
|
||||
if typ == hvm::hvm::TY_F24 {
|
||||
Op::ATN
|
||||
} else {
|
||||
// TODO: Fix
|
||||
Op::AND
|
||||
}
|
||||
}
|
||||
hvm::hvm::OP_OR => {
|
||||
if typ == hvm::hvm::TY_F24 {
|
||||
Op::LOG
|
||||
} else {
|
||||
Op::OR
|
||||
}
|
||||
}
|
||||
hvm::hvm::OP_XOR => {
|
||||
if typ == hvm::hvm::TY_F24 {
|
||||
Op::POW
|
||||
} else {
|
||||
Op::XOR
|
||||
}
|
||||
}
|
||||
hvm::hvm::OP_SHL => Op::SHL,
|
||||
hvm::hvm::FP_SHL => Op::SHL,
|
||||
hvm::hvm::OP_SHR => Op::SHR,
|
||||
hvm::hvm::FP_SHR => Op::SHR,
|
||||
_ => return None,
|
||||
};
|
||||
Some(op)
|
||||
}
|
||||
|
||||
let node = next.node_id();
|
||||
match next.slot() {
|
||||
2 => {
|
||||
// If port1 has a partially applied number, the operation has 1 node.
|
||||
// Port0 has arg1 and port1 has arg2.
|
||||
// The operation is interpreted as being pre-flipped (if its a FP_, they cancel and don't flip).
|
||||
let port1_kind = self.net.node(self.net.enter_port(Port(node, 1)).node_id()).kind.clone();
|
||||
if let NodeKind::Num { val } = port1_kind {
|
||||
match hvm::hvm::Numb::get_typ(&Numb(val)) {
|
||||
hvm::hvm::OP_ADD.. => {
|
||||
let x1_port = self.net.enter_port(Port(node, 0));
|
||||
let x2_port = self.net.enter_port(Port(node, 1));
|
||||
let mut args = vec![];
|
||||
let mut types = vec![];
|
||||
let mut ops = vec![];
|
||||
add_arg(self, x1_port, &mut args, &mut types, &mut ops);
|
||||
add_arg(self, x2_port, &mut args, &mut types, &mut ops);
|
||||
let term = opr_term_from_hvm_args(&mut args, &mut types, &mut ops, true);
|
||||
if let Term::Err = term {
|
||||
// Since that function doesn't have access to the reader, add the error here.
|
||||
self.error(ReadbackError::InvalidNumericOp);
|
||||
}
|
||||
return term;
|
||||
}
|
||||
_ => {
|
||||
// Not a partially applied number, handle it in the next case
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If port0 has a partially applied number, it also has 1 node.
|
||||
// The operation is interpreted as not pre-flipped.
|
||||
let port0_kind = self.net.node(self.net.enter_port(Port(node, 0)).node_id()).kind.clone();
|
||||
if let NodeKind::Num { val } = port0_kind {
|
||||
match hvm::hvm::Numb::get_typ(&Numb(val)) {
|
||||
hvm::hvm::OP_ADD.. => {
|
||||
let x1_port = self.net.enter_port(Port(node, 0));
|
||||
let x2_port = self.net.enter_port(Port(node, 1));
|
||||
let mut args = vec![];
|
||||
let mut types = vec![];
|
||||
let mut ops = vec![];
|
||||
add_arg(self, x1_port, &mut args, &mut types, &mut ops);
|
||||
add_arg(self, x2_port, &mut args, &mut types, &mut ops);
|
||||
let term = opr_term_from_hvm_args(&mut args, &mut types, &mut ops, false);
|
||||
if let Term::Err = term {
|
||||
// Since that function doesn't have access to the reader, add the error here.
|
||||
self.error(ReadbackError::InvalidNumericOp);
|
||||
}
|
||||
return term;
|
||||
}
|
||||
_ => {
|
||||
// Not a partially applied number, handle it in the next case
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, the operation has 2 nodes.
|
||||
// Read the top node port0 and 1, bottom node port1.
|
||||
// Args are in that order, skipping the operation.
|
||||
let bottom_id = node;
|
||||
let top_id = self.net.enter_port(Port(bottom_id, 0)).node_id();
|
||||
if let NodeKind::Opr = self.net.node(top_id).kind {
|
||||
let x1_port = self.net.enter_port(Port(top_id, 0));
|
||||
let x2_port = self.net.enter_port(Port(top_id, 1));
|
||||
let x3_port = self.net.enter_port(Port(bottom_id, 1));
|
||||
let mut args = vec![];
|
||||
let mut types = vec![];
|
||||
let mut ops = vec![];
|
||||
add_arg(self, x1_port, &mut args, &mut types, &mut ops);
|
||||
add_arg(self, x2_port, &mut args, &mut types, &mut ops);
|
||||
add_arg(self, x3_port, &mut args, &mut types, &mut ops);
|
||||
let term = opr_term_from_hvm_args(&mut args, &mut types, &mut ops, false);
|
||||
if let Term::Err = term {
|
||||
self.error(ReadbackError::InvalidNumericOp);
|
||||
}
|
||||
term
|
||||
} else {
|
||||
// Port 0 was not an OPR node, invalid.
|
||||
self.error(ReadbackError::InvalidNumericOp);
|
||||
Term::Err
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Entered from a port other than 2, invalid.
|
||||
self.error(ReadbackError::InvalidNumericOp);
|
||||
Term::Err
|
||||
}
|
||||
},
|
||||
NodeKind::Rot => {
|
||||
self.error(ReadbackError::ReachedRoot);
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a switch term from a SWI node.
|
||||
fn read_swi(&mut self, next: Port) -> Term {
|
||||
let node = next.node_id();
|
||||
match next.slot() {
|
||||
2 => {
|
||||
// Read the matched expression
|
||||
let arg = self.read_term(self.net.enter_port(Port(node, 0)));
|
||||
let bnd = if let Term::Var { nam } = &arg { nam.clone() } else { self.namegen.unique() };
|
||||
|
||||
// Read the pattern matching node
|
||||
let sel_node = self.net.enter_port(Port(node, 1)).node_id();
|
||||
|
||||
// We expect the pattern matching node to be a CON
|
||||
let sel_kind = &self.net.node(sel_node).kind;
|
||||
if sel_kind != &NodeKind::Ctr(CtrKind::Con(None)) {
|
||||
// TODO: Is there any case where we expect a different node type here on readback?
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
return Term::Err;
|
||||
}
|
||||
|
||||
let zero_term = self.read_term(self.net.enter_port(Port(sel_node, 1)));
|
||||
let mut succ_term = self.read_term(self.net.enter_port(Port(sel_node, 2)));
|
||||
// Call expand_generated in case of succ_term be a lifted term
|
||||
succ_term.expand_generated(self.book, self.recursive_defs);
|
||||
|
||||
// Succ term should be a lambda
|
||||
let (zero, succ) = match &mut succ_term {
|
||||
Term::Lam { pat, bod, .. } => {
|
||||
if let Pattern::Var(nam) = pat.as_ref() {
|
||||
let mut bod = std::mem::take(bod.as_mut());
|
||||
if let Some(nam) = nam {
|
||||
bod.subst(nam, &Term::Var { nam: Name::new(format!("{bnd}-1")) });
|
||||
}
|
||||
(zero_term, bod)
|
||||
} else {
|
||||
// Readback should never generate non-var patterns for lambdas.
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
(zero_term, succ_term)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
(zero_term, succ_term)
|
||||
}
|
||||
};
|
||||
Term::Swt {
|
||||
arg: Box::new(arg),
|
||||
bnd: Some(bnd),
|
||||
with_arg: vec![],
|
||||
with_bnd: vec![],
|
||||
pred: None,
|
||||
arms: vec![zero, succ],
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
Term::Err
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Enters both ports 1 and 2 of a node. Returns a Term if it is
|
||||
@ -375,7 +562,7 @@ impl Reader<'_> {
|
||||
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 {
|
||||
if self.net.node(self.net.enter_port(Port(node, 1)).node_id()).kind == NodeKind::Era {
|
||||
return false;
|
||||
}
|
||||
let mut wires = HashSet::new();
|
||||
@ -383,7 +570,7 @@ impl Reader<'_> {
|
||||
while let Some(port) = to_check.pop() {
|
||||
match port.slot() {
|
||||
0 => {
|
||||
let node = port.node();
|
||||
let node = port.node_id();
|
||||
let lft = self.net.enter_port(Port(node, 1));
|
||||
let rgt = self.net.enter_port(Port(node, 2));
|
||||
to_check.push(lft);
|
||||
@ -404,49 +591,17 @@ impl Reader<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum NumType {
|
||||
U24 = 1,
|
||||
_I24 = 2,
|
||||
F24 = 3,
|
||||
}
|
||||
/* Utils for numbers and numeric operations */
|
||||
|
||||
impl Op {
|
||||
fn from_native_tag(val: hvm::hvm::Tag, typ: NumType) -> Option<Op> {
|
||||
let op = match val {
|
||||
hvm::hvm::OP_ADD => Op::ADD,
|
||||
hvm::hvm::OP_SUB => Op::SUB,
|
||||
hvm::hvm::OP_MUL => Op::MUL,
|
||||
hvm::hvm::OP_DIV => Op::DIV,
|
||||
hvm::hvm::OP_REM => Op::REM,
|
||||
hvm::hvm::OP_EQ => Op::EQ,
|
||||
hvm::hvm::OP_NEQ => Op::NEQ,
|
||||
hvm::hvm::OP_LT => Op::LT,
|
||||
hvm::hvm::OP_GT => Op::GT,
|
||||
hvm::hvm::OP_AND => {
|
||||
if typ == NumType::F24 {
|
||||
Op::ATN
|
||||
} else {
|
||||
Op::AND
|
||||
}
|
||||
}
|
||||
hvm::hvm::OP_OR => {
|
||||
if typ == NumType::F24 {
|
||||
Op::LOG
|
||||
} else {
|
||||
Op::OR
|
||||
}
|
||||
}
|
||||
hvm::hvm::OP_XOR => {
|
||||
if typ == NumType::F24 {
|
||||
Op::POW
|
||||
} else {
|
||||
Op::XOR
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
Some(op)
|
||||
/// From an hvm number carrying the value and another carrying the type, return a Num term.
|
||||
fn num_from_bits_with_type(val: u32, typ: u32) -> Term {
|
||||
match hvm::hvm::Numb::get_typ(&Numb(typ)) {
|
||||
// No type information, assume u24 by default
|
||||
hvm::hvm::TY_SYM => Term::Num { val: Num::U24(Numb::get_u24(&Numb(val))) },
|
||||
hvm::hvm::TY_U24 => Term::Num { val: Num::U24(Numb::get_u24(&Numb(val))) },
|
||||
hvm::hvm::TY_I24 => Term::Num { val: Num::I24(Numb::get_i24(&Numb(val))) },
|
||||
hvm::hvm::TY_F24 => Term::Num { val: Num::F24(Numb::get_f24(&Numb(val))) },
|
||||
_ => Term::Err,
|
||||
}
|
||||
}
|
||||
|
||||
@ -510,17 +665,6 @@ impl Term {
|
||||
}
|
||||
}
|
||||
|
||||
fn num_from_bits_with_type(val: u32, typ: u32) -> Term {
|
||||
match hvm::hvm::Numb::get_typ(&Numb(typ)) {
|
||||
// No type information, assume u24 by default
|
||||
hvm::hvm::TY_SYM => Term::Num { val: Num::U24(Numb::get_u24(&Numb(val))) },
|
||||
hvm::hvm::TY_U24 => Term::Num { val: Num::U24(Numb::get_u24(&Numb(val))) },
|
||||
hvm::hvm::TY_I24 => Term::Num { val: Num::I24(Numb::get_i24(&Numb(val))) },
|
||||
hvm::hvm::TY_F24 => Term::Num { val: Num::F24(Numb::get_f24(&Numb(val))) },
|
||||
_ => Term::Err,
|
||||
}
|
||||
}
|
||||
|
||||
/* Variable name generation */
|
||||
|
||||
#[derive(Default)]
|
||||
@ -543,7 +687,7 @@ impl NameGen {
|
||||
fn decl_name(&mut self, net: &INet, var_port: Port) -> Option<Name> {
|
||||
// If port is linked to an erase node, return an unused variable
|
||||
let var_use = net.enter_port(var_port);
|
||||
let var_kind = &net.node(var_use.node()).kind;
|
||||
let var_kind = &net.node(var_use.node_id()).kind;
|
||||
(*var_kind != NodeKind::Era).then(|| self.var_name(var_port))
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ fn tree_to_inodes(tree: &Tree, tree_root: String, net_root: &str, n_vars: &mut N
|
||||
inodes.push(INode { kind, ports: [subtree_root, fst, snd] });
|
||||
}
|
||||
Tree::Swi { fst, snd } => {
|
||||
let kind = NodeKind::Mat;
|
||||
let kind = NodeKind::Swi;
|
||||
let fst = process_node_subtree(fst, net_root, &mut subtrees, n_vars);
|
||||
let snd = process_node_subtree(snd, net_root, &mut subtrees, n_vars);
|
||||
inodes.push(INode { kind, ports: [subtree_root, fst, snd] });
|
||||
|
@ -36,7 +36,7 @@ pub enum NodeKind {
|
||||
/// Numeric operations
|
||||
Opr,
|
||||
/// Pattern matching on numbers
|
||||
Mat,
|
||||
Swi,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@ -91,7 +91,7 @@ impl INet {
|
||||
|
||||
/// Returns the value stored at a port, the port on the other side of the given one.
|
||||
pub fn enter_port(&self, port: Port) -> Port {
|
||||
self.node(port.node()).port(port.slot())
|
||||
self.node(port.node_id()).port(port.slot())
|
||||
}
|
||||
|
||||
/// Links two ports.
|
||||
@ -102,7 +102,7 @@ impl INet {
|
||||
|
||||
/// Sets a port to point to another port
|
||||
pub fn set(&mut self, src: Port, dst: Port) {
|
||||
*self.nodes[src.node() as usize].port_mut(src.slot()) = dst;
|
||||
*self.nodes[src.node_id() as usize].port_mut(src.slot()) = dst;
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ impl Node {
|
||||
|
||||
impl Port {
|
||||
/// Returns the node address of a port.
|
||||
pub fn node(self) -> NodeId {
|
||||
pub fn node_id(self) -> NodeId {
|
||||
self.0
|
||||
}
|
||||
|
||||
|
9
tests/golden_tests/run_file/readback_num_ops.bend
Normal file
9
tests/golden_tests/run_file/readback_num_ops.bend
Normal file
@ -0,0 +1,9 @@
|
||||
main = [
|
||||
@a (+ a 1),
|
||||
@a (+ 1 a),
|
||||
@a @b (+ a b),
|
||||
@a (- a 1),
|
||||
@a (- 1 a),
|
||||
@a @b @c (+ a (- b c))
|
||||
@a @b @c (+ (- a b) c)
|
||||
]
|
@ -2,8 +2,4 @@
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/readback_hvm/addition.bend
|
||||
---
|
||||
[4m[1m[33mWarnings:[0m
|
||||
[1mDuring readback:[0m
|
||||
Encountered an invalid numeric operation.
|
||||
|
||||
<Invalid>
|
||||
(+ 2 1)
|
||||
|
@ -4,6 +4,6 @@ input_file: tests/golden_tests/readback_hvm/invalid_op2_op2.bend
|
||||
---
|
||||
[4m[1m[33mWarnings:[0m
|
||||
[1mDuring readback:[0m
|
||||
Encountered an invalid numeric operation.
|
||||
Encountered an invalid numeric operation. (3 occurrences)
|
||||
|
||||
λa <Invalid>
|
||||
|
@ -2,8 +2,4 @@
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/readback_hvm/tup_add.bend
|
||||
---
|
||||
[4m[1m[33mWarnings:[0m
|
||||
[1mDuring readback:[0m
|
||||
Encountered an invalid numeric operation.
|
||||
|
||||
<Invalid>
|
||||
(+ 1 2)
|
||||
|
@ -3,15 +3,7 @@ source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/lam_op2.bend
|
||||
---
|
||||
NumScott:
|
||||
[4m[1m[33mWarnings:[0m
|
||||
[1mDuring readback:[0m
|
||||
Encountered an invalid numeric operation.
|
||||
|
||||
λa <Invalid>
|
||||
λa (+ 2 a)
|
||||
|
||||
Scott:
|
||||
[4m[1m[33mWarnings:[0m
|
||||
[1mDuring readback:[0m
|
||||
Encountered an invalid numeric operation.
|
||||
|
||||
λa <Invalid>
|
||||
λa (+ 2 a)
|
||||
|
@ -3,15 +3,7 @@ source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/lam_op2_nested.bend
|
||||
---
|
||||
NumScott:
|
||||
[4m[1m[33mWarnings:[0m
|
||||
[1mDuring readback:[0m
|
||||
Encountered an invalid numeric operation.
|
||||
|
||||
λa <Invalid>
|
||||
λa (+ (* a a) (+ 3 (+ 2 a)))
|
||||
|
||||
Scott:
|
||||
[4m[1m[33mWarnings:[0m
|
||||
[1mDuring readback:[0m
|
||||
Encountered an invalid numeric operation.
|
||||
|
||||
λa <Invalid>
|
||||
λa (+ (* a a) (+ 3 (+ 2 a)))
|
||||
|
@ -3,15 +3,7 @@ source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/linearize_match.bend
|
||||
---
|
||||
NumScott:
|
||||
[4m[1m[33mWarnings:[0m
|
||||
[1mDuring readback:[0m
|
||||
Encountered an invalid numeric operation.
|
||||
|
||||
λa switch a = a { 0: λb b; _: λd <Invalid>; }
|
||||
λa switch a = a { 0: λb b; _: λd (+ a-1 d); }
|
||||
|
||||
Scott:
|
||||
[4m[1m[33mWarnings:[0m
|
||||
[1mDuring readback:[0m
|
||||
Encountered an invalid numeric operation.
|
||||
|
||||
λa switch a = a { 0: λb b; _: λd <Invalid>; }
|
||||
λa switch a = a { 0: λb b; _: λd (+ a-1 d); }
|
||||
|
@ -3,15 +3,7 @@ source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/match_mult_linearization.bend
|
||||
---
|
||||
NumScott:
|
||||
[4m[1m[33mWarnings:[0m
|
||||
[1mDuring readback:[0m
|
||||
Encountered an invalid numeric operation. (2 occurrences)
|
||||
|
||||
λa switch a = a { 0: λb λc λd <Invalid>; _: λf λg λh <Invalid>; }
|
||||
λa switch a = a { 0: λb λc λd (+ (+ b c) d); _: λf λg λh (+ (+ (+ a-1 f) g) h); }
|
||||
|
||||
Scott:
|
||||
[4m[1m[33mWarnings:[0m
|
||||
[1mDuring readback:[0m
|
||||
Encountered an invalid numeric operation. (2 occurrences)
|
||||
|
||||
λa switch a = a { 0: λb λc λd <Invalid>; _: λf λg λh <Invalid>; }
|
||||
λa switch a = a { 0: λb λc λd (+ (+ b c) d); _: λf λg λh (+ (+ (+ a-1 f) g) h); }
|
||||
|
9
tests/snapshots/run_file__readback_num_ops.bend.snap
Normal file
9
tests/snapshots/run_file__readback_num_ops.bend.snap
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/readback_num_ops.bend
|
||||
---
|
||||
NumScott:
|
||||
[λb (+ 1 b), λd (+ 1 d), λf λg (+ f g), λi (- i 1), λk (- 1 k), λm λn λo (+ m (- n o)), λq λr λs (+ (- q r) s)]
|
||||
|
||||
Scott:
|
||||
[λb (+ 1 b), λd (+ 1 d), λf λg (+ f g), λi (- i 1), λk (- 1 k), λm λn λo (+ m (- n o)), λq λr λs (+ (- q r) s)]
|
Loading…
Reference in New Issue
Block a user