vize_l2 0.507.0

L2 - the Davinci semantic IR (codename Disegno): the neutral op family every input dialect lowers into
Documentation
use super::records::Record;
use crate::file::ScriptUnit;
use crate::lang::js::file::observer::{
    CallEvent, DeclaredEvent, FileObserver, StatementEvent, SyntaxEvent,
};
use crate::resolution::{ResolutionErrorKind, SyntaxEdge, SyntaxKind};
use alloc::vec::Vec;

pub(super) struct Recorder<'a> {
    pub records: Vec<Record<'a>>,
    frames: Vec<usize>,
    jsx_count: usize,
}

impl<'a> Recorder<'a> {
    pub(super) fn new() -> Self {
        Self {
            records: Vec::new(),
            frames: Vec::new(),
            jsx_count: 0,
        }
    }
    pub(super) fn frames_complete(&self) -> bool {
        self.frames.is_empty()
    }
    pub(super) fn jsx_count(&self) -> usize {
        self.jsx_count
    }
}

impl<'a> FileObserver<'a> for Recorder<'a> {
    type Checkpoint = (usize, usize, usize);
    fn unit(&mut self, _: &ScriptUnit) {}
    fn statement(&mut self, _: StatementEvent<'_, 'a>) {}
    fn declared(&mut self, _: DeclaredEvent<'_, 'a>) {}
    fn checkpoint(&self) -> Self::Checkpoint {
        (self.records.len(), self.frames.len(), self.jsx_count)
    }
    fn call(&mut self, _: CallEvent<'_, 'a>) -> Result<(), ResolutionErrorKind> {
        Ok(())
    }
    fn syntax(&mut self, event: SyntaxEvent<'a>) -> Result<(), ResolutionErrorKind> {
        if event.edge == SyntaxEdge::Leave {
            let index = self
                .frames
                .last()
                .copied()
                .ok_or(ResolutionErrorKind::InvalidSpan)?;
            let end = self.records.len();
            let record = self
                .records
                .get_mut(index)
                .ok_or(ResolutionErrorKind::InvalidSpan)?;
            if record.span != event.span
                || record.kind != event.kind
                || record.unit != event.unit
                || record.scope != event.scope
                || record.references.start > event.reference_boundary
            {
                return Err(ResolutionErrorKind::InvalidSpan);
            }
            record.references.end = event.reference_boundary;
            record.subtree_end = end;
            self.frames.pop();
            return Ok(());
        }
        if self.records.len() >= 4096 {
            return Err(ResolutionErrorKind::TraversalLimit);
        }
        let parent = self.frames.last().copied();
        if let Some(parent) = parent {
            let parent = self
                .records
                .get(parent)
                .ok_or(ResolutionErrorKind::InvalidSpan)?;
            if event.span.start < parent.span.start
                || event.span.end > parent.span.end
                || event.unit != parent.unit
                || event.scope != parent.scope
            {
                return Err(ResolutionErrorKind::InvalidSpan);
            }
        } else if !event.kind.is_expression() {
            return Err(ResolutionErrorKind::InvalidSpan);
        }
        let index = self.records.len();
        self.records.push(Record {
            kind: event.kind,
            span: event.span,
            unit: event.unit,
            scope: event.scope,
            parent,
            subtree_end: index + 1,
            references: event.reference_boundary..event.reference_boundary,
        });
        if matches!(event.kind, SyntaxKind::Element | SyntaxKind::Fragment) {
            self.jsx_count += 1;
        }
        if event.edge == SyntaxEdge::Enter {
            self.frames.push(index);
        }
        Ok(())
    }
    fn rollback(&mut self, (records, frames, count): Self::Checkpoint) {
        self.records.truncate(records);
        self.frames.truncate(frames);
        self.jsx_count = count;
    }
}