Skip to content

Commit

Permalink
Merge pull request #258 from admire93/import-aliasing
Browse files Browse the repository at this point in the history
Added aliased import
  • Loading branch information
kanghyojun committed Apr 30, 2018
2 parents 5630d29 + 6be588a commit 4fb4391
Show file tree
Hide file tree
Showing 27 changed files with 327 additions and 132 deletions.
11 changes: 11 additions & 0 deletions CHANGES.md
Expand Up @@ -118,6 +118,15 @@ To be released.
- Fixed a compiler bug that an error message on name duplicates had referred
to a wrong line/column number. [[#255]]

- Added aliased import. It's handy to avoid a name shadowing.
[[#217], [#258]]

~~~~~~~~ nirum
import iso (country as iso-country);
import types (country);
~~~~~~~~


### Docs target

- A new required configuration `targets.docs.title` was added.
Expand Down Expand Up @@ -175,12 +184,14 @@ To be released.
March 2018.

[#13]: https://github.com/spoqa/nirum/issues/13
[#217]: https://github.com/spoqa/nirum/issues/217
[#220]: https://github.com/spoqa/nirum/issues/220
[#227]: https://github.com/spoqa/nirum/pull/227
[#253]: https://github.com/spoqa/nirum/pull/253
[#254]: https://github.com/spoqa/nirum/pull/254
[#255]: https://github.com/spoqa/nirum/pull/255
[#257]: https://github.com/spoqa/nirum/pull/257
[#258]: https://github.com/spoqa/nirum/pull/258
[#259]: https://github.com/spoqa/nirum/pull/259
[entry points]: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points
[python2-numbers-integral]: https://docs.python.org/2/library/numbers.html#numbers.Integral
Expand Down
6 changes: 6 additions & 0 deletions examples/address.nrm
@@ -1,4 +1,5 @@
import countries (country);
import geo (geo as geo-point);

record address (
float32 lat,
Expand All @@ -8,3 +9,8 @@ record address (
country country,
text postal-code,
);


record pin (
geo-point geo,
);
2 changes: 1 addition & 1 deletion examples/countries.nrm
Expand Up @@ -6,7 +6,7 @@ enum country
| ax # Åland Islands
| al # Albania
| dz # Algeria
| as # American Samoa
| `as` # American Samoa
| ad # Andorra
| ao # Angola
| ai # Anguilla
Expand Down
4 changes: 4 additions & 0 deletions examples/geo.nrm
@@ -0,0 +1,4 @@
record geo (
float32 latitude,
float32 longitude,
);
1 change: 1 addition & 0 deletions src/Nirum/Constructs/Identifier.hs
Expand Up @@ -62,6 +62,7 @@ reservedKeywords = [ "enum"
, "unboxed"
, "union"
, "default"
, "as"
]

identifierRule :: P.Parsec Void T.Text Identifier
Expand Down
14 changes: 11 additions & 3 deletions src/Nirum/Constructs/Module.hs
Expand Up @@ -57,10 +57,18 @@ instance Construct Module where
where
typeList :: [TypeDeclaration]
typeList = DS.toList types'
importIdentifiersToCode :: (Identifier, Identifier) -> T.Text
importIdentifiersToCode (i, s) = if i == s
then toCode i
else T.concat [ toCode s
, " as "
, toCode i
]
importCodes :: [T.Text]
importCodes =
[ T.concat [ "import ", toCode p, " ("
, T.intercalate ", " $ map toCode $ S.toAscList i
, T.intercalate ", " $
map importIdentifiersToCode $ S.toAscList i
, ");"
]
| (p, i) <- M.toAscList (imports m)
Expand All @@ -76,9 +84,9 @@ instance Construct Module where
instance Documented Module where
docs (Module _ docs') = docs'

imports :: Module -> M.Map ModulePath (S.Set Identifier)
imports :: Module -> M.Map ModulePath (S.Set (Identifier, Identifier))
imports (Module decls _) =
M.fromListWith S.union [(p, [i]) | Import p i _ <- DS.toList decls]
M.fromListWith S.union [(p, [(i, s)]) | Import p i s _ <- DS.toList decls]

coreModulePath :: ModulePath
coreModulePath = ["core"]
Expand Down
19 changes: 12 additions & 7 deletions src/Nirum/Constructs/TypeDeclaration.hs
Expand Up @@ -49,6 +49,7 @@ module Nirum.Constructs.TypeDeclaration ( EnumMember (EnumMember)
, service
, serviceAnnotations
, serviceName
, sourceName
, type'
, typeAnnotations
, typename
Expand Down Expand Up @@ -191,6 +192,7 @@ data TypeDeclaration
}
| Import { modulePath :: ModulePath
, importName :: Identifier
, sourceName :: Identifier
, importAnnotations :: AnnotationSet
}
deriving (Eq, Ord, Show)
Expand Down Expand Up @@ -285,13 +287,16 @@ instance Construct TypeDeclaration where
methodsText = T.intercalate "\n" $ map toCode methods'
docs' :: Maybe Docs
docs' = A.lookupDocs annotations'
toCode (Import path ident aSet) = T.concat [ "import "
, toCode path
, " ("
, toCode aSet
, toCode ident
, ");\n"
]
toCode (Import path iName sName aSet) = T.concat
[ "import "
, toCode path
, " ("
, toCode aSet
, if iName == sName
then toCode iName
else T.concat [ toCode sName, " as ", toCode iName ]
, ");\n"
]

instance Documented TypeDeclaration

Expand Down
8 changes: 4 additions & 4 deletions src/Nirum/Package/ModuleSet.hs
Expand Up @@ -90,12 +90,12 @@ detectMissingImports moduleSet =
Nothing -> [MissingModulePathError path path']
Just (Module decls _) ->
[ e
| i <- S.toList idents
, e <- case DS.lookup i decls of
| (_, s) <- S.toList idents
, e <- case DS.lookup s decls of
Just TypeDeclaration {} -> []
Just ServiceDeclaration {} -> []
Just Import {} -> [MissingImportError path path' i]
Nothing -> [MissingImportError path path' i]
Just Import {} -> [MissingImportError path path' s]
Nothing -> [MissingImportError path path' s]
]
]

Expand Down
44 changes: 33 additions & 11 deletions src/Nirum/Parser.hs
Expand Up @@ -11,6 +11,7 @@ module Nirum.Parser ( Parser
, handleNameDuplication
, handleNameDuplicationError
, identifier
, importName
, imports
, listModifier
, mapModifier
Expand All @@ -32,7 +33,7 @@ module Nirum.Parser ( Parser
, unionTypeDeclaration
) where

import Control.Monad (void, when)
import Control.Monad (unless, void, when)
import Data.Void
import qualified System.IO as SIO

Expand Down Expand Up @@ -627,28 +628,49 @@ modulePath = do
f Nothing i = Just $ ModuleName i
f (Just p) i = Just $ ModulePath p i

importName :: Parser (Identifier, A.AnnotationSet)
importName = do
importName :: [Identifier]
-> Parser (Identifier, Identifier, A.AnnotationSet)
importName forwardNames = do
aSet <- annotationSet <?> "import annotations"
spaces
iName <- identifier <?> "name to import"
return (iName, aSet)
hasAlias <- optional $ try $ do
spaces
string' "as"
aName <- case hasAlias of
Just _ -> do
spaces
uniqueIdentifier forwardNames "alias name to import"
Nothing ->
return iName
return (aName, iName, aSet)

imports :: Parser [TypeDeclaration]
imports = do
imports :: [Identifier] -> Parser [TypeDeclaration]
imports forwardNames = do
string' "import" <?> "import keyword"
spaces
path <- modulePath <?> "module path"
spaces
char '('
spaces
idents <- (importName >>= \ i -> spaces >> return i)
`sepEndBy1` (char ',' >> spaces)
<?> "names to import"
idents <- many' [] $ \ importNames' -> do
notFollowedBy $ choice [char ')', char ',' >> spaces >> char ')']
let forwardNames' = [i | (i, _, _) <- importNames'] ++ forwardNames
unless (L.null importNames') $ do
string' ","
spaces
n <- importName forwardNames'
spaces
return n
when (L.null idents) $ fail "parentheses cannot be empty"
void $ optional $ string' ","
spaces
char ')'
spaces
char ';'
return [Import path ident aSet | (ident, aSet) <- idents]
return [ Import path imp source aSet
| (imp, source, aSet) <- idents
]


module' :: Parser Module
Expand All @@ -660,7 +682,7 @@ module' = do
return d
spaces
importLists <- many $ do
importList <- imports
importList <- imports []
spaces
return importList
let imports' = [i | l <- importLists, i <- l]
Expand Down
12 changes: 6 additions & 6 deletions src/Nirum/Targets/Python/Serializers.hs
Expand Up @@ -39,22 +39,22 @@ compileSerializer mod' (TypeIdentifier typeId) pythonVar =
case lookupType typeId mod' of
Missing -> "None" -- must never happen
Local (Alias t) -> compileSerializer mod' t pythonVar
Imported modulePath' (Alias t) ->
Imported modulePath' _ (Alias t) ->
case resolveBoundModule modulePath' (boundPackage mod') of
Nothing -> "None" -- must never happen
Just foundMod -> compileSerializer foundMod t pythonVar
Local PrimitiveType { primitiveTypeIdentifier = p } ->
compilePrimitiveTypeSerializer p pythonVar
Imported _ PrimitiveType { primitiveTypeIdentifier = p } ->
Imported _ _ PrimitiveType { primitiveTypeIdentifier = p } ->
compilePrimitiveTypeSerializer p pythonVar
Local EnumType {} -> serializerCall
Imported _ EnumType {} -> serializerCall
Imported _ _ EnumType {} -> serializerCall
Local RecordType {} -> serializerCall
Imported _ RecordType {} -> serializerCall
Imported _ _ RecordType {} -> serializerCall
Local UnboxedType {} -> serializerCall
Imported _ UnboxedType {} -> serializerCall
Imported _ _ UnboxedType {} -> serializerCall
Local UnionType {} -> serializerCall
Imported _ UnionType {} -> serializerCall
Imported _ _ UnionType {} -> serializerCall
where
serializerCall :: Code
serializerCall = [qq|$pythonVar.__nirum_serialize__()|]
Expand Down
9 changes: 6 additions & 3 deletions src/Nirum/Targets/Python/TypeExpression.hs
Expand Up @@ -22,9 +22,12 @@ compileTypeExpression :: BoundModule Python
compileTypeExpression mod' (Just (TypeIdentifier i)) =
case lookupType i mod' of
Missing -> fail $ "undefined identifier: " ++ toString i
Imported _ (PrimitiveType p _) -> compilePrimitiveType p
Imported m _ -> do
insertThirdPartyImports [(toImportPath target' m, [toClassName i])]
Imported _ _ (PrimitiveType p _) -> compilePrimitiveType p
Imported m in' _ -> do
insertThirdPartyImportsA [ ( toImportPath target' m
, [(toClassName i, toClassName in')]
)
]
return $ toClassName i
Local _ -> return $ toClassName i
where
Expand Down
4 changes: 2 additions & 2 deletions src/Nirum/Targets/Python/Validators.hs
Expand Up @@ -65,13 +65,13 @@ compileValidator mod' (TypeIdentifier typeId) pythonVar =
case lookupType typeId mod' of
Missing -> return $ Validator "False" [] -- must never happen
Local (Alias typeExpr') -> compileValidator mod' typeExpr' pythonVar
Imported modulePath' (Alias typeExpr') ->
Imported modulePath' _ (Alias typeExpr') ->
case resolveBoundModule modulePath' (boundPackage mod') of
Nothing -> return $ Validator "False" [] -- must never happen
Just foundMod -> compileValidator foundMod typeExpr' pythonVar
Local PrimitiveType { primitiveTypeIdentifier = pId } ->
compilePrimitiveTypeValidator pId pythonVar
Imported _ PrimitiveType { primitiveTypeIdentifier = pId } ->
Imported _ _ PrimitiveType { primitiveTypeIdentifier = pId } ->
compilePrimitiveTypeValidator pId pythonVar
_ ->
compileInstanceValidator mod' typeId pythonVar
Expand Down
19 changes: 11 additions & 8 deletions src/Nirum/TypeInstance/BoundModule.hs
Expand Up @@ -47,25 +47,28 @@ boundTypes = findInBoundModule types DS.empty

data TypeLookup = Missing
| Local Type
| Imported ModulePath Type
| Imported ModulePath Identifier Type
deriving (Eq, Ord, Show)

lookupType :: Target t => Identifier -> BoundModule t -> TypeLookup
lookupType identifier boundModule =
case DS.lookup identifier (boundTypes boundModule) of
Nothing -> toType coreModulePath
(DS.lookup identifier $ types coreModule)
Nothing ->
toType
coreModulePath
identifier
(DS.lookup identifier $ types coreModule)
Just TypeDeclaration { type' = t } -> Local t
Just (Import path' _ _) ->
Just (Import path' _ s _) ->
case resolveModule path' (boundPackage boundModule) of
Nothing -> Missing
Just (Module decls _) ->
toType path' (DS.lookup identifier decls)
toType path' s (DS.lookup s decls)
Just ServiceDeclaration {} -> Missing
where
toType :: ModulePath -> Maybe TypeDeclaration -> TypeLookup
toType mp (Just TypeDeclaration { type' = t }) = Imported mp t
toType _ _ = Missing
toType :: ModulePath -> Identifier -> Maybe TypeDeclaration -> TypeLookup
toType mp i (Just TypeDeclaration { type' = t }) = Imported mp i t
toType _ _ _ = Missing

instance Target t => Documented (BoundModule t) where
docs = findInBoundModule Nirum.Constructs.Module.docs Nothing
4 changes: 2 additions & 2 deletions test/Nirum/CodeBuilderSpec.hs
Expand Up @@ -79,9 +79,9 @@ spec = do
let run' = fst . runBuilder package ["fruits"] ()
let core = ModuleName "core"
run' (lookupType "text") `shouldBe`
Imported core (TD.PrimitiveType TD.Text TD.String)
Imported core "text" (TD.PrimitiveType TD.Text TD.String)
run' (lookupType "int32") `shouldBe`
Imported core (TD.PrimitiveType TD.Int32 TD.Number)
Imported core "int32" (TD.PrimitiveType TD.Int32 TD.Number)


data DummyTarget = DummyTarget deriving (Eq, Ord, Show)
Expand Down
27 changes: 19 additions & 8 deletions test/Nirum/Constructs/ModuleSpec.hs
Expand Up @@ -21,20 +21,27 @@ spec =
pathT = TypeDeclaration "path" (Alias "text") (singleton docsAnno)
offsetT =
TypeDeclaration "offset" (UnboxedType "float64") empty
decls = [ Import ["foo", "bar"] "baz" empty
, Import ["foo", "bar"] "qux" empty
, Import ["zzz"] "qqq" empty
, Import ["zzz"] "ppp" empty
, Import ["xyz"] "asdf" empty
decls = [ Import ["foo", "bar"] "baz" "baz" empty
, Import ["foo", "bar"] "qux" "qux" empty
, Import ["zzz"] "qqq" "qqq" empty
, Import ["zzz"] "ppp" "ppp" empty
, Import ["xyz"] "asdf" "asdf" empty
, pathT
, offsetT
] :: DeclarationSet TypeDeclaration
decls2 = [ Import ["foo", "bar"] "qux" "baz" empty
] :: DeclarationSet TypeDeclaration
mod1 = Module decls Nothing
mod2 = Module decls $ Just "module level docs...\nblahblah"
mod3 = Module decls2 Nothing
specify "imports" $ do
imports mod1 `shouldBe` [ (["foo", "bar"], ["baz", "qux"])
, (["xyz"], ["asdf"])
, (["zzz"], ["qqq", "ppp"])
imports mod1 `shouldBe` [ ( ["foo", "bar"]
, [("baz", "baz"), ("qux", "qux")]
)
, (["xyz"], [("asdf", "asdf")])
, ( ["zzz"]
, [("qqq", "qqq"), ("ppp", "ppp")]
)
]
imports mod2 `shouldBe` imports mod1
specify "toCode" $ do
Expand All @@ -57,4 +64,8 @@ type path = text;
# path string

unboxed offset (float64);
|]
toCode mod3 `shouldBe` [q|import foo.bar (baz as qux);


|]

0 comments on commit 4fb4391

Please sign in to comment.