root/trunk/enki/EnkiBackend.d

Revision 289, 7.6 kB (checked in by h3r3tic, 9 months ago)

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

Line 
1 /+
2     Copyright (c) 2006 Eric Anderton
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.EnkiBackend;
26
27 private import enki.types;
28 private import enki.BaseParser;
29 private import enki.CodeGenerator;
30 private import enki.Expression;
31 private import enki.Directive;
32 private import enki.Rule;
33
34 debug private import tango.io.Stdout;
35
36 interface SyntaxLine{
37     public void semanticPass(BaseEnkiParser root);
38     public String toBNF();
39 }
40
41 struct UserProduction{
42     String returnType;
43     String name;
44     String description;
45     bool isTerminal;
46        
47     static UserProduction opCall(String returnType,String name,String description=null,bool isTerminal=true){
48         UserProduction _this;
49         _this.returnType = returnType;
50         _this.name = name;
51         _this.description = description ? description : name;
52         _this.isTerminal = isTerminal;
53         return _this;
54     }
55 }
56
57 class BaseEnkiParser : BaseParser{
58     SyntaxLine[] lines;
59     Rule[String] rules;
60     String[] imports;
61     String baseclass;
62     String classname;
63     String moduleName;
64     String typelib;
65     String parseType;
66     String boilerplate;
67     String header;
68     String utf;
69     UserProduction[String] userProductions;
70
71     protected void clearErrors(){
72         debug writefln("** clearErrors() **");
73         super.clearErrors();
74     }       
75    
76     public this(){
77         this.baseclass = "BaseParser";
78         this.classname = "Parser";
79         this.typelib = "enki.types";
80         this.parseType = "String";
81         this.utf = "8";
82     }
83    
84     public void createSyntax(SyntaxLine[] lines){
85         this.lines = lines;
86     }
87    
88     public void add(BaseEnkiParser other){
89         // aggregate and perform nested semantic pass on directives
90         debug writefln("Running Non-Root Semantic Pass on aggregated lines.");
91        
92         foreach(line; other.lines){
93             Directive directive = cast(Directive)line;
94             if(directive && directive.type == Directive.FirstPass){
95                 directive.semanticPass(this);
96             }
97             else{
98                 this.lines ~= line;
99             }
100         }       
101     }
102    
103     public void setImport(String imp){
104         imports ~= imp;
105     }
106    
107     public void setBaseClass(String name){
108         baseclass = name;
109     }
110    
111     public void setClassname(String name){
112         classname = name;
113     }
114    
115     public void setModulename(String name){
116         moduleName = name;
117     }
118    
119     public void setBoilerplate(String value){
120         boilerplate ~= value;
121     }   
122    
123     public void setHeader(String value){
124         header ~= value;
125     }
126        
127     public void defineUserProduction(String returnType,String name,String description){
128         if(name in rules || name in userProductions){
129             throw new Exception("Rule '" ~ name ~ "' is already defined.");
130         }
131         debug writefln("defined user production: %s",name);
132         userProductions[name] = UserProduction(returnType,name,description);
133     }
134    
135     public void setTypelibName(String name){
136         typelib = name;
137     }
138    
139     public void setParseType(String name){
140         debug writefln("parse type set to: %s",name);
141         parseType = name;
142     }
143    
144     public String getParseType(){
145         return parseType;
146     }
147    
148     public void setUTF(String value){
149         utf = value;
150     }
151    
152     public void aliasRule(String ruleName,String aliasName){
153         if(ruleName in rules){
154             rules[aliasName] = rules[ruleName];
155         }
156         else if(ruleName in userProductions){
157             debug writefln("%s aliased to user production %s",aliasName,ruleName);
158             userProductions[aliasName] = userProductions[ruleName];
159         }
160         else{
161             throw new Exception("Cannot alias '" ~ aliasName ~ "'. Rule '" ~ ruleName ~ "' is not defined.");
162         }
163     }
164    
165     public void addRule(String name,Rule rule){
166         if(name in rules) throw new Exception("Rule '" ~ name ~ "' is already defined.");
167         rules[name] = rule;
168     }
169    
170     public Rule[String] getRules(){
171         return this.rules;
172     }
173    
174     public String getTypeForRule(String name){
175         String type;
176         if(name in userProductions){
177             type = userProductions[name].returnType;
178         }
179         if(!type){
180             if(!(name in rules)){
181                 throw new Exception("Cannot find rule '" ~ name ~ "'.");
182             }
183             type = rules[name].getType(this);
184         }
185         return type;
186     }
187    
188     public bool isTerminal(String name){
189         if(name in userProductions){
190             return userProductions[name].isTerminal;
191         }       
192         return true;
193     }
194    
195     public String getTerminalName(String terminalName){
196         String name;
197         if(name in userProductions){
198             return userProductions[name].description;
199         }
200         if(!name) name = terminalName;
201         return name;
202     }
203    
204     public void semanticPass(){
205         // first/root pass
206         debug writefln("-- Root/First Semantic Pass --");
207         foreach(line; lines){       
208             Directive directive = cast(Directive)line;
209             if(directive && (directive.type == Directive.FirstPass || directive.type == Directive.RootOnly)){
210                 directive.semanticPass(this);
211             }
212         }
213        
214         // gather production names
215         foreach(line; lines){
216             Rule rule = cast(Rule)line;
217             if(rule){
218                 rules[rule.name] = rule;
219             }
220         }
221
222         // second pass - realize all other directives
223         debug writefln("-- Second Semantic Pass --");
224         foreach(line; lines){
225             Directive directive = cast(Directive)line;
226             if(directive && (directive.type == Directive.SecondPass)){
227                 directive.semanticPass(this);
228             }
229         }
230                
231         // resolve all rules last
232         debug writefln("-- Final Semantic Pass --");
233         foreach(name,rule; rules){
234             debug writefln("-- Semantic Pass for rule %s --",name);
235             rule.semanticPass(this);
236         }
237        
238     }
239        
240     String render(){
241         auto CodeGenerator generator = new CodeGenerator(this.parseType);
242         with(generator){
243             // boilerplate section
244             emit("//Generated by Enki v1.2");
245             emit("");
246             emit(boilerplate);
247            
248             // module and imports section
249             emit("");
250             if(moduleName){
251                 emit("module " ~ moduleName ~ ";");
252             }
253             emit("version(build) pragma(export_version,EnkiUTF" ~ utf ~ ");");
254             emit("");
255             emit("debug private import tango.io.Stdout;");
256            
257             if(typelib != ""){
258                 emit("private import " ~ typelib ~ ";");
259             }
260             foreach(imp; imports){
261                 emit("private import " ~ imp ~ ";");
262             }
263             emit("");
264            
265             // header section
266             emit(header);
267             emit("");           
268            
269             // parser class definition
270             if(baseclass){
271                 emit("class " ~ classname ~ " : " ~ baseclass ~ "{");
272             }
273             else{
274                 emit("class " ~ classname ~ "{");               
275             }
276             emit("");
277            
278             foreach(line; lines){
279                 auto renderable = cast(IRenderable)line;
280                 if(renderable){
281                     render(renderable);
282                 }
283             }
284            
285             emit("}");
286         }   
287         return generator.toString();
288     }
289    
290     public String toBNF(){
291         String result = "";
292         foreach(line; lines){
293             result ~= line.toBNF();
294         }
295         return result;
296     }
297 }
298
299 class Comment : SyntaxLine{
300     String text;
301     public this(String text){
302         this.text = text;
303     }
304    
305     public void semanticPass(BaseEnkiParser root){
306         //do nothing
307     }   
308    
309     public String toBNF(){
310         return "# " ~ text ~ "\n";
311     }
312 }
Note: See TracBrowser for help on using the browser.