pub mod tex_state;
use crate::commands::primitives::{PrimitiveCommands, PrimitiveIdentifier, PRIMITIVES};
use crate::commands::{PrimitiveCommand, TeXCommand};
use crate::engine::gullet::methods::CSOrActiveChar;
use crate::engine::{EngineAux, EngineReferences, EngineTypes};
use crate::tex::catcodes::{CategoryCode, CategoryCodeScheme};
use crate::tex::nodes::boxes::TeXBox;
use crate::tex::numerics::{MuSkip, Skip};
use crate::tex::tokens::control_sequences::CSName;
use crate::tex::tokens::token_lists::TokenList;
use std::fmt::Formatter;
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum GroupType {
Simple,
HBox,
VAdjust,
VBox,
VTop,
Align,
Noalign,
Output,
Math,
Disc,
Insert,
VCenter,
MathChoice,
SemiSimple,
MathShift { display: bool },
LeftRight,
}
impl std::fmt::Display for GroupType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
GroupType::Simple => write!(f, "simple"),
GroupType::HBox => write!(f, "hbox"),
GroupType::VAdjust => write!(f, "adjusted hbox"),
GroupType::VBox => write!(f, "vbox"),
GroupType::VTop => write!(f, "vtop"),
GroupType::Align => write!(f, "align"),
GroupType::Noalign => write!(f, "no align"),
GroupType::Output => write!(f, "output"),
GroupType::Math => write!(f, "math"),
GroupType::Disc => write!(f, "disc"),
GroupType::Insert => write!(f, "insert"),
GroupType::VCenter => write!(f, "vcenter"),
GroupType::MathChoice => write!(f, "math choice"),
GroupType::SemiSimple => write!(f, "semi simple"),
GroupType::MathShift { .. } => write!(f, "math shift"),
GroupType::LeftRight { .. } => write!(f, "math left"),
}
}
}
impl GroupType {
pub fn to_byte(&self) -> u8 {
match self {
GroupType::Simple => 1,
GroupType::HBox => 2,
GroupType::VAdjust => 3,
GroupType::VBox => 4,
GroupType::VTop => 5,
GroupType::Align => 6,
GroupType::Noalign => 7,
GroupType::Output => 8,
GroupType::Math => 9,
GroupType::Disc => 10,
GroupType::Insert => 11,
GroupType::VCenter => 12,
GroupType::MathChoice => 13,
GroupType::SemiSimple => 14,
GroupType::MathShift { .. } => 15,
GroupType::LeftRight => 16,
}
}
}
pub trait State<ET: EngineTypes>: Sized + Clone {
fn new(nullfont: ET::Font, aux: &mut EngineAux<ET>) -> Self;
fn register_index(i: ET::Int) -> Option<usize> {
let idx: i64 = i.into();
if idx < 0 || idx > u16::MAX.into() {
None
} else {
Some(idx as usize)
}
}
fn aftergroup(&mut self, token: ET::Token);
fn get_mathfonts(&self, fam: u8) -> (ET::Font, ET::Font, ET::Font) {
(
self.get_textfont(fam).clone(),
self.get_scriptfont(fam).clone(),
self.get_scriptscriptfont(fam).clone(),
)
}
fn register_primitive(
&mut self,
aux: &mut EngineAux<ET>,
name: &'static str,
cmd: PrimitiveCommand<ET>,
);
fn primitives(&self) -> &PrimitiveCommands<ET>;
fn push(&mut self, aux: &mut EngineAux<ET>, group_type: GroupType, line_number: usize);
fn pop(&mut self, aux: &mut EngineAux<ET>, mouth: &mut ET::Mouth);
fn get_group_type(&self) -> Option<GroupType>;
fn get_group_level(&self) -> usize;
fn get_current_font(&self) -> &ET::Font;
fn set_current_font(&mut self, aux: &mut EngineAux<ET>, fnt: ET::Font, globally: bool);
fn get_textfont(&self, fam: u8) -> &ET::Font;
fn set_textfont(&mut self, aux: &mut EngineAux<ET>, fam: u8, fnt: ET::Font, globally: bool);
fn get_scriptfont(&self, fam: u8) -> &ET::Font;
fn set_scriptfont(&mut self, aux: &mut EngineAux<ET>, fam: u8, fnt: ET::Font, globally: bool);
fn get_scriptscriptfont(&self, fam: u8) -> &ET::Font;
fn set_scriptscriptfont(
&mut self,
aux: &mut EngineAux<ET>,
fam: u8,
fnt: ET::Font,
globally: bool,
);
fn get_par_token(&self) -> ET::CSName;
fn set_par_token(&mut self, par: ET::CSName);
fn get_catcode_scheme(&self) -> &CategoryCodeScheme<ET::Char>;
fn set_catcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, cc: CategoryCode, globally: bool);
fn get_sfcode(&self, c: ET::Char) -> u16;
fn set_sfcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, sfcode: u16, globally: bool);
fn get_lccode(&self, c: ET::Char) -> ET::Char;
fn set_lccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, lccode: ET::Char, globally: bool);
fn get_uccode(&self, c: ET::Char) -> ET::Char;
fn set_uccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, uccode: ET::Char, globally: bool);
fn get_delcode(&self, c: ET::Char) -> ET::Int;
fn set_delcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, delcode: ET::Int, globally: bool);
fn get_mathcode(&self, c: ET::Char) -> u32;
fn set_mathcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, mathcode: u32, globally: bool);
fn get_endline_char(&self) -> Option<ET::Char>;
fn set_endline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
fn get_escape_char(&self) -> Option<ET::Char>;
fn set_escape_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
fn get_newline_char(&self) -> Option<ET::Char>;
fn set_newline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
fn get_parshape(&self) -> &Vec<(ET::Dim, ET::Dim)>;
fn take_parshape(&mut self) -> Vec<(ET::Dim, ET::Dim)>;
fn set_parshape(
&mut self,
aux: &EngineAux<ET>,
parshape: Vec<(ET::Dim, ET::Dim)>,
globally: bool,
);
fn get_int_register(&self, idx: usize) -> ET::Int;
fn set_int_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Int, globally: bool);
fn get_primitive_int(&self, name: PrimitiveIdentifier) -> ET::Int;
fn set_primitive_int(
&mut self,
aux: &EngineAux<ET>,
name: PrimitiveIdentifier,
v: ET::Int,
globally: bool,
);
fn get_dim_register(&self, idx: usize) -> ET::Dim;
fn set_dim_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Dim, globally: bool);
fn get_skip_register(&self, idx: usize) -> Skip<ET::Dim>;
fn set_skip_register(
&mut self,
aux: &EngineAux<ET>,
idx: usize,
v: Skip<ET::Dim>,
globally: bool,
);
fn get_muskip_register(&self, idx: usize) -> MuSkip<ET::MuDim>;
fn set_muskip_register(
&mut self,
aux: &EngineAux<ET>,
idx: usize,
v: MuSkip<ET::MuDim>,
globally: bool,
);
fn get_toks_register(&self, idx: usize) -> &TokenList<ET::Token>;
fn set_toks_register(
&mut self,
aux: &EngineAux<ET>,
idx: usize,
v: TokenList<ET::Token>,
globally: bool,
);
fn get_box_register(&self, idx: usize) -> Option<&TeXBox<ET>>;
fn get_box_register_mut(&mut self, idx: usize) -> Option<&mut TeXBox<ET>>;
fn take_box_register(&mut self, idx: usize) -> Option<TeXBox<ET>>;
fn set_box_register(
&mut self,
aux: &EngineAux<ET>,
idx: usize,
v: Option<TeXBox<ET>>,
globally: bool,
);
fn get_primitive_dim(&self, name: PrimitiveIdentifier) -> ET::Dim;
fn set_primitive_dim(
&mut self,
aux: &EngineAux<ET>,
name: PrimitiveIdentifier,
v: ET::Dim,
globally: bool,
);
fn get_primitive_skip(&self, name: PrimitiveIdentifier) -> Skip<ET::Dim>;
fn set_primitive_skip(
&mut self,
aux: &EngineAux<ET>,
name: PrimitiveIdentifier,
v: Skip<ET::Dim>,
globally: bool,
);
fn get_primitive_muskip(&self, name: PrimitiveIdentifier) -> MuSkip<ET::MuDim>;
fn set_primitive_muskip(
&mut self,
aux: &EngineAux<ET>,
name: PrimitiveIdentifier,
v: MuSkip<ET::MuDim>,
globally: bool,
);
fn get_primitive_tokens(&self, name: PrimitiveIdentifier) -> &TokenList<ET::Token>;
fn set_primitive_tokens(
&mut self,
aux: &EngineAux<ET>,
name: PrimitiveIdentifier,
v: TokenList<ET::Token>,
globally: bool,
);
fn get_command(&self, name: &ET::CSName) -> Option<&TeXCommand<ET>>;
fn set_command(
&mut self,
aux: &EngineAux<ET>,
name: ET::CSName,
cmd: Option<TeXCommand<ET>>,
globally: bool,
);
fn get_ac_command(&self, c: ET::Char) -> Option<&TeXCommand<ET>>;
fn set_ac_command(
&mut self,
aux: &EngineAux<ET>,
c: ET::Char,
cmd: Option<TeXCommand<ET>>,
globally: bool,
);
}
pub trait StateChangeTracker<ET: EngineTypes>: State<ET> {
fn stack(&mut self) -> &mut StateStack<ET>;
fn change_field<F: FnOnce(&mut Self, bool) -> StateChange<ET>>(
&mut self,
globally: bool,
f: F,
) {
let globaldefs = self.get_primitive_int(PRIMITIVES.globaldefs);
let zero = ET::Int::default();
let global = if globaldefs == zero {
globally
} else {
globaldefs > zero
};
let change = f(self, global);
if global {
self.stack().add_change_globally(change)
} else {
self.stack().add_change_locally(change)
}
}
}
#[derive(Clone, Debug)]
pub enum StateChange<ET: EngineTypes> {
Catcode {
char: ET::Char,
old: CategoryCode,
},
SfCode {
char: ET::Char,
old: u16,
},
DelCode {
char: ET::Char,
old: ET::Int,
},
LcCode {
char: ET::Char,
old: ET::Char,
},
UcCode {
char: ET::Char,
old: ET::Char,
},
MathCode {
char: ET::Char,
old: u32,
},
ParShape {
old: Vec<(ET::Dim, ET::Dim)>,
},
CurrentFont(ET::Font),
TextFont {
idx: u8,
old: ET::Font,
},
ScriptFont {
idx: u8,
old: ET::Font,
},
ScriptScriptFont {
idx: u8,
old: ET::Font,
},
EndlineChar {
old: Option<ET::Char>,
},
EscapeChar {
old: Option<ET::Char>,
},
NewlineChar {
old: Option<ET::Char>,
},
IntRegister {
idx: usize,
old: ET::Int,
},
DimRegister {
idx: usize,
old: ET::Dim,
},
SkipRegister {
idx: usize,
old: Skip<ET::Dim>,
},
MuSkipRegister {
idx: usize,
old: MuSkip<ET::MuDim>,
},
BoxRegister {
idx: usize,
old: Option<TeXBox<ET>>,
},
ToksRegister {
idx: usize,
old: TokenList<ET::Token>,
},
PrimitiveInt {
name: PrimitiveIdentifier,
old: ET::Int,
},
PrimitiveDim {
name: PrimitiveIdentifier,
old: ET::Dim,
},
PrimitiveToks {
name: PrimitiveIdentifier,
old: TokenList<ET::Token>,
},
PrimitiveSkip {
name: PrimitiveIdentifier,
old: Skip<ET::Dim>,
},
PrimitiveMuSkip {
name: PrimitiveIdentifier,
old: MuSkip<ET::MuDim>,
},
Command {
name: ET::CSName,
old: Option<TeXCommand<ET>>,
},
AcCommand {
char: ET::Char,
old: Option<TeXCommand<ET>>,
},
}
#[derive(Clone)]
pub struct StackLevel<ET: EngineTypes> {
pub group_type: GroupType,
pub aftergroup: Vec<ET::Token>,
changes: Vec<StateChangeI<ET>>,
}
pub struct StateStack<ET: EngineTypes> {
pub stack: Vec<StackLevel<ET>>,
vecs: Vec<Vec<StateChangeI<ET>>>,
}
impl<ET: EngineTypes> Clone for StateStack<ET> {
fn clone(&self) -> Self {
Self {
stack: self.stack.clone(),
vecs: vec![],
}
}
}
impl<ET: EngineTypes> Default for StateStack<ET> {
fn default() -> Self {
Self {
stack: vec![],
vecs: vec![],
}
}
}
impl<ET: EngineTypes> StateStack<ET> {
pub fn push(&mut self, group_type: GroupType) {
let lvl = StackLevel {
group_type,
aftergroup: vec![],
changes: self.vecs.pop().unwrap_or_default(),
};
self.stack.push(lvl);
}
pub fn pop(&mut self) -> (GroupType, Vec<ET::Token>, ChangeIter<'_, ET>) {
let lvl = self.stack.pop().unwrap();
(
lvl.group_type,
lvl.aftergroup,
ChangeIter {
stack: self,
chs: lvl.changes,
},
)
}
pub fn add_change_globally(&mut self, change: StateChange<ET>) {
let change = change.into();
for lvl in &mut self.stack {
for c in lvl.changes.iter_mut() {
if c.equiv(&change) {
c.active = false
}
}
}
}
pub fn add_change_locally(&mut self, change: StateChange<ET>) {
if let Some(lvl) = self.stack.last_mut() {
let change = change.into();
if lvl.changes.iter().all(|c| !c.equiv(&change)) {
lvl.changes.push(change);
}
}
}
}
pub struct ChangeIter<'a, ET: EngineTypes> {
stack: &'a mut StateStack<ET>,
chs: Vec<StateChangeI<ET>>,
}
impl<'a, ET: EngineTypes> ChangeIter<'a, ET> {
pub fn close<F: FnMut(StateChange<ET>)>(mut self, mut f: F) {
for ch in self.chs.drain(..) {
if ch.active {
f(ch.ch);
}
}
self.stack.vecs.push(self.chs);
}
}
#[derive(Clone)]
struct StateChangeI<ET: EngineTypes> {
active: bool,
id: (std::mem::Discriminant<StateChange<ET>>, usize),
ch: StateChange<ET>,
}
impl<ET: EngineTypes> StateChangeI<ET> {
fn equiv(&self, other: &StateChangeI<ET>) -> bool {
self.active && self.id == other.id
}
}
impl<ET: EngineTypes> From<StateChange<ET>> for StateChangeI<ET> {
fn from(value: StateChange<ET>) -> Self {
let u = match &value {
StateChange::Catcode { char, .. } => (*char).into() as usize,
StateChange::SfCode { char, .. } => (*char).into() as usize,
StateChange::LcCode { char, .. } => (*char).into() as usize,
StateChange::UcCode { char, .. } => (*char).into() as usize,
StateChange::MathCode { char, .. } => (*char).into() as usize,
StateChange::DelCode { char, .. } => (*char).into() as usize,
StateChange::AcCommand { char, .. } => (*char).into() as usize,
StateChange::CurrentFont(_) => 0,
StateChange::EndlineChar { .. } => 0,
StateChange::EscapeChar { .. } => 0,
StateChange::NewlineChar { .. } => 0,
StateChange::ParShape { .. } => 0,
StateChange::TextFont { idx, .. } => *idx as usize,
StateChange::ScriptFont { idx, .. } => *idx as usize,
StateChange::ScriptScriptFont { idx, .. } => *idx as usize,
StateChange::IntRegister { idx, .. } => *idx,
StateChange::DimRegister { idx, .. } => *idx,
StateChange::SkipRegister { idx, .. } => *idx,
StateChange::MuSkipRegister { idx, .. } => *idx,
StateChange::ToksRegister { idx, .. } => *idx,
StateChange::BoxRegister { idx, .. } => *idx,
StateChange::PrimitiveInt { name, .. } => name.as_u16() as usize,
StateChange::PrimitiveDim { name, .. } => name.as_u16() as usize,
StateChange::PrimitiveSkip { name, .. } => name.as_u16() as usize,
StateChange::PrimitiveMuSkip { name, .. } => name.as_u16() as usize,
StateChange::PrimitiveToks { name, .. } => name.as_u16() as usize,
StateChange::Command { name, .. } => name.id(),
};
StateChangeI {
active: true,
id: (std::mem::discriminant(&value), u),
ch: value,
}
}
}
impl<ET: EngineTypes> EngineReferences<'_, ET> {
pub fn set_command(
&mut self,
name: &CSOrActiveChar<ET::Token>,
cmd: Option<TeXCommand<ET>>,
globally: bool,
) {
match name {
CSOrActiveChar::Active(c) => self.state.set_ac_command(self.aux, *c, cmd, globally),
CSOrActiveChar::Name(cs) => self.state.set_command(self.aux, cs.clone(), cmd, globally),
}
}
}