packages feed

huff-0.1.0.0: src/Huff/QQ/Parser.y

{
-- vim: ft=haskell
{-# OPTIONS_GHC -w #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RecordWildCards #-}
module Huff.QQ.Parser where

import Huff.Compile.AST
import Huff.QQ.Lexer (lexer,Lexeme(..),Token(..),Keyword(..),SourcePos,sourceFrom)

import qualified Data.Text as T

}

%tokentype { Lexeme Token }

%token
  IDENT       { $$ @ Lexeme { lexemeToken = TIdent    {} } }
  CONIDENT    { $$ @ Lexeme { lexemeToken = TConIdent {} } }

  'domain'    { KW K_domain    $$ }
  'object'    { KW K_object    $$ }
  'predicate' { KW K_predicate $$ }
  'operator'  { KW K_operator  $$ }
  'requires'  { KW K_requires  $$ }
  'effect'    { KW K_effect    $$ }
  '{'         { KW K_lbrace    $$ }
  '}'         { KW K_rbrace    $$ }
  '('         { KW K_lparen    $$ }
  ')'         { KW K_rparen    $$ }
  ','         { KW K_comma     $$ }
  '='         { KW K_assign    $$ }
  '|'         { KW K_pipe      $$ }
  ':'         { KW K_colon     $$ }
  '!'         { KW K_not       $$ }

%monad { Parse }
%error { parseError }

%name domains domains

%%

-- Domains ---------------------------------------------------------------------

domains :: { [Domain T.Text] }
  : list1(domain) { $1 }

domain :: { Domain T.Text }
  : 'domain' CONIDENT '{' list(domain_elem) '}'
    { foldr id (Domain (lexemeText $2) [] [] []) $4 }

domain_elem :: { Domain T.Text -> Domain T.Text }
  : object_decl    { $1 }
  | predicate_decl { $1 }
  | operator_decl  { $1 }

object_decl :: { Domain T.Text -> Domain T.Text }
  : 'object' type '=' sep1(CONIDENT, '|')
    { let { objs = [ Typed lexemeText $2 | Lexeme { .. } <- $4 ] }
       in \dom -> dom { domObjects = objs ++ domObjects dom } }

type :: { Type }
  : CONIDENT { lexemeText $1 }

predicate_decl :: { Domain T.Text -> Domain T.Text }
  : 'predicate' sep1(predicate_spec, ',')
    { foldr (.) id $2 } 

predicate_spec :: { Domain T.Text -> Domain T.Text }
  : IDENT '(' sep1(type, ',') ')'
    { \dom -> dom { domPreds = App (lexemeText $1) $3 : domPreds dom } }

operator_decl :: { Domain T.Text -> Domain T.Text }
  : 'operator' CONIDENT '(' sep(param, ',') ')' '{'
       'requires' ':' sep1(term,   ',')
       'effect'   ':' sep1(effect, ',')
    '}'
    { let { op = Operator { opName = lexemeText $2
                          , opDerived = False
                          , opParams = $4
                          , opVal = Just (lexemeText $2)
                          , opPrecond = TAnd $9
                          , opEffects = EAnd $12
                          } }
       in \dom -> dom { domOperators = op : domOperators dom } }

param :: { Param }
  : IDENT ':' type { Typed (lexemeText $1) $3 }


-- Expressions -----------------------------------------------------------------

term :: { Term }
  : atom     { TLit (LAtom $1) }
  | '!' term { TNot $2 }

effect :: { Effect }
  : literal { ELit $1 }

literal :: { Literal }
  :     atom { LAtom $1 }
  | '!' atom { LNot  $2 }

atom :: { Atom }
  : IDENT '(' sep1(arg, ',') ')'
    { App (lexemeText $1) $3 }

arg :: { Arg }
  : IDENT    { AVar  (lexemeText $1) }
  | CONIDENT { AName (lexemeText $1) }


-- Utilities -------------------------------------------------------------------

opt(p)
  : {- empty -} { Nothing }
  | p           { Just $1 }

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

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

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

sep(p,q)
  : {- empty -}  { []         }
  | sep_rev(p,q) { reverse $1 }

sep1(p,q)
  : sep_rev(p,q) { reverse $1 }

sep_rev(p,q)
  : p                { [$1]    }
  | sep_rev(p,q) q p { $3 : $1 }

{

type Parse = Either ParseError

data ParseError = ParseError (Maybe SourcePos)
                  deriving (Show)

parseError :: [Lexeme Token] -> Parse a
parseError (tok:_) = Left (ParseError (Just (sourceFrom (lexemeRange tok))))
parseError []      = Left (ParseError Nothing)

pattern KW k loc <- Lexeme { lexemeToken = TKeyword k, lexemeRange = loc }

parseQQ :: SourcePos -> String -> Parse [Domain T.Text]
parseQQ start str = domains toks
  where
  toks = lexer start str

}