[sc-548] Add support for i24 and f24

This commit is contained in:
Nicolas Abril 2024-05-03 18:48:52 +02:00
parent ba748c95e8
commit 03fcf344f1
26 changed files with 452 additions and 137 deletions

View File

@ -61,6 +61,7 @@
"peekable",
"postcondition",
"prec",
"powi",
"readback",
"recursively",
"redex",

View File

@ -13,8 +13,12 @@
(SwapGT _ v x xs) = (List.cons x (Insert v xs))
// Generates a random list
(Rnd 0 s) = []
(Rnd n s) = (List.cons s (Rnd (- n 1) (% (+ (* s 1664525) 1013904223) 4294967295)))
(Rnd 0 s) = List.nil
(Rnd n s) =
let s = (^ s (* s 0b10000000000000))
let s = (^ s (/ s 0b100000000000000000))
let s = (^ s (* s 0b100000))
(List.cons s (Rnd (- n 1) s))
// Sums a list
(Sum []) = 0
@ -22,7 +26,7 @@
(Main) =
let n = 10
(Sum (Sort (Rnd (* 2 50) n)))
(Sum (Sort (Rnd 0x100 n)))
// Use an argument from cli
// (Main n) = (Sum (Sort (Rnd (* 2 50) n)))
// (Main n) = (Sum (Sort (Rnd 0x100 n)))

View File

@ -17,8 +17,12 @@ data Tree = Leaf | (Node l m r)
(Push _ x pair) = (pair λmin λmax λp (p min (List.cons x max)))
// Generates a random list
(Rnd 0 s) = (List.nil)
(Rnd n s) = (List.cons s (Rnd (- n 1) (% (+ (* s 1664525) 1013904223) 4294967295)))
(Rnd 0 s) = List.nil
(Rnd n s) =
let s = (^ s (* s 0b10000000000000))
let s = (^ s (/ s 0b100000000000000000))
let s = (^ s (* s 0b100000))
(List.cons s (Rnd (- n 1) s))
// Sums all elements in a concatenation tree
(Sum Leaf) = 0
@ -26,8 +30,7 @@ data Tree = Leaf | (Node l m r)
// Sorts and sums n random numbers
(Main) =
let n = 12
(Sum (Sort (Rnd (* 2 n) 1)))
(Sum (Sort (Rnd 0x100 1)))
// Use an argument from cli
// (Main n) = (Sum (Sort (Rnd (<< 1 n) 1)))

View File

@ -36,45 +36,44 @@ data Arr = Null | (Leaf x) | (Node a b)
// Radix : U60 -> Map
(Radix n) =
let r = Used
let r = (Swap (& n 1) r Free)
let r = (Swap (& n 2) r Free)
let r = (Swap (& n 4) r Free)
let r = (Swap (& n 8) r Free)
let r = (Swap (& n 16) r Free)
let r = (Swap (& n 0x1) r Free)
let r = (Swap (& n 0x2) r Free)
let r = (Swap (& n 0x4) r Free)
let r = (Swap (& n 0x8) r Free)
let r = (Swap (& n 0x10) r Free)
(Radix2 n r)
(Radix2 n r) =
let r = (Swap (& n 32) r Free)
let r = (Swap (& n 64) r Free)
let r = (Swap (& n 128) r Free)
let r = (Swap (& n 256) r Free)
let r = (Swap (& n 512) r Free)
let r = (Swap (& n 0x20) r Free)
let r = (Swap (& n 0x40) r Free)
let r = (Swap (& n 0x80) r Free)
let r = (Swap (& n 0x100) r Free)
let r = (Swap (& n 0x200) r Free)
(Radix3 n r)
(Radix3 n r) =
let r = (Swap (& n 1024) r Free)
let r = (Swap (& n 2048) r Free)
let r = (Swap (& n 4096) r Free)
let r = (Swap (& n 8192) r Free)
let r = (Swap (& n 16384) r Free)
let r = (Swap (& n 0x400) r Free)
let r = (Swap (& n 0x800) r Free)
let r = (Swap (& n 0x1000) r Free)
let r = (Swap (& n 0x2000) r Free)
let r = (Swap (& n 0x4000) r Free)
(Radix4 n r)
(Radix4 n r) =
let r = (Swap (& n 32768) r Free)
let r = (Swap (& n 65536) r Free)
let r = (Swap (& n 131072) r Free)
let r = (Swap (& n 262144) r Free)
let r = (Swap (& n 524288) r Free)
let r = (Swap (& n 0x8000) r Free)
let r = (Swap (& n 0x10000) r Free)
let r = (Swap (& n 0x20000) r Free)
let r = (Swap (& n 0x40000) r Free)
let r = (Swap (& n 0x80000) r Free)
(Radix5 n r)
(Radix5 n r) =
let r = (Swap (& n 1048576) r Free)
let r = (Swap (& n 2097152) r Free)
let r = (Swap (& n 4194304) r Free)
let r = (Swap (& n 8388608) r Free)
let r = (Swap (& n 0x100000) r Free)
let r = (Swap (& n 0x200000) r Free)
let r = (Swap (& n 0x400000) r Free)
let r = (Swap (& n 0x800000) r Free)
r
// Reverse : Arr -> Arr
(Reverse Null) = Null
(Reverse (Leaf a)) = (Leaf a)

View File

@ -1,4 +1,4 @@
use super::{parser::TermParser, Book, Name, NumType, Pattern, Term};
use super::{parser::TermParser, Book, Name, Num, Pattern, Term};
use crate::maybe_grow;
const BUILTINS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/term/builtins.hvm"));
@ -62,11 +62,11 @@ impl Term {
pub fn encode_str(val: &str) -> Term {
val.chars().rfold(Term::r#ref(SNIL), |acc, char| {
Term::call(Term::r#ref(SCONS), [Term::Num { typ: NumType::U24, val: char as u32 }, acc])
Term::call(Term::r#ref(SCONS), [Term::Num { val: Num::U24(char as u32 & 0x00ff_ffff) }, acc])
})
}
pub fn encode_nat(val: u64) -> Term {
pub fn encode_nat(val: u32) -> Term {
(0 .. val).fold(Term::r#ref(NAT_ZERO), |acc, _| Term::app(Term::r#ref(NAT_SUCC), acc))
}

View File

@ -1,4 +1,4 @@
use super::{Book, Definition, FanKind, Name, Op, Pattern, Rule, Tag, Term};
use super::{Book, Definition, FanKind, Name, Num, Op, Pattern, Rule, Tag, Term};
use crate::maybe_grow;
use std::{fmt, ops::Deref};
@ -114,7 +114,9 @@ impl fmt::Display for Term {
Term::Fan { fan: FanKind::Tup, tag, els } => write!(f, "{}({})", tag, DisplayJoin(|| els.iter(), ", ")),
Term::Fan { fan: FanKind::Dup, tag, els } => write!(f, "{}{{{}}}", tag, DisplayJoin(|| els, " ")),
Term::Era => write!(f, "*"),
Term::Num { typ: _, val } => write!(f, "{val}"),
Term::Num { val: Num::U24(val) } => write!(f, "{val}"),
Term::Num { val: Num::I24(val) } => write!(f, "{}{}", if *val < 0 { "-" } else { "+" }, val.abs()),
Term::Num { val: Num::F24(val) } => write!(f, "{val:.3}"),
Term::Nat { val } => write!(f, "#{val}"),
Term::Str { val } => write!(f, "{val:?}"),
Term::Opr { opr, fst, snd } => {
@ -374,7 +376,9 @@ impl Term {
}
Term::Nat { val } => write!(f, "#{val}"),
Term::Num { typ: _, val } => write!(f, "{val}"),
Term::Num { val: Num::U24(val) } => write!(f, "{val}"),
Term::Num { val: Num::I24(val) } => write!(f, "{}{}", if *val < 0 { "-" } else { "+" }, val.abs()),
Term::Num { val: Num::F24(val) } => write!(f, "{val:.3}"),
Term::Str { val } => write!(f, "{val:?}"),
Term::Ref { nam } => write!(f, "{nam}"),
Term::Era => write!(f, "*"),

View File

@ -2,7 +2,7 @@ use indexmap::IndexMap;
use crate::term::Name;
use super::{Definition, Enum, Program, Stmt, Term, Variant};
use super::{Definition, Enum, MBind, Program, Stmt, Term, Variant};
struct Ctx<'a> {
variants: &'a IndexMap<Name, Name>,
@ -58,9 +58,26 @@ impl Stmt {
arm.rgt.order_kwargs(ctx);
}
}
Stmt::Switch { .. } => unimplemented!(),
Stmt::Fold { .. } => unimplemented!(),
Stmt::Do { .. } => unimplemented!(),
Stmt::Switch { arg, arms, .. } => {
arg.order_kwargs(ctx);
for arm in arms {
arm.order_kwargs(ctx);
}
}
Stmt::Fold { arg, arms, .. } => {
arg.order_kwargs(ctx);
for arm in arms {
arm.rgt.order_kwargs(ctx);
}
}
Stmt::Do { block, .. } => {
for bind in block {
match bind {
MBind::Ask { val, .. } => val.order_kwargs(ctx),
MBind::Stmt { stmt } => stmt.order_kwargs(ctx),
}
}
}
Stmt::Return { term } => term.order_kwargs(ctx),
}
}

View File

@ -88,7 +88,7 @@ impl Term {
match self {
Term::None => lang::Term::Era,
Term::Var { nam } => lang::Term::Var { nam },
Term::Num { val } => lang::Term::Num { typ: lang::NumType::U24, val },
Term::Num { val } => lang::Term::Num { val: lang::Num::U24(val) },
Term::Call { fun, args, kwargs } => {
assert!(kwargs.is_empty());
let args = args.into_iter().map(Self::to_lang);

View File

@ -7,7 +7,7 @@ use crate::{
use indexmap::{IndexMap, IndexSet};
use interner::global::{GlobalPool, GlobalString};
use itertools::Itertools;
use std::{borrow::Cow, collections::HashMap, ops::Deref};
use std::{borrow::Cow, collections::HashMap, hash::Hash, ops::Deref};
pub mod builtins;
pub mod check;
@ -63,7 +63,7 @@ pub struct Definition {
}
/// A pattern matching rule of a definition.
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Rule {
pub pats: Vec<Pattern>,
pub body: Term,
@ -110,11 +110,10 @@ pub enum Term {
els: Vec<Term>,
},
Num {
typ: NumType,
val: u32,
val: Num,
},
Nat {
val: u64,
val: u32,
},
Str {
val: GlobalString,
@ -181,11 +180,11 @@ pub enum Op {
POW,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum NumType {
U24 = 1,
I24 = 2,
F24 = 3,
#[derive(Debug, Clone, Copy)]
pub enum Num {
U24(u32),
I24(i32),
F24(f32),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -328,7 +327,7 @@ impl Clone for Term {
Self::Use { nam, val, nxt } => Self::Use { nam: nam.clone(), val: val.clone(), nxt: nxt.clone() },
Self::App { tag, fun, arg } => Self::App { tag: tag.clone(), fun: fun.clone(), arg: arg.clone() },
Self::Fan { fan, tag, els } => Self::Fan { fan: *fan, tag: tag.clone(), els: els.clone() },
Self::Num { typ, val } => Self::Num { typ: *typ, val: *val },
Self::Num { val } => Self::Num { val: *val },
Self::Nat { val } => Self::Nat { val: *val },
Self::Str { val } => Self::Str { val: val.clone() },
Self::Lst { els } => Self::Lst { els: els.clone() },
@ -436,19 +435,19 @@ impl Term {
Term::Str { val: STRINGS.get(str) }
}
pub fn sub_num(arg: Term, val: u32, typ: NumType) -> Term {
if val == 0 {
pub fn sub_num(arg: Term, val: Num) -> Term {
if val.is_zero() {
arg
} else {
Term::Opr { opr: Op::SUB, fst: Box::new(arg), snd: Box::new(Term::Num { typ, val }) }
Term::Opr { opr: Op::SUB, fst: Box::new(arg), snd: Box::new(Term::Num { val }) }
}
}
pub fn add_num(arg: Term, val: u32, typ: NumType) -> Term {
if val == 0 {
pub fn add_num(arg: Term, val: Num) -> Term {
if val.is_zero() {
arg
} else {
Term::Opr { opr: Op::ADD, fst: Box::new(arg), snd: Box::new(Term::Num { typ, val }) }
Term::Opr { opr: Op::ADD, fst: Box::new(arg), snd: Box::new(Term::Num { val }) }
}
}
@ -800,6 +799,71 @@ impl Term {
}
}
impl Num {
pub fn is_zero(&self) -> bool {
match self {
Num::U24(val) => *val == 0,
Num::I24(val) => *val == 0,
Num::F24(val) => *val == 0.0,
}
}
pub fn to_bits(&self) -> u32 {
match self {
Num::U24(val) => {
assert!(*val <= 0xFFFFFF);
((val & 0xFFFFFF) << 4) | 0x1
}
Num::I24(val) => (((*val as u32) & 0xFFFFFF) << 4) | 0x2,
Num::F24(val) => {
let bits = val.to_bits();
let sign = (bits >> 31) & 0x1;
let expo = (bits >> 23) & 0xFF;
let mantissa = bits & 0x7FFFFF;
assert!(
(expo == 0) || (expo == 255) || (64 ..= 127).contains(&expo) || (128 ..= 190).contains(&expo)
);
let expo = (expo & 0b0011_1111) | ((expo >> 7) << 6);
let mantissa = mantissa >> 7;
let bits = (sign << 23) | (expo << 16) | mantissa;
(bits << 4) | 0x3
}
}
}
pub fn from_bits(bits: u32) -> Self {
match bits & 0xF {
0x1 => Num::U24((bits >> 4) & 0xFFFFFF),
0x2 => Num::I24((((bits >> 4) & 0xFFFFFF) as i32) << 8 >> 8),
0x3 => {
let bits = (bits >> 4) & 0xFFFFFF;
let sign = (bits >> 23) & 0x1;
let expo = (bits >> 16) & 0x7F;
let mantissa = bits & 0xFFFF;
let i_exp = (expo as i32) - 63;
let bits = (sign << 31) | (((i_exp + 127) as u32) << 23) | (mantissa << 7);
let bits = if mantissa == 0 && i_exp == -63 { sign << 31 } else { bits };
Num::F24(f32::from_bits(bits))
}
_ => unreachable!("Invalid Num bits"),
}
}
}
impl Hash for Num {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.to_bits().hash(state);
}
}
impl PartialEq for Num {
fn eq(&self, other: &Self) -> bool {
self.to_bits() == other.to_bits()
}
}
impl Eq for Num {}
impl Pattern {
pub fn binds(&self) -> impl DoubleEndedIterator<Item = &Option<Name>> + Clone {
self.iter().filter_map(|pat| match pat {
@ -862,7 +926,7 @@ impl Pattern {
Pattern::Ctr(ctr, args) => {
Term::call(Term::Ref { nam: ctr.clone() }, args.iter().map(|arg| arg.to_term()))
}
Pattern::Num(val) => Term::Num { typ: NumType::U24, val: *val },
Pattern::Num(val) => Term::Num { val: Num::U24(*val) },
Pattern::Fan(fan, tag, args) => {
Term::Fan { fan: *fan, tag: tag.clone(), els: args.iter().map(|p| p.to_term()).collect() }
}
@ -960,3 +1024,35 @@ impl Book {
}
}
}
#[test]
fn num_to_from_bits() {
let a = [
Num::U24(0),
Num::I24(0),
Num::F24(0.0),
Num::U24(0xFFFFFF),
Num::I24(0xFFFFFF),
Num::F24(0xFFFFFF as f32),
Num::U24(12345),
Num::I24(12345),
Num::I24(-12345),
Num::I24(-0),
Num::F24(0.0),
Num::F24(-0.0),
Num::F24(0.00123),
Num::F24(12345.023),
Num::F24(-1235.3849),
Num::F24(1.0),
Num::F24(-1.0),
Num::F24(12323658716.0),
Num::F24(-12323658716.0),
Num::F24(-0.00000000000000001),
Num::F24(0.00000000000000001),
Num::F24(5447856134985749851.3457896137815694178),
Num::F24(-5447856134985749851.3457896137815694178),
];
for b in a {
assert_eq!(b, Num::from_bits(Num::to_bits(&b)));
}
}

View File

@ -1,17 +1,12 @@
use crate::{
diagnostics::{DiagnosticOrigin, Diagnostics, Severity},
maybe_grow,
net::{
CtrKind::*,
INet, NodeId,
NodeKind::{self, *},
Port, SlotId, ROOT,
},
term::{num_to_name, term_to_net::Labels, Book, FanKind, Name, Pattern, Tag, Term},
net::{CtrKind, INet, NodeId, NodeKind, Port, SlotId, ROOT},
term::{num_to_name, term_to_net::Labels, Book, FanKind, Name, Op, Pattern, Tag, Term},
};
use std::collections::{BTreeSet, HashMap, HashSet};
use super::{NumType, Op};
use super::Num;
/// Converts an Interaction-INet to a Lambda Calculus term
pub fn net_to_term(
@ -41,8 +36,8 @@ pub fn net_to_term(
let snd = reader.namegen.decl_name(net, Port(node, 2));
let (fan, tag) = match reader.net.node(node).kind {
Ctr(Tup(lab)) => (FanKind::Tup, reader.labels.tup.to_tag(lab)),
Ctr(Dup(lab)) => (FanKind::Dup, reader.labels.dup.to_tag(Some(lab))),
NodeKind::Ctr(CtrKind::Tup(lab)) => (FanKind::Tup, reader.labels.tup.to_tag(lab)),
NodeKind::Ctr(CtrKind::Dup(lab)) => (FanKind::Dup, reader.labels.dup.to_tag(Some(lab))),
_ => unreachable!(),
};
@ -81,6 +76,8 @@ pub struct Reader<'a> {
impl Reader<'_> {
fn read_term(&mut self, next: Port) -> Term {
use CtrKind::*;
maybe_grow(|| {
if self.dup_paths.is_none() && !self.seen.insert(next) {
self.error(ReadbackError::Cyclic);
@ -88,15 +85,14 @@ impl Reader<'_> {
}
let node = next.node();
match &self.net.node(node).kind {
Era => {
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
}
// If we're visiting a con node...
Ctr(Con(lab)) => match next.slot() {
NodeKind::Ctr(CtrKind::Con(lab)) => match next.slot() {
// If we're visiting a port 0, then it is a lambda.
0 => {
let nam = self.namegen.decl_name(self.net, Port(node, 1));
@ -117,7 +113,7 @@ impl Reader<'_> {
}
_ => unreachable!(),
},
Mat => match next.slot() {
NodeKind::Mat => match next.slot() {
2 => {
// Read the matched expression
let arg = self.read_term(self.net.enter_port(Port(node, 0)));
@ -128,7 +124,7 @@ impl Reader<'_> {
// We expect the pattern matching node to be a CON
let sel_kind = &self.net.node(sel_node).kind;
let (zero, succ) = if *sel_kind == Ctr(Con(None)) {
let (zero, succ) = if *sel_kind == NodeKind::Ctr(Con(None)) {
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)));
@ -157,7 +153,7 @@ impl Reader<'_> {
Term::Err
}
},
Ref { def_name } => {
NodeKind::Ref { def_name } => {
if def_name.is_generated() {
// Dereference generated names since the user is not aware of them
let def = &self.book.defs[def_name];
@ -170,7 +166,7 @@ impl Reader<'_> {
}
}
// If we're visiting a fan node...
Ctr(kind @ (Dup(_) | Tup(_))) => {
NodeKind::Ctr(kind @ (Dup(_) | Tup(_))) => {
let (fan, lab) = match *kind {
Tup(lab) => (FanKind::Tup, lab),
Dup(lab) => (FanKind::Dup, Some(lab)),
@ -217,7 +213,7 @@ impl Reader<'_> {
_ => unreachable!(),
}
}
Num { val: _ } => {
NodeKind::Num { val: _ } => {
let (flp, arg) = self.read_opr_arg(next);
match arg {
NumArg::Sym(opr) => Term::Opr {
@ -225,26 +221,26 @@ impl Reader<'_> {
fst: Box::new(Term::Err),
snd: Box::new(Term::Err),
},
NumArg::Num(typ, val) => Term::Num { typ, val },
NumArg::Num(typ, val) => Term::Num { val: Num::from_bits_and_type(val, typ) },
NumArg::Par(opr, val) => {
if flp {
Term::Opr {
opr: Op::from_native_tag(opr, NumType::U24),
fst: Box::new(Term::Num { typ: NumType::U24, val }),
fst: Box::new(Term::Num { val: Num::from_bits_and_type(val, NumType::U24) }),
snd: Box::new(Term::Err),
}
} else {
Term::Opr {
opr: Op::from_native_tag(opr, NumType::U24),
fst: Box::new(Term::Err),
snd: Box::new(Term::Num { typ: NumType::U24, val }),
snd: Box::new(Term::Num { val: Num::from_bits_and_type(val, NumType::U24) }),
}
}
}
NumArg::Oth(_) => unreachable!(),
}
}
Opr => match next.slot() {
NodeKind::Opr => match next.slot() {
2 => {
let port0_kind = self.net.node(self.net.enter_port(Port(node, 0)).node()).kind.clone();
if port0_kind == NodeKind::Opr {
@ -253,7 +249,7 @@ impl Reader<'_> {
if let Term::Opr { opr, fst, snd: _ } = &fst {
let (flip, arg) = self.read_opr_arg(self.net.enter_port(Port(node, 1)));
let snd = Box::new(match arg {
NumArg::Num(typ, val) => Term::Num { typ, val },
NumArg::Num(typ, val) => Term::Num { val: Num::from_bits_and_type(val, typ) },
NumArg::Oth(term) => term,
NumArg::Sym(_) | NumArg::Par(_, _) => {
self.error(ReadbackError::InvalidNumericOp);
@ -271,34 +267,36 @@ impl Reader<'_> {
let (flip0, arg0) = self.read_opr_arg(self.net.enter_port(Port(node, 0)));
let (flip1, arg1) = self.read_opr_arg(self.net.enter_port(Port(node, 1)));
let (arg0, arg1) = if flip0 != flip1 { (arg1, arg0) } else { (arg0, arg1) };
use NumArg::*;
match (arg0, arg1) {
(Sym(opr), Num(typ, val)) | (Num(typ, val), Sym(opr)) => Term::Opr {
(NumArg::Sym(opr), NumArg::Num(typ, val)) | (NumArg::Num(typ, val), NumArg::Sym(opr)) => {
Term::Opr {
opr: Op::from_native_tag(opr, typ),
fst: Box::new(Term::Num { val: Num::from_bits_and_type(val, typ) }),
snd: Box::new(Term::Err),
}
}
(NumArg::Num(typ, num1), NumArg::Par(opr, num2))
| (NumArg::Par(opr, num1), NumArg::Num(typ, num2)) => Term::Opr {
opr: Op::from_native_tag(opr, typ),
fst: Box::new(Term::Num { typ, val }),
snd: Box::new(Term::Err),
},
(Num(typ, num1), Par(opr, num2)) | (Par(opr, num1), Num(typ, num2)) => Term::Opr {
opr: Op::from_native_tag(opr, typ),
fst: Box::new(Term::Num { typ, val: num1 }),
snd: Box::new(Term::Num { typ, val: num2 }),
fst: Box::new(Term::Num { val: Num::from_bits_and_type(num1, typ) }),
snd: Box::new(Term::Num { val: Num::from_bits_and_type(num2, typ) }),
},
// No type, so assuming u24
(Sym(opr), Oth(term)) | (Oth(term), Sym(opr)) => Term::Opr {
(NumArg::Sym(opr), NumArg::Oth(term)) | (NumArg::Oth(term), NumArg::Sym(opr)) => Term::Opr {
opr: Op::from_native_tag(opr, NumType::U24),
fst: Box::new(term),
snd: Box::new(Term::Err),
},
(Par(opr, num), Oth(term)) => Term::Opr {
(NumArg::Par(opr, num), NumArg::Oth(term)) => Term::Opr {
opr: Op::from_native_tag(opr, NumType::U24),
fst: Box::new(Term::Num { typ: NumType::U24, val: num }),
fst: Box::new(Term::Num { val: Num::from_bits_and_type(num, NumType::U24) }),
snd: Box::new(term),
},
(Oth(term), Par(opr, num)) => Term::Opr {
(NumArg::Oth(term), NumArg::Par(opr, num)) => Term::Opr {
opr: Op::from_native_tag(opr, NumType::U24),
fst: Box::new(term),
snd: Box::new(Term::Num { typ: NumType::U24, val: num }),
snd: Box::new(Term::Num { val: Num::from_bits_and_type(num, NumType::U24) }),
},
_ => {
self.error(ReadbackError::InvalidNumericOp);
@ -312,7 +310,7 @@ impl Reader<'_> {
Term::Err
}
},
Rot => {
NodeKind::Rot => {
self.error(ReadbackError::ReachedRoot);
Term::Err
}
@ -323,7 +321,7 @@ impl Reader<'_> {
fn read_opr_arg(&mut self, next: Port) -> (bool, NumArg) {
let node = next.node();
match &self.net.node(node).kind {
Num { val } => {
NodeKind::Num { val } => {
self.seen.insert(next);
let flipped = ((val >> 28) & 0x1) != 0;
let typ = val & 0xf;
@ -381,7 +379,7 @@ impl Reader<'_> {
// Eta-reduce the readback inet.
// This is not valid for all kinds of nodes, only CON/TUP/DUP, due to their interaction rules.
if matches!(node_kind, Ctr(_)) {
if matches!(node_kind, NodeKind::Ctr(_)) {
match (fst_port, snd_port) {
(Port(fst_node, 1), Port(snd_node, 2)) if fst_node == snd_node => {
if self.net.node(fst_node).kind == *node_kind {
@ -427,6 +425,13 @@ enum NumArg {
Oth(Term),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum NumType {
U24 = 1,
I24 = 2,
F24 = 3,
}
impl Op {
fn from_native_tag(val: u32, typ: NumType) -> Op {
match val {
@ -476,6 +481,12 @@ impl NumType {
}
}
impl Num {
fn from_bits_and_type(bits: u32, typ: NumType) -> Self {
Num::from_bits((bits & 0x00ff_ffff) << 4 | (typ as u32))
}
}
/* Insertion of dups in the middle of the term */
/// Represents `let #tag(fst, snd) = val` / `let #tag{fst snd} = val`
@ -589,7 +600,7 @@ impl NameGen {
// 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;
(*var_kind != Era).then(|| self.var_name(var_port))
(*var_kind != NodeKind::Era).then(|| self.var_name(var_port))
}
pub fn unique(&mut self) -> Name {

View File

@ -1,15 +1,13 @@
use crate::{
maybe_grow,
term::{
display::DisplayFn, Adt, Book, Definition, FanKind, MatchRule, Name, Op, Pattern, Rule, Tag, Term,
display::DisplayFn, Adt, Book, Definition, FanKind, MatchRule, Name, Num, Op, Pattern, Rule, Tag, Term,
STRINGS,
},
};
use highlight_error::highlight_error;
use TSPL::Parser;
use super::NumType;
// hvml grammar description:
// <Book> ::= (<Data> | <Rule>)*
// <Data> ::= "data" <Name> "=" ( <Name> | "(" <Name> (<Name>)* ")" )+
@ -188,8 +186,8 @@ impl<'a> TermParser<'a> {
// Number
if self.peek_one().map_or(false, |c| c.is_ascii_digit()) {
unexpected_tag(self)?;
let num = self.parse_u64()?;
return Ok(Pattern::Num(num as u32));
let num = self.parse_u32()?;
return Ok(Pattern::Num(num));
}
// Channel
@ -303,7 +301,7 @@ impl<'a> TermParser<'a> {
if self.starts_with("#") {
self.consume("#")?;
unexpected_tag(self)?;
let val = self.parse_u64()?;
let val = self.parse_u32()?;
return Ok(Term::Nat { val });
}
@ -318,14 +316,61 @@ impl<'a> TermParser<'a> {
if self.starts_with("'") {
unexpected_tag(self)?;
let char = self.parse_quoted_char()?;
return Ok(Term::Num { typ: NumType::U24, val: char as u32 });
return Ok(Term::Num { val: Num::U24(char as u32 & 0x00ff_ffff) });
}
// Native num
if self.peek_one().map_or(false, |c| c.is_ascii_digit()) {
// Native Number
if self.peek_one().map_or(false, |c| "0123456789+-".contains(c)) {
unexpected_tag(self)?;
let val = self.parse_u64()?;
return Ok(Term::Num { typ: NumType::U24, val: val as u32 });
let ini_idx = *self.index();
// Parses sign
let sgn = if self.try_consume("+") {
Some(1)
} else if self.try_consume("-") {
Some(-1)
} else {
None
};
// Parses main value
let num = self.parse_u32()?;
// Parses frac value (Float type)
// TODO: Will lead to some rounding errors
// TODO: Doesn't cover very large/small numbers
let fra = if let Some('.') = self.peek_one() {
self.consume(".")?;
let ini_idx = *self.index();
let fra = self.parse_u32()? as f32;
let end_idx = *self.index();
let fra = fra / 10f32.powi((end_idx - ini_idx) as i32);
Some(fra)
} else {
None
};
// F24
if let Some(fra) = fra {
let sgn = sgn.unwrap_or(1);
return Ok(Term::Num { val: Num::F24(sgn as f32 * (num as f32 + fra)) });
}
// I24
if let Some(sgn) = sgn {
let num = sgn * num as i32;
if !(-0x00800000 ..= 0x007fffff).contains(&num) {
return self.num_range_err(ini_idx, "I24");
}
return Ok(Term::Num { val: Num::I24(num) });
}
// U24
if num >= 1 << 24 {
return self.num_range_err(ini_idx, "U24");
}
return Ok(Term::Num { val: Num::U24(num) });
}
// Use
@ -493,7 +538,7 @@ impl<'a> TermParser<'a> {
}
}
c if c.is_ascii_digit() => {
let val = self.parse_u64()?;
let val = self.parse_u32()?;
if val != expected_num {
return self.expected(&expected_num.to_string());
}
@ -509,6 +554,115 @@ impl<'a> TermParser<'a> {
self.consume("}")?;
Ok(Term::Swt { arg: Box::new(arg), bnd: Some(bnd), with, pred, arms })
}
fn num_range_err<T>(&mut self, ini_idx: usize, typ: &str) -> Result<T, String> {
let ctx = highlight_error(ini_idx, *self.index(), self.input());
Err(format!("\x1b[1mNumber literal outside of range for {}.\x1b[0m\n{}", typ, ctx))
}
/* Utils */
/// Checks if the next characters in the input start with the given string.
/// Skips trivia.
fn skip_starts_with(&mut self, text: &str) -> bool {
self.skip_trivia();
self.starts_with(text)
}
fn skip_peek_one(&mut self) -> Option<char> {
self.skip_trivia();
self.peek_one()
}
/// Parses a list-like structure like "[x1, x2, x3,]".
///
/// `parser` is a function that parses an element of the list.
///
/// If `hard_sep` the separator between elements is mandatory.
/// Always accepts trailing separators.
///
/// `min_els` determines how many elements must be parsed at minimum.
fn list_like<T>(
&mut self,
parser: impl Fn(&mut Self) -> Result<T, String>,
start: &str,
end: &str,
sep: &str,
hard_sep: bool,
min_els: usize,
) -> Result<Vec<T>, String> {
self.consume(start)?;
let mut els = vec![];
for i in 0 .. min_els {
els.push(parser(self)?);
if hard_sep && !(i == min_els - 1 && self.skip_starts_with(end)) {
self.consume(sep)?;
} else {
self.try_consume(sep);
}
}
while !self.try_consume(end) {
els.push(parser(self)?);
if hard_sep && !self.skip_starts_with(end) {
self.consume(sep)?;
} else {
self.try_consume(sep);
}
}
Ok(els)
}
fn labelled<T>(
&mut self,
parser: impl Fn(&mut Self) -> Result<T, String>,
label: &str,
) -> Result<T, String> {
match parser(self) {
Ok(val) => Ok(val),
Err(_) => self.expected(label),
}
}
fn expected_spanned<T>(&mut self, exp: &str, ini_idx: usize, end_idx: usize) -> Result<T, String> {
let ctx = highlight_error(ini_idx, end_idx, self.input());
let is_eof = self.is_eof();
let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { write!(f, "\n{ctx}") });
Err(format!("\x1b[1m- expected:\x1b[0m {}\n\x1b[1m- detected:\x1b[0m{}", exp, detected))
}
/// Consumes text if the input starts with it. Otherwise, do nothing.
fn try_consume(&mut self, text: &str) -> bool {
self.skip_trivia();
if self.starts_with(text) {
self.consume(text).unwrap();
true
} else {
false
}
}
fn parse_u32(&mut self) -> Result<u32, String> {
self.skip_trivia();
let radix = match self.peek_many(2) {
Some("0x") => {
self.advance_many(2);
16
}
Some("0b") => {
self.advance_many(2);
2
}
_ => 10,
};
let num_str = self.take_while(move |c| c.is_digit(radix) || c == '_');
let num_str = num_str.chars().filter(|c| *c != '_').collect::<String>();
if num_str.is_empty() {
self.expected("numeric digit")
} else {
u32::from_str_radix(&num_str, radix).map_err(|e| e.to_string())
}
}
}
impl<'a> Parser<'a> for TermParser<'a> {

View File

@ -108,8 +108,8 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
Term::Var { nam } => self.link_var(false, nam, up),
Term::Lnk { nam } => self.link_var(true, nam, up),
Term::Ref { nam } => self.link(up, Place::Tree(LoanedMut::new(Tree::Ref { nam: nam.to_string() }))),
Term::Num { typ, val } => {
let val = (*val << 4) | (*typ as u32);
Term::Num { val } => {
let val = val.to_bits();
self.link(up, Place::Tree(LoanedMut::new(Tree::Num { val })))
}
// A lambda becomes to a con node. Ports:
@ -170,18 +170,20 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
// Partially apply
match (fst.as_ref(), snd.as_ref()) {
// Put oper in fst
(Term::Num { typ: _, val }, snd) => {
let num_val = (*val << 4) | opr.to_native_tag();
let fst = Place::Tree(LoanedMut::new(Tree::Num { val: num_val }));
(Term::Num { val }, snd) => {
let val = val.to_bits();
let val = (val & 0xffff_fff0) | opr.to_native_tag();
let fst = Place::Tree(LoanedMut::new(Tree::Num { val }));
let node = self.new_opr();
self.link(fst, node.0);
self.encode_term(snd, node.1);
self.link(up, node.2);
}
// Put oper in snd
(fst, Term::Num { typ: _, val }) => {
let num_val = (*val << 4) | opr.to_native_tag();
let snd = Place::Tree(LoanedMut::new(Tree::Num { val: num_val }));
(fst, Term::Num { val }) => {
let val = val.to_bits();
let val = (val & 0xffff_fff0) | opr.to_native_tag();
let snd = Place::Tree(LoanedMut::new(Tree::Num { val }));
let node = self.new_opr();
self.encode_term(fst, node.0);
self.link(snd, node.1);

View File

@ -1,6 +1,6 @@
use crate::{
diagnostics::{Diagnostics, WarningType},
term::{builtins, Adts, Constructors, Ctx, Definition, FanKind, Name, NumType, Pattern, Rule, Tag, Term},
term::{builtins, Adts, Constructors, Ctx, Definition, FanKind, Name, Num, Pattern, Rule, Tag, Term},
};
use std::collections::{BTreeSet, HashSet};
@ -280,7 +280,7 @@ fn num_rule(
if let Some(var) = var {
body = Term::Use {
nam: Some(var.clone()),
val: Box::new(Term::Num { typ: NumType::U24, val: *num }),
val: Box::new(Term::Num { val: Num::U24(*num) }),
nxt: Box::new(std::mem::take(&mut body)),
};
}
@ -301,7 +301,7 @@ fn num_rule(
let mut body = rule.body.clone();
if let Some(var) = var {
let last_num = *nums.last().unwrap();
let var_recovered = Term::add_num(Term::Var { nam: pred_var.clone() }, 1 + last_num, NumType::U24);
let var_recovered = Term::add_num(Term::Var { nam: pred_var.clone() }, Num::U24(1 + last_num));
body = Term::Use { nam: Some(var.clone()), val: Box::new(var_recovered), nxt: Box::new(body) };
}
let rule = Rule { pats: rule.pats[1 ..].to_vec(), body };
@ -319,10 +319,10 @@ fn num_rule(
let val = if i > 0 {
// switch arg = (pred +1 +num_i-1 - num_i) { 0: body_i; _: acc }
// nums[i] >= nums[i-1]+1, so we do a sub here.
Term::sub_num(Term::Var { nam: pred_var.clone() }, nums[i] - 1 - nums[i - 1], NumType::U24)
Term::sub_num(Term::Var { nam: pred_var.clone() }, Num::U24(nums[i] - 1 - nums[i - 1]))
} else {
// switch arg = (arg -num_0) { 0: body_0; _: acc}
Term::sub_num(Term::Var { nam: arg.clone() }, nums[i], NumType::U24)
Term::sub_num(Term::Var { nam: arg.clone() }, Num::U24(nums[i]))
};
Term::Swt {

View File

@ -1,7 +1,7 @@
use crate::{
diagnostics::{Diagnostics, WarningType, ERR_INDENT_SIZE},
maybe_grow,
term::{Adts, Constructors, Ctx, MatchRule, Name, NumType, Term},
term::{Adts, Constructors, Ctx, MatchRule, Name, Num, Term},
};
use std::collections::HashMap;
@ -105,9 +105,9 @@ impl Term {
let n_nums = arms.len() - 1;
for (i, arm) in arms.iter_mut().enumerate() {
let orig = if i == n_nums {
Term::add_num(Term::Var { nam: pred.clone().unwrap() }, i as u32, NumType::U24)
Term::add_num(Term::Var { nam: pred.clone().unwrap() }, Num::U24(i as u32))
} else {
Term::Num { typ: NumType::U24, val: i as u32 }
Term::Num { val: Num::U24(i as u32) }
};
*arm = Term::Use { nam: bnd.clone(), val: Box::new(orig), nxt: Box::new(std::mem::take(arm)) };
}

View File

@ -0,0 +1 @@
(/ (* +124.0928 1.24) (+ 0.0 -235.12235))

View File

@ -0,0 +1 @@
(* (+ +1 -1) (- -12 +14))

View File

@ -0,0 +1 @@
0x10000000

View File

@ -1 +1 @@
(+ 0xFFFF_FFFF (+ 0b101 1_000))
(+ 0xFF_FFFF (+ 0b101 1_000))

View File

@ -1 +1 @@
(+ 0b0123456789 0FA)
(+ 0b012345 0FA)

View File

@ -0,0 +1,7 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_term/f24_oper.hvm
---
b
& $(1.240 $(:[/] $(a b))) ~ [*4583519]
& $(-235.121 a) ~ [+0]

View File

@ -0,0 +1,7 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_term/i24_oper.hvm
---
b
& $(-1 $(:[*] $(a b))) ~ [+1]
& $(+14 a) ~ [-16777204]

View File

@ -0,0 +1,7 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_term/number_too_large.hvm
---
Errors:
Number literal outside of range for U24.
 1 | 0x10000000

View File

@ -3,5 +3,5 @@ source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_term/nums.hvm
---
b
& $(a b) ~ :[+16777215]
& $(a b) ~ [+16777215]
& $(1000 a) ~ [+5]

View File

@ -5,4 +5,4 @@ input_file: tests/golden_tests/compile_term/wrong_nums.hvm
Errors:
- expected: ')'
- detected:
 1 | (+ 0b0123456789 0FA)
 1 | (+ 0b012345 0FA)

View File

@ -2,4 +2,4 @@
source: tests/golden_tests.rs
input_file: examples/bubble_sort.hvm
---
2721105
5525035

View File

@ -2,4 +2,4 @@
source: tests/golden_tests.rs
input_file: examples/quick_sort.hvm
---
7311046
12741879