-
Notifications
You must be signed in to change notification settings - Fork 108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sumtype enums #151
Sumtype enums #151
Changes from 5 commits
c020f2d
8205fb2
84a7a0d
62787e4
9454bdd
339bec4
bc7bdb4
3dd6f6e
009d8a2
145a390
1c1a18e
1e0f059
68d0c76
251e93d
f1fe93d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -108,8 +108,8 @@ generateModule modName imports syntaxType modifyImport definitions importedEnv | |
[ "Prelude", "Data.Int", "Data.Word" | ||
, "Data.ProtoLens", "Data.ProtoLens.Message.Enum" | ||
, "Lens.Family2", "Lens.Family2.Unchecked", "Data.Default.Class" | ||
, "Data.Text", "Data.Map" , "Data.ByteString" | ||
, "Lens.Labels" | ||
, "Data.Text", "Data.Map", "Data.ByteString" | ||
, "Lens.Labels", "Text.Read" | ||
] | ||
++ map importSimple imports | ||
env = Map.union (unqualifyEnv definitions) importedEnv | ||
|
@@ -259,80 +259,185 @@ generateEnumExports syntaxType e = [exportAll n, exportWith n aliases] | |
|
||
generateEnumDecls :: SyntaxType -> EnumInfo Name -> [Decl] | ||
generateEnumDecls Proto3 info = | ||
-- newtype Foo = Foo Int32 | ||
[ newtypeDecl dataName "Data.Int.Int32" | ||
$ deriving' ["Prelude.Eq", "Prelude.Ord", "Prelude.Enum", "Prelude.Bounded"] | ||
|
||
-- instance Show Foo where | ||
-- showsPrec _ Value0 = "Value0" -- the Haskell name | ||
-- showsPrec p (Foo k) = showParen (p > 10) | ||
-- $ showString "toEnum " . shows k | ||
, instDecl [] ("Prelude.Show" `ihApp` [dataType]) | ||
[ [ match "showsPrec" [pWildCard, pApp (unQual n) []] | ||
$ "Prelude.showString" @@ stringExp (prettyPrint n) | ||
| n <- map enumValueName $ enumValues info | ||
] | ||
++ | ||
[ match "showsPrec" ["p", pApp (unQual dataName) ["k"]] | ||
$ "Prelude.showParen" @@ ("Prelude.>" @@ "p" @@ litInt 10) | ||
@@ ("Prelude.." @@ ("Prelude.showString" | ||
@@ stringExp "toEnum ") | ||
@@ ("Prelude.shows" @@ "k")) | ||
] | ||
] | ||
-- data FooEnum = Enum1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Formatting nit in this comment:
|
||
-- | Enum2 | ||
-- | FooEnum'Unrecognized !FooEnum'UnrecognizedValue | ||
-- deriving (Prelude.Show, Prelude.Eq, Prelude.Ord, Prelude.Read) | ||
[ dataDecl dataName | ||
( (flip conDecl [] <$> constructorNames) | ||
++ [conDecl unrecognizedName [unrecognizedValueType]] | ||
) | ||
$ deriving' ["Prelude.Show", "Prelude.Eq", "Prelude.Ord, Prelude.Read"] | ||
|
||
-- instance MessageEnum Foo where | ||
-- maybeToEnum k = Just $ toEnum k | ||
-- showEnum (Foo 0) = "Value0" -- the proto name | ||
-- showEnum (Foo k) = show k | ||
-- readEnum "Value0" = Just (Foo 0) | ||
-- readEnum _ = Nothing | ||
-- newtype FooEnum'UnrecognizedValue = FooEnum'UnrecognizedValue Data.Int.Int32 | ||
-- deriving (Prelude.Eq, Prelude.Ord, Prelude.Show, Prelude.Read) | ||
, newtypeDecl unrecognizedValueName | ||
"Data.Int.Int32" | ||
$ deriving' ["Prelude.Eq", "Prelude.Ord", "Prelude.Show, Prelude.Read"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we remove the (Also because we'd like Up to you. |
||
|
||
-- instance Data.ProtoLens.MessageEnum FooEnum where | ||
-- maybeToEnum 0 = Prelude.Just Enum1 | ||
-- maybeToEnum 3 = Prelude.Just Enum2 | ||
-- maybeToEnum k | ||
-- = Prelude.Just | ||
-- (FooEnum'Unrecognized | ||
-- (FooEnum'UnrecognizedValue (Prelude.fromIntegral k))) | ||
-- showEnum (FooEnum'Unrecognized (FooEnum'UnrecognizedValue k)) | ||
-- = Prelude.show k | ||
-- showEnum k = Prelude.show k | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this comment needs to be updated since you're not calling |
||
-- readEnum "Enum2a" = Prelude.Just Enum2a -- alias | ||
-- readEnum k = Text.Read.readMaybe k | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect this doesn't do what you think it does, for two reasons:
You probably want instead something like Also please add tests for the above cases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I see, I think I misread your code earlier. Taking another look, we actually can't use the
we want I think we should just take the string representations (not the ints like I suggested earlier), as in the code for proto2:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds good, but that still leaves us with no ability to serialize/deserialize unrecognized enum's. I'm assuming There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, good point, we do need something sensible for
Since in proto text format, it's fine to refer to an enum by its numeric value rather than the case name. |
||
, instDecl [] ("Data.ProtoLens.MessageEnum" `ihApp` [dataType]) | ||
[ [match "maybeToEnum" ["k"] | ||
$ "Prelude.Just" @@ ("Prelude.toEnum" @@ "k")] | ||
, [ match "showEnum" [pVar n] $ stringExp pn | ||
| v <- enumValues info | ||
, isNothing (enumAliasOf v) | ||
, let n = enumValueName v | ||
, let pn = T.unpack $ enumValueDescriptor v ^. name | ||
[ [ match "maybeToEnum" [pLitInt k] $ "Prelude.Just" @@ con (unQual c) | ||
| (c, k) <- constructorNumbers | ||
] | ||
, [ match "showEnum" [pApp (unQual dataName) ["k"]] | ||
$ "Prelude.show" @@ "k" | ||
++ | ||
[match "maybeToEnum" ["k"] | ||
$ "Prelude.Just" @@ | ||
(con (unQual unrecognizedName) | ||
@@ (con (unQual unrecognizedValueName) | ||
@@ ("Prelude.fromIntegral" @@ "k") | ||
) | ||
) | ||
] | ||
|
||
, [match "showEnum" [pApp (unQual unrecognizedName) | ||
[pApp (unQual unrecognizedValueName) [pVar "k"]] | ||
] | ||
$ "Prelude.show" @@ "k" | ||
] ++ | ||
[match "showEnum" ["k"] $ "Prelude.show" @@ "k"] | ||
, [ match "readEnum" [stringPat pn] | ||
$ "Prelude.Just" @@ con (unQual n) | ||
| v <- enumValues info | ||
| v <- filter (not . null . enumAliasOf) $ enumValues info | ||
, let n = enumValueName v | ||
, let pn = T.unpack $ enumValueDescriptor v ^. name | ||
] ++ | ||
[match "readEnum" ["k"] $ "Text.Read.readMaybe" @@ "k"] | ||
] | ||
|
||
-- instance Bounded Foo where | ||
-- minBound = Foo1 | ||
-- maxBound = FooN | ||
, instDecl [] ("Prelude.Bounded" `ihApp` [dataType]) | ||
[[ match "minBound" [] $ con $ unQual minBoundName | ||
, match "maxBound" [] $ con $ unQual maxBoundName | ||
]] | ||
|
||
-- instance Enum Foo where | ||
-- toEnum k = maybe (error ("Foo.toEnum: unknown argument for enum Foo: " | ||
-- ++ show k)) | ||
-- id (maybeToEnum k) | ||
-- fromEnum Foo1 = 1 | ||
-- fromEnum Foo2 = 2 | ||
-- .. | ||
-- succ FooN = error "Foo.succ: bad argument FooN." | ||
-- succ Foo1 = Foo2 | ||
-- succ Foo2 = Foo3 | ||
-- .. | ||
-- pred Foo1 = error "Foo.succ: bad argument Foo1." | ||
-- pred Foo2 = Foo1 | ||
-- pred Foo3 = Foo2 | ||
-- .. | ||
-- enumFrom = messageEnumFrom | ||
-- enumFromTo = messageEnumFromTo | ||
-- enumFromThen = messageEnumFromThen | ||
-- enumFromThenTo = messageEnumFromThenTo | ||
, instDecl [] ("Prelude.Enum" `ihApp` [dataType]) | ||
[[match "toEnum" ["k__"] | ||
$ "Prelude.maybe" @@ errorMessageExpr @@ "Prelude.id" | ||
@@ ("Data.ProtoLens.maybeToEnum" @@ "k__")] | ||
, [ match "fromEnum" [pApp (unQual c) []] $ litInt k | ||
| (c, k) <- constructorNumbers | ||
] | ||
++ | ||
[ match "readEnum" [pWildCard] "Prelude.Nothing" | ||
[match "fromEnum" [pApp (unQual unrecognizedName) | ||
[pApp (unQual unrecognizedValueName) [pVar "k"]] | ||
] | ||
$ "Prelude.fromIntegral" @@ "k" | ||
] | ||
, succDecl "succ" maxBoundName succPairs | ||
, succDecl "pred" minBoundName $ map swap succPairs | ||
, alias "enumFrom" "Data.ProtoLens.Message.Enum.messageEnumFrom" | ||
, alias "enumFromTo" "Data.ProtoLens.Message.Enum.messageEnumFromTo" | ||
, alias "enumFromThen" "Data.ProtoLens.Message.Enum.messageEnumFromThen" | ||
, alias "enumFromThenTo" | ||
"Data.ProtoLens.Message.Enum.messageEnumFromThenTo" | ||
] | ||
|
||
-- proto3 enums always default to zero. | ||
-- instance Data.Default.Class.Default Foo where | ||
-- def = FirstEnumValue | ||
, instDecl [] ("Data.Default.Class.Default" `ihApp` [dataType]) | ||
[[match "def" [] $ "Prelude.toEnum" @@ litInt 0]] | ||
[[match "def" [] defaultCon]] | ||
-- instance Data.ProtoLens.FieldDefault Foo where | ||
-- fieldDefault = FirstEnumValue | ||
, instDecl [] ("Data.ProtoLens.FieldDefault" `ihApp` [dataType]) | ||
[[match "fieldDefault" [] $ "Prelude.toEnum" @@ litInt 0]] | ||
] | ||
++ | ||
-- pattern Value0 :: Foo | ||
-- pattern Value0 = Foo 0 | ||
[[match "fieldDefault" [] defaultCon]] | ||
] ++ | ||
-- pattern Enum2a :: FooEnum | ||
-- pattern Enum2a = Enum2 | ||
concat | ||
[ [ patSynSig n dataType | ||
, patSyn (pVar n) | ||
$ pApp (unQual dataName) [pLitInt k] | ||
[ [ patSynSig aliasName dataType | ||
, patSyn (pVar aliasName) (pVar originalName) | ||
] | ||
| v <- enumValues info | ||
, let n = enumValueName v | ||
, let k = fromIntegral $ enumValueDescriptor v ^. number | ||
| EnumValueInfo | ||
{ enumValueName = aliasName | ||
, enumAliasOf = Just originalName | ||
} <- enumValues info | ||
] | ||
|
||
where | ||
dataName = enumName info | ||
EnumInfo { enumName = dataName, enumDescriptor = ed } = info | ||
errorMessage = "toEnum: unknown value for enum " ++ unpack (ed ^. name) | ||
++ ": " | ||
|
||
errorMessageExpr = "Prelude.error" | ||
@@ ("Prelude.++" @@ stringExp errorMessage | ||
@@ ("Prelude.show" @@ "k__")) | ||
alias funName implName = [match funName [] implName] | ||
|
||
dataType = tyCon $ unQual dataName | ||
|
||
unrecognizedValueName, unrecognizedName :: Name | ||
unrecognizedValueName = modifyIdent (++ "'UnrecognizedValue") dataName | ||
unrecognizedName = modifyIdent (++ "'Unrecognized") dataName | ||
|
||
unrecognizedValueType = tyCon $ unQual unrecognizedValueName | ||
|
||
constructors :: [(Name, EnumValueDescriptorProto)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a shame this now duplicates logic from the proto2 code...not sure if there's a good way to re-combine the two. (They were split apart more in #137.) |
||
constructors = List.sortBy (comparing ((^. number) . snd)) | ||
[(n, d) | EnumValueInfo | ||
{ enumValueName = n | ||
, enumValueDescriptor = d | ||
, enumAliasOf = Nothing | ||
} <- enumValues info | ||
] | ||
constructorNames = map fst constructors | ||
|
||
defaultCon = con $ unQual $ head constructorNames | ||
|
||
minBoundName = head constructorNames | ||
maxBoundName = last constructorNames | ||
|
||
constructorNumbers = map (second (fromIntegral . (^. number))) constructors | ||
|
||
succPairs = zip constructorNames $ tail constructorNames | ||
succDecl funName boundName thePairs = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. Is not erroring on Do you also want me to redefine There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this would break things, eg for
it seems like we want |
||
match funName [pApp (unQual boundName) []] | ||
("Prelude.error" @@ stringExp (concat | ||
[ prettyPrint dataName, ".", prettyPrint funName, ": bad argument " | ||
, prettyPrint boundName, ". This value would be out of bounds." | ||
])) | ||
: | ||
[ match funName [pApp (unQual from) []] $ con $ unQual to | ||
| (from, to) <- thePairs | ||
] | ||
++ | ||
[match funName [pWildCard] | ||
("Prelude.error" @@ stringExp (concat | ||
[ prettyPrint dataName, ".", prettyPrint funName, ": bad argument: unrecognized value" | ||
])) | ||
] | ||
|
||
generateEnumDecls Proto2 info = | ||
[ dataDecl dataName | ||
[conDecl n [] | n <- constructorNames] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,17 +16,18 @@ message Foo { | |
} | ||
Sub sub = 5; | ||
|
||
enum FooEnum { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What was the reason for changing this test? My preference is to add a new test case if we want to support the different situation. (Or, at minimum, add another test that does check naming of nested enums.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that was due to a transient issue, I've reverted this change. |
||
option allow_alias = true; | ||
Enum1 = 0; | ||
Enum2 = 3; | ||
Enum2a = 3; | ||
} | ||
FooEnum enum = 6; | ||
|
||
repeated int32 f = 7; | ||
} | ||
|
||
enum FooEnum { | ||
option allow_alias = true; | ||
Enum1 = 0; | ||
Enum2 = 3; | ||
Enum2a = 3; | ||
} | ||
|
||
message Strings { | ||
bytes bytes = 1; | ||
string string = 2; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code structure nit: we try to define names inside the Definitions module and use them in Generate. In this case, adding new fields to
EnumInfo
with theName
s for the unrecognized types:https://github.com/google/proto-lens/blob/master/proto-lens-protoc/src/Data/ProtoLens/Compiler/Definitions.hs#L157