summaryrefslogtreecommitdiffstats
path: root/third_party/rust/jsparagus-generated-parser/src/context_stack.rs
blob: 97138247f857803b4ddc9d4eff66e0982914be44 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
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)
    }
}