1use super::super::feature::class_expression;
11use super::super::{
12 CharClassItem, FeatureExpr, Grammar, GrammarDeclarations, GrammarExpr, GrammarFormat,
13 GrammarImportError, GrammarMacro, GrammarRule, GrammarScanner, MATCHING_MODES, Operation,
14 PrecedenceEntry, RuleAttributes, RuleKind, UnicodeClassItem, settling_problem,
15};
16use super::feature_forms::{FormCodec, render_feature_form, render_operation};
17
18const MAX_BOUND_DIGITS: usize = 9;
20
21fn native_error(line: Option<usize>, detail: impl Into<String>) -> GrammarImportError {
22 let detail = detail.into();
23 GrammarImportError::Parse {
24 format: GrammarFormat::MetaLanguage,
25 message: line.map_or_else(|| detail.clone(), |line| format!("line {line}: {detail}")),
26 }
27}
28
29#[must_use]
36pub fn render_native_grammar(grammar: &Grammar) -> String {
37 let mut lines = Vec::new();
38 if let Some(format) = grammar.source_format() {
39 lines.push(format!("format {}", format.as_str()));
40 }
41 if let Some(start) = grammar.start_rule() {
42 lines.push(format!("start {}", render_name(&start.name)));
43 }
44 let declarations = grammar.declarations();
45 let names = |names: &[String], separator: &str| {
46 names
47 .iter()
48 .map(|name| render_name(name))
49 .collect::<Vec<_>>()
50 .join(separator)
51 };
52 let parameters = |parameters: &[String]| {
53 if parameters.is_empty() {
54 String::new()
55 } else {
56 format!("({})", names(parameters, ", "))
57 }
58 };
59 let operations = |operations: &[Operation]| {
60 operations
61 .iter()
62 .map(|operation| render_operation(operation, &LineCodec))
63 .collect::<Vec<_>>()
64 .join(", ")
65 };
66 if let Some(matching) = &declarations.matching {
67 lines.push(format!("matching {matching}"));
68 }
69 if let Some(settling) = &declarations.settling {
70 lines.push(format!("settling {}", settling.join(" ")));
71 }
72 for name in &declarations.imports {
73 lines.push(format!("import {}", render_name(name)));
74 }
75 for name in &declarations.modes {
76 lines.push(format!("mode {}", render_name(name)));
77 }
78 for extra in &declarations.extras {
79 lines.push(format!("extra {}", render_native_expression(extra)));
80 }
81 for group in &declarations.conflicts {
82 lines.push(format!("conflict {}", names(group, " ")));
83 }
84 for order in &declarations.precedences {
85 let entries = order
86 .iter()
87 .map(|entry| format!("{}({})", entry.kind(), render_name(entry.value())))
88 .collect::<Vec<_>>();
89 lines.push(format!("precedences {}", entries.join(" ")));
90 }
91 for declared in &declarations.macros {
92 lines.push(format!(
93 "macro {}{} = {}",
94 render_name(&declared.name),
95 parameters(&declared.parameters),
96 render_native_expression(&declared.expression)
97 ));
98 }
99 for scanner in &declarations.scanners {
100 lines.push(format!(
101 "scanner {} tokens({}) operations({})",
102 render_name(&scanner.name),
103 names(&scanner.tokens, ", "),
104 operations(&scanner.operations)
105 ));
106 }
107 for rule in grammar.rules() {
108 let attributes = &rule.attributes;
109 let mut line = format!(
110 "rule {}{} = {} {}",
111 render_name(&rule.name),
112 parameters(&attributes.parameters),
113 rule.kind().as_str(),
114 render_native_expression(rule.expr())
115 );
116 let mut attribute = |head: &str, body: String| {
117 line.push(' ');
118 line.push_str(head);
119 line.push('(');
120 line.push_str(&body);
121 line.push(')');
122 };
123 if let Some(channel) = &attributes.channel {
124 attribute("channel", render_name(channel));
125 }
126 if let Some(modes) = &attributes.modes {
127 attribute("modes", names(modes, ", "));
128 }
129 if let Some(action) = &attributes.action {
130 attribute("action", operations(action));
131 }
132 lines.push(line);
133 }
134 lines.into_iter().map(|line| line + "\n").collect()
135}
136
137struct LineCodec;
139
140impl FormCodec for LineCodec {
141 fn name(&self, value: &str) -> String {
142 render_name(value)
143 }
144
145 fn text(&self, value: &str) -> String {
146 quote(value)
147 }
148
149 fn expression(&self, expr: &GrammarExpr) -> String {
150 render_native_expression(expr)
151 }
152
153 fn call(&self, head: &str, parts: Vec<String>) -> String {
154 if parts.is_empty() {
155 head.to_owned()
156 } else {
157 format!("{head}({})", parts.join(", "))
158 }
159 }
160
161 fn block(&self, word: &str, parts: Vec<String>) -> String {
162 format!("{word}({})", parts.join(", "))
163 }
164
165 fn byte_class(&self, negated: bool, items: Vec<String>) -> String {
166 format!(
167 "{}({})",
168 if negated { "notByteClass" } else { "byteClass" },
169 items.join(", ")
170 )
171 }
172}
173
174#[must_use]
176pub fn render_native_feature(feature: &FeatureExpr) -> String {
177 match feature {
178 FeatureExpr::UnicodeClass { negated, items } => {
179 let items = items
180 .iter()
181 .map(|item| match item {
182 UnicodeClassItem::Char(value) => format!("char({})", quote_char(*value)),
183 UnicodeClassItem::Range(start, end) => render_range(*start, *end),
184 UnicodeClassItem::Category(value) => format!("category({})", quote(value)),
185 UnicodeClassItem::Script(value) => format!("script({})", quote(value)),
186 })
187 .collect::<Vec<_>>()
188 .join(", ");
189 format!("{}({items})", if *negated { "notClass" } else { "class" })
190 }
191 FeatureExpr::Call { name, arguments } => format!(
192 "ref({}, {})",
193 render_name(name),
194 arguments
195 .iter()
196 .map(render_native_expression)
197 .collect::<Vec<_>>()
198 .join(", ")
199 ),
200 FeatureExpr::Form(_) | FeatureExpr::ByteClass { .. } => {
201 render_feature_form(feature, &LineCodec).unwrap_or_default()
202 }
203 }
204}
205
206#[must_use]
208pub fn render_native_expression(expr: &GrammarExpr) -> String {
209 let list = |items: &[GrammarExpr]| {
210 items
211 .iter()
212 .map(render_native_expression)
213 .collect::<Vec<_>>()
214 .join(", ")
215 };
216 match expr {
217 GrammarExpr::Empty => "empty".to_owned(),
218 GrammarExpr::AnyChar => "any".to_owned(),
219 GrammarExpr::Terminal(value) => format!("literal({})", quote(value)),
220 GrammarExpr::TerminalInsensitive(value) => format!("literalInsensitive({})", quote(value)),
221 GrammarExpr::CharRange(start, end) => render_range(*start, *end),
222 GrammarExpr::CharClass { negated, items } => {
223 let items = items
224 .iter()
225 .map(|item| match item {
226 CharClassItem::Range(start, end) => render_range(*start, *end),
227 CharClassItem::Char(value) => format!("char({})", quote_char(*value)),
228 })
229 .collect::<Vec<_>>()
230 .join(", ");
231 format!("{}({items})", if *negated { "notClass" } else { "class" })
232 }
233 GrammarExpr::NonTerminal(name) => format!("ref({})", render_name(name)),
234 GrammarExpr::Choice {
235 ordered,
236 alternatives,
237 } => format!(
238 "{}({})",
239 if *ordered { "orderedChoice" } else { "choice" },
240 list(alternatives)
241 ),
242 GrammarExpr::Sequence(items) => format!("seq({})", list(items)),
243 GrammarExpr::Optional(inner) => format!("optional({})", render_native_expression(inner)),
244 GrammarExpr::ZeroOrMore(inner) => format!("repeat0({})", render_native_expression(inner)),
245 GrammarExpr::OneOrMore(inner) => format!("repeat1({})", render_native_expression(inner)),
246 GrammarExpr::And(inner) => format!("and({})", render_native_expression(inner)),
247 GrammarExpr::Not(inner) => format!("not({})", render_native_expression(inner)),
248 GrammarExpr::Repeat { expr, min, max } => format!(
249 "repeat({}, {min}, {})",
250 render_native_expression(expr),
251 max.map_or_else(|| "unbounded".to_owned(), |max| max.to_string())
252 ),
253 GrammarExpr::Capture { label, expr } => format!(
254 "capture({}, {})",
255 label.as_deref().map_or_else(|| "null".to_owned(), quote),
256 render_native_expression(expr)
257 ),
258 GrammarExpr::Feature(feature) => render_native_feature(feature),
259 }
260}
261
262fn render_range(start: char, end: char) -> String {
263 format!("range({}, {})", quote_char(start), quote_char(end))
264}
265
266pub(super) fn quote(value: &str) -> String {
267 serde_json::to_string(value).unwrap_or_default()
268}
269
270fn quote_char(value: char) -> String {
271 quote(&value.to_string())
272}
273
274const fn is_name_stop(value: char) -> bool {
276 value.is_whitespace() || matches!(value, '(' | ')' | ',' | '"' | '=')
277}
278
279pub(super) fn render_name(name: &str) -> String {
280 if !name.is_empty() && !name.chars().any(is_name_stop) {
281 name.to_owned()
282 } else {
283 quote(name)
284 }
285}
286
287pub fn parse_native_grammar(source: &str) -> Result<Grammar, GrammarImportError> {
294 let mut rules: Vec<GrammarRule> = Vec::new();
295 let mut start: Option<(String, usize)> = None;
296 let mut source_format: Option<GrammarFormat> = None;
297 let mut declarations = GrammarDeclarations::default();
298 for (index, text) in source.split('\n').enumerate() {
299 let line = index + 1;
300 let text = text.strip_suffix('\r').unwrap_or(text);
301 let mut cursor = Cursor {
302 chars: text.chars().collect(),
303 line,
304 position: 0,
305 };
306 cursor.skip_spaces();
307 if cursor.done() || cursor.peek() == Some('#') {
308 continue;
309 }
310 let directive = cursor.word()?;
311 cursor.require_space()?;
312 match directive.as_str() {
313 "format" => {
314 if source_format.is_some() {
315 return Err(cursor.fail("the format is given twice"));
316 }
317 let tag = cursor.word()?;
318 source_format = Some(
319 GrammarFormat::from_tag(&tag)
320 .ok_or_else(|| cursor.fail(format!("unknown source format {tag}")))?,
321 );
322 }
323 "start" => {
324 if start.is_some() {
325 return Err(cursor.fail("the start rule is given twice"));
326 }
327 start = Some((cursor.name()?, line));
328 }
329 "rule" => {
330 let name = cursor.name()?;
331 let parameters = cursor.parameters()?;
332 cursor.skip_spaces();
333 cursor.expect('=')?;
334 cursor.skip_spaces();
335 let kind = cursor.word()?;
336 let kind = RuleKind::from_tag(&kind)
337 .ok_or_else(|| cursor.fail(format!("unknown rule kind {kind}")))?;
338 cursor.require_space()?;
339 let expr = cursor.expression()?;
340 if rules.iter().any(|rule| rule.name == name) {
341 return Err(cursor.fail(format!("rule {name} is defined twice")));
342 }
343 let mut attributes = cursor.rule_attributes()?;
344 attributes.parameters = parameters;
345 rules.push(
346 GrammarRule::new(name, expr)
347 .with_kind(kind)
348 .with_attributes(attributes),
349 );
350 }
351 "matching" => {
352 if declarations.matching.is_some() {
353 return Err(cursor.fail("the matching is given twice"));
354 }
355 let matching = cursor.word()?;
356 if !MATCHING_MODES.contains(&matching.as_str()) {
357 return Err(cursor.fail(format!("unknown matching {matching}")));
358 }
359 declarations.matching = Some(matching);
360 }
361 "settling" => {
362 if declarations.settling.is_some() {
363 return Err(cursor.fail("the settling is given twice"));
364 }
365 let mut steps = Vec::new();
366 while !cursor.done() {
367 steps.push(cursor.word()?);
368 cursor.skip_spaces();
369 }
370 if let Some(problem) = settling_problem(&steps) {
371 return Err(cursor.fail(problem));
372 }
373 declarations.settling = Some(steps);
374 }
375 "import" => declarations.imports.push(cursor.name()?),
376 "mode" => declarations.modes.push(cursor.name()?),
377 "extra" => declarations.extras.push(cursor.expression()?),
378 "conflict" => {
379 let mut group = vec![cursor.name()?];
380 cursor.skip_spaces();
381 while !cursor.done() {
382 group.push(cursor.name()?);
383 cursor.skip_spaces();
384 }
385 declarations.conflicts.push(group);
386 }
387 "precedences" => {
388 let mut order = Vec::new();
389 while !cursor.done() {
390 let kind = cursor.word()?;
391 let mut values = cursor.list(Cursor::name)?;
392 let Some(entry) = (values.len() == 1)
393 .then(|| values.pop())
394 .flatten()
395 .and_then(|value| PrecedenceEntry::from_kind(&kind, value))
396 else {
397 return Err(cursor.fail(if kind == "name" || kind == "rule" {
398 format!("{kind}(...) names one entry")
399 } else {
400 format!("a precedence entry is name(NAME) or rule(NAME), not {kind}")
401 }));
402 };
403 order.push(entry);
404 cursor.skip_spaces();
405 }
406 if order.len() < 2 {
407 return Err(cursor.fail("precedences orders at least two entries"));
408 }
409 declarations.precedences.push(order);
410 }
411 "macro" => {
412 let name = cursor.name()?;
413 let parameters = cursor.parameters()?;
414 cursor.skip_spaces();
415 cursor.expect('=')?;
416 cursor.skip_spaces();
417 let expression = cursor.expression()?;
418 declarations.macros.push(GrammarMacro {
419 name,
420 parameters,
421 expression,
422 });
423 }
424 "scanner" => {
425 let name = cursor.name()?;
426 cursor.require_space()?;
427 if cursor.word()? != "tokens" {
428 return Err(cursor.fail("expected tokens(...)"));
429 }
430 let tokens = cursor.list(Cursor::name)?;
431 cursor.require_space()?;
432 if cursor.word()? != "operations" {
433 return Err(cursor.fail("expected operations(...)"));
434 }
435 let operations = cursor.statements()?;
436 declarations.scanners.push(GrammarScanner {
437 name,
438 tokens,
439 operations,
440 });
441 }
442 other => return Err(cursor.fail(format!("unknown directive {other}"))),
443 }
444 cursor.skip_spaces();
445 if !cursor.done() {
446 return Err(cursor.fail("unexpected text at the end of the line"));
447 }
448 }
449 let Some(first) = rules.first().map(|rule| rule.name.clone()) else {
450 return Err(native_error(None, "the listing defines no rules"));
451 };
452 let start = match start {
453 Some((name, line)) => {
454 if !rules.iter().any(|rule| rule.name == name) {
455 return Err(native_error(
456 Some(line),
457 format!("start rule {name} is not defined"),
458 ));
459 }
460 name
461 }
462 None => first,
463 };
464 let mut grammar = Grammar::new();
465 for rule in rules {
466 grammar.add_rule(rule);
467 }
468 grammar.set_start(start);
469 if let Some(format) = source_format {
470 grammar.set_source_format(format);
471 }
472 grammar.set_declarations(declarations);
473 Ok(grammar)
474}
475
476pub fn parse_native_expression(text: &str) -> Result<GrammarExpr, GrammarImportError> {
483 let mut cursor = Cursor {
484 chars: text.chars().collect(),
485 line: 1,
486 position: 0,
487 };
488 cursor.skip_spaces();
489 let expr = cursor.expression()?;
490 cursor.skip_spaces();
491 if cursor.done() {
492 Ok(expr)
493 } else {
494 Err(cursor.fail("unexpected text after the expression"))
495 }
496}
497
498pub(super) struct Cursor {
499 pub(super) chars: Vec<char>,
500 pub(super) line: usize,
501 pub(super) position: usize,
502}
503
504impl Cursor {
505 pub(super) fn fail(&self, detail: impl Into<String>) -> GrammarImportError {
506 native_error(Some(self.line), detail)
507 }
508
509 pub(super) const fn done(&self) -> bool {
510 self.position >= self.chars.len()
511 }
512
513 pub(super) fn peek(&self) -> Option<char> {
514 self.chars.get(self.position).copied()
515 }
516
517 pub(super) fn text(&self, begin: usize) -> String {
518 self.chars[begin..self.position].iter().collect()
519 }
520
521 pub(super) fn skip_spaces(&mut self) {
522 while matches!(self.peek(), Some(' ' | '\t')) {
523 self.position += 1;
524 }
525 }
526
527 pub(super) fn require_space(&mut self) -> Result<(), GrammarImportError> {
528 if !matches!(self.peek(), Some(' ' | '\t')) {
529 return Err(self.fail("expected a space"));
530 }
531 self.skip_spaces();
532 Ok(())
533 }
534
535 pub(super) fn expect(&mut self, expected: char) -> Result<(), GrammarImportError> {
536 if self.peek() != Some(expected) {
537 return Err(self.fail(format!("expected {expected}")));
538 }
539 self.position += 1;
540 Ok(())
541 }
542
543 pub(super) fn word(&mut self) -> Result<String, GrammarImportError> {
544 let begin = self.position;
545 while self
546 .peek()
547 .is_some_and(|value| value.is_ascii_alphanumeric() || matches!(value, '_' | '-'))
548 {
549 self.position += 1;
550 }
551 if begin == self.position {
552 return Err(self.fail("expected a word"));
553 }
554 Ok(self.text(begin))
555 }
556
557 pub(super) fn name(&mut self) -> Result<String, GrammarImportError> {
558 if self.peek() == Some('"') {
559 return self.string();
560 }
561 let begin = self.position;
562 while self.peek().is_some_and(|value| !is_name_stop(value)) {
563 self.position += 1;
564 }
565 if begin == self.position {
566 return Err(self.fail("expected a name"));
567 }
568 Ok(self.text(begin))
569 }
570
571 pub(super) fn string(&mut self) -> Result<String, GrammarImportError> {
572 if self.peek() != Some('"') {
573 return Err(self.fail("expected a string"));
574 }
575 let begin = self.position;
576 self.position += 1;
577 while let Some(value) = self.peek() {
578 if value == '"' {
579 break;
580 }
581 self.position += if value == '\\' { 2 } else { 1 };
582 }
583 if self.done() {
584 return Err(self.fail("unterminated string"));
585 }
586 self.position += 1;
587 serde_json::from_str::<String>(&self.text(begin)).map_err(|_| self.fail("invalid string"))
588 }
589
590 pub(super) fn character(&mut self) -> Result<char, GrammarImportError> {
591 let value = self.string()?;
592 let mut chars = value.chars();
593 match (chars.next(), chars.next()) {
594 (Some(value), None) => Ok(value),
595 _ => Err(self.fail("a character must be one code point")),
596 }
597 }
598
599 fn bound(&mut self, allow_unbounded: bool) -> Result<Option<usize>, GrammarImportError> {
600 if allow_unbounded && self.peek() == Some('u') {
601 if self.word()? != "unbounded" {
602 return Err(self.fail("expected a repetition bound"));
603 }
604 return Ok(None);
605 }
606 let begin = self.position;
607 while self.peek().is_some_and(|value| value.is_ascii_digit()) {
608 self.position += 1;
609 }
610 let digits = self.text(begin);
611 if digits.is_empty() {
612 return Err(self.fail("expected a repetition bound"));
613 }
614 if digits.len() > MAX_BOUND_DIGITS {
615 return Err(self.fail(format!("repetition bound {digits} is too large")));
616 }
617 digits
618 .parse()
619 .map(Some)
620 .map_err(|_| self.fail("expected a repetition bound"))
621 }
622
623 pub(super) fn separator(&mut self) -> Result<(), GrammarImportError> {
624 self.skip_spaces();
625 self.expect(',')?;
626 self.skip_spaces();
627 Ok(())
628 }
629
630 pub(super) fn open(&mut self) -> Result<(), GrammarImportError> {
631 self.expect('(')?;
632 self.skip_spaces();
633 Ok(())
634 }
635
636 pub(super) fn close(&mut self) -> Result<(), GrammarImportError> {
637 self.skip_spaces();
638 self.expect(')')
639 }
640
641 pub(super) fn list<T>(
642 &mut self,
643 item: fn(&mut Self) -> Result<T, GrammarImportError>,
644 ) -> Result<Vec<T>, GrammarImportError> {
645 self.open()?;
646 let mut items = Vec::new();
647 self.skip_spaces();
648 while self.peek() != Some(')') {
649 if !items.is_empty() {
650 self.separator()?;
651 }
652 items.push(item(self)?);
653 self.skip_spaces();
654 if self.done() {
655 return Err(self.fail("expected )"));
656 }
657 }
658 self.close()?;
659 Ok(items)
660 }
661
662 fn inner(&mut self) -> Result<Box<GrammarExpr>, GrammarImportError> {
663 self.open()?;
664 let expr = self.expression()?;
665 self.close()?;
666 Ok(Box::new(expr))
667 }
668
669 pub(super) fn expression(&mut self) -> Result<GrammarExpr, GrammarImportError> {
670 let kind = self.word()?;
671 Ok(match kind.as_str() {
672 "empty" => GrammarExpr::Empty,
673 "any" => GrammarExpr::AnyChar,
674 "literal" | "literalInsensitive" => {
675 self.open()?;
676 let value = self.string()?;
677 self.close()?;
678 if kind == "literal" {
679 GrammarExpr::Terminal(value)
680 } else {
681 GrammarExpr::TerminalInsensitive(value)
682 }
683 }
684 "range" => {
685 self.open()?;
686 let start = self.character()?;
687 self.separator()?;
688 let end = self.character()?;
689 self.close()?;
690 GrammarExpr::CharRange(start, end)
691 }
692 "class" | "notClass" => {
693 let negated = kind == "notClass";
694 class_expression(negated, self.list(Self::class_item)?)
695 }
696 "ref" => {
697 self.open()?;
698 let name = self.name()?;
699 let mut arguments = Vec::new();
700 self.skip_spaces();
701 while self.peek() == Some(',') {
702 self.separator()?;
703 arguments.push(self.expression()?);
704 self.skip_spaces();
705 }
706 self.close()?;
707 if arguments.is_empty() {
708 GrammarExpr::NonTerminal(name)
709 } else {
710 GrammarExpr::feature(FeatureExpr::Call { name, arguments })
711 }
712 }
713 "choice" | "orderedChoice" => GrammarExpr::Choice {
714 ordered: kind == "orderedChoice",
715 alternatives: self.list(Self::expression)?,
716 },
717 "seq" => GrammarExpr::Sequence(self.list(Self::expression)?),
718 "repeat" => {
719 self.open()?;
720 let expr = Box::new(self.expression()?);
721 self.separator()?;
722 let min = self.bound(false)?.unwrap_or_default();
723 self.separator()?;
724 let max = self.bound(true)?;
725 self.close()?;
726 if max.is_some_and(|max| max < min) {
727 let max = max.unwrap_or_default();
728 return Err(self.fail(format!("repetition bounds {min}, {max} are reversed")));
729 }
730 GrammarExpr::Repeat { expr, min, max }
731 }
732 "capture" => {
733 self.open()?;
734 let label = if self.peek() == Some('"') {
735 Some(self.string()?)
736 } else if self.word()? == "null" {
737 None
738 } else {
739 return Err(self.fail("expected a capture label or null"));
740 };
741 self.separator()?;
742 let expr = Box::new(self.expression()?);
743 self.close()?;
744 GrammarExpr::Capture { label, expr }
745 }
746 other => {
747 if let Some(feature) = self.feature(other)? {
748 return Ok(feature);
749 }
750 match other {
751 "optional" => GrammarExpr::Optional(self.inner()?),
752 "repeat0" => GrammarExpr::ZeroOrMore(self.inner()?),
753 "repeat1" => GrammarExpr::OneOrMore(self.inner()?),
754 "and" => GrammarExpr::And(self.inner()?),
755 "not" => GrammarExpr::Not(self.inner()?),
756 _ => return Err(self.fail(format!("unknown expression {other}"))),
757 }
758 }
759 })
760 }
761
762 fn class_item(&mut self) -> Result<UnicodeClassItem, GrammarImportError> {
763 let kind = self.word()?;
764 self.open()?;
765 let item = match kind.as_str() {
766 "char" => UnicodeClassItem::Char(self.character()?),
767 "range" => {
768 let start = self.character()?;
769 self.separator()?;
770 UnicodeClassItem::Range(start, self.character()?)
771 }
772 "category" => UnicodeClassItem::Category(self.string()?),
773 "script" => UnicodeClassItem::Script(self.string()?),
774 other => return Err(self.fail(format!("unknown class item {other}"))),
775 };
776 self.close()?;
777 Ok(item)
778 }
779
780 fn parameters(&mut self) -> Result<Vec<String>, GrammarImportError> {
782 if self.peek() != Some('(') {
783 return Ok(Vec::new());
784 }
785 let names = self.list(Self::name)?;
786 if names.is_empty() {
787 return Err(self.fail("a parameter list names at least one parameter"));
788 }
789 let mut seen = std::collections::BTreeSet::new();
790 if !names.iter().all(|name| seen.insert(name)) {
791 return Err(self.fail("a parameter is named twice"));
792 }
793 Ok(names)
794 }
795
796 fn rule_attributes(&mut self) -> Result<RuleAttributes, GrammarImportError> {
799 const ORDER: [&str; 3] = ["channel", "modes", "action"];
800 let mut attributes = RuleAttributes::default();
801 let mut next = 0;
802 self.skip_spaces();
803 while !self.done() {
804 let begin = self.position;
805 while self.peek().is_some_and(|value| value.is_ascii_alphabetic()) {
806 self.position += 1;
807 }
808 let attribute = self.text(begin);
809 let position = ORDER.iter().position(|word| *word == attribute);
810 let Some(position) = position.filter(|_| self.peek() == Some('(')) else {
811 self.position = begin;
812 return Err(self.fail("unexpected text at the end of the line"));
813 };
814 if position < next {
815 return Err(self.fail(format!("rule attribute {attribute} is out of order")));
816 }
817 next = position + 1;
818 match position {
819 0 => {
820 self.open()?;
821 attributes.channel = Some(self.name()?);
822 self.close()?;
823 }
824 1 => attributes.modes = Some(self.list(Self::name)?),
825 _ => attributes.action = Some(self.statements()?),
826 }
827 self.skip_spaces();
828 }
829 Ok(attributes)
830 }
831}