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}