mirror of
https://github.com/tree-sitter/tree-sitter.git
synced 2026-09-10 07:26:23 -04:00
perf(generate): inline single-action parse table entries
`ParseTableEntry` held a `Vec<ParseAction>` per entry. `ActionList` keeps up to one action inline and spills to a Vec only for conflict entries. Reduces walltime by >20%, peak rss by ~30%.
This commit is contained in:
parent
2a81995058
commit
b14413e4f4
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()"),
|
||||
|
|
|
|||
|
|
@ -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<ParseAction>),
|
||||
}
|
||||
|
||||
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<ParseAction> {
|
||||
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<H: std::hash::Hasher>(&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<ParseAction>,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue