1
0

refactor: refactor EnumsMigration but not finished

This commit is contained in:
2026-01-26 11:11:58 +08:00
parent e0e5c9b090
commit c68bdce37b
36 changed files with 669 additions and 409 deletions

View File

@@ -0,0 +1,126 @@
## ======== Personal ========
# Additional remove for JetBrains IDEA
.idea/
*.iml
## ======== ANTLR Output ========
*.interp
*.tokens
CKGeneralLexer*.java
CKEnumsParser*.java
CKDefinesParser*.java
## ======== Java ========
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
## ======== JetBrains ========
# Covers JetBrains IDEs: IntelliJ, GoLand, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
.idea/sonarlint.xml # see https://community.sonarsource.com/t/is-the-file-idea-idea-idea-sonarlint-xml-intended-to-be-under-source-control/121119
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based HTTP Client
.idea/httpRequests
http-client.private.env.json
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# Apifox Helper cache
.idea/.cache/.Apifox_Helper
.idea/ApifoxUploaderProjectSetting.xml
# Github Copilot persisted session migrations, see: https://github.com/microsoft/copilot-intellij-feedback/issues/712#issuecomment-3322062215
.idea/**/copilot.data.migration.*.xml

View File

@@ -0,0 +1,6 @@
parser grammar CKDefinesParser;
options { tokenVocab = CKGeneralLexer; }
prog: definePair+ ;
definePair: CKGENERAL_DEFINE CKGENERAL_ID (CKGENERAL_NUM | CKGENERAL_ID) ;

View File

@@ -0,0 +1,14 @@
parser grammar CKEnumsParser;
options { tokenVocab = CKGeneralLexer; }
prog: enumBody* ;
enumBody: CKGENERAL_TYPEDEF? CKGENERAL_ENUM CKGENERAL_ID CKGENERAL_LBRACKET
entryPair+
CKGENERAL_RBRACKET CKGENERAL_ID? CKGENERAL_SEMICOLON ;
entryPair: CKGENERAL_ID (CKGENERAL_EQUAL entryValue)? CKGENERAL_COMMA? ;
entryValue: CKGENERAL_NUM (CKGENERAL_LSHIFT CKGENERAL_NUM)? # entryDirectValue
| CKGENERAL_ID (CKGENERAL_OR CKGENERAL_ID)* # entryRelativeValue
;

View File

@@ -0,0 +1,26 @@
lexer grammar CKGeneralLexer;
channels { COMMENTS, WHITESPACE }
// keywords
CKGENERAL_TYPEDEF: 'typedef' ;
CKGENERAL_DEFINE: '#define' ;
CKGENERAL_ENUM: 'enum' ;
// symbols
CKGENERAL_LBRACKET: '{' ;
CKGENERAL_RBRACKET: '}' ;
CKGENERAL_EQUAL: '=';
CKGENERAL_SEMICOLON: ';' ;
CKGENERAL_LSHIFT: '<<' ;
CKGENERAL_OR: '|' ;
CKGENERAL_COMMA: ',' ;
// identifider and number
CKGENERAL_ID: [_a-zA-Z][_a-zA-Z0-9]* ;
CKGENERAL_NUM: (('0'[xX]) | '-')? [0-9a-fA-F]+ [uUlL]* ;
// comments
CKGENERAL_LINE_COMMENT: '//' ~[\r\n]* -> channel(COMMENTS);
CKGENERAL_BLOCK_COMMENT: '/*' .*? '*/' -> channel(COMMENTS);
// whitespace
CKGENERAL_WS: [ \t\r\n]+ -> channel(WHITESPACE);

View File

@@ -0,0 +1,168 @@
import java.io.OutputStreamWriter;
/**
* Write enum declarations and accessible value into CSharp format.
*/
public class CSharpWriter {
// =========== C# Enum Declaration Writer ===========
/**
* Get corredponding C# underlying type of given enum.
* <p>
* This is C# specific function.
*
* @param canUnsigned The parameter stored in Enum_t that indicate whether this
* enum can use unsigned int as its underlying type.
* @return The string form of its underlying type.
*/
private static String getEnumUnderlyingType(boolean canUnsigned) {
return canUnsigned ? "uint" : "int";
}
/**
* Internal real enum declaration writer.
*
* @param writer {@linkplain java.io.OutputStreamWriter} instance for writing.
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for writing.
* @throws Exception
*/
private static void internalWriteEnums(OutputStreamWriter writer, EnumsHelper.BEnumCollection prog)
throws Exception {
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.CSharp);
for (EnumsHelper.BEnum benum : prog.mEnums) {
// write enum comment
indent.briefComment(benum.mEnumComment);
// write enum start
// write flasg attribute if it is
if (benum.mUseFlags) {
indent.puts("[Flags]");
}
indent.printf("public enum %s : %s {", benum.mEnumName, getEnumUnderlyingType(benum.mCanUnsigned));
indent.inc();
// write enum entries
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
// write entry self
if (enumEntry.mEntryValue == null) {
indent.printf("%s,", enumEntry.mEntryName);
} else {
indent.printf("%s = %s,", enumEntry.mEntryName, enumEntry.mEntryValue);
}
// write entry comment after member
indent.afterMemberComment(enumEntry.mEntryComment);
}
// write enum tail
indent.dec();
indent.puts("}");
}
}
/**
* Write an enum declaration collection into given file.
* <p>
* Actually this is a wrapper of internal enum declaration collection writer.
*
* @param filename The name of written file.
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for
* writing.
* @throws Exception
*/
public static void writeEnums(String filename, EnumsHelper.BEnumCollection prog) throws Exception {
// open file and write
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
internalWriteEnums(fs, prog);
fs.close();
}
/**
* Write a single enum declaration into given file.
* <p>
* Actually this is a wrapper of internal enum declaration collection writer.
*
* @param filename The name of written file.
* @param _enum {@linkplain EnumsHelper.BEnum} instance for writing.
* @throws Exception
*/
public static void writeEnum(String filename, EnumsHelper.BEnum _enum) throws Exception {
// create collection from single enum
EnumsHelper.BEnumCollection col = new EnumsHelper.BEnumCollection();
col.mEnums.add(_enum);
// open file and write
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
internalWriteEnums(fs, col);
fs.close();
}
// =========== C# Enum Accessible Value Writer ===========
/**
* Internal real enum accessible value writer.
*
* @param writer {@linkplain java.io.OutputStreamWriter} instance for writing.
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for writing.
* @throws Exception
*/
private static void internalWriteAccVals(OutputStreamWriter writer, EnumsHelper.BEnumCollection prog)
throws Exception {
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.CSharp);
// write enum collections
for (EnumsHelper.BEnum benum : prog.mEnums) {
// write enum desc header
indent.printf(
"public static readonly System.Collections.Generic.Dictionary<%s, string> %s = new System.Collections.Generic.Dictionary<%s, string>() {",
benum.mEnumName, benum.mEnumName, benum.mEnumName);
indent.inc();
// write enum desc entries
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
indent.printf("{ %s.%s, \"%s\" },", benum.mEnumName, enumEntry.mEntryName, enumEntry.mEntryName);
}
// write enum tail
indent.dec();
indent.puts("};");
}
}
/**
* Write an enum accessible value collection into given file.
* <p>
* Actually this is a wrapper of internal enum accessible value collection
* writer.
*
* @param filename The name of written file.
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for
* writing.
* @throws Exception
*/
public static void writeAccVals(String filename, EnumsHelper.BEnumCollection prog) throws Exception {
// open file and write
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
internalWriteAccVals(fs, prog);
fs.close();
}
/**
* Write a single enum accessible value into given file.
* <p>
* Actually this is a wrapper of internal enum accessible value collection
* writer.
*
* @param filename The name of written file.
* @param _enum {@linkplain EnumsHelper.BEnum} instance for writing.
* @throws Exception
*/
public static void writeAccVal(String filename, EnumsHelper.BEnum _enum) throws Exception {
// create a collection with single enum.
EnumsHelper.BEnumCollection col = new EnumsHelper.BEnumCollection();
col.mEnums.add(_enum);
// open file and write
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
internalWriteAccVals(fs, col);
fs.close();
}
}

View File

@@ -0,0 +1,119 @@
import java.util.Stack;
import org.antlr.v4.runtime.*;
/**
* The specialized walker for collecting CK_CLASSID and its inherit
* relationship.
*/
public class ClassidWalker extends CKDefinesParserBaseListener {
public ClassidWalker(BufferedTokenStream tokenStream) {
mTokenStream = tokenStream;
mResult = null;
mLevelStack = null;
mCurrentEnum = null;
mCurrentEntry = null;
}
public EnumsHelper.BEnum getEnum() {
return mResult;
}
private int getClassidLevel(Token defineHead) {
Token ws = CommonHelper.getPreChannelToken(mTokenStream, defineHead, CKGeneralLexer.WHITESPACE);
if (ws == null)
return 0;
// reverse finding how many tab used.
int counter = 0;
char[] wstxt = ws.getText().toCharArray();
for (int i = wstxt.length - 1; i >= 0; i--) {
if (wstxt[i] == '\t') {
counter++;
} else {
break;
}
}
return counter;
}
private void safePop() {
if (mLevelStack.size() != 0)
mLevelStack.pop();
}
private void safePopTimes(int times) {
for (int i = 0; i < times; i++) {
if (mLevelStack.size() != 0)
mLevelStack.pop();
}
}
private BufferedTokenStream mTokenStream;
private EnumsHelper.BEnum mResult;
private int mLevel;
private Stack<EnumsHelper.BHierarchyEnumEntry> mLevelStack;
private EnumsHelper.BEnum mCurrentEnum;
private EnumsHelper.BHierarchyEnumEntry mCurrentEntry;
@Override
public void enterProg(CKDefinesParser.ProgContext ctx) {
mLevel = 0;
mLevelStack = new Stack<EnumsHelper.BHierarchyEnumEntry>();
mCurrentEnum = new EnumsHelper.BEnum();
}
@Override
public void exitProg(CKDefinesParser.ProgContext ctx) {
mLevel = 0;
mLevelStack = null;
// classid is signed int and do not have flags feature.
mCurrentEnum.mCanUnsigned = false;
mCurrentEnum.mUseFlags = false;
mResult = mCurrentEnum;
mCurrentEnum = null;
}
@Override
public void enterDefinePair(CKDefinesParser.DefinePairContext ctx) {
mCurrentEntry = new EnumsHelper.BHierarchyEnumEntry();
}
@Override
public void exitDefinePair(CKDefinesParser.DefinePairContext ctx) {
// fill entry info
mCurrentEntry.mEntryName = ctx.CKGENERAL_ID(0).getText();
mCurrentEntry.mEntryValue = ctx.CKGENERAL_NUM().getText();
// fill entry level info
int this_level = getClassidLevel(ctx.getStart());
if (this_level > mLevel) {
// level up
mLevel++;
mLevelStack.push(mCurrentEntry);
mCurrentEntry.mHierarchy.addAll(mLevelStack);
} else if (this_level == mLevel) {
safePop();
mLevelStack.push(mCurrentEntry);
mCurrentEntry.mHierarchy.addAll(mLevelStack);
} else if (this_level < mLevel) {
// level down
safePopTimes(mLevel - this_level + 1);
mLevel = this_level;
mLevelStack.push(mCurrentEntry);
mCurrentEntry.mHierarchy.addAll(mLevelStack);
}
// move to list
mCurrentEnum.mEntries.add(mCurrentEntry);
mCurrentEntry = null;
}
}

View File

@@ -0,0 +1,51 @@
import java.util.List;
import org.antlr.v4.runtime.*;
public class CommentsFinder {
enum CommentsPosition {
Unknown, Precomment, Postcomment
}
public CommentsFinder(BufferedTokenStream tokenStream) {
mTokenStream = tokenStream;
mCommentsPos = CommentsPosition.Unknown;
}
private BufferedTokenStream mTokenStream;
private CommentsPosition mCommentsPos;
public String getComment(Token preToken, Token postToken) {
switch (mCommentsPos) {
case Unknown: {
// if we don't know where is our token,
// we should assume it is from precomment to postcomment
// and check it.
List<Token> precomment = CommonHelper.getPreChannelTokens(mTokenStream, preToken, CKGeneralLexer.COMMENTS);
if (precomment != null) {
mCommentsPos = CommentsPosition.Precomment;
return CommonHelper.cutComments(precomment);
}
List<Token> postcomment = CommonHelper.getPostChannelTokens(mTokenStream, postToken, CKGeneralLexer.COMMENTS);
if (postcomment != null) {
mCommentsPos = CommentsPosition.Postcomment;
return CommonHelper.cutComments(postcomment);
}
// really do not have comment, return empty and keep comment pos
return null;
}
case Precomment: {
return CommonHelper.cutComments(CommonHelper.getPreChannelTokens(mTokenStream, preToken, CKGeneralLexer.COMMENTS));
}
case Postcomment: {
return CommonHelper.cutComments(CommonHelper.getPostChannelTokens(mTokenStream, postToken, CKGeneralLexer.COMMENTS));
}
default:
return null;
}
}
}

View File

@@ -0,0 +1,257 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import org.antlr.v4.runtime.*;
public class CommonHelper {
// =========== Token Finder & Comments Processing ===========
public static Token getPreChannelToken(BufferedTokenStream stream, Token token, int channel) {
List<Token> tokens = stream.getHiddenTokensToLeft(token.getTokenIndex(), channel);
if (tokens == null)
return null;
return tokens.get(0);
}
public static Token getPostChannelToken(BufferedTokenStream stream, Token token, int channel) {
List<Token> tokens = stream.getHiddenTokensToRight(token.getTokenIndex(), channel);
if (tokens == null)
return null;
return tokens.get(0);
}
public static List<Token> getPreChannelTokens(BufferedTokenStream stream, Token token, int channel) {
return stream.getHiddenTokensToLeft(token.getTokenIndex(), channel);
}
public static List<Token> getPostChannelTokens(BufferedTokenStream stream, Token token, int channel) {
return stream.getHiddenTokensToRight(token.getTokenIndex(), channel);
}
/**
* Cut the head and tail of comment
*
* @param comment The comment need to be cut.
* @return The cut comment.
*/
public static String cutComment(Token comment) {
if (comment == null)
return null;
switch (comment.getType()) {
case CKGeneralLexer.CKGENERAL_LINE_COMMENT: {
return removeStars(comment.getText().substring(2));
}
case CKGeneralLexer.CKGENERAL_BLOCK_COMMENT: {
String cache = comment.getText();
return removeStars(cache.substring(2, cache.length() - 4));
}
default:
return comment.getText();
}
}
/**
* Cut multiple comments.
* <p>
* Each comment will be cut the head and tail first. And strip all whitespace.
* Then join together.
*
* @param tokens Multiple comments.
* @return The joined comment.
*/
public static String cutComments(List<Token> tokens) {
if (tokens == null)
return null;
return tokens.stream().map(value -> {
String text = cutComment(value).strip();
return text + " ";
}).collect(Collectors.joining(""));
}
// =========== Number Operations ===========
/**
* Check whether Antlr captured CKGENERAL_NUM is a negative number.
*
* @param numstr The captured number.
* @return true if it is negative number.
*/
public static boolean isNegativeNumber(String numstr) {
return numstr.startsWith("-");
}
/**
* Check whether Altlr captured CKGENERAL_NUM is a hex number.
*
* @param numstr The captured number.
* @return true if it is hex number.
*/
public static boolean isHexNumber(String numstr) {
return numstr.startsWith("0x") || numstr.startsWith("0X");
}
/**
* Convert accepted string into Python cupported format.
*
* It actually just remove trail "UL".
*
* @param numstr The captured number.
* @return The Python style number string.
*/
public static String convertToPythonNumber(String numstr) {
return numstr.replaceFirst("[ulUL]+$", "");
}
// =========== Parts ===========
enum CKParts {
CK2, VxMath
}
enum LangType {
Cpp, Python, CSharp
}
public static String getCKPartsNamespace(CKParts parts) {
switch (parts) {
case CK2:
return "CK2";
case VxMath:
return "VxMath";
default:
throw new IllegalArgumentException("Unexpected value: " + parts);
}
}
// =========== File Operations ===========
private static Path getRootDirectoryPath() throws Exception {
String rootDir = System.getenv("ENUMS_MIGRATION_ROOT");
return Paths.get(rootDir);
}
public static class InputFilePair {
public CharStream mAntlrStream;
public FileInputStream mUnderlyingStream;
}
public static InputFilePair openInputFile(String filename) throws Exception {
InputFilePair pair = new InputFilePair();
pair.mUnderlyingStream = new FileInputStream(filename);
pair.mAntlrStream = CharStreams.fromStream(pair.mUnderlyingStream, StandardCharsets.UTF_8);
return pair;
}
public static String getInputFilePath(String filename) throws Exception {
Path rootDir = getRootDirectoryPath();
Path filePath = rootDir.resolve("Input").resolve(filename);
return filePath.toString();
}
/**
* Get output file for writing.
*
* @param filename The name of file opening.
* @return An OutputStreamWriter.
* @throws Exception
*/
public static OutputStreamWriter openOutputFile(String filename) throws Exception {
FileOutputStream fs = new FileOutputStream(filename);
return new OutputStreamWriter(fs, StandardCharsets.UTF_8);
}
public static String getOutputFilePath(String filename) throws Exception {
Path rootDir = getRootDirectoryPath();
Path filePath = rootDir.resolve("Intermediate").resolve(filename);
return filePath.toString();
}
// =========== String Process ===========
/**
* Escape String
*
* Escape all characters which are invalid in string quote.
*
* @param strl The string need to be escaped.
* @return The escaped string.
* @see removeEol
*/
public static String escapeString(String strl) {
return strl.replace("\\", "\\\\").replace("\t", "\\t").replace("\b", "\\b").replace("\n", "\\n")
.replace("\r", "\\r").replace("\f", "\\f").replace("\"", "\\\"");
}
/**
* Remove all EOL (End of Line) characters.
*
* @param strl The string need to be processed.
* @return The string eliminated all EOL.
* @see escapeString
*/
public static String removeEol(String strl) {
return strl.replace("\n", "").replace("\r", "");
}
/**
* Remove redundent star '*' (at least 5 continuous star chars)
*
* @param cmt The string provided.
* @return The string processed.
*/
public static String removeStars(String cmt) {
// only remove at least 5 continuous star chars.
return cmt.replaceAll("\\*{5,}", "");
}
/**
* Get indent char by style
*
* @param use_tab Whether indent use Tab, not Space.
* @return The indent chars
*/
public static String getIndentString(boolean use_tab) {
if (use_tab)
return "\t";
else
return " ";
}
/**
* Re-indent a block of string
*
* This function will first split block string by line. Then remove all indent
* (strip Tab and Space). At last, re-indent and join each line
*
* @param block_str The string provided.
* @param use_tab Use Tab, not Space as indent chars.
* @param indent_level The level of indent, started by 0.
* @return The re-indent string
*/
public static String reindentBlockString(String block_str, boolean use_tab, int indent_level) {
// pre-create indent string
String indentChars = getIndentString(use_tab);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < indent_level; ++i) {
sb.append(indentChars);
}
String indents = sb.toString();
// split line
return block_str.lines().map((String line) -> {
// strip space and tab, then re-indent it.
return indents + line.trim();
}).collect(Collectors.joining(System.lineSeparator())); // then join with new line breaker and return.
}
}

View File

@@ -0,0 +1,273 @@
import java.io.OutputStreamWriter;
import java.util.stream.Collectors;
/**
* Write enum declarations and accessible value into C++ format.
*/
public class CppWriter {
// =========== C++ Enum Declaration Writer ===========
/**
* Get corredponding C++ underlying type of given enum.
* <p>
* This is C++ specific function.
*
* @param canUnsigned The parameter stored in Enum_t that indicate whether this
* enum can use unsigned int as its underlying type.
* @return The string form of its underlying type.
*/
private static String getEnumUnderlyingType(boolean canUnsigned) {
return canUnsigned ? "CKDWORD" : "CKINT";
}
/**
* Internal real enum declarations writer.
*
* @param writer {@linkplain java.io.OutputStreamWriter} instance for writing.
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for writing.
* @throws Exception
*/
private static void internalWriteEnums(OutputStreamWriter writer, EnumsHelper.BEnumCollection prog)
throws Exception {
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.Cpp);
for (EnumsHelper.BEnum benum : prog.mEnums) {
// write enum comment
indent.briefComment(benum.mEnumComment);
// write enum start
indent.printf("enum class %s : %s {", benum.mEnumName, getEnumUnderlyingType(benum.mCanUnsigned));
indent.inc();
// write enum entries
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
// write entry self
if (enumEntry.mEntryValue == null) {
indent.printf("%s,", enumEntry.mEntryName);
} else {
indent.printf("%s = %s,", enumEntry.mEntryName, enumEntry.mEntryValue);
}
// write entry comment after member
indent.afterMemberComment(enumEntry.mEntryComment);
}
// write enum tail
indent.dec();
indent.puts("};");
}
}
/**
* Write an enum collection into given file.
* <p>
* Actually this is a wrapper of internal enum collection writer.
*
* @param filename The name of written file.
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for
* writing.
* @throws Exception
*/
public static void writeEnums(String filename, EnumsHelper.BEnumCollection prog) throws Exception {
// open file and write
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
internalWriteEnums(fs, prog);
fs.close();
}
/**
* Write a single enum into given file.
* <p>
* Actually this is a wrapper of internal enum collection writer.
*
* @param filename The name of written file.
* @param _enum {@linkplain EnumsHelper.BEnum} instance for writing.
* @throws Exception
*/
public static void writeEnum(String filename, EnumsHelper.BEnum _enum) throws Exception {
// create an collection from single enum declaration
// for suit the argument requirement of real writer.
EnumsHelper.BEnumCollection col = new EnumsHelper.BEnumCollection();
col.mEnums.add(_enum);
// open file and write
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
internalWriteEnums(fs, col);
fs.close();
}
// =========== C++ Enum Accessible Value Writer ===========
/**
* Internal real enum collection accessible value writer.
*
* @param writer {@linkplain java.io.OutputStreamWriter} instance for writing.
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for writing.
* @param parts The part of these enum declarations. It will indicate the
* namespace where find given enum collection.
* @throws Exception
*/
private static void internalWriteAccVals(OutputStreamWriter writer, EnumsHelper.BEnumCollection prog,
CommonHelper.CKParts parts) throws Exception {
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.Cpp);
// write type defination (just to let user know what the type is)
indent.puts("// struct GeneralReflection { const char8_t* mName; };");
indent.puts("// template<typename _Ty, std::enable_if_t<std::is_enum_v<_Ty>, int> = 0>");
indent.puts("// using GeneralReflectionArray = std::vector<std::pair<_Ty, GeneralReflection>>;");
indent.puts("");
indent.puts("");
indent.puts("");
// write declarations
for (EnumsHelper.BEnum benum : prog.mEnums) {
indent.printf("extern const GeneralReflectionArray<LibCmo::%s::%s> %s;",
CommonHelper.getCKPartsNamespace(parts), benum.mEnumName, benum.mEnumName);
}
indent.puts("");
indent.puts("");
indent.puts("");
// write implements
for (EnumsHelper.BEnum benum : prog.mEnums) {
// write enum desc header
indent.printf("const GeneralReflectionArray<LibCmo::%s::%s> %s {", CommonHelper.getCKPartsNamespace(parts),
benum.mEnumName, benum.mEnumName);
indent.inc();
// write enum desc entries
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
indent.printf("{ LibCmo::%s::%s::%s, { u8\"%s\" } },", CommonHelper.getCKPartsNamespace(parts),
benum.mEnumName, enumEntry.mEntryName, enumEntry.mEntryName);
}
// write enum tail
indent.dec();
indent.puts("};");
}
}
/**
* Write an enum collection accessible value into given file.
* <p>
* Actually this is a wrapper of internal enum collection accessible value
* writer.
*
* @param filename The name of written file.
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for
* writing.
* @param parts The part of these enum declarations.
* @throws Exception
*/
public static void writeAccVals(String filename, EnumsHelper.BEnumCollection prog, CommonHelper.CKParts parts)
throws Exception {
// open file and write
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
internalWriteAccVals(fs, prog, parts);
fs.close();
}
/**
* Write a single enum accessible value into given file.
* <p>
* Actually this is a wrapper of internal enum collection accessible value
* writer.
*
* @param filename The name of written file.
* @param _enum {@linkplain EnumsHelper.BEnum} instance for writing.
* @param parts The part of these enum declarations.
* @throws Exception
*/
public static void writeAccVal(String filename, EnumsHelper.BEnum _enum, CommonHelper.CKParts parts)
throws Exception {
// create a enum collection to fulfill the requirement of internal writer.
EnumsHelper.BEnumCollection col = new EnumsHelper.BEnumCollection();
col.mEnums.add(_enum);
// open file and write
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
internalWriteAccVals(fs, col, parts);
fs.close();
}
// =========== Specialized C++ Enum Accessible Value Writer ===========
// Only accessible value part of CERROR and CK_CLASSID need to be specialized.
// The enum self do not need special treat. Just write them normally.
/**
* Specialized CKERROR accessible value writer.
* <p>
* The declaration of CKERROR do not need special treat. It is okey to use
* common writer.
*
* @param filename The name of written file.
* @param errors The {@linkplain EnumsHelper.BEnum} instance storing CKERROR.
* @throws Exception
*/
public static void writeCkErrorAccVal(String filename, EnumsHelper.BEnum errors) throws Exception {
OutputStreamWriter writer = CommonHelper.openOutputFile(filename);
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.Cpp);
// write type defination (just to let user know what the type is)
indent.puts("// struct CkErrorReflection { const char8_t* mName; const char8_t* mDescription; };");
indent.puts("// using CkErrorReflectionArray = std::vector<std::pair<LibCmo::CK2::CKERROR, CkErrorReflection>>;");
indent.puts("");
indent.puts("");
indent.puts("");
// write implementation
indent.puts("const CkErrorReflectionArray CKERROR {");
indent.inc();
for (EnumsHelper.BEnumEntry entry : errors.mEntries) {
String comment = CommonHelper.escapeString(entry.mEntryComment);
if (comment == null)
comment = "";
indent.printf("{ LibCmo::CK2::CKERROR::%s, { u8\"%s\", u8\"%s\" } },", entry.mEntryName, entry.mEntryName,
comment);
}
indent.dec();
indent.puts("};");
writer.close();
}
/**
* Specialized CK_CLASSID accessible value writer.
* <p>
* The declaration of CK_CLASSID do not need special treat. It is okey to use
* common writer.
*
* @param filename The name of written file.
* @param classids The {@linkplain EnumsHelper.BEnum} instance storing
* CK_CLASSID.
* @throws Exception
*/
public static void writeCkClassidAccVal(String filename, EnumsHelper.BEnum classids) throws Exception {
OutputStreamWriter writer = CommonHelper.openOutputFile(filename);
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.Cpp);
// write type defination (just to let user know what the type is)
indent.puts("// struct CkClassidReflection { std::vector<const char8_t*> mHierarchy; };");
indent.puts("// using CkClassidReflectionArray = std::vector<std::pair<LibCmo::CK2::CK_CLASSID, CkClassidReflection>>;");
indent.puts("");
indent.puts("");
indent.puts("");
indent.puts("const CkClassidReflectionArray CK_CLASSID {");
indent.inc();
for (EnumsHelper.BEnumEntry entry : classids.mEntries) {
EnumsHelper.BHierarchyEnumEntry specialized = (EnumsHelper.BHierarchyEnumEntry) entry;
String hierarchy = specialized.mHierarchy.stream().map(value -> value.mEntryName)
.collect(Collectors.joining("\", u8\""));
indent.printf("{ LibCmo::CK2::CK_CLASSID::%s, { { u8\"%s\" } } },", entry.mEntryName, hierarchy);
}
indent.dec();
indent.puts("};");
writer.close();
}
}

View File

@@ -0,0 +1,72 @@
import org.antlr.v4.runtime.*;
/**
* The generic walker for collecting defines as a enum.
*/
public class DefinesWalker extends CKDefinesParserBaseListener {
public DefinesWalker(BufferedTokenStream tokenStream) {
mCommentsFinder = new CommentsFinder(tokenStream);
mResult = null;
mCurrentEnum = null;
mCurrentEntry = null;
}
public EnumsHelper.BEnum getEnum() {
return mResult;
}
private CommentsFinder mCommentsFinder;
private EnumsHelper.BEnum mResult;
private EnumsHelper.BEnum mCurrentEnum;
private EnumsHelper.BEnumEntry mCurrentEntry;
@Override
public void enterProg(CKDefinesParser.ProgContext ctx) {
mCurrentEnum = new EnumsHelper.BEnum();
}
@Override
public void exitProg(CKDefinesParser.ProgContext ctx) {
mResult = mCurrentEnum;
mCurrentEnum = null;
}
@Override
public void enterDefinePair(CKDefinesParser.DefinePairContext ctx) {
mCurrentEntry = new EnumsHelper.BEnumEntry();
}
@Override
public void exitDefinePair(CKDefinesParser.DefinePairContext ctx) {
// set values
mCurrentEntry.mEntryName = ctx.CKGENERAL_ID(0).getText();
mCurrentEntry.mEntryComment = mCommentsFinder.getComment(ctx.getStart(), ctx.getStop());
if (ctx.CKGENERAL_NUM() == null) {
// define with id
mCurrentEntry.mEntryValue = ctx.CKGENERAL_ID(1).getText();
} else {
// define with number
String num = ctx.CKGENERAL_NUM().getText();
mCurrentEntry.mEntryValue = num;
// check whether this enum can be unsigned
if (CommonHelper.isNegativeNumber(num)) {
mCurrentEnum.mCanUnsigned = false;
}
// if the number is in hex form, this enum MIGHT have flags feature
if (CommonHelper.isHexNumber(num)) {
mCurrentEnum.mUseFlags = true;
}
}
// add entry
mCurrentEnum.mEntries.add(mCurrentEntry);
mCurrentEntry = null;
}
}

View File

@@ -0,0 +1,72 @@
import java.util.Vector;
public class EnumsHelper {
/**
* The struct to describe the entry of an enum.
*/
public static class BEnumEntry {
public BEnumEntry() {
mEntryName = null;
mEntryValue = null;
mEntryComment = null;
}
/** The name of this entry. Can not be null. */
public String mEntryName;
/** The value of this entry. null if this entry do not have explicit value. */
public String mEntryValue;
/** The comment of this entry. null if no comment. */
public String mEntryComment;
}
/**
* The specialized EnumEntry type which can store extra hierarchy info.
* Used in CK_CLASSID parsing.
*/
public static class BHierarchyEnumEntry extends BEnumEntry {
public BHierarchyEnumEntry() {
super();
mHierarchy = new Vector<BHierarchyEnumEntry>();
}
/** The list to store this CK_CLASSID inheritance relationship. */
public Vector<BHierarchyEnumEntry> mHierarchy;
}
/**
* The struct to describe an enum.
*/
public static class BEnum {
public BEnum() {
mEnumName = null;
mEnumComment = null;
mCanUnsigned = true;
mUseFlags = false;
mEntries = new Vector<BEnumEntry>();
}
/** The name of this enum. Can not be null. */
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;
/** The list to store entries of this enum. */
public Vector<BEnumEntry> mEntries;
}
/**
* The struct to describe a collection of enums.
*/
public static class BEnumCollection {
public BEnumCollection() {
mEnums = new Vector<BEnum>();
}
/** The list to store enums. */
public Vector<BEnum> mEnums;
}
}

View File

@@ -0,0 +1,131 @@
import java.util.List;
import java.util.stream.Collectors;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
/**
* The generic walker for collecting multiple enums.
*/
public class EnumsWalker extends CKEnumsParserBaseListener {
public EnumsWalker(BufferedTokenStream tokenStream) {
mTokenStream = tokenStream;
mCommentsFinder = new CommentsFinder(tokenStream);
mResult = null;
mCurrentProg = null;
mCurrentEnum = null;
mCurrentEntry = null;
}
public EnumsHelper.BEnumCollection getEnums() {
return mResult;
}
private String getEnumComment(Token enumHead) {
return CommonHelper
.cutComments(CommonHelper.getPreChannelTokens(mTokenStream, enumHead, CKGeneralLexer.COMMENTS));
}
private BufferedTokenStream mTokenStream;
private CommentsFinder mCommentsFinder;
private EnumsHelper.BEnumCollection mResult;
private EnumsHelper.BEnumCollection mCurrentProg;
private EnumsHelper.BEnum mCurrentEnum;
private EnumsHelper.BEnumEntry mCurrentEntry;
@Override
public void enterProg(CKEnumsParser.ProgContext ctx) {
mCurrentProg = new EnumsHelper.BEnumCollection();
}
@Override
public void exitProg(CKEnumsParser.ProgContext ctx) {
mResult = mCurrentProg;
mCurrentProg = null;
}
@Override
public void enterEnumBody(CKEnumsParser.EnumBodyContext ctx) {
mCurrentEnum = new EnumsHelper.BEnum();
}
@Override
public void exitEnumBody(CKEnumsParser.EnumBodyContext ctx) {
// get enum comment
mCurrentEnum.mEnumComment = getEnumComment(ctx.getStart());
// get the last name (for typedef case)
List<TerminalNode> allNames = ctx.CKGENERAL_ID();
mCurrentEnum.mEnumName = allNames.get(allNames.size() - 1).getText();
mCurrentProg.mEnums.add(mCurrentEnum);
mCurrentEnum = null;
}
@Override
public void enterEntryPair(CKEnumsParser.EntryPairContext ctx) {
mCurrentEntry = new EnumsHelper.BEnumEntry();
}
@Override
public void exitEntryPair(CKEnumsParser.EntryPairContext ctx) {
// get entry comment
mCurrentEntry.mEntryComment = mCommentsFinder.getComment(ctx.getStart(), ctx.getStop());
// get entry name
mCurrentEntry.mEntryName = ctx.CKGENERAL_ID().getText();
mCurrentEnum.mEntries.add(mCurrentEntry);
mCurrentEntry = null;
}
@Override
public void exitEntryDirectValue(CKEnumsParser.EntryDirectValueContext ctx) {
// get all numbers
List<TerminalNode> nums = ctx.CKGENERAL_NUM();
switch (nums.size()) {
case 1: {
// set value
TerminalNode node = nums.get(0);
mCurrentEntry.mEntryValue = node.getText();
// check whether this enum can be unsigned
if (CommonHelper.isNegativeNumber(node.getText())) {
mCurrentEnum.mCanUnsigned = false;
}
// if the number is in hex form, this enum MIGHT have flags feature
if (CommonHelper.isHexNumber(node.getText())) {
mCurrentEnum.mUseFlags = true;
}
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());
}
}
@Override
public void exitEntryRelativeValue(CKEnumsParser.EntryRelativeValueContext ctx) {
// get all identifiers and join them
mCurrentEntry.mEntryValue = ctx.CKGENERAL_ID().stream().map(value -> value.getText())
.collect(Collectors.joining(" | "));
// | operator appears. this enum must have flags feature
mCurrentEnum.mUseFlags = true;
}
}

View File

@@ -0,0 +1,116 @@
import java.io.OutputStreamWriter;
public class IndentHelper {
public IndentHelper(OutputStreamWriter writer, CommonHelper.LangType lang_type) {
mIndent = 0;
mLangType = lang_type;
mWriter = writer;
// set indent chars
switch (mLangType) {
case Cpp:
case CSharp:
mIndentChars = CommonHelper.getIndentString(true);
break;
case Python:
mIndentChars = CommonHelper.getIndentString(false);
break;
default:
mIndentChars = "";
break;
}
}
private int mIndent;
private CommonHelper.LangType mLangType;
private String mIndentChars;
private OutputStreamWriter mWriter;
private void rawIndent() throws Exception {
for (int i = 0; i < mIndent; ++i) {
mWriter.write(mIndentChars);
}
}
public void inc() {
++mIndent;
}
public void dec() {
--mIndent;
}
public void indent() throws Exception {
mWriter.write(System.lineSeparator());
rawIndent();
}
public void puts(String data) throws Exception {
indent();
mWriter.write(data);
}
public void printf(String fmt, Object... args) throws Exception {
indent();
mWriter.write(String.format(fmt, args));
}
/**
* Write standard Doxygen document comment.
* <p>
* Usually be called before writing content.
*
* @param comment
* @throws Exception
*/
public void briefComment(String comment) throws Exception {
if (comment == null)
return;
switch (mLangType) {
case Cpp:
case CSharp:
puts("/**");
mWriter.write(System.lineSeparator());
mWriter.write(CommonHelper.reindentBlockString(comment, true, mIndent));
puts(" */");
break;
case Python:
puts("\"\"\"!");
mWriter.write(System.lineSeparator());
mWriter.write(CommonHelper.reindentBlockString(comment, false, mIndent));
puts("\"\"\"");
break;
}
}
/**
* Write suffix style Doxygen document comment.
* <p>
* Usually be called after writing content.
*
* @param comment
* @throws Exception
*/
public void afterMemberComment(String comment) throws Exception {
if (comment == null)
return;
mWriter.write(mIndentChars);
switch (mLangType) {
case Cpp:
case CSharp:
mWriter.write(String.format("/**< %s */", CommonHelper.removeEol(comment)));
break;
case Python:
mWriter.write(String.format("##< %s", CommonHelper.removeEol(comment)));
break;
}
}
}

View File

@@ -0,0 +1,73 @@
import java.io.OutputStreamWriter;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class JsonWriter {
private static JsonObject writeBEnumEntry(EnumsHelper.BEnumEntry enumEntry) {
JsonObject data = new JsonObject();
data.addProperty("name", enumEntry.mEntryName);
data.addProperty("value", enumEntry.mEntryValue);
data.addProperty("comment", enumEntry.mEntryComment);
// Export hierarchy if possible
if (enumEntry instanceof EnumsHelper.BHierarchyEnumEntry hierarchyEnumEntry) {
// We only export name in hierarchy.
// Otherwise we may cause recursive calling.
JsonArray entries = new JsonArray();
for (EnumsHelper.BHierarchyEnumEntry subEntry : hierarchyEnumEntry.mHierarchy) {
entries.add(subEntry.mEntryName);
}
data.add("hierarchy", entries);
}
return data;
}
private static JsonObject writeBEnum(EnumsHelper.BEnum benum) {
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);
JsonArray entries = new JsonArray();
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
entries.add(writeBEnumEntry(enumEntry));
}
data.add("entries", entries);
return data;
}
private static JsonArray writeBEnumCollection(EnumsHelper.BEnumCollection enumCollection) {
JsonArray data = new JsonArray();
for (EnumsHelper.BEnum benum : enumCollection.mEnums) {
data.add(writeBEnum(benum));
}
return data;
}
private static void writeJson(String filename, EnumsHelper.BEnumCollection enumCollection) throws Exception {
OutputStreamWriter writer = CommonHelper.openOutputFile(filename);
//Gson gsonInstance = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
Gson gsonInstance = new GsonBuilder().disableHtmlEscaping().create();
writer.write(gsonInstance.toJson(writeBEnumCollection(enumCollection)));
writer.close();
}
public static void writeEnums(String filename, EnumsHelper.BEnumCollection enumCollection) throws Exception {
writeJson(filename, enumCollection);
}
public static void writeEnum(String filename, EnumsHelper.BEnum benum) throws Exception {
// Build collection manually.
EnumsHelper.BEnumCollection collection = new EnumsHelper.BEnumCollection();
collection.mEnums.add(benum);
// Call underlying writer
writeEnums(filename, collection);
}
}

View File

@@ -0,0 +1,196 @@
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
public class MainRunner {
/**
* Extract an enums collection from given file.
* <p>
* This function is the most commonly used function for extracting enums.
* <p>
* This function is used for a file which only contain enum declarations. This
* is not suit for extracting CKERROR and CK_CLASSID. For these declarations,
* please use their specialized extractor as described following.
*
* @param filename The file name relative to input directory for reading.
* @return An {@linkplain EnumsHelper.BEnumCollection} instance.
* @throws Exception
*/
private static EnumsHelper.BEnumCollection getEnumsCollection(String filename) throws Exception {
String infile = CommonHelper.getInputFilePath(filename);
CommonHelper.InputFilePair pair = CommonHelper.openInputFile(infile);
CKGeneralLexer lexer = new CKGeneralLexer(pair.mAntlrStream);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CKEnumsParser parser = new CKEnumsParser(tokens);
ParseTree tree = parser.prog();
ParseTreeWalker walker = new ParseTreeWalker();
EnumsWalker worker = new EnumsWalker(tokens);
walker.walk(worker, tree);
pair.mUnderlyingStream.close();
return worker.getEnums();
}
/**
* Extract a series of "#define" syntax as an enum.
* <p>
* This function will assume that given file only contain C++ "#define" syntax.
* After reading it, it will re-organize it as an enum and return. This only is
* used by CKERROR now. But it suit for more scenarios if there are something
* like CKERROR in future.
*
* @param filename The file name relative to input directory for reading.
* @param assignedEnumName The desired name of organized enum instance.
* Contemporary this field should always be "CKERROR"
* because no one else is using it.
* @return An {@linkplain EnumsHelper.BEnum} instance.
* @throws Exception
*/
private static EnumsHelper.BEnum organiseDefines(String filename, String assignedEnumName) throws Exception {
String infile = CommonHelper.getInputFilePath(filename);
CommonHelper.InputFilePair pair = CommonHelper.openInputFile(infile);
CKGeneralLexer lexer = new CKGeneralLexer(pair.mAntlrStream);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CKDefinesParser parser = new CKDefinesParser(tokens);
ParseTree tree = parser.prog();
ParseTreeWalker walker = new ParseTreeWalker();
DefinesWalker worker = new DefinesWalker(tokens);
walker.walk(worker, tree);
pair.mUnderlyingStream.close();
EnumsHelper.BEnum result = worker.getEnum();
result.mEnumName = assignedEnumName;
return result;
}
/**
* Extract a series of macro define as an enum, considering its indent to build
* hierarchy.
* <p>
* This is specialized enum extractor of CK_CLASSID. The given file should use a
* series "#define" syntax to describe enum, and use Tab as the indent before
* each "#define" syntax to indicate its hierarchy.
*
* @param filename The file name relative to input directory for reading.
* @return An {@linkplain EnumsHelper.BEnum} instance. Actually it is an
* instance to {@linkplain EnumsHelper.BEnum} whose entries is
* {@linkplain EnumsHelper.BHierarchyEnumEntry}, the child class of
* {@linkplain EnumsHelper.BEnumEntry} (the entry type of common
* {@linkplain EnumsHelper.BEnum}) with extra hierarchy infos.
* @throws Exception
*/
private static EnumsHelper.BEnum organiseClassid(String filename) throws Exception {
String infile = CommonHelper.getInputFilePath(filename);
CommonHelper.InputFilePair pair = CommonHelper.openInputFile(infile);
CKGeneralLexer lexer = new CKGeneralLexer(pair.mAntlrStream);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CKDefinesParser parser = new CKDefinesParser(tokens);
ParseTree tree = parser.prog();
ParseTreeWalker walker = new ParseTreeWalker();
ClassidWalker worker = new ClassidWalker(tokens);
walker.walk(worker, tree);
pair.mUnderlyingStream.close();
EnumsHelper.BEnum result = worker.getEnum();
result.mEnumName = "CK_CLASSID";
return result;
}
public static void main(String[] args) throws Exception {
// =========== CKERROR ===========
EnumsHelper.BEnum ckerror = organiseDefines("CKERROR.txt", "CKERROR");
JsonWriter.writeEnum(CommonHelper.getOutputFilePath("CKERROR.json"), ckerror);
// CppWriter.writeEnum("dest/CKERROR.hpp", ckerror);
// PythonWriter.writeEnum("dest/CKERROR.py", ckerror);
// CSharpWriter.writeEnum("dest/CKERROR.cs", ckerror);
// CppWriter.writeCkErrorAccVal("dest/CKERROR.AccVal.hpp", ckerror);
// PythonWriter.writeAccVal("dest/CKERROR.AccVal.py", ckerror);
// CSharpWriter.writeAccVal("dest/CKERROR.AccVal.cs", ckerror);
// =========== CK_CLASSID ===========
EnumsHelper.BEnum classid = organiseClassid("CK_CLASSID.txt");
JsonWriter.writeEnum(CommonHelper.getOutputFilePath("CK_CLASSID.json"), classid);
// CppWriter.writeEnum("dest/CK_CLASSID.hpp", classid);
// PythonWriter.writeEnum("dest/CK_CLASSID.py", classid);
// CSharpWriter.writeEnum("dest/CK_CLASSID.cs", classid);
// CppWriter.writeCkClassidAccVal("dest/CK_CLASSID.AccVal.hpp", classid);
// PythonWriter.writeAccVal("dest/CK_CLASSID.AccVal.py", classid);
// =========== Define2 ===========
// Define2 do not need annotation output.
// Because they are CKStateChunk used value which are not exposed to outside.
EnumsHelper.BEnumCollection def2 = getEnumsCollection("Defines2.txt");
JsonWriter.writeEnums(CommonHelper.getOutputFilePath("Defines2.json"), def2);
// CppWriter.writeEnums("dest/Defines2.hpp", def2);
// PythonWriter.writeEnums("dest/Defines2.py", def2);
// CSharpWriter.writeEnums("dest/Defines2.cs", def2);
// =========== Combined enums ===========
EnumsHelper.BEnumCollection ck2Enums = getEnumsCollection("CKEnums.txt"),
vxEnums = getEnumsCollection("VxEnums.txt");
JsonWriter.writeEnums(CommonHelper.getOutputFilePath("CKEnums.json"), ck2Enums);
JsonWriter.writeEnums(CommonHelper.getOutputFilePath("VxEnums.json"), vxEnums);
// CppWriter.writeEnums("dest/CKEnums.hpp", ck2Enums);
// PythonWriter.writeEnums("dest/CKEnums.py", ck2Enums);
// CSharpWriter.writeEnums("dest/CKEnums.cs", ck2Enums);
// CppWriter.writeAccVals("dest/CKEnums.AccVal.hpp", ck2Enums, CommonHelper.CKParts.CK2);
// PythonWriter.writeAccVals("dest/CKEnums.AccVal.py", ck2Enums);
// CSharpWriter.writeAccVals("dest/CKEnums.AccVal.cs", ck2Enums);
// CppWriter.writeEnums("dest/VxEnums.hpp", vxEnums);
// PythonWriter.writeEnums("dest/VxEnums.py", vxEnums);
// CSharpWriter.writeEnums("dest/VxEnums.cs", vxEnums);
// CppWriter.writeAccVals("dest/VxEnums.AccVal.hpp", vxEnums, CommonHelper.CKParts.VxMath);
// PythonWriter.writeAccVals("dest/VxEnums.AccVal.py", vxEnums);
// CSharpWriter.writeAccVals("dest/VxEnums.AccVal.cs", vxEnums);
// =========== Single enums ===========
EnumsHelper.BEnum single;
single = organiseDefines("CK_STATECHUNK_CHUNKVERSION.txt", "CK_STATECHUNK_CHUNKVERSION");
JsonWriter.writeEnum(CommonHelper.getOutputFilePath("CK_STATECHUNK_CHUNKVERSION.json"), single);
// CppWriter.writeEnum("dest/CK_STATECHUNK_CHUNKVERSION.hpp", single);
// PythonWriter.writeEnum("dest/CK_STATECHUNK_CHUNKVERSION.py", single);
// CSharpWriter.writeEnum("dest/CK_STATECHUNK_CHUNKVERSION.cs", single);
// CppWriter.writeAccVal("dest/CK_STATECHUNK_CHUNKVERSION.AccVal.hpp", single, CommonHelper.CKParts.CK2);
// PythonWriter.writeAccVal("dest/CK_STATECHUNK_CHUNKVERSION.AccVal.py", single);
// CSharpWriter.writeAccVal("dest/CK_STATECHUNK_CHUNKVERSION.AccVal.cs", single);
single = organiseDefines("CK_STATECHUNK_DATAVERSION.txt", "CK_STATECHUNK_DATAVERSION");
JsonWriter.writeEnum(CommonHelper.getOutputFilePath("CK_STATECHUNK_DATAVERSION.json"), single);
// CppWriter.writeEnum("dest/CK_STATECHUNK_DATAVERSION.hpp", single);
// PythonWriter.writeEnum("dest/CK_STATECHUNK_DATAVERSION.py", single);
// CSharpWriter.writeEnum("dest/CK_STATECHUNK_DATAVERSION.cs", single);
// CppWriter.writeAccVal("dest/CK_STATECHUNK_DATAVERSION.AccVal.hpp", single, CommonHelper.CKParts.CK2);
// PythonWriter.writeAccVal("dest/CK_STATECHUNK_DATAVERSION.AccVal.py", single);
// CSharpWriter.writeAccVal("dest/CK_STATECHUNK_DATAVERSION.AccVal.cs", single);
single = organiseDefines("CK_BITMAPDATA_FLAGS.txt", "CK_BITMAPDATA_FLAGS");
JsonWriter.writeEnum(CommonHelper.getOutputFilePath("CK_BITMAPDATA_FLAGS.json"), single);
// CppWriter.writeEnum("dest/CK_BITMAPDATA_FLAGS.hpp", single);
// PythonWriter.writeEnum("dest/CK_BITMAPDATA_FLAGS.py", single);
// CSharpWriter.writeEnum("dest/CK_BITMAPDATA_FLAGS.cs", single);
// CppWriter.writeAccVal("dest/CK_BITMAPDATA_FLAGS.AccVal.hpp", single, CommonHelper.CKParts.CK2);
// PythonWriter.writeAccVal("dest/CK_BITMAPDATA_FLAGS.AccVal.py", single);
// CSharpWriter.writeAccVal("dest/CK_BITMAPDATA_FLAGS.AccVal.cs", single);
single = organiseDefines("CK_CAMERA_PROJECTION.txt", "CK_CAMERA_PROJECTION");
JsonWriter.writeEnum(CommonHelper.getOutputFilePath("CK_CAMERA_PROJECTION.json"), single);
// CppWriter.writeEnum("dest/CK_CAMERA_PROJECTION.hpp", single);
// PythonWriter.writeEnum("dest/CK_CAMERA_PROJECTION.py", single);
// CSharpWriter.writeEnum("dest/CK_CAMERA_PROJECTION.cs", single);
// CppWriter.writeAccVal("dest/CK_CAMERA_PROJECTION.AccVal.hpp", single, CommonHelper.CKParts.CK2);
// PythonWriter.writeAccVal("dest/CK_CAMERA_PROJECTION.AccVal.py", single);
// CSharpWriter.writeAccVal("dest/CK_CAMERA_PROJECTION.AccVal.cs", single);
// print message.
System.out.println("Done");
}
}

View File

@@ -0,0 +1,185 @@
import java.io.OutputStreamWriter;
import java.util.Locale;
/**
* Write enum declarations and accessible value into Python format.
*/
public class PythonWriter {
// =========== Python Enum Declaration Writer ===========
/**
* Internal real enum declaration writer.
*
* @param writer {@linkplain java.io.OutputStreamWriter} instance for writing.
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for writing.
* @throws Exception
*/
private static void internalWriteEnums(OutputStreamWriter writer, EnumsHelper.BEnumCollection prog)
throws Exception {
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.Python);
for (EnumsHelper.BEnum benum : prog.mEnums) {
// write enum start
indent.printf("class %s(enum.IntEnum):", benum.mEnumName);
indent.inc();
// write enum comment
indent.briefComment(benum.mEnumComment);
// write enum entries
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
// write entry self
if (enumEntry.mEntryValue == null) {
indent.printf("%s = auto()", enumEntry.mEntryName);
} else {
indent.printf("%s = %s", enumEntry.mEntryName,
CommonHelper.convertToPythonNumber(enumEntry.mEntryValue));
}
// write entry comment after member
indent.afterMemberComment(enumEntry.mEntryComment);
}
// enum tail
indent.dec();
}
}
/**
* Write an enum declaration collection into given file.
* <p>
* Actually this is a wrapper of internal enum declaration collection writer.
*
* @param filename The name of written file.
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for
* writing.
* @throws Exception
*/
public static void writeEnums(String filename, EnumsHelper.BEnumCollection prog) throws Exception {
// open file and write
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
internalWriteEnums(fs, prog);
fs.close();
}
/**
* Write a single enum declaration into given file.
* <p>
* Actually this is a wrapper of internal enum declaration collection writer.
*
* @param filename The name of written file.
* @param _enum {@linkplain EnumsHelper.BEnum} instance for writing.
* @throws Exception
*/
public static void writeEnum(String filename, EnumsHelper.BEnum _enum) throws Exception {
// create collection from single enum
EnumsHelper.BEnumCollection col = new EnumsHelper.BEnumCollection();
col.mEnums.add(_enum);
// open file and write
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
internalWriteEnums(fs, col);
fs.close();
}
// =========== Python Enum Accessible Value Writer ===========
/**
* Try generate human readable name from enum entry name.
* <p>
* This function is only served for Python code generation.
* <p>
* As you noticed, almost entries of CK enums are fully capital and splitted by
* underline. This is really not good for human reading, especially those who
* are not programmer. So this function will try give these programmer-oriented
* entry name a human readable name as its display name. However, this extract
* method is not perfect. It simply do some split and replacement so the
* generated content may still not good for reader.
*
* @param entry_name The name of enum entry
* @return A human readable entry name. No guaranteen that return value is must
* human readable.
*/
private static String extractHumanReadableEntryName(String entry_name) {
// remove first part (any content before underline '_')
entry_name = entry_name.replaceFirst("^[a-zA-Z0-9]+_", "");
// lower all chars except first char
if (entry_name.length() < 1)
return entry_name;
else
return entry_name.substring(0, 1) + entry_name.substring(1).toLowerCase(Locale.ROOT);
}
/**
* Internal real enum accessible value writer.
*
* @param writer {@linkplain java.io.OutputStreamWriter} instance for writing.
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for writing.
* @throws Exception
*/
private static void internalWriteAccVals(OutputStreamWriter writer, EnumsHelper.BEnumCollection prog)
throws Exception {
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.Python);
// write implements
for (EnumsHelper.BEnum benum : prog.mEnums) {
// write enum desc header
indent.printf("g_Annotation_%s: dict[int, EnumAnnotation] = {", benum.mEnumName);
indent.inc();
// write enum desc entries
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
String comment = "";
if (enumEntry.mEntryComment != null) {
comment = CommonHelper.escapeString(enumEntry.mEntryComment);
}
indent.printf("%s.%s.value: EnumAnnotation(\"%s\", \"%s\"),", benum.mEnumName, enumEntry.mEntryName,
extractHumanReadableEntryName(enumEntry.mEntryName), comment);
}
// write enum tail
indent.dec();
indent.puts("}");
}
}
/**
* Write an enum accessible value collection into given file.
* <p>
* Actually this is a wrapper of internal enum accessible value collection
* writer.
*
* @param filename The name of written file.
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for
* writing.
* @throws Exception
*/
public static void writeAccVals(String filename, EnumsHelper.BEnumCollection prog) throws Exception {
// open file and write
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
internalWriteAccVals(fs, prog);
fs.close();
}
/**
* Write a single enum accessible value into given file.
* <p>
* Actually this is a wrapper of internal enum accessible value collection
* writer.
*
* @param filename The name of written file.
* @param _enum {@linkplain EnumsHelper.BEnum} instance for writing.
* @throws Exception
*/
public static void writeAccVal(String filename, EnumsHelper.BEnum _enum) throws Exception {
// create a collection with single enum.
EnumsHelper.BEnumCollection col = new EnumsHelper.BEnumCollection();
col.mEnums.add(_enum);
// open file and write
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
internalWriteAccVals(fs, col);
fs.close();
}
}