root/trunk/dparser/dparse.d

Revision 269, 38.4 kB (checked in by BCS, 7 years ago)

fixed a debug output issue
added a grammar fixer utility

Line 
1 /++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 This is a template meta-program that generates a recursive decent
3 parser. It is to be mixed into a class or struct. The parameters
4 for the template are: a string containing almost normal BNF
5 reduction definitions, and a string defining the start rule.
6
7 It extends BNF by allowing "|" for "or" but doesn't implement
8 optional or repeated terms.
9
10 $(TABLE
11 $(TR $(TD Grammar)  $(TD ::= Rule Grammar | Rule))
12 $(TR $(TD Rule)     $(TD ::= ID ":" Cases ";"))
13 $(TR $(TD Cases)    $(TD ::= Case "|" Alt | Case))
14 $(TR $(TD Case)     $(TD ::= ID "/" Sequence))
15 $(TR $(TD Sequence) $(TD ::= ID Sequence | ID))
16 $(TR $(TD Item)     $(TD ::= ID "[?+*]?";))
17 $(TR $(TD ID)       $(TD ::= "[A-Za-z][A-Za-z0-9]*";))
18 )
19
20 The term between the ':' and '/' is the name of the reduction
21 action. If a reduction is used but not defined a terminal must
22 be defined for it.
23
24 Example:
25
26 -----------------------------------------
27 struct set
28 {
29 /******* action code ********/
30 static PObject Action(char[] string : "MULTIPLY")(PObject[3] set)
31    {...}
32 static PObject Action(char[] string : "SUM"     )(PObject[3] set)
33    {...}
34 static PObject Action(char[] string : "PASS"    )(PObject[1] set)
35    {...}
36
37 /******** Terminal code *********/
38 static PObject Terminal(char[] name : "NUM")(IParser p)
39    {...}
40 static PObject Terminal(char[] name : "PLUS")(IParser p)
41    {...}
42 static PObject Terminal(char[] name : "TIMES")(IParser p)
43    {...}
44
45 // "ADD" indicates the root rule
46
47 mixin Parser!("ADD", ReduceWhite(
48     "PRIMARY : PASS/     NUM;
49      MUL     : MULTIPLY/ PRIMARY TIMES MUL
50              | PASS/     PRIMARY;
51      ADD     : SUM/      MUL PLUS ADD
52              | PASS/     MUL;
53     "));
54 }
55
56 void main()
57 {
58    auto a = new ExpGrammer;
59    a.data = "1 + 3 * 4 + 5 * 5 ";
60
61    Value v = cast(Value)set.Parser(a);
62
63    assert(v !is null,"ERR0R");
64    writef("%d\n", v.value);
65 }
66 ----------------
67
68 This meta program is distributed with no warranty of any kind.
69
70 Author: Benjamin Shropshire (shro8822drop_this at vandals DOT uidaho edu)
71
72 Please give credit where credit is due. Don't remove this notice.
73
74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
75 module syntax.dparse;
76
77 // Add not grammer, might use non-Action parser
78 private import std.stdio : writef;
79 private import std.string : ToString = toString ;
80
81 private import glue.templates;
82
83
84 /******************************************************************************
85 ********* PObject Hieracrcy
86 ******************************************************************************/
87
88
89 /** Base class for parsed objects
90 */
91 abstract class PObject
92 {
93     ///
94     abstract bool fail();
95
96     abstract char[] BaseName();
97 }
98
99
100 /************************************************
101     Common base type for ObjectVector types
102 */
103 class PObjectVectorBase : PObject
104 {
105     char[] BaseName(){return typeof(this).stringof;}
106     abstract char[] string();
107 }
108
109 /************************************************
110     A template sub class of PObject for basic tree building
111 */
112 class PObjectVector(uint i) : PObjectVectorBase
113 {
114     char[] BaseName(){return typeof(this).stringof;}
115     alias Tupleof!(i,PObject) type;
116     type data;
117     /// constructor taking i PObjects
118     this(type s)
119     {
120         foreach(int j, t; type)
121             data[j] = s[j];
122     }
123
124     this(PObject[i] d)
125     {
126         foreach(uint j, v; data)
127         {
128             data[j] = d[j];
129         }
130     }
131
132     PObject[] Get()
133     {
134         PObject[i] ret;
135         foreach(uint j, v; data)
136         {
137             ret[j] = data[j];
138         }
139         return ret.dup;
140     }
141
142     /// Get the j'th item
143     PObject Get(uint j)
144     {
145         switch(j)
146         {
147             foreach(int k, t; type)
148                 case k: return data[k];
149             default: throw new Error("Out of bound");
150         }
151     }
152
153     /// Get the j'th item
154     PObject GetT(uint j)()
155     {
156         static if(j<data.length)
157             return data[j];
158         else
159             static assert(false);
160     }
161
162     char[] string()
163     {
164         char[] ret =  "[";
165
166         foreach(i,_;type)
167         {
168             if(auto v = cast(PObjectVectorBase)data[i])
169             {
170                 ret ~= " " ~ v.string();
171             }
172             else if(auto v = cast(PObjectBoxBase)data[i])
173             {
174                 ret ~= " \"" ~ v.string()~\";
175             }
176             else ret ~= " <UNKNOWN>";
177         }
178         return ret ~ "]";
179     }
180
181     bool fail() { return false; }
182 }
183
184 /************************************************
185     Common base type for BoxObject types
186 */
187 class PObjectBoxBase : PObject
188 {
189     char[] BaseName(){return typeof(this).stringof;}
190     abstract char[] string();
191 }
192
193 /**
194 */
195 class PObjectBox(T,bool str = true) : PObjectBoxBase
196 {
197     char[] BaseName(){return typeof(this).stringof~"!("~T.stringof~")";}
198     T t;
199
200     ///
201     T Get() { return t; }
202
203     ///
204     this(T ti){t=ti;}
205
206     bool fail() { return false; }
207
208     ///
209     char[] string()
210     {
211         static if(str)
212         {
213             static if(is(T == char[]))
214                 return t;
215             else static if(is(ToString(t)))
216                 return ToString(t);
217             else
218                 return "<<"~T.stringof~">>";
219         }
220         else
221             return "<<"~T.stringof~">>";
222     }
223 }
224 //typedef PObjectVector!(5) Five;
225
226 /**
227 */
228 class PObjectList(T) : PObject
229 {
230     char[] BaseName(){return typeof(this).stringof;}
231     debug(TypeReport) pragma(msg, ">>T-"__FILE__~":"~itoa!(__LINE__)~": "~typeof(this).stringof~"!("~T.stringof~")");
232
233     T[] list;
234     int at=0;
235
236     this(){ list = null; }
237
238     this(T ti)
239     {
240         static if(is(T :Object)) assert(ti !is null);
241         list = new T[5];
242         list[at] = ti;
243         at++;
244
245     }
246
247     /// Add a T
248     uint Add(T ti)
249     {
250         static if(is(T :Object)) assert(ti !is null);
251         if(list.length <= at) list.length = at + 5;
252         list[at] = ti;
253         at++;
254         return at-1;
255     }
256
257     /// return the number of items stored
258     uint Count(){return at;}
259
260     ///
261     T[] get()
262     {
263         return list[0..at];
264     }
265
266     bool fail() { return false; }
267 }
268 unittest
269 {
270     writef("unittest@"__FILE__":"~itoa!(__LINE__)~\n);
271    
272     auto o = new PObjectList!(int);
273     o.Add(1);
274     o.Add(2);
275     o.Add(3);
276     o.Add(4);
277     o.Add(5);
278     o.Add(6);
279
280     assert(o.get == [1,2,3,4,5,6], "PObjectList failed");
281 }
282
283 /************************************************
284     List Object that build to the left
285 */
286 class PObjectListLeft(T) : PObjectList!(T)
287 {
288     char[] BaseName(){return typeof(this).stringof;}
289     debug(TypeReport) pragma(msg, ">>T-"__FILE__~":"~itoa!(__LINE__)~": "~typeof(this).stringof~"!("~T.stringof~")");
290
291     this(T t)
292     {
293         list.length = 5;
294         list[$-1] = t;
295         at = 1;
296     }
297     this(){}
298    
299     /// Add a T
300     uint Add(T ti)
301     {
302         static if(is(T :Object)) assert(ti !is null);
303
304         if(list.length <= at)
305         {
306             auto t = list[$-at..$].dup;
307             list.length = at + 5;
308             list[$-at..$] = t;
309         }
310         list[$-1-at] = ti;
311         at++;
312         return at-1;
313     }
314
315     T[] get()
316     {
317         return list[$-at..$];
318     }
319 }
320
321 unittest
322 {
323     writef("unittest@"__FILE__":"~itoa!(__LINE__)~\n);
324    
325     auto o = new PObjectListLeft!(int)( 0);
326                o.Add( 1); o.Add( 2); o.Add( 3); o.Add( 4); o.Add( 5); o.Add( 6); o.Add( 7); o.Add( 8); o.Add( 9);
327     o.Add(10); o.Add(11); o.Add(12); o.Add(13); o.Add(14); o.Add(15); o.Add(16); o.Add(17); o.Add(18); o.Add(19);
328     o.Add(20); o.Add(21); o.Add(22); o.Add(23); o.Add(24); o.Add(25); o.Add(26); o.Add(27); o.Add(28); o.Add(29);
329
330     assert(o.get == [29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0], "PObjectListLeft failed");
331 }
332
333
334 /************************************************
335     A sub class of PObject that is used for repeated reductions
336 */
337 class PObjectSet : PObjectList!(PObject)
338 {
339     char[] BaseName(){return typeof(this).stringof;}
340     /// Discard some PObjects
341     void Back(uint b)
342     {
343         at = b;
344     }
345 }
346
347
348
349 /************************************************
350     Uniform term to prevent needing to know the
351     exact type at compile time.
352 */
353 interface PInterfaceLeftFactor(T) { PObject InsertLeft(T,PObject); }
354
355 /************************************************
356     PObject that uses T.Action!(str) to process a
357     left side term with a Right side term
358 */
359 class PObjectLeftFactorT(Tp, int i, char[] str) : PObjectVector!(i), PInterfaceLeftFactor!(Tp)
360 {
361     this(PObject[i] d){super(d);}
362
363     PObject InsertLeft(Tp that, PObject L)
364     {
365         PObject[i+1] args;
366
367         args[1..$] = Get;
368         args[0] = L;
369
370         return SpecialAction!(Tp,str)(that,args);
371     }
372 }
373
374
375 /************************************************
376     default fail object
377 */
378 class PObjectFail : PObject
379 {
380     char[] BaseName(){return typeof(this).stringof;}
381     char[][] msg;
382
383     this()
384     {
385         msg.length = 1;
386         msg[0] = "Failed";
387     }
388
389     this(char[] m)
390     {
391         msg.length = 1;
392         msg[0] = m.dup;
393     }
394
395     void Add(char[] m)
396     {
397         msg ~= m.dup;
398     }
399
400     bool fail() { return true; }
401 }
402
403 /************************************************
404     default Pass object
405 */
406 class PObjectPass : PObject
407 {
408     char[] BaseName(){return typeof(this).stringof;}
409     this() { }
410     bool fail() { return false; }
411 }
412
413 /************************************************
414     default Filler object, returns fail state as instructed
415 */
416 class PObjectFill : PObject
417 {
418     char[] BaseName(){return typeof(this).stringof;}
419     bool b;
420     this(bool _b){b=_b;}
421     bool fail(){return b;}
422 }
423
424 /*******************************************************
425     interface to handle parser data
426 */
427 interface IParser
428 {
429     uint pos();
430     void pos(uint);
431     debug(dParse_runtime) void mark();
432 }
433
434
435 /******************************************************************************
436 ******** genaric Template code
437 ******************************************************************************/
438
439
440 /** generate a tuple of i U's
441 */
442 template Tupleof(uint i, U)
443 {
444     static if(i == 0)
445         alias T!() Tupleof;
446     else
447     static if(i == 1)
448         alias T!(U) Tupleof;
449     else
450         alias T!(Tupleof!(i-1, U), U) Tupleof;
451 }
452
453 /** Tuple literal template
454 */
455 template T(A...){alias A T;}
456
457
458 /******************************************************************************
459 ******** CTFE functions
460 ******************************************************************************/
461
462 /******************************
463 drop leading whitespace
464 */
465 char[] DropWhiteF(char[] str)
466 {
467
468     foreach(int i, char c; str)
469         switch(c)
470         {
471             case ' ', '\t', '\r', '\n':
472                 continue;
473             default:
474                 return str[i..$];
475         }
476         return "";
477 }
478 struct UnittetDropWhite{
479     static const char[] str = DropWhiteF(" \t\n\rhello");
480     static assert(DropWhiteF(" \t\n\rhello") == "hello");
481 }
482
483
484 /**********************************
485   find first instance of t in str,
486   return str up through that char
487 */
488 char[] FindChar(char t)(char[] str)
489 {
490     foreach(int i, char c; str)
491         if(c == t)
492             return str[0..i+1];
493     return str;
494 }
495
496
497 /**********************************
498   return an identifier
499 */
500 char[] GetID(char[] instr)
501 {
502     char[] str = DropWhiteF(instr);
503
504     if(str.length == 0) return "";
505
506     if(
507         !('a' <= str[0] && str[0] <= 'z') &&
508         !('A' <= str[0] && str[0] <= 'Z') &&
509         !('_' == str[0])
510     ) return "";
511
512     foreach(int i, char c; str)
513     if(
514         !('a' <= c && c <= 'z') &&
515         !('A' <= c && c <= 'Z') &&
516         !('0' <= c && c <= '9') &&
517         !('_' == c)
518     ) return str[0..i];
519
520     return str;
521 }
522 struct UnittetGetID{
523     static assert(GetID("hello world") == "hello", GetID("hello world"));
524 }
525
526 /*********************************
527   replace all sequences of white
528   space with single spaces
529 */
530 public char[] ReduceWhite(char[] str)
531 {
532     bool pass = true;
533     int at = 0;
534     foreach(char c; str)
535         switch(c)
536         {
537             case ' ', '\n', '\r', '\t':
538                 if(!pass)
539                 {
540                     pass = true;
541                     str[at++] = ' ';
542                 }
543                 break;
544
545             default:
546                 pass = false;
547                 str[at++] = c;
548                 break;
549         }
550     return str[0..at];;
551 }
552 static const char[] test = ReduceWhite("hello");
553
554 /*********************************
555     Extract # from "$(C,#,NAME)"
556 */
557 int ExtractCount(char[] str)
558 {
559     int ret = int.min;
560
561     if(str.length > 4 && '0' <= str[0] && str[0] <= '9')
562     {
563         ret = str[0] - '0';
564         for(int i = 1; i<str.length; i++)
565         {
566             if('0' <= str[i] && str[i] <= '9')
567             {
568                 ret *= 10;
569                 ret += str[i] - '0';
570             }
571             else
572                 break;
573         }
574     }
575
576     return ret;
577 }
578
579 /*********************************
580     Extract NAME from "$(C,#,NAME)"
581 */
582 char[] ExtractAct(char[] str)
583 {
584     int c, i;
585     for(i=0; i < str.length && c < 2; i++)
586         c += (str[i] == ',');
587
588     str = str[i..$];
589     i=0;
590     int d = 1;
591     while(i < str.length && d > 0)
592     {
593         d += (str[i] == '(');
594         d -= (str[i] == ')');
595         i++;
596     }
597
598     return str[0..i-1];
599 }
600
601 static assert(ExtractAct("$(T,1,$(N,1,abc,def))") == "$(N,1,abc,def)", ExtractAct("$(T,1,$(N,1,abc,def))"));
602
603 /******************************************************************************
604 ******* Special Action Code
605 ******************************************************************************/
606
607 /// Pack up stuff in a computed type of PObjectLeftFactorT Object
608 PObject L_Action(Tp, char[] str ) (Tp, PObject[ExtractCount(str[4..$])] i)
609 {
610     static const int c = ExtractCount(str[4..$]);
611     static const char[] n = ExtractAct(str);
612     static if(false) pragma(msg, str~" == $(L,"~c.stringof~","~n~")");
613
614     return new PObjectLeftFactorT!(Tp, c, n)(i);
615 }
616
617 /// Left Process a Leftmost term and right side terms list.
618 PObject T_Action(Tp,char[] str) (Tp that, PObject[1+ExtractCount(str[4..$])] i)
619 {
620     static const int num = ExtractCount(str[4..$]);
621     static const char[] act = ExtractAct(str);
622     static if(false) pragma(msg, str~" == $(T,"~num.stringof~","~act~")");
623
624     PObject[num] args = i[0..num];
625     auto ret = SpecialAction!(Tp,act)(that,args);
626
627     auto listO = cast(PObjectList!(PObject)) i[num];
628     auto list = listO.get;
629
630     foreach(obj; list)
631     {
632         auto Tobj = cast(PInterfaceLeftFactor!(Tp))obj;
633         ret = Tobj.InsertLeft(that,ret);
634     }
635     return ret;
636 }
637
638
639 template GetSize(Tp, char[] str)
640 {
641     alias Tp.Action!(str) Fn;
642     static if(is(typeof(Fn) Args == function))
643         const int size = Args[0].length;
644     else
645         static assert(false);
646 }
647
648 template GetArgs(Tp, char[] str)
649 {
650     alias Tp.Action!(str) Fn;
651     static if(is(typeof(Fn) Args == function))
652     {}
653     else
654         static assert(false);
655 }
656
657 /// Next actions
658
659 template N_parts(char[] str)
660 {
661     const int num = ExtractCount(str[4..$]);
662     const char[] firstAct = ExtractAct(str[4..$]);
663     const char[] s1 = str[FindChar!(',')(str[4..$]).length + 4..$];
664     const char[] secondAct = s1[FindChar!(',')(s1).length..$-1];
665 }
666
667 //alias N_parts!("$(N,3,dummy,dummy)") NP;
668
669 PObject N_Action(Tp,char[] str) (Tp that, GetArgs!(Tp,N_parts!(str).firstAct).Args i)
670 {
671     alias N_parts!(str) P;
672     static if(false) pragma(msg, str~" == $(N,"~P.num.stringof~","~P.act~")");
673
674 //  pragma(msg,">"~str);    pragma(msg,">"~P.firstAct); pragma(msg,">"~P.secondAct);
675
676     PObject[] ret;
677     ret[0] = SpecialAction!(Tp,P.firstAct)(that,i);
678     ret[0] = SpecialAction!(Tp,P.secondAct)(that,ret);
679
680     return ret[0];
681 }
682
683
684
685
686 template SpecialAction(Tp,char[] str)
687 {
688 //  pragma(msg,str);
689     static if(str.length > 3 && str[0..2] == "$(")
690     {
691         static if(false) pragma(msg, str);
692         static if(str[2] == 'T')
693             alias T_Action!(Tp,str) SpecialAction;
694         else static if(str[2] == 'L')
695             alias L_Action!(Tp,str) SpecialAction;
696         else static if(str[2] == 'N')
697         {
698             alias N_Action!(Tp,str) SpecialAction;
699         }
700         else
701             static assert (false, "unknown special action: "~str);
702     }
703     else
704     {
705        
706         PObject SpecialAction(Tp that, GetArgs!(Tp,str).Args i)
707         {
708             return that.Action!(str)(i);
709         }
710         //static assert (false, "unknown special action: "~str);
711     }
712 }
713
714 /******************************************************************************
715 ******* Parser
716 ******************************************************************************/
717
718
719 /************************************************
720     Parse an Identifier name (ID ::= "[A-Za-z][A-Za-z0-9]*")
721 */
722 template Parse_ID (char[] str)
723 {
724     private const char[] without = DropWhiteF(str);
725     private const char[] text = GetID(without);
726     const bool Match = (text.length != 0);
727     static if(Match)
728     {
729         const char[] Text = text;
730         const char[] Remaining = without[Text.length .. $];
731         const char[] Used = str[0 .. $-Remaining.length];
732     }
733 }
734 struct UnittetParse_ID
735 {
736     // Tests
737     alias Parse_ID!(" \thello world") Parse_ID_test1;
738     static assert(Parse_ID_test1.without =="hello world","Parse_ID failed: "~Parse_ID_test1.without);
739     static assert(Parse_ID_test1.Remaining == " world",  "Parse_ID failed: "~Parse_ID_test1.Remaining);
740     static assert(Parse_ID_test1.Text == "hello",        "Parse_ID failed: "~Parse_ID_test1.Text);
741     static assert(Parse_ID_test1.Match,                  "Parse_ID failed: ");
742     static assert(Parse_ID_test1.Used == " \thello",     "Parse_ID failed: \""~Parse_ID_test1.Used~\");
743
744     alias Parse_ID!("   \t!hello world") Parse_ID_test2;
745     static assert(!Parse_ID_test2.Match,"Parse_ID failed: ");
746 }
747
748
749 char[] MatchPairs(char I, char O)(char[] str)
750 {
751     int i = 1;
752     foreach(int j, char c; str)
753     {
754         if (c == I) i++;
755         if (c == O) i--;
756         if (i == 0) return str[0..j+1];
757     }
758     return "";
759 }
760
761
762 /************************************************
763     Parse a Sepcial action name name
764     '$([^)]*)' |
765     '$[A-Z][A-Za-z0-9_]*' |
766     '$'
767 */
768 template Parse_SpecialAct (char[] str)
769 {
770     private const char[] without = DropWhiteF(str);
771     //pragma(msg,">>"__FILE__":"~__LINE__.stringof[0..$-1]~": '"~without~\');
772
773     //pragma(msg,__LINE__.stringof~":"~without);
774
775     static if(without.length < 4 || without[0..2] != "$(")
776     {
777         pragma(msg,">>"__FILE__":"~__LINE__.stringof[0..$-1]~": unknown Special '"~without~"' from '"~str~\');
778         const bool Match = false;
779     }
780     else
781     {
782         const char[] t = MatchPairs!('(',')')(without[2..$]);
783
784         //pragma(msg,__LINE__.stringof~":"~t);
785         const bool Match = (t != "");
786         static if(Match)
787         {
788             const char[] Text = without[0..2+t.length];
789             const char[] Remaining = without[Text.length .. $];
790             const char[] Used = str[0 .. $-Remaining.length];
791         }
792     }
793     //pragma(msg,__LINE__.stringof~":"~Match.stringof);
794 }
795
796 struct UnittetParse_Parse_SpecialAct{
797     // Tests
798     static assert(Parse_SpecialAct!("$(Ltree) ").Match);
799
800     //static assert(!Parse_SpecialAct!("$a ").Match);
801     //static assert(!Parse_SpecialAct!("a ").Match);
802 }
803
804
805
806 /************************************************
807     The types of rules
808 */
809 enum ItemType
810 {
811     single,
812     star,
813     plus,
814     optional
815 }
816
817 /************************************************
818     Parse a rule part (Item ::= ID "[?+*]?")
819 */
820 struct Parse_Item(char[] str)
821 {
822     private alias Parse_ID!(str) Item;
823     static if(Item.Match)
824     {
825         /// test set if the rule matched
826         static const bool Match = true;
827         static const char[] Text = Item.Text;
828
829         private static const char[] tmp = DropWhiteF(Item.Remaining);
830         static if(tmp.length > 0)
831         {
832             static if(tmp[0] == '*')
833             {
834                 static const char[] Remaining = tmp[1..$];
835                 static const ItemType Type = ItemType.star;
836             }
837             else
838             static if(tmp[0] == '+')
839             {
840                 static const char[] Remaining = tmp[1..$];
841                 static const ItemType Type = ItemType.plus;
842             }
843             else
844             static if(tmp[0] == '?')
845             {
846                 static const char[] Remaining = tmp[1..$];
847                 static const ItemType Type = ItemType.optional;
848             }
849             else
850             {
851                 static const char[] Remaining = Item.Remaining;
852                 static const ItemType Type = ItemType.single;
853             }
854         }
855         else
856         {
857             static const char[] Remaining = Item.Remaining;
858             static const ItemType Type = ItemType.single;
859         }
860         static const char[] Used = str[0 .. $-Remaining.length];
861     }
862     else
863         static const bool Match = false;
864 }
865 struct UnittestParse_Item
866 {
867     // Tests
868     alias Parse_Item!(" hello world") Parse_Item_test1;
869     static assert(Parse_Item_test1.Match);
870     static assert(Parse_Item_test1.Text == "hello");
871     static assert(Parse_Item_test1.Used == " hello",       Parse_Item_test1.Used);
872     static assert(Parse_Item_test1.Remaining == " world");
873     static assert(Parse_Item_test1.Type == ItemType.single);
874
875     alias Parse_Item!(" hello +world") Parse_Item_test2;
876     static assert(Parse_Item_test2.Match);
877     static assert(Parse_Item_test2.Text == "hello");
878     static assert(Parse_Item_test2.Used == " hello +",       Parse_Item_test1.Used);
879     static assert(Parse_Item_test2.Remaining == "world");
880     static assert(Parse_Item_test2.Type == ItemType.plus);
881
882     alias Parse_Item!(" hello* +world") Parse_Item_test3;
883     static assert(Parse_Item_test3.Match);
884     static assert(Parse_Item_test3.Text == "hello");
885     static assert(Parse_Item_test3.Used == " hello*",       Parse_Item_test1.Used);
886     static assert(Parse_Item_test3.Remaining == " +world");
887     static assert(Parse_Item_test3.Type == ItemType.star);
888
889     alias Parse_Item!(" hello?*+world") Parse_Item_test4;
890     static assert(Parse_Item_test4.Match);
891     static assert(Parse_Item_test4.Text == "hello");
892     static assert(Parse_Item_test4.Used == " hello?",       Parse_Item_test1.Used);
893     static assert(Parse_Item_test4.Remaining == "*+world");
894     static assert(Parse_Item_test4.Type == ItemType.optional);
895
896     alias Parse_Item!(" \t?*+world") Parse_Item_test5;
897     static assert(!Parse_Item_test5.Match);
898 }
899
900
901 /************************************************
902     Parse a Sequence of rule parts (Sequence ::= ID Sequence | ID)
903 */
904 struct Parse_Sequence (char[] str)
905 {
906     private alias Parse_Item!(str) first;
907     static if(first.Match)
908     {
909         static const bool Match = true;
910         alias Parse_Sequence!(first.Remaining) rest;
911         static if(rest.Match)
912         {
913             static alias T!(first, rest.Clauses) Clauses;
914
915             static const char[] Used = str[0 .. first.Used.length + rest.Used.length];
916         }
917         else
918         {
919             alias T!(first) Clauses;
920             static const char[] Used = first.Used;
921         }
922         static const char[] Remaining = str[Used.length .. $];
923     }
924     else
925         static const bool Match = false;
926 }
927 struct UnittestParse_Sequence
928 {
929     // Tests
930     alias Parse_Sequence!("Hello world+this?is* good; by") Parse_Sequence_test1;
931     static assert(Parse_Sequence_test1.Match);
932     static assert(Parse_Sequence_test1.Used == "Hello world+this?is* good", Parse_Sequence_test1.Used);
933     static assert(Parse_Sequence_test1.Remaining == "; by");
934     static assert(Parse_Sequence_test1.Clauses[0].Text == "Hello");
935
936     alias Parse_Sequence!("+this?is* good; by") Parse_Sequence_test2;
937     static assert(!Parse_Sequence_test2.Match);
938 }
939
940
941
942 /************************************************
943     Parse a Reduction Case (Case ::= ID "/" Sequence)
944 */
945 struct Parse_Case(char[] str)
946 {
947     static private alias Parse_ID!(str) act_1;
948     static if(act_1.Match)
949     {
950         alias act_1 act;
951         const bool Special = false;
952         //pragma(msg,__LINE__.stringof~":"~str);
953     }
954     else
955     {
956         alias Parse_SpecialAct!(str) act;
957         const bool Special = act.Match;
958         //pragma(msg,__LINE__.stringof~":"~Special.stringof~":"~str);
959     }
960
961     static if(act.Match)
962     {
963         static const char[] n1 = DropWhiteF(act.Remaining);
964         static if(n1.length > 1 && n1[0] == '/')
965         {
966             static private alias Parse_Sequence!(n1[1..$]) n2;
967
968             static const bool Match = true;
969             static const char[] Action = act.Text;
970             static const char[] Seq = n2.Used;
971             static alias n2.Clauses Clauses;
972         }
973         else
974         {
975             //pragma(msg,__LINE__.stringof~":"~n1);
976             static const bool Match = false;
977         }
978     }
979     else
980     {
981         //pragma(msg,__LINE__.stringof~":"~str);
982         static const bool Match = false;
983     }
984
985 }
986
987
988 struct UnittestParse_Case
989 {
990     // Tests
991     alias Parse_Case!("Act/foo?bar+baz*sig ") Parse_Case_test1;
992     static assert(Parse_Case_test1.Match);
993     static assert(Parse_Case_test1.Action == "Act");
994     static assert(Parse_Case_test1.Seq == "foo?bar+baz*sig");
995     static assert(Parse_Case_test1.Clauses[0].Text == "foo");
996
997     alias Parse_Case!("Act / foo? bar+ baz* sig|") Parse_Case_test2;
998     static assert(Parse_Case_test2.Match);
999     static assert(Parse_Case_test2.Action == "Act");
1000     static assert(Parse_Case_test2.Seq == " foo? bar+ baz* sig");
1001     static assert(Parse_Case_test2.Clauses[0].Text == "foo");
1002
1003     alias Parse_Case!("Act foo? / bar+ baz* sig|") Parse_Case_test3;
1004     static assert(!Parse_Case_test3.Match);
1005 }
1006
1007
1008
1009 /************************************************
1010     Parse Set of Reduction cases (Cases ::= Case "|" Alt | Case)
1011 */
1012 struct Parse_Cases(char[] str)
1013 {
1014     const char[] t_p = FindChar!('|')(str);
1015     const char[] t_s = FindChar!(';')(str);
1016     static if(t_p[$-1] == '|')
1017     {
1018         //pragma(msg,__LINE__.stringof~":"~str);
1019         const char[] t = t_p[0..$-1];
1020     }
1021     else static if(t_s[$-1] == ';')
1022     {
1023         //pragma(msg,__LINE__.stringof~":"~str);
1024         const char[] t = t_s[0..$-1];
1025     }
1026     else
1027     {
1028         pragma(msg, "Debugging? '"~str~\');
1029         const char[] t = str;
1030     }
1031    
1032
1033     private alias Parse_Case!(t) c1;
1034     static if(c1.Match)
1035     {
1036         static const bool Match = true;
1037         private static const char[] n1 = DropWhiteF(str[t.length..$]);
1038         static if(n1.length > 1 && n1[0] == '|')
1039         {
1040             private alias Parse_Cases!(n1[1..$]) c2;
1041             static if(c2.Match)
1042             {
1043                 alias T!(c1, c2.Disjuncts) Disjuncts;
1044                 static const char[] Remaining = c2.Remaining;
1045             }
1046             else
1047             {
1048                 alias T!(c1) Disjuncts;
1049                 static const char[] Remaining = str[t.length..$];
1050             }
1051         }
1052         else
1053         {
1054             static alias T!(c1) Disjuncts;
1055             static const char[] Remaining = str[t.length..$];
1056         }
1057         static const char[] Used = str[0 .. $-Remaining.length];
1058     }
1059     else
1060     {
1061         //pragma(msg,__LINE__.stringof~":"~str);
1062         static const bool Match = false;
1063     }
1064 }
1065
1066
1067 struct UnittestParse_Cases
1068 {
1069     // Tests
1070     alias Parse_Cases!("Act/foo| For/Bar ") Parse_Cases_test1;
1071     static assert(Parse_Cases_test1.Match);
1072     static assert(Parse_Cases_test1.Remaining == "");
1073     static assert(Parse_Cases_test1.Used == "Act/foo| For/Bar ");
1074     static assert(Parse_Cases_test1.Disjuncts[0].Action == "Act");
1075     static assert(Parse_Cases_test1.Disjuncts[1].Action == "For");
1076
1077     alias Parse_Cases!("Act/foo| For Bar ") Parse_Cases_test2;
1078     static assert(Parse_Cases_test2.Match);
1079     static assert(Parse_Cases_test2.Remaining == "| For Bar ");
1080     static assert(Parse_Cases_test2.Used == "Act/foo");
1081     static assert(Parse_Cases_test2.Disjuncts[0].Action == "Act");
1082 }
1083
1084
1085
1086 /************************************************
1087     Parse a Rule (Rule ::= ID ":" Cases ";")
1088 */
1089 struct Parse_Rule (char[] str)
1090 {
1091         // parse off the rule name
1092     static private alias Parse_ID!(str) name;
1093     static if(name.Match)
1094     {
1095             // parse off to the ':'
1096         static private const char[] n1 = DropWhiteF(name.Remaining);
1097         static if(n1.length > 1 && n1[0] == ':')
1098         {
1099                 // pares the cases
1100             static private alias Parse_Cases!(n1[1..$]) cases;
1101             static if(cases.Match)
1102             {
1103                 static private const char[] n2 = DropWhiteF(cases.Remaining);
1104                 static if(n2.length >= 1 && n2[0] == ';')
1105                 {
1106                     static const bool Match = true;
1107                     static const char[] Name = name.Text;
1108                     static const char[] Remaining = n2[1..$];
1109                     static const char[] Used = str[0 .. $-Remaining.length];
1110                     static alias cases.Disjuncts Disjuncts;
1111                 }
1112                 else
1113                 {
1114                     //pragma(msg,__LINE__.stringof~":"~n2~\n\n);
1115                     static const bool Match = false;
1116                 }
1117             }
1118             else
1119             {
1120                 //pragma(msg,__LINE__.stringof~":"~str);
1121                 static const bool Match = false;
1122             }
1123         }
1124         else
1125         {
1126             //pragma(msg,__LINE__.stringof~":"~str);
1127             static const bool Match = false;
1128         }
1129     }
1130     else
1131     {
1132         //pragma(msg,__LINE__.stringof~":"~str);
1133         static const bool Match = false;
1134     }
1135 }
1136 struct UnittestParse_Rule
1137 {
1138     //tests
1139     alias Parse_Rule!("Foo:bar/baz | pig/owl*horse ;  ") Parse_Rule_test1;
1140     static assert(Parse_Rule_test1.Match);
1141     static assert(Parse_Rule_test1.Used == "Foo:bar/baz | pig/owl*horse ;");
1142     static assert(Parse_Rule_test1.Remaining == "  ");
1143     static assert(Parse_Rule_test1.Name == "Foo");
1144     static assert(Parse_Rule_test1.Disjuncts[0].Action == "bar");
1145     static assert(Parse_Rule_test1.Disjuncts[0].Clauses[0].Text == "baz");
1146     static assert(Parse_Rule_test1.Disjuncts[1].Action == "pig");
1147 }
1148
1149 char[] strOf(int v)
1150 {
1151     if(v==0) return "0";
1152
1153     char[] ret;
1154
1155     while(v)
1156     {
1157         ret = cast(char)('0' + (v%10)) ~ ret;
1158         v/=10;
1159     }
1160     return ret;
1161 }
1162
1163 char[] GramParts(char[] str)
1164 {
1165     char[] ret, match;
1166
1167     int i;
1168
1169     while(str.length > 0)
1170     {
1171         auto tmp = FindChar!(';')(str);
1172         if(ReduceWhite(tmp) != "")
1173         {
1174             ret ~= "Parse_Rule!(\""~ReduceWhite(tmp)~"\"), ";
1175             match ~= "Reductions["~strOf(i)~"].Match && ";
1176         }
1177         str = str[tmp.length..$];
1178     }
1179
1180     return
1181         "alias T!("~ret[0..$-2]~") Reductions;\n"
1182         "const bool Match = "~match[0..$-4]~";";
1183 }
1184
1185
1186 /************************************************
1187     Parse a Grammar (Grammar ::= Rule Grammar | Rule)
1188 */
1189 template Parse_Grammar(char[] str)
1190 {
1191     debug(dparse_verbose) pragma(msg,GramParts(str));
1192     mixin(GramParts(str));
1193 }
1194
1195 struct UnittestParse_Grammar
1196 {
1197     alias Parse_Grammar!("Foo:bar/baz | pig/owl*horse ;  ") Parse_Grammar_test1;
1198
1199     static assert(Parse_Grammar_test1.Match);
1200     static assert(Parse_Grammar_test1.Reductions[0].Name == "Foo");
1201     static assert(Parse_Grammar_test1.Reductions[0].Disjuncts[0].Action == "bar");
1202     static assert(Parse_Grammar_test1.Reductions[0].Disjuncts[0].Clauses[0].Text == "baz");
1203     static assert(Parse_Grammar_test1.Reductions[0].Disjuncts[1].Action == "pig");
1204     static assert(Parse_Grammar_test1.Reductions[0].Disjuncts[1].Clauses[0].Text == "owl");
1205 }
1206
1207 template MakeMixin(char[] starter, char[] str)
1208 {
1209     const char[] MakeMixin =
1210     MakeMixin_fn(str,starter);
1211     debug(dparse_verbose) pragma(msg,MakeMixin);
1212 }
1213
1214 char[] MakeMixin_fn(char[] str,char[] starter)
1215 {
1216     char[] ret;
1217
1218     while(str.length > 0)
1219     {
1220         auto tmp = FindChar!(';')(str);
1221         if(ReduceWhite(tmp) != "")
1222         {
1223             ret ~=
1224             ("PObject Terminal(char[] name : \""~GetID(FindChar!(':')(tmp)[0..$-1])~"\")(IParser p)"
1225             "{"
1226                 "return Rule!(typeof(this), Parse_Rule!(\""~ReduceWhite(tmp)~"\"))(this,p);"
1227             "}\n");
1228         }
1229
1230         str = str[tmp.length..$];
1231     }
1232
1233     return ret ~"alias Terminal!(\""~starter~"\") Parser;\n";
1234 }
1235
1236
1237
1238 enum Places
1239 {
1240     Back,
1241     Revert,
1242     Start
1243 }
1244
1245 template CaseLable(int i)
1246 {
1247     const int Back   = i*(Places.max+1) + Places.Back;
1248     const int Revert = i*(Places.max+1) + Places.Revert;
1249     const int Start  = i*(Places.max+1) + Places.Start;
1250 //  debug(dparse_verbose) pragma(msg, ">> CaseLabel!("~itoa!(i)~") = { Back : "~itoa!(Back)~", Revert : "~itoa!(Revert)~", Start : "~itoa!(Start)~"}");
1251 }
1252
1253 /****
1254 PARSER   : RULE PARSER | ;
1255 RULE     : ID.name ":" CASE ALT ";";
1256 ALT  : "|" CASE ALT | ;
1257 CASE     : ID.action "/" SEQUENCE;
1258 SEQUENCE : ID SEQUENCE | ;
1259 */
1260 debug(dParse_runtime) int counter = 0;
1261
1262 debug(dParse_runtime) int tager = 0;
1263
1264 private struct Frame{uint pos; uint rule; uint count; debug(dParse_runtime) int tag;}
1265
1266 PObject Rule(ParserBase,rule)(ParserBase parserBase, IParser p)
1267 {
1268     debug(dParse_runtime)
1269     {
1270         int ind = counter++;
1271         writef("Try\t(%d)%s...\n", ind, rule.Name);
1272     }
1273     debug(dParse_runtime) scope(success) writef("Done\t(%d)%s\n", ind, rule.Name);
1274     debug(dParse_runtime) scope(failure) writef("FAILED\t(%d)%s\n", ind, rule.Name);
1275
1276     static const char[] nameIs = rule.Name;
1277     //debug(dParse_light)
1278     //pragma(msg, "<used>"~nameIs~"</used>");
1279
1280     Stack!(Frame) backups;
1281     Frame store;
1282
1283     debug(dparse_verbose) pragma(msg,"Build: \""~rule.Name~"\"" );
1284         // record start location
1285     uint start = p.pos;
1286         // try case
1287
1288     caseLoop: foreach(ci,casev;rule.Disjuncts)
1289     {
1290         debug(dParse_runtime) writef("*\t(%d)%s:%s...\n", ind, rule.Name,casev.Action);
1291
1292             // dump all checkpoints
1293         backups.Empty();
1294
1295             // return to start location
1296         static if(ci != 0)
1297         {
1298             debug(dParse_runtime) writef("backing\t (%d):%d\n", ind ,start);
1299             p.pos = start;
1300         }
1301
1302             // allocate storage for returns
1303         const int count = casev.Clauses.length;
1304         PObject[count] temps;
1305         static assert(count == temps.length);
1306
1307         // debug(dparse_verbose) pragma(msg, "ICE from "~rule.name);
1308         debug(dparse_verbose) pragma(msg, "\tfor \""~rule.Name~"\" doing case #"~itoa!(ci)~" action = \""~casev.Action~"\", length = "~itoa!(count));
1309
1310         static const int FirstCase = -1;
1311
1312         int action = FirstCase;
1313         int back = 0;
1314
1315         mixin("failBack_"~itoa!(ci)~":;");  // insert uniqe label here
1316
1317         switch(action)
1318         {
1319             case FirstCase:
1320
1321             foreach(index, cl; casev.Clauses)
1322             {
1323                 debug(dParse_runtime) p.mark();
1324
1325                 static if(/*ci == 0 &&*/ index == 0 && nameIs == cl.Text)
1326                     pragma(msg, "Directly recursive rule: "~nameIs)
1327                     // static stuff
1328
1329                 debug(dParse_runtime) writef("Attemping clause %s (%d)...\n", cl.Text, index);
1330
1331                 debug(dparse_verbose) pragma(msg, "\t\tgenerating clause: \""~cl.Text~"\" ("~itoa!(index)~")");
1332
1333                 static if(cl.Type != ItemType.single)
1334                 {
1335                     // on [*+?] add a set
1336                     temps[index] = new PObjectSet();
1337                 }
1338
1339                 static if(cl.Type == ItemType.single || cl.Type == ItemType.plus)
1340                 {
1341                     // for cases where empty match is not allowed
1342                         // get one
1343                     debug(dparse_verbose) pragma(msg, "\t\t\t recurse in from \""~rule.Name~"\" on \""~cl.Text~\"\n);
1344                         //alias DotAction!(nameIs, cl.Text) _;
1345                     auto tmpStore1 = parserBase.Terminal!(cl.Text)(p);
1346                     assert(tmpStore1 !is null);
1347                     debug(dParse_runtime) writef("clause %s (%d) returned a %s fail=%s\n", cl.Text, index, tmpStore1.BaseName, tmpStore1.fail);
1348                     debug(dparse_verbose) pragma(msg, "\n\t\t\t recurse out from \""~rule.Name~"\" on \""~cl.Text~\");
1349
1350                         // test it
1351                     if(tmpStore1.fail())
1352                     {
1353                         if(backups.Count != 0) // checkpoints remain
1354                         {
1355                             store = backups.Pop();  // get checkpoint
1356                             debug(dParse_runtime) writef(">>>>>Poped %d\n", store.tag);
1357                             p.pos = store.pos;      // move input steram back
1358                             action = store.rule;    // set point to return to
1359                             back = store.count;     // set backput amount
1360
1361                             // goto uniqe label from here
1362                             mixin("goto failBack_"~itoa!(ci)~";");
1363                         }
1364                         else
1365                         {
1366                         // try another
1367                             continue caseLoop;
1368                         }
1369                     }
1370
1371                         // on good: store
1372                     static if(cl.Type != ItemType.single)
1373                     {
1374                         (cast(PObjectSet)temps[index]).Add(tmpStore1);
1375                     }
1376                     else
1377                     {
1378                         temps[index] = tmpStore1;
1379                     }
1380                 }
1381
1382                 case CaseLable!(index).Back:;
1383
1384                 static if(cl.Type != ItemType.single)
1385                 {
1386                     debug(dParse_runtime)
1387                     {
1388                         writef("ReAttemping clause %s (%d)...\n", cl.Text, index);
1389                         store.tag = tager++;
1390                         writef("=======saved  %d @ ", store.tag);
1391                         p.mark();
1392                     }
1393                     store.pos = p.pos;
1394                     store.rule = CaseLable!(index).Revert;
1395                     store.count = (cast(PObjectSet)temps[index]).Count();
1396
1397                     debug(dParse_runtime) writef("<<<<<< pushed  %d\n", store.tag);
1398                     backups.Push(store);
1399
1400                     debug(dparse_verbose) pragma(msg, "\t\t\trecurse in from \""~rule.Name~"\" on \""~cl.Text~\"\n);
1401                         //alias DotAction!(nameIs, cl.Text) __;
1402                     auto tmpStore2 = parserBase.Terminal!(cl.Text)(p);
1403                     assert(tmpStore2 ! is null);
1404                     debug(dParse_runtime) writef("clause %s (%d) returned a %s fail=%s\n", cl.Text, index, tmpStore2.BaseName, tmpStore2.fail);
1405                     debug(dparse_verbose) pragma(msg, "\n\t\t\trecurse out from \""~rule.Name~"\" on \""~cl.Text~\");
1406
1407                     if(!tmpStore2.fail())
1408                     {
1409                         (cast(PObjectSet)temps[index]).Add(tmpStore2);
1410
1411                         static if(cl.Type == ItemType.star || cl.Type == ItemType.plus)
1412                         {
1413                             goto case CaseLable!(index).Back;
1414                         }
1415                     }
1416
1417                         // label clause
1418                     goto case CaseLable!(index).Start;
1419                     case CaseLable!(index).Revert:;
1420
1421                     (cast(PObjectSet)temps[index]).Back(back);       // back out parsed data
1422                     case CaseLable!(index).Start:;
1423                 }
1424             }
1425         }
1426         debug(dparse_verbose) pragma(msg,"\tdoing Action \""~casev.Action~\");
1427         debug(dParse_runtime) writef("doing Action \""~casev.Action~\"\n);
1428         auto ret = SpecialAction!(ParserBase,casev.Action)(parserBase,temps);
1429         debug(dParse_runtime) writef("Action \""~casev.Action~"\" done\n");
1430         debug(dParse_runtime) writef("\treturn (%d)Act '%s' fail=%s\n", ind, casev.Action, ret.fail);
1431         return ret;
1432     }
1433
1434     debug(dParse_runtime) writef("Return (%d)failed\n", ind);
1435     return new PObjectFail("Failed while looking for "~nameIs~\n);//    debug(dparse_verbose) pragma(msg, ">>"__FILE__":"~itoa!(__LINE__)~": is this right?");
1436     debug(dparse_verbose) pragma(msg, "Done: "~rule.Name);
1437 }
1438
1439 template DotAction(char[] from, char[] to)
1440 {
1441     pragma(msg, from ~ " -> " ~ to);
1442 }
1443
1444 /************************************************
1445     A basic stack struct used internally to the parser implementation
1446 */
1447 struct Stack(T)
1448 {
1449     debug(TypeReport) pragma(msg, ">>T-"__FILE__~":"~itoa!(__LINE__)~": "~typeof(*this).stringof);
1450     private T[] data = null;
1451     private int at = 0;
1452
1453         /**
1454             Push an item onto the stack
1455         */
1456     void Push(T din)
1457     {
1458         if(data.length <= at) data.length = at + 10;
1459         data[at] = din;
1460         at++;
1461     }
1462
1463         /**
1464             Pop an item off the stack
1465
1466             Throw an "Error" exception on underflow
1467         */
1468     T Pop()
1469     {
1470         if(at)
1471         {
1472             at--;
1473             return data[at];
1474         }
1475         else
1476             throw new Error("Stack Underflow");
1477     }
1478
1479         /// return the number of items on the stack
1480     uint Count(){return at;}
1481
1482         /// Clear the stack
1483     void Empty(){ at = 0; }
1484
1485         /// Dump everything
1486     void Dist(){delete data;}
1487
1488     unittest // unittest Stack!(T)
1489     {
1490         writef("unittest@"__FILE__":"~itoa!(__LINE__)~"!("~T.stringof~")\n");
1491
1492         Stack!(int) st;
1493         int i;
1494         for(i = 0; i < 20; i++)
1495         {
1496             assert(st.Count == i);
1497             st.Push(i);
1498         }
1499
1500         i--;
1501
1502         for(; i >= 0; i--)
1503         {
1504             int j = st.Pop;
1505             assert(j == i);
1506             assert(st.Count == i);
1507         }
1508
1509         assert(st.Count == 0);
1510     }
1511 }
1512
1513 debug(dparse_unittest)
1514 {
1515     // Unittest code
1516     unittest
1517     {
1518         writef("unittest@"__FILE__":"~itoa!(__LINE__)~\n);
1519
1520         P p;
1521         data d = new data;
1522         char[][] pass =
1523             [
1524             "BB"[],         //root > next baz > (baz) baz)
1525             "HOB",          //root > next baz > (owl* horse owl) baz > (() horse owl) baz
1526             "OHOB",         //root > next baz > (owl* horse owl) baz > ((owl) horse owl) baz
1527             "OOHOB",        //root > next baz > (owl* horse owl) baz > ((owl owl) horse owl) baz
1528             "BHO",          //root > baz horse owl
1529             "CHB",          //root > cat horse+ owl* baz > cat (horse) () baz
1530             "CHOB",         //root > cat horse+ owl* baz > cat (horse) (owl) baz
1531             "CHHOB",        //root >  cat horse+ owl* baz > cat (horse horse) (owl) baz
1532             "CCC",          //root > cat* cat cat > (cat) cat cat
1533             "KQKQKQKQQKQK", // root > twin* car > (twin twin twin) (car) > ((king qween) (king qween) (king qween)) (king qween qween king)
1534             ],
1535             fail =
1536             [
1537             "BHHO"[],
1538             "COB",
1539             "CB",
1540             "CHHOOB",
1541             ];
1542
1543         foreach(char[] passTest; pass)
1544         {
1545             d.dat = passTest;
1546             d.i=0;
1547             assert(!p.Parser(d).fail, \"~passTest~"\" failed to parse");
1548         }
1549
1550         foreach(char[] failTest; fail)
1551         {
1552             d.dat = failTest;
1553             d.i=0;
1554              assert(p.Parser(d).fail, \"~failTest~"\" failed to fail to parse");
1555         }
1556     }
1557
1558     struct P
1559     {
1560         PObject Terminal(char[] str : "baz")(IParser i)   {data d = cast(data)i; if(d.i >= d.dat.length || d.dat[d.i] != 'B') return new PObjectFail; d.i++; return new PObjectBox!(char)('B');}
1561         PObject Terminal(char[] str : "owl")(IParser i)   {data d = cast(data)i; if(d.i >= d.dat.length || d.dat[d.i] != 'O') return new PObjectFail; d.i++; return new PObjectBox!(char)('O');}
1562         PObject Terminal(char[] str : "horse")(IParser i) {data d = cast(data)i; if(d.i >= d.dat.length || d.dat[d.i] != 'H') return new PObjectFail; d.i++; return new PObjectBox!(char)('H');}
1563         PObject Terminal(char[] str : "cat")(IParser i)   {data d = cast(data)i; if(d.i >= d.dat.length || d.dat[d.i] != 'C') return new PObjectFail; d.i++; return new PObjectBox!(char)('C');}
1564         PObject Terminal(char[] str : "king")(IParser i)  {data d = cast(data)i; if(d.i >= d.dat.length || d.dat[d.i] != 'K') return new PObjectFail; d.i++; return new PObjectBox!(char)('K');}
1565         PObject Terminal(char[] str : "qween")(IParser i) {data d = cast(data)i; if(d.i >= d.dat.length || d.dat[d.i] != 'Q') return new PObjectFail; d.i++; return new PObjectBox!(char)('Q');}
1566
1567         PObject Action(char[] str : "bar") (PObject[1] i){return new PObjectVector!(1)(i);}
1568         PObject Action(char[] str : "beer")(PObject[2] i){return new PObjectVector!(2)(i);}
1569         PObject Action(char[] str : "pig") (PObject[3] i){return new PObjectVector!(3)(i);}
1570         PObject Action(char[] str : "keg") (PObject[4] i){return new PObjectVector!(4)(i);}
1571
1572         const char[] gram = "
1573             next :
1574                 bar / baz |
1575                 pig / owl * horse owl ;
1576             root :
1577                 beer / next baz |
1578                 pig  / baz horse owl |
1579                 keg  / cat horse+ owl? baz |
1580                 pig  / cat* cat cat |
1581                 beer / twin * car;
1582             twin :
1583                 beer / king qween* ;
1584             car :
1585                 keg / king qween qween king;
1586                 ";
1587
1588         const char[] gram2 = import("dmp.g");
1589
1590         PObject Terminal(char[] str)(IParser i){return null;}
1591         PObject Action(char[] str)(PObject[] i){return null;}
1592
1593         mixin(MakeMixin!("root",ReduceWhite(gram)));
1594 //      mixin(MakeMixin!("Module",ReduceWhite(gram2)));
1595     }
1596
1597     class data : IParser
1598     {
1599         char[] dat;
1600         uint i =0;
1601         uint pos()      {return i;}
1602         void pos(uint j){ i = j;}
1603     }
1604     void main(){}
1605 }
Note: See TracBrowser for help on using the browser.