use crate::{
datum::Datum,
error::{Error, Result},
expr::{Expr, NumberLiteral, QuoteMode},
id::Symbol,
ref_id::{ContentId, Coordinate, HandleId, Ref},
};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OpKey {
pub namespace: Symbol,
pub name: Symbol,
pub version: u16,
}
impl OpKey {
pub fn new(namespace: Symbol, name: Symbol, version: u16) -> Self {
Self {
namespace,
name,
version,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Term {
Literal(Datum),
Ref(Ref),
Local(Symbol),
Let {
name: Symbol,
value: Box<Term>,
body: Box<Term>,
},
Op {
op: OpKey,
input: Box<Term>,
},
Call {
target: Box<Term>,
args: Vec<Term>,
},
Seq(Vec<Term>),
Quote {
mode: QuoteMode,
datum: Datum,
},
Annotated {
term: Box<Term>,
annotations: Vec<(Symbol, Datum)>,
},
Extension {
tag: Symbol,
payload: Datum,
},
}
impl Term {
pub fn lower(expr: Expr) -> Result<Self> {
match expr {
Expr::Nil => Ok(Self::Literal(Datum::Nil)),
Expr::Bool(value) => Ok(Self::Literal(Datum::Bool(value))),
Expr::Number(value) => Ok(Self::Literal(Datum::Number(value))),
Expr::Symbol(symbol) => Ok(Self::Ref(Ref::Symbol(symbol))),
Expr::Local(symbol) => Ok(Self::Local(symbol)),
Expr::String(value) => Ok(Self::Literal(Datum::String(value))),
Expr::Bytes(value) => Ok(Self::Literal(Datum::Bytes(value))),
Expr::List(items) => data_literal(Expr::List(items)),
Expr::Vector(items) => data_literal(Expr::Vector(items)),
Expr::Map(entries) => data_literal(Expr::Map(entries)),
Expr::Set(items) => data_literal(Expr::Set(items)),
Expr::Call { operator, args } => Ok(Self::Call {
target: Box::new(Self::lower(*operator)?),
args: lower_terms(args)?,
}),
Expr::Infix {
operator,
left,
right,
} => Ok(Self::Call {
target: Box::new(Self::Ref(Ref::Symbol(operator))),
args: vec![Self::lower(*left)?, Self::lower(*right)?],
}),
Expr::Prefix { operator, arg } | Expr::Postfix { operator, arg } => Ok(Self::Call {
target: Box::new(Self::Ref(Ref::Symbol(operator))),
args: vec![Self::lower(*arg)?],
}),
Expr::Block(items) => Ok(Self::Seq(lower_terms(items)?)),
Expr::Quote { mode, expr } => Ok(Self::Quote {
mode,
datum: Datum::try_from(*expr)?,
}),
Expr::Annotated { expr, annotations } => {
let annotations = annotations
.into_iter()
.map(|(name, expr)| Ok((name, Datum::try_from(expr)?)))
.collect::<Result<Vec<_>>>()?;
Ok(Self::Annotated {
term: Box::new(Self::lower(*expr)?),
annotations,
})
}
Expr::Extension { tag, payload } => Ok(Self::Extension {
tag,
payload: Datum::try_from(*payload)?,
}),
}
}
}
impl TryFrom<Expr> for Term {
type Error = Error;
fn try_from(expr: Expr) -> Result<Self> {
Self::lower(expr)
}
}
impl From<Term> for Expr {
fn from(term: Term) -> Self {
match term {
Term::Literal(datum) => Self::from(datum),
Term::Ref(reference) => ref_to_expr(reference),
Term::Local(symbol) => Self::Local(symbol),
Term::Let { name, value, body } => Self::Extension {
tag: core_symbol("term-let"),
payload: Box::new(Self::Map(vec![
(Self::Symbol(Symbol::new("name")), Self::Symbol(name)),
(Self::Symbol(Symbol::new("value")), Self::from(*value)),
(Self::Symbol(Symbol::new("body")), Self::from(*body)),
])),
},
Term::Op { op, input } => Self::Extension {
tag: core_symbol("term-op"),
payload: Box::new(Self::Map(vec![
(
Self::Symbol(Symbol::new("op")),
Self::from(op_key_datum(op)),
),
(Self::Symbol(Symbol::new("input")), Self::from(*input)),
])),
},
Term::Call { target, args } => Self::Call {
operator: Box::new(Self::from(*target)),
args: args.into_iter().map(Self::from).collect(),
},
Term::Seq(items) => Self::Block(items.into_iter().map(Self::from).collect()),
Term::Quote { mode, datum } => Self::Quote {
mode,
expr: Box::new(Self::from(datum)),
},
Term::Annotated { term, annotations } => Self::Annotated {
expr: Box::new(Self::from(*term)),
annotations: annotations
.into_iter()
.map(|(name, datum)| (name, Self::from(datum)))
.collect(),
},
Term::Extension { tag, payload } => Self::Extension {
tag,
payload: Box::new(Self::from(payload)),
},
}
}
}
fn lower_terms(exprs: Vec<Expr>) -> Result<Vec<Term>> {
exprs.into_iter().map(Term::lower).collect()
}
fn data_literal(expr: Expr) -> Result<Term> {
Datum::try_from(expr).map(Term::Literal)
}
fn ref_to_expr(reference: Ref) -> Expr {
match reference {
Ref::Symbol(symbol) => Expr::Symbol(symbol),
other => Expr::from(ref_datum(other)),
}
}
fn ref_datum(reference: Ref) -> Datum {
match reference {
Ref::Symbol(symbol) => Datum::Node {
tag: core_symbol("ref"),
fields: vec![
(Symbol::new("kind"), Datum::Symbol(core_symbol("symbol"))),
(Symbol::new("symbol"), Datum::Symbol(symbol)),
],
},
Ref::Content(content) => Datum::Node {
tag: core_symbol("ref"),
fields: vec![
(Symbol::new("kind"), Datum::Symbol(core_symbol("content"))),
(Symbol::new("content"), content_id_datum(content)),
],
},
Ref::Handle(handle) => Datum::Node {
tag: core_symbol("ref"),
fields: vec![
(Symbol::new("kind"), Datum::Symbol(core_symbol("handle"))),
(Symbol::new("id"), handle_id_datum(handle)),
],
},
Ref::Coord(coordinate) => coordinate_datum(coordinate),
}
}
fn coordinate_datum(coordinate: Coordinate) -> Datum {
Datum::Node {
tag: core_symbol("ref"),
fields: vec![
(Symbol::new("kind"), Datum::Symbol(core_symbol("coord"))),
(Symbol::new("space"), Datum::Symbol(coordinate.space)),
(Symbol::new("ordinal"), content_id_datum(coordinate.ordinal)),
],
}
}
fn content_id_datum(content: ContentId) -> Datum {
Datum::Node {
tag: core_symbol("content-id"),
fields: vec![
(Symbol::new("algorithm"), Datum::Symbol(content.algorithm)),
(Symbol::new("bytes"), Datum::Bytes(content.bytes.to_vec())),
],
}
}
fn handle_id_datum(handle: HandleId) -> Datum {
Datum::Bytes(handle.0.to_be_bytes().to_vec())
}
fn op_key_datum(op: OpKey) -> Datum {
Datum::Node {
tag: core_symbol("op-key"),
fields: vec![
(Symbol::new("namespace"), Datum::Symbol(op.namespace)),
(Symbol::new("name"), Datum::Symbol(op.name)),
(
Symbol::new("version"),
Datum::Number(NumberLiteral {
domain: core_symbol("u16"),
canonical: op.version.to_string(),
}),
),
],
}
}
fn core_symbol(name: &str) -> Symbol {
Symbol::qualified("core", name)
}