import java.io.OutputStreamWriter; import java.util.List; import java.util.Vector; import java.util.stream.Collectors; import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.tree.*; public class CKEnumRunner { public static class EnumEntry_t { public EnumEntry_t() { mEntryName = null; mEntryValue = null; mEntryComment = null; } public String mEntryName; public String mEntryValue; // setting to null mean this entry do not have specified value. public String mEntryComment; } public static class Enum_t { public Enum_t() { mEnumName = null; mEnumComment = null; mCanUnsigned = true; mEntries = new Vector(); } public String mEnumName; public String mEnumComment; public boolean mCanUnsigned; public Vector mEntries; } public static class EnumWalker extends CKEnumParserBaseListener { public EnumWalker(BufferedTokenStream tokenStream) { mTokenStream = tokenStream; mCommentsHelper = new CKEnumCommentsHelper(tokenStream); mResult = null; mCurrentProg = null; mCurrentEnum = null; mCurrentEntry = null; } public List getResult() { return mResult; } private BufferedTokenStream mTokenStream; private CKEnumCommentsHelper mCommentsHelper; private Vector mResult; private Vector mCurrentProg; private Enum_t mCurrentEnum; private EnumEntry_t mCurrentEntry; @Override public void enterProg(CKEnumParser.ProgContext ctx) { mCurrentProg = new Vector(); } @Override public void exitProg(CKEnumParser.ProgContext ctx) { mResult = mCurrentProg; mCurrentProg = null; } @Override public void enterEnumBody(CKEnumParser.EnumBodyContext ctx) { mCurrentEnum = new Enum_t(); } @Override public void exitEnumBody(CKEnumParser.EnumBodyContext ctx) { // get enum comment mCurrentEnum.mEnumComment = CKCommonHelper.cutComment( CKCommonHelper.getPreChannelToken(mTokenStream, ctx.getStart(), CKGeneralLexer.COMMENTS)); // get the last name (for typedef case) List allNames = ctx.CKGENERAL_ID(); mCurrentEnum.mEnumName = allNames.get(allNames.size() - 1).getText(); mCurrentProg.add(mCurrentEnum); mCurrentEnum = null; } @Override public void enterEntryPair(CKEnumParser.EntryPairContext ctx) { mCurrentEntry = new EnumEntry_t(); } @Override public void exitEntryPair(CKEnumParser.EntryPairContext ctx) { // get entry comment mCurrentEntry.mEntryComment = mCommentsHelper.getComment(ctx.getStart(), ctx.getStop()); // get entry name mCurrentEntry.mEntryName = ctx.CKGENERAL_ID().getText(); mCurrentEnum.mEntries.add(mCurrentEntry); mCurrentEntry = null; } @Override public void exitEntryDirectValue(CKEnumParser.EntryDirectValueContext ctx) { // get all numbers List nums = ctx.CKGENERAL_NUM(); switch (nums.size()) { case 1: { TerminalNode node = nums.get(0); // check whether target is minus number if (CKCommonHelper.isNegtiveNumber(node.getText())) { mCurrentEnum.mCanUnsigned = false; } // set value mCurrentEntry.mEntryValue = node.getText(); break; } case 2: { TerminalNode num = nums.get(0), offset = nums.get(1); // set value mCurrentEntry.mEntryValue = String.format("{} << {}", num.getText(), offset.getText()); break; } default: throw new IllegalArgumentException("Unexpected value: " + nums.size()); } } @Override public void exitEntryRelativeValue(CKEnumParser.EntryRelativeValueContext ctx) { // get all identifiers and join them mCurrentEntry.mEntryValue = String.join(" | ", ctx.CKGENERAL_ID().stream().map(value -> value.getText()).collect(Collectors.toList())); } } public static class EnumWriter { private static void writeEnum(CKIndentHelper indent, List prog) throws Exception { for (Enum_t enum_t : prog) { // write enum comment indent.briefComment(enum_t.mEnumComment); // write enum start indent.printf("enum class %s : %s {", enum_t.mEnumName, CKCommonHelper.getEnumUnderlyingType(enum_t.mCanUnsigned)); indent.inc(); // write enum entries for (EnumEntry_t enumEntry_t : enum_t.mEntries) { // write entry self if (enumEntry_t.mEntryValue == null) { indent.printf("%s,", enumEntry_t.mEntryName); } else { indent.printf("%s = %s,", enumEntry_t.mEntryName, enumEntry_t.mEntryValue); } // write entry comment after member indent.afterMemberComment(enumEntry_t.mEntryComment); } // write enum tail indent.dec(); indent.puts("};"); } } public static void writeEnums(OutputStreamWriter writer, List ck2_prog, List vxmath_prog) throws Exception { CKIndentHelper indent = new CKIndentHelper(writer); indent.puts("#pragma once"); indent.puts("#include "); indent.puts("namespace LibCmo::CK2 {"); indent.inc(); writeEnum(indent, ck2_prog); indent.dec(); indent.puts("}"); indent.puts("namespace LibCmo::VxMath {"); indent.inc(); writeEnum(indent, vxmath_prog); indent.dec(); indent.puts("}"); } private static void writeAccessibleValue(CKIndentHelper indent, String parts, List prog) throws Exception { for (Enum_t enum_t : prog) { // write enum desc header indent.printf("const EnumNameofArray {} {", parts, enum_t.mEnumName, enum_t.mEnumName); indent.inc(); // write enum desc entries for (EnumEntry_t enumEntry_t : enum_t.mEntries) { indent.printf("{ LibCmo::%s::%s::%s, \"%s\" },", parts, enum_t.mEnumName, enumEntry_t.mEntryName, enumEntry_t.mEntryName); } // write enum tail indent.dec(); indent.puts("};"); } } public static void writeAccessibleValues(OutputStreamWriter writer, List ck2_prog, List vxmath_prog) throws Exception { CKIndentHelper indent = new CKIndentHelper(writer); indent.puts("#pragma once"); indent.puts("#include \"CKEnums.hpp\""); indent.puts("#include "); indent.puts("#include "); indent.puts("#include "); indent.puts("namespace Unvirt::AccessibleValue::EnumDesc {"); indent.inc(); indent.puts("namespace CK2 {"); indent.inc(); writeAccessibleValue(indent, "CK2", ck2_prog); indent.dec(); indent.puts("}"); indent.puts("namespace VxMath {"); indent.inc(); writeAccessibleValue(indent, "VxMath", vxmath_prog); indent.dec(); indent.puts("}"); indent.dec(); indent.puts("}"); } } private static List getProg(String infile) throws Exception { CKCommonHelper.InputFilePair pair = CKCommonHelper.openInputFile(infile); CKGeneralLexer lexer = new CKGeneralLexer(pair.mAntlrStream); CommonTokenStream tokens = new CommonTokenStream(lexer); CKEnumParser parser = new CKEnumParser(tokens); ParseTree tree = parser.prog(); ParseTreeWalker walker = new ParseTreeWalker(); EnumWalker worker = new EnumWalker(tokens); walker.walk(worker, tree); pair.mUnderlyingStream.close(); return worker.getResult(); } public static void run(String inCk2Enums, String inVxEnums, String outEnums, String outAccessibleValues) throws Exception { List ck2prog = getProg(inCk2Enums); List vxprog = getProg(inVxEnums); OutputStreamWriter fs = CKCommonHelper.openOutputFile(outEnums); EnumWriter.writeEnums(fs, ck2prog, vxprog); fs.close(); fs = CKCommonHelper.openOutputFile(outAccessibleValues); EnumWriter.writeAccessibleValues(fs, ck2prog, vxprog); fs.close(); } }