Skip to content
/ levels Public

Easier expression parsing with parser combinators

License

Notifications You must be signed in to change notification settings

ronyhe/levels

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

levels - Easier expression parsing with parser combinators

The problem

Expression grammars tend to tightly couple several "levels" of expression. Each level corresponds to a binding strength. This is a result of the common need to eliminate left recursion. An arithmetic example:

def expr = term ("+" ~ term)* ^^ combineAddition
def term = factor ("*" ~ factor)* ^^ combineMultiplication
def factor = numberParser | "(" ~> expr <~ ")"

These are hardcoded relationships that are inflexible and hard to get right.

The solution

The Levels trait factors out the the "top level parser" and the "next level parser". Each level becomes a function that takes those two and produces a parser.

type LevelCreator[A] = (=> Parser[A], Parser[A]) => Parser[A]

These level creators can be passed to the levels method that combines them into a single expression parser:

class Arithmetic extends Parsers with Levels {
    val bottom = failure("No viable alternative")
    levels(bottom)(Seq(
      (top, next) => chainl1(next, "+" ~> next, success(combineAddition)),
      (top, next) => chainl1(next, "*" ~> next, success(combineMultiplication)),
      (top, next) => numberParser | next
      (top, next) => "(" ~> expr <~ ")" | next
    ))
}

Now the grammar is easier to modify, extend and reason about. Any change is a simple matter of rearranging the Seq.

But wait, there's more

As long as we're here, might as well use the utility methods in the Levels trait to make our intentions clear:

class Arithmetic extends Parsers with Levels {
    levelsWithDefault(Seq(
      leftAssociativeNextLevel("+", combineAddition),
      leftAssociativeNextLevel("*", combineMultiplication),
      orNext(numberParser),
      (top, next) => "(" ~> top <~ ")" | next
    ))
}

Want to add an exponentiation operator? No problem:

class Arithmetic extends Parsers with Levels {
    levelsWithDefault(Seq(
      leftAssociativeNextLevel("+", combineAddition),
      leftAssociativeNextLevel("*", combineMultiplication),
      rightAssociativeNextLevel("^", combineExponentiation)
      orNext(numberParser),
      (top, next) => "(" ~> top <~ ")" | next
    ))
}

About

Easier expression parsing with parser combinators

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages