diff --git a/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/ClassidWalker.java b/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/ClassidWalker.java index 031565e..4300cf2 100644 --- a/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/ClassidWalker.java +++ b/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/ClassidWalker.java @@ -72,9 +72,13 @@ public class ClassidWalker extends CKDefinesParserBaseListener { mLevel = 0; mLevelStack = null; - // classid is signed int and do not have flags feature. - mCurrentEnum.mCanUnsigned = false; - mCurrentEnum.mUseFlags = false; + // update self + mCurrentEnum.updateByEntries(); + // we forcely set classid is signed and do not have flags feature. + mCurrentEnum.mIsFlag = false; + mCurrentEnum.mIsUnsigned = false; + + // and return mResult = mCurrentEnum; mCurrentEnum = null; } @@ -90,6 +94,11 @@ public class ClassidWalker extends CKDefinesParserBaseListener { mCurrentEntry.mEntryName = ctx.CKGENERIC_ID(0).getText(); mCurrentEntry.mEntryValue = ctx.CKGENERIC_NUM().getText(); + // All classid number is positive. + mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Positive; + // And all in ordinary number style so it doesn't have flag feature. + mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.NotFlag; + // fill entry level info int this_level = getClassidLevel(ctx.getStart()); if (this_level > mLevel) { diff --git a/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/DefinesWalker.java b/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/DefinesWalker.java index 6e6ee8b..57143b5 100644 --- a/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/DefinesWalker.java +++ b/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/DefinesWalker.java @@ -30,6 +30,9 @@ public class DefinesWalker extends CKDefinesParserBaseListener { @Override public void exitProg(CKDefinesParser.ProgContext ctx) { + // update enum + mCurrentEnum.updateByEntries(); + // and return mResult = mCurrentEnum; mCurrentEnum = null; } @@ -48,19 +51,26 @@ public class DefinesWalker extends CKDefinesParserBaseListener { if (ctx.CKGENERIC_NUM() == null) { // define with id mCurrentEntry.mEntryValue = ctx.CKGENERIC_ID(1).getText(); - + // it refers other memeber, so its sign is unknown + mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Unknown; + // it refers other memeber, so it may flag. + mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.MayFlag; } else { // define with number String num = ctx.CKGENERIC_NUM().getText(); mCurrentEntry.mEntryValue = num; - // check whether this enum can be unsigned + // check the sign of this number if (CommonHelper.isNegativeNumber(num)) { - mCurrentEnum.mCanUnsigned = false; + mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Negative; + } else { + mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Positive; } - // if the number is in hex form, this enum MIGHT have flags feature + // if the number is in hex form, it may belong to flag enum if (CommonHelper.isHexNumber(num)) { - mCurrentEnum.mUseFlags = true; + mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.MayFlag; + } else { + mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.NotFlag; } } diff --git a/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/EnumsHelper.java b/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/EnumsHelper.java index 6c653fc..98bafa1 100644 --- a/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/EnumsHelper.java +++ b/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/EnumsHelper.java @@ -1,6 +1,45 @@ import java.util.Vector; public class EnumsHelper { + + /** + * The kind of enum entry value. + * This kind indicates whether this enum entry belong to a flag enum. + */ + public enum BEnumEntryFlagKind { + /** + * This enum entry can not belong to a flag enum. + * Because its value is ordinary. + */ + NotFlag, + /** + * This enum entry may belong to a flag enum. + * Because its value is in HEX format, and refering other members. + */ + MayFlag, + /** + * This enum entry must belong to a flag enum. + * Because its value use bitwise operation. + */ + MustFlag, + } + + /** + * The kind of enum entry value. + * This kind indicates the sign of this enum entry value. + */ + public enum BEnumEntrySignKind { + /** The value of this enum entry is positive number or zero. */ + Positive, + /** The value of this enum entry is negative. */ + Negative, + /** + * The value of this enum entry is unknown. + * This is may be caused by that it refer other memeber. + */ + Unknown, + } + /** * The struct to describe the entry of an enum. */ @@ -8,6 +47,8 @@ public class EnumsHelper { public BEnumEntry() { mEntryName = null; mEntryValue = null; + mEntryFlagKind = null; + mEntrySignKind = null; mEntryComment = null; } @@ -15,6 +56,10 @@ public class EnumsHelper { public String mEntryName; /** The value of this entry. null if this entry do not have explicit value. */ public String mEntryValue; + /** The flag kind of this entry value. */ + public BEnumEntryFlagKind mEntryFlagKind; + /** The sign kind of this entry value. */ + public BEnumEntrySignKind mEntrySignKind; /** The comment of this entry. null if no comment. */ public String mEntryComment; } @@ -44,8 +89,8 @@ public class EnumsHelper { public BEnum() { mEnumName = null; mEnumComment = null; - mCanUnsigned = true; - mUseFlags = false; + mIsUnsigned = true; + mIsFlag = false; mEntries = new Vector(); } @@ -53,12 +98,48 @@ public class EnumsHelper { public String mEnumName; /** The comment of this enum. null if no comment. */ public String mEnumComment; - /** True if this enum can use unsigned integer as its underlying type. */ - public boolean mCanUnsigned; - /** True if this enum will use flags feature (supporting OR, AND, operators). */ - public boolean mUseFlags; + /** True if this enum should use unsigned integer as its underlying type, otherwise false. */ + public boolean mIsUnsigned; + /** True if this enum shoule have flags feature (supporting OR, AND, operators), otherwise false. */ + public boolean mIsFlag; /** The list to store entries of this enum. */ public Vector mEntries; + + /** + * Update some properties located in this class according to existing entries. + */ + public void updateByEntries() { + // If there is at least one negative entry, the enum should be signed, + // Otherwise, it is unsigned. + // For unknown entries, ignore them. + boolean has_negative = false; + for (BEnumEntry entry : this.mEntries) { + if (entry.mEntrySignKind == BEnumEntrySignKind.Negative) { + has_negative = true; + } + } + this.mIsUnsigned = !has_negative; + + // For flag kind, if there is "Must Flag" entry, the enum should be a flag enum. + // Then, if "May Flag" entry is more than "Not Flag", the enum would be a flag enum. + // Otherwise, it is not flag. + boolean has_must_flag = false; + int cnt_may_flag = 0, cnt_not_flag = 0; + for (BEnumEntry entry : this.mEntries) { + switch (entry.mEntryFlagKind) { + case NotFlag -> ++cnt_not_flag; + case MayFlag -> ++cnt_may_flag; + case MustFlag -> has_must_flag = true; + } + } + if (has_must_flag) { + this.mIsFlag = true; + } else if (cnt_may_flag > cnt_not_flag) { + this.mIsFlag = true; + } else { + this.mIsFlag = false; + } + } } /** diff --git a/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/EnumsWalker.java b/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/EnumsWalker.java index f2b240a..fb7045e 100644 --- a/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/EnumsWalker.java +++ b/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/EnumsWalker.java @@ -59,6 +59,8 @@ public class EnumsWalker extends CKEnumsParserBaseListener { List allNames = ctx.CKGENERIC_ID(); mCurrentEnum.mEnumName = allNames.get(allNames.size() - 1).getText(); + // update self and add into list + mCurrentEnum.updateByEntries(); mCurrentProg.mEnums.add(mCurrentEnum); mCurrentEnum = null; } @@ -75,6 +77,14 @@ public class EnumsWalker extends CKEnumsParserBaseListener { // get entry name mCurrentEntry.mEntryName = ctx.CKGENERIC_ID().getText(); + // if its value is null, we manually fill 2 kinds + if (mCurrentEntry.mEntryValue == null) { + // the sign kind is unknown because it relys on other value (+1) + mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Unknown; + // because it just adds one from previous member, it should not belong to a flag enum + mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.NotFlag; + } + mCurrentEnum.mEntries.add(mCurrentEntry); mCurrentEntry = null; } @@ -85,34 +95,42 @@ public class EnumsWalker extends CKEnumsParserBaseListener { List nums = ctx.CKGENERIC_NUM(); switch (nums.size()) { - case 1: { - // set value - TerminalNode node = nums.get(0); - mCurrentEntry.mEntryValue = node.getText(); + case 1: { + // value is immediate number + TerminalNode node = nums.get(0); + String num = node.getText(); + mCurrentEntry.mEntryValue = num; - // check whether this enum can be unsigned - if (CommonHelper.isNegativeNumber(node.getText())) { - mCurrentEnum.mCanUnsigned = false; + // check whether this enum can be unsigned + if (CommonHelper.isNegativeNumber(num)) { + mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Negative; + } else { + mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Positive; + } + // if the number is in hex form, this entry may belong to flag enum + if (CommonHelper.isHexNumber(num)) { + mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.MayFlag; + } else { + mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.NotFlag; + } + + break; } - // if the number is in hex form, this enum MIGHT have flags feature - if (CommonHelper.isHexNumber(node.getText())) { - mCurrentEnum.mUseFlags = true; + case 2: { + // value is bitwise operation + TerminalNode num = nums.get(0), offset = nums.get(1); + mCurrentEntry.mEntryValue = String.format("%s << %s", num.getText(), offset.getText()); + + // << operator appears. + // it shoud be unsigned. + mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Positive; + // and it must belong to flag enum + mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.MustFlag; + + break; } - - break; - } - case 2: { - // set value - TerminalNode num = nums.get(0), offset = nums.get(1); - mCurrentEntry.mEntryValue = String.format("%s << %s", num.getText(), offset.getText()); - - // << operator appears. this enum must have flags feature - mCurrentEnum.mUseFlags = true; - - break; - } - default: - throw new IllegalArgumentException("Unexpected value: " + nums.size()); + default: + throw new IllegalArgumentException("Unexpected value: " + nums.size()); } } @@ -123,9 +141,19 @@ public class EnumsWalker extends CKEnumsParserBaseListener { mCurrentEntry.mEntryValue = ctx.CKGENERIC_ID().stream().map(value -> value.getText()) .collect(Collectors.joining(" | ")); - // | operator appears. this enum must have flags feature - mCurrentEnum.mUseFlags = true; - + if (ctx.CKGENERIC_ID().size() > 1) { + // If there is more than one ID, it means | operator appears. + // It should be unsigned. + mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Positive; + // And it must belong to flag enum. + mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.MustFlag; + } else { + // Otherwise it just refer other member. + // The sign of its value is unclear. + mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Unknown; + // And it may belong to flag enum because it refers other memeber. + mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.MayFlag; + } } } \ No newline at end of file diff --git a/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/JsonWriter.java b/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/JsonWriter.java index 761ab8b..8d70093 100644 --- a/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/JsonWriter.java +++ b/Assets/CodeGen/EnumsMigration/EnumsAnalyzer/JsonWriter.java @@ -6,10 +6,28 @@ import com.google.gson.GsonBuilder; public class JsonWriter { + private static String writeBEnumEntryFlagKind(EnumsHelper.BEnumEntryFlagKind kind) { + return switch (kind) { + case NotFlag -> "not-flag"; + case MayFlag -> "may-flag"; + case MustFlag -> "must-flag"; + }; + } + + private static String writeBEnumEntrySignKind(EnumsHelper.BEnumEntrySignKind kind) { + return switch (kind) { + case Positive -> "positive"; + case Negative -> "negative"; + case Unknown -> "unknown"; + }; + } + private static JsonObject writeBEnumEntry(EnumsHelper.BEnumEntry enumEntry) { JsonObject data = new JsonObject(); data.addProperty("name", enumEntry.mEntryName); data.addProperty("value", enumEntry.mEntryValue); + data.addProperty("flag_kind", writeBEnumEntryFlagKind(enumEntry.mEntryFlagKind)); + data.addProperty("sign_kind", writeBEnumEntrySignKind(enumEntry.mEntrySignKind)); data.addProperty("comment", enumEntry.mEntryComment); // Export hierarchy if possible @@ -30,9 +48,8 @@ public class JsonWriter { JsonObject data = new JsonObject(); data.addProperty("name", benum.mEnumName); data.addProperty("comment", benum.mEnumComment); - data.addProperty("can_unsigned", benum.mCanUnsigned); - data.addProperty("use_flags", benum.mUseFlags); - data.addProperty("use_flags", benum.mUseFlags); + data.addProperty("is_unsigned", benum.mIsUnsigned); + data.addProperty("is_flag", benum.mIsFlag); JsonArray entries = new JsonArray(); for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) { diff --git a/Assets/CodeGen/EnumsMigration/EnumsRender/json_loader.py b/Assets/CodeGen/EnumsMigration/EnumsRender/json_loader.py index 9b0dfd9..506ea8e 100644 --- a/Assets/CodeGen/EnumsMigration/EnumsRender/json_loader.py +++ b/Assets/CodeGen/EnumsMigration/EnumsRender/json_loader.py @@ -87,9 +87,9 @@ class BEnum: """The name of this enum.""" __enum_comment: str | None """The comment of this enum. None if no comment.""" - __can_unsigned: bool + __is_unsigned: bool """True if this enum can use unsigned integer as its underlying type.""" - __use_flags: bool + __is_flag: bool """True if this enum will use flags feature (supporting OR, AND, operators).""" __entries: list[BEnumEntry] """The list to store entries of this enum.""" @@ -101,14 +101,14 @@ class BEnum: self, enum_name: str, enum_comment: str | None, - can_unsigned: bool, - use_flags: bool, + is_unsigned: bool, + is_flag: bool, entries: list[BEnumEntry], ): self.__enum_name = enum_name self.__enum_comment = enum_comment - self.__can_unsigned = can_unsigned - self.__use_flags = use_flags + self.__is_unsigned = is_unsigned + self.__is_flag = is_flag self.__entries = entries self.__entries_map = {e.get_entry_name(): e for e in entries} @@ -120,13 +120,13 @@ class BEnum: """Get the comment of this enum. None if no comment.""" return self.__enum_comment - def get_can_unsigned(self) -> bool: + def is_unsigned(self) -> bool: """True if this enum can use unsigned integer as its underlying type.""" - return self.__can_unsigned + return self.__is_unsigned - def get_use_flags(self) -> bool: + def is_flag(self) -> bool: """True if this enum will use flags feature (supporting OR, AND, operators).""" - return self.__use_flags + return self.__is_flag def iter_entries(self) -> typing.Iterator[BEnumEntry]: """Get the iterator of entries of this enum.""" @@ -140,8 +140,8 @@ class BEnum: return BEnum( data["name"], data.get("comment", None), - data["can_unsigned"], - data["use_flags"], + data["is_unsigned"], + data["is_flag"], list(map(lambda i: BEnum.__create_entry_by_content(i), data["entries"])), ) diff --git a/Assets/CodeGen/EnumsMigration/EnumsRender/main.py b/Assets/CodeGen/EnumsMigration/EnumsRender/main.py index 7c89da5..2a9b7eb 100644 --- a/Assets/CodeGen/EnumsMigration/EnumsRender/main.py +++ b/Assets/CodeGen/EnumsMigration/EnumsRender/main.py @@ -11,6 +11,7 @@ def main(): render.render_cpp_enum("CKERROR.hpp", ckerror) render.render_py_enum("CKERROR.py", ckerror) render.render_cs_enum("CKERROR.cs", ckerror) + render.render_rs_enum("CKERROR.rs", ckerror) render.render_cpp_ckerror_docstring("CKERROR.docstring.hpp", "CKERROR.docstring.cpp", ckerror) render.render_py_enum_docstring("CKERROR.docstring.py", ckerror) render.render_cs_enum_docstring("CKERROR.docstring.cs", ckerror) diff --git a/Assets/CodeGen/EnumsMigration/EnumsRender/templates/generic.cs.jinja b/Assets/CodeGen/EnumsMigration/EnumsRender/templates/generic.cs.jinja index 0bb6b47..f09b138 100644 --- a/Assets/CodeGen/EnumsMigration/EnumsRender/templates/generic.cs.jinja +++ b/Assets/CodeGen/EnumsMigration/EnumsRender/templates/generic.cs.jinja @@ -1,9 +1,9 @@ {%- for benum in payload.iter_enums() %} {%- if benum.get_enum_comment() is not none %} {{ benum.get_enum_comment() | block_comment('/// ') }} -{%- endif %} {%- if benum.get_use_flags() %} +{%- endif %} {%- if benum.is_flag() %} [Flags]{%- endif %} -public enum {{ benum.get_enum_name() }} : {% if benum.get_can_unsigned() -%} uint {%- else -%} int {%- endif %} { +public enum {{ benum.get_enum_name() }} : {% if benum.is_unsigned() -%} uint {%- else -%} int {%- endif %} { {%- for entry in benum.iter_entries() %} {%- if entry.get_entry_comment() is not none %} /// diff --git a/Assets/CodeGen/EnumsMigration/EnumsRender/templates/generic.hpp.jinja b/Assets/CodeGen/EnumsMigration/EnumsRender/templates/generic.hpp.jinja index e28996f..dc82ccd 100644 --- a/Assets/CodeGen/EnumsMigration/EnumsRender/templates/generic.hpp.jinja +++ b/Assets/CodeGen/EnumsMigration/EnumsRender/templates/generic.hpp.jinja @@ -4,7 +4,7 @@ {{ benum.get_enum_comment() | block_comment(' * ') }} */ {%- endif %} -enum class {{ benum.get_enum_name() }} : {% if benum.get_can_unsigned() -%} CKDWORD {%- else -%} CKINT {%- endif %} { +enum class {{ benum.get_enum_name() }} : {% if benum.is_unsigned() -%} CKDWORD {%- else -%} CKINT {%- endif %} { {%- for entry in benum.iter_entries() %} {{ entry.get_entry_name() }} {%- if entry.get_entry_value() is not none %} = {{ entry.get_entry_value() }} {%- endif %}, {%- if entry.get_entry_comment() is not none %} /**< {{ entry.get_entry_comment() | line_comment }} */ {%- endif %} {%- endfor %} diff --git a/Assets/CodeGen/EnumsMigration/EnumsRender/templates/generic.rs.jinja b/Assets/CodeGen/EnumsMigration/EnumsRender/templates/generic.rs.jinja index c76af08..777b11a 100644 --- a/Assets/CodeGen/EnumsMigration/EnumsRender/templates/generic.rs.jinja +++ b/Assets/CodeGen/EnumsMigration/EnumsRender/templates/generic.rs.jinja @@ -2,9 +2,10 @@ {%- if benum.get_enum_comment() is not none %} {{ benum.get_enum_comment() | block_comment('/// ') }} {%- endif %} -{%- if not benum.get_use_flags() %} +{%- if not benum.is_flag() %} +#[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr({% if benum.get_can_unsigned() -%} u32 {%- else -%} i32 {%- endif %})] +#[repr({% if benum.is_unsigned() -%} u32 {%- else -%} i32 {%- endif %})] pub enum {{ benum.get_enum_name() }} { {%- for entry in benum.iter_entries() %} {%- if entry.get_entry_comment() is not none %} @@ -16,10 +17,10 @@ pub enum {{ benum.get_enum_name() }} { {%- else %} #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] -pub struct {{ benum.get_enum_name() }}({% if benum.get_can_unsigned() -%} u32 {%- else -%} i32 {%- endif %}); +pub struct {{ benum.get_enum_name() }}({% if benum.is_unsigned() -%} u32 {%- else -%} i32 {%- endif %}); bitflags! { - impl {{ benum.get_enum_name() }}: {% if benum.get_can_unsigned() -%} u32 {%- else -%} i32 {%- endif %} { + impl {{ benum.get_enum_name() }}: {% if benum.is_unsigned() -%} u32 {%- else -%} i32 {%- endif %} { {%- for entry in benum.iter_entries() %} {%- if entry.get_entry_comment() is not none %} /// {{ entry.get_entry_comment() | line_comment }}