Skip to content

Commit bdc1193

Browse files
committed
Document the parser concatenation function
1 parent 0c14518 commit bdc1193

File tree

1 file changed

+45
-4
lines changed
  • core/common/src/internal/format/parser

1 file changed

+45
-4
lines changed

core/common/src/internal/format/parser/Parser.kt

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,22 @@ internal class ParserStructure<in Output>(
4242
}
4343

4444
/**
45-
* Concatenates a list of parser structures into a single structure, processing them in reverse order.
46-
* Simplifies the result by merging number spans and handling unconditional modifications.
45+
* Concatenates a list of parser structures into a single *valid* structure.
46+
*
47+
* A *valid* parser is one where, if numeric values are parsed consecutively without a separator
48+
* (or with zero-width [UnconditionalModification] separators) between them,
49+
* they are represented as a single [NumberSpanParserOperation].
4750
*/
4851
internal fun <T> List<ParserStructure<T>>.concat(): ParserStructure<T> {
52+
/**
53+
* Returns a *valid* parser obtained by prepending [baseOperations] followed by [numberSpan]
54+
* to [simplifiedParserStructure],
55+
* while ensuring that [unconditionalModifications] are preserved in the result.
56+
*
57+
* Requirements:
58+
* - [simplifiedParserStructure] must have non-empty [ParserStructure.operations].
59+
* - [simplifiedParserStructure] is a *valid* parser.
60+
*/
4961
fun mergeOperations(
5062
baseOperations: List<ParserOperation<T>>,
5163
numberSpan: List<NumberConsumer<T>>?,
@@ -76,13 +88,27 @@ internal fun <T> List<ParserStructure<T>>.concat(): ParserStructure<T> {
7688
return ParserStructure(mergedOperations, simplifiedParserStructure.followedBy)
7789
}
7890

79-
// Simplifies this parser and appends [other] to all execution paths.
80-
// Merges number spans, collects unconditional modifications, and flattens alternatives.
91+
/**
92+
* Returns a *valid* parser obtained by prepending *any* parser `this` to a *valid* parser [other].
93+
*/
8194
fun ParserStructure<T>.simplifyAndAppend(other: ParserStructure<T>): ParserStructure<T> {
8295
val newOperations = mutableListOf<ParserOperation<T>>()
8396
var currentNumberSpan: MutableList<NumberConsumer<T>>? = null
8497
val unconditionalModifications = mutableListOf<UnconditionalModification<T>>()
8598

99+
// Loop invariant:
100+
//
101+
// |- zero-width parsers interspersing the number span
102+
// |
103+
// unconditionalModifications
104+
// \-------------------------/
105+
// operation, ..., operation, number, number, UnconditionalModification, number, operation, operation
106+
// \_______________________/ \______________ . . . . . . . . . . . . . ______/ \_______/
107+
// newOperations currentNumberSpan op
108+
// | | |- next operation
109+
// |- operations where spans of |- the continued span of
110+
// number parsers are merged into number parsers
111+
// `NumberSpanParserOperation`
86112
for (op in operations) {
87113
when (op) {
88114
is NumberSpanParserOperation -> {
@@ -105,6 +131,7 @@ internal fun <T> List<ParserStructure<T>>.concat(): ParserStructure<T> {
105131
}
106132
}
107133

134+
// *Valid* parsers resulting from appending [other] to every parser in `this.followedBy`.
108135
val mergedTails = followedBy.flatMap {
109136
val simplified = it.simplifyAndAppend(other)
110137
// Parser `ParserStructure(emptyList(), p)` is equivalent to `p`,
@@ -116,7 +143,12 @@ internal fun <T> List<ParserStructure<T>>.concat(): ParserStructure<T> {
116143
else
117144
listOf(simplified)
118145
}.ifEmpty {
146+
// We only enter this branch if [followedBy] is empty.
147+
// In that case, [mergedTails] is exactly `listOf(other)`.
148+
// We optimize this common case here as a fast-path and to reduce indirection in the resulting parser.
119149
if (other.operations.isNotEmpty()) {
150+
// Directly append `other` to the simplified `this`.
151+
// The call is valid: `other.operations` is non-empty
120152
return mergeOperations(newOperations, currentNumberSpan, unconditionalModifications, other)
121153
}
122154
// [other] has no operations, just alternatives; use them as our tails
@@ -156,6 +188,15 @@ internal fun <T> List<ParserStructure<T>>.concat(): ParserStructure<T> {
156188
}
157189
}
158190

191+
// Loop invariant:
192+
//
193+
// this = Parser, ..., Parser, operations, operations, operations, Parser, Parser, ...
194+
// \____/ \________________________________/ \_________________/
195+
// parser accumulatedOperations.reversed() result
196+
// | | |- simplified parser
197+
// | |- span of parsers without branching
198+
// |
199+
// |- next parser to be processed
159200
for (parser in this.asReversed()) {
160201
if (parser.followedBy.isEmpty()) {
161202
accumulatedOperations.add(parser.operations)

0 commit comments

Comments
 (0)