diff --git a/.gitignore b/.gitignore index 3187246..add8f5e 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ temp/ ## Special Treat of CodeGen CodeGen.old/ CodeGen/dest/*.txt +CodeGen/.* +CodeGen/*.class CodeGen/CKGeneralLexer*.* CodeGen/CKEnumParser*.* diff --git a/CodeGen/CKCommonHelper.java b/CodeGen/CKCommonHelper.java new file mode 100644 index 0000000..c54d716 --- /dev/null +++ b/CodeGen/CKCommonHelper.java @@ -0,0 +1,76 @@ +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.antlr.v4.runtime.*; + +public class CKCommonHelper { + + public static Token getPreChannelToken(BufferedTokenStream stream, Token token, int channel) { + List 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 tokens = stream.getHiddenTokensToRight(token.getTokenIndex(), channel); + if (tokens == null) + return null; + return tokens.get(0); + } + + public static String cutComment(Token comment) { + if (comment == null) + return null; + + switch (comment.getType()) { + case CKGeneralLexer.CKGENERAL_LINE_COMMENT: { + return comment.getText().substring(2); + } + case CKGeneralLexer.CKGENERAL_BLOCK_COMMENT: { + String cache = comment.getText(); + return cache.substring(2, cache.length() - 4); + } + default: + return comment.getText(); + } + } + + public static boolean isNegtiveNumber(String numstr) { + return numstr.startsWith("-"); + } + + enum CKParts { + CK2, VxMath + } + + public static String getCKPartsNamespace(CKParts parts) { + switch (parts) { + case CK2: + return "CK2"; + case VxMath: + return "VxMath"; + default: + throw new IllegalArgumentException("Unexpected value: " + parts); + } + } + + public static String getEnumUnderlayingType(boolean canUnsigned) { + return canUnsigned ? "uint32_t" : "int32_t"; + } + + public static OutputStreamWriter openOutputFile(String filename) throws Exception { + FileOutputStream fs = new FileOutputStream(filename); + return new OutputStreamWriter(fs, StandardCharsets.UTF_8); + } + + public static String escapeString(String strl) { + + } + +} diff --git a/CodeGen/CKEnumCommentsHelper.java b/CodeGen/CKEnumCommentsHelper.java new file mode 100644 index 0000000..1d4122b --- /dev/null +++ b/CodeGen/CKEnumCommentsHelper.java @@ -0,0 +1,51 @@ +import org.antlr.v4.runtime.*; + +public class CKEnumCommentsHelper { + + enum CommentsPosition { + Unknown, Precomment, Postcomment + } + + public CKEnumCommentsHelper(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. + Token precomment = CKCommonHelper.getPreChannelToken(mTokenStream, preToken, CKGeneralLexer.COMMENTS); + if (precomment != null) { + mCommentsPos = CommentsPosition.Precomment; + return CKCommonHelper.cutComment(precomment); + } + + Token postcomment = CKCommonHelper.getPostChannelToken(mTokenStream, postToken, CKGeneralLexer.COMMENTS); + if (postcomment != null) { + mCommentsPos = CommentsPosition.Postcomment; + return CKCommonHelper.cutComment(postcomment); + } + + // really do not have comment, return empty and keep comment pos + return null; + } + case Precomment: { + Token comment = CKCommonHelper.getPreChannelToken(mTokenStream, preToken, CKGeneralLexer.COMMENTS); + return CKCommonHelper.cutComment(comment); + } + case Postcomment: { + Token comment = CKCommonHelper.getPostChannelToken(mTokenStream, postToken, CKGeneralLexer.COMMENTS); + return CKCommonHelper.cutComment(comment); + } + default: + return null; + } + } + +} diff --git a/CodeGen/CKEnumParser.g4 b/CodeGen/CKEnumParser.g4 index a792e54..5b8551f 100644 --- a/CodeGen/CKEnumParser.g4 +++ b/CodeGen/CKEnumParser.g4 @@ -4,11 +4,11 @@ options { tokenVocab = CKGeneralLexer; } prog: enumBody+ ; enumBody: CKGENERAL_TYPEDEF? CKGENERAL_ENUM CKGENERAL_ID CKGENERAL_LBRACKET -entryPair (CKGENERAL_COMMA entryPair)* +entryPair+ CKGENERAL_RBRACKET CKGENERAL_ID? CKGENERAL_SEMICOLON ; -entryPair: CKGENERAL_ID (CKGENERAL_EQUAL entryValue)? ; +entryPair: CKGENERAL_ID (CKGENERAL_EQUAL entryValue)? CKGENERAL_COMMA? ; -entryValue: CKGENERAL_NUM (CKGENERAL_LSHIFT CKGENERAL_NUM)* # entryDirectValue +entryValue: CKGENERAL_NUM (CKGENERAL_LSHIFT CKGENERAL_NUM)? # entryDirectValue | CKGENERAL_ID (CKGENERAL_OR CKGENERAL_ID)* # entryRelativeValue ; \ No newline at end of file diff --git a/CodeGen/CKEnumRunner.java b/CodeGen/CKEnumRunner.java new file mode 100644 index 0000000..58b1f5e --- /dev/null +++ b/CodeGen/CKEnumRunner.java @@ -0,0 +1,192 @@ +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 EnumProgWriter { + public static void writeEnums(OutputStreamWriter writer, CKCommonHelper.CKParts parts, List prog) + throws Exception { + CKIndentHelper indent = new CKIndentHelper(writer); + indent.puts("#pragma once"); + indent.puts("#include "); + indent.printf("namespace LibCmo::{} {{", CKCommonHelper.getCKPartsNamespace(parts)); + indent.inc(); + + // write enums + for (Enum_t enum_t : prog) { + // write enum comment + indent.briefComment(enum_t.mEnumComment); + + // write enum start + indent.printf("enum class {} : {} {{", enum_t.mEnumName, + CKCommonHelper.getEnumUnderlayingType(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("{},", enumEntry_t.mEntryName); + } else { + indent.printf("{} = {},", enumEntry_t.mEntryName, enumEntry_t.mEntryValue); + } + + // write entry comment after member + indent.afterMemberComment(enumEntry_t.mEntryComment); + } + + // write enum tail + indent.dec(); + indent.puts("}"); + } + + indent.dec(); + indent.puts("}"); + } + + public static void writeComments(OutputStreamWriter writer, List prog) throws Exception { + CKIndentHelper indent = new CKIndentHelper(writer); + + } + } + + public static void Run(String infilename, String outfilename) { + + } +} diff --git a/CodeGen/CKGeneralLexer.g4 b/CodeGen/CKGeneralLexer.g4 index 6ce1e37..64919eb 100644 --- a/CodeGen/CKGeneralLexer.g4 +++ b/CodeGen/CKGeneralLexer.g4 @@ -17,7 +17,7 @@ CKGENERAL_COMMA: ',' ; // identifider and number CKGENERAL_ID: [_a-zA-Z][_a-zA-Z0-9]* ; -CKGENERAL_NUM: '-'? ('0' [xX])? [0-9a-fA-F]+ [uUlL]* ; +CKGENERAL_NUM: (('0'[xX]) | '-')? [0-9a-fA-F]+ [uUlL]* ; // comments CKGENERAL_LINE_COMMENT: '//' ~[\r\n]* -> channel(COMMENTS); diff --git a/CodeGen/CKIndentHelper.java b/CodeGen/CKIndentHelper.java new file mode 100644 index 0000000..01f6f9e --- /dev/null +++ b/CodeGen/CKIndentHelper.java @@ -0,0 +1,47 @@ +import java.io.OutputStreamWriter; + +public class CKIndentHelper { + public CKIndentHelper(OutputStreamWriter writer) { + mIndent = 0; + } + + private int mIndent; + private OutputStreamWriter mWriter; + + public void inc() { + ++mIndent; + } + + public void dec() { + --mIndent; + } + + public void indent() throws Exception { + mWriter.write("\n"); + for (int i = 0; i < mIndent; ++i) { + mWriter.write("\t"); + } + } + + 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)); + } + + public void briefComment(String fmt) throws Exception { + if (fmt == null) return; + printf("/**< {} */", fmt.replaceAll("[\\r\\n]+", "")); + } + + public void afterMemberComment(String fmt) throws Exception { + if (fmt == null) return; + mWriter.write("\t"); + mWriter.write(fmt); + } + +} diff --git a/CodeGen/src/CKMISC.txt b/CodeGen/src/CKENUMS.txt similarity index 100% rename from CodeGen/src/CKMISC.txt rename to CodeGen/src/CKENUMS.txt diff --git a/CodeGen/src/VXENUMS.txt b/CodeGen/src/VXENUMS.txt new file mode 100644 index 0000000..e69de29