Skip to content
This repository was archived by the owner on Sep 29, 2023. It is now read-only.

Commit d08c780

Browse files
author
jchapuis
committed
expansion mechanism
1 parent c1edd9c commit d08c780

File tree

2 files changed

+54
-34
lines changed

2 files changed

+54
-34
lines changed

src/main/scala/com/nexthink/utils/parsing/combinator/completion/RegexCompletionSupportWithExpansion.scala

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,26 @@ import scala.util.parsing.input.{CharSequenceReader, Position, Reader}
1111

1212
trait RegexCompletionSupportWithExpansion extends RegexCompletionSupport {
1313

14-
def expandedCompletions[T](p: Parser[T]): Parser[T] = expandedCompletions(p, p)
14+
/**
15+
* Adapts a parser so that completing it will list all possible expanded completions (which successfully parse)
16+
* (note that if this is used within the context of a grammar allowing for infinitely growing expressions, this
17+
* will trigger infinite recursion and will end up in a `StackOverflowException`)
18+
* @param p the parser
19+
* @tparam T the parser type
20+
* @return a parser adapter performing completion expansion
21+
*/
22+
def expandedCompletions[T](p: Parser[T]): Parser[T] = expandedCompletions(p, p)
23+
24+
/**
25+
* Adapts a parser so that completing it will construct the list of all possible alternatives up to the point
26+
* where the passed `stop` parser successfully parses the expansions.
27+
* (note that if this is used within the context of a grammar allowing for infinitely growing expressions,
28+
* selecting the relevant stop parser is critical to avoid infinite recursion)
29+
* @param p the parser
30+
* @param stop the parser signalling the end of exploration upon successful parse
31+
* @tparam T the parser type
32+
* @return a parser adapter performing completion expansion limited according to `stop` parser
33+
*/
1534
def expandedCompletions[T](p: Parser[T], stop: Parser[T]): Parser[T] =
1635
Parser(p, in => {
1736
exploreCompletions(p, stop, in)
@@ -24,25 +43,19 @@ trait RegexCompletionSupportWithExpansion extends RegexCompletionSupport {
2443
else
2544
completions.allSets
2645
.map(cSet => {
27-
val completionPos = completions.position.column
28-
val inputCompletedWithFirstSetElement = ExplorerReader(p, completeString(str, completionPos, cSet.completions.head))
29-
if (stop(inputCompletedWithFirstSetElement).successful) {
30-
// we consider we have reached stop condition with this completion set
31-
Completions(in.pos, CompletionSet(cSet.tag, cSet.completions.map(c => Completion(completeString(str, completionPos, c), c.score, c.kind))))
32-
} else {
33-
// continue exploring
34-
cSet.completions
35-
.map(c => {
36-
val completedInput = completeString(str, completionPos, c)
37-
println(completedInput)
46+
cSet.completions
47+
.map(c => {
48+
val completedInput = completeString(str, completions.position.column, c)
49+
if (stop(new CharSequenceReader(completedInput)).successful) {
50+
Completions(in.pos, CompletionSet(cSet.tag, Set(Completion(completedInput, c.score, c.kind))))
51+
} else {
3852
exploreCompletionsRec(completedInput, p.completions(ExplorerReader(p, completedInput)))
39-
})
40-
.reduce((a, b) => a | b)
41-
}
53+
}
54+
})
55+
.reduce((a, b) => a | b)
4256
})
4357
.reduce((a, b) => a | b)
4458
}
45-
4659
if (in match {
4760
case ExplorerReader(exploredParser, _, _) if exploredParser == p => true
4861
case _ => false

src/test/scala/com/nexthink/utils/parsing/combinator/completion/ExpandedCompletionsTest.scala

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,60 +7,67 @@
77

88
package com.nexthink.utils.parsing.combinator.completion
99

10+
import com.nexthink.utils.parsing.combinator.completion.CompletionTestDefinitions.Tagged
1011
import org.junit.{Assert, Test}
1112

1213
import scala.util.parsing.combinator.Parsers
1314

1415
class ExpandedCompletionsTest {
1516

1617
object FiniteArithmeticParser extends Parsers with RegexCompletionSupportWithExpansion {
17-
val number = "[0-9]+".r %> "99"
18+
val number = "[0-9]+".r %> "99"
1819
def expr(maxNesting: Int) = term(maxNesting) ~ ("+" | "-") ~! term(maxNesting)
19-
def term(maxNesting: Int) = factor(maxNesting) ~ ("*" | "/") ~! factor(maxNesting)
20+
def term(maxNesting: Int) = factor(maxNesting) ~ "*" ~! factor(maxNesting)
2021
def factor(maxNesting: Int): Parser[Any] = if (maxNesting == 0) number else number | "(" ~> expr(maxNesting - 1) <~ ")"
2122

2223
def exprWithExpandedCompletions() = expandedCompletions(expr(1))
2324
}
2425

2526
@Test
2627
def finiteExpressionParses(): Unit = {
27-
val result = FiniteArithmeticParser.parse(FiniteArithmeticParser.exprWithExpandedCompletions(), "( 99 / 99 - 99 / 99 ) / ( 99 / 99 + 99 / 99 ) - ( 99 * 99 - 99 * 99 ) / ( 99 * 99 - 99 / 99 )")
28-
println(result)
28+
val result = FiniteArithmeticParser.parse(FiniteArithmeticParser.exprWithExpandedCompletions(),
29+
"( 99 * 99 + 99 * 99 ) * ( 99 * 99 + 99 * 99 ) + ( 99 * 99 + 99 * 99 ) * ( 99 * 99 + 99 * 99 )")
2930
assert(result.successful)
3031
}
3132

3233
@Test
3334
def finiteExpressionCompletesToAllAlternatives(): Unit = {
3435
val completions = FiniteArithmeticParser.completeString(FiniteArithmeticParser.exprWithExpandedCompletions(), "")
35-
println(completions)
36+
Assert.assertEquals(completions.length, 162)
3637
}
3738

38-
object InfiniteExpressionParser extends Parsers with RegexCompletionSupportWithExpansion {
39+
object InfiniteExpressionParser extends Parsers with RegexCompletionSupportWithExpansion with CompletionTestParser {
3940
val fox = "the quick brown fox"
40-
val jumpsOver = "which jumps over the lazy"
41-
val jumpsOverDogOrCat = jumpsOver ~ ("dog" | "cat")
41+
val jumpsOver = "which jumps over the lazy" % "action"
42+
val jumpsOverDogOrCat = jumpsOver ~ ("dog" | "cat") % "animal" %? "dogs and cats" % 10
4243
lazy val parser = jumpsOverDogOrCat | jumpsOverDogOrCat ~ which()
4344
def which(): Parser[Any] = expandedCompletions(parser, stop = jumpsOverDogOrCat ~ jumpsOverDogOrCat)
4445
lazy val infiniteDogsAndCats = fox ~ which
4546
}
4647

4748
@Test
4849
def infiniteExpressionParses(): Unit = {
49-
val result = InfiniteExpressionParser.parse(InfiniteExpressionParser.infiniteDogsAndCats,
50-
"the quick brown fox which jumps over the lazy cat which jumps over the lazy dog which jumps over the lazy cat")
50+
val result = InfiniteExpressionParser.parse(
51+
InfiniteExpressionParser.infiniteDogsAndCats,
52+
"the quick brown fox which jumps over the lazy cat which jumps over the lazy dog which jumps over the lazy cat"
53+
)
5154
assert(result.successful)
5255
}
5356

5457
@Test
5558
def infiniteExpressionCompletesToAlternativesUpToStop(): Unit = {
56-
val completions = InfiniteExpressionParser.completeString(InfiniteExpressionParser.infiniteDogsAndCats, "the quick brown fox ")
57-
Assert.assertEquals(
58-
Seq(
59-
" which jumps over the lazy cat which jumps over the lazy cat",
60-
" which jumps over the lazy cat which jumps over the lazy dog",
61-
" which jumps over the lazy dog which jumps over the lazy cat",
62-
" which jumps over the lazy dog which jumps over the lazy dog"
63-
),
59+
val completions = InfiniteExpressionParser.complete(InfiniteExpressionParser.infiniteDogsAndCats, "the quick brown fox ")
60+
InfiniteExpressionParser.assertHasCompletions(
61+
Set(
62+
Tagged(
63+
"animal",
64+
Some("dogs and cats"),
65+
10,
66+
" which jumps over the lazy cat which jumps over the lazy cat",
67+
" which jumps over the lazy cat which jumps over the lazy dog",
68+
" which jumps over the lazy dog which jumps over the lazy cat",
69+
" which jumps over the lazy dog which jumps over the lazy dog"
70+
)),
6471
completions
6572
)
6673
}

0 commit comments

Comments
 (0)