Skip to main content

flams_stex/quickparse/latex/
rules.rs

1use crate::quickparse::{
2    latex::{FromLaTeXToken, LaTeXParser, Macro},
3    stex::DiagnosticLevel,
4};
5use flams_utils::{parsing::SourceParser, sourcerefs::StringPosition};
6
7use super::{Environment, ParserState};
8
9#[derive(Debug)]
10pub enum MacroResult<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>> {
11    Success(T),
12    Simple(Macro<'a, Pos>),
13    Other(Vec<T>),
14}
15
16#[derive(Debug)]
17pub enum EnvironmentResult<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>> {
18    Success(T),
19    Simple(Environment<'a, Pos, T>),
20    Other(Vec<T>),
21}
22
23pub type MacroRule<
24    'a,
25    Pos: StringPosition,
26    T: FromLaTeXToken<'a, Pos>,
27    State: ParserState<'a, Pos, T>,
28> = fn(Macro<'a, Pos>, &mut LaTeXParser<'a, Pos, T, State>) -> MacroResult<'a, Pos, T>;
29
30pub type EnvOpenRule<
31    'a,
32    Pos: StringPosition,
33    T: FromLaTeXToken<'a, Pos>,
34    State: ParserState<'a, Pos, T>,
35> = for<'b, 'c> fn(&'b mut Environment<'a, Pos, T>, &'c mut LaTeXParser<'a, Pos, T, State>);
36
37pub type EnvCloseRule<
38    'a,
39    Pos: StringPosition,
40    T: FromLaTeXToken<'a, Pos>,
41    State: ParserState<'a, Pos, T>,
42> = for<'b> fn(
43    Environment<'a, Pos, T>,
44    &'b mut LaTeXParser<'a, Pos, T, State>,
45) -> EnvironmentResult<'a, Pos, T>;
46
47pub type EnvironmentRule<
48    'a,
49    Pos: StringPosition,
50    T: FromLaTeXToken<'a, Pos>,
51    State: ParserState<'a, Pos, T>,
52> = (
53    EnvOpenRule<'a, Pos, T, State>,
54    EnvCloseRule<'a, Pos, T, State>,
55);
56
57#[allow(clippy::type_complexity)]
58pub struct DynMacro<
59    'a,
60    Pos: StringPosition,
61    T: FromLaTeXToken<'a, Pos>,
62    State: ParserState<'a, Pos, T>,
63    Arg,
64> {
65    pub ptr:
66        fn(&Arg, Macro<'a, Pos>, &mut LaTeXParser<'a, Pos, T, State>) -> MacroResult<'a, Pos, T>,
67    pub arg: Arg,
68}
69
70#[allow(clippy::type_complexity)]
71pub struct DynEnv<
72    'a,
73    Pos: StringPosition,
74    T: FromLaTeXToken<'a, Pos>,
75    State: ParserState<'a, Pos, T>,
76    Arg,
77> {
78    pub open: for<'b, 'c> fn(
79        &Arg,
80        &'b mut Environment<'a, Pos, T>,
81        &'c mut LaTeXParser<'a, Pos, T, State>,
82    ),
83    pub close: for<'b> fn(
84        Environment<'a, Pos, T>,
85        &'b mut LaTeXParser<'a, Pos, T, State>,
86    ) -> EnvironmentResult<'a, Pos, T>,
87    pub arg: Arg,
88}
89
90pub enum AnyMacro<
91    'a,
92    Pos: StringPosition,
93    T: FromLaTeXToken<'a, Pos>,
94    State: ParserState<'a, Pos, T>,
95> {
96    Ptr(MacroRule<'a, Pos, T, State>),
97    Str(DynMacro<'a, Pos, T, State, &'a str>),
98    Ext(DynMacro<'a, Pos, T, State, State::MacroArg>),
99}
100
101impl<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>, State: ParserState<'a, Pos, T>>
102    AnyMacro<'a, Pos, T, State>
103{
104    pub fn call(
105        &self,
106        m: Macro<'a, Pos>,
107        p: &mut LaTeXParser<'a, Pos, T, State>,
108    ) -> MacroResult<'a, Pos, T> {
109        match self {
110            Self::Ptr(ptr) => ptr(m, p),
111            Self::Str(str) => (str.ptr)(&str.arg, m, p),
112            Self::Ext(ext) => (ext.ptr)(&ext.arg, m, p),
113        }
114    }
115}
116
117impl<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>, State: ParserState<'a, Pos, T>> Clone
118    for AnyMacro<'a, Pos, T, State>
119{
120    fn clone(&self) -> Self {
121        match self {
122            Self::Ptr(ptr) => Self::Ptr(*ptr),
123            Self::Str(str) => Self::Str(DynMacro {
124                ptr: str.ptr,
125                arg: str.arg.clone(),
126            }),
127            Self::Ext(ext) => Self::Ext(DynMacro {
128                ptr: ext.ptr,
129                arg: ext.arg.clone(),
130            }),
131        }
132    }
133}
134
135pub enum AnyEnv<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>, State: ParserState<'a, Pos, T>>
136{
137    Ptr(EnvironmentRule<'a, Pos, T, State>),
138    Str(DynEnv<'a, Pos, T, State, &'a str>),
139    Ext(DynEnv<'a, Pos, T, State, State::MacroArg>),
140}
141
142impl<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>, State: ParserState<'a, Pos, T>>
143    AnyEnv<'a, Pos, T, State>
144{
145    pub fn open<'b, 'c>(
146        &self,
147        e: &'b mut Environment<'a, Pos, T>,
148        p: &'c mut LaTeXParser<'a, Pos, T, State>,
149    ) {
150        match self {
151            Self::Ptr((ptr, _)) => ptr(e, p),
152            Self::Str(str) => (str.open)(&str.arg, e, p),
153            Self::Ext(ext) => (ext.open)(&ext.arg, e, p),
154        }
155    }
156    pub fn close(self) -> EnvCloseRule<'a, Pos, T, State> {
157        match self {
158            Self::Ptr((_, close)) => close,
159            Self::Str(str) => str.close,
160            Self::Ext(ext) => ext.close,
161        }
162    }
163}
164
165impl<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>, State: ParserState<'a, Pos, T>> Clone
166    for AnyEnv<'a, Pos, T, State>
167{
168    fn clone(&self) -> Self {
169        match self {
170            Self::Ptr(ptr) => Self::Ptr(*ptr),
171            Self::Str(str) => Self::Str(DynEnv {
172                open: str.open,
173                close: str.close,
174                arg: str.arg.clone(),
175            }),
176            Self::Ext(ext) => Self::Ext(DynEnv {
177                open: ext.open,
178                close: ext.close,
179                arg: ext.arg.clone(),
180            }),
181        }
182    }
183}
184
185pub fn read_verbatim_char<
186    'a,
187    Pos: StringPosition,
188    T: FromLaTeXToken<'a, Pos>,
189    State: ParserState<'a, Pos, T>,
190>(
191    mac: &mut Macro<'a, Pos>,
192    p: &mut LaTeXParser<'a, Pos, T, State>,
193    end: char,
194) {
195    //let tstart = p.curr_pos();
196    let _t = p.tokenizer.reader.read_until(|c| c == end);
197    /*if let Some(text) = T::from_text(
198        SourceRange {
199            start: tstart,
200            end: p.curr_pos(),
201        },
202        t,
203    ) {
204        mac.args.push(text);
205    }*/
206    if let Some(h2) = p.tokenizer.reader.next_char() {
207        if h2 != end {
208            p.tokenizer.problem(
209                mac.range.start,
210                "Expected end of verbatim",
211                DiagnosticLevel::Error,
212            );
213        }
214    } else {
215        p.tokenizer.problem(
216            mac.range.start,
217            "Expected end of verbatim",
218            DiagnosticLevel::Error,
219        );
220    }
221}
222
223pub fn read_verbatim_str<
224    'a,
225    Pos: StringPosition,
226    T: FromLaTeXToken<'a, Pos>,
227    State: ParserState<'a, Pos, T>,
228>(
229    _env: &mut Environment<'a, Pos, T>,
230    p: &mut LaTeXParser<'a, Pos, T, State>,
231    end_str: &str,
232) {
233    //let tstart = p.curr_pos();
234    let _t = p.tokenizer.reader.read_until_str(end_str);
235    /*if let Some(text) = T::from_text(
236        SourceRange {
237            start: tstart,
238            end: p.curr_pos(),
239        },
240        t,
241    ) {
242        env.args.push(text);
243    }*/
244}
245
246#[macro_export]
247macro_rules! texrules {
248    ($name:ident <= $(($($rl:tt)*))*) => {
249        $(
250            $crate::tex!($($rl)*)
251        )*
252        paste!{
253            pub fn [<$name _macros>]<'a, Pa: ParseSource<'a>, T: FromLaTeXToken<'a, Pa::Str, Pa::Pos>>() ->
254            [(Pa::Str,MacroRule<'a,Pa,T>);texrules!( $( ($($rl)*) )* )] {[
255                todo!()
256            ]}
257        }
258    };
259    (@count ) => (0usize);
260    (@count ($($i:tt)*) $($r:tt)* ) => {
261        (1usize + texrules!(@count $($r)*))
262    }
263}
264
265#[macro_export]
266macro_rules! tex {
267    ($p:ident => $name:ident$($args:tt)*) => {
268        #[allow(unused_mut,non_snake_case)]
269        pub fn $name<'a,
270            Pos: ::flams_utils::sourcerefs::StringPosition,
271            T: $crate::quickparse::latex::FromLaTeXToken<'a, Pos>,
272            State: $crate::quickparse::latex::ParserState<'a,Pos,T>
273        >(
274            mut $name:$crate::quickparse::latex::Macro<'a,Pos>,
275            $p:&mut $crate::quickparse::latex::LaTeXParser<'a, Pos, T, State>
276        ) -> $crate::quickparse::latex::rules::MacroResult<'a, Pos,T> {
277            tex!{@args $p:$name$($args)*}
278        }
279    };
280
281    ($p:ident => @begin{$name:ident}$( ($($args:tt)* ) )? {$($start:tt)*} $($end:tt)*) => {paste::paste!(
282        #[allow(unused,unused_mut,non_snake_case,clippy::missing_const_for_fn)]
283        pub fn [<$name _open>]<'a,
284            Pos: ::flams_utils::sourcerefs::StringPosition,
285            T: $crate::quickparse::latex::FromLaTeXToken<'a, Pos>,
286            State: $crate::quickparse::latex::ParserState<'a,Pos,T>
287        >(
288            $name:&mut $crate::quickparse::latex::Environment<'a, Pos, T>,
289            $p:&mut $crate::quickparse::latex::LaTeXParser<'a, Pos, T, State>
290        ) {
291            $( tex!{@envargs $p:$name $($args)* } )?
292            $($start)*
293        }
294        #[allow(unused,unused_mut,non_snake_case,clippy::missing_const_for_fn)]
295        pub fn [<$name _close>]<'a,
296            Pos: ::flams_utils::sourcerefs::StringPosition,
297            T: $crate::quickparse::latex::FromLaTeXToken<'a, Pos>,
298            State: $crate::quickparse::latex::ParserState<'a,Pos,T>
299        >(
300            mut $name:$crate::quickparse::latex::Environment<'a,Pos, T>,
301            $p:&mut $crate::quickparse::latex::LaTeXParser<'a, Pos, T, State>
302        ) -> $crate::quickparse::latex::rules::EnvironmentResult<'a,Pos,T> {
303            tex!{@end $name $($end)*}
304        }
305    );};
306
307    (<{$($tks:tt)+} M{$($mtks:tt)+} P{$($ptks:tt)+} R{$($rtks:tt)+}> $p:ident => $name:ident $($args:tt)*) => {
308        #[allow(unused_mut,non_snake_case,clippy::missing_const_for_fn)]
309        /// # Panics
310        pub fn $name<$($tks)*>(
311            mut $name:$crate::quickparse::latex::Macro<$($mtks)*>,
312            $p:&mut $crate::quickparse::latex::LaTeXParser<$($ptks)*>
313        ) -> $crate::quickparse::latex::rules::MacroResult<$($rtks)*> {
314            tex!{@args $p:$name$($args)*}
315        }
316    };
317
318    (<{$($tks:tt)+} E{$($mtks:tt)+} P{$($ptks:tt)+} R{$($rtks:tt)+}> $p:ident => @begin{$name:ident}$( ($($args:tt)* ) )? {$($start:tt)*} $($end:tt)*) => {paste::paste!(
319        #[allow(unused,unused_mut,non_snake_case)]
320        pub fn [<$name _open>]<$($tks)*>(
321            $name:&mut $crate::quickparse::latex::Environment<$($mtks)*>,
322            $p:&mut $crate::quickparse::latex::LaTeXParser<$($ptks)*>
323        ) {
324            $( tex!{@envargs $p:$name $($args)* } )?
325            $($start)*
326        }
327        #[allow(unused,unused_mut,non_snake_case)]
328        pub fn [<$name _close>]<$($tks)*>(
329            mut $name:$crate::quickparse::latex::Environment<$($mtks)*>,
330            $p:&mut $crate::quickparse::latex::LaTeXParser<$($ptks)*>
331        ) -> $crate::quickparse::latex::rules::EnvironmentResult<$($rtks)*> {
332            tex!{@end $name $($end)*}
333        }
334    );};
335
336    (@end $name:ident $b:block !) => {
337        $b
338        $crate::quickparse::latex::rules::EnvironmentResult::Simple($name)
339    };
340    (@end $name:ident !) => {
341        $crate::quickparse::latex::rules::EnvironmentResult::Simple($name)
342    };
343    (@end $name:ident $b:block) => {$b};
344
345    (@envargs $p:ident:$name:ident{$arg:ident:name}$($args:tt)*) => {
346        let Some($arg) = $p.read_name(&mut $name.begin) else {
347            $p.tokenizer.problem($name.begin.range.start,concat!("Expected { after \\",stringify!($name)),DiagnosticLevel::Error);
348            return;
349        };
350        tex!{@envargs $p:$name $($args)*}
351    };
352    (@envargs $p:ident:$name:ident{$arg:ident:!name}$($args:tt)*) => {
353        let Some($arg) = $p.read_name_normalized(&mut $name.begin) else {
354            $p.tokenizer.problem($name.begin.range.start,concat!("Expected { after \\",stringify!($name)),DiagnosticLevel::Error);
355            return;
356        };
357        tex!{@envargs $p:$name $($args)*}
358    };
359    (@envargs $p:ident:$name:ident{$arg:ident:name+}$($args:tt)*) => {
360        let $arg = $p.read_names(&mut $name.begin);
361        if $arg.is_empty() {
362            $p.tokenizer.problem($name.begin.range.start,concat!("Expected { after \\",stringify!($name)),DiagnosticLevel::Error);
363            return;
364        };
365        tex!{@envargs $p:$name $($args)*}
366    };
367    (@envargs $p:ident:$name:ident{$arg:ident:!name+}$($args:tt)*) => {
368        let $arg = $p.read_names_normalized(&mut $name.begin);
369        if $arg.is_empty() {
370            $p.tokenizer.problem($name.begin.range.start,concat!("Expected { after \\",stringify!($name)),DiagnosticLevel::Error);
371            return;
372        };
373        tex!{@envargs $p:$name $($args)*}
374    };
375    (@envargs $p:ident:$name:ident{$arg:ident:T}$($args:tt)*) => {
376        let mode = $p.tokenizer.mode;
377        $p.open_group();
378        $p.tokenizer.mode = $crate::quickparse::tokenizer::Mode::Text;
379        let $arg = $p.get_argument(&mut $name.begin);
380        $p.tokenizer.mode = mode;
381        $p.close_group();
382        tex!{@envargs $p:$name $($args)*}
383    };
384    (@envargs $p:ident:$name:ident{$arg:ident:T!}$($args:tt)*) => {
385        let mode = $p.tokenizer.mode;
386        $p.open_group();
387        $p.tokenizer.mode = $crate::quickparse::tokenizer::Mode::Text;
388        let $arg = $p.get_argument_with_str(&mut $name.begin);
389        $p.tokenizer.mode = mode;
390        $p.close_group();
391        tex!{@envargs $p:$name $($args)*}
392    };
393    (@envargs $p:ident:$name:ident{_:T}$($args:tt)*) => {
394        let mode = $p.tokenizer.mode;
395        $p.open_group();
396        $p.tokenizer.mode = $crate::quickparse::tokenizer::Mode::Text;
397        $p.read_argument(&mut $name.begin);
398        $p.tokenizer.mode = mode;
399        $p.close_group();
400        tex!{@envargs $p:$name $($args)*}
401    };
402    (@envargs $p:ident:$name:ident{$arg:ident:M}$($args:tt)*) => {
403        let mode = $p.tokenizer.mode;
404        let $arg = if matches!($p.tokenizer.mode,$crate::quickparse::tokenizer::Mode::Math{..}) {
405            $p.get_argument(&mut $name.begin)
406        } else {
407            $p.tokenizer.open_math(false);
408            let r = $p.get_argument(&mut $name.begin);
409            $p.tokenizer.close_math();
410            r
411        };
412        tex!{@envargs $p:$name $($args)*}
413    };
414    (@envargs $p:ident:$name:ident{_:M}$($args:tt)*) => {
415        let mode = $p.tokenizer.mode;
416        if matches!($p.tokenizer.mode,$crate::quickparse::tokenizer::Mode::Math{..}) {
417            $p.read_argument(&mut $name.begin);
418        } else {
419            $p.tokenizer.open_math(false);
420            $p.read_argument(&mut $name.begin);
421            $p.tokenizer.close_math();
422        }
423        tex!{@envargs $p:$name $($args)*}
424    };
425    (@envargs $p:ident:$name:ident{_}$($args:tt)*) => {
426        $p.skip_arg(&mut $name.begin);
427        tex!{@envargs $p:$name $($args)*}
428    };
429    (@envargs $p:ident:$name:ident[_?$opt:ident]$($args:tt)*) => {
430        let $opt = $p.skip_opt(&mut $name.begin);
431        tex!{@envargs $p:$name $($args)*}
432    };
433    (@envargs $p:ident:$name:ident[_]$($args:tt)*) => {
434        $p.skip_opt(&mut $name.begin);
435        tex!{@envargs $p:$name $($args)*}
436    };
437    (@envargs $p:ident:$name:ident[$opt:ident:str]$($args:tt)*) => {
438        let $opt = $p.read_opt_str(&mut $name.begin).into_name();
439        tex!{@envargs $p:$name $($args)*}
440    };
441    (@envargs $p:ident:$name:ident[$opt:ident:!name]$($args:tt)*) => {
442        let $opt = $p.read_opt_name_normalized(&mut $name.begin);
443        tex!{@envargs $p:$name $($args)*}
444    };
445    (@envargs $p:ident:$name:ident[$opt:ident]$($args:tt)*) => {
446        let $opt = $p.read_opt_str(&mut $name.begin);
447        tex!{@envargs $p:$name $($args)*}
448    };
449    (@envargs $p:ident:$name:ident[mut $opt:ident:Map]$($args:tt)*) => {
450        let mut $opt = $p.read_opt_map(&mut $name.begin);
451        tex!{@envargs $p:$name $($args)*}
452    };
453    (@envargs $p:ident:$name:ident[$opt:ident:Map]$($args:tt)*) => {
454        let $opt = $p.read_opt_map(&mut $name.begin);
455        tex!{@envargs $p:$name $($args)*}
456    };
457    (@envargs $p:ident:$name:ident[$opt:ident:type $tp:ty]$($args:tt)*) => {
458        let $opt = <Vec<$tp> as $crate::quickparse::latex::KeyValValues<_,_,_>>::parse_opt($p);
459        tex!{@envargs $p:$name $($args)*}
460    };
461    (@envargs $p:ident:$name:ident V:C($c:expr) $($args:tt)*) => {
462        $crate::quickparse::latex::rules::read_verbatim_char(&mut $name.begin,$p,$c);
463        tex!{@envargs $p:$name $($args)*}
464    };
465    (@envargs $p:ident:$name:ident V) => {
466        $crate::quickparse::latex::rules::read_verbatim_str($name,$p,concat!("\\end{",stringify!($name),"}"));
467    };
468    (@envargs $p:ident:$name:ident V!) => {
469        $crate::quickparse::latex::rules::read_verbatim_str($name,$p,&format!("\\end{{{}}}",$name.name));
470    };
471    (@envargs $p:ident:$name:ident($c:literal?$t:ident)$($args:tt)*) => {
472        let $t = $p.tokenizer.reader.starts_with($c) && {
473            $p.tokenizer.reader.pop_head();true
474        };
475        tex!{@envargs $p:$name $($args)*}
476    };
477    (@envargs $p:ident:$name:ident($t:ident)$($args:tt)*) => {
478        if let Some($t) = $p.tokenizer.reader.pop_head() {
479            tex!{@envargs $p:$name $($args)*}
480        } else {
481            $p.tokenizer.problem("Expected character",DiagnosticLevel::Error);
482        }
483    };
484    (@envargs $p:ident:$name:ident => $b:block) => {$b};
485    (@envargs $p:ident:$name:ident) => {};
486
487    (@args $p:ident:$name:ident{$arg:ident:name}$($args:tt)*) => {
488        let Some($arg) = $p.read_name(&mut $name) else {
489            $p.tokenizer.problem($name.range.start,concat!("Expected { after \\",stringify!($name)),DiagnosticLevel::Error);
490            return $crate::quickparse::latex::rules::MacroResult::Simple($name);
491        };
492        tex!{@args $p:$name $($args)*}
493    };
494    (@args $p:ident:$name:ident{$arg:ident:!name}$($args:tt)*) => {
495        let Some($arg) = $p.read_name_normalized(&mut $name) else {
496            $p.tokenizer.problem($name.range.start,concat!("Expected { after \\",stringify!($name)),DiagnosticLevel::Error);
497            return $crate::quickparse::latex::rules::MacroResult::Simple($name);
498        };
499        tex!{@args $p:$name $($args)*}
500    };
501    (@args $p:ident:$name:ident{$arg:ident}$($args:tt)*) => {
502        let $arg = $p.get_argument(&mut $name);
503        tex!{@args $p:$name $($args)*}
504    };
505    (@args $p:ident:$name:ident{$arg:ident:T}$($args:tt)*) => {
506        let mode = $p.tokenizer.mode;
507        $p.open_group();
508        $p.tokenizer.mode = $crate::quickparse::tokenizer::Mode::Text;
509        let $arg = $p.get_argument(&mut $name);
510        $p.tokenizer.mode = mode;
511        $p.close_group();
512        tex!{@args $p:$name $($args)*}
513    };
514    (@args $p:ident:$name:ident{$arg:ident:T!}$($args:tt)*) => {
515        let mode = $p.tokenizer.mode;
516        $p.open_group();
517        $p.tokenizer.mode = $crate::quickparse::tokenizer::Mode::Text;
518        let $arg = $p.get_argument_with_str(&mut $name);
519        $p.tokenizer.mode = mode;
520        $p.close_group();
521        tex!{@args $p:$name $($args)*}
522    };
523    (@args $p:ident:$name:ident{_:T}$($args:tt)*) => {
524        let mode = $p.tokenizer.mode;
525        $p.open_group();
526        $p.tokenizer.mode = $crate::quickparse::tokenizer::Mode::Text;
527        $p.read_argument(&mut $name);
528        $p.tokenizer.mode = mode;
529        $p.close_group();
530        tex!{@args $p:$name $($args)*}
531    };
532    (@args $p:ident:$name:ident{$arg:ident:M}$($args:tt)*) => {
533        let mode = $p.tokenizer.mode;
534        let $arg = if matches!($p.tokenizer.mode,$crate::quickparse::tokenizer::Mode::Math{..}) {
535            $p.get_argument(&mut $name)
536        } else {
537            $p.tokenizer.open_math(false);
538            let r = $p.get_argument(&mut $name);
539            $p.tokenizer.close_math();
540            r
541        };
542        tex!{@args $p:$name $($args)*}
543    };
544    (@args $p:ident:$name:ident{_:M}$($args:tt)*) => {
545        if matches!($p.tokenizer.mode,$crate::quickparse::tokenizer::Mode::Math{..}) {
546            $p.read_argument(&mut $name);
547        } else {
548            $p.tokenizer.open_math(false);
549            $p.read_argument(&mut $name);
550            $p.tokenizer.close_math();
551        }
552        tex!{@args $p:$name $($args)*}
553    };
554    (@args $p:ident:$name:ident{_}$($args:tt)*) => {
555        $p.skip_arg(&mut $name);
556        tex!{@args $p:$name $($args)*}
557    };
558    (@args $p:ident:$name:ident[_?$opt:ident]$($args:tt)*) => {
559        let $opt = $p.skip_opt(&mut $name);
560        tex!{@args $p:$name $($args)*}
561    };
562    (@args $p:ident:$name:ident[_]$($args:tt)*) => {
563        $p.skip_opt(&mut $name);
564        tex!{@args $p:$name $($args)*}
565    };
566    (@args $p:ident:$name:ident[$opt:ident:str]$($args:tt)*) => {
567        let $opt = $p.read_opt_str(&mut $name).into_name();
568        tex!{@args $p:$name $($args)*}
569    };
570    (@args $p:ident:$name:ident[$opt:ident:!name]$($args:tt)*) => {
571        let $opt = $p.read_opt_name_normalized(&mut $name);
572        tex!{@args $p:$name $($args)*}
573    };
574    (@args $p:ident:$name:ident[$opt:ident]$($args:tt)*) => {
575        let $opt = $p.read_opt_str(&mut $name);
576        tex!{@args $p:$name $($args)*}
577    };
578    (@args $p:ident:$name:ident[mut $opt:ident:Map]$($args:tt)*) => {
579        let mut $opt = $p.read_opt_map(&mut $name);
580        tex!{@args $p:$name $($args)*}
581    };
582    (@args $p:ident:$name:ident[$opt:ident:Map]$($args:tt)*) => {
583        let $opt = $p.read_opt_map(&mut $name);
584        tex!{@args $p:$name $($args)*}
585    };
586    (@args $p:ident:$name:ident[$opt:ident:type $tp:ty]$($args:tt)*) => {
587        let $opt = <Vec<$tp> as $crate::quickparse::latex::KeyValValues<_,_,_>>::parse_opt($p);
588        tex!{@args $p:$name $($args)*}
589    };
590    (@args $p:ident:$name:ident V:C($c:expr) $($args:tt)*) => {
591        $crate::quickparse::latex::rules::read_verbatim_char(&mut $name,$p,$c);
592        tex!{@args $p:$name $($args)*}
593    };
594    (@args $p:ident:$name:ident($c:literal?$t:ident)$($args:tt)*) => {
595        let $t = $p.tokenizer.reader.starts_with($c) && {
596            $p.tokenizer.reader.next_char();true
597        };
598        tex!{@args $p:$name $($args)*}
599    };
600    (@args $p:ident:$name:ident($t:ident)$($args:tt)*) => {
601        if let Some($t) = $p.tokenizer.reader.next_char() {
602            tex!{@args $p:$name $($args)*}
603        } else {
604            $p.tokenizer.problem($name.range.start,"Expected character",DiagnosticLevel::Error);
605            $crate::quickparse::latex::rules::MacroResult::Simple($name)
606        }
607    };
608    (@args $p:ident:$name:ident !) => {
609        $crate::quickparse::latex::rules::MacroResult::Simple($name)
610    };
611    (@args $p:ident:$name:ident => $b:block !) => {
612        $b;
613        $crate::quickparse::latex::rules::MacroResult::Simple($name)
614    };
615    (@args $p:ident:$name:ident => $b:block) => {$b};
616}
617
618tex!(p => begin{n:name} => {
619    match p.environment(begin,n.0,n.1) {
620        EnvironmentResult::Success(e) => MacroResult::Success(e),
621        EnvironmentResult::Other(v) => MacroResult::Other(v),
622        EnvironmentResult::Simple(e) => p.state.from_environment(e).map_or_else(
623            || MacroResult::Other(Vec::new()),
624            MacroResult::Success
625        )
626    }
627});
628
629tex!(p => end{n:name} => {
630    p.tokenizer.problem(end.range.start,format!("environment {} not open",n.0),DiagnosticLevel::Error);
631}!);
632
633tex!(p => lstinline[_](c)V:C(c)!);
634tex!(p => verb[_](c)V:C(c)!);
635tex!(p => stexcodeinline[_](c)V:C(c)!);
636tex!(p => stexinline[_](c)V:C(c)!);
637tex!(p => begingroup => { p.open_group() }!);
638tex!(p => endgroup => { p.close_group() }!);
639tex!(p => makeatletter => { p.add_letters("@") }!);
640tex!(p => makeatother => { p.remove_letters("@") }!);
641tex!(p => ExplSyntaxOn => { p.add_letters(":_") }!);
642tex!(p => ExplSyntaxOff => { p.remove_letters(":_") }!);
643tex!(p => lstdefinelanguage{_}[_?o]{_} => {
644    if o {p.skip_arg(&mut lstdefinelanguage);}
645}!);
646tex!(p => r#ref{_}!);
647tex!(p => label{_}!);
648tex!(p => cite{_}!);
649tex!(p => includegraphics[_]{_}!);
650tex!(p => url[_]{_}!);
651
652tex!(p => newcommand{_}[_][_]{_}!);
653tex!(p => providecommand{_}[_][_]{_}!);
654tex!(p => renewcommand{_}[_][_]{_}!);
655tex!(p => NewDocumentCommand{_}{_}{_}!);
656tex!(p => DeclareDocumentCommand{_}{_}{_}!);
657tex!(p => DeclareRobustCommand{_}{_}{_}!);
658tex!(p => newenvironment{_}[_][_]{_}{_}!);
659tex!(p => renewenvironment{_}[_][_]{_}{_}!);
660tex!(p => provideenvironment{_}[_][_]{_}{_}!);
661tex!(p => NewDocumentEnvironment{_}{_}{_}{_}!);
662tex!(p => DeclareDocumentEnvironment{_}{_}{_}{_}!);
663
664tex!(p => hbox{t:T} => { MacroResult::Other(t.1) });
665tex!(p => vbox{t:T} => { MacroResult::Other(t.1) });
666tex!(p => fbox{t:T} => { MacroResult::Other(t.1) });
667tex!(p => mvbox{t:T} => { MacroResult::Other(t.1) });
668tex!(p => text{t:T} => { MacroResult::Other(t.1) });
669tex!(p => texttt{t:T} => { MacroResult::Other(t.1) });
670tex!(p => textrm{t:T} => { MacroResult::Other(t.1) });
671tex!(p => textbf{t:T} => { MacroResult::Other(t.1) });
672tex!(p => scalebox{_}{t:T} => { MacroResult::Other(t.1) });
673tex!(p => raisebox{_}{t:T} => { MacroResult::Other(t.1) });
674tex!(p => ensuremath{t:M} => { MacroResult::Other(t.1) });
675
676tex!(p => def => {
677    p.tokenizer.reader.read_until(|c| c == '{');
678    p.skip_arg(&mut def);
679}!);
680tex!(p => edef => {def(edef,p)});
681tex!(p => gdef => {def(gdef,p)});
682tex!(p => xdef => {def(xdef,p)});
683
684tex!(p => @begin{document} {}{
685    let _start = p.curr_pos();
686    p.in_document = true;
687    let _rest = p.tokenizer.reader.read_until_str("this string should never occur FOOBARBAZ BLA BLA asdk<ösndkf.k<asfb.mdv <sdasdjn");
688}!);
689tex!(p => @begin{verbatim}(V) {}{}!);
690tex!(p => @begin{lstlisting}(V) {}{}!);
691tex!(p => @begin{stexcode}(V) {}{}!);
692
693tex!(p => @begin{general_listing}(V!) {}{}!);