Skip to content

Commit 9639740

Browse files
Add caching to ParserStructure.simplify to improve performance
1 parent a32c665 commit 9639740

File tree

1 file changed

+66
-63
lines changed
  • core/common/src/internal/format/parser

1 file changed

+66
-63
lines changed

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

Lines changed: 66 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -49,78 +49,81 @@ internal fun <T> List<ParserStructure<T>>.concat(): ParserStructure<T> {
4949
ParserStructure(operations, followedBy.map { it.append(other) })
5050
}
5151

52-
fun <T> ParserStructure<T>.simplify(unconditionalModifications: List<UnconditionalModification<T>>): ParserStructure<T> {
53-
val newOperations = mutableListOf<ParserOperation<T>>()
54-
var currentNumberSpan: MutableList<NumberConsumer<T>>? = null
55-
val unconditionalModificationsForTails = unconditionalModifications.toMutableList()
56-
// joining together the number consumers in this parser before the first alternative;
57-
// collecting the unconditional modifications to push them to the end of all the parser's branches.
58-
for (op in operations) {
59-
if (op is NumberSpanParserOperation) {
60-
if (currentNumberSpan != null) {
61-
currentNumberSpan.addAll(op.consumers)
52+
val cache = hashMapOf<Pair<ParserStructure<T>, List<UnconditionalModification<T>>>, ParserStructure<T>>()
53+
54+
fun ParserStructure<T>.simplify(unconditionalModifications: List<UnconditionalModification<T>>): ParserStructure<T> =
55+
cache.getOrPut(this to unconditionalModifications) {
56+
val newOperations = mutableListOf<ParserOperation<T>>()
57+
var currentNumberSpan: MutableList<NumberConsumer<T>>? = null
58+
val unconditionalModificationsForTails = unconditionalModifications.toMutableList()
59+
// joining together the number consumers in this parser before the first alternative;
60+
// collecting the unconditional modifications to push them to the end of all the parser's branches.
61+
for (op in operations) {
62+
if (op is NumberSpanParserOperation) {
63+
if (currentNumberSpan != null) {
64+
currentNumberSpan.addAll(op.consumers)
65+
} else {
66+
currentNumberSpan = op.consumers.toMutableList()
67+
}
68+
} else if (op is UnconditionalModification) {
69+
unconditionalModificationsForTails.add(op)
6270
} else {
63-
currentNumberSpan = op.consumers.toMutableList()
64-
}
65-
} else if (op is UnconditionalModification) {
66-
unconditionalModificationsForTails.add(op)
67-
} else {
68-
if (currentNumberSpan != null) {
69-
newOperations.add(NumberSpanParserOperation(currentNumberSpan))
70-
currentNumberSpan = null
71+
if (currentNumberSpan != null) {
72+
newOperations.add(NumberSpanParserOperation(currentNumberSpan))
73+
currentNumberSpan = null
74+
}
75+
newOperations.add(op)
7176
}
72-
newOperations.add(op)
7377
}
74-
}
75-
val mergedTails = followedBy.flatMap {
76-
val simplified = it.simplify(unconditionalModificationsForTails)
77-
// parser `ParserStructure(emptyList(), p)` is equivalent to `p`,
78-
// unless `p` is empty. For example, ((a|b)|(c|d)) is equivalent to (a|b|c|d).
79-
// As a special case, `ParserStructure(emptyList(), emptyList())` represents a parser that recognizes an empty
80-
// string. For example, (|a|b) is not equivalent to (a|b).
81-
if (simplified.operations.isEmpty())
82-
simplified.followedBy.ifEmpty { listOf(simplified) }
83-
else
84-
listOf(simplified)
85-
}.ifEmpty {
86-
// preserving the invariant that `mergedTails` contains all unconditional modifications
87-
listOf(ParserStructure(unconditionalModificationsForTails, emptyList()))
88-
}
89-
return if (currentNumberSpan == null) {
90-
// the last operation was not a number span, or it was a number span that we are allowed to interrupt
91-
ParserStructure(newOperations, mergedTails)
92-
} else if (mergedTails.none {
93-
it.operations.firstOrNull()?.let { it is NumberSpanParserOperation } == true
94-
}) {
95-
// the last operation was a number span, but there are no alternatives that start with a number span.
96-
newOperations.add(NumberSpanParserOperation(currentNumberSpan))
97-
ParserStructure(newOperations, mergedTails)
98-
} else {
99-
val newTails = mergedTails.map {
100-
when (val firstOperation = it.operations.firstOrNull()) {
101-
is NumberSpanParserOperation -> {
102-
ParserStructure(
103-
listOf(NumberSpanParserOperation(currentNumberSpan + firstOperation.consumers)) + it.operations.drop(
104-
1
105-
),
78+
val mergedTails = followedBy.flatMap {
79+
val simplified = it.simplify(unconditionalModificationsForTails)
80+
// parser `ParserStructure(emptyList(), p)` is equivalent to `p`,
81+
// unless `p` is empty. For example, ((a|b)|(c|d)) is equivalent to (a|b|c|d).
82+
// As a special case, `ParserStructure(emptyList(), emptyList())` represents a parser that recognizes an empty
83+
// string. For example, (|a|b) is not equivalent to (a|b).
84+
if (simplified.operations.isEmpty())
85+
simplified.followedBy.ifEmpty { listOf(simplified) }
86+
else
87+
listOf(simplified)
88+
}.ifEmpty {
89+
// preserving the invariant that `mergedTails` contains all unconditional modifications
90+
listOf(ParserStructure(unconditionalModificationsForTails, emptyList()))
91+
}
92+
if (currentNumberSpan == null) {
93+
// the last operation was not a number span, or it was a number span that we are allowed to interrupt
94+
ParserStructure(newOperations, mergedTails)
95+
} else if (mergedTails.none {
96+
it.operations.firstOrNull()?.let { it is NumberSpanParserOperation } == true
97+
}) {
98+
// the last operation was a number span, but there are no alternatives that start with a number span.
99+
newOperations.add(NumberSpanParserOperation(currentNumberSpan))
100+
ParserStructure(newOperations, mergedTails)
101+
} else {
102+
val newTails = mergedTails.map {
103+
when (val firstOperation = it.operations.firstOrNull()) {
104+
is NumberSpanParserOperation -> {
105+
ParserStructure(
106+
listOf(NumberSpanParserOperation(currentNumberSpan + firstOperation.consumers)) + it.operations.drop(
107+
1
108+
),
109+
it.followedBy
110+
)
111+
}
112+
113+
null -> ParserStructure(
114+
listOf(NumberSpanParserOperation(currentNumberSpan)),
106115
it.followedBy
107116
)
108-
}
109-
110-
null -> ParserStructure(
111-
listOf(NumberSpanParserOperation(currentNumberSpan)),
112-
it.followedBy
113-
)
114117

115-
else -> ParserStructure(
116-
listOf(NumberSpanParserOperation(currentNumberSpan)) + it.operations,
117-
it.followedBy
118-
)
118+
else -> ParserStructure(
119+
listOf(NumberSpanParserOperation(currentNumberSpan)) + it.operations,
120+
it.followedBy
121+
)
122+
}
119123
}
124+
ParserStructure(newOperations, newTails)
120125
}
121-
ParserStructure(newOperations, newTails)
122126
}
123-
}
124127
val naiveParser = foldRight(ParserStructure<T>(emptyList(), emptyList())) { parser, acc -> parser.append(acc) }
125128
return naiveParser.simplify(emptyList())
126129
}

0 commit comments

Comments
 (0)