1use std::{fmt, rc::Rc};
2
3use once_cell::sync::Lazy;
4#[cfg(feature = "codegen")]
5use proc_macro2::TokenStream;
6#[cfg(feature = "codegen")]
7use quote::{ToTokens, quote};
8use regex::Regex;
9
10use crate::{
11 Digested,
12 common::{
13 arena::{self, SymStr},
14 error::{emit_warn, *},
15 object::Object,
16 },
17 definition::{
18 BeforeDigestClosure, Definition, DigestionClosure, argument::ArgWrap, constructor::Constructor,
19 },
20 gullet,
21 mouth::Mouth,
22 pin,
23 state::*,
24 token::{Catcode, Token},
25 tokens::Tokens,
26 whatsit::Whatsit,
27};
28
29pub type ReaderFn = dyn Fn(Option<&Parameters>, &[Tokens]) -> Result<ArgWrap>;
30pub type ReaderPredigestFn = dyn Fn(ArgWrap, &[Tokens]) -> Result<Option<Digested>>;
31pub type ReaderPredigestClosure = Rc<ReaderPredigestFn>;
32pub type ReaderClosure = Rc<ReaderFn>;
33
34pub type ReversionClosure =
43 Rc<dyn Fn(Vec<Token>, Option<&Parameters>, &[Tokens]) -> Result<Tokens>>;
44
45pub type DigestedReversionClosure = Rc<dyn Fn(&Digested) -> Result<Tokens>>;
49
50static LAST_WCHAR_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"\w$").unwrap());
51static FIRST_WCHAR_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^\w").unwrap());
52
53#[derive(Clone)]
54pub struct Parameter {
55 pub novalue: bool,
56 pub semiverbatim: Option<Vec<char>>,
57 pub optional: bool,
58 pub name: SymStr,
59 pub spec: SymStr,
60 pub extra: Vec<Tokens>,
61 pub inner: Option<Parameters>,
62 pub reader: ReaderClosure,
63 pub predigest: Option<ReaderPredigestClosure>,
64 pub reversion: Option<ReversionClosure>,
65 pub digested_reversion: Option<DigestedReversionClosure>,
69 pub before_digest: Vec<BeforeDigestClosure>,
70 pub after_digest: Vec<DigestionClosure>,
71}
72impl Default for Parameter {
73 fn default() -> Self {
74 Parameter {
75 novalue: false,
76 semiverbatim: None,
77 optional: false,
78 name: pin!("parameter_default"),
79 spec: pin!(""),
80 extra: Vec::new(),
81 inner: None,
82 reader: Rc::new(|_args, _extra| {
83 Warn!(
84 "Parameter",
85 "mock_reader",
86 "Please define a real reader, this is a mock fallback!"
87 );
88 Ok(ArgWrap::None)
89 }),
90 predigest: None,
91 reversion: None,
92 digested_reversion: None,
93 before_digest: Vec::new(),
94 after_digest: Vec::new(),
95 }
96 }
97}
98impl fmt::Debug for Parameter {
99 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100 writeln!(
101 f,
102 "Parameter(\n\t name:{:?}, novalue:{:?}, semiverbatim:{:?},",
103 self.name, self.novalue, self.semiverbatim,
104 )?;
105 writeln!(f, "\t optional:{:?}, spec:{:?}", self.optional, self.spec)?;
106 writeln!(f, "\t inner: {:?}", self.inner)?;
107 writeln!(
108 f,
109 "\t extra: {:?}\n\t reversion: {:?}, before_digest: {:?}, after_digest: {:?} )",
110 self.extra,
111 self.reversion.is_some(),
112 self.before_digest.len(),
113 self.after_digest.len()
114 )
115 }
116}
117impl fmt::Display for Parameter {
118 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119 arena::with(self.name, |name| write!(f, "{name}"))
120 }
121}
122
123impl PartialEq for Parameter {
124 fn eq(&self, other: &Parameter) -> bool { self.name == other.name }
125}
126impl Object for Parameter {
127 fn stringify(&self) -> String { arena::to_string(self.spec) }
128}
129
130static OPTIONAL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^Optional(.+)$").unwrap());
131static SKIP_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^Skip(.+)$").unwrap());
132
133impl Parameter {
134 pub fn new<T: AsRef<str>>(name: T, spec: T, extra: Option<Vec<Tokens>>) -> Result<Self> {
135 Parameter {
136 name: arena::pin(name),
137 spec: arena::pin(spec),
138 extra: extra.unwrap_or_default(),
139 ..Parameter::default()
140 }
141 .init()
142 }
143 pub fn init(mut self) -> Result<Self> {
144 let mut descriptor: Option<Rc<Parameter>> =
148 with_mapping_sym(pin!("PARAMETER_TYPES"), self.name, |looked_up_mapping| {
149 if let Some(Stored::Parameter(d_lookup)) = looked_up_mapping {
150 Some(Rc::clone(d_lookup))
151 } else {
152 None
153 }
154 });
155 if descriptor.is_none() {
156 let basetype_opt = arena::with(self.name, |name| {
158 OPTIONAL_REGEX
159 .captures(name)
160 .map(|captures| captures.get(1).map_or("", |m| m.as_str()).to_string())
161 })
162 .map(arena::pin);
163 if let Some(basetype) = basetype_opt {
164 descriptor = with_mapping_sym(pin!("PARAMETER_TYPES"), basetype, |basetype_param_opt| {
165 match basetype_param_opt {
166 Some(Stored::Parameter(d_lookup)) => Ok(Some(d_lookup.clone())),
167 _ => match Parameter::check_reader_function(&arena::with(self.name, |name| {
168 s!("Read{name}")
169 })) {
170 Some(reader) => Ok(Some(Rc::new(Parameter {
171 reader,
172 optional: true,
173 ..Parameter::default()
174 }))),
175 None => match Parameter::check_reader_function(&arena::with(basetype, |type_str| {
176 s!("Read{type_str}")
177 })) {
178 Some(reader) => Ok(Some(Rc::new(Parameter {
179 reader,
180 optional: true,
181 novalue: true,
182 ..Parameter::default()
183 }))),
184 None => fatal!(
185 Parameter,
186 Init,
187 s!("Can't initialize parameter {:?}, unknown?", self.name)
188 ),
189 },
190 },
191 }
192 })?;
193 self.optional = true;
194 } else {
195 let basetype_opt = arena::with(self.name, |name| {
202 SKIP_REGEX
203 .captures(name)
204 .map(|captures| captures.get(1).map_or("", |m| m.as_str()).to_string())
205 })
206 .map(arena::pin);
207 if let Some(basetype) = basetype_opt {
208 descriptor = with_mapping_sym(pin!("PARAMETER_TYPES"), basetype, |basetype_param_opt| {
209 match basetype_param_opt {
210 Some(Stored::Parameter(d_lookup)) => Some(d_lookup.clone()),
211 _ => match arena::with(self.name, |name| Parameter::check_reader_function(name)) {
212 Some(reader) => Some(Rc::new(Parameter {
213 reader,
214 optional: true,
215 novalue: true,
216 ..Parameter::default()
217 })),
218 None => Parameter::check_reader_function(&arena::with(basetype, |type_str| {
219 s!("Read{type_str}")
220 }))
221 .map(|reader| {
222 Rc::new(Parameter {
223 reader,
224 optional: true,
225 novalue: true,
226 ..Parameter::default()
227 })
228 }),
229 },
230 }
231 });
232 if let Some(ref _desc) = descriptor {
233 self.novalue = true;
234 self.optional = true;
235 }
236 } else {
237 descriptor =
238 Parameter::check_reader_function(&arena::with(self.name, |name| s!("Read{name}")))
239 .map(|reader| Rc::new(Parameter { reader, ..Parameter::default() }));
240 }
241 }
242 }
243 match descriptor {
244 Some(descriptor) => {
245 self.reader = descriptor.reader.clone(); if descriptor.novalue {
249 self.novalue = true;
250 }
251 self.semiverbatim.clone_from(&descriptor.semiverbatim);
252 if descriptor.optional {
255 self.optional = true;
256 }
257 self.reversion.clone_from(&descriptor.reversion);
258 self
259 .digested_reversion
260 .clone_from(&descriptor.digested_reversion);
261 self.before_digest.clone_from(&descriptor.before_digest);
262 self.after_digest.clone_from(&descriptor.after_digest);
263 self.predigest.clone_from(&descriptor.predigest);
264 },
265 None => fatal!(
266 Parameter,
267 Unknown,
268 arena::with2(self.name, self.spec, |name, spec| s!(
269 "Unrecognized parameter type with name {:?}, spec {:?}",
270 name,
271 spec
272 ))
273 ),
274 }
275 self.inner = self.inner.map(|inner_ps| match inner_ps.clone().init() {
277 Ok(ps) => ps,
278 Err(e) => {
279 emit_warn(
280 "internal",
281 "parameter",
282 &format!("inner parameter init failed: {e}"),
283 );
284 inner_ps
285 },
286 });
287 Ok(self)
288 }
289
290 pub fn check_reader_function(name: &str) -> Option<ReaderClosure> {
292 with_mapping("PARAMETER_TYPES", name, |param_opt| {
296 if let Some(Stored::Parameter(param)) = param_opt {
297 Some(param.reader.clone())
298 } else {
299 None
300 }
301 })
302 }
303
304 pub fn setup_catcodes(&self) {
305 if self.semiverbatim.is_some() {
306 begin_semiverbatim(self.semiverbatim.as_deref());
307 }
308 }
309
310 pub fn revert_catcodes(&self) -> Result<()> {
311 if self.semiverbatim.is_some() {
312 end_semiverbatim()?;
313 }
314 Ok(())
315 }
316
317 pub fn read(&self, fordefn: Option<&dyn Definition>) -> Result<ArgWrap> {
318 self.setup_catcodes();
323
324 let closure = &self.reader;
325 let value_from_reader: ArgWrap = closure(self.inner.as_ref(), &self.extra)?;
326 let value_arg = match value_from_reader {
332 ArgWrap::Tokens(mut value) => {
333 if let Some(ref semi_chars) = self.semiverbatim {
334 value = value.neutralize(semi_chars);
335 }
336 ArgWrap::Tokens(value)
337 },
338 other => other,
339 };
340 self.revert_catcodes()?;
341
342 let (is_optional_match, is_until) = arena::with(self.name, |name| {
347 (name.starts_with("OptionalMatch"), name.starts_with("Until"))
348 });
349
350 if !value_arg.is_none() && self.optional && is_optional_match {
352 gullet::skip_spaces()?;
353 }
354
355 let checked_value =
356 if !self.optional && !self.novalue && (value_arg.is_none() && self.predigest.is_none()) {
357 if !is_until {
361 let fordefn_str = fordefn.map(|fdefn| fdefn.stringify()).unwrap_or_default();
362 Error!(
363 "expected",
364 self,
365 s!("Missing argument {} for {}", self.stringify(), fordefn_str)
366 );
367 ArgWrap::Tokens(Tokens!(T_OTHER!("missing")))
368 } else {
369 value_arg
370 }
371 } else {
372 value_arg
373 };
374 Ok(checked_value)
375 }
376
377 pub fn digest(
378 &self,
379 mut value_arg: ArgWrap,
380 _fordefn: Option<&Constructor>,
381 ) -> Result<Option<Digested>> {
382 let mode = lookup_string_from_sym(crate::pin!("MODE"));
384 if self.semiverbatim.is_some() {
386 self.setup_catcodes();
387 if value_arg.is_tokens() {
388 if let Some(value) = value_arg.owned_tokens() {
389 let neutralized = gullet::reading_from_mouth(Mouth::default(), move || {
390 gullet::unread(value);
391 let mut tokens = Vec::new();
392 loop {
393 match gullet::get_pending_comment() {
394 Some(token) => tokens.push(token),
395 None => match gullet::read_x_token(Some(true), false, None) {
396 Ok(token_opt) => match token_opt {
397 Some(token) => tokens.push(token),
398 None => break,
399 },
400 Err(x) => return Err(x),
401 },
402 }
403 }
404 Ok(Tokens::new(tokens).neutralize(&[]))
405 })?;
406 value_arg = ArgWrap::Tokens(neutralized);
407 } else {
408 value_arg = ArgWrap::default();
409 }
410 }
411 }
412
413 for pre in self.before_digest.iter() {
414 pre()?; }
417 let digested_value = if let Some(closure) = &self.predigest {
418 closure(value_arg, &self.extra)?
419 } else {
420 if self.optional && value_arg.is_none() {
430 None
431 } else {
432 Some(value_arg.be_digested()?)
433 }
434 };
435 for post in self.after_digest.iter() {
436 let mut w = Whatsit::default();
438 post(&mut w)?; }
440
441 self.revert_catcodes()?;
442
443 let newmode = lookup_string_from_sym(crate::pin!("MODE"));
445 if mode != newmode && mode != "horizontal" {
446 crate::stomach::leave_horizontal_internal();
447 }
448
449 Ok(digested_value)
450 }
451
452 pub fn revert(&self, value_opt: Option<Tokens>) -> Result<Option<Tokens>> {
453 if let Some(ref reverter) = self.reversion {
454 if let Some(value) = value_opt {
455 Ok(Some((reverter)(
456 value.unlist(),
457 self.inner.as_ref(),
458 &self.extra,
459 )?))
460 } else {
461 Ok(None)
462 }
463 } else if let Some(value) = value_opt {
464 Ok(Some(Tokens::new(value.revert())))
465 } else {
466 Ok(None)
467 }
468 }
469
470 pub fn reparse(&self, tokens: Tokens) -> Result<ArgWrap> {
475 if self.name == pin!("Plain") || self.predigest.is_some() {
478 Ok(ArgWrap::Tokens(tokens))
480 } else if self.semiverbatim.is_some() {
481 Ok(ArgWrap::Tokens(
484 tokens.neutralize(self.semiverbatim.as_ref().unwrap().as_slice()),
485 ))
486 } else {
487 gullet::reading_from_mouth(Mouth::default(), || {
488 let mut tokens = tokens.unlist();
490 if !tokens.is_empty() && arena::with(self.name,|name|
492 matches!(name, "Number"|"Dimension"|"Glue"|"MuDimension"|"MuGlue"))
493 && tokens.first().map(|t| t.get_catcode() == Catcode::BEGIN)
494 .unwrap_or(false)
495 && tokens.last().map(|t| t.get_catcode() == Catcode::END).unwrap_or(false)
496 {
497 tokens.remove(0);
498 tokens.pop();
499 }
500 gullet::unread_vec(tokens); let value = self.read(None)?;
502 gullet::skip_spaces()?;
503 Ok(value)
504 })
505 }
506 }
507}
508
509#[derive(Clone, Debug, Default)]
510pub struct Parameters(Vec<Parameter>);
511
512impl PartialEq for Parameters {
513 fn eq(&self, other: &Parameters) -> bool { self.0 == other.0 }
514}
515impl Object for Parameters {
516 fn stringify(&self) -> String {
517 let mut result = String::new();
518 for parameter in self.0.iter() {
519 let s = parameter.stringify();
520 let lead_letter = match s.chars().next() {
521 Some(c) => c.is_alphanumeric(),
522 None => false,
523 };
524 let trail_letter = match result.chars().last() {
525 Some(c) => c.is_alphanumeric(),
526 None => false,
527 };
528 if lead_letter && trail_letter {
529 result.push(' ');
530 }
531 result.push_str(&s);
532 }
533 result
534 }
535}
536
537impl Parameters {
538 pub fn new(params: Vec<Parameter>) -> Self { Parameters(params) }
539 pub fn get_num_args(&self) -> usize { self.0.iter().filter(|&p| !p.novalue).count() }
540 pub fn get_parameters(&self) -> Vec<&Parameter> { self.0.iter().collect() }
541 pub fn take_parameters(self) -> Vec<Parameter> { self.0 }
542 pub fn revert_arguments(&self, args: Vec<Option<Tokens>>) -> Result<Vec<Token>> {
543 let mut tokens = Vec::new();
544 for (parameter, arg) in self.0.iter().zip(args) {
545 if !parameter.novalue
546 && let Some(reverted_tks) = parameter.revert(arg)?
547 {
548 tokens.extend(reverted_tks.unlist());
549 }
550 }
551 Ok(tokens)
552 }
553
554 pub fn revert_digested_arguments(
559 &self,
560 digested_args: &[Option<Digested>],
561 ) -> Result<Vec<Token>> {
562 let mut tokens = Vec::new();
563 for (parameter, arg_opt) in self.0.iter().zip(digested_args) {
564 if !parameter.novalue {
565 let reverted = if let Some(ref digested_rev) = parameter.digested_reversion {
566 match arg_opt {
568 Some(arg) => Some(digested_rev(arg)?),
569 None => None,
570 }
571 } else {
572 let token_reverted = match arg_opt {
574 Some(arg) => Some(arg.revert()?),
575 None => None,
576 };
577 parameter.revert(token_reverted)?
578 };
579 if let Some(tks) = reverted {
580 tokens.extend(tks.unlist());
581 }
582 }
583 }
584 Ok(tokens)
585 }
586 pub fn init(mut self) -> Result<Self> {
588 let mut initialized = Vec::new();
589 for param in self.0.drain(..) {
590 initialized.push(param.init()?);
591 }
592 self.0 = initialized;
593 Ok(self)
594 }
595
596 pub fn read_arguments(&self, fordefn: Option<&dyn Definition>) -> Result<Vec<ArgWrap>> {
597 let mut args = Vec::with_capacity(self.0.len());
598 for parameter in &self.0 {
599 let values = parameter.read(fordefn)?;
600 if parameter.predigest.is_some() {
601 }
608 if !parameter.novalue {
609 args.push(values);
610 }
611 }
612 Ok(args)
613 }
614
615 pub fn read_arguments_and_digest(&self, fordefn: &Constructor) -> Result<Vec<Option<Digested>>> {
616 let mut args = Vec::with_capacity(self.0.len());
617 for parameter in &self.0 {
618 let value = parameter.read(Some(fordefn))?;
619 if !parameter.novalue {
620 let digested_value = parameter.digest(value, Some(fordefn))?;
621 args.push(digested_value);
622 }
623 }
624 Ok(args)
625 }
626
627 pub fn reparse_argument(&self, value: ArgWrap) -> Result<Vec<ArgWrap>> {
628 if value.is_none() {
629 return Ok(Vec::new());
630 }
631 let value_tokens = value.revert()?;
632 let reader_mouth = Mouth::new("", None)?;
634 gullet::reading_from_mouth(reader_mouth, || {
635 gullet::unread(value_tokens); let values = self.read_arguments(None)?;
637 gullet::skip_spaces()?;
638 Ok(values)
639 })
640 }
641
642 pub fn as_keysets(&self) -> Vec<String> { self.0.iter().map(|p| p.stringify()).collect() }
643}
644impl fmt::Display for Parameters {
645 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
646 let mut content = String::new();
647 for parameter in &self.0 {
648 let param_content = parameter.to_string();
649 if LAST_WCHAR_RE.is_match(&content) && FIRST_WCHAR_RE.is_match(¶m_content) {
650 content.push(' ');
651 }
652 content.push_str(¶m_content);
653 }
654 write!(f, "{content}")
655 }
656}
657
658impl From<Parameters> for Vec<Parameter> {
659 fn from(ps: Parameters) -> Vec<Parameter> { ps.0 }
660}
661
662#[cfg(feature = "codegen")]
665impl ToTokens for Parameters {
666 fn to_tokens(&self, stream: &mut TokenStream) {
667 let params = &self.0;
668 stream.extend(quote! {
669 Parameters::new(<[Parameter]>::into_vec(Box::new([ #(#params),* ])))
670 });
671 }
672}
673
674#[cfg(feature = "codegen")]
675impl ToTokens for Parameter {
676 fn to_tokens(&self, stream: &mut TokenStream) {
677 let name = arena::with(self.name, |name| quote!(arena::pin_static(#name)));
678 let spec = arena::with(self.spec, |spec| quote!(arena::pin_static(#spec)));
679 let extra = &self.extra;
680 let inner = match &self.inner {
681 None => quote!(None),
682 Some(inner_ps) => quote!(Some(#inner_ps)),
683 };
684 stream.extend(quote! {
685 Parameter {
686 name: #name,
687 spec: #spec,
688 extra: <[Tokens]>::into_vec(Box::new([ #(#extra),* ])),
689 inner: #inner,
690 ..Parameter::default()
691 }
692 });
693 }
694}
695
696#[cfg(test)]
697mod tests {
698 use super::*;
699
700 #[test]
701 fn parameter_default_has_expected_fields() {
702 let p = Parameter::default();
703 assert!(!p.novalue);
704 assert!(p.semiverbatim.is_none());
705 assert!(!p.optional);
706 assert!(p.inner.is_none());
707 assert!(p.extra.is_empty());
708 assert!(p.before_digest.is_empty());
709 assert!(p.after_digest.is_empty());
710 assert!(p.predigest.is_none());
711 assert!(p.reversion.is_none());
712 assert!(p.digested_reversion.is_none());
713 assert_eq!(arena::to_string(p.name), "parameter_default");
714 assert_eq!(arena::to_string(p.spec), "");
715 }
716
717 #[test]
718 fn parameter_display_is_name() {
719 let p = Parameter {
720 name: arena::pin("Plain"),
721 ..Default::default()
722 };
723 assert_eq!(format!("{p}"), "Plain");
724 }
725
726 #[test]
727 fn parameter_stringify_is_spec() {
728 let p = Parameter {
729 spec: arena::pin("{}"),
730 ..Default::default()
731 };
732 assert_eq!(p.stringify(), "{}");
733 }
734
735 #[test]
736 fn parameter_partial_eq_by_name() {
737 let mut a = Parameter::default();
740 let mut b = Parameter::default();
741 a.name = arena::pin("x");
742 b.name = arena::pin("x");
743 assert_eq!(a, b);
744 b.name = arena::pin("y");
745 assert_ne!(a, b);
746 }
747
748 #[test]
749 fn parameters_new_and_take() {
750 let p = Parameter::default();
751 let ps = Parameters::new(vec![p]);
752 let taken = ps.take_parameters();
753 assert_eq!(taken.len(), 1);
754 }
755
756 #[test]
757 fn parameters_get_num_args_counts_valued() {
758 let mut a = Parameter::default();
760 let mut b = Parameter::default();
761 let mut c = Parameter::default();
762 a.novalue = false;
763 b.novalue = true;
764 c.novalue = false;
765 let ps = Parameters::new(vec![a, b, c]);
766 assert_eq!(ps.get_num_args(), 2);
767 }
768
769 #[test]
770 fn parameters_empty() {
771 let ps = Parameters::new(vec![]);
772 assert_eq!(ps.get_num_args(), 0);
773 assert_eq!(ps.get_parameters().len(), 0);
774 }
775
776 #[test]
777 fn parameters_get_parameters_returns_refs_to_all() {
778 let mut a = Parameter::default();
781 let mut b = Parameter::default();
782 a.novalue = false;
783 b.novalue = true;
784 let ps = Parameters::new(vec![a, b]);
785 assert_eq!(ps.get_parameters().len(), 2);
786 }
787}