LCOV - code coverage report
Current view: top level - dashql-parser/src/parser - driver.rs Coverage Total Hit
Test: DashQL 6e74948 Lines: 89.1 % 238 212
Test Date: 2026-11-06 17:46:50

            Line data    Source code
       1              : //! The LALR(1) driver, the `parse()` loop of bison's lalr1.cc skeleton configured with
       2              : //! `parse.lac full`, `parse.error verbose` plus an error hint, error recovery through the `error`
       3              : //! token, and a custom default location for reductions.
       4              : 
       5              : use super::actions::{ReduceError, ReduceStack};
       6              : use super::context::ParseContext;
       7              : use super::value::Value;
       8              : use crate::scanner::SymbolValue;
       9              : use crate::symbol_kind::*;
      10              : use crate::tables::*;
      11              : use crate::{symbol_name, SymbolKind};
      12              : use dashql_buffers::parser::SymbolSpan;
      13              : 
      14              : /// A parser state
      15              : pub type StateId = i32;
      16              : 
      17              : /// The state of `goto` after reducing to `lhs` in `state` (`yy_lr_goto_state_`)
      18     17242035 : pub fn goto_state(state: StateId, lhs: SymbolKind) -> StateId {
      19     17242035 :     let nterm = lhs as usize - YYNTOKENS;
      20     17242035 :     let r = YYPGOTO[nterm] as i32 + state;
      21     17242035 :     if (0..=YYLAST).contains(&r) && YYCHECK[r as usize] as i32 == state {
      22      9164770 :         YYTABLE[r as usize] as i32
      23              :     } else {
      24      8077265 :         YYDEFGOTO[nterm] as i32
      25              :     }
      26     17242035 : }
      27              : 
      28              : /// The action of a state on a token: `Some(Shift(state))`, `Some(Reduce(rule))`, or `None` for the
      29              : /// default action (or an error if the default is to fail).
      30              : #[derive(Clone, Copy, Debug, PartialEq, Eq)]
      31              : pub enum Action {
      32              :     Shift(StateId),
      33              :     Reduce(usize),
      34              :     Error,
      35              : }
      36              : 
      37              : /// The table action of `state` on `token`, `None` if the default action applies
      38     20265967 : pub fn table_action(state: StateId, token: SymbolKind) -> Option<Action> {
      39     20265967 :     let pact = YYPACT[state as usize] as i32;
      40     20265967 :     if pact == YYPACT_NINF {
      41      2904099 :         return None;
      42     17361868 :     }
      43     17361868 :     let i = pact + token as i32;
      44     17361868 :     if !(0..=YYLAST).contains(&i) || YYCHECK[i as usize] as i32 != token as i32 {
      45     15284053 :         return None;
      46      2077815 :     }
      47      2077815 :     let action = YYTABLE[i as usize] as i32;
      48      2077815 :     Some(if action > 0 {
      49      1960737 :         Action::Shift(action)
      50       117078 :     } else if action == YYTABLE_NINF {
      51           96 :         Action::Error
      52              :     } else {
      53       116982 :         Action::Reduce(-action as usize)
      54              :     })
      55     20265967 : }
      56              : 
      57              : /// Would the parser eventually shift `token` from the given stack of states? (`yy_lac_check_`)
      58              : ///
      59              : /// Simulates the reductions on a scratch stack, the stack itself is not modified.
      60      3275339 : pub fn lac_check(states: &[StateId], token: SymbolKind, scratch: &mut Vec<StateId>) -> bool {
      61      3275339 :     scratch.clear();
      62              :     // The number of states popped from `states`
      63      3275339 :     let mut lac_top = 0;
      64              :     loop {
      65     18962649 :         let top_state = *scratch.last().unwrap_or(&states[states.len() - 1 - lac_top]);
      66     18962649 :         let rule = match table_action(top_state, token) {
      67      1486009 :             Some(Action::Shift(_)) => return true,
      68           96 :             Some(Action::Error) => return false,
      69        75999 :             Some(Action::Reduce(rule)) => rule,
      70     17400545 :             None => match YYDEFACT[top_state as usize] as usize {
      71      1789234 :                 0 => return false,
      72     15611311 :                 rule => rule,
      73              :             },
      74              :         };
      75              :         // Pop the right-hand side, first from the scratch stack
      76     15687310 :         let mut len = YYR2[rule] as usize;
      77     15687310 :         if len < scratch.len() {
      78      6143049 :             scratch.truncate(scratch.len() - len);
      79      6143049 :             len = 0;
      80      9548471 :         } else if !scratch.is_empty() {
      81      7479834 :             len -= scratch.len();
      82      7479834 :             scratch.clear();
      83      7479834 :         }
      84     15687310 :         lac_top += len;
      85     15687310 :         let top_state = *scratch.last().unwrap_or(&states[states.len() - 1 - lac_top]);
      86     15687310 :         scratch.push(goto_state(top_state, YYR1[rule] as SymbolKind));
      87              :     }
      88      3275339 : }
      89              : 
      90              : /// Feed a token to a stack of states: reduce until the token is shifted. Returns false (and leaves
      91              : /// the stack in an unspecified state) if the token is a syntax error.
      92            0 : pub fn feed(states: &mut Vec<StateId>, token: SymbolKind) -> bool {
      93              :     loop {
      94            0 :         let top = *states.last().unwrap();
      95            0 :         let rule = match table_action(top, token) {
      96            0 :             Some(Action::Shift(next)) => {
      97            0 :                 states.push(next);
      98            0 :                 return true;
      99              :             }
     100            0 :             Some(Action::Error) => return false,
     101            0 :             Some(Action::Reduce(rule)) => rule,
     102            0 :             None => match YYDEFACT[top as usize] as usize {
     103            0 :                 0 => return false,
     104            0 :                 rule => rule,
     105              :             },
     106              :         };
     107            0 :         let len = YYR2[rule] as usize;
     108            0 :         states.truncate(states.len() - len);
     109            0 :         let next = goto_state(*states.last().unwrap(), YYR1[rule] as SymbolKind);
     110            0 :         states.push(next);
     111              :     }
     112            0 : }
     113              : 
     114              : /// The location of the left-hand side of a reduction (bison's `YYLLOC_DEFAULT`).
     115              : /// Uses wrapping uint32 arithmetic.
     116       924270 : fn default_location(rhs: &[SymbolSpan], below: SymbolSpan) -> SymbolSpan {
     117       924270 :     match (rhs.first(), rhs.last()) {
     118       773809 :         (Some(first), Some(last)) => SymbolSpan::new(
     119       773809 :             first.offset(),
     120       773809 :             last.offset().wrapping_add(last.length()).wrapping_sub(first.offset()),
     121              :         ),
     122       150461 :         _ => SymbolSpan::new(below.offset().wrapping_add(below.length()), 0),
     123              :     }
     124       924270 : }
     125              : 
     126              : /// A lookahead token
     127              : #[derive(Clone, Copy, Debug)]
     128              : struct Lookahead {
     129              :     /// The token kind
     130              :     kind: SymbolKind,
     131              :     /// The semantic value of the token
     132              :     value: Value,
     133              : }
     134              : 
     135              : /// The parser stack
     136              : struct Stack {
     137              :     /// The automaton states
     138              :     states: Vec<StateId>,
     139              :     /// The semantic values, parallel to the states
     140              :     values: Vec<Value>,
     141              :     /// The symbol-index locations, parallel to the states
     142              :     locations: Vec<SymbolSpan>,
     143              : }
     144              : 
     145              : impl Stack {
     146      1264383 :     fn push(&mut self, state: StateId, value: Value, location: SymbolSpan) {
     147      1264383 :         self.states.push(state);
     148      1264383 :         self.values.push(value);
     149      1264383 :         self.locations.push(location);
     150      1264383 :     }
     151              : 
     152       924434 :     fn pop(&mut self, n: usize) {
     153       924434 :         let len = self.states.len() - n;
     154       924434 :         self.states.truncate(len);
     155       924434 :         self.values.truncate(len);
     156       924434 :         self.locations.truncate(len);
     157       924434 :     }
     158              : 
     159      2175107 :     fn top(&self) -> StateId {
     160      2175107 :         *self.states.last().unwrap()
     161      2175107 :     }
     162              : }
     163              : 
     164              : impl ReduceStack for Stack {
     165      1050027 :     fn value_at(&self, i: usize) -> Value {
     166      1050027 :         self.values[self.values.len() - 1 - i]
     167      1050027 :     }
     168       266753 :     fn location_at(&self, i: usize) -> SymbolSpan {
     169       266753 :         self.locations[self.locations.len() - 1 - i]
     170       266753 :     }
     171              : }
     172              : 
     173              : /// The token stream of the parser: scanned symbols located by their
     174              : /// index, followed by EOF tokens of length 0 once the symbols are exhausted.
     175              : pub struct TokenStream {
     176              :     /// The index of the next symbol to return
     177              :     pub next_index: u32,
     178              : }
     179              : 
     180              : impl TokenStream {
     181       340741 :     pub fn next(&mut self, ctx: &ParseContext) -> (SymbolKind, Value, SymbolSpan) {
     182       340741 :         let index = self.next_index;
     183       340741 :         match ctx.symbols().get(index as usize) {
     184       340666 :             Some(symbol) => {
     185       340666 :                 self.next_index += 1;
     186       340666 :                 let value = match symbol.value {
     187       150728 :                     SymbolValue::None => Value::Empty,
     188        99997 :                     SymbolValue::Name(id) => Value::Name(id),
     189        89941 :                     SymbolValue::Text(text) => Value::Str(text),
     190              :                 };
     191       340666 :                 (symbol.kind, value, SymbolSpan::new(index, 1))
     192              :             }
     193           75 :             None => (S_YYEOF, Value::Empty, SymbolSpan::new(index, 0)),
     194              :         }
     195       340741 :     }
     196              : }
     197              : 
     198              : /// Where the parse loop continues
     199              : enum Label {
     200              :     NewState,
     201              :     Default,
     202              :     Reduce(usize),
     203              :     ErrLab,
     204              :     ErrLab1,
     205              : }
     206              : 
     207              : /// Build the verbose syntax error message (`yysyntax_error_`) and its hint.
     208          293 : fn syntax_error(states: &[StateId], lookahead: Option<SymbolKind>, scratch: &mut Vec<StateId>) -> (String, String) {
     209              :     const ARGS_MAX: usize = 5;
     210          293 :     let Some(token) = lookahead else {
     211            0 :         return ("syntax error".to_string(), String::new());
     212              :     };
     213          293 :     let mut args = vec![token];
     214          293 :     let mut overflow = false;
     215        32100 :     for t in 0..YYNTOKENS as SymbolKind {
     216        32100 :         if t == S_YYerror || t == S_YYUNDEF || !lac_check(states, t, scratch) {
     217        30724 :             continue;
     218         1376 :         }
     219         1376 :         if args.len() == ARGS_MAX {
     220          268 :             overflow = true;
     221          268 :             break;
     222         1108 :         }
     223         1108 :         args.push(t);
     224              :     }
     225          293 :     if overflow {
     226          268 :         args.truncate(1);
     227          268 :     }
     228          329 :     let names: Vec<_> = args.iter().map(|&a| symbol_name(a)).collect();
     229          293 :     let mut message = format!("syntax error, unexpected {}", names[0]);
     230          293 :     for (i, name) in names.iter().enumerate().skip(1) {
     231           36 :         message.push_str(if i == 1 { ", expecting " } else { " or " });
     232           36 :         message.push_str(name);
     233              :     }
     234              :     // Suggest a double-quoted identifier when a string literal is used where an identifier fits.
     235          293 :     let hint = if token == S_SCONST && lac_check(states, S_IDENT, scratch) {
     236            5 :         "string literals cannot be used as identifiers here; use a double-quoted identifier instead".to_string()
     237              :     } else {
     238          288 :         String::new()
     239              :     };
     240          293 :     (message, hint)
     241          293 : }
     242              : 
     243              : /// Run the parser with the grammar actions. Returns false if parsing was aborted.
     244         7386 : pub fn parse(ctx: &mut ParseContext) -> bool {
     245         7386 :     let mut stack = Stack {
     246         7386 :         states: vec![0],
     247         7386 :         values: vec![Value::Empty],
     248         7386 :         locations: vec![SymbolSpan::default()],
     249         7386 :     };
     250         7386 :     let mut tokens = TokenStream { next_index: 0 };
     251         7386 :     let mut lookahead: Option<Lookahead> = None;
     252              :     // The location of the lookahead; it outlives the lookahead itself, like bison's yyla.location.
     253         7386 :     let mut lookahead_location = SymbolSpan::default();
     254         7386 :     let mut lac_established = false;
     255         7386 :     let mut scratch = Vec::new();
     256         7386 :     let mut errstatus = 0;
     257         7386 :     let mut error_range_begin = SymbolSpan::default();
     258         7386 :     let mut label = Label::NewState;
     259              : 
     260              :     loop {
     261      3098630 :         label = match label {
     262              :             Label::NewState => {
     263      1271769 :                 let state = stack.top();
     264      1271769 :                 if state == YYFINAL {
     265         7360 :                     return true;
     266      1264409 :                 }
     267      1264409 :                 if YYPACT[state as usize] as i32 == YYPACT_NINF {
     268       476154 :                     Label::Default
     269              :                 } else {
     270       788255 :                     let la = match lookahead {
     271       447514 :                         Some(la) => la,
     272              :                         None => {
     273       340741 :                             let (kind, value, location) = tokens.next(ctx);
     274       340741 :                             lookahead_location = location;
     275       340741 :                             let la = Lookahead { kind, value };
     276       340741 :                             lookahead = Some(la);
     277       340741 :                             la
     278              :                         }
     279              :                     };
     280       788255 :                     let mut establish = |stack: &Stack| {
     281       448142 :                         if !lac_established {
     282       157179 :                             lac_established = true;
     283       157179 :                             return lac_check(&stack.states, la.kind, &mut scratch);
     284       290963 :                         }
     285       290963 :                         true
     286       448142 :                     };
     287       788255 :                     match table_action(state, la.kind) {
     288              :                         None => {
     289       426431 :                             if establish(&stack) {
     290       425802 :                                 Label::Default
     291              :                             } else {
     292          629 :                                 Label::ErrLab
     293              :                             }
     294              :                         }
     295            0 :                         Some(Action::Error) => Label::ErrLab,
     296        21711 :                         Some(Action::Reduce(rule)) => {
     297        21711 :                             if establish(&stack) {
     298        21705 :                                 Label::Reduce(rule)
     299              :                             } else {
     300            6 :                                 Label::ErrLab
     301              :                             }
     302              :                         }
     303       340113 :                         Some(Action::Shift(next)) => {
     304       340113 :                             if errstatus > 0 {
     305          411 :                                 errstatus -= 1;
     306       339702 :                             }
     307       340113 :                             stack.push(next, la.value, lookahead_location);
     308       340113 :                             lookahead = None;
     309       340113 :                             lac_established = false;
     310       340113 :                             Label::NewState
     311              :                         }
     312              :                     }
     313              :                 }
     314              :             }
     315       901956 :             Label::Default => match YYDEFACT[stack.top() as usize] as usize {
     316            0 :                 0 => Label::ErrLab,
     317       901956 :                 rule => Label::Reduce(rule),
     318              :             },
     319       923661 :             Label::Reduce(rule) => {
     320       923661 :                 let len = YYR2[rule] as usize;
     321       923661 :                 let n = stack.states.len();
     322       923661 :                 let lhs_state = goto_state(stack.states[n - 1 - len], YYR1[rule] as SymbolKind);
     323       923661 :                 let location = default_location(&stack.locations[n - len..], stack.locations[n - 1]);
     324       923661 :                 error_range_begin = location;
     325       923661 :                 match reduce(ctx, rule, &stack, location) {
     326       923661 :                     Ok(value) => {
     327       923661 :                         if std::mem::take(&mut ctx.state.borrow_mut().clear_lookahead) {
     328          602 :                             lookahead = None;
     329       923059 :                         }
     330       923661 :                         stack.pop(len);
     331       923661 :                         stack.push(lhs_state, value.unwrap_or(Value::Empty), location);
     332       923661 :                         Label::NewState
     333              :                     }
     334              :                     Err(ReduceError::YyError) => {
     335            0 :                         if std::mem::take(&mut ctx.state.borrow_mut().clear_lookahead) {
     336            0 :                             lookahead = None;
     337            0 :                         }
     338              :                         // yyerrorlab: do not reclaim the symbols of the rule
     339            0 :                         stack.pop(len);
     340            0 :                         Label::ErrLab1
     341              :                     }
     342              :                 }
     343              :             }
     344              :             Label::ErrLab => {
     345          635 :                 if errstatus == 0 {
     346          293 :                     let (message, hint) = syntax_error(&stack.states, lookahead.map(|la| la.kind), &mut scratch);
     347          293 :                     ctx.state.borrow_mut().pending_hint = hint;
     348          293 :                     ctx.error(lookahead_location, &message);
     349          342 :                 }
     350          635 :                 error_range_begin = lookahead_location;
     351          635 :                 if errstatus == 3 {
     352              :                     // Discard the lookahead that failed to be reused after an error
     353          311 :                     match lookahead {
     354          311 :                         Some(la) if la.kind == S_YYEOF => return false,
     355          285 :                         Some(_) => lookahead = None,
     356            0 :                         None => {}
     357              :                     }
     358          324 :                 }
     359          609 :                 Label::ErrLab1
     360              :             }
     361              :             Label::ErrLab1 => {
     362          609 :                 errstatus = 3;
     363              :                 // Pop states until one shifts the error token
     364          609 :                 let next = loop {
     365         1382 :                     if let Some(Action::Shift(next)) = table_action(stack.top(), S_YYerror) {
     366          609 :                         break next;
     367          773 :                     }
     368          773 :                     if stack.states.len() == 1 {
     369            0 :                         return false;
     370          773 :                     }
     371          773 :                     error_range_begin = *stack.locations.last().unwrap();
     372          773 :                     stack.pop(1);
     373              :                 };
     374          609 :                 let error_location = default_location(&[error_range_begin, lookahead_location], SymbolSpan::default());
     375          609 :                 lac_established = false;
     376          609 :                 stack.push(next, Value::Empty, error_location);
     377          609 :                 Label::NewState
     378              :             }
     379              :         };
     380              :     }
     381         7386 : }
     382              : 
     383              : /// Run the action of a rule
     384       923661 : fn reduce(ctx: &mut ParseContext, rule: usize, stack: &Stack, location: SymbolSpan) -> Result<Option<Value>, ReduceError> {
     385       923661 :     crate::tables::reduce(ctx, rule, stack, location)
     386       923661 : }
     387              : 
     388              : #[cfg(test)]
     389              : mod tests {
     390              :     use super::*;
     391              : 
     392              :     #[test]
     393            1 :     fn rejects_unknown_tokens() {
     394            1 :         let mut scratch = Vec::new();
     395            1 :         assert!(lac_check(&[0], S_SELECT, &mut scratch));
     396            1 :         assert!(!lac_check(&[0], S_RRB, &mut scratch));
     397            1 :     }
     398              : }
        

Generated by: LCOV version 2.0-1