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.pbenum;
5 import dprotobuf.pbgeneral;
6 
7 version(D_Version2) {
8 	import std.algorithm;
9 	import std.range;
10 	import std.regex;
11 } else
12 	import dprotobuf.d1support;
13 
14 import std.conv;
15 import std.stdio;
16 import std..string;
17 
18 struct PBEnum {
19 	string name;
20 	string[] comments;
21 	string[][int] valueComments;
22 	string[int] values;
23 
24 	// string-modifying constructor
25 	static PBEnum opCall(ref ParserData pbstring)
26 	in {
27 		assert(pbstring.length);
28 	} body {
29 		PBEnum pbenum;
30 		// strip of "enum" and following whitespace
31 		pbstring = pbstring["enum".length..pbstring.length];
32 		pbstring = stripLWhite(pbstring);
33 		// grab name
34 		pbenum.name = stripValidChars(CClass.Identifier,pbstring);
35 		if (!pbenum.name.length) throw new PBParseException("Enum Definition","Could not pull name from definition.", pbstring.line);
36 		if (!validIdentifier(pbenum.name)) throw new PBParseException("Enum Definition","Invalid name identifier "~pbenum.name~".", pbstring.line);
37 		pbstring = stripLWhite(pbstring);
38 
39 		// rip out the comment...
40 		if (pbstring.length>1 && pbstring.input[0..2] == "//") {
41 			pbenum.comments ~= stripValidChars(CClass.Comment,pbstring);
42 			pbstring = stripLWhite(pbstring);
43 		}
44 
45 		// make sure the next character is the opening {
46 		if (!pbstring.input.skipOver("{")) {
47 			throw new PBParseException("Enum Definition("~pbenum.name~")","Expected next character to be '{'. You may have a space in your enum name: "~pbenum.name, pbstring.line);
48 		}
49 
50 		CommentManager storeComment;
51 		int elementNum;
52 
53 		pbstring = stripLWhite(pbstring);
54 		// now we're ready to enter the loop and parse children
55 		while(pbstring[0] != '}') {
56 			if (pbstring.input.skipOver("option")) {
57 				pbstring = stripLWhite(pbstring);
58 				writefln("Ignoring option %s",
59 				ripOption(pbstring).name);
60 				pbstring.input.skipOver(";");
61 			}
62 			else if (pbstring.length>1 && pbstring.input[0..2] == "//") {
63 				// rip out the comment...
64 				storeComment ~= stripValidChars(CClass.Comment,pbstring);
65 				storeComment.line = pbstring.line;
66 			} else {
67 				// start parsing, we shouldn't have any whitespace here
68 				elementNum = pbenum.grabEnumValue(pbstring);
69 				storeComment.lastElementLine = pbstring.line;
70 				if(!storeComment.comments.empty()) {
71 					pbenum.valueComments[elementNum] = storeComment.comments;
72 					storeComment.comments = null;
73 				}
74 			}
75 			if(storeComment.line == storeComment.lastElementLine) {
76 				pbenum.valueComments[elementNum] = storeComment.comments;
77 				storeComment.comments = null;
78 			}
79 			pbstring = stripLWhite(pbstring);
80 		}
81 		// rip off the }
82 		pbstring = pbstring[1..pbstring.length];
83 		return pbenum;
84 	}
85 
86 	/**
87 	 * returns:
88 	 *     enum entry value
89 	 */
90 	int grabEnumValue(ref ParserData pbstring)
91 	in {
92 		assert(pbstring.length);
93 	} body {
94 		// whitespace has already been ripped
95 		// snag item name
96 		string tmp = stripValidChars(CClass.Identifier,pbstring);
97 		if (!tmp.length) throw new PBParseException("Enum Definition("~name~")","Could not pull item name from definition.", pbstring.line);
98 		if (!validIdentifier(tmp)) throw new PBParseException("Enum Definition("~name~")","Invalid item name identifier "~tmp~".", pbstring.line);
99 		pbstring = stripLWhite(pbstring);
100 		// ensure that the name doesn't already exist
101 		foreach(val;values.values) if (tmp == val) throw new PBParseException("Enum Definition("~name~")","Multiple defined element("~tmp~")", pbstring.line);
102 		// make sure to traverse the '='
103 		if (!pbstring.input.skipOver("=")) throw new PBParseException("Enum Definition("~name~"."~tmp~")","Expected '=', but got something else. You may have a space in one of your enum items.", pbstring.line);
104 
105 		pbstring = stripLWhite(pbstring);
106 		// now parse a numeric
107 		string num = stripValidChars(CClass.Numeric,pbstring);
108 		if (!num.length) throw new PBParseException("Enum Definition("~name~"."~tmp~")","Could not pull numeric enum value.", pbstring.line);
109 		values[to!(int)(num)] = tmp;
110 		pbstring = stripLWhite(pbstring);
111 		// deal with inline options
112 		if (pbstring[0] == '[') {
113 			ripOptions(pbstring);
114 		}
115 		// make sure we snatch a semicolon
116 		if (!pbstring.input.skipOver(";"))
117 			throw new PBParseException("Enum Definition("~name~"."~tmp~"="~num~")","Expected ';'.", pbstring.line);
118 
119 		return to!(int)(num);
120 	}
121 }