Skip to main content

rowan_peg/
lib.rs

1use core::fmt;
2use std::{collections::HashSet, fmt::Display, mem::take};
3use char_classes::any;
4use linked_hash_map::LinkedHashMap as HashMap;
5
6use rowan::ast::AstNode;
7use to_true::{InTrue, ToTrue};
8use unicode_ident::{is_xid_continue, is_xid_start};
9use rowan_peg_utils::match_options;
10
11use crate::utils::UsedBound;
12
13mod utils;
14mod bootstarp;
15
16pub use bootstarp::*;
17
18impl Repeat {
19    pub fn count_bounds(&self) -> (u32, Option<u32>) {
20        if self.plus().is_some() {
21            (1, None)
22        } else if let Some(rest) = self.repeat_rest() {
23            let lower_bound = self.number().as_ref().map_or(0, value::number);
24            let upper_bound = rest.number().as_ref().map(value::number);
25            (lower_bound, upper_bound)
26        } else if let Some(number) = self.number() {
27            let bound = value::number(&number);
28            (bound, bound.into())
29        } else {
30            unreachable!()
31        }
32    }
33}
34
35pub mod value {
36    use crate::{SyntaxKind as Kind, Label, SyntaxToken};
37
38    #[track_caller]
39    pub fn string(s: &SyntaxToken) -> &str {
40        debug_assert_eq!(s.kind(), Kind::STRING);
41        let s = s.text();
42        &s[1..s.len()-1]
43    }
44
45    #[track_caller]
46    pub fn matches(s: &SyntaxToken) -> &str {
47        debug_assert_eq!(s.kind(), Kind::MATCHES);
48        let s = s.text();
49        &s[1..s.len()-1]
50    }
51
52    #[track_caller]
53    pub fn label(l: &Label) -> String {
54        l.ident()
55            .map(|ident| ident.text().to_owned())
56            .unwrap_or_else(|| string(&l.string().unwrap()).to_owned())
57    }
58
59    pub fn number(s: &SyntaxToken) -> u32 {
60        debug_assert_eq!(s.kind(), Kind::NUMBER);
61        s.text().parse().unwrap()
62    }
63}
64
65#[derive(Debug)]
66pub enum Error {
67    EmptyLiteral(SyntaxToken),
68    UnknownLiteral(SyntaxToken),
69    MatchesWithoutSlice(SyntaxToken),
70    DisallowedSlice(SyntaxNode),
71}
72
73impl Display for Error {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        match self {
76            Error::EmptyLiteral(t) => {
77                write!(f, "empty literal {:?}", t.text())
78            },
79            Error::UnknownLiteral(t) => {
80                write!(f, "unknown literal {:?}", t.text())
81            },
82            Error::MatchesWithoutSlice(t) => {
83                write!(f, "matches without slice {:?}", t.text())
84            },
85            Error::DisallowedSlice(t) => {
86                write!(f, "disallowed slice {:?}", t.text())
87            },
88        }
89    }
90}
91
92type Result<T, E = Error> = core::result::Result<T, E>;
93
94#[derive(Debug, PartialEq, Eq)]
95enum Method {
96    Optional,
97    Strict,
98    Many,
99}
100
101#[derive(Debug, PartialEq, Eq)]
102struct DeclMeta {
103    methods: Vec<(String, Method)>,
104    docs: String,
105}
106
107pub struct Processor<W: fmt::Write> {
108    out: W,
109    kind_names_map: HashMap<String, String>,
110    slice: u32,
111    is_token_decl: bool,
112    exports: HashMap<String, String>,
113    decl_name: String,
114    refs_bound: HashMap<String, UsedBound>,
115    is_tokens: HashSet<String>,
116    decls: HashMap<String, DeclMeta>,
117}
118
119impl<W: fmt::Write> From<W> for Processor<W> {
120    fn from(out: W) -> Self {
121        Self {
122            out,
123            kind_names_map: HashMap::new(),
124            slice: 0,
125            is_token_decl: false,
126            exports: HashMap::new(),
127            decl_name: String::new(),
128            refs_bound: HashMap::new(),
129            is_tokens: HashSet::new(),
130            decls: HashMap::new(),
131        }
132    }
133}
134
135const PRE_DEFINE_ITEMS: &str = {
136r#"// Generated by rowan-peg, do not edit it
137use rowan::{ast::{support, AstChildren, AstNode}, Language};
138
139macro_rules! decl_ast_node {
140    ($node:ident, $kind:ident $(, #[$meta:meta])?) => {
141        $(#[$meta])?
142        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
143        pub struct $node(SyntaxNode);
144        impl AstNode for $node {
145            type Language = Lang;
146
147            fn syntax(&self) -> &rowan::SyntaxNode<Self::Language> {
148                &self.0
149            }
150
151            fn can_cast(kind: <Self::Language as Language>::Kind) -> bool {
152                kind == SyntaxKind::$kind
153            }
154
155            fn cast(node: rowan::SyntaxNode<Self::Language>) -> Option<Self> {
156                if Self::can_cast(node.kind()) {
157                    Some(Self(node))
158                } else {
159                    None
160                }
161            }
162        }
163        impl core::fmt::Display for $node {
164            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
165                core::fmt::Display::fmt(self.syntax(), f)
166            }
167        }
168    };
169}
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
172pub enum Lang {}
173impl Language for Lang {
174    type Kind = SyntaxKind;
175
176    fn kind_to_raw(kind: Self::Kind) -> ::rowan::SyntaxKind {
177        kind.into()
178    }
179
180    fn kind_from_raw(raw: ::rowan::SyntaxKind) -> Self::Kind {
181        raw.into()
182    }
183}
184
185pub type SyntaxNode = ::rowan::SyntaxNode<Lang>;
186pub type SyntaxToken = ::rowan::SyntaxToken<Lang>;
187"#};
188const PRE_DEFINE_RULES: &str = r#""#;
189
190impl<W: fmt::Write> Processor<W> {
191    fn gen_tok_wrap<F, R>(&mut self, kind: &str, f: F) -> R
192    where F: FnOnce(&mut Self) -> R,
193    {
194        write!(self.out, "(g:({{state.quiet().guard_token(SyntaxKind::{kind})}}) s:$((").unwrap();
195        let result = f(self);
196        write!(self.out, ")) {{g.accept_token(s)}})").unwrap();
197        result
198    }
199
200    fn gen_node_wrap<F, R>(&mut self, kind: &str, f: F) -> R
201    where F: FnOnce(&mut Self) -> R,
202    {
203        write!(self.out, "(g:({{state.guard(SyntaxKind::{kind})}}) (").unwrap();
204        let result = f(self);
205        write!(self.out, ") {{g.accept()}})").unwrap();
206        result
207    }
208
209    fn gen_quiet_wrap<F, R>(&mut self, f: F) -> R
210    where F: FnOnce(&mut Self) -> R,
211    {
212        write!(self.out, "(g:({{state.quiet().guard_none()}}) (quiet!{{").unwrap();
213        let result = f(self);
214        write!(self.out, "}}) {{g.accept_none()}})").unwrap();
215        result
216    }
217
218    fn gen_back_wrap<F, R>(&mut self, f: F) -> R
219    where F: FnOnce(&mut Self) -> R,
220    {
221        write!(self.out, "(g:({{state.guard_none()}})(").unwrap();
222        let result = f(self);
223        write!(self.out, "){{g.accept_none()}})").unwrap();
224        result
225    }
226
227    fn regist_name(&mut self, name: &str) -> (String, String) {
228        let name = utils::rule_name_of(name);
229        let kind_name = self.kind_names_map.entry(name.to_owned())
230            .or_insert_with(|| utils::kind_name_of(self.exports.get(&name).unwrap_or(&name)));
231        (name, kind_name.clone())
232    }
233
234    fn regist_tok_name(&mut self, token: &SyntaxToken) -> Result<(String, String)> {
235        let content = if token.kind() == SyntaxKind::STRING { value::string(token) } else { value::matches(token) };
236        if content.is_empty() {
237            return Err(Error::EmptyLiteral(token.clone()));
238        }
239        let (name, kind_name) = if let Some(name) = utils::punct_name_of(content) {
240            (name.to_owned(), utils::kind_name_of(&name))
241        } else if is_xid_start(content.chars().next().unwrap())
242            && content.chars().all(|ch| matches!(ch, '-' | '_') || is_xid_continue(ch))
243        {
244            let name = utils::rule_name_of(&format!("{content}_kw"));
245            let kind_name = utils::kind_name_of(&name);
246            (name, kind_name)
247        } else {
248            return Err(Error::UnknownLiteral(token.to_owned()));
249        };
250
251        self.is_tokens.insert(name.clone());
252        self.kind_names_map.insert(name.clone(), kind_name.clone());
253
254        Ok((name, kind_name))
255    }
256
257    fn add_bound(&mut self, name: impl Into<String>) {
258        if !self.is_token_decl {
259            let mut name = name.into();
260            if let Some(renamed_name) = self.exports.get(&name) {
261                name = renamed_name.to_owned();
262            }
263            *self.refs_bound.entry(name).or_default() += 1;
264        }
265    }
266
267    fn dis_refs_bound<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
268        let refs_bound = self.take_refs_bound();
269        let result = f(self);
270        self.refs_bound = refs_bound;
271        result
272    }
273
274    #[must_use]
275    fn take_refs_bound(&mut self) -> HashMap<String, UsedBound> {
276        take(&mut self.refs_bound)
277    }
278
279    pub fn start_process(&mut self, decl_list: &DeclList) -> Result<()> {
280        for export in decl_list.export_list().iter().flat_map(|list| list.exports()) {
281            let name = export.ident();
282            let new_name = export.named()
283                .map_or(name.clone(), |it| it.ident());
284            self.exports.insert(
285                utils::rule_name_of(name.text()),
286                utils::rule_name_of(new_name.text()),
287            );
288        }
289
290        writeln!(self.out, "{PRE_DEFINE_ITEMS}").unwrap();
291        writeln!(self.out, "::peg::parser!(pub grammar parser<'b>(state: \
292            &'b ::rowan_peg_utils::ParseState<'input>) for str {{").unwrap();
293        writeln!(self.out, "{PRE_DEFINE_RULES}").unwrap();
294        for decl in decl_list.decls() {
295            self.process_decl(decl)?;
296        }
297        writeln!(self.out, "}});").unwrap();
298        writeln!(self.out, "#[repr(u16)]").unwrap();
299        writeln!(self.out, "#[allow(non_camel_case_types)]").unwrap();
300        writeln!(self.out, "#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]").unwrap();
301        writeln!(self.out, "pub enum SyntaxKind {{").unwrap();
302        let mut first = true;
303        let mut last = None;
304        for kind in self.kind_names_map.values() {
305            first.to_false(|| {
306                writeln!(self.out, "    {kind} = 0,").unwrap();
307            }).unwrap_or_else(|| {
308                writeln!(self.out, "    {kind},").unwrap();
309            });
310            last = kind.into();
311        }
312        writeln!(self.out, "}}").unwrap();
313        writeln!(self.out, "impl From<::rowan::SyntaxKind> for SyntaxKind {{ \
314            fn from(kind: ::rowan::SyntaxKind) -> Self {{ \
315                ::core::assert!(kind.0 <= Self::{} as u16); \
316                unsafe {{ ::core::mem::transmute::<u16, SyntaxKind>(kind.0) }} \
317            }} \
318        }}", last.unwrap()).unwrap();
319        writeln!(self.out, "impl From<SyntaxKind> for ::rowan::SyntaxKind {{ \
320            fn from(kind: SyntaxKind) -> Self {{ \
321                ::rowan::SyntaxKind(kind as u16) \
322            }} \
323        }}").unwrap();
324        for (rule_name, mut meta) in self.decls.drain() {
325            if self.is_tokens.contains(&rule_name) { continue }
326            let node_name = utils::node_name_of(&rule_name);
327            let node_kind = utils::kind_name_of(&rule_name);
328            meta.methods.sort_by(|a, b| a.0.cmp(&b.0));
329
330            writeln!(self.out, "decl_ast_node!(\
331                    {node_name}, \
332                    {node_kind}, \
333                    #[doc = {:?}]\
334                    );",
335                    meta.docs,
336            ).unwrap();
337            writeln!(self.out, "impl {node_name} {{").unwrap();
338            for (child_name, method) in meta.methods {
339                let is_token = self.is_tokens.contains(&child_name);
340                let mut base_ty = if is_token {
341                    "SyntaxToken".to_owned()
342                } else {
343                    utils::node_name_of(&child_name)
344                };
345                base_ty = match method {
346                    Method::Optional => format!("Option<{base_ty}>"),
347                    Method::Strict => base_ty,
348                    Method::Many if is_token => "impl Iterator<Item = SyntaxToken>".into(),
349                    Method::Many => format!("AstChildren<{base_ty}>"),
350                };
351                let body = if is_token {
352                    let kind = utils::kind_name_of(&child_name);
353                    match method {
354                        Method::Optional => format!("support::token(self.syntax(), SyntaxKind::{kind})"),
355                        Method::Strict => format!("support::token(self.syntax(), SyntaxKind::{kind}).unwrap()"),
356                        Method::Many => format!("::rowan_peg_utils::tokens(self.syntax(), SyntaxKind::{kind})"),
357                    }
358                } else {
359                    match method {
360                        Method::Optional => "support::child(self.syntax())",
361                        Method::Strict => "support::child(self.syntax()).unwrap()",
362                        Method::Many => "support::children(self.syntax())",
363                    }.into()
364                };
365                let method_name = if method == Method::Many {
366                    if is_token {
367                        format!("{child_name}_tokens")
368                    } else if child_name.ends_with('s') {
369                        format!("{child_name}es")
370                    } else {
371                        format!("{child_name}s")
372                    }
373                } else {
374                    child_name.clone()
375                };
376                if let Some(punct) = utils::punct_of(&child_name)
377                    && punct.trim() == punct
378                {
379                    let hint = child_name.replace('_', " ");
380                    if punct.contains('`') {
381                        writeln!(self.out, r#"    /// Get {hint} `` {punct} ``"#).unwrap();
382                    } else {
383                        writeln!(self.out, r#"    /// Get {hint} `{punct}`"#).unwrap();
384                    }
385                    if !punct.chars().any(any!("'\" \t\r\n_\\")) {
386                        writeln!(self.out, r#"    #[doc(alias = {punct:?})]"#).unwrap();
387                    }
388                }
389                writeln!(self.out, "    pub fn {method_name}(&self) -> {base_ty} {{").unwrap();
390                writeln!(self.out, "        {body}").unwrap();
391                writeln!(self.out, "    }}").unwrap();
392            }
393            writeln!(self.out, "}}").unwrap();
394        }
395        Ok(())
396    }
397
398    fn decl_is_token(&self, decl: &Decl) -> bool {
399        let Some(list) = utils::one_elem(decl.pat_choice().pat_lists()) else { return false };
400        let Some(op) = utils::one_elem(list.pat_ops()) else { return false };
401        op.dollar().is_some()
402    }
403
404    fn process_decl(&mut self, decl: Decl) -> Result<()> {
405        let (name, kind_name) = self.regist_name(decl.named().ident().text());
406        self.is_token_decl = self.decl_is_token(&decl);
407        self.decl_name = name;
408        let name = &self.decl_name;
409        let mut vis = "";
410
411        if self.is_token_decl {
412            let new_name = self.exports.get(name).unwrap_or(name);
413            self.is_tokens.insert(name.clone());
414            self.is_tokens.insert(new_name.clone());
415        }
416        if let Some(new_name) = self.exports.get(name) {
417            if name == new_name {
418                vis = "pub ";
419            } else {
420                writeln!(self.out, "    pub rule {new_name}() = {name}").unwrap();
421            }
422        }
423
424        self.refs_bound.clear();
425        write!(self.out, "    {vis}rule {name}() = ").unwrap();
426        if self.is_token_decl {
427            write!(self.out, "()").unwrap();
428            self.process_pat_choice(decl.pat_choice())?;
429        } else {
430            self.gen_node_wrap(&kind_name, |this| {
431                this.process_pat_choice(decl.pat_choice())
432            })?;
433        }
434
435        writeln!(self.out).unwrap();
436        let methods = self.refs_bound.iter().filter_map(|(name, bound)| {
437            let ty = match bound {
438                UsedBound(0, 0) => return None,
439                UsedBound(0, 1) => Method::Optional,
440                UsedBound(1, 1) => Method::Strict,
441                _ => Method::Many,
442            };
443            Some((name.clone(), ty))
444        }).collect();
445        let name = self.exports.get(&self.decl_name).unwrap_or(&self.decl_name);
446        self.decls.insert(name.clone(), DeclMeta {
447            methods,
448            docs: format!("```abnf\n{decl}\n```"),
449        });
450        Ok(())
451    }
452
453    fn process_pat_choice(&mut self, patchoice: PatChoice) -> Result<()> {
454        let mut first = true;
455        let refs_bound = self.take_refs_bound();
456        let mut prev_bound: Option<HashMap<String, UsedBound>> = None;
457        write!(self.out, "(").unwrap();
458        for patlist in patchoice.pat_lists() {
459            first.in_false(|| write!(self.out, " / ").unwrap());
460            write!(self.out, "()").unwrap();
461            self.gen_back_wrap(|this| this.process_pat_list(patlist))?;
462            if let Some(prev_bound) = &mut prev_bound {
463                self.merge_cover_to(prev_bound);
464            } else {
465                prev_bound = Some(self.take_refs_bound());
466            }
467        }
468        assert_eq!(self.refs_bound.len(), 0);
469        self.merge_add(refs_bound);
470        self.merge_add(prev_bound.unwrap());
471        if let Some(expected) = patchoice.pat_expect() {
472            let name = value::label(&expected.label());
473            write!(self.out, " / expected!({name:?})").unwrap();
474        }
475        write!(self.out, ")").unwrap();
476        Ok(())
477    }
478
479    fn merge_cover_to(&mut self, prev_bound: &mut HashMap<String, UsedBound>) {
480        for key in self.refs_bound.keys() {
481            if !prev_bound.contains_key(key) {
482                prev_bound.insert(key.clone(), UsedBound::default());
483            }
484        }
485        for (key, value) in &mut *prev_bound {
486            let other = self.refs_bound.get(key).copied().unwrap_or_default();
487            *value = value.cover(other);
488        }
489        self.refs_bound.clear();
490    }
491
492    fn merge_add(&mut self, refs_bound: HashMap<String, UsedBound>) {
493        for (key, value) in refs_bound {
494            *self.refs_bound.entry(key).or_default() += value;
495        }
496    }
497
498    fn process_pat_list(&mut self, patlist: PatList) -> Result<()> {
499        let mut first = true;
500        for patop in patlist.pat_ops() {
501            first.in_false(|| write!(self.out, " ").unwrap());
502            self.process_patop(patop)?;
503        }
504        Ok(())
505    }
506
507    fn process_patop(&mut self, patop: PatOp) -> Result<()> {
508        let atom = patop.pat_atom();
509        if patop.amp().is_some() {
510            write!(self.out, "&").unwrap();
511            self.gen_quiet_wrap(|this| this.dis_refs_bound(|this| this.process_patatom(atom)))?;
512        } else if patop.bang().is_some() {
513            write!(self.out, "!").unwrap();
514            self.gen_quiet_wrap(|this| this.dis_refs_bound(|this| this.process_patatom(atom)))?;
515        } else if patop.tilde().is_some() {
516            write!(self.out, "quiet!{{").unwrap();
517            self.dis_refs_bound(|this| this.process_patatom(atom))?;
518            write!(self.out, "}}").unwrap();
519        } else if patop.dollar().is_some() {
520            if self.is_token_decl && self.slice == 0 {
521                let name = &self.decl_name.clone();
522                let (_, kind_name) = self.regist_name(name);
523                self.slice += 1;
524                self.gen_tok_wrap(&kind_name, |this| {
525                    this.dis_refs_bound(|this| this.process_patatom(atom))
526                })?;
527                self.slice -= 1;
528            } else {
529                return Err(Error::DisallowedSlice(patop.syntax().clone()));
530            }
531        } else if let Some(repeat) = patop.repeat() {
532            let refs_bound = self.take_refs_bound();
533            self.gen_back_wrap(|this| this.process_patatom(atom))?;
534            let (lower_bound, upper_bound) = repeat.count_bounds();
535            match (lower_bound, upper_bound) {
536                (1, None) => write!(self.out, "+"),
537                (0, None) => write!(self.out, "*"),
538                (lower, None) => write!(self.out, "*<{lower},>"),
539                (lower, Some(upper)) => write!(self.out, "*<{lower},{upper}>"),
540            }.unwrap();
541            let repeat_meta = UsedBound(
542                lower_bound.try_into().unwrap(),
543                upper_bound.unwrap_or(255).try_into().unwrap(),
544            );
545            self.refs_bound.iter_mut().for_each(|(_, bound)| *bound *= repeat_meta);
546            self.merge_add(refs_bound);
547        } else {
548            self.process_patatom(atom)?;
549        }
550        Ok(())
551    }
552
553    fn process_patatom(&mut self, atom: PatAtom) -> Result<()> {
554        match_options! {match atom {
555            l_paren as _ => self.process_pat_choice(atom.pat_choice().unwrap())?,
556            l_brack as _ => {
557                let refs_bound = self.take_refs_bound();
558                self.gen_back_wrap(|this| this.process_pat_choice(atom.pat_choice().unwrap()))?;
559                write!(self.out, "?").unwrap();
560                self.refs_bound.iter_mut().for_each(|(_, bound)| bound.0 = 0);
561                self.merge_add(refs_bound);
562            },
563            ident => {
564                let name = utils::rule_name_of(ident.text());
565                write!(self.out, "{}()", name).unwrap();
566                self.add_bound(name);
567            },
568            string => self.tok_or_in_slice(&string)?,
569            matches if value::matches(&matches).chars().count() == 1 => {
570                // special unit string
571                self.tok_or_in_slice(&matches)?;
572            },
573            matches => {
574                if self.slice == 0 {
575                    return Err(Error::MatchesWithoutSlice(matches));
576                }
577                let content = value::matches(&matches);
578                write!(self.out, "(quiet!{{").unwrap();
579                if let Some(pat) = content.strip_prefix('^') {
580                    write!(self.out, "[^::char_classes::any!(@\"{pat}\")]").unwrap();
581                } else {
582                    write!(self.out, "[::char_classes::any!(@\"{content}\")]").unwrap();
583                }
584                write!(self.out, "}}/expected!({:?}))", matches.text()).unwrap();
585            },
586            _ => unreachable!(),
587        }}
588        Ok(())
589    }
590
591    fn tok_or_in_slice(&mut self, token: &SyntaxToken) -> Result<()> {
592        let content = if token.kind() == SyntaxKind::STRING {
593            value::string(token)
594        } else {
595            value::matches(token)
596        };
597        if self.slice == 0 {
598            let (name, kind_name) = self.regist_tok_name(token)?;
599            self.add_bound(name);
600            self.gen_tok_wrap(&kind_name, |this| {
601                write!(this.out, "{content:?}").unwrap();
602            });
603        } else {
604            write!(self.out, "{content:?}").unwrap();
605        }
606        Ok(())
607    }
608}
609
610pub fn quick_process(src: &str) -> Result<String, String> {
611    let state = &mut rowan_peg_utils::ParseState::default();
612    match parser::decl_list(src, state) {
613        Ok(()) => (),
614        Err(e) => {
615            return Err(format!("parse grammar {e}"));
616        },
617    }
618    let syntax_node = SyntaxNode::new_root(state.finish());
619    let decl_list = DeclList::cast(syntax_node).unwrap();
620    let mut buf = String::new();
621    let mut proc = Processor::from(&mut buf);
622    match proc.start_process(&decl_list) {
623        Ok(()) => {},
624        Err(e) => {
625            let range = match &e {
626                Error::EmptyLiteral(tok)
627                | Error::UnknownLiteral(tok)
628                | Error::MatchesWithoutSlice(tok) => tok.text_range(),
629                Error::DisallowedSlice(node) => node.text_range(),
630            };
631            let index = range.start().into();
632            let (line, col) = line_column::line_column(src, index);
633            return Err(format!("processing error at {line}:{col} {e}"));
634        },
635    }
636    Ok(buf)
637}
638
639#[cfg(test)]
640mod tests {
641    use rowan::TextSize;
642
643    use super::*;
644
645    #[test]
646    fn full_parser() {
647        let s = r#"
648;; use ABNF like grammar
649;; char-val to case-sensitive
650;; prose-val -> regexp
651;; add peg lookaheads `!` `&`
652;; add quiet `~`
653;; add slice `$`
654;; remove num-var
655;;
656;; vim:nowrap
657
658comment     = ~<;[^\n]*(?:\n|$)> @comment
659_           = ~<[ \t\r\n]*> [comment _]
660ident       = ~<(?![0-9])(?:[0-9a-zA-Z\-_]|[^\x00-\xa0])+> @ident
661number      = ~<[0-9]+> @number
662string      = ~(<"> <[^\"\r\n]*> <">) @string
663match       = ~("<" <[^\x3e\r\n]*> ">") @match
664label       = ident / string
665repeat      = "+"
666            / "*" [number]
667            / number ["*" [number]]
668patatom     = ident !(_ "=")            ; a rule reference
669            / string                    ; keyword
670            / match                     ; regular expressions
671            / "[" _ patchoice _ "]"     ; optional
672            / "(" _ patchoice _ ")"     ; simple paren
673            / "{" _ patchoice _ "}"     ; list group brace
674patrepeat   = repeat _ patatom
675            / patatom
676patop       = "&" patrepeat ; positive lookahead
677            / "!" patrepeat ; negative lookahead
678            / "~" patrepeat ; quiet
679            / "$" patrepeat ; slice
680            / patrepeat
681patlist     = patop *(_ patop)
682patchoice   = patlist *(_ "/" _ patlist)
683              *(_ "@" label); extra expected branch
684decl        = ident _ "=" _ patchoice
685decl-list   = +(_ decl) _
686    "#;
687        let mut state = rowan_peg_utils::ParseState::default();
688        parser::decl_list(s, &state).unwrap();
689        dbg!(&state);
690        let node = SyntaxNode::new_root(state.finish());
691        dbg!(&node);
692        assert_eq!(TextSize::of(s), node.text_range().end());
693        dbg!(&s.len());
694        let decl_list = DeclList::cast(node).unwrap();
695        println!("{decl_list}")
696    }
697}