root/trunk/enki/Expression.d

Revision 289, 28.0 kB (checked in by h3r3tic, 4 years ago)

ported Enki to Tango; bootstraps, no other tests done yet

Line 
1 /+
2     Copyright (c) 2006 Eric Anderton, BCS
3
4     Permission is hereby granted, free of charge, to any person
5     obtaining a copy of this software and associated documentation
6     files (the "Software"), to deal in the Software without
7     restriction, including without limitation the rights to use,
8     copy, modify, merge, publish, distribute, sublicense, and/or
9     sell copies of the Software, and to permit persons to whom the
10     Software is furnished to do so, subject to the following
11     conditions:
12
13     The above copyright notice and this permission notice shall be
14     included in all copies or substantial portions of the Software.
15
16     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23     OTHER DEALINGS IN THE SOFTWARE.
24 +/
25 module enki.Expression;
26
27 private import enki.types;
28 private import enki.CodeGenerator;
29 private import enki.EnkiBackend;
30 private import enki.Rule;
31
32 private import tango.io.Stdout;
33
34 class Expression  : IRenderable{
35     SubExpression[][] terms;
36     bool areAllShort;
37     bool[] isShort;
38     String errorText;
39    
40     public this(SubExpression[][] terms){
41         this.terms = terms;
42         assert(terms.length > 0);
43     }
44    
45     public this(SubExpression[] factors...){
46         assert(factors.length > 0);
47         this.terms ~= factors.dup;
48     }
49
50     public void semanticPass(Rule thisRule,BaseEnkiParser root){
51         // determine short expressions and error expressions
52         areAllShort = true;
53         String[] keyTerms;
54         foreach(uint i,SubExpression[] term; terms){
55             bool thisIsShort = true;
56            
57             auto firstShort = cast(ShortExpression)term[0];
58             if(firstShort){
59                 if(firstShort.isDescribable){
60                     keyTerms ~= firstShort.getDescription();
61                 }
62             }
63            
64             // test entire term for shorthand and run semantic passes
65             foreach(SubExpression expr; term){
66                 if(!cast(ShortExpression)expr){
67                     thisIsShort = false;
68                 }
69                 expr.semanticPass(thisRule,root);
70             }
71            
72             this.isShort ~= thisIsShort;
73             if(!thisIsShort) areAllShort = false;
74         }
75        
76         // build up the error expression
77         errorText = "";
78         foreach(i,term; keyTerms){
79             if(!root.isTerminal(term)) continue;
80            
81             String terminalName = root.getTerminalName(term);
82             if(i == 0){
83                 errorText ~= terminalName;
84             }
85             else if(i == keyTerms.length-1 && keyTerms.length > 1){
86                 errorText ~= " or " ~ terminalName;
87             }
88             else{
89                 errorText ~= ", " ~ terminalName;
90             }
91         }
92         if(errorText.length > 0){
93             errorText = "Expected " ~ errorText ~ "."; 
94         }
95     }
96    
97     public String resolveBindingType(String name,BaseEnkiParser root){
98         String type = null; // doesn't exist
99                
100         debug writefln("Expression.resolveBindingType %s",name);
101         foreach(SubExpression[] term; terms){
102             foreach(SubExpression expr; term){
103                 String exprType = expr.resolveBindingType(name,root);
104                 if(!type) type = exprType;
105                 else if(exprType && type != exprType){
106                     debug writefln("type %s vs type %s",type,exprType);
107                     throw new Exception("Cannot resolve type of binding '" ~ name ~ "' as it resolves to more than one type.");
108                 }
109             }
110         }
111         return type;   
112     }
113    
114     public void render(CodeGenerator generator,Statement pass,Statement fail){
115         Statement clearError = new Call("clearErrors");
116         Statement raiseError;
117        
118         if(errorText.length > 0 && !fail.isEmpty){
119              raiseError = new CompositeStatement(
120                 //clearError, // experimental
121                 new Call("setError","\"" ~ errorText ~"\"")
122              );
123         }
124         else{
125             raiseError = Statement.empty;
126         }       
127        
128         if(areAllShort){
129             String text = "";
130             bool isFirstTerm = true;
131             foreach(SubExpression[] term; terms){
132                 if(isFirstTerm){
133                     isFirstTerm = false;
134                     text ~= "(";
135                 }
136                 else{
137                     text ~= " || (";
138                 }
139                 bool isFirstFactor = true;
140                 foreach(SubExpression factor; term){
141                     if(isFirstFactor){
142                         isFirstFactor = false;
143                     }
144                     else{
145                         text ~= " && ";
146                     }
147                     text ~= (cast(ShortExpression)factor).renderShort();
148                 }
149                 text ~= ")";
150             }
151            
152            
153             with(generator){
154                 auto startLabel = getUniqueLabel("start");
155                 emit("{//Expression");
156                 indent();
157                     emit("uint " ~ startLabel ~ " = position;");
158                     renderIfTest(text,
159                         new CompositeStatement(clearError,pass),
160                         new CompositeStatement(
161                             raiseError,
162                             new Literal("position = " ~ startLabel ~ ";"), // unwind on failure of expression
163                             fail
164                         )
165                     );
166                 unindent();
167                 emit("}");
168             }
169         }
170         else{
171             debug writefln("isShort.length %d terms.length",isShort.length,terms.length);
172             assert(isShort.length == terms.length);
173             with(generator){
174                 auto matchLabel = new Label("match");
175                 auto gotoMatch = new Goto(matchLabel);
176                 auto startLabel = getUniqueLabel("start");
177                
178                 emit("{//Expression");
179                 indent();
180                     emit("uint " ~ startLabel ~ " = position;");
181                     foreach(uint i,SubExpression[] term; terms){ // or grouping
182                         if(isShort[i]){
183                             String text = "";
184                             bool isFirstFactor = true;
185                             foreach(SubExpression expr; term){
186                                 if(isFirstFactor){
187                                     isFirstFactor = false;
188                                 }
189                                 else{
190                                     text ~= " && ";
191                                 }
192                                 text ~= (cast(ShortExpression)expr).renderShort();
193                             }
194                             renderIfTest(text, gotoMatch, Statement.empty);
195                         }
196                         else{
197                             auto mismatchLabel = new Label("mismatch");
198                             auto gotoMismatch = new Goto(mismatchLabel);                           
199                             foreach(SubExpression expr; term){ // and grouping
200                                 renderFail(expr,gotoMismatch);
201                             }
202                             render(gotoMatch);                         
203                             render(mismatchLabel);
204                         }                       
205                     }
206                     render(raiseError);
207                     emit("position = " ~ startLabel ~ ";"); // unwind on failure of expression
208                     render(fail);
209                     render(matchLabel);
210                     render(clearError);
211                     render(pass);
212                 unindent();
213                 emit("}");
214             }
215         }
216     }
217    
218     public String toBNF(){
219         String result = "";
220        
221         foreach(uint i,SubExpression[] term; terms){
222             if(i != 0) result ~= " |";
223             foreach(SubExpression expr; term){
224                 result ~= " " ~ expr.toBNF();
225             }
226         }
227         return result;
228     }
229 }
230
231 interface SubExpression : IRenderable{
232     public void semanticPass(Rule thisRule,BaseEnkiParser root);
233     public String resolveBindingType(String name,BaseEnkiParser root);
234     public void render(CodeGenerator generator,Statement pass,Statement fail);
235     public String toBNF(); 
236 }
237
238 alias SubExpression[] Term;
239
240 interface ShortExpression{
241     public String getDescription();
242     public bool isDescribable();
243     public String renderShort();
244 }
245
246 class Production : SubExpression, ShortExpression{
247     String name;
248     String desc;
249     String type;
250     Binding binding;
251     ProductionArg[] args;
252    
253     public this(String name,Binding binding,ProductionArg[] args...){
254         this.name = name;
255         this.desc = name;
256         this.binding = binding;
257         this.args = args.dup;
258     }
259    
260     public String getDescription(){
261         return desc;   
262     }
263    
264     //TODO: determine this via some semantic pass and if the production is user-defined or not.
265     public bool isDescribable(){
266         return true;
267     }
268    
269     public String renderShort(){
270         String result = "parse_" ~ name ~ "(";
271        
272         foreach(uint i,arg; args){
273             if(i == 0){
274                 result ~= arg.toString();
275             }
276             else{
277                 result ~= "," ~ arg.toString();
278             }
279         }
280    
281         if(binding){
282             result ~= ")" ~ binding.postAssignExpr();
283         }
284         else{
285             result ~= ").success";
286         }
287        
288         return result;
289     }
290    
291     public void semanticPass(Rule thisRule,BaseEnkiParser root){
292         type = root.getTypeForRule(name);
293        
294         //TODO: match each arg up to the rule predicate params, and determine each type
295        
296         foreach(arg; args){
297             BindingProductionArg bindingArg = cast(BindingProductionArg)arg;
298             if(bindingArg){
299                 bindingArg.semanticPass(thisRule,root);
300             }
301         }
302         if(binding) binding.semanticPass(thisRule,root);
303     }
304    
305     public String resolveBindingType(String name,BaseEnkiParser root){
306         debug writefln("Production.resolveBindingType (%s) %s",this.name,name);
307         if(binding && binding.name == name){
308             debug writefln("--match %s ",type);
309             return type;
310         }
311         return null;
312     }
313
314     public void render(CodeGenerator generator,Statement pass,Statement fail){
315         generator.renderIfTest(renderShort(), pass, fail);
316     }
317    
318     public String toBNF(){
319         String result = " " ~ name;
320         if(args.length > 0){
321             result ~= "!(";
322             foreach(uint i,arg; args){
323                 if(i == 0){
324                     result ~= arg.toBNF();
325                 }
326                 else{
327                     result ~= "," ~ arg.toBNF();
328                 }
329             }
330             result ~= ")";
331         }
332         if(binding) result ~= binding.toBNF();
333         return result;
334     }
335 }
336
337 abstract class ProductionArg{
338     public String toString();
339     public String toBNF();
340 }
341
342 class StringProductionArg : ProductionArg{
343     String value;
344     public this(String value){
345         this.value = value;
346     }
347    
348     public String toString(){
349     //TODO: use a smart cast here...?
350         return "\"" ~ value ~ "\"";
351     }
352    
353     public String toBNF(){
354         return toString();
355     }
356 }
357
358 class BindingProductionArg : ProductionArg{
359     String name;
360     String type;
361    
362     public this(String name){
363         this.name = name;
364     }
365
366     public void semanticPass(Rule thisRule,BaseEnkiParser root){
367         thisRule.resolveBinding(name);
368         type = thisRule.resolveBindingType(name,root); 
369     }
370    
371     public String toString(){
372     //TODO: use a smart cast here...?
373         return "bind_" ~ name;
374     }
375    
376     public String toBNF(){
377         return name;
378     }
379 }
380
381 class GroupExpr : SubExpression{
382     Expression expr;
383     Binding binding;
384    
385     public this(Expression expr,Binding binding){
386         this.expr = expr;
387         this.binding = binding;
388     }
389    
390     public String resolveBindingType(String name,BaseEnkiParser root){
391         String type = null; // doesn't exist
392                
393         debug writefln("GroupExpr.resolveBindingType");
394         String exprType = null;
395         if(binding && binding.name == name){
396             exprType = root.getParseType();
397         }
398         type = expr.resolveBindingType(name,root);
399        
400         if(!type) type = exprType;
401         else if(exprType && type != exprType){
402             debug writefln("type %s vs type %s",type,exprType);
403             throw new Exception("Cannot resolve type of binding '" ~ name ~ "' as it resolves to more than one type.");
404         }
405        
406         return type;       
407     }
408    
409     public void semanticPass(Rule thisRule,BaseEnkiParser root){
410         expr.semanticPass(thisRule,root);
411         if(binding) binding.semanticPass(thisRule,root);
412     }   
413
414     public void render(CodeGenerator generator,Statement pass,Statement fail){
415         if(binding){
416             with(generator){
417                 auto startLabel = getUniqueLabel("start");
418                 emit("{//GroupExpr");
419                 indent();
420                     emit("uint " ~ startLabel ~ " = position;");
421                     render(expr,pass,fail);
422                     emit(binding.assignExpr(generator.getParseType(),"sliceData(" ~ startLabel ~ ",position)")~";");               
423                 unindent();
424                 emit("}");
425             }
426         }
427         else{
428             generator.render(expr,pass,fail);
429         }
430     }
431    
432     public String toBNF(){
433         if(binding) return "(" ~ expr.toBNF() ~ ")" ~ binding.toBNF() ~ " ";
434          return "(" ~ expr.toBNF() ~ ")";
435     }   
436 }
437
438 class OptionalExpr : SubExpression{
439     Expression expr;
440     Binding binding;
441    
442     public this(Expression expr,Binding binding){
443         this.expr = expr;
444         this.binding = binding;
445     }
446
447     public String resolveBindingType(String name,BaseEnkiParser root){
448         String type = null; // doesn't exist
449                
450         debug writefln("OptionalExpr.resolveBindingType");
451         String exprType = null;
452         if(binding && binding.name == name){
453             exprType = root.getParseType();
454         }
455         type = expr.resolveBindingType(name,root);
456        
457         if(!type) type = exprType;
458         else if(exprType && type != exprType){
459             debug writefln("type %s vs type %s",type,exprType);
460             throw new Exception("Cannot resolve type of binding '" ~ name ~ "' as it resolves to more than one type.");
461         }
462        
463         return type;           
464     }
465    
466     public void semanticPass(Rule thisRule,BaseEnkiParser root){
467         expr.semanticPass(thisRule,root);
468         if(binding) binding.semanticPass(thisRule,root);
469     }   
470    
471     public void render(CodeGenerator generator,Statement pass,Statement fail){
472         with(generator){
473             auto startLabel = getUniqueLabel("start");
474             emit("{//OptionalExpr");
475             indent();
476                 if(binding) emit("uint " ~ startLabel ~ " = position;");
477                
478                 render(expr);
479                
480                 if(binding) emit(binding.assignExpr(generator.getParseType(),"sliceData(" ~ startLabel ~ ",position)")~";");               
481             unindent();
482             emit("}");
483         }       
484     }
485    
486     public String toBNF(){
487         if(binding) return "[" ~ expr.toBNF() ~ "]" ~ binding.toBNF()~ " ";
488          return "[" ~ expr.toBNF() ~ "]";
489     }       
490 }
491
492 class ZeroOrMoreExpr : SubExpression{
493     Expression expr;
494     Binding binding;
495     SubExpression term;
496     String termErrorText;
497    
498     public this(Expression expr,Binding binding,SubExpression term){
499         this.expr = expr;
500         this.binding = binding;
501         this.term = term;
502        
503         //if(!this.term){
504         //  this.term = new Expression(new Production("eoi",null));
505         //}
506     }
507    
508     public String resolveBindingType(String name,BaseEnkiParser root){
509         debug writefln("ZeroOrMoreExpr.resolveBindingType");
510         String type = null;
511         String exprType = null;
512                
513         if(binding && binding.name == name){
514             type = root.getParseType();
515         }
516         exprType = expr.resolveBindingType(name,root);
517        
518         if(!type) type = exprType;
519         else if(exprType && type != exprType){
520             debug writefln("type %s vs type %s",type,exprType);
521             throw new Exception("Cannot resolve type of binding '" ~ name ~ "' as it resolves to more than one type.");
522         }
523        
524         if(term){
525             exprType = term.resolveBindingType(name,root);
526            
527             if(!type) type = exprType;
528             else if(exprType && type != exprType){
529                 debug writefln("type %s vs type %s",type,exprType);
530                 throw new Exception("Cannot resolve type of binding '" ~ name ~ "' as it resolves to more than one type.");
531             }
532         }
533
534         return type;       
535     }
536    
537     public void semanticPass(Rule thisRule,BaseEnkiParser root){
538         expr.semanticPass(thisRule,root);
539         if(binding) binding.semanticPass(thisRule,root);
540         if(term){
541             term.semanticPass(thisRule,root);
542            
543             // get the error message for the terminator if it is describable
544             auto firstShort = cast(ShortExpression)term;
545             if(firstShort){
546                 if(firstShort.isDescribable){
547                     String desc = firstShort.getDescription();
548                     if(root.isTerminal(desc)){
549                         termErrorText = "Expected " ~root.getTerminalName(desc);
550                     }
551                 }
552             }
553         }
554     }       
555      
556     public void render(CodeGenerator generator,Statement pass,Statement fail){
557         auto loopStart = new Label("loop");
558         auto loopEnd = new Label("loopend");
559         auto exprStart = new Label("exprStart");
560         auto gotoLoop = new Goto(loopStart);
561         auto gotoLoopEnd = new Goto(loopEnd);
562         auto gotoLoopExprStart = new Goto(exprStart);
563        
564         Statement raiseTermError;
565         if(term && termErrorText.length > 0){
566             raiseTermError = new Call("setError","\"" ~ termErrorText ~ "\"");
567         }
568         else{
569             raiseTermError = Statement.empty;
570         }
571        
572         Statement exprFail = fail;
573         if(!term){
574             exprFail = gotoLoopEnd;
575         }
576        
577         with(generator){
578             auto startLabel = getUniqueLabel("start");
579             auto termPos = getUniqueLabel("termPos");
580            
581             emit("{//ZeroOrMoreExpr");
582             indent();
583                 if(term) emit("uint " ~ startLabel ~ " = position;");
584                 emit("uint " ~ termPos ~ ";");
585                 render(loopStart);
586    
587                 emit(termPos ~ " = position;");
588                 if(term){
589                     render(term,gotoLoopEnd,gotoLoopExprStart);
590                 }
591                 render(exprStart);
592                 render(expr,gotoLoop,gotoLoopEnd);
593                
594                 render(loopEnd);
595                 if(binding) emit(binding.assignExpr(generator.getParseType(),"sliceData(" ~ startLabel ~ "," ~ termPos ~ ")")~";");
596                 render(pass);
597             unindent();
598             emit("}");
599         }
600     }
601    
602     public String toBNF(){
603         String result = "{" ~ expr.toBNF() ~ "}";
604         if(binding) result ~= binding.toBNF();
605         if(term) result ~= term.toBNF();
606         return result;
607     }       
608 }
609
610 class Terminal : SubExpression, ShortExpression{
611     String text;
612     Binding binding;
613    
614     public this(String text,Binding binding){
615         this.text = text;
616         this.binding = binding;
617     }
618    
619     public String getDescription(){
620         return "\\\"" ~ text ~ "\\\"";
621     }   
622    
623     public bool isDescribable(){
624         return false; // set to false, to reduce error noise
625     }   
626    
627     public String renderShort(){       
628         if(binding){
629             return "terminal(\"" ~ text ~ "\")" ~ binding.postAssignExpr();
630         }
631         else{
632             return "terminal(\"" ~ text ~ "\").success";
633         }           
634     }
635    
636     public void semanticPass(Rule thisRule,BaseEnkiParser root){
637         if(binding) binding.semanticPass(thisRule,root);
638     }   
639    
640     public String resolveBindingType(String name,BaseEnkiParser root){
641         debug writefln("Terminal.resolveBindingType %s",text);
642         if(binding && binding.name == name){
643             return root.getParseType();
644         }
645         return null;
646     }   
647    
648     public void render(CodeGenerator generator,Statement pass,Statement fail){
649         generator.renderIfTest(renderShort(), pass, fail);
650     }
651            
652     public String toBNF(){
653         if(binding){
654             return " \"" ~ text ~ "\"" ~ binding.toBNF();
655         }
656         else{
657             return " \"" ~ text ~ "\"";
658         }
659     }   
660 }
661
662 class Regexp : SubExpression, ShortExpression{
663     String text;
664     Binding binding;
665    
666     public this(String text,Binding binding){
667         this.text = text;
668         this.binding = binding;
669     }
670    
671     public String getDescription(){
672         return "`" ~ text ~ "`";
673     }   
674    
675     public bool isDescribable(){
676         return false; // set to false, to reduce error noise
677     }   
678    
679     public String renderShort(){       
680         if(binding){
681             return "regexp(`" ~ text ~ "`)" ~ binding.postAssignExpr();
682         }
683         else{
684             return "regexp(`" ~ text ~ "`).success";
685         }           
686     }
687    
688     public void semanticPass(Rule thisRule,BaseEnkiParser root){
689         if(binding) binding.semanticPass(thisRule,root);
690     }   
691    
692     public String resolveBindingType(String name,BaseEnkiParser root){
693         debug writefln("Regexp.resolveBindingType %s",text);
694         if(binding && binding.name == name){
695             return root.getParseType();
696         }
697         return null;
698     }   
699    
700     public void render(CodeGenerator generator,Statement pass,Statement fail){
701         generator.renderIfTest(renderShort(), pass, fail);
702     }
703            
704     public String toBNF(){
705         if(binding){
706             return "`" ~ text ~ "`" ~ binding.toBNF();
707         }
708         else{
709             return "`" ~ text ~ "`";
710         }
711     }   
712 }
713
714 class Substitution : SubExpression, ShortExpression{
715     Binding binding;
716     String bindingName;
717     String type;
718    
719     public this(String bindingName,Binding binding){
720         this.bindingName = bindingName;
721         this.binding = binding;
722     }
723    
724     public String getDescription(){
725         return "";
726     }   
727    
728     public bool isDescribable(){
729         return false; // set to false, to reduce error noise
730     }   
731    
732     public String renderShort(){
733         //TODO: use a smart cast here from the binding to a string
734         if(binding){
735             return "terminal(convert!(String," ~ type ~ ")(bind_" ~ bindingName ~ "))" ~ binding.postAssignExpr();
736         }
737         else{
738             return "terminal(convert!(String," ~ type ~ ")(bind_" ~ bindingName ~ ")).success";
739         }
740     }
741    
742     public void semanticPass(Rule thisRule,BaseEnkiParser root){
743         thisRule.resolveBinding(bindingName);
744         this.type = thisRule.resolveBindingType(bindingName,root);
745        
746         if(binding) binding.semanticPass(thisRule,root);
747     }   
748    
749     public String resolveBindingType(String name,BaseEnkiParser root){
750         debug writefln("Terminal.resolveBindingType %s",bindingName);
751         if(binding && binding.name == name){
752             return root.getParseType();
753         }
754         return null;
755     }   
756    
757     public void render(CodeGenerator generator,Statement pass,Statement fail){
758         generator.renderIfTest(renderShort(), pass, fail);
759     }
760            
761     public String toBNF(){
762         if(binding){
763             return "." ~ bindingName ~ binding.toBNF();
764         }
765         else{
766             return "." ~ bindingName;
767         }
768     }   
769 }
770
771 class Negate : SubExpression{
772     SubExpression expr;
773    
774     public this(SubExpression expr){
775         this.expr = expr;
776     }
777
778     public String resolveBindingType(String name,BaseEnkiParser root){
779         return expr.resolveBindingType(name,root);         
780     }
781    
782     public void semanticPass(Rule thisRule,BaseEnkiParser root){
783         expr.semanticPass(thisRule,root);
784     }   
785    
786     public void render(CodeGenerator generator,Statement pass,Statement fail){
787         auto matchLabel = new Label("match");
788         auto gotoMatch = new Goto(matchLabel);
789         auto mismatchLabel = new Label("mismatch");
790         auto gotoMismatch = new Goto(mismatchLabel);
791        
792         with(generator){
793             emit("{//Negate");
794             indent();
795                 render(expr,gotoMatch,gotoMismatch);
796                                
797                 render(matchLabel);
798                 render(fail);
799                
800                 render(mismatchLabel);
801                 render(pass);
802             unindent();
803             emit("}");
804         }       
805     }
806    
807     public String toBNF(){
808          return "!" ~ expr.toBNF();
809     }       
810 }
811
812 class Test : SubExpression{
813     SubExpression expr;
814    
815     public this(SubExpression expr){
816         this.expr = expr;
817     }
818
819     public String resolveBindingType(String name,BaseEnkiParser root){
820         return expr.resolveBindingType(name,root);         
821     }
822    
823     public void semanticPass(Rule thisRule,BaseEnkiParser root){
824         expr.semanticPass(thisRule,root);
825     }   
826    
827     public void render(CodeGenerator generator,Statement pass,Statement fail){
828         auto matchLabel = new Label("match");
829         auto gotoMatch = new Goto(matchLabel);
830         auto mismatchLabel = new Label("mismatch");
831         auto gotoMismatch = new Goto(mismatchLabel);
832         auto endLabel = new Label("end");
833         auto gotoEnd = new Goto(endLabel);
834        
835         with(generator){
836             auto startLabel = getUniqueLabel("start");
837             emit("{//Test");
838             indent();
839                 emit("uint " ~ startLabel ~ " = position;");
840                 render(expr,gotoMatch,gotoMismatch);
841                                
842                 render(matchLabel);
843                 emit("position = " ~ startLabel ~ ";");
844                 if(pass == Statement.empty){
845                     render(gotoEnd);
846                 }               
847                 else{
848                     render(pass);
849                 }
850                
851                 render(mismatchLabel);
852                 render(fail);
853             render(endLabel);
854             emit("{/*do nothing*/}");
855             unindent();
856             emit("}");
857         }       
858     }
859    
860     public String toBNF(){
861          return "/" ~ expr.toBNF();
862     }       
863 }
864
865
866 class LiteralExpr: SubExpression{
867     Binding binding;
868     String literalName;
869     ProductionArg[] args;
870    
871     public this(String literalName,Binding binding,ProductionArg[] args...){
872         this.literalName = literalName;
873         this.binding = binding;
874         this.args = args.dup;
875     }
876    
877     public String getDescription(){
878         return "";
879     }   
880    
881     public bool isDescribable(){
882         return false; // set to false, to reduce error noise
883     }
884    
885     public void semanticPass(Rule thisRule,BaseEnkiParser root){       
886         //TODO: match each arg up to the rule predicate params, and determine each type
887         foreach(arg; args){
888             BindingProductionArg bindingArg = cast(BindingProductionArg)arg;
889             if(bindingArg){
890                 bindingArg.semanticPass(thisRule,root);
891             }
892         }
893         if(binding) binding.semanticPass(thisRule,root);
894     }
895    
896     public String resolveBindingType(String name,BaseEnkiParser root){
897         debug writefln("LiteralExpr.resolveBindingType %s",literalName);
898         // cannot resolve binding type
899         return null;
900     }   
901    
902     public void render(CodeGenerator generator,Statement pass,Statement fail){
903         String result = this.literalName;
904        
905         if(args.length > 0){
906             result ~= "(";     
907             foreach(uint i,arg; args){
908                 if(i == 0){
909                     result ~= arg.toString();
910                 }
911                 else{
912                     result ~= "," ~ arg.toString();
913                 }
914             }
915             result ~= ")";
916         }
917         if(binding){
918             result = binding.blindAssignExpr(result);
919         }
920                
921         generator.emit(result ~ ";");
922     }
923            
924     public String toBNF(){
925         String result = "@" ~ literalName;
926         if(args.length > 0){
927             result ~= "!(";
928             foreach(uint i,arg; args){
929                 if(i == 0){
930                     result ~= arg.toBNF();
931                 }
932                 else{
933                     result ~= "," ~ arg.toBNF();
934                 }
935             }
936             result ~= ")";
937         }
938         if(binding) result ~= binding.toBNF();
939         return result;     
940     }   
941 }
942
943 class CustomTerminal : SubExpression, ShortExpression{
944     String name;
945     Binding binding;
946    
947     public this(String name,Binding binding){
948         this.name = name;
949         this.binding = binding;
950     }
951    
952     public String getDescription(){
953         return "\\\"" ~ name ~ "\\\"";
954     }   
955    
956     public bool isDescribable(){
957         return false; // set to false, to reduce error noise
958     }   
959    
960     public String renderShort(){       
961         if(binding){
962             return "terminal(" ~ name ~ ")" ~ binding.postAssignExpr();
963         }
964         else{
965             return "terminal(" ~ name ~ ").success";
966         }           
967     }
968    
969     public void semanticPass(Rule thisRule,BaseEnkiParser root){
970         if(binding) binding.semanticPass(thisRule,root);
971     }   
972    
973     public String resolveBindingType(String name,BaseEnkiParser root){
974         debug writefln("Terminal.resolveBindingType %s",name);
975         if(binding && binding.name == name){
976             return root.getParseType();
977         }
978         return null;
979     }   
980    
981     public void render(CodeGenerator generator,Statement pass,Statement fail){
982         generator.renderIfTest(renderShort(), pass, fail);
983     }
984            
985     public String toBNF(){
986         if(binding){
987             return "&" ~ name ~ binding.toBNF();
988         }
989         else{
990             return "&" ~ name;
991         }
992     }   
993 }
994
995 class Range : SubExpression, ShortExpression{
996     String start;
997     String end;
998     Binding binding;
999     bool noRange;
1000     String type;
1001    
1002     public this(String start,String end,Binding binding){
1003         this.start = start;
1004         this.end = end;
1005         this.binding = binding;
1006     }
1007        
1008     public String getDescription(){
1009         return "#" ~ start ~ "-#" ~ end;
1010     }   
1011    
1012     public bool isDescribable(){
1013         return false; // set to false, to reduce error noise
1014     }   
1015    
1016     public String renderShort(){
1017         if(noRange){
1018             if(binding){
1019                 return "terminal(cast("~ type ~ ")0x" ~ start ~ ")" ~ binding.postAssignExpr();
1020             }
1021             else{
1022                 return "terminal(cast("~ type ~ ")0x" ~ start ~ ").success";
1023             }
1024         }
1025         else{
1026             if(binding){
1027                 return "range(cast("~ type ~ ")0x" ~ start ~ ",cast("~ type ~ ")0x" ~ end ~ ")" ~ binding.postAssignExpr();
1028             }
1029             else{
1030                 return "range(cast("~ type ~ ")0x" ~ start ~ ",cast("~ type ~ ")0x" ~ end ~ ").success";
1031             }
1032         }
1033     }
1034    
1035     public void semanticPass(Rule thisRule,BaseEnkiParser root){
1036         // find if we're a singular expression or not
1037         this.noRange = (!end || start == end);
1038        
1039         // determine the byte-length of the range to investigate
1040         uint length = start.length > end.length ? start.length : end.length;
1041        
1042         switch(length){
1043         case 2:
1044             type = "ubyte";
1045             break;
1046         case 4:
1047             type = "ushort";
1048             break;
1049         case 8:
1050             type = "uint";
1051             break;
1052         case 16:
1053             type = "ulong";
1054             break;
1055         default:
1056             assert(false); // this shouldn't happen
1057         }
1058        
1059         if(binding) binding.semanticPass(thisRule,root);
1060     }   
1061    
1062     public String resolveBindingType(String name,BaseEnkiParser root){
1063         debug writefln("Range.resolveBindingType %s",name);
1064         if(binding && binding.name == name){
1065             return root.getParseType();
1066         }
1067         return null;
1068     }   
1069    
1070     public void render(CodeGenerator generator,Statement pass,Statement fail){
1071         generator.renderIfTest(renderShort(), pass, fail);
1072     }
1073            
1074     public String toBNF(){
1075         if(noRange){
1076             if(binding){
1077                 return "#" ~ start ~ binding.toBNF();
1078             }
1079             else{
1080                 return "#" ~ start;
1081             }
1082         }
1083         else{
1084             if(binding){
1085                 return "#" ~ start ~ "-#" ~ end ~ binding.toBNF();
1086             }
1087             else{
1088                 return "#" ~ start ~ "-#" ~ end;
1089             }           
1090         }
1091     }   
1092 }
1093
1094 class Binding{
1095     bool isConcat;
1096     String name;
1097     String type;
1098    
1099     public this(bool isConcat,String name){
1100         this.isConcat = isConcat;
1101         this.name = name;
1102     }
1103    
1104     public String assignExpr(String type,String rvalue){
1105         if(isConcat){
1106             return "smartAssignCat!(" ~ this.type ~ "," ~ type ~ ")(bind_" ~ name ~ "," ~ rvalue ~ ")";
1107         }
1108         else{
1109             return "smartAssign!(" ~ this.type ~ "," ~ type ~ ")(bind_" ~ name ~ "," ~ rvalue ~ ")";
1110         }
1111     }
1112    
1113     public String blindAssignExpr(String rvalue){
1114         if(isConcat){
1115             return "smartAssignCat(bind_" ~ name ~ "," ~ rvalue ~ ")";
1116         }
1117         else{
1118             return "smartAssign(bind_" ~ name ~ "," ~ rvalue ~ ")";
1119         }
1120     }
1121    
1122     public void semanticPass(Rule thisRule,BaseEnkiParser root){
1123         thisRule.resolveBinding(name); // ensure that the binding is declared
1124         type = thisRule.resolveBindingType(name,root); // find the type
1125     }
1126    
1127     public String postAssignExpr(){
1128         if(isConcat){
1129             return ".assignCat!(" ~ type ~ ")(bind_" ~ name ~ ")";
1130         }
1131         else{
1132             return ".assign!(" ~ type ~ ")(bind_" ~ name ~ ")";
1133         }
1134     }
1135    
1136     public String toBNF(){
1137         String result = ":";
1138         if(isConcat) result ~= "~";
1139         result ~= name;
1140         return result;
1141     }       
1142 }
Note: See TracBrowser for help on using the browser.