#[cfg(all(feature = "codec-lisp", feature = "shape"))]
use std::sync::Arc;
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
use sim_kernel::{Cx, Expr, Factory, Object, ObjectCompat, Shape, Symbol, Value};
#[cfg(any(feature = "codec-binary", feature = "codec-lisp"))]
use sim_kernel::{Lib, Result};
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
use sim_shape::{AnyShape, CaptureShape, ListShape};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ReexportKind {
Class,
Function,
Macro,
Shape,
Codec,
NumberDomain,
Value,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ReexportSpec {
pub(crate) kind: ReexportKind,
pub(crate) export: sim_kernel::Symbol,
pub(crate) target: sim_kernel::Symbol,
}
impl ReexportSpec {
pub fn new(kind: ReexportKind, export: sim_kernel::Symbol, target: sim_kernel::Symbol) -> Self {
Self {
kind,
export,
target,
}
}
pub fn kind(&self) -> ReexportKind {
self.kind
}
pub fn export(&self) -> &sim_kernel::Symbol {
&self.export
}
pub fn target(&self) -> &sim_kernel::Symbol {
&self.target
}
}
#[cfg(any(
feature = "codec-binary",
all(feature = "codec-lisp", not(feature = "shape"))
))]
pub(crate) struct ReexportLib {
manifest: sim_kernel::LibManifest,
exports: Vec<ReexportSpec>,
}
#[cfg(any(
feature = "codec-binary",
all(feature = "codec-lisp", not(feature = "shape"))
))]
impl ReexportLib {
pub(crate) fn new(manifest: sim_kernel::LibManifest, exports: Vec<ReexportSpec>) -> Self {
Self { manifest, exports }
}
}
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct SourceMacroSpec {
pub(crate) symbol: sim_kernel::Symbol,
pub(crate) fixed_params: Vec<sim_kernel::Symbol>,
pub(crate) rest_param: Option<sim_kernel::Symbol>,
pub(crate) template: sim_kernel::Expr,
}
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
#[derive(Clone)]
pub struct SourceTemplateMacro {
symbol: Symbol,
syntax_shape: Arc<dyn Shape>,
template: Expr,
parser_trusted: bool,
}
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
impl SourceTemplateMacro {
pub fn new(symbol: Symbol, syntax_shape: Arc<dyn Shape>, template: Expr) -> Self {
Self {
symbol,
syntax_shape,
template,
parser_trusted: false,
}
}
pub fn symbol(&self) -> Symbol {
self.symbol.clone()
}
pub fn syntax_shape(&self) -> Arc<dyn Shape> {
self.syntax_shape.clone()
}
pub fn parser_trusted(&self) -> bool {
self.parser_trusted
}
pub fn expand(&self, _input: Expr, captures: sim_shape::Bindings) -> Result<Expr> {
crate::lisp_source::template::instantiate_macro_template(&self.template, &captures)
}
}
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
impl Object for SourceTemplateMacro {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok(format!("#<source-macro {}>", self.symbol))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
impl ObjectCompat for SourceTemplateMacro {
fn class(&self, cx: &mut Cx) -> Result<sim_kernel::ClassRef> {
let symbol = Symbol::qualified("core", "Macro");
if let Some(value) = cx.registry().class_by_symbol(&symbol) {
return Ok(value.clone());
}
cx.factory()
.class_stub(sim_kernel::CORE_MACRO_CLASS_ID, symbol)
}
fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
Ok(Expr::Symbol(self.symbol.clone()))
}
fn as_table(&self, cx: &mut Cx) -> Result<sim_kernel::TableRef> {
let shape = self.syntax_shape();
let doc = shape.describe(cx)?;
let mut entries = vec![
(
Symbol::new("symbol"),
cx.factory().string(self.symbol.to_string())?,
),
(Symbol::new("syntax-shape"), cx.factory().string(doc.name)?),
(
Symbol::new("parser-trusted"),
cx.factory().bool(self.parser_trusted)?,
),
];
for (index, detail) in doc.details.into_iter().enumerate() {
entries.push((
Symbol::qualified("syntax-detail", index.to_string()),
cx.factory().string(detail)?,
));
}
cx.factory().table(entries)
}
}
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
pub(crate) struct SourceLib {
manifest: sim_kernel::LibManifest,
exports: Vec<ReexportSpec>,
macros: Vec<SourceMacroSpec>,
}
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
impl SourceLib {
pub(crate) fn new(
manifest: sim_kernel::LibManifest,
exports: Vec<ReexportSpec>,
macros: Vec<SourceMacroSpec>,
) -> Self {
Self {
manifest,
exports,
macros,
}
}
}
#[cfg(any(feature = "codec-binary", feature = "codec-lisp"))]
fn link_reexport(linker: &mut sim_kernel::Linker<'_>, export: &ReexportSpec) -> Result<()> {
match export.kind {
ReexportKind::Class => {
let value = linker
.registry()
.class_by_symbol(&export.target)
.cloned()
.ok_or(sim_kernel::Error::UnknownClass {
class: export.target.clone(),
})?;
linker.class_value(export.export.clone(), value)?;
}
ReexportKind::Function => {
let value = linker
.registry()
.function_by_symbol(&export.target)
.cloned()
.ok_or(sim_kernel::Error::UnknownFunction {
function: export.target.clone(),
})?;
linker.function_value(export.export.clone(), value)?;
}
ReexportKind::Macro => {
let value = linker
.registry()
.macro_by_symbol(&export.target)
.cloned()
.ok_or(sim_kernel::Error::UnknownSymbol {
symbol: export.target.clone(),
})?;
linker.macro_value(export.export.clone(), value)?;
}
ReexportKind::Shape => {
let value = linker
.registry()
.shape_by_symbol(&export.target)
.cloned()
.ok_or(sim_kernel::Error::UnknownSymbol {
symbol: export.target.clone(),
})?;
linker.shape_value(export.export.clone(), value)?;
}
ReexportKind::Codec => {
let value = linker
.registry()
.codec_by_symbol(&export.target)
.cloned()
.ok_or(sim_kernel::Error::UnknownSymbol {
symbol: export.target.clone(),
})?;
linker.codec_value(export.export.clone(), value)?;
}
ReexportKind::NumberDomain => {
let value = linker
.registry()
.number_domain_by_symbol(&export.target)
.cloned()
.ok_or(sim_kernel::Error::UnknownSymbol {
symbol: export.target.clone(),
})?;
linker.number_domain_value(export.export.clone(), value)?;
}
ReexportKind::Value => {
let value = linker
.registry()
.value_by_symbol(&export.target)
.cloned()
.ok_or(sim_kernel::Error::UnknownSymbol {
symbol: export.target.clone(),
})?;
linker.value(export.export.clone(), value)?;
}
}
Ok(())
}
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
impl Lib for SourceLib {
fn manifest(&self) -> sim_kernel::LibManifest {
self.manifest.clone()
}
fn load(
&self,
_cx: &mut sim_kernel::LoadCx,
linker: &mut sim_kernel::Linker<'_>,
) -> Result<()> {
for export in &self.exports {
if matches!(export.kind, ReexportKind::Macro)
&& export.export == export.target
&& self.macros.iter().any(|mac| mac.symbol == export.export)
{
continue;
}
link_reexport(linker, export)?;
}
for mac in &self.macros {
let syntax_shape = positional_macro_shape(
mac.symbol.clone(),
&mac.fixed_params,
mac.rest_param.as_ref(),
);
let value = macro_value(Arc::new(SourceTemplateMacro::new(
mac.symbol.clone(),
syntax_shape,
mac.template.clone(),
)));
linker.macro_value(mac.symbol.clone(), value)?;
}
Ok(())
}
}
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
fn macro_value(mac: Arc<SourceTemplateMacro>) -> Value {
sim_kernel::DefaultFactory
.opaque(mac)
.expect("source macro object should always be boxable")
}
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
fn positional_macro_shape(head: Symbol, fixed: &[Symbol], rest: Option<&Symbol>) -> Arc<dyn Shape> {
let fixed_tail = fixed
.iter()
.cloned()
.map(|name| Arc::new(CaptureShape::new(name, Arc::new(AnyShape))) as Arc<dyn Shape>)
.collect::<Vec<_>>();
let items = std::iter::once(literal_head_shape(head))
.chain(fixed_tail)
.collect::<Vec<_>>();
match rest {
Some(rest) => Arc::new(ListShape::with_rest(
items,
Arc::new(CaptureShape::new(rest.clone(), Arc::new(AnyShape))),
)),
None => Arc::new(ListShape::new(items)),
}
}
#[cfg(all(feature = "codec-lisp", feature = "shape"))]
fn literal_head_shape(symbol: Symbol) -> Arc<dyn Shape> {
Arc::new(sim_shape::ExactExprShape::new(Expr::Symbol(symbol)))
}
#[cfg(any(
feature = "codec-binary",
all(feature = "codec-lisp", not(feature = "shape"))
))]
impl Lib for ReexportLib {
fn manifest(&self) -> sim_kernel::LibManifest {
self.manifest.clone()
}
fn load(
&self,
_cx: &mut sim_kernel::LoadCx,
linker: &mut sim_kernel::Linker<'_>,
) -> Result<()> {
for export in &self.exports {
link_reexport(linker, export)?;
}
Ok(())
}
}