root/trunk/minid/compiler.d

Revision 389, 9.5 kB (checked in by JarrettBillingsley, 2 months ago)

Updated the docs (and the build script for them). Also realized that although I implemented the semantics for opEquals, the interpreter was still using opCmp in cases of equality. Urk. So now it actually uses opEquals.

Line 
1 /******************************************************************************
2 License:
3 Copyright (c) 2008 Jarrett Billingsley
4
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the
7 use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it freely,
11 subject to the following restrictions:
12
13     1. The origin of this software must not be misrepresented; you must not
14     claim that you wrote the original software. If you use this software in a
15     product, an acknowledgment in the product documentation would be
16     appreciated but is not required.
17
18     2. Altered source versions must be plainly marked as such, and must not
19     be misrepresented as being the original software.
20
21     3. This notice may not be removed or altered from any source distribution.
22 ******************************************************************************/
23
24 module minid.compiler;
25
26 import tango.core.Vararg;
27
28 import tango.io.FilePath;
29 import tango.io.UnicodeFile;
30
31 import minid.alloc;
32 import minid.astvisitor;
33 import minid.codegen;
34 import minid.compilertypes;
35 import minid.interpreter;
36 import minid.lexer;
37 import minid.parser;
38 import minid.semantic;
39 import minid.string;
40 import minid.types;
41
42 /**
43 This class encapsulates all the functionality needed for compiling MiniD code.
44 */
45 scope class Compiler : ICompiler
46 {
47     mixin ICompilerMixin;
48
49     /**
50     An enumeration of flags that can be passed to the compiler to change its behavior.
51     */
52     enum
53     {
54         /**
55         Do not generate code for any optional features.
56         */
57         None = 0,
58        
59         /**
60         Generate code to check parameter type constraints.
61         */
62         TypeConstraints = 1,
63        
64         /**
65         Generate code for assert statements.
66         */
67         Asserts = 2,
68
69         /**
70         Generate debug info.  Currently always on.
71         */
72         Debug = 4,
73
74         /**
75         Generate code for all optional features.
76         */
77         All = TypeConstraints | Asserts | Debug
78     }
79
80     private MDThread* t;
81     private uint mFlags;
82     private bool mIsEof;
83     private bool mIsLoneStmt;
84     private Lexer mLexer;
85     private Parser mParser;
86     private word mStringTab;
87
88 // ================================================================================================================================================
89 // Public
90 // ================================================================================================================================================
91
92     /**
93     Constructs a compiler.  The given thread will be used to hold temporary data structures,
94     to throw exceptions, and to return the functions that result from compilation.
95
96     Params:
97         t = The thread with which this compiler will be associated.
98         flags = A bitwise or of any code-generation flags you want to use for this compiler.
99             Defaults to All.
100     */
101     public this(MDThread* t, uint flags = All)
102     {
103         this.t = t;
104         mFlags = flags;
105         mLexer = Lexer(this);
106         mParser = Parser(this, &mLexer);
107     }
108
109     ~this()
110     {
111         for(auto n = mHead; n !is null; )
112         {
113             auto next = n.next;
114
115             n.cleanup(t.vm.alloc);
116             auto arr = n.toVoidArray();
117             t.vm.alloc.freeArray(arr);
118
119             n = next;
120         }
121     }
122
123     /**
124     Set the compiler's code-generation flags.
125     */
126     public void setFlags(uint flags)
127     {
128         mFlags = flags;
129     }
130
131     /**
132     Returns whether or not code for asserts should be generated.
133     */
134     public override bool asserts()
135     {
136         return (mFlags & Asserts) != 0;
137     }
138
139     /**
140     Returns whether or not code for parameter type constraint checking should be generated.
141     */
142     public override bool typeConstraints()
143     {
144         return (mFlags & TypeConstraints) != 0;
145     }
146
147     /**
148     Returns whether or not the most recently-thrown exception was thrown due to an unexpected end-of-file.
149     As an example, this is used by MDCL (that is, minid.commandline) to detect when more code must be entered
150     to complete a code segment.  A simple example of use:
151     
152 -----
153 scope c = new Compiler(t);
154
155 try
156     c.compileExpression(someCode, "test");
157 catch(MDException e)
158 {
159     auto ex = catchException(t);
160     
161     if(c.isEof())
162     {
163         // error was caused by an unexpected end-of-file
164     }
165 }
166 -----
167     */
168     public override bool isEof()
169     {
170         return mIsEof;
171     }
172    
173     /**
174     Returns whether or not the most recently-thrown exception was thrown due to a no-effect expression being used
175     as a statement (yes, this method has a horrible name).  Its use is identical to isEof().
176     */
177     public override bool isLoneStmt()
178     {
179         return mIsLoneStmt;
180     }
181
182     public override void exception(ref CompileLoc loc, char[] msg, ...)
183     {
184         vexception(loc, msg, _arguments, _argptr);
185     }
186
187     public override void eofException(ref CompileLoc loc, char[] msg, ...)
188     {
189         mIsEof = true;
190         vexception(loc, msg, _arguments, _argptr);
191     }
192
193     public override void loneStmtException(ref CompileLoc loc, char[] msg, ...)
194     {
195         mIsLoneStmt = true;
196         vexception(loc, msg, _arguments, _argptr);
197     }
198
199     public override MDThread* thread()
200     {
201         return t;
202     }
203
204     public override Allocator* alloc()
205     {
206         return &t.vm.alloc;
207     }
208
209     public override char[] newString(char[] data)
210     {
211         auto s = string.create(t.vm, data);
212         pushStringObj(t, s);
213         pushBool(t, true);
214         idxa(t, mStringTab);
215         return s.toString();
216     }
217
218     /**
219     Compile a source code file into a function closure.  Takes the path to the source file, compiles
220     that file, and pushes the top-level closure onto the stack.  The environment of the closure is
221     just set to the global namespace of the compiler's thread; you must create and set a namespace
222     for the module function before calling it.
223
224     You shouldn't have to deal with this function that much.  Most of the time the compilation of
225     modules should be handled for you by the import system.
226
227     Params:
228         filename = The filename of the source file to compile.
229
230     Returns:
231         The stack index of the newly-pushed function closure that represents the top-level function
232         of the module.
233     */
234     public word compileModule(char[] filename)
235     {
236         scope path = new FilePath(filename);
237         scope file = new UnicodeFile!(char)(path, Encoding.Unknown);
238         auto src = file.read();
239
240         scope(exit)
241             delete src;
242
243         return compileModule(src, filename);
244     }
245
246     /**
247     Same as above, but compiles from a string holding the source rather than from a file.
248
249     Params:
250         source = The source code as a string.
251         name = The name which should be used as the source name in compiler error message.  Takes the
252             place of the filename when compiling from a source file.
253
254     Returns:
255         The stack index of the newly-pushed function closure that represents the top-level function
256         of the module.
257     */
258     public word compileModule(char[] source, char[] name)
259     {
260         return commonCompile(
261         {
262             mLexer.begin(name, source);
263             auto mod = mParser.parseModule();
264
265             scope sem = new Semantic(this);
266             mod = sem.visit(mod);
267
268             scope cg = new Codegen(this);
269             cg.visit(mod);
270         });
271     }
272
273     /**
274     Compile a list of statements into a function which takes a variadic number of arguments.  The environment
275     of the compiled function closure is set to the globals of the compiler's thread.
276
277     Params:
278         source = The source code as a string.
279         name = The name to use as the source name for compilation errors.
280
281     Returns:
282         The stack index of the newly-pushed function closure.
283     */
284     public word compileStatements(char[] source, char[] name)
285     {
286         return commonCompile(
287         {
288             mLexer.begin(name, source);
289             auto fd = mParser.parseStatements(name);
290
291             scope sem = new Semantic(this);
292             fd = sem.visitStatements(fd);
293
294             scope cg = new Codegen(this);
295             cg.codegenStatements(fd);
296         });
297     }
298
299     /**
300     Compile a single expression into a function which returns the value of that expression when called.
301
302     Params:
303         source = The source code as a string.
304         name = The name to use as the source name for compilation errors.
305
306     Returns:
307         The stack index of the newly-pushed function closure.
308     */
309     public word compileExpression(char[] source, char[] name)
310     {
311         return commonCompile(
312         {
313             mLexer.begin(name, source);
314             auto fd = mParser.parseExpressionFunc(name);
315
316             scope sem = new Semantic(this);
317             fd = sem.visit(fd);
318
319             scope cg = new Codegen(this);
320             cg.codegenStatements(fd);
321         });
322     }
323
324     /**
325     Parses a JSON string into a MiniD value, pushes that value onto the stack, and returns the
326     index of the newly-pushed value.
327     */
328     public word loadJSON(char[] source)
329     {
330         mStringTab = newTable(t);
331
332         scope(failure)
333         {
334             if(stackSize(t) >= mStringTab + 1)
335                 pop(t, stackSize(t) - mStringTab);
336         }
337
338         mLexer.begin("JSON", source, true);
339         mParser.parseJSON();
340
341         insertAndPop(t, -2);
342         return stackSize(t) - 1;
343     }
344
345 // ================================================================================================================================================
346 // Private
347 // ================================================================================================================================================
348
349     private void vexception(ref CompileLoc loc, char[] msg, TypeInfo[] arguments, va_list argptr)
350     {
351         pushVFormat(t, msg, arguments, argptr);
352         pushFormat(t, "{}({}:{}): ", loc.file, loc.line, loc.col);
353         insert(t, -2);
354         cat(t, 2);
355         throwException(t);
356     }
357
358     private word commonCompile(void delegate() dg)
359     {
360         mStringTab = newTable(t);
361
362         scope(failure)
363         {
364             if(stackSize(t) >= mStringTab + 1)
365                 pop(t, stackSize(t) - mStringTab);
366         }
367
368         dg();
369         insertAndPop(t, -2);
370         return stackSize(t) - 1;
371     }
372 }
Note: See TracBrowser for help on using the browser.