blob: 39e030db9e8b9abfda6cf65274a499f8bb450f8b [file] [log] [blame] [edit]
--
-- Copyright 2016, NICTA
--
-- This software may be distributed and modified according to the terms of
-- the GNU General Public License version 2. Note that NO WARRANTY is provided.
-- See "LICENSE_GPLv2.txt" for details.
--
-- @TAG(NICTA_GPL)
--
{-# LANGUAGE DeriveDataTypeable #-}
module Isabelle.InnerAST where
-- system imports
import Data.Data
import Data.List
import Data.Typeable
import Text.Parsec.Expr (Assoc(..))
import Text.PrettyPrint.ANSI.Leijen
import Data.Char (ord)
import Text.Printf (printf)
-- friends
import Isabelle.PrettyHelper
--
-- The AST for the term language does NOT follow the definition in the ISAR Reference manual
-- exactly. This is because first-order and higher-order logic syntax is defined using Isabelle's
-- extensible syntax. Here we have amalgamated the base term language, and other admissible terms
-- defined in other theory files into one AST to simplify the implementation.
--
--
data Term = TermIdent Ident
| TermApp Term Term
| TermWithType Term Type -- A :: bool
| QuantifiedTerm Quantifier [Ident] Term -- \\<
| TermUnOp TermUnOp Term
| TermBinOp TermBinOp Term Term
| AntiTerm String
| ConstTerm Const
| ListTerm String [Term] String
| CaseOf Term [(Term, Term)]
deriving (Data, Typeable, Eq, Ord, Show)
data Const = TrueC | FalseC
| IntLiteral Integer
| CharLiteral Char
| StringLiteral String
| Top | Bottom
deriving (Data, Typeable, Eq, Ord, Show)
data Quantifier = MetaBind -- \<And>
| Lambda
| Forall -- \<forall>
| Exists -- \<exists>
| ExistsBang
deriving (Data, Typeable, Eq, Ord, Show)
data TermBinOp =
-- Isabelle/Pure
Equiv
| MetaImp -- ==> or \<Longrightarrow>
-- Isabelle/HOL
| Eq
| NotEq
| Iff
| Conj
| Disj
| Implies
deriving (Data, Typeable, Eq, Ord, Show)
data TermUnOp =
-- Isabelle/HOL
Not deriving (Data, Typeable, Eq, Ord, Show)
type Id = String
data Ident = Id Id
| Wildcard
| TypedIdent Ident Type
deriving (Data, Typeable, Eq, Ord, Show)
data PrimType = IntT
| BoolT
| NatT
deriving (Data, Typeable, Eq, Ord, Show)
data Type = TyVar String
| TyDatatype String [Type]
| TyPrim PrimType
| TyArrow Type Type
| AntiType String
| TyTuple Type Type
deriving (Data, Typeable, Eq, Ord, Show)
-- typeToId :: Type -> Id
-- typeToId (TyDatatype s ts) = s
-- typeToId _ = error "panic: typeToId: must be datatype"
data Arity = Arity (Maybe [Sort]) Sort deriving (Data, Typeable, Eq, Ord, Show)
type Sort = Id -- FIXME: zilinc
-- Smart constructors
mkId :: Id -> Term
mkId = TermIdent . Id
mkTru = ConstTerm TrueC
mkFls = ConstTerm FalseC
mkInt = ConstTerm . IntLiteral
mkBool b = if b then mkTru else mkFls
mkChar = ConstTerm . CharLiteral
mkString = ConstTerm . StringLiteral
tyTerm :: Term -> Type -> Term
tyTerm = TermWithType
mkApp :: Term -> [Term] -> Term
mkApp t0 [] = t0
mkApp t0 (t:ts) = mkApp (TermApp t0 t) ts
mkPair :: Term -> Term -> Term
mkPair a b = ListTerm "(" [a, b] ")"
mkList :: [Term] -> Term
mkList xs = ListTerm "[" xs "]"
mkTuple :: [Term] -> Term
mkTuple xs = ListTerm "(" xs ")"
lamTerm :: [Ident] -> Term -> Term
lamTerm ids t = QuantifiedTerm Lambda ids t
mkLambda :: [Id] -> Term -> Term
mkLambda vs t = lamTerm (map Id vs) t
subSym :: String
subSym = "\\<^sub>"
-- ^sub's a string
subSymStr :: String -> String
subSymStr = foldl (\s v -> s ++ subSym ++ [v]) []
--
-- The associativity, precedence and symbols for Isabelle/HOL terms can be found in the Isabelle
-- source at src/HOL/HOL.thy For Isabelle/Pure terms I don't know where to look for associativity
-- and precedence. I consulted a cheat sheet I found on the Internet.
--
termBinOpRec :: TermBinOp -> BinOpRec
termBinOpRec b = case b of
Equiv -> BinOpRec AssocRight 2 "\\<equiv>"
MetaImp -> BinOpRec AssocRight 1 "\\<Longrightarrow>"
Eq -> BinOpRec AssocLeft 50 "="
NotEq -> BinOpRec AssocLeft 50 "\\<noteq>"
Iff -> BinOpRec AssocRight 24 "\\<Leftrightarrow>"
Conj -> BinOpRec AssocRight 35 "\\<and>"
Disj -> BinOpRec AssocRight 30 "\\<or>"
Implies -> BinOpRec AssocRight 25 "\\<longrightarrow>"
-- You must include all binary operators in this list. Miss one and it doesn't get parsed.
-- Order does NOT matter. They are sorted by precedence.
binOps = [Equiv, MetaImp, Eq, NotEq, Iff, Conj, Disj, Implies]
termBinOpPrec :: TermBinOp -> Precedence
termBinOpPrec b = if p >= termAppPrec
then error (show (binOpRecSym baux) ++
" should not have a precedence higher than that of application (termAppPrec)")
else p
where
baux = termBinOpRec b
p = binOpRecPrec baux
termBinOpSym :: TermBinOp -> String
termBinOpSym = binOpRecSym . termBinOpRec
termBinOpAssoc :: TermBinOp -> Assoc
termBinOpAssoc = binOpRecAssoc . termBinOpRec
-- Predence for a unary operator
--
-- The precedences for Isabelle/HOL terms can be found in the Isabelle source at src/HOL/HOL.thy
-- For Isabelle/Pure terms I don't know where to look. I consulted a cheat sheet.
--
termUnOpRec :: TermUnOp -> UnOpRec
termUnOpRec u = case u of
Not -> UnOpRec 40 "\\<not>"
termUnOpPrec = unOpRecPrec . termUnOpRec
termUnOpSym = unOpRecSym . termUnOpRec
-- You must include all unary operators in this list. Miss one and it doesn't get parsed.
-- Order does NOT matter. They are sorted by precedence.
termUnOps = [Not]
data QuantifierRec = QuantifierRec { quantifierRecPrecedence :: Precedence, quantifierRecSymbol :: String }
--
-- The precedences for Isabelle/HOL terms can be found in the Isabelle source at src/HOL/HOL.thy
-- For Isabelle/Pure terms I don't know where to look. I consulted a cheat sheet.
--
quantifierAux :: Quantifier -> QuantifierRec
quantifierAux q = case q of
MetaBind -> QuantifierRec 0 "\\<And>"
Lambda -> QuantifierRec 3 "\\<lambda>"
Forall -> QuantifierRec 10 "\\<forall>"
Exists -> QuantifierRec 10 "\\<exists>"
ExistsBang -> QuantifierRec 10 "\\<exists>!"
quantifierPrec = quantifierRecPrecedence . quantifierAux
quantifierSym = quantifierRecSymbol . quantifierAux
--
-- You must include all quantifiers in this list. Miss one and it doesn't get parsed.
-- Order does NOT matter. They are sorted by precedence.
--
quantifiers = [MetaBind, Lambda, Forall, Exists, ExistsBang]
--
-- Pretty printing
--
abstractor :: String -> [Doc] -> Doc -> Doc
abstractor s docs doc = string s <> hsep docs <> char '.' <+> doc
absTerm :: String -> [Ident] -> Term -> Doc
absTerm s idents term = abstractor s (map pretty idents) (pretty term)
binOp :: String -> Doc -> Doc -> Doc
binOp s d d' = d <+> string s <+> d'
binOpTerm p s t t' = binOp s (prettyTerm p t) (prettyTerm p t')
-- precedence of application. Nothing should be higher
termAppPrec = 100
prettyTerm :: Precedence -> Term -> Doc
prettyTerm p t = case t of
TermIdent i -> pretty i
-- highest precedence and left associative
TermApp t t' -> prettyParen (p > termAppPrec) $ prettyTerm termAppPrec t <+>
prettyTerm (termAppPrec+1) t'
TermWithType t typ -> prettyParen True $ pretty t <+> string "::" <+> pretty typ
QuantifiedTerm q is t -> prettyQuantifier p q is t
TermBinOp b t t' -> (case b of
MetaImp -> prettyMetaImp p t t'
_ -> prettyBinOpTerm p b t t')
TermUnOp u t -> prettyUnOpTerm p u t
ListTerm l ts r -> pretty l <> hcat (intersperse (string ", ") (map (prettyTerm termAppPrec) ts)) <> pretty r
ConstTerm const -> pretty const
AntiTerm str -> pretty str -- FIXME: zilinc
CaseOf e alts -> parens (string "case" <+> pretty e <+> string "of" <+> sep (punctuate (text "|") (map prettyAlt alts)))
prettyAlt :: (Term, Term) -> Doc
prettyAlt (p, e) = pretty p <+> pretty "\\<Rightarrow>" <+> pretty e
prettyBinOpTerm :: Precedence -> TermBinOp -> Term -> Term -> Doc
prettyBinOpTerm p b = prettyBinOp p prettyTerm (termBinOpRec b) prettyTerm
prettyUnOpTerm :: Precedence -> TermUnOp -> Term -> Doc
prettyUnOpTerm p u = prettyUnOp p (termUnOpRec u) prettyTerm
--
-- [| P_1; ...; P_n |] ==> Q is syntactic sugar for P_1 ==> ... ==> P_n ==> Q
--
-- @prettyMetaImp@ takes care of printing it that way.
prettyMetaImp :: Precedence -> Term -> Term -> Doc
prettyMetaImp p t t' = case t' of
t'@(TermBinOp MetaImp _ _) -> go [t] t'
_ -> prettyBinOpTerm p MetaImp t t'
where
p' = termBinOpPrec MetaImp
go ts (TermBinOp MetaImp t t') = go (t:ts) t'
go ts t =
string "\\<lbrakk>" <>
(hsep . punctuate semi . map (prettyTerm (p'+1)) . reverse $ ts) <>
string "\\<rbrakk>" <+> string (termBinOpSym MetaImp) <+> prettyTerm p' t
prettyQuantifier :: Precedence -> Quantifier -> [Ident] -> Term -> Doc
prettyQuantifier p q is t = prettyParen (p > quantifierPrec q) $ string (quantifierSym q) <>
(hsep . map pretty $ is) <> char '.' <+> pretty t
instance Pretty Ident where
pretty ident = case ident of
Id id -> string id
Wildcard -> string "_"
TypedIdent id ty -> pretty id <+> string "::" <+> pretty ty
instance Pretty Term where
pretty = prettyTerm 0
instance Pretty PrimType where
pretty ty = string $ case ty of
IntT -> "int"
BoolT -> "bool"
NatT -> "nat"
instance Pretty Type where
pretty = prettyType 0
tyArrowSym = "\\<Rightarrow>"
tyTupleSym = "\\<times>"
prettyTypeVars :: [Type] -> Doc
prettyTypeVars [] = empty
prettyTypeVars [ty] = prettyType 100 ty -- application has highest precedence
prettyTypeVars tys = char '(' <> (hsep . punctuate (char ',') . map (prettyType 0) $ tys) <> char ')' -- FIXME: not very pretty / zilinc
prettyType :: Precedence -> Type -> Doc
prettyType p ty =
case ty of
TyVar v -> char '\'' <> string v
TyDatatype s tys -> prettyTypeVars tys <+> string s
TyPrim t -> pretty t
-- TyArrow is right associative
TyArrow t t' -> prettyParen (p > pa) $ prettyType (pa+1) t <+>
string tyArrowSym <+> prettyType pa t'
-- TyTuple is right associative
TyTuple t t' -> prettyParen (p > pt) $ prettyType (pt+1) t <+>
string tyTupleSym <+> prettyType pt t'
AntiType t -> string t -- FIXME: zilinc
where
pa = 1
pt = 2
instance Pretty Const where
pretty c = case c of
TrueC -> string "True"
FalseC -> string "False"
IntLiteral i -> integer i
CharLiteral c -> string $ printf "CHR %#02x" $ ord c
StringLiteral s | '\'' `elem` s -> string $ "[" ++ intercalate "," (map repr s) ++ "]"
| otherwise -> string $ "''" ++ s ++ "''"
where repr = printf "CHR %#02x" . ord
Top -> string "\\<top>"
Bottom -> string "\\<bottom>"
instance Pretty Arity where
pretty (Arity Nothing n) = string n
pretty (Arity (Just ns) n) = parens (sep $ punctuate comma $ map string ns) <+> string n
--------
idn = mkId
infixr 5 `imp`
imp = TermBinOp MetaImp
newtype Lines a = Lines [a]
instance Show a => Show (Lines a) where
show (Lines xs) = concat . intersperse "\n" . map show $ xs
test = pretty t0 <+> string "|" <+> pretty t1 <+> string "|" <+> pretty t2
where conj = TermBinOp Conj
disj = TermBinOp Disj
t0 = (idn "A") `conj` (idn "B")
t1 = ((idn "A") `conj` (idn "B")) `disj` (idn "C") -- A \<and> B \<or> C
t2 = (idn "A") `conj` ((idn "B") `disj` (idn "C")) -- A \<and> (B \<or> C)
test2 = Lines [ pretty (idn "A" `imp` idn "B" `imp` idn "C" `imp` idn "D")
, pretty ((idn "A" `imp` idn "B") `imp` idn "C")
, pretty (TermBinOp Equiv (TermApp (idn "A") (idn "x")) (TermApp (idn "B") (idn "x"))) ]
testt = Lines [ pretty (v "x" ==> v "y" ==> v "z")
, pretty ((v "x" ==> v "y") ==> v "z")
, pretty (TyDatatype "option" [TyVar "a"])
, pretty (TyDatatype "list" [TyVar "a"])
, pretty (TyDatatype "option" [TyTuple (v "a") (TyTuple (v "b") (v "c"))])
, pretty (TyDatatype "option" [v "a" ==> v "b"])
, pretty (TyDatatype "twoparam" [TyTuple (v "a") (v "b"), (v "c")]) ]
where
v x = TyVar x
infixr 5 ==>
a ==> b = TyArrow a b