summaryrefslogtreecommitdiffstats
path: root/third_party/rust/jsparagus-generated-parser/src/context_stack.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/jsparagus-generated-parser/src/context_stack.rs')
-rw-r--r--third_party/rust/jsparagus-generated-parser/src/context_stack.rs405
1 files changed, 405 insertions, 0 deletions
diff --git a/third_party/rust/jsparagus-generated-parser/src/context_stack.rs b/third_party/rust/jsparagus-generated-parser/src/context_stack.rs
new file mode 100644
index 0000000000..97138247f8
--- /dev/null
+++ b/third_party/rust/jsparagus-generated-parser/src/context_stack.rs
@@ -0,0 +1,405 @@
+use ast::source_atom_set::SourceAtomSetIndex;
+use std::iter::{Skip, Take};
+use std::slice::Iter;
+
+// The kind of BindingIdentifier found while parsing.
+//
+// Given we don't yet have the context stack, at the point of finding
+// a BindingIdentifier, we don't know what kind of binding it is.
+// So it's marked as `Unknown`.
+//
+// Once the parser reaches the end of a declaration, bindings found in the
+// range are marked as corresponding kind.
+//
+// This is a separate enum than `DeclarationKind` for the following reason:
+// * `DeclarationKind` is determined only when the parser reaches the end of
+// the entire context (also because we don't have context stack), not each
+// declaration
+// * As long as `BindingKind` is known for each binding, we can map it to
+// `DeclarationKind`
+// * `DeclarationKind::CatchParameter` and some others don't to be marked
+// this way, because `AstBuilder` knows where they are in the `bindings`
+// vector.
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub enum BindingKind {
+ // The initial state.
+ // BindingIdentifier is found, and haven't yet marked as any kind.
+ Unknown,
+
+ // BindingIdentifier is inside VariableDeclaration.
+ Var,
+
+ // BindingIdentifier is the name of FunctionDeclaration.
+ Function,
+
+ // BindingIdentifier is the name of GeneratorDeclaration,
+ // AsyncFunctionDeclaration, or AsyncGeneratorDeclaration.
+ AsyncOrGenerator,
+
+ // BindingIdentifier is inside LexicalDeclaration with let.
+ Let,
+
+ // BindingIdentifier is inside LexicalDeclaration with const.
+ Const,
+
+ // BindingIdentifier is the name of ClassDeclaration.
+ Class,
+}
+
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub struct BindingInfo {
+ pub name: SourceAtomSetIndex,
+ // The offset of the BindingIdentifier in the source.
+ pub offset: usize,
+ pub kind: BindingKind,
+}
+
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub struct BindingsIndex {
+ pub index: usize,
+}
+
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub enum ControlKind {
+ Continue,
+ Break,
+}
+
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub struct ControlInfo {
+ pub label: Option<SourceAtomSetIndex>,
+ // The offset of the nested control in the source.
+ pub offset: usize,
+ pub kind: ControlKind,
+}
+
+impl ControlInfo {
+ pub fn new_continue(offset: usize, label: Option<SourceAtomSetIndex>) -> Self {
+ Self {
+ label,
+ kind: ControlKind::Continue,
+ offset,
+ }
+ }
+
+ pub fn new_break(offset: usize, label: Option<SourceAtomSetIndex>) -> Self {
+ Self {
+ label,
+ kind: ControlKind::Break,
+ offset,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub struct BreakOrContinueIndex {
+ pub index: usize,
+}
+
+impl BreakOrContinueIndex {
+ pub fn new(index: usize) -> Self {
+ Self { index }
+ }
+}
+
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub enum LabelKind {
+ Other,
+
+ Function,
+
+ Loop,
+
+ LabelledLabel,
+}
+
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub struct LabelInfo {
+ pub name: SourceAtomSetIndex,
+ // The offset of the BindingIdentifier in the source.
+ pub offset: usize,
+ pub kind: LabelKind,
+}
+
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub struct LabelIndex {
+ pub index: usize,
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct ContextMetadata {
+ // The stack of information about BindingIdentifier.
+ //
+ // When the parser found BindingIdentifier, the information (`name` and
+ // `offset`) are noted to this vector, and when the parser determined what
+ // kind of binding it is, `kind` is updated.
+ //
+ // The bindings are sorted by offset.
+ //
+ // When the parser reaches the end of a scope, all bindings declared within
+ // that scope (not including nested scopes) are fed to an
+ // EarlyErrorsContext to detect Early Errors.
+ //
+ // When leaving a context that is not one of script/module/function,
+ // lexical items (`kind != BindingKind::Var) in the corresponding range
+ // are removed, while non-lexical items (`kind == BindingKind::Var) are
+ // left there, so that VariableDeclarations and labels are propagated to the
+ // enclosing context.
+ //
+ // FIXME: Once the context stack gets implemented, this structure and
+ // related methods should be removed and each declaration should be
+ // fed directly to EarlyErrorsContext.
+ bindings: Vec<BindingInfo>,
+
+ // Track continues and breaks without the use of a context stack.
+ //
+ // FIXME: Once th context stack geets implmentd, this structure and
+ // related metehods should be removed, and each break/continue should be
+ // fed directly to EarlyErrorsContext.
+ breaks_and_continues: Vec<ControlInfo>,
+
+ labels: Vec<LabelInfo>,
+}
+
+impl ContextMetadata {
+ pub fn new() -> Self {
+ Self {
+ bindings: Vec::new(),
+ breaks_and_continues: Vec::new(),
+ labels: Vec::new(),
+ }
+ }
+
+ pub fn push_binding(&mut self, binding: BindingInfo) {
+ self.bindings.push(binding);
+ }
+
+ pub fn last_binding(&mut self) -> Option<&BindingInfo> {
+ self.bindings.last()
+ }
+
+ pub fn push_break_or_continue(&mut self, control: ControlInfo) {
+ self.breaks_and_continues.push(control);
+ }
+
+ pub fn push_label(&mut self, label: LabelInfo) {
+ self.labels.push(label);
+ }
+
+ // Update the binding kind of all names declared in a specific range of the
+ // source (and not in any nested scope). This is used e.g. when the parser
+ // reaches the end of a VariableStatement to mark all the variables as Var
+ // bindings.
+ //
+ // It's necessary because the current parser only calls AstBuilder methods
+ // at the end of each production, not at the beginning.
+ //
+ // Bindings inside `StatementList` must be marked using this method before
+ // we reach the end of its scope.
+ pub fn mark_binding_kind(&mut self, from: usize, to: Option<usize>, kind: BindingKind) {
+ for info in self.bindings.iter_mut().rev() {
+ if info.offset < from {
+ break;
+ }
+
+ if to.is_none() || info.offset < to.unwrap() {
+ info.kind = kind;
+ }
+ }
+ }
+
+ pub fn bindings_from(&self, index: BindingsIndex) -> Skip<Iter<'_, BindingInfo>> {
+ self.bindings.iter().skip(index.index)
+ }
+
+ pub fn bindings_from_to(
+ &self,
+ from: BindingsIndex,
+ to: BindingsIndex,
+ ) -> Take<Skip<Iter<'_, BindingInfo>>> {
+ self.bindings
+ .iter()
+ .skip(from.index)
+ .take(to.index - from.index)
+ }
+
+ // Returns the index of the first label at/after `offset` source position.
+ pub fn find_first_binding(&mut self, offset: usize) -> BindingsIndex {
+ let mut i = self.bindings.len();
+ for info in self.bindings.iter_mut().rev() {
+ if info.offset < offset {
+ break;
+ }
+ i -= 1;
+ }
+ BindingsIndex { index: i }
+ }
+
+ // Remove all bindings after `index`-th item.
+ //
+ // This should be called when leaving function/script/module.
+ pub fn pop_bindings_from(&mut self, index: BindingsIndex) {
+ self.bindings.truncate(index.index)
+ }
+
+ pub fn labels_from(&self, index: LabelIndex) -> Skip<Iter<'_, LabelInfo>> {
+ self.labels.iter().skip(index.index)
+ }
+
+ // Update the label kind of a label declared in a specific range of the
+ // source (and not in any nested scope). There should never be more than one.
+ //
+ // It's necessary because the current parser only calls AstBuilder methods
+ // at the end of each production, not at the beginning.
+ //
+ // Labels inside `StatementList` must be marked using this method before
+ // we reach the end of its scope.
+ pub fn mark_label_kind_at_offset(&mut self, from: usize, kind: LabelKind) {
+ let maybe_label = self.find_label_at_offset(from);
+ if let Some(info) = maybe_label {
+ info.kind = kind
+ } else {
+ panic!("Tried to mark a non-existant label");
+ }
+ }
+
+ // Remove lexical bindings after `index`-th item,
+ // while keeping var bindings.
+ //
+ // This should be called when leaving block.
+ pub fn pop_lexical_bindings_from(&mut self, index: BindingsIndex) {
+ let len = self.bindings.len();
+ let mut i = index.index;
+
+ while i < len && self.bindings[i].kind == BindingKind::Var {
+ i += 1;
+ }
+
+ let mut j = i;
+ while j < len {
+ if self.bindings[j].kind == BindingKind::Var {
+ self.bindings[i] = self.bindings[j];
+ i += 1;
+ }
+ j += 1;
+ }
+
+ self.bindings.truncate(i)
+ }
+
+ // Returns the index of the first binding at/after `offset` source position.
+ pub fn find_first_label(&mut self, offset: usize) -> LabelIndex {
+ let mut i = self.labels.len();
+ for info in self.labels.iter_mut().rev() {
+ if info.offset < offset {
+ break;
+ }
+ i -= 1;
+ }
+ LabelIndex { index: i }
+ }
+
+ // Remove all bindings after `index`-th item.
+ //
+ // This should be called when leaving function/script/module.
+ pub fn pop_labels_from(&mut self, index: LabelIndex) {
+ self.labels.truncate(index.index)
+ }
+
+ pub fn breaks_and_continues_from(
+ &self,
+ index: BreakOrContinueIndex,
+ ) -> Skip<Iter<'_, ControlInfo>> {
+ self.breaks_and_continues.iter().skip(index.index)
+ }
+
+ // Returns the index of the first break or continue at/after `offset` source position.
+ pub fn find_first_break_or_continue(&mut self, offset: usize) -> BreakOrContinueIndex {
+ let mut i = self.breaks_and_continues.len();
+ for info in self.breaks_and_continues.iter_mut().rev() {
+ if info.offset < offset {
+ break;
+ }
+ i -= 1;
+ }
+ BreakOrContinueIndex::new(i)
+ }
+
+ pub fn pop_labelled_breaks_and_continues_from_index(
+ &mut self,
+ index: BreakOrContinueIndex,
+ name: SourceAtomSetIndex,
+ ) {
+ let len = self.breaks_and_continues.len();
+ let mut i = index.index;
+
+ let mut j = i;
+ while j < len {
+ let label = self.breaks_and_continues[j].label;
+ if label.is_none() || label.unwrap() != name {
+ self.breaks_and_continues[i] = self.breaks_and_continues[j];
+ i += 1;
+ }
+ j += 1;
+ }
+
+ self.breaks_and_continues.truncate(i)
+ }
+
+ pub fn pop_unlabelled_breaks_and_continues_from(&mut self, offset: usize) {
+ let len = self.breaks_and_continues.len();
+ let index = self.find_first_break_or_continue(offset);
+ let mut i = index.index;
+
+ while i < len && self.breaks_and_continues[i].label.is_some() {
+ i += 1;
+ }
+
+ let mut j = i;
+ while j < len {
+ if self.breaks_and_continues[j].label.is_some() {
+ self.breaks_and_continues[i] = self.breaks_and_continues[j];
+ i += 1;
+ }
+ j += 1;
+ }
+
+ self.breaks_and_continues.truncate(i)
+ }
+
+ pub fn pop_unlabelled_breaks_from(&mut self, offset: usize) {
+ let len = self.breaks_and_continues.len();
+ let index = self.find_first_break_or_continue(offset);
+ let mut i = index.index;
+
+ while i < len && self.breaks_and_continues[i].label.is_some() {
+ i += 1;
+ }
+
+ let mut j = i;
+ while j < len {
+ if self.breaks_and_continues[j].label.is_some()
+ || self.breaks_and_continues[j].kind == ControlKind::Continue
+ {
+ self.breaks_and_continues[i] = self.breaks_and_continues[j];
+ i += 1;
+ }
+ j += 1;
+ }
+
+ self.breaks_and_continues.truncate(i)
+ }
+
+ pub fn find_break_or_continue_at(&self, index: BreakOrContinueIndex) -> Option<&ControlInfo> {
+ self.breaks_and_continues.get(index.index)
+ }
+
+ pub fn find_label_index_at_offset(&self, offset: usize) -> Option<LabelIndex> {
+ let index = self.labels.iter().position(|info| info.offset == offset);
+ index.map(|index| LabelIndex { index })
+ }
+
+ pub fn find_label_at_offset(&mut self, offset: usize) -> Option<&mut LabelInfo> {
+ self.labels.iter_mut().find(|info| info.offset == offset)
+ }
+}