1#[macro_use]
2pub mod expandable;
3pub mod argument;
4pub mod conditional;
5pub mod constructor;
6pub mod math_primitive;
7pub mod primitive;
8pub mod register;
9
10use std::{borrow::Cow, fmt, rc::Rc};
11
12use libxml::tree::Node;
13
14use self::{
15 argument::ArgWrap,
16 register::{RegisterType, RegisterValue},
17};
18use crate::{
19 Digested,
20 common::{
21 arena::{self, SymHashMap, SymStr},
22 dimension::Dimension,
23 error::{emit_warn, *},
24 font::Font,
25 object::Object,
26 store::Stored,
27 },
28 definition::conditional::ConditionalType,
29 document::Document,
30 gullet::Gullet,
31 mouth,
32 parameter::Parameters,
33 state::{Scope, expire_state_unlocked, local_state_unlocked},
34 token::Token,
35 tokens::{NO_TOKENS, TeXString, Tokens},
36 whatsit::Whatsit,
37};
38
39pub type ExpansionClosure = Rc<dyn Fn(Vec<ArgWrap>) -> Result<Tokens>>;
40pub type ConditionalClosure = Rc<dyn Fn(Vec<ArgWrap>) -> Result<bool>>;
41pub type PrimitiveFn = dyn Fn(Vec<ArgWrap>) -> Result<Vec<Digested>>;
42pub type PrimitiveClosure = Rc<PrimitiveFn>;
43pub type BeforeDigestClosure = Rc<dyn Fn() -> Result<Vec<Digested>>>;
44pub type PropertiesClosure = Rc<dyn Fn(&Vec<Option<Digested>>) -> Result<SymHashMap<Stored>>>;
45pub type DigestionClosure = Rc<dyn Fn(&mut Whatsit) -> Result<Vec<Digested>>>;
46pub type ReplacementClosure =
47 Rc<dyn Fn(&mut Document, &Vec<Option<Digested>>, &SymHashMap<Stored>) -> Result<()>>;
48pub type ConstructionClosure = Rc<dyn Fn(&mut Document, &Whatsit) -> Result<()>>;
49pub type DigestedReversionClosure = Rc<dyn Fn(&Whatsit, &Vec<Option<Digested>>) -> Result<Tokens>>;
50pub type SizingClosure = Rc<dyn Fn(&Whatsit) -> Result<(Dimension, Dimension, Dimension)>>;
51pub type FontClosure = Rc<dyn Fn(Option<&Whatsit>) -> Result<Font>>;
52
53#[derive(Clone)]
54pub enum ExpansionBody {
55 Closure(ExpansionClosure),
56 Tokens(Tokens),
57}
58
59impl ExpansionBody {
60 pub fn push(&mut self, t: Token) {
63 match self {
64 ExpansionBody::Tokens(tks) => tks.unlist_mut().push(t),
65 ExpansionBody::Closure(_) => {
66 },
68 }
69 }
70}
71
72impl fmt::Debug for ExpansionBody {
73 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74 match self {
75 ExpansionBody::Closure(code) => write!(f, "CODE({:p})", Rc::as_ptr(code)),
76 ExpansionBody::Tokens(ts) => write!(f, "{ts:?}"),
77 }
78 }
79}
80
81impl Default for ExpansionBody {
82 fn default() -> Self { ExpansionBody::Tokens(NO_TOKENS) }
83}
84
85impl PartialEq for ExpansionBody {
86 fn eq(&self, other: &ExpansionBody) -> bool {
87 match self {
88 ExpansionBody::Closure(self_closure) => match other {
89 ExpansionBody::Closure(other_closure) => Rc::ptr_eq(self_closure, other_closure),
90 ExpansionBody::Tokens(other_tokens) => {
91 format!("CODE({:p})", Rc::as_ptr(self_closure)) == other_tokens.to_string()
95 },
96 },
97 ExpansionBody::Tokens(self_tks) => match other {
98 ExpansionBody::Tokens(other_tks) => self_tks == other_tks,
99 ExpansionBody::Closure(other_closure) => {
100 format!("CODE({:p})", Rc::as_ptr(other_closure)) == self_tks.to_string()
101 },
102 },
103 }
104 }
105}
106
107#[derive(Clone)]
108pub enum PrimitiveBody {
109 Closure(PrimitiveClosure),
110 String(SymStr),
111}
112impl From<char> for PrimitiveBody {
113 fn from(c: char) -> Self { PrimitiveBody::String(arena::pin_char(c)) }
114}
115
116#[derive(Clone)]
117pub enum Reversion {
118 Closure(DigestedReversionClosure),
119 Tokens(Tokens),
120}
121
122impl PartialEq for Reversion {
123 fn eq(&self, other: &Reversion) -> bool {
124 match self {
125 Reversion::Tokens(t) => match other {
126 Reversion::Tokens(t2) => t == t2,
127 _ => false,
128 },
129 _ => false,
131 }
132 }
133}
134
135impl From<&str> for Reversion {
143 fn from(t: &str) -> Reversion {
144 Reversion::Tokens(
145 mouth::tokenize_internal(TeXString::assembled(t.to_string()))
146 .pack_parameters()
147 .unwrap(),
148 )
149 }
150}
151impl From<Tokens> for Reversion {
152 fn from(ts: Tokens) -> Reversion { Reversion::Tokens(ts) }
153}
154
155impl From<Token> for ExpansionBody {
156 fn from(t: Token) -> ExpansionBody { ExpansionBody::Tokens(Tokens!(t)) }
157}
158
159impl From<Token> for Option<ExpansionBody> {
160 fn from(t: Token) -> Option<ExpansionBody> { Some(ExpansionBody::Tokens(Tokens!(t))) }
161}
162
163impl From<Tokens> for ExpansionBody {
164 fn from(t: Tokens) -> ExpansionBody { ExpansionBody::Tokens(t) }
165}
166
167impl From<Tokens> for Option<ExpansionBody> {
168 fn from(t: Tokens) -> Option<ExpansionBody> { if t.is_empty() { None } else { Some(t.into()) } }
169}
170
171impl From<&str> for ExpansionBody {
172 fn from(s: &str) -> ExpansionBody {
173 mouth::tokenize_internal(TeXString::assembled(s.to_string())).into()
174 }
175}
176
177impl From<String> for ExpansionBody {
178 fn from(s: String) -> ExpansionBody { s.as_str().into() }
179}
180
181impl From<ArgWrap> for ExpansionBody {
182 fn from(t: ArgWrap) -> ExpansionBody {
183 ExpansionBody::Tokens(t.owned_tokens().unwrap_or_default())
184 }
185}
186impl From<ArgWrap> for Option<ExpansionBody> {
187 fn from(t: ArgWrap) -> Option<ExpansionBody> {
188 match t.owned_tokens() {
189 Some(tks) if !tks.is_empty() => Some(ExpansionBody::Tokens(tks)),
190 _ => None,
191 }
192 }
193}
194
195#[derive(Clone)]
196pub enum FontDirective {
197 Closure(FontClosure),
198 Asset(Rc<Font>),
199}
200
201impl From<Font> for FontDirective {
202 fn from(f: Font) -> Self { FontDirective::Asset(Rc::new(f)) }
203}
204impl From<FontClosure> for FontDirective {
205 fn from(fc: FontClosure) -> Self { FontDirective::Closure(fc) }
206}
207impl FontDirective {
208 pub fn get_font(&self, whatsit: Option<&Whatsit>) -> Result<Rc<Font>> {
209 match self {
210 FontDirective::Closure(fc) => Ok(Rc::new((fc)(whatsit)?)),
211 FontDirective::Asset(font) => Ok(Rc::clone(font)),
212 }
213 }
214 pub fn get_asset(&self) -> Option<Rc<Font>> {
215 if let FontDirective::Asset(font) = self {
216 Some(Rc::clone(font))
217 } else {
218 None
219 }
220 }
221}
222impl fmt::Debug for FontDirective {
223 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224 match self {
225 FontDirective::Closure(_) => write!(f, "<FontClosure>"),
226 FontDirective::Asset(font) => write!(f, "{:?}", *font),
227 }
228 }
229}
230impl PartialEq for FontDirective {
231 fn eq(&self, other: &FontDirective) -> bool {
232 match self {
233 FontDirective::Closure(_) => false, FontDirective::Asset(asset1) => match other {
235 FontDirective::Closure(_) => false,
236 FontDirective::Asset(asset2) => *asset1 == *asset2,
237 },
238 }
239 }
240}
241
242pub trait Definition: Object {
243 fn invoke(&self, once_only: bool) -> Result<Tokens>;
244 fn invoke_primitive(&self) -> Result<Vec<Digested>>;
245
246 fn get_cs(&self) -> Cow<'_, Token>;
249 fn get_cs_name(&self) -> Cow<'_, str>;
250 fn get_cs_or_alias(&self) -> Cow<'_, Token> {
251 match self.get_alias() {
252 Some(alias) => Cow::Owned(T_CS!(alias)),
253 None => self.get_cs(),
254 }
255 }
256 fn get_sizer(&self) -> Option<SizingClosure> { None }
257 fn get_alias(&self) -> Option<&String>;
258 fn is_protected(&self) -> bool { false }
259 fn is_register(&self) -> bool { false }
260 fn is_prefix(&self) -> bool { false }
261 fn is_readonly(&self) -> bool { false }
262 fn get_test(&self) -> Option<&ConditionalClosure> { None }
263 fn get_conditional_type(&self) -> Option<ConditionalType> { None }
264
265 fn read_arguments(&self) -> Result<Vec<ArgWrap>>
266 where Self: Sized {
267 match self.get_parameters() {
268 None => Ok(Vec::new()),
269 Some(params) => params.read_arguments(Some(self)),
270 }
271 }
272 fn get_parameters(&self) -> Option<&Parameters>;
273
274 fn invocation(&mut self, args: Vec<Option<Tokens>>, _gullet: &mut Gullet) -> Result<Tokens> {
277 let mut invocation_result: Vec<Token> = vec![self.get_cs().into_owned()];
278
279 match self.get_parameters() {
280 None => {},
281 Some(params) => {
282 for result_token in params.revert_arguments(args)? {
283 invocation_result.push(result_token);
284 }
285 },
286 }
287 Ok(Tokens::new(invocation_result))
288 }
289
290 fn get_num_args(&self) -> usize { 0 }
291
292 fn do_absorption(&self, _document: &mut Document, _whatsit: &Whatsit) -> Result<Vec<Node>>;
293 fn before_digest(&self) -> Option<&Vec<BeforeDigestClosure>> { None }
294 fn after_digest(&self) -> Option<&Vec<DigestionClosure>> { None }
295 fn after_digest_body(&self) -> Option<&Vec<DigestionClosure>> { None }
296 fn capture_body(&self) -> bool { false }
297
298 fn execute_before_digest(&self) -> Result<Vec<Digested>> {
299 local_state_unlocked(true);
300 if let Some(pre_list) = self.before_digest() {
311 for pre in pre_list.iter() {
312 for bx in pre()? {
313 crate::stomach::push_box_list(bx);
314 }
315 }
316 }
317 expire_state_unlocked();
318 Ok(Vec::new())
319 }
320 fn execute_after_digest(&self, whatsit: &mut Whatsit) -> Result<Vec<Digested>> {
321 local_state_unlocked(true);
322 let mut after_digested = Vec::new();
323 if let Some(post_list) = self.after_digest() {
324 for post in post_list.iter() {
325 after_digested.extend(post(whatsit)?);
326 }
327 }
328 expire_state_unlocked();
329 Ok(after_digested)
330 }
331
332 fn execute_after_digest_body(&self, whatsit: &mut Whatsit) -> Result<Vec<Digested>> {
333 local_state_unlocked(true);
334 let mut after_body_digested = Vec::new();
335 if let Some(post_list) = self.after_digest_body() {
336 for post in post_list {
339 let after_body_digest_result = post(whatsit)?;
340 after_body_digested.extend(after_body_digest_result);
341 }
342 }
343 expire_state_unlocked();
344 Ok(after_body_digested)
345 }
346
347 fn value_of(&self, _args: Vec<ArgWrap>) -> Option<RegisterValue> { None }
348 fn set_value(&self, _value: RegisterValue, _scope: Option<Scope>, _args: Vec<ArgWrap>) {
350 emit_warn(
351 "internal",
352 "register",
353 "set_value called on non-register definition",
354 );
355 }
356 fn register_type(&self) -> Option<RegisterType> { None }
357 fn get_reversion_spec(&self) -> Option<Reversion> { None }
358 fn get_expansion(&self) -> Option<&ExpansionBody> { None }
359
360 fn stringify_type(&self, deftype: &str) -> String {
361 let name = match self.get_alias() {
362 Some(alias) => alias.clone(),
363 None => self.get_cs().with_cs_name(ToString::to_string),
364 };
365 if let Some(parameters) = self.get_parameters() {
366 s!("{}[{} {}]", deftype, name, parameters.stringify())
367 } else {
368 s!("{}[{}]", deftype, name)
369 }
370 }
371}
372
373impl PartialEq for dyn Definition {
382 fn eq(&self, other: &dyn Definition) -> bool { self.stringify() == other.stringify() }
383}
384
385impl fmt::Display for dyn Definition {
386 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
387 if let Some(params) = self.get_parameters() {
388 write!(f, "{} {}", self.get_cs_name(), params)
389 } else {
390 write!(f, "{}", self.get_cs_name())
391 }
392 }
393}
394
395impl fmt::Display for ExpansionBody {
396 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
397 match self {
398 ExpansionBody::Tokens(t) => write!(f, "{t}"),
399 ExpansionBody::Closure(code) => {
400 write!(f, "ExpansionBody::Closure({:p})", Rc::as_ptr(code))
401 }, }
403 }
404}