使用antlr4, 用ts/js还原protobuf生成的jav

2017-03-23  本文已影响0人  ThirstyZebra

目录:

test004.ts

import {ANTLRInputStream, CommonTokenStream} from "antlr4ts";
import {JavaLexer} from "../antlr/Java/JavaLexer";
import {CompilationUnitContext, JavaParser} from "../antlr/Java/JavaParser";
import {fs} from "mz";
import {CustomJavaListener} from "./CustomJavaListener";
import {ParseTreeWalker} from "antlr4ts/tree";
import * as nodefs from "fs";
import {CustomClassBodyDeclarationVisitor} from "./CustomClassBodyDeclarationVisitor";
import {PbStructure} from "./PbStructure";

let data = nodefs.readFileSync('./../../src/ref/msg_svc$PbGetDiscussMsgResp.java', "utf-8");

beginParse(data);


function beginParse(data) {

    let inputStream = new ANTLRInputStream(data);
    let lexer = new JavaLexer(inputStream);
    let tokenStream = new CommonTokenStream(lexer);
    let parser = new JavaParser(tokenStream);

    // Parse the input, where `compilationUnit` is whatever entry point you defined
    let tree: CompilationUnitContext = parser.compilationUnit();

    // let walker: ParseTreeWalker = new ParseTreeWalker(); // create standard walker
    // let javaListener: CustomJavaListener = new CustomJavaListener();
    // walker.walk(javaListener, tree); // initiate walk of tree with listener

    let pbStructure = new PbStructure();
    let javaVisitor = new CustomClassBodyDeclarationVisitor(pbStructure);
    javaVisitor.visit(tree);

    console.log(pbStructure);

    // console.log(tree);

    // console.log(value);
}

好了, 看下使用visitor写的代码, 这是解析fieldMap字段的

import {JavaVisitor} from "../antlr/Java/JavaVisitor";
import {AbstractParseTreeVisitor} from "antlr4ts/tree";
import {
    ArrayInitializerContext,
    ClassDeclarationContext,
    CreatorContext,
    ExpressionListContext,
    FieldDeclarationContext,
    VariableDeclaratorContext,
    VariableInitializerContext
} from "../antlr/Java/JavaParser";
import {PbStructure} from "./PbStructure";
export class CustomClassBodyDeclarationVisitor<Result> extends AbstractParseTreeVisitor<Result> implements JavaVisitor<Result> {


    public constructor(public pbStructure: PbStructure) {
        super();
    }

    public visitClassDeclaration(ctx: ClassDeclarationContext): Result {
        this.pbStructure.messageName = ctx.Identifier().text;
        this.pbStructure.extend = ctx.typeType().classOrInterfaceType().Identifier(0).text;
        this.visit(ctx.classBody());
        return;
    }

    public visitFieldDeclaration(ctx: FieldDeclarationContext): Result {
        if (
            ctx.typeType().text == 'MessageMicro.FieldMap'
        ) {
            this.visit(ctx.variableDeclarators());
        }
        return;
    }

    public visitVariableDeclarator(ctx: VariableDeclaratorContext): Result {
        if (ctx.variableDeclaratorId().text == '__fieldMap__') {
            if (ctx.variableInitializer()) {
                this.visitFieldMapVariableInitializer(ctx.variableInitializer());
            }
        }
        return;
    }

    protected defaultResult(): Result {
        return undefined;
    }

    /**
     * // MessageMicro.initFieldMap(
     * //      new int[] { 8, 16, 24, 32 },
     * //      new String[] { "uint32_start_time", "rpt_uint32_fieldlist", "rpt_uint64_uinlist", "uint64_bind_uin" },
     * //      new Object[] { Integer.valueOf(0), Integer.valueOf(0), Long.valueOf(0L), Long.valueOf(0L) },
     * //      ReqBody.class
     * // );
     * @param ctx
     */
    private visitFieldMapVariableInitializer(ctx: VariableInitializerContext) {

        let initFunc = ctx.expression().expression(0);
        if (initFunc.text != 'MessageMicro.initFieldMap') {
            throw new Error('fieldMap function name must be "MessageMicro.initFieldMap"');
        }
        let expressList = ctx.expression().expressionList();
        this.visitFieldMapExpressList(expressList);
    }

    /**
     *                // MessageMicro.initFieldMap(
     * expression 0-> //      new int[] { 8, 16, 24, 32 },
     * expression 1-> //      new String[] { "uint32_start_time", "rpt_uint32_fieldlist", "rpt_uint64_uinlist", "uint64_bind_uin" },
     * expression 2-> //      new Object[] { Integer.valueOf(0), Integer.valueOf(0), Long.valueOf(0L), Long.valueOf(0L) },
     *                //      ReqBody.class
     *                // );
     *  @param ctx
     */
    private visitFieldMapExpressList(ctx: ExpressionListContext) {

        this.pbStructure.flags = this.visit(ctx.expression(0));
        this.pbStructure.fields = this.visit(ctx.expression(1));
        this.pbStructure.defaultValues = this.visit(ctx.expression(2));
    }

    public visitCreator(ctx: CreatorContext): Result | any {
        let creatorName = ctx.createdName().text;
        let arrayInitializr = ctx.arrayCreatorRest().arrayInitializer();
        switch (creatorName) {
            case 'int':
                return this.visitIntArrayInitializer(arrayInitializr);
            case 'String':
                return this.visitStringArrayInitializer(arrayInitializr);
            case 'Object':
                return this.visitObjectArrayInitializer(arrayInitializr);
            default:
                return this.visitDefaultArrayInitializer(arrayInitializr);
        }
    }

    public visitDefaultArrayInitializer(ctx: ArrayInitializerContext) {
        return ctx.variableInitializer().map(variable => variable);
    }

    public visitIntArrayInitializer(ctx: ArrayInitializerContext) {
        return ctx.variableInitializer().map(variable => parseInt(variable.text));
    }

    public visitObjectArrayInitializer(ctx: ArrayInitializerContext) {
        return ctx.variableInitializer().map(variable => {
            //类型初始化
            let expr = variable.expression();
            if (expr &&
                expr.getChild(1).text == '(' &&
                expr.getChild(3).text == ')'
            ) {
                switch (expr.expression(0).text) {
                    case 'Integer.valueOf':
                    case 'Long.valueOf':
                        return parseInt(expr.expressionList().text);
                    default:
                        return variable.text;
                }

            } else {
                return variable.text;
            }
        });
    }

    public visitStringArrayInitializer(ctx: ArrayInitializerContext) {
        return ctx.variableInitializer().map(variable => variable.text.replace(/"(.+?)"|'(.+?)'/, "$1$2"));
    }

}

运行一下查看一下PbStructure类

PbStructure {
  messageName: 'Oidb_0x5d0$ReqBody',
  extend: 'MessageMicro',
  flags: [ 8, 16, 24, 32 ],
  fields: 
   [ 'uint32_start_time',
     'rpt_uint32_fieldlist',
     'rpt_uint64_uinlist',
     'uint64_bind_uin' ],
  defaultValues: [ 0, 0, 0, 0 ] }

完善其他字段

再次添加解析所有字段声明的代码


PbStructure类

export class PbStructure {
    public messageName;

    //another info
    public extend;

    public flags;
    public fields;
    public defaultValues;
    fieldMap: Map<string, { type, name, defaultValue, repeated }>;

    constructor() {
        this.fieldMap = new Map();
    }
}

CustomClassBodyDeclarationVisitor

import {JavaVisitor} from "../antlr/Java/JavaVisitor";
import {AbstractParseTreeVisitor} from "antlr4ts/tree";
import {
    ArrayInitializerContext,
    ClassDeclarationContext,
    CreatorContext,
    ExpressionListContext,
    FieldDeclarationContext,
    VariableDeclaratorContext,
    VariableInitializerContext
} from "../antlr/Java/JavaParser";
import {PbStructure} from "./PbStructure";
export class CustomClassBodyDeclarationVisitor<Result> extends AbstractParseTreeVisitor<Result> implements JavaVisitor<Result> {

    private currentHandleField;

    private resetCurrentHandleField() {
        this.currentHandleField = {
            type: null,
            name: null,
            defaultValue: null,
            repeated: null
        };
    }

    public constructor(public pbStructure: PbStructure) {
        super();
    }

    public visitClassDeclaration(ctx: ClassDeclarationContext): Result {
        this.pbStructure.messageName = ctx.Identifier().text;
        this.pbStructure.extend = ctx.typeType().classOrInterfaceType().Identifier(0).text;
        this.visit(ctx.classBody());
        return;
    }

    public visitFieldDeclaration(ctx: FieldDeclarationContext): Result {
        switch (ctx.typeType().text) {
            case 'MessageMicro.FieldMap':
                this.visit(ctx.variableDeclarators());
                break;
            case 'PBBoolField':
            case 'PBBytesField':
            case 'PBDoubleField':
            case 'PBEnumField':
            case 'PBField':
            case 'PBFixed32Field':
            case 'PBFixed64Field':
            case 'PBFloatField':
            case 'PBInt32Field':
            case 'PBInt64Field':
            case 'PBPrimitiveField':
            case 'PBRepeatField':
            case 'PBRepeatMessageField':
            case 'PBSFixed32Field':
            case 'PBSFixed64Field':
            case 'PBSInt32Field':
            case 'PBSInt64Field':
            case 'PBStringField':
            case 'PBUInt32Field':
            case 'PBUInt64Field':
                this.resetCurrentHandleField();
                this.currentHandleField.type = ctx.typeType().text;
                this.visit(ctx.variableDeclarators());
                this.pbStructure.fieldMap.set(this.currentHandleField.name, this.currentHandleField);
                this.resetCurrentHandleField();
                break;
        }
        return;
    }

    public visitVariableDeclarator(ctx: VariableDeclaratorContext): Result {
        if (ctx.variableDeclaratorId().text == '__fieldMap__') {
            if (ctx.variableInitializer()) {
                this.visitFieldMapVariableInitializer(ctx.variableInitializer());
            }
        } else {
            if (ctx.variableInitializer()) {
                this.currentHandleField.name = ctx.variableDeclaratorId().text;
                this.visitPBFieldVariableInitializer(ctx.variableInitializer());

            }
        }
        return;
    }

    public visitPBFieldVariableInitializer(ctx: VariableInitializerContext) {
        let expr = ctx.expression();
        if (expr &&
            expr.getChild(1).text == '(' &&
            expr.getChild(3).text == ')'
        ) {
            switch (expr.expression(0).text) {
                case 'PBField.initBool':
                case 'PBField.initFixed32':
                case 'PBField.initInt32':
                case 'PBField.initInt64':
                case 'PBField.initSFixed32':
                case 'PBField.initSFixed64':
                case 'PBField.initSInt32':
                case 'PBField.initSInt64':
                case 'PBField.initUInt32':
                case 'PBField.initUInt64':
                    this.currentHandleField.defaultValue = parseInt(expr.expressionList().text);
                    break;
                case 'PBField.initBytes'://ByteStringMicro/byte[]
                    this.currentHandleField.defaultValue = expr.expressionList().text;
                    break;
                case 'PBField.initEnum':
                    break;
                case 'PBField.initFloat':
                case 'PBField.initDouble':
                    this.currentHandleField.defaultValue = parseFloat(expr.expressionList().text);
                    break;
                case 'PBField.initRepeat':
                    const index = expr.expressionList().text.indexOf('__repeatHelper__');
                    if (index > 0) {
                        this.currentHandleField.repeated = expr.expressionList().text.substring(0, index - 1);
                    }
                    break;
                case 'PBField.initString':
                    this.currentHandleField.defaultValue = expr.expressionList().text.replace(/"(.+?)"|'(.+?)'/, "$1$2")
            }

        } else {
        }
    }

    protected defaultResult(): Result {
        return undefined;
    }

    /**
     * // MessageMicro.initFieldMap(
     * //      new int[] { 8, 16, 24, 32 },
     * //      new String[] { "uint32_start_time", "rpt_uint32_fieldlist", "rpt_uint64_uinlist", "uint64_bind_uin" },
     * //      new Object[] { Integer.valueOf(0), Integer.valueOf(0), Long.valueOf(0L), Long.valueOf(0L) },
     * //      ReqBody.class
     * // );
     * @param ctx
     */
    private visitFieldMapVariableInitializer(ctx: VariableInitializerContext) {

        let initFunc = ctx.expression().expression(0);
        if (initFunc.text != 'MessageMicro.initFieldMap') {
            throw new Error('fieldMap function name must be "MessageMicro.initFieldMap"');
        }
        let expressList = ctx.expression().expressionList();
        this.visitFieldMapExpressList(expressList);
    }

    /**
     *                // MessageMicro.initFieldMap(
     * expression 0-> //      new int[] { 8, 16, 24, 32 },
     * expression 1-> //      new String[] { "uint32_start_time", "rpt_uint32_fieldlist", "rpt_uint64_uinlist", "uint64_bind_uin" },
     * expression 2-> //      new Object[] { Integer.valueOf(0), Integer.valueOf(0), Long.valueOf(0L), Long.valueOf(0L) },
     *                //      ReqBody.class
     *                // );
     *  @param ctx
     */
    private visitFieldMapExpressList(ctx: ExpressionListContext) {

        this.pbStructure.flags = this.visit(ctx.expression(0));
        this.pbStructure.fields = this.visit(ctx.expression(1));
        this.pbStructure.defaultValues = this.visit(ctx.expression(2));
    }

    public visitCreator(ctx: CreatorContext): Result | any {
        let creatorName = ctx.createdName().text;
        let arrayInitializr = ctx.arrayCreatorRest().arrayInitializer();
        switch (creatorName) {
            case 'int':
                return this.visitIntArrayInitializer(arrayInitializr);
            case 'String':
                return this.visitStringArrayInitializer(arrayInitializr);
            case 'Object':
                return this.visitObjectArrayInitializer(arrayInitializr);
            default:
                return this.visitDefaultArrayInitializer(arrayInitializr);
        }
    }

    public visitDefaultArrayInitializer(ctx: ArrayInitializerContext) {
        return ctx.variableInitializer().map(variable => variable);
    }

    public visitIntArrayInitializer(ctx: ArrayInitializerContext) {
        return ctx.variableInitializer().map(variable => parseInt(variable.text));
    }

    public visitStringArrayInitializer(ctx: ArrayInitializerContext) {
        return ctx.variableInitializer().map(variable => variable.text.replace(/"(.+?)"|'(.+?)'/, "$1$2"));
    }

    public visitObjectArrayInitializer(ctx: ArrayInitializerContext) {
        return ctx.variableInitializer().map(variable => {
            //类型初始化
            let expr = variable.expression();
            if (expr &&
                expr.getChild(1).text == '(' &&
                expr.getChild(3).text == ')'
            ) {
                switch (expr.expression(0).text) {
                    case 'Integer.valueOf':
                    case 'Long.valueOf':
                        return parseInt(expr.expressionList().text);
                    default:
                        return variable.text;
                }

            } else {
                return variable.text;
            }
        });
    }

}

查看打印出来的PbStructure类

PbStructure {
  fieldMap: 
   Map {
     'rpt_uint32_fieldlist' => { type: 'PBRepeatField',
     name: 'rpt_uint32_fieldlist',
     defaultValue: null,
     repeated: 'PBUInt32Field' },
     'rpt_uint64_uinlist' => { type: 'PBRepeatField',
     name: 'rpt_uint64_uinlist',
     defaultValue: null,
     repeated: 'PBUInt64Field' },
     'uint32_start_time' => { type: 'PBUInt32Field',
     name: 'uint32_start_time',
     defaultValue: 0,
     repeated: null },
     'uint64_bind_uin' => { type: 'PBUInt64Field',
     name: 'uint64_bind_uin',
     defaultValue: 0,
     repeated: null } },
  messageName: 'Oidb_0x5d0$ReqBody',
  extend: 'MessageMicro',
  flags: [ 8, 16, 24, 32 ],
  fields: 
   [ 'uint32_start_time',
     'rpt_uint32_fieldlist',
     'rpt_uint64_uinlist',
     'uint64_bind_uin' ],
  defaultValues: [ 0, 0, 0, 0 ] }

最后通过PbStructure类生成proto文件


添加GeneProtoFile代码

import {PbStructure} from "./PbStructure";
import {Java2PBType, JavaFieldMap, PBType} from "./PBMap";
export class GeneProtoFile {
    private indent: number = 0;


    public constructor(private pbStructure: PbStructure) {

    }

    public indentWrite(string) {
        this.indentInc();
        this.write(string);
        this.indentDec();
    }

    public indentWriteLn(string) {
        this.indentInc();
        this.writeLn(string);
        this.indentDec();
    }

    public write(string) {
        let indent = this.indent;
        while (indent-- > 0) {
            this.put("    ");
        }
        this.put(string);
    }

    public writeLn(string) {
        this.write(string);
        this.put("\n");
    }

    public put(string) {
        process.stdout.write(string);
    }

    public indentInc() {
        ++this.indent;
    }

    public indentDec() {
        --this.indent;
        if (this.indent < 0) {
            this.indent = 0;
        }
    }


    public emitProto() {
        this.emitMessage();

    }

    private emitMessage() {
        this.writeLn(`message ${this.pbStructure.messageName} {`)
        this.indentInc();
        this.emitProtoBody();
        this.indentDec();
        this.write(`}`)
    }

    private emitProtoBody() {
        this.pbStructure.flags.map((flag, index) => {
            this.emitProtoField(index);
        });
    }

    private emitProtoField(index) {
        let name = this.pbStructure.fields[index];
        let fieldData = this.pbStructure.fieldMap.get(name);

        switch (fieldData.type) {
            case JavaFieldMap.PBBytesField:
                this.writeLn(`${PBType.bytes} ${fieldData.name};`);

                break;
            case JavaFieldMap.PBDoubleField:
                this.writeLn(`${PBType.double} ${fieldData.name};`);

                break;
            case JavaFieldMap.PBEnumField:
                this.writeLn(`enum ${fieldData.name};`);//fixme

                break;
            case JavaFieldMap.PBField:
                this.writeLn(`pb ${fieldData.name};`);//fixme

                break;
            case JavaFieldMap.PBFixed32Field:
                this.writeLn(`${PBType.fixed32} ${fieldData.name};`);

                break;
            case JavaFieldMap.PBFixed64Field:
                this.writeLn(`${PBType.fixed64} ${fieldData.name};`);

                break;
            case JavaFieldMap.PBFloatField:
                this.writeLn(`${PBType.float} ${fieldData.name};`);

                break;
            case JavaFieldMap.PBInt32Field:
                this.writeLn(`${PBType.int32} ${fieldData.name};`);

                break;
            case JavaFieldMap.PBInt64Field:
                this.writeLn(`${PBType.int64} ${fieldData.name};`);
                this.put(`;`);
                break;
            case JavaFieldMap.PBPrimitiveField:
                this.writeLn(`primitive ${fieldData.name};`);//fixme

                break;
            case JavaFieldMap.PBRepeatField:
                this.writeLn(`repeated ${Java2PBType[fieldData.repeated]} ${fieldData.name};`);
                break;
            case JavaFieldMap.PBRepeatMessageField:
                this.writeLn(`repeated message ${fieldData.type} ${fieldData.name};`);
                break;
            case JavaFieldMap.PBSFixed32Field:
                this.writeLn(`${PBType.sfixed32} ${fieldData.name};`);
                break;
            case JavaFieldMap.PBSFixed64Field:
                this.writeLn(`${PBType.sfixed64} ${fieldData.name};`);
                break;
            case JavaFieldMap.PBSInt32Field:
                this.writeLn(`${PBType.sint32} ${fieldData.name};`);
                break;
            case JavaFieldMap.PBSInt64Field:
                this.writeLn(`${PBType.sint64} ${fieldData.name};`);

                break;
            case JavaFieldMap.PBStringField:
                this.writeLn(`${PBType.string} ${fieldData.name};`);

                break;
            case JavaFieldMap.PBUInt32Field:
                this.writeLn(`${PBType.uint32} ${fieldData.name};`);

                break;
            case JavaFieldMap.PBUInt64Field:
                this.writeLn(`${PBType.uint64} ${fieldData.name};`);

                break;
        }


    }
}

修改test004.ts添加如下几行

let gene = new GeneProtoFile(pbStructure);

    gene.emitProto();

最后完成批量解析, 以及一些bug修正
git地址: http://git.oschina.net/gradee/antlr
https://github.com/linbolen/botmm

上一篇下一篇

猜你喜欢

热点阅读