jeovazero / boring-packrat

A boring packrat (PEG + memoization)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Boring Packrat

A packrat parser using Boring Haskell (a.k.a Simple Haskell).

The initial objective of the project was to make an email validator to use in another project, but it would be very easy just use a library from Hackage, so why not make a parser for fun?

Examples

Email

The email grammar was based on RFC 5321.

Check out the grammar here

Toy-Grammar

Expr    <- Add | Addend
Add     <- Addend "+" Expr
Addend  <- Mult | Factor
Mult    <- Factor "*" Addend
Factor  <- Parens | Decimal
Parens  <- "(" Expr ")"
Decimal <- [0-9]+
{-# LANGUAGE OverloadedStrings #-}
import BoringPackrat (
    parse,
    astFrom,
    AST(..),
    Terminal'(..),
    PEG(..),
    Grammar
  )
import BoringPackrat.PrettyPrint (prettyPrint)
import qualified Data.Word8 as W
import qualified Data.ByteString.Char8 as B8
import Data.Maybe (fromJust)

n = NonTerminal
_ParenLeft  = Terminal $ Lit W._parenleft  -- '('
_ParenRight = Terminal $ Lit W._parenright -- ')'
_Times      = Terminal $ Lit W._asterisk   -- '*'
_Plus       = Terminal $ Lit W._plus       -- '+'

grammar :: Grammar
grammar =
  [ ("Expr", Choice [n"Add",n"Addend"])
  , ("Add", Sequence [n"Addend",_Plus,n"Expr"])
  , ("Addend", Choice [n"Mult",n"Factor"])
  , ("Mult", Sequence [n"Factor",_Times,n"Addend"])
  , ("Factor", Choice [n"Parens",n"Decimal"])
  , ("Parens", Sequence [_ParenLeft,n"Expr",_ParenRight])
  , ("Decimal", Many1 $ Terminal Digit)
  ]

data Expr
    = Add Expr Expr
    | Mult Expr Expr
    | Decimal Int
    deriving (Show)

transformAST :: AST -> Expr
transformAST ast =
  case ast of
    Rule _ "Add" (Seq _ [a,_,b]) ->
      Add (transformAST a) (transformAST b)
    Rule _ "Mult" (Seq _ [a,_,b]) ->
      Mult (transformAST a) (transformAST b)
    Rule _ "Parens" (Seq _ [_,e,_]) ->
      transformAST e
    Rule _ "Decimal" (Str d) ->
      Decimal (read $ B8.unpack d)
    Rule _ _ e -> transformAST e
    _ -> error "Not implemented"

main = do
  let input = "(1+2)*3"
  let result = parse grammar "Expr" input
  let ast = fromJust $ astFrom result

  prettyPrint input ast
  print $ transformAST ast

Output:

Rule Expr -> (0,6) -> "(1+2)*3"
  Rule Addend -> (0,6) -> "(1+2)*3"
    Rule Mult -> (0,6) -> "(1+2)*3"
      Sequence -> (0,6) -> "(1+2)*3"
        -Rule Factor -> (0,4) -> "(1+2)"
          Rule Parens -> (0,4) -> "(1+2)"
            Sequence -> (0,4) -> "(1+2)"
              -Str "("
              -Rule Expr -> (1,3) -> "1+2"
                Rule Add -> (1,3) -> "1+2"
                  Sequence -> (1,3) -> "1+2"
                    -Rule Addend -> (1,1) -> "1"
                      Rule Factor -> (1,1) -> "1"
                        Rule Decimal -> (1,1) -> "1"
                          Str "1"
                    -Str "+"
                    -Rule Expr -> (3,3) -> "2"
                      Rule Addend -> (3,3) -> "2"
                        Rule Factor -> (3,3) -> "2"
                          Rule Decimal -> (3,3) -> "2"
                            Str "2"
              -Str ")"
        -Str "*"
        -Rule Addend -> (6,6) -> "3"
          Rule Factor -> (6,6) -> "3"
            Rule Decimal -> (6,6) -> "3"
              Str "3"
Mult (Add (Decimal 1) (Decimal 2)) (Decimal 3)

a boring haskell project by jeovazero 😔

About

A boring packrat (PEG + memoization)


Languages

Language:Haskell 97.0%Language:Nix 2.7%Language:Makefile 0.3%