Skip to content

Commit

Permalink
Add basic Rust target (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
tirr-c committed Feb 10, 2018
1 parent f449697 commit 268dab5
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 0 deletions.
3 changes: 3 additions & 0 deletions examples/package.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ version = "0.3.0"
[targets.python]
name = "nirum-examples"
minimum_runtime = "0.3.9"

[targets.rust]
name = "nirum-examples"
1 change: 1 addition & 0 deletions src/Nirum/Targets.hs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import Nirum.Targets.List (targetProxyMapQ)
-- docs/target/x.md file too.
import Nirum.Targets.Docs ()
import Nirum.Targets.Python ()
import Nirum.Targets.Rust ()

data BuildError = TargetNameError TargetName
| CompileError (M.Map FilePath Text)
Expand Down
110 changes: 110 additions & 0 deletions src/Nirum/Targets/Rust.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
{-# LANGUAGE DeriveDataTypeable, ExtendedDefaultRules, OverloadedLists,
QuasiQuotes, TypeFamilies, TypeSynonymInstances,
MultiParamTypeClasses #-}
module Nirum.Targets.Rust ( Rust
, Code
, CompileError
) where

import qualified Data.Map.Strict as M
import qualified Data.SemVer as SV
import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8)
import Data.Text.Lazy (toStrict)
import Data.Typeable (Typeable)

import GHC.Exts (IsList (toList))

import System.FilePath (joinPath, replaceExtension)

import Text.Blaze.Renderer.Text
import Text.Heterocephalus (compileText)

import qualified Nirum.Constructs.Identifier as I
import Nirum.Constructs.Module
import Nirum.Constructs.ModulePath (ModulePath)
import Nirum.Constructs.Name
import Nirum.Constructs.TypeDeclaration
import Nirum.Package.Metadata
import qualified Nirum.Package.ModuleSet as MS
import Nirum.Targets.Rust.Keyword
import Nirum.TypeInstance.BoundModule

data Rust = Rust { packageName :: T.Text
}
deriving (Eq, Ord, Show, Typeable)

type Code = T.Text
type CompileError' = ()

genCargoToml :: Package Rust -> Code
genCargoToml Package { metadata = Metadata { version = version'
, target = Rust { packageName = name' }
}
} =
toStrict $
renderMarkup [compileText|[package]
name = "#{ name' }"
version = "#{ SV.toLazyText version' }"
|]

compileModule :: BoundModule Rust -> Code
compileModule m =
toStrict $
renderMarkup [compileText|%{ forall (moduleName, members') <- enums }
pub enum #{ toRustIdentifier I.toPascalCaseText $ facialName moduleName } {
%{ forall EnumMember memberName _ <- members' }
#{ toRustIdentifier I.toPascalCaseText $ facialName memberName },
%{ endforall }
}
%{ endforall }
|]
where
moduleTypes :: [TypeDeclaration]
moduleTypes = toList $ boundTypes m
enums :: [(Name, [EnumMember])]
enums =
[ (moduleName, toList members')
| TypeDeclaration { typename = moduleName
, type' = EnumType { members = members' }
} <- moduleTypes
]

compilePackage' :: Package Rust
-> M.Map FilePath (Either CompileError' Code)
compilePackage' package =
M.fromList $
[ ( toFilename mp
, Right $ compileModule m
)
| (mp, _) <- modules'
, Just m <- [resolveBoundModule mp package]
] ++
[ ("Cargo.toml", Right $ genCargoToml package)
, (joinPath ["src", "lib.rs"], Right "")
]
where
convertModulePath :: ModulePath -> [FilePath]
convertModulePath mp =
"src" :
[ T.unpack (toRustIdentifier I.toSnakeCaseText i)
| i <- toList mp
]
toFilename :: ModulePath -> FilePath
toFilename mp =
replaceExtension (joinPath $ convertModulePath mp) "rs"
modules' :: [(ModulePath, Module)]
modules' = MS.toAscList $ modules package

instance Target Rust where
type CompileResult Rust = Code
type CompileError Rust = CompileError'

targetName _ = "rust"
parseTarget table = do
name' <- stringField "name" table
return Rust { packageName = name'
}
compilePackage = compilePackage'
showCompileError _ _ = ""
toByteString _ = encodeUtf8
48 changes: 48 additions & 0 deletions src/Nirum/Targets/Rust/Keyword.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{-# LANGUAGE ExtendedDefaultRules, OverloadedLists, TypeSynonymInstances #-}
module Nirum.Targets.Rust.Keyword ( isPossibleKeyword
, toRustIdentifier
) where

import qualified Data.Set as S
import qualified Data.Text as T

import qualified Nirum.Constructs.Identifier as I

-- | The set of Rust keywords.
-- See also: https://doc.rust-lang.org/reference/keywords.html
strictKeywords :: S.Set T.Text
strictKeywords =
[ "as", "box", "break", "const", "continue"
, "crate", "else", "enum", "extern", "false"
, "fn", "for", "if", "impl", "in", "let"
, "loop", "match", "mod", "move", "mut", "pub"
, "ref", "return", "self", "Self", "static"
, "struct", "super", "trait", "true", "type"
, "unsafe", "use", "where", "while"
]
weakKeywords :: S.Set T.Text
weakKeywords =
[ "catch", "default", "union", "'static" ]
reservedKeywords :: S.Set T.Text
reservedKeywords =
[ "abstract", "alignof", "become", "do"
, "final", "macro", "offsetof", "override"
, "priv", "proc", "pure", "sizeof", "typeof"
, "unsized", "virtual", "yield"
]

isPossibleKeyword :: T.Text -> Bool
isPossibleKeyword name' =
(findMember strictKeywords) ||
(findMember weakKeywords) ||
(findMember reservedKeywords)
where
findMember :: S.Set T.Text -> Bool
findMember = S.member name'

toRustIdentifier :: (I.Identifier -> T.Text) -> I.Identifier -> T.Text
toRustIdentifier convertIdent identifier =
if isPossibleKeyword attrName then attrName `T.snoc` '_' else attrName
where
attrName :: T.Text
attrName = convertIdent identifier

0 comments on commit 268dab5

Please sign in to comment.