-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Negative lookahead? #58
Comments
Maybe something like this? operator fun <T> Parser<T>.not(): NotParser<T> {
return NotParser(this)
}
class NotParser<T>(
private val child: Parser<T>
) : Parser<T> {
override fun tryParse(tokens: TokenMatchesSequence, fromPosition: Int): ParseResult<T> {
return when (val res = child.tryParse(tokens, fromPosition)) {
is Parsed<*> -> {
val tokenMatch = tokens[fromPosition] ?: return UnexpectedEof(tokens.first().type)
NegatedResult(tokenMatch)
}
else -> res
}
}
}
data class NegatedResult(val unexpected: TokenMatch) : ErrorResult()
|
I guess this won't work, I'd need some kind of "empty success" value on the positive branch 🤔 |
I'd say you can use a class NegativeLookaheadCombinator(internal val lookaheadParser: Parser<*>) : Parser<Unit> {
override fun tryParse(tokens: TokenMatchesSequence, fromPosition: Int): ParseResult<Unit> =
when (lookaheadParser.tryParse(tokens, fromPosition)) {
is Parsed -> LookaheadFoundInNegativeLookahead
else -> ParsedValue(Unit, fromPosition)
}
}
object LookaheadFoundInNegativeLookahead : ErrorResult()
fun not(parser: Parser<*>): SkipParser =
skip(NegativeLookaheadCombinator(parser))
class NegativeLookaheadCombinatorTest {
@Test
fun testNegativeLookahead() {
val grammar = object : Grammar<Int>() {
val digits by regexToken("[0-9]+")
val dot by literalToken(".")
val semi by literalToken(";")
override val rootParser: Parser<Int>
get() = (digits * not(dot * digits) * -semi).use { text.toInt() }
}
assertEquals(LookaheadFoundInNegativeLookahead, grammar.tryParseToEnd("1.0;"))
assertEquals(ParsedValue(1, 2), grammar.tryParseToEnd("1;"))
}
} |
Great, thanks! I'll give this a try and submit a PR if it looks good. :) |
hmm, a few things:
|
My idea was that this combinator should only test the specified parser for a match at the given position. It should not "advance" the position, as it only looks ahead. What does your test check?
I'm not sure I'm getting it right. Should this non-A value rather get consumed and parsed by some of the following parsers that get invoked after the negative lookahead combinator returns the successful (Unit) result?
|
This is what I was going for: val abAndNotDAndD by a and b and not(d) and d
val tokens = tokenizer.tokenize("abcd")
val res = abAndNotDAndD.parseToEnd(tokens)
assertSame(a, res.t1.type)
assertSame(b, res.t2.type)
assertSame(d, res.t3.type) // TODO can we avoid discarding the `c`?
// assertSame(d, res.t4.type) |
It looks like it's very uncommon for PEG parsers to return any value from a lookahead operator, so for now I'll get rid of my test case and maybe add positive lookahead while I'm at it. :) |
Extremely WIP attempt in #59! |
Hi there, I have a grammar with some negative lookahead rules. From what I can tell, some other PEG parsers have a
!
operator that can help with this, but it appears this library doesn't. Is this something that would be easy to implement "in userspace"? I'm not a parsing expert. Thanks!The text was updated successfully, but these errors were encountered: