1 module toml.grammar;
2 
3 import pegged.grammar;
4 
5 enum PEG = `
6 TOML:
7 
8   Document          <- (ValueLine)* KeyGroup*
9 
10   KeyGroup          <- Header ValueLine* 
11 
12   Header            <- ignore "[" HeaderName "]" line_end
13   HeaderName        <- qualifiedIdentifier
14 
15   ValueLine         <- :ignore Name :ws :"=" :ws Value line_end
16   Name              <- identifier 
17   Value             <- DatetimeValue / StringValue / FloatValue / IntegerValue / BooleanValue / Array
18 
19 
20   #
21   # Strings
22   # -----------------------------------------------------------------
23 
24   StringValue       <~ :doublequote (!doublequote Char)* :doublequote 
25   Char              <~ backslash ( doublequote  # '\' Escapes
26                         / quote
27                         / backslash
28                         / [bfnrt]
29                         / [0-2][0-7][0-7]
30                         / [0-7][0-7]?
31                         / 'x' Hex Hex
32                         / 'u' Hex Hex Hex Hex
33                         / 'U' Hex Hex Hex Hex Hex Hex Hex Hex
34                         )
35                         / . # Or any char, really
36   Hex               <- [0-9a-fA-F]             
37 
38   #
39   # Numbers
40   # -------------------------------------------
41 
42   Digit             <- [0-9]
43   IntegerValue      <~ "-"? [1-9] digit* 
44   FloatValue        <~ IntegerValue "." digit+ 
45 
46   #
47   # DateTime
48   # -------------------------------------------
49 
50   DatetimeValue     <~ ([1-9] digit digit digit) "-" (digit digit) "-" (digit digit) "T" (digit digit) ":" (digit digit) ":" (digit digit) "Z"
51 
52   #
53   # Boolean
54   # -------------------------------------------
55 
56   BooleanValue      <- ("true" / "false")
57 
58   #
59   # Arrays
60   # -------------------------------------------
61 
62   Array             <- EmptyArray / DatetimeArray / StringArray / IntegerArray / FloatArray / ArrayOfArray
63   EmptyArray        <- :"[" ws :"]"
64   StringArray       <- :"[" ws StringValue      (ws :"," ws StringValue)*   ws :"]"
65   IntegerArray      <- :"[" ws IntegerValue     (ws :"," ws IntegerValue)*  ws :"]"
66   FloatArray        <- :"[" ws FloatValue       (ws :"," ws FloatValue)*    ws :"]"
67   DatetimeArray     <- :"[" ws DatetimeValue    (ws :"," ws DatetimeValue)* ws :"]"
68   ArrayOfArray      <- :"[" ws Array            (ws :"," ws Array)*         ws :"]"
69 
70 
71   #
72   # Helpers
73   # -----------------------------------------
74 
75   line_end        <- ws comment? !(!eol .)
76   ignore          <- :(comment / space / eol)*
77   comment         <- "#" (!eol .)*
78   ws              <- :space*
79 
80 
81 `;
82 
83 //pragma(msg, grammar(PEG));
84 
85 mixin(grammar(PEG));
86 
87 unittest {
88     import std.stdio;
89     import pegged.tester.grammartester;
90     
91     auto grtest = new GrammarTester!(TOML, "Document");
92 
93     grtest.assertSimilar(`key = 1`, `
94             Document -> {
95                 ValueLine -> {
96                     Name
97                     Value -> {
98                         IntegerValue
99                         }
100                 }
101             }
102             `);
103 
104     grtest.assertSimilar(`key = "hello world"`, `
105             Document -> {
106                 ValueLine -> {
107                     Name
108                     Value -> {
109                         StringValue
110                         }
111                 }
112             }
113             `);
114 
115     auto header_checker = new GrammarTester!(TOML, "Header");
116     header_checker.assertSimilar("[test]", `
117             Header ->
118                 HeaderName
119             `);
120 
121     auto intArray = new GrammarTester!(TOML, "IntegerArray");
122     intArray.assertSimilar(`[1,2]`,`
123             IntegerArray -> {
124                 IntegerValue
125                 IntegerValue
126                 }
127             `);
128 
129     auto emptyarray = new GrammarTester!(TOML, "EmptyArray");
130     emptyarray.assertSimilar(`[         ]`,`
131             EmptyArray
132             `);
133 
134 
135     auto arrayOfArray = new GrammarTester!(TOML, "ArrayOfArray");
136     arrayOfArray.assertSimilar(`[[1,2] ]`,`
137             ArrayOfArray -> {
138                 Array -> {
139                     IntegerArray -> {
140                         IntegerValue
141                         IntegerValue
142                     }
143                 }
144             }
145             `);
146 
147    auto keygroup_checker = new GrammarTester!(TOML, "KeyGroup");
148     keygroup_checker.assertSimilar(`
149             [test]
150             key = 1
151             `, `
152             KeyGroup -> {
153                 Header -> {
154                     HeaderName
155                 }
156                 ValueLine -> {
157                     Name
158                     Value -> {
159                         IntegerValue
160                         }
161                 }
162             }
163             `);
164 
165     auto date_checker = new GrammarTester!(TOML, "DatetimeValue");
166     date_checker.assertSimilar("1979-05-27T07:32:00Z",  `
167             DatetimeValue
168             `);
169 
170 
171     enum TEST1 = `
172         key_string = "hello"
173         key_integer = 10
174         key_bool = true
175     `;
176 
177     auto test1 = TOML(TEST1);
178     writeln(test1);
179 
180     enum TEST2 = `
181         key_1 = "test"
182         [keygroup]
183         key_string = "hello"
184         key_integer = 10
185         key_bool = true
186         key_string_array = ["abc","cde"]
187         arrayOfArrays = [[1,2], ["a","b"], [1.2, 1.2]]
188         date = 1979-05-27T07:32:00Z
189         multistring = "
190             hello \tworld
191             this is a big 
192             multiline
193             string
194             "
195 
196         [servers]
197 
198         [servers.alpha]
199         z = 1
200     `;
201 
202     auto test2 = TOML(TEST2);
203     writeln(test2);
204 }