better-parse icon indicating copy to clipboard operation
better-parse copied to clipboard

ClassCastException when combining a Tuple2 parser

Open Benjozork opened this issue 3 years ago • 3 comments

Hello,

The following code :

val typePrefix by (type * -znws * parser(this::referenceParser) * -znws * -assign)

// ...

val traitFunction    by parser(this::functionSignature) use { CyanTraitDeclaration.Element.Function(CyanFunctionDeclaration(this, null), span) }
val traitProperty    by structProperty                  use { CyanTraitDeclaration.Element.Property(ident, type, span) }
val traitElement     by (traitFunction or traitProperty)
val traitDeclaration by (typePrefix * -znws * -trait * -znws * -lcur * -znws * (separatedTerms(traitElement, newLine)
    .use { toTypedArray() }) * -znws * -rcur) use { CyanTraitDeclaration(t2, t3, span(t1, t3.last())) }

Yields the following exception :

com.github.h0tk3y.betterParse.utils.Tuple2 cannot be cast to com.github.h0tk3y.betterParse.lexer.TokenMatch
java.lang.ClassCastException: com.github.h0tk3y.betterParse.utils.Tuple2 cannot be cast to com.github.h0tk3y.betterParse.lexer.TokenMatch
	at cyan.compiler.parser.CyanModuleParser$$special$$inlined$and2Operator$2.invoke(andFunctions.kt:9)
	at cyan.compiler.parser.CyanModuleParser$$special$$inlined$and2Operator$2.invoke(andFunctions.kt)
	at com.github.h0tk3y.betterParse.combinators.AndCombinator.tryParse(AndCombinator.kt:60)
	at com.github.h0tk3y.betterParse.combinators.MapCombinator.tryParse(MapCombinator.kt:14)
	at com.github.h0tk3y.betterParse.combinators.OrCombinator.tryParse(OrCombinator.kt:13)
	at com.github.h0tk3y.betterParse.combinators.AndCombinator.tryParse(AndCombinator.kt:40)
	at com.github.h0tk3y.betterParse.combinators.AndCombinator.tryParse(AndCombinator.kt:40)
	at com.github.h0tk3y.betterParse.combinators.SeparatedCombinator.tryParse(Separated.kt:16)
	at com.github.h0tk3y.betterParse.combinators.MapCombinator.tryParse(MapCombinator.kt:14)
	at com.github.h0tk3y.betterParse.combinators.MapCombinator.tryParse(MapCombinator.kt:14)
	at com.github.h0tk3y.betterParse.combinators.AndCombinator.tryParse(AndCombinator.kt:40)
	at com.github.h0tk3y.betterParse.combinators.MapCombinator.tryParse(MapCombinator.kt:14)
	at com.github.h0tk3y.betterParse.parser.ParserKt.tryParseToEnd(Parser.kt:18)
	at com.github.h0tk3y.betterParse.parser.ParserKt.parseToEnd(Parser.kt:29)
	at com.github.h0tk3y.betterParse.grammar.GrammarKt.parseToEnd(Grammar.kt:65)
	at cyan.compiler.parser.StatementsTest.doTest(StatementsTest.kt:29)
	at cyan.compiler.parser.StatementsTest.access$doTest(StatementsTest.kt:24)
	at cyan.compiler.parser.StatementsTest$TypeDeclarations.trait with one function(StatementsTest.kt:34)
        <...>

From a quick look into the code, it seems like in andFunctions.kt this is function is called for the first * operator in typePrefix * -znws * -trait :

@JvmName("and2") inline infix fun <reified T1, reified T2, reified T3>
    AndCombinator<Tuple2<T1, T2>>.and(p3: Parser<T3>) =
    AndCombinator(consumers + p3, {
        Tuple3(it[0] as T1, it[1] as T2, it[2] as T3)
    })

But looking at the debugger, I see values packed into a tuple and not "flat mapped" (don't know if that makess sense, couldn't think of a better way to put it) :

image

Here, t1 of element 0 (which is the result of typePrefix) is the match for token type, and t2 is from parser(this::referenceParser). Yet, the code thinks t1 is element 0, and t2 is element 1, while they are in fact both inside element 0. This obviously causes a ClassCastException when it tries to cast element 0 into what I assume reified type parameter T1 is (TokenMatch).

I couldn't figure out which part of the library laid out those elements like that, which is where I'm stuck.

I hope this all made sense, and thanks in advance.

Benjozork avatar Aug 22 '20 00:08 Benjozork

@Benjozork did you manage to solve this for Cyan? I've run into exactly the same problem, on a much smaller grammar

richardhundt avatar May 07 '21 21:05 richardhundt

I think the issue is that the SkipParser throws away type information, so the reified types don't propagate, and for some reason Kotlin doesn't complain.

public class SkipParser(public val innerParser: Parser<*>)

Can't this be made generic?

richardhundt avatar May 07 '21 21:05 richardhundt

Hey, it's been a long time since I worked on cyan and I think I just reshuffled the parser a bit to where it wasn't causing an issue and forgot about it. Sorry I don't have a more useful solution 😬

Benjozork avatar May 09 '21 00:05 Benjozork