{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveTraversable #-} {-| Definitions for fixity, precedence levels, and declared syntax. -} module Agda.Syntax.Fixity where import Data.Foldable import Data.Function import Data.List as List import Data.Set (Set) import qualified Data.Set as Set import Data.Traversable import Data.Typeable (Typeable) import Agda.Syntax.Position import Agda.Syntax.Common import qualified Agda.Syntax.Abstract.Name as A import Agda.Syntax.Concrete.Name import Agda.Syntax.Notation import Agda.Utils.List #include "undefined.h" import Agda.Utils.Impossible -- * Notation coupled with 'Fixity' -- | The notation is handled as the fixity in the renamer. -- Hence, they are grouped together in this type. data Fixity' = Fixity' { theFixity :: Fixity , theNotation :: Notation } deriving (Typeable, Show, Eq) -- | Decorating something with @Fixity'@. data ThingWithFixity x = ThingWithFixity x Fixity' deriving (Functor, Foldable, Traversable, Typeable, Show) -- | All the notation information related to a name. data NewNotation = NewNotation { notaName :: QName , notaNames :: Set A.Name -- ^ The names the syntax and/or fixity belong to. -- -- Invariant: The set is non-empty. Every name in the list matches -- 'notaName'. , notaFixity :: Fixity -- ^ Associativity and precedence (fixity) of the names. , notation :: Notation -- ^ Syntax associated with the names. } deriving (Typeable, Show) -- | If an operator has no specific notation, then it is computed from -- its name. namesToNotation :: QName -> A.Name -> NewNotation namesToNotation q n = NewNotation { notaName = q , notaNames = Set.singleton n , notaFixity = f , notation = if null syn then syntaxOf $ unqualify q else syn } where Fixity' f syn = A.nameFixity n -- | Return the 'IdPart's of a notation, the first part qualified, -- the other parts unqualified. -- This allows for qualified use of operators, e.g., -- @M.for x ∈ xs return e@, or @x ℕ.+ y@. notationNames :: NewNotation -> [QName] notationNames (NewNotation q _ _ parts) = zipWith ($) (reQualify : repeat QName) [Name noRange [Id x] | IdPart x <- parts ] where -- The qualification of @q@. modules = init (qnameParts q) -- Putting the qualification onto @x@. reQualify x = List.foldr Qual (QName x) modules -- | Create a 'Notation' (without binders) from a concrete 'Name'. -- Does the obvious thing: -- 'Hole's become 'NormalHole's, 'Id's become 'IdParts'. -- If 'Name' has no 'Hole's, it returns 'noNotation'. syntaxOf :: Name -> Notation syntaxOf (NoName _ _) = noNotation syntaxOf (Name _ [_]) = noNotation syntaxOf (Name _ xs) = mkSyn 0 xs where -- Turn a concrete name into a Notation, -- numbering the holes from left to right. -- Result will have no 'BindingHole's. mkSyn :: Int -> [NamePart] -> Notation mkSyn n [] = [] mkSyn n (Hole : xs) = NormalHole (defaultNamedArg n) : mkSyn (1 + n) xs mkSyn n (Id x : xs) = IdPart x : mkSyn n xs defaultFixity' :: Fixity' defaultFixity' = Fixity' defaultFixity defaultNotation noFixity' :: Fixity' noFixity' = Fixity' noFixity noNotation -- | Merges all 'NewNotation's that have the same notation. -- -- If all 'NewNotation's with a given notation have the same fixity, -- then this fixity is preserved, and otherwise it is replaced by -- 'defaultFixity'. -- -- Precondition: No 'A.Name' may occur in more than one list element. -- Every 'NewNotation' must have the same 'notaName'. -- -- Postcondition: No 'A.Name' occurs in more than one list element. mergeNotations :: [NewNotation] -> [NewNotation] mergeNotations = map (merge . fixFixities) . groupOn notation where fixFixities ns | allEqual (map notaFixity ns) = ns | otherwise = map (\n -> n { notaFixity = defaultFixity }) ns merge :: [NewNotation] -> NewNotation merge [] = __IMPOSSIBLE__ merge ns@(n : _) = n { notaNames = Set.unions $ map notaNames ns } -- * Fixity -- | Associativity. data Associativity = NonAssoc | LeftAssoc | RightAssoc deriving (Eq, Ord, Show, Typeable) -- | Fixity of operators. data Fixity = Fixity { fixityRange :: Range , fixityLevel :: Integer , fixityAssoc :: Associativity } deriving (Typeable, Show) instance Eq Fixity where f1 == f2 = compare f1 f2 == EQ instance Ord Fixity where compare = compare `on` (\f -> (fixityLevel f, fixityAssoc f)) -- For @instance Pretty Fixity@, see Agda.Syntax.Concrete.Pretty -- | The default fixity. Currently defined to be @'NonAssoc' 20@. defaultFixity :: Fixity defaultFixity = Fixity noRange 20 NonAssoc -- | Hack used for @syntax@ facility. noFixity :: Fixity noFixity = Fixity noRange (negate 666) NonAssoc -- Ts,ts,ts, why the number of the beast? Revelation 13, 18 -- -- It's not the number of the beast, it's the negation of the -- number of the beast, which must be a divine number, right? -- -- The divine is not the negation of evil. -- Evil is only the absense of the good and divine. -- * Precendence -- | Precedence is associated with a context. data Precedence = TopCtx | FunctionSpaceDomainCtx | LeftOperandCtx Fixity | RightOperandCtx Fixity | FunctionCtx | ArgumentCtx | InsideOperandCtx | WithFunCtx | WithArgCtx | DotPatternCtx deriving (Show,Typeable) -- | The precedence corresponding to a possibly hidden argument. hiddenArgumentCtx :: Hiding -> Precedence hiddenArgumentCtx NotHidden = ArgumentCtx hiddenArgumentCtx Hidden = TopCtx hiddenArgumentCtx Instance = TopCtx -- | Do we need to bracket an operator application of the given fixity -- in a context with the given precedence. opBrackets :: Fixity -> Precedence -> Bool opBrackets (Fixity _ n1 LeftAssoc) (LeftOperandCtx (Fixity _ n2 LeftAssoc)) | n1 >= n2 = False opBrackets (Fixity _ n1 RightAssoc) (RightOperandCtx (Fixity _ n2 RightAssoc)) | n1 >= n2 = False opBrackets f1 (LeftOperandCtx f2) | fixityLevel f1 > fixityLevel f2 = False opBrackets f1 (RightOperandCtx f2) | fixityLevel f1 > fixityLevel f2 = False opBrackets _ TopCtx = False opBrackets _ FunctionSpaceDomainCtx = False opBrackets _ InsideOperandCtx = False opBrackets _ WithArgCtx = False opBrackets _ WithFunCtx = False opBrackets _ _ = True -- | Does a lambda-like thing (lambda, let or pi) need brackets in the -- given context? A peculiar thing with lambdas is that they don't -- need brackets in certain right operand contexts. However, we insert -- brackets anyway, for the following reasons: -- -- * Clarity. -- -- * Sometimes brackets are needed. Example: @m₁ >>= (λ x → x) >>= m₂@ -- (here @_>>=_@ is left associative). lamBrackets :: Precedence -> Bool lamBrackets TopCtx = False lamBrackets _ = True -- | Does a function application need brackets? appBrackets :: Precedence -> Bool appBrackets ArgumentCtx = True appBrackets DotPatternCtx = True appBrackets _ = False -- | Does a with application need brackets? withAppBrackets :: Precedence -> Bool withAppBrackets TopCtx = False withAppBrackets FunctionSpaceDomainCtx = False withAppBrackets WithFunCtx = False withAppBrackets _ = True -- | Does a function space need brackets? piBrackets :: Precedence -> Bool piBrackets TopCtx = False piBrackets _ = True roundFixBrackets :: Precedence -> Bool roundFixBrackets DotPatternCtx = True roundFixBrackets _ = False instance HasRange Fixity where getRange = fixityRange instance KillRange Fixity where killRange f = f { fixityRange = noRange } instance KillRange Fixity' where killRange (Fixity' f n) = killRange2 Fixity' f n instance KillRange x => KillRange (ThingWithFixity x) where killRange (ThingWithFixity c f) = ThingWithFixity (killRange c) f ------------------------------------------------------------------------ -- * Printing ------------------------------------------------------------------------ -- deriving instance Show Fixity'