metrale_grammar/earley/
accept.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//
3// Acceptable-next-byte computation for the Earley parser.
4//
5// The C++ matcher derives the set of bytes a parser can consume by
6// inspecting the outgoing char-range edges / sequence elements of every
7// latest scanable state. This module exposes that as a reusable query
8// so the W6 `GrammarMatcher` can build its token bitmask on top.
9
10use super::parser::EarleyParser;
11use super::state::ParserState;
12use crate::grammar::GrammarExprType;
13
14impl EarleyParser {
15    /// Append the byte ranges `state` can accept to `ranges` as
16    /// inclusive `(min, max)` pairs. FSM-backed states contribute their
17    /// char-range edges; non-FSM states contribute the current
18    /// element's byte/character-class ranges.
19    fn acceptable_ranges_of(&self, state: &ParserState, ranges: &mut Vec<(u8, u8)>) {
20        if state.rule_id != -1 {
21            let fsm = match self.grammar.per_rule_fsms[state.rule_id as usize].as_ref() {
22                Some(f) => f,
23                None => return,
24            };
25            for edge in fsm.fsm().edges(state.element_id as usize) {
26                if edge.is_char_range() {
27                    ranges.push((edge.min as u8, edge.max as u8));
28                }
29            }
30            return;
31        }
32        let seq = self.grammar.expr(state.sequence_id);
33        if state.element_id as usize >= seq.len() {
34            return;
35        }
36        let element = self.grammar.expr(seq[state.element_id as usize]);
37        match element.kind {
38            GrammarExprType::ByteString if (state.sub_element_id as usize) < element.len() => {
39                let b = element[state.sub_element_id as usize] as u8;
40                ranges.push((b, b));
41            }
42            GrammarExprType::CharacterClass | GrammarExprType::CharacterClassStar => {
43                // Only the ASCII portion of the ranges is a direct byte
44                // range; multi-byte handling is deferred to `advance`.
45                let mut i = 1;
46                while i + 1 < element.len() {
47                    let lo = element[i].clamp(0, 255) as u8;
48                    let hi = element[i + 1].clamp(0, 255) as u8;
49                    if element[i] <= 255 {
50                        ranges.push((lo, hi));
51                    }
52                    i += 2;
53                }
54            }
55            _ => {}
56        }
57    }
58
59    /// All byte ranges acceptable as the next input, across every
60    /// latest scanable state. Ranges may overlap and are unsorted.
61    pub fn acceptable_byte_ranges(&self) -> Vec<(u8, u8)> {
62        let mut ranges = Vec::new();
63        for state in self.latest_scanable_states() {
64            self.acceptable_ranges_of(state, &mut ranges);
65        }
66        ranges
67    }
68
69    /// A 256-entry bitmap: `out[b]` is true if byte `b` can be the next
70    /// input. This is the exact acceptable-next-byte set.
71    pub fn acceptable_byte_mask(&self) -> [bool; 256] {
72        let mut mask = [false; 256];
73        for (lo, hi) in self.acceptable_byte_ranges() {
74            for b in lo..=hi {
75                mask[b as usize] = true;
76            }
77        }
78        mask
79    }
80
81    /// True if byte `ch` can be accepted as the next input without
82    /// mutating the parser. Note: for negative character classes this
83    /// is a conservative pre-check; `advance` is authoritative.
84    pub fn can_accept(&self, ch: u8) -> bool {
85        self.acceptable_byte_mask()[ch as usize]
86    }
87}