h0tk3y / better-parse

A nice parser combinator library for Kotlin
Apache License 2.0
422 stars 42 forks source link

Advanced rules #3

Open barsan-md opened 7 years ago

barsan-md commented 7 years ago

I wonder if there is it possible to implement advanced rules, something like if(condition), then(this rule can be applied).

I'm new to both kotlin and parser combinators so maybe my understanding of the problem is completely wrong.

As a concrete example what I want to do is very similar to the aritmetic evaluator example: https://github.com/h0tk3y/better-parse/blob/master/demo/src/main/kotlin/com/example/ArithmeticsEvaluator.kt except I have many different levels of precedece on operators (not just 3: sum, sub < mul, div < pow). (Small note here: exponentiation is right associative, not left (a^b^c = a^(b^c))) Suppose, instead of collectiong the same level operations into the same rule (like the example), a general single rule is the only way possible, to be more general and open to the addition of new operator.

val digits by token("\\d+")
val plus by token("+")
val minus
// val ...
val operator = plus or minus or ...
// this won't work because it will match addition before others
val operation: Parser<Tree> = digits and operator and digits
// it must match an operation that has the maximum value in the chain
val operation: Parser<Tree> = not(operator with higher precedence) and digits and operator and digits and not(operator with higher precedence)
// or you must associate the digits with the operator having higher precedence
val assignLeft: Parser<Tree> = (operator with higher precedence) and digits and (operator with lower precedence)
val rightAssign: Parser<Tree> = (operator with lower precedence) and digits and (operator with higher precedence)

Is it possible or does this approach make sense? PS: this library is awesome and the infix notation makes the rules very readable when compared with other libraries and languages. Good job, thank you!

h0tk3y commented 7 years ago

@barsan-ds I'm not quite sure I understand your use case. Is it similar to the operator table in jparsec? If not, could you please provide a more concrete example with operators and maybe the input that should be parsed?

barsan-md commented 7 years ago

From what I could understand from the example, that seems to be the case. Does anything like that exist in better-parse?

h0tk3y commented 7 years ago

Not yet. There's no built-in features that allow defining operator rules like this. Though I considered adding something like that (and especially with support for left recursion removal), and I might add this among other features in future.

You can define combinators in your project according to your needs, and I'll be grateful if you come up with an example implementation of the operator table.

InfectedBytes commented 2 years ago

hey there, this sounds like a nice feature and I prototyped a lightweight solution for this. With it the ArithmeticEvaluator could be simplified to this:

// ... tokens as usual
val number by num use { text.toInt() }
val term: Parser<Int> by number or
        (skip(minus) and parser(::term) map { -it }) or
        (skip(lpar) and parser(::rootParser) and skip(rpar))

// subSumchain, divMulChain, powChain can be merged to this:
override val rootParser: Parser<Int> by operatorTable(term)
    .infixl(pow) { a, _, b -> a.toDouble().pow(b.toDouble()).toInt() } // highest priority first
    .infixl(div or mul use {type }) { a, op, b -> if (op == div) a / b else a * b }
    .infixl(plus or minus use { type }) { a, op, b -> if (op == plus) a + b else a - b } // lowest priority last

the operator table provides these helpers:

I can create a PullRequest if you think this might be a good fit for your lib