use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::{BTreeSet, HashMap};
use std::fmt;
use std::rc::Rc;
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BaseType {
Unit,
Bool,
Int,
Float,
Length,
String,
InlineText,
BlockText,
MathText,
MathBoxes,
Image,
InlineBoxes,
BlockBoxes,
Context,
Document,
PrePath,
Path,
Graphics,
Font,
TextInfo,
}
impl BaseType {
pub fn name(self) -> &'static str {
match self {
BaseType::Unit => "unit",
BaseType::Bool => "bool",
BaseType::Int => "int",
BaseType::Float => "float",
BaseType::Length => "length",
BaseType::String => "string",
BaseType::InlineText => "inline-text",
BaseType::BlockText => "block-text",
BaseType::MathText => "math",
BaseType::MathBoxes => "math-boxes",
BaseType::Image => "image",
BaseType::InlineBoxes => "inline-boxes",
BaseType::BlockBoxes => "block-boxes",
BaseType::Context => "context",
BaseType::Document => "document",
BaseType::PrePath => "pre-path",
BaseType::Path => "path",
BaseType::Graphics => "graphics",
BaseType::Font => "font",
BaseType::TextInfo => "text-info",
}
}
}
impl fmt::Display for BaseType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
static FRESH_ID: AtomicU64 = AtomicU64::new(1 << 32);
fn fresh_id() -> u64 {
FRESH_ID.fetch_add(1, Ordering::Relaxed)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Kind {
Universal,
Record(BTreeSet<String>),
}
#[derive(Debug)]
enum TyVarLink {
Free {
id: u64,
level: u32,
kind: Kind,
},
Bound(MonoType),
}
#[derive(Clone, Debug)]
pub struct TyVarRef(Rc<RefCell<TyVarLink>>);
impl TyVarRef {
pub(crate) fn new(id: u64, level: u32, kind: Kind) -> Self {
TyVarRef(Rc::new(RefCell::new(TyVarLink::Free { id, level, kind })))
}
pub fn same(&self, other: &TyVarRef) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
pub(crate) fn ptr_key(&self) -> usize {
Rc::as_ptr(&self.0) as usize
}
pub fn id(&self) -> Option<u64> {
match &*self.0.borrow() {
TyVarLink::Free { id, .. } => Some(*id),
TyVarLink::Bound(_) => None,
}
}
pub fn level(&self) -> Option<u32> {
match &*self.0.borrow() {
TyVarLink::Free { level, .. } => Some(*level),
TyVarLink::Bound(_) => None,
}
}
pub fn set_level(&self, new_level: u32) {
if let TyVarLink::Free { level, .. } = &mut *self.0.borrow_mut() {
*level = new_level;
}
}
pub fn kind(&self) -> Kind {
match &*self.0.borrow() {
TyVarLink::Free { kind, .. } => kind.clone(),
TyVarLink::Bound(_) => Kind::Universal,
}
}
pub fn set_kind(&self, new_kind: Kind) {
if let TyVarLink::Free { kind, .. } = &mut *self.0.borrow_mut() {
*kind = new_kind;
}
}
pub fn bind(&self, ty: MonoType) {
*self.0.borrow_mut() = TyVarLink::Bound(ty);
}
pub fn is_free(&self) -> bool {
matches!(&*self.0.borrow(), TyVarLink::Free { .. })
}
}
impl PartialEq for TyVarRef {
fn eq(&self, other: &Self) -> bool {
self.same(other)
}
}
impl Eq for TyVarRef {}
pub(crate) fn new_ty_var(level: u32) -> TyVarRef {
TyVarRef::new(fresh_id(), level, Kind::Universal)
}
#[derive(Debug)]
enum RowVarLink {
Free {
id: u64,
level: u32,
kind: BTreeSet<String>,
},
Bound(Row),
}
#[derive(Clone, Debug)]
pub struct RowVarRef(Rc<RefCell<RowVarLink>>);
impl RowVarRef {
pub(crate) fn new(id: u64, level: u32, kind: BTreeSet<String>) -> Self {
RowVarRef(Rc::new(RefCell::new(RowVarLink::Free { id, level, kind })))
}
pub fn same(&self, other: &RowVarRef) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
pub(crate) fn ptr_key(&self) -> usize {
Rc::as_ptr(&self.0) as usize
}
pub fn id(&self) -> Option<u64> {
match &*self.0.borrow() {
RowVarLink::Free { id, .. } => Some(*id),
RowVarLink::Bound(_) => None,
}
}
pub fn level(&self) -> Option<u32> {
match &*self.0.borrow() {
RowVarLink::Free { level, .. } => Some(*level),
RowVarLink::Bound(_) => None,
}
}
pub fn set_level(&self, new_level: u32) {
if let RowVarLink::Free { level, .. } = &mut *self.0.borrow_mut() {
*level = new_level;
}
}
pub fn kind(&self) -> BTreeSet<String> {
match &*self.0.borrow() {
RowVarLink::Free { kind, .. } => kind.clone(),
RowVarLink::Bound(_) => BTreeSet::new(),
}
}
pub fn set_kind(&self, new_kind: BTreeSet<String>) {
if let RowVarLink::Free { kind, .. } = &mut *self.0.borrow_mut() {
*kind = new_kind;
}
}
pub fn bind(&self, row: Row) {
*self.0.borrow_mut() = RowVarLink::Bound(row);
}
pub fn is_free(&self) -> bool {
matches!(&*self.0.borrow(), RowVarLink::Free { .. })
}
}
impl PartialEq for RowVarRef {
fn eq(&self, other: &Self) -> bool {
self.same(other)
}
}
impl Eq for RowVarRef {}
pub(crate) fn new_row_var(level: u32) -> RowVarRef {
RowVarRef::new(fresh_id(), level, BTreeSet::new())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Stage {
Persistent0,
Stage0,
#[default]
Stage1,
}
impl Stage {
pub fn as_str(self) -> &'static str {
match self {
Stage::Persistent0 => "persistent stage",
Stage::Stage0 => "stage 0",
Stage::Stage1 => "stage 1",
}
}
pub fn parse(s: &str) -> Option<Stage> {
match s.trim() {
"persistent" => Some(Stage::Persistent0),
"0" => Some(Stage::Stage0),
"1" => Some(Stage::Stage1),
_ => None,
}
}
pub fn can_reference(self, bound: Stage) -> bool {
matches!(
(self, bound),
(_, Stage::Persistent0) | (Stage::Stage0, Stage::Stage0) | (Stage::Stage1, Stage::Stage1)
)
}
}
#[derive(Clone, Debug, syan::visit::Ast)]
#[subast(crate::types::Row, crate::types::CmdArgType)]
pub enum MonoType {
Var(TyVarRef),
Base(BaseType),
Func(Box<Row>, Box<MonoType>, Box<MonoType>),
Product(Vec<MonoType>),
List(Box<MonoType>),
Ref(Box<MonoType>),
Record(Row),
Variant(String, Vec<MonoType>),
Code(Box<MonoType>),
InlineCmd(Vec<CmdArgType>),
BlockCmd(Vec<CmdArgType>),
MathCmd(Vec<CmdArgType>),
}
#[derive(Clone, Debug, syan::visit::Ast)]
#[subast(crate::types::MonoType)]
pub struct CmdArgType {
pub optional: bool,
pub opt_labels: Vec<(String, MonoType)>,
pub ty: MonoType,
}
#[derive(Clone, Debug, syan::visit::Ast)]
#[subast(crate::types::MonoType)]
pub enum Row {
Empty,
Var(RowVarRef),
Cons(String, Box<MonoType>, Box<Row>),
}
pub fn resolve(ty: &MonoType) -> Cow<'_, MonoType> {
if let MonoType::Var(v) = ty {
let next = match &*v.0.borrow() {
TyVarLink::Bound(inner) => Some(inner.clone()),
TyVarLink::Free { .. } => None,
};
if let Some(inner) = next {
return Cow::Owned(resolve(&inner).into_owned());
}
}
Cow::Borrowed(ty)
}
pub fn resolve_row(row: &Row) -> Cow<'_, Row> {
if let Row::Var(v) = row {
let next = match &*v.0.borrow() {
RowVarLink::Bound(inner) => Some(inner.clone()),
RowVarLink::Free { .. } => None,
};
if let Some(inner) = next {
return Cow::Owned(resolve_row(&inner).into_owned());
}
}
Cow::Borrowed(row)
}
#[derive(Clone, Debug)]
pub struct PolyType {
vars: Vec<TyVarRef>,
row_vars: Vec<RowVarRef>,
body: MonoType,
}
impl PolyType {
pub fn mono(ty: MonoType) -> PolyType {
PolyType {
vars: Vec::new(),
row_vars: Vec::new(),
body: ty,
}
}
pub(crate) fn from_vars(
vars: Vec<TyVarRef>,
row_vars: Vec<RowVarRef>,
body: MonoType,
) -> PolyType {
PolyType {
vars,
row_vars,
body,
}
}
pub fn body(&self) -> &MonoType {
&self.body
}
pub fn is_monomorphic(&self) -> bool {
self.vars.is_empty() && self.row_vars.is_empty()
}
}
impl fmt::Display for PolyType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.body, f)
}
}
pub struct TypeContext {
next_id: u64,
level: u32,
}
impl TypeContext {
pub fn new() -> Self {
TypeContext {
next_id: 0,
level: 0,
}
}
fn next_id(&mut self) -> u64 {
let id = self.next_id;
self.next_id += 1;
id
}
pub fn level(&self) -> u32 {
self.level
}
pub fn enter_level(&mut self) {
self.level += 1;
}
pub fn leave_level(&mut self) {
self.level -= 1;
}
pub fn fresh_var(&mut self) -> TyVarRef {
self.fresh_var_with_kind(Kind::Universal)
}
pub fn fresh_var_with_kind(&mut self, kind: Kind) -> TyVarRef {
TyVarRef::new(self.next_id(), self.level, kind)
}
pub fn fresh_row_var(&mut self) -> RowVarRef {
self.fresh_row_var_with_kind(BTreeSet::new())
}
pub fn fresh_row_var_with_kind(&mut self, kind: BTreeSet<String>) -> RowVarRef {
RowVarRef::new(self.next_id(), self.level, kind)
}
}
impl Default for TypeContext {
fn default() -> Self {
Self::new()
}
}
pub fn generalize(level: u32, ty: &MonoType) -> PolyType {
let mut vars = Vec::new();
let mut row_vars = Vec::new();
collect_generalizable(level, ty, &mut vars, &mut row_vars);
PolyType {
vars,
row_vars,
body: ty.clone(),
}
}
fn collect_generalizable(
level: u32,
ty: &MonoType,
vars: &mut Vec<TyVarRef>,
row_vars: &mut Vec<RowVarRef>,
) {
match &*resolve(ty) {
MonoType::Var(v) => {
if let Some(lv) = v.level() {
if lv > level && !vars.iter().any(|x| x.same(v)) {
vars.push(v.clone());
}
}
}
MonoType::Base(_) => {}
MonoType::Func(row, a, b) => {
collect_generalizable_row(level, &row, vars, row_vars);
collect_generalizable(level, &a, vars, row_vars);
collect_generalizable(level, &b, vars, row_vars);
}
MonoType::Product(ts) => {
for t in ts {
collect_generalizable(level, t, vars, row_vars);
}
}
MonoType::List(t) | MonoType::Ref(t) | MonoType::Code(t) => {
collect_generalizable(level, &t, vars, row_vars)
}
MonoType::Record(row) => collect_generalizable_row(level, &row, vars, row_vars),
MonoType::Variant(_, args) => {
for t in args {
collect_generalizable(level, t, vars, row_vars);
}
}
MonoType::InlineCmd(cs) | MonoType::BlockCmd(cs) | MonoType::MathCmd(cs) => {
for c in cs {
collect_generalizable(level, &c.ty, vars, row_vars);
for (_, lty) in &c.opt_labels {
collect_generalizable(level, lty, vars, row_vars);
}
}
}
}
}
fn collect_generalizable_row(
level: u32,
row: &Row,
vars: &mut Vec<TyVarRef>,
row_vars: &mut Vec<RowVarRef>,
) {
match &*resolve_row(row) {
Row::Empty => {}
Row::Var(v) => {
if let Some(lv) = v.level() {
if lv > level && !row_vars.iter().any(|x| x.same(v)) {
row_vars.push(v.clone());
}
}
}
Row::Cons(_, t, rest) => {
collect_generalizable(level, &t, vars, row_vars);
collect_generalizable_row(level, &rest, vars, row_vars);
}
}
}
pub fn instantiate(poly: &PolyType, level: u32) -> MonoType {
let mut var_map: HashMap<usize, MonoType> = HashMap::new();
for v in &poly.vars {
let fresh = TyVarRef::new(fresh_id(), level, v.kind());
var_map.insert(v.ptr_key(), MonoType::Var(fresh));
}
let mut row_map: HashMap<usize, Row> = HashMap::new();
for v in &poly.row_vars {
let fresh = RowVarRef::new(fresh_id(), level, v.kind());
row_map.insert(v.ptr_key(), Row::Var(fresh));
}
substitute(&poly.body, &var_map, &row_map)
}
pub(crate) fn substitute(
ty: &MonoType,
var_map: &HashMap<usize, MonoType>,
row_map: &HashMap<usize, Row>,
) -> MonoType {
match &*resolve(ty) {
MonoType::Var(v) => var_map
.get(&v.ptr_key())
.cloned()
.unwrap_or_else(|| MonoType::Var(v.clone())),
MonoType::Base(b) => MonoType::Base(*b),
MonoType::Func(row, a, b) => MonoType::Func(
Box::new(substitute_row(&row, var_map, row_map)),
Box::new(substitute(&a, var_map, row_map)),
Box::new(substitute(&b, var_map, row_map)),
),
MonoType::Product(ts) => {
MonoType::Product(ts.iter().map(|t| substitute(t, var_map, row_map)).collect())
}
MonoType::List(t) => MonoType::List(Box::new(substitute(&t, var_map, row_map))),
MonoType::Ref(t) => MonoType::Ref(Box::new(substitute(&t, var_map, row_map))),
MonoType::Code(t) => MonoType::Code(Box::new(substitute(&t, var_map, row_map))),
MonoType::Record(row) => MonoType::Record(substitute_row(&row, var_map, row_map)),
MonoType::Variant(name, args) => MonoType::Variant(
name.clone(),
args.iter()
.map(|t| substitute(t, var_map, row_map))
.collect(),
),
MonoType::InlineCmd(cs) => MonoType::InlineCmd(substitute_cmd_args(&cs, var_map, row_map)),
MonoType::BlockCmd(cs) => MonoType::BlockCmd(substitute_cmd_args(&cs, var_map, row_map)),
MonoType::MathCmd(cs) => MonoType::MathCmd(substitute_cmd_args(&cs, var_map, row_map)),
}
}
pub(crate) fn substitute_row(
row: &Row,
var_map: &HashMap<usize, MonoType>,
row_map: &HashMap<usize, Row>,
) -> Row {
match &*resolve_row(row) {
Row::Empty => Row::Empty,
Row::Var(v) => row_map
.get(&v.ptr_key())
.cloned()
.unwrap_or_else(|| Row::Var(v.clone())),
Row::Cons(label, t, rest) => Row::Cons(
label.clone(),
Box::new(substitute(&t, var_map, row_map)),
Box::new(substitute_row(&rest, var_map, row_map)),
),
}
}
fn substitute_cmd_args(
cs: &[CmdArgType],
var_map: &HashMap<usize, MonoType>,
row_map: &HashMap<usize, Row>,
) -> Vec<CmdArgType> {
cs.iter()
.map(|c| CmdArgType {
optional: c.optional,
opt_labels: c
.opt_labels
.iter()
.map(|(l, t)| (l.clone(), substitute(t, var_map, row_map)))
.collect(),
ty: substitute(&c.ty, var_map, row_map),
})
.collect()
}
pub(crate) fn ptr_key(v: &TyVarRef) -> usize {
v.ptr_key()
}
struct VarNamer {
names: HashMap<usize, String>,
next: usize,
}
impl VarNamer {
fn new() -> Self {
VarNamer {
names: HashMap::new(),
next: 0,
}
}
fn name_for(&mut self, key: usize) -> String {
if let Some(n) = self.names.get(&key) {
return n.clone();
}
let n = Self::letter(self.next);
self.next += 1;
self.names.insert(key, n.clone());
n
}
fn letter(i: usize) -> String {
let letter = (b'a' + (i % 26) as u8) as char;
let suffix = i / 26;
if suffix == 0 {
format!("'{letter}")
} else {
format!("'{letter}{suffix}")
}
}
}
fn is_atomic(ty: &MonoType) -> bool {
match ty {
MonoType::Base(_) | MonoType::Var(_) | MonoType::Record(_) => true,
MonoType::Variant(_, args) => args.is_empty(),
MonoType::Func(_, _, _)
| MonoType::Product(_)
| MonoType::List(_)
| MonoType::Ref(_)
| MonoType::Code(_)
| MonoType::InlineCmd(_)
| MonoType::BlockCmd(_)
| MonoType::MathCmd(_) => false,
}
}
fn needs_parens_as_operand(ty: &MonoType) -> bool {
matches!(ty, MonoType::Func(_, _, _) | MonoType::Product(_))
}
fn fmt_operand(ty: &MonoType, f: &mut fmt::Formatter<'_>, namer: &mut VarNamer) -> fmt::Result {
if needs_parens_as_operand(&resolve(ty)) {
f.write_str("(")?;
fmt_mono(ty, f, namer)?;
f.write_str(")")
} else {
fmt_mono(ty, f, namer)
}
}
fn fmt_mono(ty: &MonoType, f: &mut fmt::Formatter<'_>, namer: &mut VarNamer) -> fmt::Result {
let ty = resolve(ty);
match &*ty {
MonoType::Var(v) => write!(f, "{}", namer.name_for(v.ptr_key())),
MonoType::Base(b) => write!(f, "{b}"),
MonoType::Func(row, dom, cod) => {
fmt_func_row(row, f, namer)?;
fmt_operand(dom, f, namer)?;
f.write_str(" -> ")?;
let rcod = resolve(cod);
if is_atomic(&rcod) {
fmt_mono(cod, f, namer)
} else {
f.write_str("(")?;
fmt_mono(cod, f, namer)?;
f.write_str(")")
}
}
MonoType::Product(ts) => {
for (i, t) in ts.iter().enumerate() {
if i > 0 {
f.write_str(" * ")?;
}
fmt_operand(t, f, namer)?;
}
Ok(())
}
MonoType::List(t) => fmt_postfix(t, "list", f, namer),
MonoType::Ref(t) => fmt_postfix(t, "ref", f, namer),
MonoType::Code(t) => fmt_postfix(t, "code", f, namer),
MonoType::Record(row) => fmt_row(row, f, namer),
MonoType::Variant(name, args) => match args.as_slice() {
[] => write!(f, "{name}"),
[one] => fmt_postfix(one, name, f, namer),
many => {
f.write_str("(")?;
for (i, t) in many.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
fmt_mono(t, f, namer)?;
}
write!(f, ") {name}")
}
},
MonoType::InlineCmd(cs) => fmt_cmd(cs, "inline-cmd", f, namer),
MonoType::BlockCmd(cs) => fmt_cmd(cs, "block-cmd", f, namer),
MonoType::MathCmd(cs) => fmt_cmd(cs, "math-cmd", f, namer),
}
}
fn fmt_postfix(
operand: &MonoType,
suffix: &str,
f: &mut fmt::Formatter<'_>,
namer: &mut VarNamer,
) -> fmt::Result {
fmt_operand(operand, f, namer)?;
write!(f, " {suffix}")
}
fn fmt_cmd(
cs: &[CmdArgType],
suffix: &str,
f: &mut fmt::Formatter<'_>,
namer: &mut VarNamer,
) -> fmt::Result {
f.write_str("[")?;
for (i, c) in cs.iter().enumerate() {
if i > 0 {
f.write_str("; ")?;
}
fmt_opt_labels(&c.opt_labels, f, namer)?;
fmt_mono(&c.ty, f, namer)?;
if c.optional {
f.write_str("?")?;
}
}
write!(f, "] {suffix}")
}
fn fmt_opt_labels(
labels: &[(String, MonoType)],
f: &mut fmt::Formatter<'_>,
namer: &mut VarNamer,
) -> fmt::Result {
if labels.is_empty() {
return Ok(());
}
let mut fields = labels.to_vec();
fields.sort_by(|a, b| a.0.cmp(&b.0));
f.write_str("?(")?;
for (i, (label, ty)) in fields.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{label} : ")?;
fmt_mono(ty, f, namer)?;
}
f.write_str(") ")
}
fn fmt_func_row(row: &Row, f: &mut fmt::Formatter<'_>, namer: &mut VarNamer) -> fmt::Result {
let mut fields: Vec<(String, MonoType)> = Vec::new();
let mut cur = resolve_row(row).into_owned();
let tail_name = loop {
match cur {
Row::Empty => break None,
Row::Var(v) => break Some(namer.name_for(v.ptr_key())),
Row::Cons(label, ty, rest) => {
fields.push((label, *ty));
cur = resolve_row(&rest).into_owned();
}
}
};
if fields.is_empty() && tail_name.is_none() {
return Ok(());
}
fields.sort_by(|a, b| a.0.cmp(&b.0));
f.write_str("?(")?;
for (i, (label, ty)) in fields.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{label} : ")?;
fmt_mono(ty, f, namer)?;
}
if let Some(name) = tail_name {
if !fields.is_empty() {
f.write_str(" ")?;
}
write!(f, "| ?{name}")?;
}
f.write_str(") ")
}
fn fmt_row(row: &Row, f: &mut fmt::Formatter<'_>, namer: &mut VarNamer) -> fmt::Result {
let mut fields: Vec<(String, MonoType)> = Vec::new();
let mut cur = resolve_row(row).into_owned();
let tail_name = loop {
match cur {
Row::Empty => break None,
Row::Var(v) => break Some(namer.name_for(v.ptr_key())),
Row::Cons(label, ty, rest) => {
fields.push((label, *ty));
cur = resolve_row(&rest).into_owned();
}
}
};
fields.sort_by(|a, b| a.0.cmp(&b.0));
f.write_str("(| ")?;
for (i, (label, ty)) in fields.iter().enumerate() {
if i > 0 {
f.write_str("; ")?;
}
write!(f, "{label} : ")?;
fmt_mono(ty, f, namer)?;
}
if let Some(name) = tail_name {
if !fields.is_empty() {
f.write_str(" ")?;
}
write!(f, "| {name}")?;
}
f.write_str(" |)")
}
impl fmt::Display for MonoType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut namer = VarNamer::new();
fmt_mono(self, f, &mut namer)
}
}
impl fmt::Display for Row {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut namer = VarNamer::new();
fmt_row(self, f, &mut namer)
}
}