packages feed

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

{
{-# 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 Data.Text (Text)
import qualified Data.List.NonEmpty as NE
import qualified Data.Scientific
import qualified Data.Text as T
import qualified Data.ByteString.Lazy.Char8 as B8

import Data.Text.Prettyprint.Doc
import qualified Data.Text.Prettyprint.Doc.Render.ShowS as Render

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
  '('            { L _ TokLParen      }
  ')'            { L _ TokRParen      }
  '['            { L _ TokLBracket    }
  ']'            { L _ TokRBracket    }
  "'"            { L _ TokQuote       }
  '#'            { L _ TokHash        }
  Symbol         { L _ (TokSymbol  _) }
  Keyword        { L _ (TokKeyword _) }
  Integer        { L _ (TokInt     _) }
  Real           { L _ (TokReal    _) }
  String         { L _ (TokStr     _) }
  Bool           { L _ (TokBool    _) }

%%

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

Sexp :: { Sexp }
  : Atom                                  { (\a p -> Atom p a) @@ $1 }
  | '(' ListBody ')'                      { const $2 @@ $1 }
  | '[' VectorBody ']'                    { const $2 @@ $1 }
  | '#' '(' VectorBody ')'                { const $3 @@ $1 }
  | "'" Sexp                              { const (\p -> Quoted p $2) @@ $1 }

Atom :: { LocatedBy Position Atom }
  : Bool         { fmap (AtomBool    . getBool)           $1 }
  | Integer      { fmap (AtomInt     . getInt)            $1 }
  | Real         { fmap (AtomReal    . getReal)           $1 }
  | String       { fmap (AtomString  . getString)         $1 }
  | Symbol       { fmap (AtomSymbol  . getSymbol)         $1 }
  | Keyword      { fmap (AtomKeyword . mkKw . getKeyword) $1 }

ListBody :: { Position -> Sexp }
  : list(Sexp)   { \p -> List p $1 }

VectorBody :: { Position -> Sexp }
  : list(Sexp)   { \p -> Vector p $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 }

{
mkKw :: Text -> Kw
mkKw t = case T.uncons t of
  Nothing -> error "Keyword should start with :"
  Just (_, rs) -> Kw rs

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