1 module pbd.parse;
2 
3 import pegged.grammar;
4 
5 mixin(grammar(`
6 Proto:
7     Root < Syntax?  :';'?
8            Package? :';'?
9            ((Import / Option / Enum / Message) :';'?)*
10 
11     Spacing <~ (space / endOfLine / Comment)*
12     Comment <~ "//" (!endOfLine .)* endOfLine
13 
14     Syntax < :"syntax" :'=' String
15 
16     Package < :"package" identifier
17 
18     Import < :"import" String
19 
20     Option < :"option" identifier :'=' Value
21 
22     Enum < :"enum" identifier :'{'
23                (EnumField :';'?)*
24            :'}'
25 
26     EnumField < identifier :'=' Integer
27 
28     Message < :"message" identifier :'{'
29                   ((SingleField / RepeatedField / Oneof / Map / Enum / Message) :';'?)*
30               :'}'
31 
32     SingleField < identifier identifier :'=' Integer
33 
34     RepeatedField < :"repeated" identifier identifier :'=' Integer
35                     (:'[' "packed" :'=' Bool :']')?
36 
37     Oneof < :"oneof" identifier :'{'
38                (SingleField :';'?)*
39              :'}'
40 
41     Map < :"map<" identifier :","  identifier :">" identifier :'=' Integer
42 
43     Value  <- String
44             / Integer
45             / Bool
46 
47     Bool   <- "true" / "false"
48 
49     String <~ :doublequote Char* :doublequote
50     Char   <~ backslash doublequote
51             / backslash backslash
52             / backslash [bfnrt]
53             / (!doublequote .)
54 
55     Integer <~ '0'
56             / [1-9] Digit*?
57     Digit  <- [0-9]
58 `));
59 
60 
61 ///
62 version (pbd_test)
63 unittest
64 {
65   enum exampleProto = `
66 /// test test
67 syntax = "proto3";
68 
69 /// test
70 package tensorflow;
71 
72 /// test
73 option cc_enable_arenas = true;
74 option java_outer_classname = "OpDefProtos";
75 import "tensorflow/core/framework/attr_value.proto";
76 import "tensorflow/core/framework/types.proto";
77 
78 // Defines an operation. A NodeDef in a GraphDef specifies an Op by
79 // using the "op" field which should match the name of a OpDef.
80 // LINT.IfChange
81 message OpDef {
82   // Op names starting with an underscore are reserved for internal use.
83   // Names should be CamelCase and match the regexp "[A-Z][a-zA-Z0-9>_]*".
84   string name = 1;
85 
86   // For describing inputs and outputs.
87   message ArgDef {
88     // Name for the input/output.  Should match the regexp "[a-z][a-z0-9_]*".
89     string name = 1;
90 
91     // Human readable description.
92     string description = 2;
93   };
94 
95   repeated AttrDef attr = 4 [packed = true]; // test
96 
97   oneof test_oneof {
98     string name = 4;
99     string sub_message = 9; //test
100   };
101 
102   map<string, int32> str2int = 5;
103 
104   enum Visibility {
105     // Normally this is "VISIBLE" unless you are inheriting a
106     // different value from another ApiDef.
107     DEFAULT_VISIBILITY = 0;
108     // Publicly visible in the API.
109     VISIBLE = 1;
110     // Do not include this op in the generated API. If visibility is
111     // set to 'SKIP', other fields are ignored for this op.
112     SKIP = 2;
113     // Hide this op by putting it into an internal namespace (or whatever
114     // is appropriate in the target language).
115     HIDDEN = 3;
116   }
117   Visibility visibility = 2;
118 }
119 `;
120 
121   auto tree = Proto(exampleProto);
122   assert(tree.successful, tree.failMsg);
123 
124   assert(tree.name == "Proto");
125 
126   auto root = tree.children[0];
127   assert(root.name == "Proto.Root");
128 
129   // syntax = "proto3";
130   auto syntax = root.children[0];
131   assert(syntax.name == "Proto.Syntax");
132   assert(syntax.matches == ["proto3"]);
133 
134   // package tensorflow;
135   auto package_ = root.children[1];
136   assert(package_.name == "Proto.Package");
137   assert(package_.matches == ["tensorflow"]);
138 
139   // option cc_enable_arenas = true;
140   auto option0 = root.children[2];
141   assert(option0.name == "Proto.Option");
142   assert(option0.matches == ["cc_enable_arenas", "true"]);
143   assert(option0.children[0].name == "Proto.Value");
144   assert(option0.children[0].children[0].name == "Proto.Bool");
145 
146   // option java_outer_classname = "OpDefProtos";
147   auto option1 = root.children[3];
148   assert(option1.name == "Proto.Option");
149   assert(option1.matches == ["java_outer_classname", "OpDefProtos"]);
150 
151   // import "tensorflow/core/framework/attr_value.proto";
152   auto import0 = root.children[4];
153   assert(import0.name == "Proto.Import");
154   assert(import0.matches == ["tensorflow/core/framework/attr_value.proto"]);
155 
156   // import "tensorflow/core/framework/types.proto";
157   auto import1 = root.children[5];
158   assert(import1.name == "Proto.Import");
159   assert(import1.matches == ["tensorflow/core/framework/types.proto"]);
160 
161   // message OpDef {
162   auto message = root.children[6];
163   assert(message.name == "Proto.Message");
164   assert(message.matches[0] == "OpDef");
165 
166   //   string name = 1;
167   auto field0 = message.children[0];
168   assert(field0.name == "Proto.SingleField");
169   assert(field0.matches == ["string", "name", "1"]);
170   assert(field0.children[0].name == "Proto.Integer");
171   assert(field0.children[0].matches == ["1"]);
172 
173   //   message ArgDef {
174   auto subMessage = message.children[1];
175   assert(subMessage.name == "Proto.Message");
176   assert(subMessage.matches[0] == "ArgDef");
177 
178   //     string name = 1;
179   auto subField0 = subMessage.children[0];
180   assert(subField0.name == "Proto.SingleField");
181   assert(subField0.matches == ["string", "name", "1"]);
182 
183   //   repeated AttrDef attr = 4; // test
184   auto repeated = message.children[2];
185   assert(repeated.name == "Proto.RepeatedField");
186   assert(repeated.matches == ["AttrDef", "attr", "4", "packed", "true"]);
187 
188   //   oneof test_oneof {
189   auto oneof = message.children[3];
190   assert(oneof.name == "Proto.Oneof");
191   assert(oneof.matches[0] == "test_oneof");
192 
193   //     string name = 4;
194   auto oneofField0 = oneof.children[0];
195   assert(oneofField0.name == "Proto.SingleField");
196   assert(oneofField0.matches == ["string", "name", "4"]);
197 
198   //     string sub_message = 9; //test
199   auto oneofField1 = oneof.children[1];
200   assert(oneofField1.name == "Proto.SingleField");
201   assert(oneofField1.matches == ["string", "sub_message", "9"]);
202 
203   //   map<string, int32> str2int = 5;
204   auto map = message.children[4];
205   assert(map.name == "Proto.Map");
206   assert(map.matches == ["string", "int32", "str2int", "5"]);
207 
208   //   enum Visibility
209   auto enumDecl = message.children[5];
210   assert(enumDecl.name == "Proto.Enum");
211   assert(enumDecl.matches == [
212       "Visibility",
213       "DEFAULT_VISIBILITY", "0",
214       "VISIBLE", "1",
215       "SKIP", "2",
216       "HIDDEN", "3"]);
217   auto enumField0 = enumDecl.children[0];
218   assert(enumField0.name == "Proto.EnumField");
219   assert(enumField0.matches == ["DEFAULT_VISIBILITY", "0"]);
220 }