flams_stex/quickparse/latex/
rules.rs

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