Giter VIP home page Giter VIP logo

gophercon22-parser-combinators's Introduction

Simple parser combinator package as shown at GopherCon 2022

Parser combinators are a composable way of building parsers in code; they stand in contrast to traditional parser generators like goyacc. They work particularly well in Go now that Go has generics, and they are remarkably easy to implement from scratch. This repo contains a simple parser combinator implementation, and an example of using that to build a parser for a simple configuration language.

I wrote it principally for a presentation at GopherCon 2022, in which my goal was to do a quick introduction to parser combinators, show how to use a few primitives to implement a parser for a microlanguage, and show how to implement all the primitives -- each in just a few lines of Go.

Usage in your projects

Feel free to grab and use this -- just copy the code so you can modify it to suit your ends, and keep the copyright around somewhere.

The parser API is very loosely inspired by the parser package for the Elm language.

Exercises

If you want to get more familiar with this implementation of parser combinators, here are a few exercises to try out.

Simpler

  • Add a Parser[Empty] named End which succeeds only when you have no more input remaining. Remove the check for remaining input in the Parse function and modify the example grammar to use End to ensure no input remains.

  • Add a function to the parser package called Lookahead that takes a Parser[T] as an argument, and returns a Parser[T] which returns the same value as the input parser, but without consuming any input -- in other words, it looks to see if the argument parser matches the upcoming input but doesn't actually consume that input.

  • Extend the state implementation in parser to track line and column numbers, and add a parser function called GetPosition that returns the current line and column numbers, while consuming no input. (This could be used in sequences, say, to get text positions.)

More complex

  • As written, OneOf always backtracks. Add a function Commit that transforms its argument parser such that if it has an error, OneOf will immediately fail instead of continuing to try more parsers. Here's a a rough example, where the idea is that after seeing a { if this parser fails, a OneOf containing it should not proceed to try others.

    myCodeParser := AndThen(
      StartSkipping(Exactly("{")),
      func (Empty) Parser[MyCodeBody] {
          return AndThen(Commit(myCodeBodyParser),
              func (body MyCodeBody) Parser[MyCodeBody] {
                  return Map(Exactly("}"), 
                      func(Empty) MyCodeBody {
                          return body
                      }
                  )
              }
    

    Hint: You'll have to modify OneOf to make this work. The interactions between backtracking control and sequencing (with AndThen or with the special sequencing forms) also bears thinking about.

Improving the debugging experience:

Because our combinators are implemented as functions, if we use the debugger to analyze mid-parse, we just see a stack of closures.

  • Approach A: Add a payload data type to Parser, so it's Parser[T any, D any]. Write a WithData wrapper function that takes a parser and payload data and returns a parser in which incoming state will contain the new payload data when the underlying parser is run. Add a GetData parser that, when run, returns the current payload data. This will enable you to provide contextual data (e.g. in printfs or errors from your parsing.)

  • Approach B: replace the functions with interfaces. Change the type of Parser from func (state)... to

       type Parser[T any] interface { 
          parse(state) (T, state, error)
      }
    

    Now modify all of the parser-generating and combining functions to return various structs implementing the interface. The debugger should show a stack of method calls on specific struct types now. Is it more clear? (Let me know, I haven't yet tried this myself.)

Feedback appreciated

I'd be very grateful for any feedback, comments, or questions.

gophercon22-parser-combinators's People

Contributors

jhbrown-veradept avatar jhbrown94 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.