1 // this file implements the structures and lexer for the protocol buffer format 2 // required to parse a protocol buffer file or tree and generate 3 // code to read and write the specified format 4 module dprotobuf.pbchild; 5 import dprotobuf.pbgeneral; 6 import dprotobuf.wireformat; 7 8 version(D_Version2) import std.range; 9 10 import std..string; 11 import std.stdio; 12 import std.conv; 13 14 struct PBChild { 15 string modifier; 16 string type; 17 string name; 18 string[] comments; 19 int index; 20 string valdefault; 21 bool packed = false; 22 bool is_dep = false; 23 24 static PBChild opCall(ref ParserData pbstring) 25 in { 26 assert(pbstring.length); 27 } body { 28 PBChild child; 29 // all of the modifiers happen to be the same length...whodathunkit 30 // also, it's guaranteed to be there by previous code, so it shouldn't need error checking 31 child.modifier = pbstring.input[0..8]; 32 pbstring = pbstring[8..pbstring.length]; 33 pbstring = stripLWhite(pbstring); 34 // now we want to pull out the type 35 child.type = stripValidChars(CClass.MultiIdentifier,pbstring); 36 if (!child.type.length) throw new PBParseException("Child Instantiation","Could not pull type from definition.", pbstring.line); 37 if (!validateMultiIdentifier(child.type)) throw new PBParseException("Child Instantiation","Invalid type identifier "~child.type~".", pbstring.line); 38 pbstring = stripLWhite(pbstring); 39 // pull out the name of the instance, now 40 child.name = stripValidChars(CClass.Identifier,pbstring); 41 if (!child.name.length) throw new PBParseException("Child Instantiation("~child.type~")","Could not pull name from definition.", pbstring.line); 42 if (!validIdentifier(child.name)) throw new PBParseException("Child Instantiation("~child.type~")","Invalid name identifier "~child.name~".", pbstring.line); 43 pbstring = stripLWhite(pbstring); 44 // make sure the next character is =, because we need to snag the index, next 45 if (pbstring[0] != '=') throw new PBParseException("Child Instantiation("~child.type~" "~child.name~")","Missing '=' for child instantiation.", pbstring.line); 46 pbstring = pbstring[1..pbstring.length]; 47 pbstring = stripLWhite(pbstring); 48 // pull numeric index 49 string tmp = stripValidChars(CClass.Numeric,pbstring); 50 if (!tmp.length) throw new PBParseException("Child Instantiation("~child.type~" "~child.name~")","Could not pull numeric index.", pbstring.line); 51 child.index = to!(int)(tmp); 52 if (child.index <= 0) throw new PBParseException("Child Instantiation("~child.type~" "~child.name~")","Numeric index can not be less than 1.", pbstring.line); 53 if (child.index > (1<<29)-1) throw new PBParseException("Child Instantiation("~child.type~" "~child.name~")","Numeric index can not be greater than (1<<29)-1.", pbstring.line); 54 // deal with inline options 55 pbstring = stripLWhite(pbstring); 56 if (pbstring[0] == '[') { 57 PBOption[]opts = ripOptions(pbstring); 58 foreach (opt;opts) if (opt.name == "default") { 59 if (child.modifier == "repeated") throw new PBParseException("Default Option("~child.name~" default)","Default options can not be applied to repeated fields.", pbstring.line); 60 child.valdefault = opt.value; 61 } else if (opt.name == "deprecated" && opt.value == "true") { 62 if (child.modifier == "required") throw new PBParseException("Deprecated Option("~child.name~" deprecated)","Deprecated options can not be applied to repeated fields.", pbstring.line); 63 child.is_dep = true; 64 } else if (opt.name == "packed" && opt.value == "true") { 65 if (child.modifier == "required" || child.modifier == "optional") throw new PBParseException("Packed Option("~child.name~" packed)","Packed options can not be applied to "~child.modifier~" fields.", pbstring.line); 66 if (child.type == "string" || child.type == "bytes") throw new PBParseException("Packed Option("~child.name~" packed)","Packed options can not be applied to "~child.type~" types.", pbstring.line); 67 // applying packed to message types is not properly checked, but is avoided in deser code 68 child.packed = true; 69 } 70 } 71 // now, check to see if we have a semicolon so we can be done 72 pbstring = stripLWhite(pbstring); 73 if (pbstring[0] == ';') { 74 // rip off the semicolon 75 pbstring = pbstring[1..pbstring.length]; 76 return child; 77 } 78 throw new PBParseException("Child Instantiation(" 79 ~child.type~" "~child.name~")", 80 "No idea what to do with string after index and options.", 81 pbstring.line); 82 } 83 } 84 85 string toDType(string intype) { 86 string retstr; 87 switch(intype) { 88 case "sint32","sfixed32","int32": 89 retstr = "int"; 90 break; 91 case "sint64","sfixed64","int64": 92 retstr = "long"; 93 break; 94 case "fixed32","uint32": 95 retstr = "uint"; 96 break; 97 case "fixed64","uint64": 98 retstr = "ulong"; 99 break; 100 case "string": 101 retstr = "string"; 102 break; 103 case "bytes": 104 retstr = "ubyte[]"; 105 break; 106 case "float": 107 case "double": 108 case "bool": 109 default: 110 retstr = intype; 111 } 112 return retstr; 113 } 114 115 unittest { 116 writefln("unittest ProtocolBuffer.pbchild"); 117 // assumes leading whitespace has already been stripped 118 auto childtxt = ParserData("optional int32 i32test = 1[default=5];"); 119 auto child = PBChild(childtxt); 120 debug writefln("Checking modifier..."); 121 assert(child.modifier == "optional"); 122 debug writefln("Checking type..."); 123 assert(child.type == "int32"); 124 debug writefln("Checking name..."); 125 assert(child.name == "i32test"); 126 debug writefln("Checking message index..."); 127 assert(child.index == 1); 128 debug writefln(""); 129 } 130 131 bool isPackable(string type) { 132 auto wt = wTFromType(type); 133 if (wt == WireType.varint 134 || wt == WireType.fixed64 135 || wt == WireType.fixed32) { 136 return true; 137 } 138 return false; 139 } 140 141 WireType wTFromType(string type) { 142 switch(type) { 143 case "float","sfixed32","fixed32": 144 return WireType.fixed32; 145 case "double","sfixed64","fixed64": 146 return WireType.fixed64; 147 case "bool","int32","int64","uint32","uint64","sint32","sint64": 148 return WireType.varint; 149 case "string","bytes": 150 return WireType.lenDelimited; 151 default: 152 return WireType.undecided; 153 } 154 }