LCOV - code coverage report
Current view: top level - dashql-parser/src/parser - probe.rs Coverage Total Hit
Test: DashQL 915828b Lines: 93.9 % 213 200
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : //! The LR probes of the completion.
       2              : //!
       3              : //! The probes run the automaton without grammar actions, only the state numbers matter:
       4              : //! - [`parse_until_with_snapshot`] parses up to the cursor token, returns the expected symbols there
       5              : //!   and a snapshot of the state stack,
       6              : //! - [`parse_until_after`] feeds a symbol at the cursor and returns the expected symbols after it,
       7              : //! - [`probe_suffix_batch`] and [`probe_suffix_sequence`] replay the tokens after the cursor from a
       8              : //!   snapshot to test whether candidate symbols fit the rest of the script.
       9              : 
      10              : use super::driver::{lac_check, table_action, Action, StateId};
      11              : use crate::scanner::Symbol;
      12              : use crate::symbol_kind::*;
      13              : use crate::tables::*;
      14              : use crate::SymbolKind;
      15              : 
      16              : /// A completion replay stops after this many reductions without a shift. A restored prefix can enter
      17              : /// nullable reduction cycles that a regular parse never reaches.
      18              : const MAX_REDUCTIONS_WITHOUT_SHIFT: usize = 128;
      19              : 
      20              : /// The state stack before the read of the cursor token
      21              : #[derive(Clone, Debug, Default)]
      22              : pub struct PrefixSnapshot {
      23              :     /// The states, bottom to top
      24              :     pub state_stack: Vec<StateId>,
      25              :     /// Did the prefix parse reach the cursor without failing or accepting first?
      26              :     pub reached_target: bool,
      27              : }
      28              : 
      29              : /// The expected symbols at the cursor and the snapshot they were computed from
      30              : #[derive(Clone, Debug, Default)]
      31              : pub struct ExpectedAtCursor {
      32              :     /// The tokens the parser would eventually shift at the cursor, empty if the cursor was not reached
      33              :     pub expected: Vec<SymbolKind>,
      34              :     /// The state stack before the read of the cursor token
      35              :     pub prefix: PrefixSnapshot,
      36              : }
      37              : 
      38              : /// How a fed symbol fits the tokens after the cursor
      39              : #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
      40              : pub struct SuffixProbe {
      41              :     /// Did the next real token shift after the fed symbols?
      42              :     pub k1_compatible: bool,
      43              :     /// The number of real tokens shifted before an error or the accept
      44              :     pub depth_consumed: u16,
      45              :     /// Did the replay accept?
      46              :     pub reached_eof: bool,
      47              : }
      48              : 
      49              : /// The token reader of the probes, a `TokenStream` without semantic values
      50              : struct Tokens<'a> {
      51              :     /// The scanned symbols
      52              :     symbols: &'a [Symbol],
      53              :     /// The index of the next symbol to read, EOF past the end
      54              :     next_index: u32,
      55              : }
      56              : 
      57              : impl Tokens<'_> {
      58       142698 :     fn next(&mut self) -> SymbolKind {
      59       142698 :         match self.symbols.get(self.next_index as usize) {
      60       142203 :             Some(symbol) => {
      61       142203 :                 self.next_index += 1;
      62       142203 :                 symbol.kind
      63              :             }
      64          495 :             None => S_YYEOF,
      65              :         }
      66       142698 :     }
      67              : }
      68              : 
      69              : /// The hooks of the driver
      70              : trait Policy {
      71              :     /// Inspect or replace a token that was just read. `pre_index` is the token index before the read,
      72              :     /// rewind `tokens` to it to read the real token again later.
      73              :     fn on_lookahead_read(&mut self, states: &[StateId], next: &mut SymbolKind, pre_index: u32, tokens: &mut Tokens, stop: &mut bool);
      74        32818 :     fn on_shifted(&mut self, _kind: SymbolKind) {}
      75            0 :     fn on_accept(&mut self) {}
      76              :     /// Stop instead of recovering from an error?
      77            6 :     fn short_circuit_on_error(&mut self, _states: &[StateId]) -> bool {
      78            6 :         false
      79            6 :     }
      80              : }
      81              : 
      82              : /// Run the automaton without actions
      83        21109 : fn drive<P: Policy>(policy: &mut P, states: &mut Vec<StateId>, tokens: &mut Tokens) {
      84        21109 :     let mut lookahead: Option<SymbolKind> = None;
      85        21109 :     let mut lac_established = false;
      86        21109 :     let mut scratch = Vec::new();
      87        21109 :     let mut errstatus = 0;
      88        21109 :     let mut reductions_without_shift = 0;
      89              : 
      90              :     enum Label {
      91              :         NewState,
      92              :         Default,
      93              :         Reduce(usize),
      94              :         ErrLab,
      95              :         ErrLab1,
      96              :     }
      97        21109 :     let mut label = Label::NewState;
      98              :     loop {
      99      2037466 :         label = match label {
     100              :             Label::NewState => {
     101       786179 :                 let state = *states.last().unwrap();
     102       786179 :                 if state == YYFINAL {
     103        12389 :                     policy.on_accept();
     104        12389 :                     return;
     105       773790 :                 }
     106       773790 :                 if YYPACT[state as usize] as i32 == YYPACT_NINF {
     107       259818 :                     Label::Default
     108              :                 } else {
     109       513972 :                     let kind = match lookahead {
     110       371274 :                         Some(kind) => kind,
     111              :                         None => {
     112       142698 :                             let pre_index = tokens.next_index;
     113       142698 :                             let mut next = tokens.next();
     114       142698 :                             let mut stop = false;
     115       142698 :                             policy.on_lookahead_read(states, &mut next, pre_index, tokens, &mut stop);
     116       142698 :                             if stop {
     117          405 :                                 return;
     118       142293 :                             }
     119       142293 :                             lookahead = Some(next);
     120       142293 :                             next
     121              :                         }
     122              :                     };
     123       513567 :                     if kind == S_YYerror {
     124              :                         // The scanner already reported the error, recover directly
     125            0 :                         lookahead = Some(S_YYUNDEF);
     126            0 :                         Label::ErrLab1
     127              :                     } else {
     128       513567 :                         let mut establish = |states: &[StateId]| {
     129       379619 :                             if !lac_established {
     130        84047 :                                 lac_established = true;
     131        84047 :                                 return lac_check(states, kind, &mut scratch);
     132       295572 :                             }
     133       295572 :                             true
     134       379619 :                         };
     135       513567 :                         match table_action(state, kind) {
     136              :                             None => {
     137       360347 :                                 if establish(states) {
     138       351974 :                                     Label::Default
     139              :                                 } else {
     140         8373 :                                     Label::ErrLab
     141              :                                 }
     142              :                             }
     143            0 :                             Some(Action::Error) => Label::ErrLab,
     144        19272 :                             Some(Action::Reduce(rule)) => {
     145        19272 :                                 if establish(states) {
     146        19272 :                                     Label::Reduce(rule)
     147              :                                 } else {
     148            0 :                                     Label::ErrLab
     149              :                                 }
     150              :                             }
     151       133948 :                             Some(Action::Shift(next)) => {
     152       133948 :                                 if errstatus > 0 {
     153            0 :                                     errstatus -= 1;
     154       133948 :                                 }
     155       133948 :                                 states.push(next);
     156       133948 :                                 lookahead = None;
     157       133948 :                                 lac_established = false;
     158       133948 :                                 reductions_without_shift = 0;
     159       133948 :                                 policy.on_shifted(kind);
     160       133948 :                                 Label::NewState
     161              :                             }
     162              :                         }
     163              :                     }
     164              :                 }
     165              :             }
     166       611792 :             Label::Default => match YYDEFACT[*states.last().unwrap() as usize] as usize {
     167            0 :                 0 => Label::ErrLab,
     168       611792 :                 rule => Label::Reduce(rule),
     169              :             },
     170       631064 :             Label::Reduce(rule) => {
     171       631064 :                 reductions_without_shift += 1;
     172       631064 :                 if reductions_without_shift > MAX_REDUCTIONS_WITHOUT_SHIFT {
     173            0 :                     return;
     174       631064 :                 }
     175       631064 :                 let len = YYR2[rule] as usize;
     176       631064 :                 let n = states.len();
     177       631064 :                 let next = super::driver::goto_state(states[n - 1 - len], YYR1[rule] as SymbolKind);
     178       631064 :                 states.truncate(n - len);
     179       631064 :                 states.push(next);
     180       631064 :                 Label::NewState
     181              :             }
     182              :             Label::ErrLab => {
     183         8373 :                 if policy.short_circuit_on_error(states) {
     184         8315 :                     return;
     185           58 :                 }
     186           58 :                 if errstatus == 3 {
     187           30 :                     match lookahead {
     188            0 :                         Some(S_YYEOF) => return,
     189           30 :                         Some(_) => lookahead = None,
     190            0 :                         None => {}
     191              :                     }
     192           28 :                 }
     193           58 :                 Label::ErrLab1
     194              :             }
     195              :             Label::ErrLab1 => {
     196           58 :                 errstatus = 3;
     197           58 :                 let next = loop {
     198          114 :                     if let Some(Action::Shift(next)) = table_action(*states.last().unwrap(), S_YYerror) {
     199           58 :                         break next;
     200           56 :                     }
     201           56 :                     if states.len() == 1 {
     202            0 :                         return;
     203           56 :                     }
     204           56 :                     states.pop();
     205              :                 };
     206           58 :                 lac_established = false;
     207           58 :                 states.push(next);
     208           58 :                 Label::NewState
     209              :             }
     210              :         };
     211              :     }
     212        21109 : }
     213              : 
     214              : /// All tokens that the parser would eventually shift
     215         5899 : pub fn collect_expected_symbols(states: &[StateId]) -> Vec<SymbolKind> {
     216         5899 :     let mut scratch = Vec::new();
     217         5899 :     (0..YYNTOKENS as SymbolKind)
     218      3014389 :         .filter(|&t| t != S_YYerror && t != S_YYUNDEF && lac_check(states, t, &mut scratch))
     219         5899 :         .collect()
     220         5899 : }
     221              : 
     222              : /// Capture the state stack when the cursor token would be read
     223              : struct PrefixCapture<'a> {
     224              :     /// The symbol index of the cursor token
     225              :     target: u32,
     226              :     /// The captured snapshot
     227              :     out: &'a mut PrefixSnapshot,
     228              : }
     229              : 
     230              : impl Policy for PrefixCapture<'_> {
     231         2214 :     fn on_lookahead_read(&mut self, states: &[StateId], _next: &mut SymbolKind, pre_index: u32, _tokens: &mut Tokens, stop: &mut bool) {
     232         2214 :         if pre_index >= self.target {
     233          405 :             self.out.reached_target = true;
     234          405 :             self.out.state_stack.extend_from_slice(states);
     235          405 :             *stop = true;
     236         1810 :         }
     237         2214 :     }
     238              : }
     239              : 
     240              : /// Parse until the cursor token and collect the expected symbols there
     241          405 : pub fn parse_until_with_snapshot(symbols: &[Symbol], target: u32) -> ExpectedAtCursor {
     242          405 :     let mut out = ExpectedAtCursor::default();
     243          405 :     let mut states = vec![0];
     244          405 :     let mut tokens = Tokens { symbols, next_index: 0 };
     245          405 :     drive(&mut PrefixCapture { target, out: &mut out.prefix }, &mut states, &mut tokens);
     246          405 :     if out.prefix.reached_target && !out.prefix.state_stack.is_empty() {
     247          405 :         out.expected = collect_expected_symbols(&out.prefix.state_stack);
     248          405 :     }
     249          405 :     out
     250          405 : }
     251              : 
     252              : /// Replace the cursor token with a symbol, then inject COMPLETE_HERE to collect the expected
     253              : /// symbols after it at the next error
     254              : struct CollectAfter {
     255              :     /// The symbol index of the cursor token
     256              :     target: u32,
     257              :     /// The symbol that replaces the cursor token
     258              :     feed: SymbolKind,
     259              :     /// 0 = parsing, 1 = fed the symbol, 2 = injected COMPLETE_HERE
     260              :     phase: u8,
     261              :     /// The expected symbols after the fed symbol, collected at the first error after COMPLETE_HERE
     262              :     expected: Vec<SymbolKind>,
     263              : }
     264              : 
     265              : impl Policy for CollectAfter {
     266        36533 :     fn on_lookahead_read(&mut self, _states: &[StateId], next: &mut SymbolKind, pre_index: u32, _tokens: &mut Tokens, _stop: &mut bool) {
     267        36533 :         if self.phase == 1 {
     268         5494 :             *next = S_COMPLETE_HERE;
     269         5494 :             self.phase = 2;
     270         5494 :             return;
     271        31039 :         }
     272        31039 :         if self.phase == 0 && pre_index >= self.target {
     273         5494 :             *next = self.feed;
     274         5494 :             self.phase = 1;
     275        25545 :         }
     276        36533 :     }
     277         5546 :     fn short_circuit_on_error(&mut self, states: &[StateId]) -> bool {
     278         5546 :         if self.phase == 2 {
     279         5494 :             self.expected = collect_expected_symbols(states);
     280         5494 :             return true;
     281           52 :         }
     282           52 :         false
     283         5546 :     }
     284              : }
     285              : 
     286              : /// Parse until the cursor token, feed a symbol instead, and collect the expected symbols after it
     287         5494 : pub fn parse_until_after(symbols: &[Symbol], target: u32, feed: SymbolKind) -> Vec<SymbolKind> {
     288         5494 :     let mut policy = CollectAfter {
     289         5494 :         target,
     290         5494 :         feed,
     291         5494 :         phase: 0,
     292         5494 :         expected: Vec::new(),
     293         5494 :     };
     294         5494 :     let mut states = vec![0];
     295         5494 :     let mut tokens = Tokens { symbols, next_index: 0 };
     296         5494 :     drive(&mut policy, &mut states, &mut tokens);
     297         5494 :     policy.expected
     298         5494 : }
     299              : 
     300              : /// Feed symbols at the cursor and count the real tokens that shift after them
     301              : struct SuffixProbePolicy<'a> {
     302              :     /// The symbols fed at the cursor
     303              :     feed: &'a [SymbolKind],
     304              :     /// Does the first fed symbol replace the cursor token instead of being inserted before it?
     305              :     replace_target: bool,
     306              :     /// The probe result
     307              :     probe: SuffixProbe,
     308              :     /// The number of fed symbols
     309              :     fed_count: usize,
     310              :     /// The number of fed symbols that shifted
     311              :     feed_shifted: usize,
     312              : }
     313              : 
     314              : impl Policy for SuffixProbePolicy<'_> {
     315       103951 :     fn on_lookahead_read(&mut self, _states: &[StateId], next: &mut SymbolKind, pre_index: u32, tokens: &mut Tokens, _stop: &mut bool) {
     316       103951 :         if self.fed_count < self.feed.len() {
     317              :             // Only the first fed symbol replaces the target, all other fed symbols are insertions
     318        15249 :             let consumes_target = self.replace_target && self.fed_count == 0;
     319        15249 :             if !consumes_target {
     320           39 :                 tokens.next_index = pre_index;
     321        15210 :             }
     322        15249 :             *next = self.feed[self.fed_count];
     323        15249 :             self.fed_count += 1;
     324        88702 :         }
     325       103951 :     }
     326       101130 :     fn on_shifted(&mut self, kind: SymbolKind) {
     327       101130 :         if self.feed_shifted < self.feed.len() {
     328        15249 :             self.feed_shifted += 1;
     329        15249 :             return;
     330        85881 :         }
     331        85881 :         if kind != S_YYEOF {
     332        73492 :             if self.probe.depth_consumed == 0 {
     333        13596 :                 self.probe.k1_compatible = true;
     334        60630 :             }
     335        73492 :             self.probe.depth_consumed = self.probe.depth_consumed.saturating_add(1);
     336        12389 :         }
     337       101130 :     }
     338        12389 :     fn on_accept(&mut self) {
     339        12389 :         self.probe.reached_eof = true;
     340        12389 :     }
     341         2821 :     fn short_circuit_on_error(&mut self, _states: &[StateId]) -> bool {
     342              :         // Recovering would consume the whole suffix without telling anything about the candidate
     343         2821 :         self.fed_count >= 1
     344         2821 :     }
     345              : }
     346              : 
     347              : /// Probe a sequence of symbols fed at the cursor against the tokens after it
     348        15214 : pub fn probe_suffix_sequence(symbols: &[Symbol], target: u32, prefix: &PrefixSnapshot, feed: &[SymbolKind], replace_target: bool) -> SuffixProbe {
     349        15214 :     if !prefix.reached_target || feed.is_empty() {
     350            4 :         return SuffixProbe::default();
     351        15210 :     }
     352        15210 :     let mut states = prefix.state_stack.clone();
     353        15210 :     if states.is_empty() {
     354            0 :         return SuffixProbe::default();
     355        15210 :     }
     356              :     // Advance the tokens to the target
     357        15210 :     let next_index = target.min(symbols.len() as u32);
     358        15210 :     let mut tokens = Tokens { symbols, next_index };
     359        15210 :     let mut policy = SuffixProbePolicy {
     360        15210 :         feed,
     361        15210 :         replace_target,
     362        15210 :         probe: SuffixProbe::default(),
     363        15210 :         fed_count: 0,
     364        15210 :         feed_shifted: 0,
     365        15210 :     };
     366        15210 :     drive(&mut policy, &mut states, &mut tokens);
     367        15210 :     policy.probe
     368        15214 : }
     369              : 
     370              : /// Probe every symbol on its own
     371           42 : pub fn probe_suffix_batch(symbols: &[Symbol], target: u32, prefix: &PrefixSnapshot, feed: &[SymbolKind], replace_target: bool) -> Vec<SuffixProbe> {
     372           42 :     if feed.is_empty() || !prefix.reached_target {
     373            0 :         return vec![SuffixProbe::default(); feed.len()];
     374           42 :     }
     375           42 :     feed.iter()
     376        15171 :         .map(|&kind| probe_suffix_sequence(symbols, target, prefix, &[kind], replace_target))
     377           42 :         .collect()
     378           42 : }
        

Generated by: LCOV version 2.0-1