ast-description-lang 0.4.0

A language to describe your language's AST
Documentation
use super::{
  super::{
    raw::{Ast as RawAst, NodeDef, NodeKindDef},
    Modifier,
  },
  Ast, Node, NodeKind, TaggedNodeKind,
};
use crate::{Ident, Specs};
use std::{cell::RefCell, collections::hash_map::HashMap, marker::PhantomData};
use thiserror::Error;

impl<'a> RawAst<'a> {
  pub fn transform(&'a self, specs: &'a Specs<'a>) -> Result<Ast<'a>, Error> {
    let validator = Validator::new(self, specs);
    Ok(Ast(
      self
        .iter()
        .map(|entry| -> Result<_, _> {
          validator
            .validate_node_def(&entry.node)
            .map(|node_def| Node {
              ident: entry.ident,
              kind: node_from_def(node_def.kind),
              tag: node_def.tag,
            })
        })
        .collect::<Result<_, _>>()?,
    ))
  }
}

fn node_from_def(node_def_kind: ValidNodeKind) -> NodeKind {
  match node_def_kind {
    ValidNodeKind::Node(ident) => NodeKind::Node(ident),
    ValidNodeKind::Modified(inner, modifier) => {
      NodeKind::Modified(Box::new(node_from_def(inner.kind)), modifier)
    }
    ValidNodeKind::StaticToken(ident) => NodeKind::StaticToken(ident),
    ValidNodeKind::DynamicToken(ident) => NodeKind::DynamicToken(ident),
    ValidNodeKind::Group { nodes, inline } => NodeKind::Group {
      nodes: (nodes
        .into_iter()
        .map(|node| TaggedNodeKind {
          kind: node_from_def(node.kind),
          tag: node.tag,
        })
        .collect::<Vec<_>>()),
      inline,
    },
    ValidNodeKind::Choice { nodes, inline } => NodeKind::Choice {
      nodes: nodes
        .into_iter()
        .map(|node| TaggedNodeKind {
          kind: node_from_def(node.kind),
          tag: node.tag,
        })
        .collect::<Vec<_>>(),
      inline,
    },
    ValidNodeKind::Delimited(inner, delimiter) => {
      NodeKind::Delimited(Box::new(node_from_def(inner.kind)), delimiter)
    }
    ValidNodeKind::Todo => NodeKind::Todo,
    ValidNodeKind::End => NodeKind::End,
  }
}

struct Validator<'v, 'ast: 'v, 's: 'ast> {
  ast: &'v RawAst<'ast>,
  specs: &'v Specs<'s>,
  cache: RefCell<HashMap<&'v NodeKindDef<'ast>, ValidNodeKind<'ast>>>,
  _marker: PhantomData<&'s ()>,
}

impl<'v, 'ast: 'v, 's: 'ast> Validator<'v, 'ast, 's> {
  fn new(ast: &'v RawAst<'ast>, specs: &'v Specs<'s>) -> Validator<'v, 'ast, 's> {
    Validator {
      ast,
      specs,
      cache: Default::default(),
      _marker: PhantomData,
    }
  }

  fn validate_node_def(&self, node_def: &'v NodeDef<'ast>) -> Result<ValidNode<'ast>, Error> {
    let cache = self.cache.borrow();
    Ok(ValidNode {
      tag: node_def.tag,
      kind: if let Some(valid_node_def) = cache.get(&node_def.kind) {
        valid_node_def.clone()
      } else {
        drop(cache);
        let valid_node = match &node_def.kind {
          NodeKindDef::Simple(ident) => {
            let ident = *ident;
            if self.ast.get(ident).is_some() {
              ValidNodeKind::Node(ident)
            } else if self.specs.static_tokens.keys().copied().any(|t| t == ident) {
              ValidNodeKind::StaticToken(ident)
            } else if self
              .specs
              .dynamic_tokens
              .keys()
              .copied()
              .any(|t| t == ident)
            {
              ValidNodeKind::DynamicToken(ident)
            } else {
              return Err(Error::UnknownIdent(String::from(ident.0)));
            }
          }
          NodeKindDef::Modified(inner, modifier) => {
            ValidNodeKind::Modified(Box::new(self.validate_node_def(inner)?), *modifier)
          }
          NodeKindDef::Delimiter(inner, delimiter) => {
            if self.specs.delimiters.contains_key(delimiter) {
              ValidNodeKind::Delimited(Box::new(self.validate_node_def(inner)?), *delimiter)
            } else {
              return Err(Error::UnknownDelim(String::from(delimiter.0)));
            }
          }
          NodeKindDef::Group { nodes, inline } => ValidNodeKind::Group {
            nodes: nodes
              .iter()
              .map(|node| self.validate_node_def(node))
              .collect::<Result<_, _>>()?,
            inline: *inline,
          },
          NodeKindDef::Choice {
            first,
            second,
            inline,
          } => ValidNodeKind::Choice {
            nodes: self.validate_choices(first, second, Vec::with_capacity(1))?,
            inline: *inline,
          },
          NodeKindDef::Todo => ValidNodeKind::Todo,
          NodeKindDef::End => ValidNodeKind::End,
        };
        self
          .cache
          .borrow_mut()
          .insert(&node_def.kind, valid_node.clone());
        valid_node
      },
    })
  }

  fn validate_choices(
    &self,
    first: &'v NodeDef<'ast>,
    second: &'v NodeDef<'ast>,
    mut processed: Vec<ValidNode<'ast>>,
  ) -> Result<Vec<ValidNode<'ast>>, Error> {
    processed.push(self.validate_node_def(first)?);
    match &second.kind {
      NodeKindDef::Choice {
        first,
        second,
        inline: _,
      } => self.validate_choices(first, second, processed),
      _ => {
        processed.push(self.validate_node_def(second)?);
        Ok(processed)
      }
    }
  }
}

#[derive(Clone, Debug)]
struct ValidNode<'a> {
  kind: ValidNodeKind<'a>,
  tag: Option<Ident<'a>>,
}

#[derive(Clone, Debug)]
enum ValidNodeKind<'a> {
  Node(Ident<'a>),
  Modified(Box<ValidNode<'a>>, Modifier),
  StaticToken(Ident<'a>),
  DynamicToken(Ident<'a>),
  Group {
    nodes: Vec<ValidNode<'a>>,
    inline: bool,
  },
  Choice {
    nodes: Vec<ValidNode<'a>>,
    inline: bool,
  },
  Delimited(Box<ValidNode<'a>>, Ident<'a>),
  Todo,
  End,
}

#[derive(Clone, Debug, Error)]
pub enum Error {
  #[error("unknown identifier: `{0}`")]
  UnknownIdent(String),
  #[error("unknown delimiter: `{0}`")]
  UnknownDelim(String),
}