@@ -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 */
4851internal 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