1 /**
2 Copyright 2018 Mark Fisher
3 
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 **/
22 module dxx.util.grammar;
23 
24 public import pegged.grammar;
25 
26 private import dxx.util;
27 
28 template parseGrammar(string g) {
29     enum parseGrammar = grammar(g);
30 }
31 template parseGrammarFile(string fname) {
32     enum parseGrammarFile = parseGrammar!(import(fname));
33 }
34 
35 template parseData(alias Grammar,string data) {
36     enum parseData = Grammar(data);
37 }
38 template parseData(alias Grammar) {
39     auto parseData(string data) {
40         return Grammar(data);
41     }
42 }
43 //template parseData(string data,string g,alias Grammar) {
44     //auto g = parseGrammar!g;
45 //    enum parseData = Grammar(data);
46 //}
47 template parseDataFile(alias Grammar,string grammarFile,string dataFile) {
48     mixin(parseGrammarFile!grammarFile);
49     enum parseDataFile = parseDataFile!(Grammar,dataFile);
50 }
51 template parseDataFile(alias Grammar,string dataFile) {
52     enum parseDataFile = Grammar(import(dataFile));
53 }
54 template outputData(Sink,alias data) {
55     auto outputData(Sink sink) {
56         return sink ~= data;
57     }
58 }
59 
60 template iterateParseTree(alias Node,alias Fnc) {
61     string iterateParseTree() {
62         appender:string s;
63         static foreach(n;Node.children) {
64             s ~= iterateParseTree!n;
65         }
66         return Fnc!(Node.name,Node,s);
67     }
68 }
69 
70 string renderData(Data)() {
71     // string buffer
72     outputData!Data(sb);
73     return sb.data;
74 }
75 
76 template GrammarParser(string g,string name) {
77     mixin(parseGrammar!g);
78     auto GrammarParser(string d) {
79         mixin(name)(d);
80     }
81 }
82 
83 unittest {
84 
85     import std.stdio;
86     import std.conv;
87     import std.array;
88     import std.algorithm;
89 
90     enum g=q{
91 MyGrammar:
92     test < "test" {myaction} test2
93     test2 < "test2" identifier
94     };
95     enum d=`test test2 testId`;
96 
97     class MyGrammar {
98         static bool _assert = false;
99         static string[] names;
100 
101         mixin(parseGrammar!g);
102 
103         static PT myaction(PT)(PT p) {
104             writeln(text("_action ",p));
105             _assert = true;
106             names ~= p.name;
107             return p;
108         }
109         this() {
110             auto a = parseData!(MyGrammar)(d);
111             writeln("Grammar test " ~ a.toString);
112             assert(_assert);
113         }
114     }
115     auto a = new MyGrammar;
116 
117     class MyGrammar2 {
118     enum g=q{
119 MyGrammar2:
120     test < "test" {myaction} test2
121     test2 <- "test2" identifier
122     };
123     enum d=`test test2 testId`;
124         mixin(parseGrammar!g);
125 
126         static PT myaction(PT)(PT p) {
127             return p;
128         }
129         enum data = parseData!(MyGrammar2,d);
130         enum _dataString = dataString(data);
131 
132         static string dataString(ParseTree Data) {
133             string n = "";
134             switch(Data.name) {
135                 case "MyGrammar2":
136                     n = "enum __a = \"";
137                     n ~= "_MY:";
138                     n ~= dataString(Data.children[0]);
139                     n ~= "\";";
140                     break;
141                 case "MyGrammar2.test":
142                 case "MyGrammar2.test2":
143                     n = nodeToString(Data);
144                     n ~= Data.children.map!(dataString).join(", ").text;
145                 default:
146                     break;
147             }
148             return n;
149         }
150         static string nodeToString(N)(N n) {
151             return "\" ~ impl_" ~ n.name ~ "!(\""~ n.name ~ "\") ~ \"";
152         }
153 
154     }
155 
156     auto b = new MyGrammar2;
157     class impl_MyGrammar2 {
158         static auto test(string n)() {
159             return "TEST";
160         }
161         static auto test2(string n)() {
162             return "TEST2";
163         }
164         mixin(MyGrammar2._dataString);
165     }
166     writeln("Grammar2 test " ~ impl_MyGrammar2.__a);
167 
168 }
169 
170 unittest {
171     import std.array;
172     import std..string;
173     import std.stdio;
174     import std.algorithm;
175     import std.regex;
176 
177     enum GRAMMAR = q{
178 MYGRAMMAR(Template):
179     MyDoc <- MyLine+ :endOfInput
180     MyLine <- :LDelim ^Template :RDelim / Text
181     LDelim <- "{{"
182     RDelim <- "}}"
183     Text <- ~((!LDelim) Char )*
184     Char <- .
185     };
186     mixin(parseGrammar!GRAMMAR);
187 
188     struct range {
189         enum varOne = "varOne";
190         static string opCall(string i)() {
191             return i;
192         }
193     }
194 
195     template compile(string n)
196     {
197         enum compile = "range.opCall!(\"" ~ n ~ "\")()";
198     }
199 
200     static string dataString(alias Data)() {
201         string n = "";
202         pragma(msg,"Data "~ Data.toString);
203         switch(Data.name) {
204             case "MYGRAMMAR":
205             case "MYGRAMMAR.MyDoc":
206             case "MYGRAMMAR.MyLine":
207                 static foreach(x;Data.children) n~=dataString!x();
208                 break;
209             case "MYGRAMMAR.Text":
210                 n ~= Data.matches.join("");
211                 break;
212             case "identifier":
213                 n ~= compile!(Data.matches.join(""));
214                 break;
215             default:
216                 break;
217         }
218         return n;
219     }
220     enum d = "enum fred = {{varOne}};";
221     enum data = parseData!(MYGRAMMAR!identifier,d);
222     pragma(msg,data);
223     enum _dataString = dataString!(data);
224     pragma(msg,_dataString);
225     writeln("Grammar Test: " ~ _dataString);
226     mixin(_dataString);
227     static assert(__traits(compiles,"writeln(fred);"));
228     writeln("" ~ fred);
229     static assert("varOne" == fred);
230 }