use crate::texmacro;
use crate::token;
use crate::variable;
use crate::vm;
use once_cell::sync::OnceCell;
use std::num;
use std::rc;
use std::sync;
pub(crate) mod map;
pub use crate::error::Error;
pub use map::Map;
pub type Result<T> = std::result::Result<T, Box<Error>>;
pub type ExpansionFn<S> =
fn(token: token::Token, input: &mut vm::ExpansionInput<S>) -> Result<Vec<token::Token>>;
pub type ExecutionFn<S> = fn(token: token::Token, input: &mut vm::ExecutionInput<S>) -> Result<()>;
pub enum Command<S> {
Expansion(ExpansionFn<S>, Option<Tag>),
Macro(rc::Rc<texmacro::Macro>),
Execution(ExecutionFn<S>, Option<Tag>),
Variable(rc::Rc<variable::Command<S>>),
Character(token::Value),
}
impl<S> std::fmt::Display for Command<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Command::Expansion(_, _) => write![f, "an expansion command"],
Command::Macro(_) => write![f, "a user-defined macro"],
Command::Execution(_, _) => write![f, "an execution command"],
Command::Variable(_) => write![f, "a variable command"],
Command::Character(_) => write![f, "a token alias"],
}
}
}
impl<S> Command<S> {
pub fn tag(&self) -> Option<Tag> {
match self {
Command::Expansion(_, tag) => *tag,
Command::Macro(_) => None,
Command::Execution(_, tag) => *tag,
Command::Variable(_) => None,
Command::Character(_) => None,
}
}
}
pub struct BuiltIn<S> {
cmd: Command<S>,
doc: Option<&'static str>,
}
impl<S> BuiltIn<S> {
pub fn new_expansion(t: ExpansionFn<S>) -> BuiltIn<S> {
t.into()
}
pub fn new_execution(t: ExecutionFn<S>) -> BuiltIn<S> {
t.into()
}
pub fn new_variable(cmd: variable::Command<S>) -> BuiltIn<S> {
Command::Variable(rc::Rc::new(cmd)).into()
}
pub fn with_tag(mut self, tag: Tag) -> BuiltIn<S> {
match &mut self.cmd {
Command::Expansion(_, t) => *t = Some(tag),
Command::Execution(_, t) => *t = Some(tag),
Command::Macro(_) | Command::Variable(_) | Command::Character(_) => {
panic!("cannot add a tag to this type of command")
}
}
self
}
pub fn with_doc(mut self, doc: &'static str) -> BuiltIn<S> {
self.doc = Some(doc);
self
}
pub fn cmd(&self) -> &Command<S> {
&self.cmd
}
pub fn doc(&self) -> Option<&'static str> {
self.doc
}
}
impl<S> Clone for Command<S> {
fn clone(&self) -> Self {
match self {
Command::Expansion(e, t) => Command::Expansion::<S>(*e, *t),
Command::Macro(m) => Command::Macro(m.clone()),
Command::Execution(e, t) => Command::Execution(*e, *t),
Command::Variable(v) => Command::Variable(v.clone()),
Command::Character(tv) => Command::Character(*tv),
}
}
}
impl<S> Clone for BuiltIn<S> {
fn clone(&self) -> Self {
Self {
cmd: self.cmd.clone(),
doc: self.doc,
}
}
}
impl<S> From<ExpansionFn<S>> for BuiltIn<S> {
fn from(cmd: ExpansionFn<S>) -> Self {
Command::Expansion(cmd, None).into()
}
}
impl<S> From<rc::Rc<texmacro::Macro>> for BuiltIn<S> {
fn from(cmd: rc::Rc<texmacro::Macro>) -> Self {
Command::Macro(cmd).into()
}
}
impl<S> From<ExecutionFn<S>> for BuiltIn<S> {
fn from(cmd: ExecutionFn<S>) -> Self {
Command::Execution(cmd, None).into()
}
}
impl<S> From<variable::Command<S>> for BuiltIn<S> {
fn from(cmd: variable::Command<S>) -> Self {
Command::Variable(rc::Rc::new(cmd)).into()
}
}
impl<S> From<Command<S>> for BuiltIn<S> {
fn from(cmd: Command<S>) -> Self {
BuiltIn { cmd, doc: None }
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord, Hash)]
pub struct Tag(num::NonZeroU32);
static NEXT_TAG_VALUE: sync::Mutex<u32> = sync::Mutex::new(1);
impl Tag {
#[allow(clippy::new_without_default)]
pub fn new() -> Tag {
let mut n = NEXT_TAG_VALUE.lock().unwrap();
let tag = Tag(num::NonZeroU32::new(*n).unwrap());
*n = n.checked_add(1).unwrap();
tag
}
}
pub struct StaticTag(OnceCell<Tag>);
impl StaticTag {
pub const fn new() -> StaticTag {
StaticTag(OnceCell::new())
}
pub fn get(&self) -> Tag {
*self.0.get_or_init(Tag::new)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum PrimitiveKey {
Execution(usize, Option<Tag>),
Expansion(usize, Option<Tag>),
VariableSingleton(variable::Key),
VariableArrayStatic(variable::Key, variable::Index),
VariableArrayDynamic(variable::Key, usize),
}
impl PrimitiveKey {
pub(crate) fn new<S>(command: &Command<S>) -> Option<Self> {
match command {
Command::Expansion(f, tag) => Some(PrimitiveKey::Expansion(*f as usize, *tag)),
Command::Execution(f, tag) => Some(PrimitiveKey::Execution(*f as usize, *tag)),
Command::Variable(v) => {
let variable_key = variable::Key::new(v.getters());
match v.index_resolver() {
None => Some(PrimitiveKey::VariableSingleton(variable_key)),
Some(index_resolver) => match index_resolver {
variable::IndexResolver::Static(a) => {
Some(PrimitiveKey::VariableArrayStatic(variable_key, *a))
}
variable::IndexResolver::Dynamic(f) => Some(
PrimitiveKey::VariableArrayDynamic(variable_key, *f as usize),
),
},
}
}
Command::Macro(_) => None,
Command::Character(_) => None,
}
}
pub(crate) fn _variable_key(&self) -> Option<variable::Key> {
match self {
PrimitiveKey::Execution(_, _) => None,
PrimitiveKey::Expansion(_, _) => None,
PrimitiveKey::VariableSingleton(key) => Some(*key),
PrimitiveKey::VariableArrayStatic(key, _) => Some(*key),
PrimitiveKey::VariableArrayDynamic(key, _) => Some(*key),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn func_size() {
assert_eq!(std::mem::size_of::<Command<()>>(), 16);
}
static STATIC_TAG_1: StaticTag = StaticTag::new();
static STATIC_TAG_2: StaticTag = StaticTag::new();
#[test]
fn tag() {
let tag_1_val_1 = STATIC_TAG_1.get();
let tag_2_val_1 = STATIC_TAG_2.get();
let other_tag_1 = Tag::new();
let tag_1_val_2 = STATIC_TAG_1.get();
let tag_2_val_2 = STATIC_TAG_2.get();
let other_tag_2 = Tag::new();
assert_eq!(tag_1_val_1, tag_1_val_2);
assert_eq!(tag_2_val_1, tag_2_val_2);
assert_ne!(tag_1_val_1, tag_2_val_2);
assert_ne!(tag_1_val_1, other_tag_1);
assert_ne!(tag_1_val_1, other_tag_2);
}
#[test]
fn tag_size() {
assert_eq!(std::mem::size_of::<Option<Tag>>(), 4);
}
}