use std::collections::HashMap;
use uuid::Uuid;
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub enum Space {
ParaBreak,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub enum ListType {
Unordered,
Ordered,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub enum Value {
/// unbound variable, referenced by UUID to make renaming easier
UnboundVar(Uuid),
/// bound variable, referenced using [De Bruijn indices](https://en.wikipedia.org/wiki/De_Bruijn_index),
/// starting at 0
BoundVar(usize),
/// tagged elements, no order of evaluation is guaranteed, arguments are lazily evaluated
Tag(Uuid, Vec<Value>),
Space(Space),
List(ListType, Vec<Value>),
}
/** document data structure
## header stuff
we omit any document type tags of older formats, because we don't want to
differentiate that much here, because it would make parsing cumbersome.
we rather differentiate directly via indexes. Historically, type tags were
vastly different per format nonetheless, making them slightly incompatible.
**/
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Document {
#[serde(flatten)]
ident: Ident,
unbound_vars: HashMap<Uuid, String>,
toplevel: Vec<Value>,
}
pub struct Context<'a> {
unbound_vars: &'a HashMap<Uuid, String>,
bound_vars: Vec<String>,
}
impl Document {
pub fn ctx(&self) -> Context<'_> {
Context {
unbound_vars: &self.unbound_vars,
bound_vars: Vec::new(),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("unbound variable not found: {0}")]
UnboundVarNotFound(Uuid),
#[error("bound variable not found")]
BoundVarNotFound,
}
impl<'a> Context<'a> {
pub fn lookup_unbound_var(&self, id: &Uuid) -> Result<&'a str, Error> {
}
pub fn on_bound_var<F, R>(&mut self, name: String, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
#[cfg(debug_assertions)]
let name2 = name.clone();
self.bound_vars.push(name);
let tmp = catch_unwind(AssertUnwindSafe(|| f(self)));
#[allow(unused_variables)]
let tmp2 = self.bound_vars.pop();
match tmp {
Ok(x) => {
#[cfg(debug_assertions)]
if tmp2 != Some(name2) {
panic!("mismatching bound vars");
}
x
Err(y) => resume_unwind(y),
}
}
pub fn lookup_bound_var(&self, id: usize) -> Result<&str, Error> {
}
}
/// Formatter-oriented serialization, because we usually define that per formatter,
/// and the structures which are serialized stay mostly the same.
pub trait Serializer: Sized {
type Ok;
type Error: From<Error> + std::error::Error;
fn serialize_value(self, v: &Value, ctx: &mut Context<'_>) -> Result<Self::Ok, Self::Error>;
fn serialize_document(self, d: &Document) -> Result<Self::Ok, Self::Error>;
}
self.bound_vars
.get(self.bound_vars.len() - 1 - id)
.map(|i| i.as_str())
.ok_or(Error::BoundVarNotFound)
}
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
self.unbound_vars
.get(id)
.map(|i| i.as_str())
.ok_or_else(|| Error::UnboundVarNotFound(*id))
use crate::Ident;
use serde::{Deserialize, Serialize};