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 }