use std::sync::Arc;
use sim_kernel::{
Cx, Datum, DefaultFactory, Factory, LocatedExpr, LocatedExprTree, ReadPolicy, Result, Symbol,
Term, Value, WriteCx,
};
use crate::{
CodecDefaultDecode, CodecRuntime, DecodeLimits, DecodePosition, DecodeTarget, Input, Output,
ReadCx, encode_value_expr,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DecodedForm {
Datum(Datum),
Term(Term),
}
pub fn codec_value(codec: CodecRuntime) -> Value {
DefaultFactory
.opaque(Arc::new(codec))
.expect("codec runtime should always be boxable")
}
pub fn decode_with_codec(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
) -> Result<sim_kernel::Expr> {
decode_with_codec_and_limits(cx, symbol, input, read_policy, DecodeLimits::default())
}
pub fn decode_with_codec_and_limits(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
limits: DecodeLimits,
) -> Result<sim_kernel::Expr> {
decode_expr_with_codec_and_limits(cx, symbol, input, read_policy, limits).map(|(expr, _)| expr)
}
pub fn decode_datum_with_codec(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
) -> Result<Datum> {
decode_datum_with_codec_and_limits(cx, symbol, input, read_policy, DecodeLimits::default())
}
pub fn decode_datum_with_codec_and_limits(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
limits: DecodeLimits,
) -> Result<Datum> {
let (expr, _) = decode_expr_with_codec_and_limits(cx, symbol, input, read_policy, limits)?;
Datum::try_from(expr)
}
pub fn decode_term_with_codec(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
) -> Result<Term> {
decode_term_with_codec_and_limits(cx, symbol, input, read_policy, DecodeLimits::default())
}
pub fn decode_term_with_codec_and_limits(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
limits: DecodeLimits,
) -> Result<Term> {
let (expr, default_decode) =
decode_expr_with_codec_and_limits(cx, symbol, input, read_policy, limits)?;
term_from_expr(default_decode, expr)
}
pub fn decode_default_with_codec(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
position: DecodePosition,
) -> Result<DecodedForm> {
decode_default_with_codec_and_limits(
cx,
symbol,
input,
read_policy,
position,
DecodeLimits::default(),
)
}
pub fn decode_default_with_codec_and_limits(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
position: DecodePosition,
limits: DecodeLimits,
) -> Result<DecodedForm> {
let (expr, default_decode) =
decode_expr_with_codec_and_limits(cx, symbol, input, read_policy, limits)?;
match default_decode.target_for(position) {
DecodeTarget::Datum => Datum::try_from(expr).map(DecodedForm::Datum),
DecodeTarget::Term => term_from_expr(default_decode, expr).map(DecodedForm::Term),
}
}
fn decode_expr_with_codec_and_limits(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
limits: DecodeLimits,
) -> Result<(sim_kernel::Expr, CodecDefaultDecode)> {
let value = cx.resolve_codec(symbol)?;
let codec =
value
.object()
.downcast_ref::<CodecRuntime>()
.ok_or(sim_kernel::Error::TypeMismatch {
expected: "codec",
found: "non-codec",
})?;
let mut read_cx = ReadCx {
cx,
codec: codec.id,
read_policy,
limits,
};
codec
.decode(&mut read_cx, input)
.map(|expr| (expr, codec.default_decode))
}
pub fn encode_with_codec(
cx: &mut Cx,
symbol: &Symbol,
expr: &sim_kernel::Expr,
options: sim_kernel::EncodeOptions,
) -> Result<Output> {
let value = cx.resolve_codec(symbol)?;
let codec =
value
.object()
.downcast_ref::<CodecRuntime>()
.ok_or(sim_kernel::Error::TypeMismatch {
expected: "codec",
found: "non-codec",
})?;
let mut write_cx = WriteCx {
cx,
codec: codec.id,
options,
};
codec.encode(&mut write_cx, expr)
}
pub fn encode_datum_with_codec(
cx: &mut Cx,
symbol: &Symbol,
datum: &Datum,
options: sim_kernel::EncodeOptions,
) -> Result<Output> {
encode_with_codec(cx, symbol, &sim_kernel::Expr::from(datum.clone()), options)
}
pub fn encode_term_with_codec(
cx: &mut Cx,
symbol: &Symbol,
term: &Term,
options: sim_kernel::EncodeOptions,
) -> Result<Output> {
encode_with_codec(cx, symbol, &sim_kernel::Expr::from(term.clone()), options)
}
pub fn encode_value_with_codec(
cx: &mut Cx,
symbol: &Symbol,
value: &Value,
options: sim_kernel::EncodeOptions,
) -> Result<Output> {
let codec_value = cx.resolve_codec(symbol)?;
let codec = codec_value.object().downcast_ref::<CodecRuntime>().ok_or(
sim_kernel::Error::TypeMismatch {
expected: "codec",
found: "non-codec",
},
)?;
let mut write_cx = WriteCx {
cx,
codec: codec.id,
options,
};
let expr = encode_value_expr(&mut write_cx, value)?;
codec.encode(&mut write_cx, &expr)
}
pub fn decode_located_with_codec(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
source_id: impl Into<String>,
) -> Result<LocatedExpr> {
decode_located_with_codec_and_limits(
cx,
symbol,
input,
read_policy,
source_id,
DecodeLimits::default(),
)
}
pub fn decode_located_with_codec_and_limits(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
source_id: impl Into<String>,
limits: DecodeLimits,
) -> Result<LocatedExpr> {
let value = cx.resolve_codec(symbol)?;
let codec =
value
.object()
.downcast_ref::<CodecRuntime>()
.ok_or(sim_kernel::Error::TypeMismatch {
expected: "codec",
found: "non-codec",
})?;
let mut read_cx = ReadCx {
cx,
codec: codec.id,
read_policy,
limits,
};
codec.decode_located(&mut read_cx, input, source_id.into())
}
pub fn encode_located_with_codec(
cx: &mut Cx,
symbol: &Symbol,
expr: &LocatedExpr,
options: sim_kernel::EncodeOptions,
) -> Result<Output> {
let value = cx.resolve_codec(symbol)?;
let codec =
value
.object()
.downcast_ref::<CodecRuntime>()
.ok_or(sim_kernel::Error::TypeMismatch {
expected: "codec",
found: "non-codec",
})?;
let mut write_cx = WriteCx {
cx,
codec: codec.id,
options,
};
codec.encode_located(&mut write_cx, expr)
}
pub fn encode_tree_with_codec(
cx: &mut Cx,
symbol: &Symbol,
expr: &LocatedExprTree,
options: sim_kernel::EncodeOptions,
) -> Result<Output> {
let value = cx
.registry()
.codec_by_symbol(symbol)
.cloned()
.ok_or_else(|| sim_kernel::Error::Eval(format!("unknown codec {}", symbol)))?;
let codec = value
.object()
.as_any()
.downcast_ref::<CodecRuntime>()
.ok_or_else(|| sim_kernel::Error::Eval(format!("{} is not a codec runtime", symbol)))?;
let mut write_cx = WriteCx {
cx,
codec: codec.id,
options,
};
codec.encode_tree(&mut write_cx, expr)
}
pub fn decode_tree_with_codec(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
source_id: impl Into<String>,
) -> Result<LocatedExprTree> {
decode_tree_with_codec_and_limits(
cx,
symbol,
input,
read_policy,
source_id,
DecodeLimits::default(),
)
}
pub fn decode_tree_with_codec_and_limits(
cx: &mut Cx,
symbol: &Symbol,
input: Input,
read_policy: ReadPolicy,
source_id: impl Into<String>,
limits: DecodeLimits,
) -> Result<LocatedExprTree> {
let value = cx.resolve_codec(symbol)?;
let codec =
value
.object()
.downcast_ref::<CodecRuntime>()
.ok_or(sim_kernel::Error::TypeMismatch {
expected: "codec",
found: "non-codec",
})?;
let mut read_cx = ReadCx {
cx,
codec: codec.id,
read_policy,
limits,
};
codec.decode_tree(&mut read_cx, input, source_id.into())
}
fn term_from_expr(default_decode: CodecDefaultDecode, expr: sim_kernel::Expr) -> Result<Term> {
match default_decode {
CodecDefaultDecode::Datum => Term::lower(expr),
CodecDefaultDecode::TermInEvalDatumOtherwise => Term::lower(lower_eval_surface(expr)),
}
}
fn lower_eval_surface(expr: sim_kernel::Expr) -> sim_kernel::Expr {
use sim_kernel::Expr;
match expr {
Expr::List(items) if items.len() > 1 => {
let mut items = items
.into_iter()
.map(lower_eval_surface)
.collect::<Vec<_>>();
let operator = Box::new(items.remove(0));
Expr::Call {
operator,
args: items,
}
}
Expr::List(items) => Expr::List(items.into_iter().map(lower_eval_surface).collect()),
Expr::Vector(items) => Expr::Vector(items.into_iter().map(lower_eval_surface).collect()),
Expr::Map(entries) => Expr::Map(
entries
.into_iter()
.map(|(key, value)| (lower_eval_surface(key), lower_eval_surface(value)))
.collect(),
),
Expr::Set(items) => Expr::Set(items.into_iter().map(lower_eval_surface).collect()),
Expr::Block(items) => Expr::Block(items.into_iter().map(lower_eval_surface).collect()),
Expr::Quote { mode, expr } => Expr::Quote { mode, expr },
Expr::Annotated { expr, annotations } => Expr::Annotated {
expr: Box::new(lower_eval_surface(*expr)),
annotations: annotations
.into_iter()
.map(|(name, value)| (name, lower_eval_surface(value)))
.collect(),
},
Expr::Extension { tag, payload } => Expr::Extension {
tag,
payload: Box::new(lower_eval_surface(*payload)),
},
other => other,
}
}