Skip to main content

ktrs_parser/parsing/
mod.rs

1//! Port of `org.jetbrains.kotlin.parsing` (third_party/kotlin/compiler/psi/parser/src/.../parsing).
2//!
3//! # Mapping Java onto one `Parser`
4//! `AbstractKotlinParsing`, `KotlinParsing` and `KotlinExpressionParsing` are all `impl Parser`
5//! blocks: `abstract_kotlin_parsing.rs`, `declarations/` (KotlinParsing.java) and
6//! `expressions/` (KotlinExpressionParsing.java), split into files by upstream line range, each
7//! keeping upstream method order. Porting rules:
8//! - One Rust fn per Java method, `snake_case` of the Java name, same order as the Java file.
9//!   `private static final` fields become module `const`s with the Java name; nested enums/classes
10//!   become Rust types in the same file.
11//! - Name collisions between the two classes get an `expr_` prefix on the expression side (today
12//!   only `create`, whose expression-side variant is unreachable and needn't be ported).
13//! - Overloads: the one with the fewest parameters keeps the name; each other overload appends
14//!   `_{param count}` (`expect(t)`, `expect_2(t, msg)`, `expect_3(t, msg, set)`,
15//!   `parse_type_ref()`, `parse_type_ref_1(set)`, `parse_type_ref_2(set, b)`).
16//! - `myExpressionParsing.x()` / `myKotlinParsing.x()` → `self.x()`; `myBuilder.x()` →
17//!   `self.my_builder.x()`; `isLazy` → `self.is_lazy`.
18//! - Markers: `Marker m = mark();` → `let m = self.mark();`; `m.done(T)` → `m.done(self, T)`,
19//!   likewise `drop/rollback_to/precede/collapse/error/done_before/set_custom_edge_token_binders`.
20//! - Nullable `IElementType` results (`tt()`, `lookahead(k)`, `rawLookup`) are
21//!   `Option<SyntaxKind>`: `tt() == LBRACE` → `self.tt() == Some(LBRACE)`. Parameters that are
22//!   never null take `SyntaxKind`. `KtTokens.X`/`KtNodeTypes.X` → `SyntaxKind::X`; `KtTokens` sets
23//!   → `crate::kt_tokens::*`; `TokenSet.create(a, b)` → `TokenSet::create(&[a, b])`,
24//!   `orSet(a, b)` → `TokenSet::or_set(&[a, b])` (all `const fn`). `x instanceof KtKeywordToken` /
25//!   `isSoft()` / `getValue()` → `kt_tokens::is_keyword_token` / `is_soft_keyword` /
26//!   `kind.keyword_text()`.
27//! - Java `assert` → `debug_assert!`. `Consumer<IElementType>` → `&mut dyn Consumer<SyntaxKind>`.
28//! - Token-stream pattern fields (`lastDotAfterReceiver*Pattern`) are `reset()` before every use,
29//!   so build them as locals at the use site (see `token_stream.rs`); anonymous predicates are
30//!   closures `|p: &mut Parser, top_level: bool| -> bool`.
31//!
32//! # Wrapped builders and sub-parsers
33//! Java creates new parser objects over wrapped builders; here they are scoped context switches
34//! that push a builder [`Layer`] and restore everything afterwards:
35//! - `createForByClause(myBuilder, isLazy).myExpressionParsing.parseExpression()` →
36//!   `self.create_for_by_clause(self.is_lazy, |p| p.parse_expression())`.
37//! - `createTruncatedBuilder(eof).parseTypeRefWithoutIntersections()` →
38//!   `self.create_truncated_builder(eof, |p| p.parse_type_ref_without_intersections())`.
39//! - The by-clause parser's anonymous `parseCallWithClosure` override → start
40//!   `parse_call_with_closure` with
41//!   `if let Some(n) = self.by_clause_stack_size() { if n <= 0 { return false; } }`.
42//!
43//! # Entry points and lazy nodes
44//! `kotlin_parser.rs` ports `KotlinParser.java`. Collapsed `BLOCK`/`LAMBDA_EXPRESSION` leaves and
45//! `DOC_COMMENT` tokens are reparsed when the tree is built, over their own text with a fresh
46//! builder (`reparse_lazy`), exactly like the compiler's chameleons.
47
48mod abstract_kotlin_parsing;
49mod declarations;
50mod expressions;
51pub(crate) mod kotlin_parser;
52mod optional_marker;
53mod token_stream;
54
55pub use optional_marker::OptionalMarker;
56pub use token_stream::{
57    AbstractTokenStreamPattern, At, AtSet, FirstBefore, LastBefore, Or, TokenStreamPattern, TokenStreamPredicate,
58};
59
60use crate::builder::{Layer, MarkerHost, PsiBuilder, SemanticWhitespaceAwarePsiBuilder};
61
62/// `Consumer.java`.
63pub trait Consumer<T> {
64    fn consume(&mut self, item: T);
65}
66
67/// The state shared by one `KotlinParsing` + `KotlinExpressionParsing` pair.
68pub struct Parser {
69    pub(crate) my_builder: SemanticWhitespaceAwarePsiBuilder,
70    pub(crate) is_lazy: bool,
71    /// Index of this context's `ForByClause` builder layer; `None` for top-level parsers.
72    by_clause_layer: Option<usize>,
73}
74
75impl MarkerHost for Parser {
76    fn psi_builder(&mut self) -> &mut PsiBuilder {
77        &mut self.my_builder.psi
78    }
79}
80
81impl Parser {
82    /// `KotlinParsing.createForTopLevel`.
83    pub(crate) fn create_for_top_level(builder: SemanticWhitespaceAwarePsiBuilder) -> Parser {
84        Parser { my_builder: builder, is_lazy: true, by_clause_layer: None }
85    }
86
87    /// `KotlinParsing.createForTopLevelNonLazy`.
88    #[allow(dead_code)] // TODO: only `KotlinLightParser` uses it upstream; not ported yet.
89    pub(crate) fn create_for_top_level_non_lazy(builder: SemanticWhitespaceAwarePsiBuilder) -> Parser {
90        Parser { my_builder: builder, is_lazy: false, by_clause_layer: None }
91    }
92
93    /// `KotlinParsing.createForByClause(myBuilder, isLazy)`: runs `f` on a non-top-level parser
94    /// over a `SemanticWhitespaceAwarePsiBuilderForByClause` wrapping the current builder.
95    pub(crate) fn create_for_by_clause<R>(&mut self, is_lazy: bool, f: impl FnOnce(&mut Parser) -> R) -> R {
96        let layer = self.my_builder.push_layer(Layer::ForByClause { stack_size: 0 });
97        self.with_context(is_lazy, Some(layer), f)
98    }
99
100    /// `getStackSize()` of the by-clause builder if this is a by-clause parser, else `None`.
101    pub(crate) fn by_clause_stack_size(&self) -> Option<i32> {
102        self.by_clause_layer.map(|layer| self.my_builder.for_by_clause_stack_size(layer))
103    }
104
105    /// Runs `f` with a new parser context, then pops the layer pushed by the caller and restores.
106    fn with_context<R>(&mut self, is_lazy: bool, by_clause_layer: Option<usize>, f: impl FnOnce(&mut Parser) -> R) -> R {
107        let saved = (self.is_lazy, self.by_clause_layer);
108        self.is_lazy = is_lazy;
109        self.by_clause_layer = by_clause_layer;
110        let result = f(self);
111        (self.is_lazy, self.by_clause_layer) = saved;
112        self.my_builder.pop_layer();
113        result
114    }
115}