diff --git a/crates/generate/src/build_tables.rs b/crates/generate/src/build_tables.rs index 4f1640c49..78b0186b3 100644 --- a/crates/generate/src/build_tables.rs +++ b/crates/generate/src/build_tables.rs @@ -30,7 +30,7 @@ use crate::{ node_types::VariableInfo, rules::{AliasMap, Symbol, SymbolType, TokenSet}, strpool::StrPool, - tables::{LexTable, ParseAction, ParseTable, ParseTableEntry}, + tables::{ActionList, LexTable, ParseAction, ParseTable, ParseTableEntry}, }; pub struct Tables { @@ -204,7 +204,7 @@ fn populate_error_state( let recover_entry = ParseTableEntry { reusable: false, - actions: vec![ParseAction::Recover], + actions: ActionList::One(ParseAction::Recover), }; // Exclude from the error-recovery state any token that conflicts with one of diff --git a/crates/generate/src/build_tables/build_parse_table.rs b/crates/generate/src/build_tables/build_parse_table.rs index 133508077..9b98fdff2 100644 --- a/crates/generate/src/build_tables/build_parse_table.rs +++ b/crates/generate/src/build_tables/build_parse_table.rs @@ -21,7 +21,7 @@ use crate::{ rules::{Associativity, Precedence, Symbol, SymbolType, TokenSet}, strpool::StrPool, tables::{ - FieldLocation, GotoAction, ParseAction, ParseState, ParseStateId, ParseTable, + ActionList, FieldLocation, GotoAction, ParseAction, ParseState, ParseStateId, ParseTable, ParseTableEntry, ProductionInfo, ProductionInfoId, }, }; @@ -688,10 +688,10 @@ impl<'a> ParseTableBuilder<'a> { .entry(*terminal) .or_insert(ParseTableEntry { reusable: true, - actions: vec![ParseAction::Shift { + actions: ActionList::One(ParseAction::Shift { state: *state_id, is_repetition: false, - }], + }), }); } @@ -709,7 +709,7 @@ impl<'a> ParseTableBuilder<'a> { .entry(*extra_token) .or_insert(ParseTableEntry { reusable: true, - actions: vec![ParseAction::ShiftExtra], + actions: ActionList::One(ParseAction::ShiftExtra), }); } } @@ -840,7 +840,7 @@ impl<'a> ParseTableBuilder<'a> { } if shift_is_more && !shift_is_less { - entry.actions.drain(0..entry.actions.len() - 1); + entry.actions.keep_last(); } // If the REDUCE actions have higher precedence, remove the SHIFT action. else if shift_is_less && !shift_is_more { @@ -861,7 +861,7 @@ impl<'a> ParseTableBuilder<'a> { (false, false, true) ) { - entry.actions.drain(0..entry.actions.len() - 1); + entry.actions.keep_last(); } else { entry.actions.pop(); conflicting_items.retain(|item| item.is_done()); @@ -884,7 +884,7 @@ impl<'a> ParseTableBuilder<'a> { conflicting_items.retain(|item| item.is_done()); } (false, false, true) => { - entry.actions.drain(0..entry.actions.len() - 1); + entry.actions.keep_last(); } _ => {} } diff --git a/crates/generate/src/render.rs b/crates/generate/src/render.rs index 84595b0ff..1dfbbf116 100644 --- a/crates/generate/src/render.rs +++ b/crates/generate/src/render.rs @@ -20,8 +20,8 @@ use super::{ rules::{AliasMap, Symbol, SymbolType, TokenSet}, strpool::{StrId, StrPool}, tables::{ - AdvanceAction, FieldLocation, GotoAction, LexState, LexTable, ParseAction, ParseTable, - ParseTableEntry, + ActionList, AdvanceAction, FieldLocation, GotoAction, LexState, LexTable, ParseAction, + ParseTable, ParseTableEntry, }, }; @@ -1311,7 +1311,7 @@ impl Generator { // Parse action lists zero is for the default value, when a symbol is not valid. Self::get_parse_action_list_id( &ParseTableEntry { - actions: Vec::new(), + actions: ActionList::Empty, reusable: false, }, &mut parse_table_entries, @@ -1501,9 +1501,9 @@ impl Generator { entry.actions.len(), entry.reusable ); - for action in entry.actions { + for action in &entry.actions { add!(self, " "); - match action { + match *action { ParseAction::Accept => add!(self, " ACCEPT_INPUT()"), ParseAction::Recover => add!(self, "RECOVER()"), ParseAction::ShiftExtra => add!(self, "SHIFT_EXTRA()"), diff --git a/crates/generate/src/tables.rs b/crates/generate/src/tables.rs index 5fdfc8dfa..7d12ec74d 100644 --- a/crates/generate/src/tables.rs +++ b/crates/generate/src/tables.rs @@ -38,9 +38,93 @@ pub enum GotoAction { ShiftExtra, } +/// Action list for one parse table entry. Holds entries with <= 1 action inline. +#[derive(Clone, Debug, Default, Eq)] +pub enum ActionList { + #[default] + Empty, + One(ParseAction), + Many(Vec), +} + +impl ActionList { + pub fn push(&mut self, action: ParseAction) { + match self { + Self::Empty => *self = Self::One(action), + Self::One(first) => *self = Self::Many(vec![*first, action]), + Self::Many(actions) => actions.push(action), + } + } + + pub fn pop(&mut self) -> Option { + match self { + Self::Empty => None, + Self::One(action) => { + let action = *action; + *self = Self::Empty; + Some(action) + } + Self::Many(actions) => actions.pop(), + } + } + + pub fn clear(&mut self) { + *self = Self::Empty; + } + + pub fn keep_last(&mut self) { + if let Self::Many(actions) = self + && let Some(&last) = actions.last() + { + *self = Self::One(last); + } + } +} + +impl std::ops::Deref for ActionList { + type Target = [ParseAction]; + fn deref(&self) -> &[ParseAction] { + match self { + Self::Empty => &[], + Self::One(action) => std::slice::from_ref(action), + Self::Many(actions) => actions, + } + } +} + +impl std::ops::DerefMut for ActionList { + fn deref_mut(&mut self) -> &mut [ParseAction] { + match self { + Self::Empty => &mut [], + Self::One(action) => std::slice::from_mut(action), + Self::Many(actions) => actions, + } + } +} + +impl PartialEq for ActionList { + fn eq(&self, other: &Self) -> bool { + self[..] == other[..] + } +} + +impl std::hash::Hash for ActionList { + fn hash(&self, state: &mut H) { + self[..].hash(state); + } +} + +impl<'a> IntoIterator for &'a ActionList { + type Item = &'a ParseAction; + type IntoIter = std::slice::Iter<'a, ParseAction>; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ParseTableEntry { - pub actions: Vec, + pub actions: ActionList, pub reusable: bool, } @@ -100,7 +184,7 @@ impl ParseTableEntry { pub const fn new() -> Self { Self { reusable: true, - actions: Vec::new(), + actions: ActionList::Empty, } } }