use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum Literal {
Int(i64),
Float(f64),
Text(String),
Duration(i64),
Date(i32),
Moment(i64),
}
impl Eq for Literal {}
impl fmt::Display for Literal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Literal::Int(n) => write!(f, "{}", n),
Literal::Float(x) => write!(f, "{}", x),
Literal::Text(s) => write!(f, "{:?}", s),
Literal::Duration(nanos) => {
let abs = nanos.unsigned_abs();
let sign = if *nanos < 0 { "-" } else { "" };
if abs >= 3_600_000_000_000 {
write!(f, "{}{}h", sign, abs / 3_600_000_000_000)
} else if abs >= 60_000_000_000 {
write!(f, "{}{}min", sign, abs / 60_000_000_000)
} else if abs >= 1_000_000_000 {
write!(f, "{}{}s", sign, abs / 1_000_000_000)
} else if abs >= 1_000_000 {
write!(f, "{}{}ms", sign, abs / 1_000_000)
} else if abs >= 1_000 {
write!(f, "{}{}μs", sign, abs / 1_000)
} else {
write!(f, "{}{}ns", sign, abs)
}
}
Literal::Date(days) => {
let days = *days as i64;
let (year, month, day) = days_to_ymd(days);
write!(f, "{:04}-{:02}-{:02}", year, month, day)
}
Literal::Moment(nanos) => {
let secs = nanos / 1_000_000_000;
let days = secs / 86400;
let time_secs = secs % 86400;
let hours = time_secs / 3600;
let mins = (time_secs % 3600) / 60;
let secs_rem = time_secs % 60;
let (year, month, day) = days_to_ymd(days);
write!(f, "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
year, month, day, hours, mins, secs_rem)
}
}
}
}
fn days_to_ymd(days: i64) -> (i64, u8, u8) {
let z = days + 719468;
let era = if z >= 0 { z / 146097 } else { (z - 146096) / 146097 };
let doe = (z - era * 146097) as u32;
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
let y = yoe as i64 + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
let year = if m <= 2 { y + 1 } else { y };
(year, m as u8, d as u8)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Universe {
Prop,
Type(u32),
}
impl Universe {
pub fn succ(&self) -> Universe {
match self {
Universe::Prop => Universe::Type(1),
Universe::Type(n) => Universe::Type(n + 1),
}
}
pub fn max(&self, other: &Universe) -> Universe {
match (self, other) {
(Universe::Prop, u) | (u, Universe::Prop) => u.clone(),
(Universe::Type(a), Universe::Type(b)) => Universe::Type((*a).max(*b)),
}
}
pub fn is_subtype_of(&self, other: &Universe) -> bool {
match (self, other) {
(Universe::Prop, _) => true,
(Universe::Type(i), Universe::Type(j)) => i <= j,
(Universe::Type(_), Universe::Prop) => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Term {
Sort(Universe),
Var(String),
Global(String),
Pi {
param: String,
param_type: Box<Term>,
body_type: Box<Term>,
},
Lambda {
param: String,
param_type: Box<Term>,
body: Box<Term>,
},
App(Box<Term>, Box<Term>),
Match {
discriminant: Box<Term>,
motive: Box<Term>,
cases: Vec<Term>,
},
Fix {
name: String,
body: Box<Term>,
},
Lit(Literal),
Hole,
}
impl fmt::Display for Universe {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Universe::Prop => write!(f, "Prop"),
Universe::Type(n) => write!(f, "Type{}", n),
}
}
}
impl fmt::Display for Term {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Term::Sort(u) => write!(f, "{}", u),
Term::Var(name) => write!(f, "{}", name),
Term::Global(name) => write!(f, "{}", name),
Term::Pi {
param,
param_type,
body_type,
} => {
if param == "_" {
write!(f, "{} -> {}", param_type, body_type)
} else {
write!(f, "Π({}:{}). {}", param, param_type, body_type)
}
}
Term::Lambda {
param,
param_type,
body,
} => {
write!(f, "λ({}:{}). {}", param, param_type, body)
}
Term::App(func, arg) => {
let arg_needs_inner_parens =
matches!(arg.as_ref(), Term::Pi { param, .. } if param == "_");
if arg_needs_inner_parens {
write!(f, "({} ({}))", func, arg)
} else {
write!(f, "({} {})", func, arg)
}
}
Term::Match {
discriminant,
motive,
cases,
} => {
write!(f, "match {} return {} with ", discriminant, motive)?;
for (i, case) in cases.iter().enumerate() {
if i > 0 {
write!(f, " | ")?;
}
write!(f, "{}", case)?;
}
Ok(())
}
Term::Fix { name, body } => {
write!(f, "fix {}. {}", name, body)
}
Term::Lit(lit) => {
write!(f, "{}", lit)
}
Term::Hole => {
write!(f, "_")
}
}
}
}