Skip to content

Commit 268dab5

Browse files
committed
Add basic Rust target (WIP)
1 parent f449697 commit 268dab5

File tree

4 files changed

+162
-0
lines changed

4 files changed

+162
-0
lines changed

examples/package.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ version = "0.3.0"
33
[targets.python]
44
name = "nirum-examples"
55
minimum_runtime = "0.3.9"
6+
7+
[targets.rust]
8+
name = "nirum-examples"

src/Nirum/Targets.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import Nirum.Targets.List (targetProxyMapQ)
4646
-- docs/target/x.md file too.
4747
import Nirum.Targets.Docs ()
4848
import Nirum.Targets.Python ()
49+
import Nirum.Targets.Rust ()
4950

5051
data BuildError = TargetNameError TargetName
5152
| CompileError (M.Map FilePath Text)

src/Nirum/Targets/Rust.hs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
{-# LANGUAGE DeriveDataTypeable, ExtendedDefaultRules, OverloadedLists,
2+
QuasiQuotes, TypeFamilies, TypeSynonymInstances,
3+
MultiParamTypeClasses #-}
4+
module Nirum.Targets.Rust ( Rust
5+
, Code
6+
, CompileError
7+
) where
8+
9+
import qualified Data.Map.Strict as M
10+
import qualified Data.SemVer as SV
11+
import qualified Data.Text as T
12+
import Data.Text.Encoding (encodeUtf8)
13+
import Data.Text.Lazy (toStrict)
14+
import Data.Typeable (Typeable)
15+
16+
import GHC.Exts (IsList (toList))
17+
18+
import System.FilePath (joinPath, replaceExtension)
19+
20+
import Text.Blaze.Renderer.Text
21+
import Text.Heterocephalus (compileText)
22+
23+
import qualified Nirum.Constructs.Identifier as I
24+
import Nirum.Constructs.Module
25+
import Nirum.Constructs.ModulePath (ModulePath)
26+
import Nirum.Constructs.Name
27+
import Nirum.Constructs.TypeDeclaration
28+
import Nirum.Package.Metadata
29+
import qualified Nirum.Package.ModuleSet as MS
30+
import Nirum.Targets.Rust.Keyword
31+
import Nirum.TypeInstance.BoundModule
32+
33+
data Rust = Rust { packageName :: T.Text
34+
}
35+
deriving (Eq, Ord, Show, Typeable)
36+
37+
type Code = T.Text
38+
type CompileError' = ()
39+
40+
genCargoToml :: Package Rust -> Code
41+
genCargoToml Package { metadata = Metadata { version = version'
42+
, target = Rust { packageName = name' }
43+
}
44+
} =
45+
toStrict $
46+
renderMarkup [compileText|[package]
47+
name = "#{ name' }"
48+
version = "#{ SV.toLazyText version' }"
49+
|]
50+
51+
compileModule :: BoundModule Rust -> Code
52+
compileModule m =
53+
toStrict $
54+
renderMarkup [compileText|%{ forall (moduleName, members') <- enums }
55+
pub enum #{ toRustIdentifier I.toPascalCaseText $ facialName moduleName } {
56+
%{ forall EnumMember memberName _ <- members' }
57+
#{ toRustIdentifier I.toPascalCaseText $ facialName memberName },
58+
%{ endforall }
59+
}
60+
%{ endforall }
61+
|]
62+
where
63+
moduleTypes :: [TypeDeclaration]
64+
moduleTypes = toList $ boundTypes m
65+
enums :: [(Name, [EnumMember])]
66+
enums =
67+
[ (moduleName, toList members')
68+
| TypeDeclaration { typename = moduleName
69+
, type' = EnumType { members = members' }
70+
} <- moduleTypes
71+
]
72+
73+
compilePackage' :: Package Rust
74+
-> M.Map FilePath (Either CompileError' Code)
75+
compilePackage' package =
76+
M.fromList $
77+
[ ( toFilename mp
78+
, Right $ compileModule m
79+
)
80+
| (mp, _) <- modules'
81+
, Just m <- [resolveBoundModule mp package]
82+
] ++
83+
[ ("Cargo.toml", Right $ genCargoToml package)
84+
, (joinPath ["src", "lib.rs"], Right "")
85+
]
86+
where
87+
convertModulePath :: ModulePath -> [FilePath]
88+
convertModulePath mp =
89+
"src" :
90+
[ T.unpack (toRustIdentifier I.toSnakeCaseText i)
91+
| i <- toList mp
92+
]
93+
toFilename :: ModulePath -> FilePath
94+
toFilename mp =
95+
replaceExtension (joinPath $ convertModulePath mp) "rs"
96+
modules' :: [(ModulePath, Module)]
97+
modules' = MS.toAscList $ modules package
98+
99+
instance Target Rust where
100+
type CompileResult Rust = Code
101+
type CompileError Rust = CompileError'
102+
103+
targetName _ = "rust"
104+
parseTarget table = do
105+
name' <- stringField "name" table
106+
return Rust { packageName = name'
107+
}
108+
compilePackage = compilePackage'
109+
showCompileError _ _ = ""
110+
toByteString _ = encodeUtf8

src/Nirum/Targets/Rust/Keyword.hs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{-# LANGUAGE ExtendedDefaultRules, OverloadedLists, TypeSynonymInstances #-}
2+
module Nirum.Targets.Rust.Keyword ( isPossibleKeyword
3+
, toRustIdentifier
4+
) where
5+
6+
import qualified Data.Set as S
7+
import qualified Data.Text as T
8+
9+
import qualified Nirum.Constructs.Identifier as I
10+
11+
-- | The set of Rust keywords.
12+
-- See also: https://doc.rust-lang.org/reference/keywords.html
13+
strictKeywords :: S.Set T.Text
14+
strictKeywords =
15+
[ "as", "box", "break", "const", "continue"
16+
, "crate", "else", "enum", "extern", "false"
17+
, "fn", "for", "if", "impl", "in", "let"
18+
, "loop", "match", "mod", "move", "mut", "pub"
19+
, "ref", "return", "self", "Self", "static"
20+
, "struct", "super", "trait", "true", "type"
21+
, "unsafe", "use", "where", "while"
22+
]
23+
weakKeywords :: S.Set T.Text
24+
weakKeywords =
25+
[ "catch", "default", "union", "'static" ]
26+
reservedKeywords :: S.Set T.Text
27+
reservedKeywords =
28+
[ "abstract", "alignof", "become", "do"
29+
, "final", "macro", "offsetof", "override"
30+
, "priv", "proc", "pure", "sizeof", "typeof"
31+
, "unsized", "virtual", "yield"
32+
]
33+
34+
isPossibleKeyword :: T.Text -> Bool
35+
isPossibleKeyword name' =
36+
(findMember strictKeywords) ||
37+
(findMember weakKeywords) ||
38+
(findMember reservedKeywords)
39+
where
40+
findMember :: S.Set T.Text -> Bool
41+
findMember = S.member name'
42+
43+
toRustIdentifier :: (I.Identifier -> T.Text) -> I.Identifier -> T.Text
44+
toRustIdentifier convertIdent identifier =
45+
if isPossibleKeyword attrName then attrName `T.snoc` '_' else attrName
46+
where
47+
attrName :: T.Text
48+
attrName = convertIdent identifier

0 commit comments

Comments
 (0)