//parse 语法分析 if(_STUDIO_INVOKED) import ide; import fsys; namespace protobuf; class parser{ ctor(){ this.packageTree = {}; this.subMessage = {} this.subEnum = {} this.oneof = {}; this.name = ""; this.root = this; this.isRootPackage = true; this.libPaths = {"."}; } parseFile = function (protofile,ns="",inProject ){ this.path = protofile; var str = ..string.load(protofile); if(!str) error("打开 *.proto 文件失败:" + protofile,2); this.lex = ..protobuf.parser.lex(str); this.lex.protoPath = ..io.fullpath(protofile); this.rootProtoDir = ..io.splitpath(this.lex.protoPath).dir; this.lex.next(); this.nameSpace = ns; this.inProject = inProject; this._parseMessage( this ); for(k,message in this.subMessage){ message.parseFullTypeName(); } this._createLib( inProject,this ); if(_STUDIO_INVOKED) ..ide.refreshUserLib(); } parse = function (str,ns="",inProject=true){ this.lex = ..protobuf.parser.lex(str) this.lex.next(); this.rootProtoDir = "/"; this.nameSpace = ns; this.inProject = inProject; this._parseMessage( this ); for(k,message in this.subMessage){ message.parseFullTypeName(); } this._createLib( inProject,this ); if(_STUDIO_INVOKED) ..ide.refreshUserLib(); } importFile = function(protofile,msgParent){ var saveLex = this.lex; var str; if(protofile[1]=='.'#){ var dir = this.rootProtoDir; if(saveLex.protoPath){ dir = ..fsys.getParentDir(saveLex.protoPath); } var path = ..io.joinpath(dir,protofile); str = ..string.load(path); } elseif(this.libPaths) { for i,p in ..table.eachIndex(this.libPaths){ if(p==".") p = this.rootProtoDir; var path = ..io.joinpath(p,protofile) if(..io.exist(path)){ str = ..string.load(path); break; } } } if(!str) { if(..string.cmp(protofile,"google/protobuf/timestamp.proto")==0){ str = "package google.protobuf;message Timestamp { int64 seconds = 1; int32 nanos = 2;}" } else { error("导入 proto 文件失败:" + protofile ,2); } } this.lex = ..protobuf.parser.lex(str); this.lex.protoPath = protofile; this.lex.next(); var msg = ..protobuf.parser.message(); msg.isRootPackage = true; msg.name = ""; this.subMessage[protofile] = msg; this._parseMessage( msg ); this.lex = saveLex; } _createLib = function(inProject,packageSpace){ var libDir = inProject ? ( _STUDIO_INVOKED ? ..ide.getProjectDir() ) : "\" libDir = ..io.joinpath( libDir,"lib\\"); for(k,message in packageSpace.subMessage){ if(!message.isClass){ this._createLib(inProject,message); continue; } var code = message.serialize(); for(k,message in packageSpace.subMessage){ var ns = ..protobuf.parser.message.combineNamespace(message.nameSpace,message.name ); if( ..string.find(code,"@"".." + ns + '"') ){ code = "import " + ns + ';\r\n' + code; } } for(name,body in packageSpace.subEnum){ var ns = ..protobuf.parser.message.combineNamespace(packageSpace.nameSpace,name ); if( ..string.find(code,"@"".." + ns + '"') ){ code = "import " + ns + ';\r\n' + code; } } var classDir = ..io.fullpath( libDir ++ ..string.replace(message.nameSpace,"\.","\") ); ..fsys.createDir(classDir); var path = ..io.joinpath(classDir,message.name + ".aardio") ..string.save(path, (#message.nameSpace?("namespace "+message.nameSpace + ';\r\n'):"") + 'import protobuf.message;\r\nimport util.metaProperty;\r\n\r\n' + code ) } var classDir = ..io.fullpath( libDir ++ ..string.replace(packageSpace.nameSpace,"\.","\") ); ..fsys.createDir(classDir); var template = ..protobuf.parser.template; for(name,body in packageSpace.subEnum){ var ns = ..protobuf.parser.message.combineNamespace(packageSpace.nameSpace,name ); var code = template.enum.format( name = name; body = body; ) var path = ..io.joinpath(classDir,name + ".aardio") ..string.save(path,(#packageSpace.nameSpace?("namespace "+packageSpace.nameSpace + ';\r\n'):"") + 'import protobuf.message;\r\nimport util.metaProperty;\r\n\r\n' + code ) } } _parseMessage = function( msgParent ) { var lex = this.lex; while ( lex.token ){ lex.checkKeyword() if ( ..string.cmp( lex.data,'message') == 0 ) { lex.next(); lex.checkKeyword() var nameMsg = lex.data; var msg = ..protobuf.parser.message(); msg.isClass = true; msg.name = lex.data; var nameSpace = ..protobuf.parser.message.combineNamespace( msgParent.nameSpace,msgParent.name ); msg.nameSpace = nameSpace; msg.parent = msgParent; msg.root = msgParent.root; msgParent.subMessage[ msg.name ] = msg; msg.packageTree = this.packageTree; var packagePath = ..protobuf.parser.message.combineNamespace( nameSpace,msg.name); var msgOrPackage = ..table.get(packagePath,this.packageTree); if(msgOrPackage){ ..table.assign(msgOrPackage,msg); } else { ..table.set(packagePath,msg,this.packageTree); } if(#nameSpace){ var parentPackageNode = ..table.get(nameSpace,this.packageTree); msg.parentPackageNode = parentPackageNode; } else { msg.parentPackageNode = this.packageTree; } lex.next(); //lex.next();//exit 'message name' var beginLine = lex.line; lex.checkNext('{'#) if(lex.token != '}'#){ this._parseMessage( msg ); } if( lex.token != '}'# ){ error("缺少'}' (行:" + lex.line + ") 匹配'}'(行:" +beginLine + ")",2) } lex.next(); } else if ( ..string.cmp( lex.data,'enum') == 0 ) { lex.next();//exit 'enum' lex.checkKeyword() var nameEnum = lex.data; lex.next(); lex.check('{'#); var body = lex.matchNext("%{}"); body = ..string.replace(body," ",'\t');//规范缩进格式 body = ..string.replace(body,'\t\t','\t');//移除多余缩进 body = ..string.replace(body,'\t\\}','}');//移除多余缩进 msgParent.subEnum[nameEnum] = body; } else if ( ..string.cmp( lex.data,'package') == 0 ) { lex.next();//exit 'package' lex.checkKeyword() if( msgParent == this ){ if(#msgParent.nameSpace){ //已经指定了自定义的命名空间 this.package = lex.data; } else { this.nameSpace = lex.data; } var packageNode = ..table.get(this.nameSpace,this.packageTree); if( packageNode != msgParent ){ ..table.assign(packageNode,msgParent); } else { ..table.set(this.nameSpace,msgParent,this.packageTree); } } else{ if( (#this.package) && ..string.startsWith(msgParent.package,this.package ) ){ msgParent.nameSpace = ..string.right(lex.data,-(#this.package+2)); } else { msgParent.nameSpace = lex.data; } var packageNode = ..table.get(msgParent.nameSpace,this.packageTree); if( packageNode != msgParent ){ ..table.assign(packageNode,msgParent); } else { ..table.set(msgParent.nameSpace,msgParent,this.packageTree); } } lex.next(); lex.checkNext(';'#); } else if ( ..string.cmp( lex.data,'service') == 0 ) { lex.matchNext("%{}"); } else if ( ..string.cmp( lex.data,'oneof') == 0 ) { lex.next(); lex.checkKeyword(); ..table.push( this.oneof ,lex.data ); lex.next(); lex.checkNext('{'#) } else if ( ..string.cmp( lex.data,'import') == 0 ) { lex.next(); if(lex.testKeyword && lex.data=="public"){ lex.next(); } lex.checkString(); var path = lex.data; this.importFile(path,msgParent); lex.next(); lex.testNext(';'#); } else if ( ..string.cmp( lex.data,'syntax') == 0 ) { lex.next(); lex.checkNext('='#); lex.checkString(); this.syntax = lex.data; if(this.syntax=="proto3"){ //兼容 proto3 语法 } lex.next(); lex.testNext(';'#); } else { var field = ..protobuf.parser.field(); field.comment = lex.comment; if(#this.oneof){ field.oneof = this.oneof[#this.oneof]; } if( msgParent.fieldTypes ){ ..table.push(msgParent.fieldTypes,field); } if ( ..string.cmp( lex.data,'required') == 0 ) { //field.required = true; lex.next() } elseif ( ..string.cmp( lex.data,'optional') == 0 ) { //field.optional = true; lex.next() } elseif ( ..string.cmp( lex.data,'singular') == 0 ) { //field.optional = true; lex.next() } elseif ( ..string.cmp( lex.data,'repeated') == 0 ) { field.repeated = true; lex.next() } elseif ( ..string.cmp( lex.data,'option') == 0 ) { field.option = true; lex.next() field.varName = lex.data; lex.next(); lex.checkNext('='#); if( !msgParent.option ){ msgParent.option = {} } msgParent.option[field.varName] = lex.data; lex.next(); lex.checkNext(';'#); continue; } field.typeName = lex.data; lex.next(); if( lex.testNext('<'#) ){ field.mapKeyTypeName = lex.data; lex.next(); lex.checkNext(','#); field.mapValueTypeName = lex.data; lex.next(); lex.checkNext('>'#); } field.varName = lex.data; lex.next(); lex.checkNext('='#); lex.checkNumber(); field.fieldIndex = lex.data; lex.next(); if( lex.testNext('['#) ){ lex.checkKeyword() var attr = lex.data; lex.next(); lex.checkNext('='#); if(lex.testKeyword()){ if( (lex.data == "true" || lex.data == "false" || lex.data == "null") ){ field[attr] = eval(lex.data); } else{ //proto3 不支持默认值,所以不需要寻找在后面定义的类型 field[attr] = msgParent.findType( field.typeName, lex.data ); } } else { field[attr] = lex.data; } lex.next(); lex.checkNext(']'#); } elseif(field.repeated && packableTypes[field.typeName] && this.syntax=="proto3"){ field.packed = true; } lex.checkNext(';'#); if(lex.token == '}'#){ if(#this.oneof){ var oneof = ..table.pop(this.oneof); lex.next(); if(lex.token == '}'#) return; } else { return; } } } } } } namespace parser{ packableTypes = { enum=1; int32=1; int64=1; sint32=1; sint64=1; sfixed32=1; sfixed64=1; fixed32=1; fixed64=1; uint32=1; uint64=1; float=1; double=1; bool=1; } } //类作为父命名空间时,import 子库必须放在类定义之后 import protobuf.parser.lex; import protobuf.parser.message; //@guide [示例](doc://example/Network/protobuf/QuickStart.html) /**intellisense() protobuf.parser() = Protobuf 结构定义文件 *.proto 解析器,\n支持 proto2,proto3\n!protobuf_parser. !protobuf_parser.libPaths = 指定导入 proto 文件时搜索的目录,\n此属性只能为字符串数组。\n默认为 {"."} ,"." 表示第一个解析的 proto 文件所在目录。\n如果自内存字符串解析 proto,但 "." 表示应用程序根目录 !protobuf_parser.parseFile( = 编译 proto 文件 !protobuf_parser.parseFile(.(protoFile,namespace,inProject) = 编译 proto 文件。\n\n@protoFile 参数指定 proto 文件路径。\n可选用 @namespace 参数指定默认根命名空间\n可选用 @inProject 参数指定是否优先在工程目录下创建用户库\n└──@inProject 默认为 true \n└──如果 @inProject 为 false 则始终在 "/" 目录下创建用户库\n└──工程内部运行的代码文件 "/" 等价于工程目录\n\n字段名中下划线后接小写字母会自动去掉下划线转换为驼峰式命名 !protobuf_parser.parse( = 编译 proto 代码 !protobuf_parser.parse(.(strProto,namespace,inProject) = 编译 proto 代码。\n\n@strProto 参数指定 proto 代码。\n可选用 @namespace 参数指定默认根命名空间\n可选用 @inProject 参数指定是否优先在工程目录下创建用户库\n└──@inProject 默认为 true \n└──如果 @inProject 为 false 则始终在 "/" 目录下创建用户库\n└──工程内部运行的代码文件 "/" 等价于工程目录\n\n字段名中下划线后接小写字母会自动去掉下划线转换为驼峰式命名 end intellisense**/