language-smtlib-0.1.0.0: src/Language/SMTLIB/Parser/Term.hs
-- | Parsers for terms (with their binders and the @match@ form) and for the
-- declaration shapes built on top of them: sorted vars, datatype declarations
-- and function definitions.
module Language.SMTLIB.Parser.Term
( pTerm
, pVarBinding
, pSortedVar
, pPattern
, pMatchCase
, pSortDec
, pSelectorDec
, pConstructorDec
, pDatatypeDec
, pFunctionDec
, pFunctionDef
) where
import Data.Char (isDigit)
import Data.Functor (($>))
import Text.Megaparsec
import Language.SMTLIB.Parser.Internal
import Language.SMTLIB.Syntax.Annotation (SrcSpan)
import Language.SMTLIB.Syntax.Constant (Symbol)
import Language.SMTLIB.Syntax.Datatype
import Language.SMTLIB.Syntax.Term
-- | A @term@.
--
-- The first character selects the form directly instead of attempting and
-- rolling back alternatives: a non-paren term is a spec-constant (digit, @#@ or
-- @\"@) or a bare symbol; a paren term is dispatched by 'parenCompound'.
pTerm :: P (Term SrcSpan)
pTerm = withSpan $ do
c <- lookAhead anySingle
case c of
'(' -> parenCompound
_ | isConstStart c -> TConstant <$> pSpecConstant
| otherwise -> TQualIdent <$> pQualIdentifier
where
isConstStart x = isDigit x || x == '#' || x == '"'
-- | A parenthesised compound term: a binder
-- (@let@\/@lambda@\/@forall@\/@exists@), @match@, @!@, a qualified identifier
-- used directly (@(as ...)@ or @(_ ...)@), or an application.
--
-- The head word immediately after the @(@ is peeked (without committing) to
-- choose the form, so no alternative has to be parsed and rolled back. The
-- @(as ...)@ \/ @(_ ...)@ cases delegate to 'pQualIdentifier', which consumes
-- the paren itself; the other cases consume it here.
parenCompound :: P (SrcSpan -> Term SrcSpan)
parenCompound = do
mw <- lookAhead (openP *> optional pAnyWord)
case mw of
Just "let" -> binder (TLet <$> parens (some pVarBinding) <*> pTerm)
Just "lambda" -> binder (TLambda <$> parens (some pSortedVar) <*> pTerm)
Just "forall" -> binder (TForall <$> parens (some pSortedVar) <*> pTerm)
Just "exists" -> binder (TExists <$> parens (some pSortedVar) <*> pTerm)
Just "match" -> binder (TMatch <$> pTerm <*> parens (some pMatchCase))
Just "!" -> binder (TAnnot <$> pTerm <*> some pAttribute)
Just "as" -> TQualIdent <$> pQualIdentifier
Just "_" -> TQualIdent <$> pQualIdentifier
_ -> openP *> (TApp <$> pQualIdentifier <*> some pTerm) <* closeP
where
-- consume @(@ and the (already-peeked) head keyword, then the body
binder body = openP *> pAnyWord *> body <* closeP
-- | A @var_binding@ @(symbol term)@.
pVarBinding :: P (VarBinding SrcSpan)
pVarBinding = withSpan (parens (VarBinding <$> pSymbolRaw <*> pTerm))
-- | A @sorted_var@ @(symbol sort)@.
pSortedVar :: P (SortedVar SrcSpan)
pSortedVar = withSpan (parens (SortedVar <$> pSymbolRaw <*> pSort))
-- | A @pattern@: a single symbol, or @(constructor x1 ... xn)@. The bound
-- variables (and a whole single-symbol pattern) may be the @_@ wildcard
-- (SMT-LIB 2.7); the constructor symbol itself may not.
pPattern :: P (Pattern SrcSpan)
pPattern = withSpan (pvar <|> ctor)
where
pvar = PVar <$> pPatternSymbol
ctor = parens (PCtor <$> pSymbolRaw <*> some pPatternSymbol)
-- | A symbol in a @match@ pattern binding position: an ordinary symbol or the
-- @_@ wildcard. @_@ is otherwise a reserved word, so 'pSymbolRaw' rejects it.
pPatternSymbol :: P Symbol
pPatternSymbol = pSymbolRaw <|> (tok "_" $> "_")
-- | A @match_case@ @(pattern term)@.
pMatchCase :: P (MatchCase SrcSpan)
pMatchCase = withSpan (parens (MatchCase <$> pPattern <*> pTerm))
-- | A @sort_dec@ @(symbol numeral)@.
pSortDec :: P (SortDec SrcSpan)
pSortDec = withSpan (parens (SortDec <$> pSymbolRaw <*> numeral))
-- | A @selector_dec@ @(symbol sort)@.
pSelectorDec :: P (SelectorDec SrcSpan)
pSelectorDec = withSpan (parens (SelectorDec <$> pSymbolRaw <*> pSort))
-- | A @constructor_dec@ @(symbol selector_dec*)@.
pConstructorDec :: P (ConstructorDec SrcSpan)
pConstructorDec = withSpan (parens (ConstructorDec <$> pSymbolRaw <*> many pSelectorDec))
-- | A @datatype_dec@: @(constructor_dec+)@ or @(par (u+) (constructor_dec+))@.
pDatatypeDec :: P (DatatypeDec SrcSpan)
pDatatypeDec = withSpan $ do
_ <- openP
r <- par <|> nonPar
_ <- closeP
pure r
where
par = tok "par" *> (DatatypeDec <$> parens (some pSymbolRaw)
<*> parens (some pConstructorDec))
nonPar = DatatypeDec [] <$> some pConstructorDec
-- | A @function_dec@ @(symbol (sorted_var*) sort)@.
pFunctionDec :: P (FunctionDec SrcSpan)
pFunctionDec =
withSpan (parens (FunctionDec <$> pSymbolRaw
<*> parens (many pSortedVar)
<*> pSort))
-- | A @function_def@ @symbol (sorted_var*) sort term@ (no surrounding parens).
pFunctionDef :: P (FunctionDef SrcSpan)
pFunctionDef =
withSpan (FunctionDef <$> pSymbolRaw
<*> parens (many pSortedVar)
<*> pSort
<*> pTerm)