− 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};