Agda-2.3.0: src/full/Agda/Syntax/Concrete/Name.hs
{-# LANGUAGE CPP, DeriveDataTypeable #-}
{-| Names in the concrete syntax are just strings (or lists of strings for
qualified names).
-}
module Agda.Syntax.Concrete.Name where
import Control.Applicative
import Data.List
import Data.Maybe
import Data.Generics (Typeable, Data)
import System.FilePath
import Test.QuickCheck
import Agda.Syntax.Common
import Agda.Syntax.Position
import Agda.Utils.FileName
import Agda.Utils.Pretty
#include "../../undefined.h"
import Agda.Utils.Impossible
{-| A name is a non-empty list of alternating 'Id's and 'Hole's. A normal name
is represented by a singleton list, and operators are represented by a list
with 'Hole's where the arguments should go. For instance: @[Hole,Id "+",Hole]@
is infix addition.
Equality and ordering on @Name@s are defined to ignore range so same names
in different locations are equal.
-}
data Name = Name !Range [NamePart]
| NoName !Range NameId
deriving (Typeable, Data)
data NamePart = Hole | Id String
deriving (Typeable, Data)
-- | @noName_ = 'noName' 'noRange'@
noName_ :: Name
noName_ = noName noRange
-- | @noName r = 'Name' r ['Hole']@
noName :: Range -> Name
noName r = NoName r (NameId 0 0)
isNoName :: Name -> Bool
isNoName (NoName _ _) = True
isNoName (Name _ [Hole]) = True -- TODO: Track down where these come from
isNoName (Name _ []) = True
isNoName _ = False
-- | Is the name an operator?
isOperator :: Name -> Bool
isOperator (NoName {}) = False
isOperator (Name _ ps) = length ps > 1
nameParts :: Name -> [NamePart]
nameParts (Name _ ps) = ps
nameParts (NoName _ _) = [Hole]
nameStringParts :: Name -> [String]
nameStringParts n = [ s | Id s <- nameParts n ]
-- | @qualify A.B x == A.B.x@
qualify :: QName -> Name -> QName
qualify (QName m) x = Qual m (QName x)
qualify (Qual m m') x = Qual m $ qualify m' x
-- | @unqualify A.B.x == x@
--
-- The range is preserved.
unqualify :: QName -> Name
unqualify q = unqualify' q `withRangeOf` q
where
unqualify' (QName x) = x
unqualify' (Qual _ x) = unqualify' x
-- | @qnameParts A.B.x = [A, B, x]@
qnameParts :: QName -> [Name]
qnameParts (Qual x q) = x : qnameParts q
qnameParts (QName x) = [x]
-- Define equality on @Name@ to ignore range so same names in different
-- locations are equal.
--
-- Is there a reason not to do this? -Jeff
--
-- No. But there are tons of reasons to do it. For instance, when using
-- names as keys in maps you really don't want to have to get the range
-- right to be able to do a lookup. -Ulf
instance Eq Name where
Name _ xs == Name _ ys = xs == ys
NoName _ i == NoName _ j = i == j
_ == _ = False
instance Ord Name where
compare (Name _ xs) (Name _ ys) = compare xs ys
compare (NoName _ i) (NoName _ j) = compare i j
compare (NoName {}) (Name {}) = LT
compare (Name {}) (NoName {}) = GT
instance Eq NamePart where
Hole == Hole = True
Id s1 == Id s2 = s1 == s2
_ == _ = False
instance Ord NamePart where
compare Hole Hole = EQ
compare Hole (Id {}) = LT
compare (Id {}) Hole = GT
compare (Id s1) (Id s2) = compare s1 s2
-- | @QName@ is a list of namespaces and the name of the constant.
-- For the moment assumes namespaces are just @Name@s and not
-- explicitly applied modules.
-- Also assumes namespaces are generative by just using derived
-- equality. We will have to define an equality instance to
-- non-generative namespaces (as well as having some sort of
-- lookup table for namespace names).
data QName = Qual Name QName
| QName Name
deriving (Typeable, Data, Eq, Ord)
-- | Top-level module names.
--
-- Invariant: The list must not be empty.
newtype TopLevelModuleName
= TopLevelModuleName { moduleNameParts :: [String] }
deriving (Show, Eq, Ord, Typeable, Data)
-- | Turns a qualified name into a 'TopLevelModuleName'. The qualified
-- name is assumed to represent a top-level module name.
toTopLevelModuleName :: QName -> TopLevelModuleName
toTopLevelModuleName = TopLevelModuleName . map show . qnameParts
-- | Turns a top-level module name into a file name with the given
-- suffix.
moduleNameToFileName :: TopLevelModuleName -> String -> FilePath
moduleNameToFileName (TopLevelModuleName []) ext = __IMPOSSIBLE__
moduleNameToFileName (TopLevelModuleName ms) ext =
joinPath (init ms) </> last ms <.> ext
-- | Finds the current project's \"root\" directory, given a project
-- file and the corresponding top-level module name.
--
-- Example: If the module \"A.B.C\" is located in the file
-- \"/foo/A/B/C.agda\", then the root is \"/foo/\".
--
-- Precondition: The module name must be well-formed.
projectRoot :: AbsolutePath -> TopLevelModuleName -> AbsolutePath
projectRoot file (TopLevelModuleName m) =
mkAbsolute $
foldr (.) id (replicate (length m - 1) takeDirectory) $
takeDirectory $
filePath file
isHole :: NamePart -> Bool
isHole Hole = True
isHole _ = False
isPrefix, isPostfix, isInfix, isNonfix :: Name -> Bool
isPrefix x = not (isHole (head xs)) && isHole (last xs) where xs = nameParts x
isPostfix x = isHole (head xs) && not (isHole (last xs)) where xs = nameParts x
isInfix x = isHole (head xs) && isHole (last xs) where xs = nameParts x
isNonfix x = not (isHole (head xs)) && not (isHole (last xs)) where xs = nameParts x
instance Show Name where
show (Name _ xs) = concatMap show xs
show (NoName _ _) = "_"
instance Show NamePart where
show Hole = "_"
show (Id s) = s
instance Show QName where
show (Qual m x) = show m ++ "." ++ show x
show (QName x) = show x
instance Pretty TopLevelModuleName where
pretty (TopLevelModuleName ms) = text $ intercalate "." ms
instance Arbitrary TopLevelModuleName where
arbitrary = TopLevelModuleName <$> listOf1 (listOf1 $ elements "AB")
instance CoArbitrary TopLevelModuleName where
coarbitrary (TopLevelModuleName m) = coarbitrary m
instance HasRange Name where
getRange (Name r ps) = r
getRange (NoName r _) = r
instance HasRange QName where
getRange (QName x) = getRange x
getRange (Qual n x) = fuseRange n x
instance SetRange Name where
setRange r (Name _ ps) = Name r ps
setRange r (NoName _ i) = NoName r i
instance KillRange QName where
killRange (QName x) = QName $ killRange x
killRange (Qual n x) = killRange n `Qual` killRange x
instance KillRange Name where
killRange (Name r ps) = Name (killRange r) ps
killRange (NoName r i) = NoName (killRange r) i