Proposal: Enums
CertainLach opened this issue · 14 comments
Simple 1<=>1 enum mapping
Makes output code much easier to use
enum("a", type: ::u8)=>{ # Incorrect code, idents can't appear in Item
value("A") => "3",
value("B") => "4"
}
Needs to be done:
- allowing idents to appear in items
- enum item
@rom1504 It is, but if i want to have a enum
declaration in other line of file?
Or same union keys (But not values) is used in different points of file?
Yeah, it is something like repeating yourself, but... Output code will be more usable if there is a actually enum in needed point
I think this is covered by map
Well, there is map
available in protodefc? :D
So in the old JSON protodef, it was called "mapper" (https://github.com/PrismarineJS/minecraft-data/blob/master/data/pc/1.12.2/protocol.json#L3846). Can't find it in protodefc. But this is basically what you want right ?
Yeah, exactly
As far as I can see, there is nothing this can accomplish that can't be done with union. I also don't really see how this would make the output code any easier to read. A couple more examples would be appreciated.
It would be useful to use same mapper in multiple unions, to have only one enum emitted in target code
I still don't understand what this is supposed to accomplish, could you please post some more concrete examples?
As far as I understand it this does exactly what you are trying to do.
virtual_field("tag", value: "enum/@tag") => ::u8;
field("enum") => union("name_of_enum", tag: "../tag") {
variant("a", match: "3") => container {};
variant("b", match: "4") => container {};
};
enum("packet_type", type: ::u8)=>{
variant("entity_look", match: "0")
variant("position", match: "1")
}
namespace("play") {
namespace("server") {
def("to_client") => container {
virtual_field("tag", value: "enum/@tag") => ::u8;
field("data ") => switch(::packet_type, tag: "../tag") { # Incorrect code (ident in item)
variant("entity_look") => ::play::server::s_entity_look;
variant("position") => ::play::server::s_position;
};
}
}
namespace("client") {
def("to_server") => container {
field("client_version") => ::u8;
virtual_field("tag", value: "enum/@tag") => ::u8;
field("data") => switch(::packet_type, tag: "../tag") { # Incorrect code (ident in item)
variant("entity_look") => ::play::server::c_entity_look;
variant("position") => ::play::server::c_position;
};
}
}
}
My syntax is bad, needs to be improved
=> (Compiled code)
class Protocol {
public static enum PacketType {
ENTITY_LOOK(0),
POSITION(1);
byte match; // byte - because of u8
PacketType(byte match){
this.match = match;
}
static PacketType byMatch(byte){
//...
}
}
// Wrapping class
public static class Play {
public static class Server {
@Data
public static class ToClient {
Object data; // Can be Protocol.Play.Server.SEntityLook or Protocol.Play.Server.SPosition
public deserialize(byte[] data, offset){
Protocol.PacketType packetType = Protocol.PacketType.byMatch(readU8(data,offset));
switch(packetType) {
case ENTITY_LOOK:
// ...
// ...
}
}
}
}
}
}
In addition to what Rom said, the example you posted only makes sense for languages that don't support union types. It would make it harder to generate idiomatic code for languages like TypeScript, Rust or Haskell.
On the other hand, there is a feature that I have been planning that can accomplish some of the same things I imagine you intend with this, namely supporting native container types. This would enable you to write something like an Option and write idiomatic implementations in most languages.