Skip to main content

perl_parser/incremental/
state.rs

1use crate::incremental::checkpoint::{LexCheckpoint, ParseCheckpoint, ScopeSnapshot};
2use crate::incremental::lex::create_lex_checkpoints;
3use perl_lexer::{PerlLexer, Token, TokenType};
4use perl_line_index::LineIndex;
5use perl_parser_core::ast::{Node, NodeKind, SourceLocation};
6use perl_parser_core::parser::Parser;
7use ropey::Rope;
8
9#[derive(Clone)]
10pub struct IncrementalState {
11    pub rope: Rope,
12    pub line_index: LineIndex,
13    pub lex_checkpoints: Vec<LexCheckpoint>,
14    pub parse_checkpoints: Vec<ParseCheckpoint>,
15    pub ast: Node,
16    pub tokens: Vec<Token>,
17    pub source: String,
18}
19
20impl IncrementalState {
21    pub fn new(source: String) -> Self {
22        let rope = Rope::from_str(&source);
23        let line_index = LineIndex::new(&source);
24        let mut parser = Parser::new(&source);
25        let ast = match parser.parse() {
26            Ok(ast) => ast,
27            Err(e) => Node::new(
28                NodeKind::Error {
29                    message: e.to_string(),
30                    expected: vec![],
31                    found: None,
32                    partial: None,
33                },
34                SourceLocation { start: 0, end: source.len() },
35            ),
36        };
37        let mut lexer = PerlLexer::new(&source);
38        let mut tokens = Vec::new();
39        while let Some(token) = lexer.next_token() {
40            if token.token_type == TokenType::EOF {
41                break;
42            }
43            tokens.push(token);
44        }
45        let lex_checkpoints = create_lex_checkpoints(&tokens, &line_index);
46        let parse_checkpoints = Self::create_parse_checkpoints(&ast);
47        Self { rope, line_index, lex_checkpoints, parse_checkpoints, ast, tokens, source }
48    }
49    pub fn find_lex_checkpoint(&self, byte: usize) -> Option<&LexCheckpoint> {
50        self.lex_checkpoints.iter().rev().find(|cp| cp.byte <= byte)
51    }
52    pub fn find_parse_checkpoint(&self, byte: usize) -> Option<&ParseCheckpoint> {
53        self.parse_checkpoints.iter().rev().find(|cp| cp.byte <= byte)
54    }
55
56    pub(crate) fn create_parse_checkpoints(ast: &Node) -> Vec<ParseCheckpoint> {
57        let mut checkpoints = vec![];
58        let mut scope = ScopeSnapshot::default();
59        walk_ast_for_checkpoints(ast, &mut checkpoints, &mut scope, 0);
60        checkpoints
61    }
62}
63
64fn walk_ast_for_checkpoints(
65    node: &Node,
66    checkpoints: &mut Vec<ParseCheckpoint>,
67    scope: &mut ScopeSnapshot,
68    node_id: usize,
69) {
70    match &node.kind {
71        NodeKind::Package { name, .. } => {
72            scope.package_name = name.clone();
73            checkpoints.push(ParseCheckpoint {
74                byte: node.location.start,
75                scope_snapshot: scope.clone(),
76                node_id,
77            });
78        }
79        NodeKind::Subroutine { .. } | NodeKind::Block { .. } => checkpoints.push(ParseCheckpoint {
80            byte: node.location.start,
81            scope_snapshot: scope.clone(),
82            node_id,
83        }),
84        NodeKind::VariableDeclaration { variable, .. } => {
85            if let NodeKind::Variable { name, sigil, .. } = &variable.kind {
86                scope.locals.push(format!("{}{}", sigil, name));
87            }
88        }
89        NodeKind::VariableListDeclaration { variables, .. } => {
90            for var in variables {
91                if let NodeKind::Variable { name, sigil, .. } = &var.kind {
92                    scope.locals.push(format!("{}{}", sigil, name));
93                }
94            }
95        }
96        _ => {}
97    }
98    match &node.kind {
99        NodeKind::Program { statements } | NodeKind::Block { statements } => {
100            for (i, stmt) in statements.iter().enumerate() {
101                walk_ast_for_checkpoints(
102                    stmt,
103                    checkpoints,
104                    scope,
105                    node_id.wrapping_mul(101).wrapping_add(i),
106                );
107            }
108        }
109        _ => {}
110    }
111}