packages feed

sexp-grammar-2.2.1: src/Language/Sexp/Parser.y

{
{-# LANGUAGE CPP               #-}
{-# LANGUAGE OverloadedStrings #-}

{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures  #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing      #-}
{-# OPTIONS_GHC -fno-warn-tabs                #-}
{-# OPTIONS_GHC -fno-warn-unused-binds        #-}
{-# OPTIONS_GHC -fno-warn-unused-matches      #-}

module Language.Sexp.Parser
  ( parseSexp_
  , parseSexps_
  ) where

import qualified Data.ByteString.Lazy.Char8 as B8
import qualified Data.List.NonEmpty as NE
import qualified Data.Scientific
import Data.Text (Text)
import qualified Data.Text as T
import Data.Text.Prettyprint.Doc

#if MIN_VERSION_prettyprinter(1,7,0)
import qualified Data.Text.Prettyprint.Doc.Render.String as Render
#else
import qualified Data.Text.Prettyprint.Doc.Render.ShowS as Render
#endif

import Language.Sexp.Token
import Language.Sexp.Lexer
import Language.Sexp.Types
}

%name parseSexp_ Sexp_
%name parseSexps_ Sexps_
%error { parseError }
%tokentype { LocatedBy Position Token }
%monad { Either String }

%token
  '('            { _ :< TokLParen     }
  ')'            { _ :< TokRParen     }
  '['            { _ :< TokLBracket   }
  ']'            { _ :< TokRBracket   }
  '{'            { _ :< TokLBrace     }
  '}'            { _ :< TokRBrace     }
  PREFIX         { _ :< (TokPrefix _) }
  SYMBOL         { _ :< (TokSymbol _) }
  NUMBER         { _ :< (TokNumber _) }
  STRING         { _ :< (TokString _) }

  EOF            { _ :< TokEOF        }

%%

Sexps_ :: { [Sexp] }
  : Sexps EOF                             { $1 }

Sexps :: { [Sexp] }
  : list(Sexp)                            { $1 }

Sexp_ :: { Sexp }
  : Sexp EOF                              { $1 }

Sexp :: { Sexp }
  : Atom                                  { AtomF                       @@ $1 }
  | '(' list(Sexp) ')'                    { const (ParenListF $2)       @@ $1 }
  | '[' list(Sexp) ']'                    { const (BracketListF $2)     @@ $1 }
  | '{' list(Sexp) '}'                    { const (BraceListF $2)       @@ $1 }
  | PREFIX Sexp                           { const (ModifiedF
                                                    (getPrefix (extract $1))
                                                    $2)                 @@ $1 }

Atom :: { LocatedBy Position Atom }
  : NUMBER                                { fmap (AtomNumber . getNumber) $1 }
  | STRING                                { fmap (AtomString . getString) $1 }
  | SYMBOL                                { fmap (AtomSymbol . getSymbol) $1 }

-- Utils

rev_list1(p)
  : p                      { [$1]    }
  | rev_list1(p) p         { $2 : $1 }

list1(p)
  : rev_list1(p)           { reverse $1 }

list(p)
  : {- empty -}            { [] }
  | list1(p)               { $1 }

{

type Sexp = Fix (Compose (LocatedBy Position) SexpF)

(@@) :: (a -> e (Fix (Compose (LocatedBy p) e))) -> LocatedBy p a -> Fix (Compose (LocatedBy p) e)
(@@) f (p :< a) = Fix . Compose . (p :<) . f $ a

parseError :: [LocatedBy Position Token] -> Either String b
parseError toks = case toks of
  [] ->
    Left "EOF: Unexpected end of file"
  (pos :< tok : _) ->
    Left $ flip Render.renderShowS [] . layoutPretty (LayoutOptions (AvailablePerLine 80 0.8)) $
      pretty pos <> colon <+> "Unexpected token:" <+> pretty tok
}