changeset 89:a49bb982a7b0 new_gen

Using the new SourceLocation system to handle errors. Also, this is the first push for making the errors don't throw, but continue to check the source.
author Anders Johnsen <skabet@gmail.com>
date Sun, 04 May 2008 20:27:01 +0200
parents eb5b2c719a39
children 4b6d8563e943
files basic/Message.d basic/Messages.d basic/SourceManager.d dang/compiler.d gen/CodeGen.d lexer/Lexer.d parser/Parser.d tests/code/basic_2.d tests/code/func_1.d tests/code/math_3.d tests/code/scope_1.d tests/code/scope_2.d tests/code/switch_5.d tests/code/switch_6.d tests/lexer/Comments2.d tests/run.d
diffstat 16 files changed, 358 insertions(+), 116 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/basic/Message.d	Sun May 04 20:27:01 2008 +0200
@@ -0,0 +1,157 @@
+module basic.Message;
+
+import tango.core.Exception,
+       Array = tango.core.Array,
+       tango.io.Stdout,
+       tango.text.Util;
+
+import tango.stdc.stdlib;
+
+import llvm.type;
+
+import lexer.Token,
+       lexer.Lexer,
+       sema.DType,
+       sema.Symbol;
+
+import basic.SourceLocation,
+       basic.SourceManager;
+
+public import basic.Messages;
+
+class MessageHandler
+{
+public:
+
+    this(SourceManager src_mgr)
+    {
+        this.src_mgr = src_mgr;
+    }
+
+    Message report(uint opcode, SLoc location, bool fatal = false)
+    {
+        Message m = new Message(opcode, location, src_mgr);
+        messages ~= m;
+        if(fatal)
+            checkErrors();
+        return m;
+    }
+
+    void checkErrors()
+    {
+        if(messages.length == 0)
+            return;
+
+        foreach(m ; messages)
+            if(m.type == MessageType.Error)
+            {
+                Stdout(m).newline;
+            }
+
+        exit(1);
+    }
+
+    void checkWarnings()
+    {
+    }
+
+    void showWarnings(bool value)
+    {
+        warnings = value;
+    }
+
+private:
+    Message[] messages;
+    SourceManager src_mgr;
+    bool warnings;
+}
+
+class Message
+{
+
+    this(int opcode, SLoc location, SourceManager src_mgr)
+    {
+        this.src_mgr = src_mgr;
+        this.location = location;
+        args ~= Messages[opcode].message;
+        this.type = Messages[opcode].type;
+    }
+
+    char[] toString()
+    {
+        char[256] tmp = void;
+        char[] msg = layout(tmp, args);
+
+        Lexer l = new Lexer(location, src_mgr, new MessageHandler(src_mgr));
+
+        Token t = l.next;
+        
+        if (src_mgr.getRawData(location).length > 0)
+            msg = src_mgr.getLocationAsString(location) ~ ": " ~ msg;
+        else
+            msg = msg.dup;
+
+
+        char[] line = src_mgr.getLine(location);
+        char[] marks = line.dup;
+        marks[] = ' ';
+        size_t p = src_mgr.getOffsetToLine(location);
+        marks[p .. p + t.length] = '^';
+
+        msg ~= "\n    ";
+        msg ~= line;
+        msg ~= "\n    ";
+        msg ~= marks;
+
+        return msg;
+    }
+
+    Message arg(char[] s)
+    {
+        if (args.length == 11)
+            throw new Exception("Sorry, errors only support up to 10 args");
+        args ~= s;
+        return this;
+    }
+
+    Message arg(char[][] s)
+    {
+        char[] res = s[0 .. $ - 1].join(", ");
+        if (s.length > 1)
+            res ~= " and ";
+        res ~= s[$ - 1];
+        return arg(res);
+    }
+
+    Message arg(char c)
+    {
+        return arg([c]);
+    }
+
+    Message arg(DType[] types)
+    {
+        char[][] res;
+        foreach (type; types)
+            res ~= type.name();
+        return arg(res);
+    }
+
+    Message arg(Symbol sym)
+    {
+        return arg(sym.type.name ~ " " ~ sym.id.get);
+    }
+
+    /*
+    Message loc(SLoc loc)
+    {
+        location = loc;
+        return this;
+    }
+    */
+
+    MessageType type;
+private:
+    char[][] args;
+    SLoc location;
+    SourceManager src_mgr;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/basic/Messages.d	Sun May 04 20:27:01 2008 +0200
@@ -0,0 +1,54 @@
+module basic.Messages;
+
+enum : uint
+{
+    InvalidType,
+    InvalidSymbol,
+    InvalidIlligaleType,
+    UnexpectedEOFBlock,
+    OnlyOneDotFloating,
+    OnlyOneEFloating,
+    UnexpectedTokMulti,
+    UnexpectedTokSingle,
+    UnexpectedTok,
+    CaseValueMustBeInt,
+    UnexpectedBeginStmt,
+    UnexpectedTokType,
+    ExpectedIdAfterDot,
+    ExpectedExp,
+    ExpectedCastType
+}
+
+enum MessageType
+{
+    Warning,
+    Error,
+}
+
+MessageEntry[uint] Messages;
+
+struct MessageEntry
+{
+    MessageType type;
+    char[] message;
+}
+
+static this()
+{
+    Messages = [
+        InvalidType         : MessageEntry(MessageType.Error, "InvalidType"),
+        UnexpectedEOFBlock  : MessageEntry(MessageType.Error, "Unexpected end of file. Unclosed comment block"),
+        InvalidSymbol       : MessageEntry(MessageType.Error, "Read invalid symbol: '%0'"),
+        OnlyOneDotFloating  : MessageEntry(MessageType.Error, "Only one '.' is allowed in an floating number"),
+        OnlyOneEFloating    : MessageEntry(MessageType.Error, "Only one E is allowed in an floating number"),
+        UnexpectedTokMulti  : MessageEntry(MessageType.Error, "Unexpected token, got %0 expected one of %1"),
+        UnexpectedTokSingle : MessageEntry(MessageType.Error, "Unexpected token, got %0 expected %1"),
+        UnexpectedTok       : MessageEntry(MessageType.Error, "Unexpected token %0"),
+        CaseValueMustBeInt  : MessageEntry(MessageType.Error, "Cases can only be integer literals"),
+        UnexpectedBeginStmt : MessageEntry(MessageType.Error, "Unexpected begining of statement."),
+        UnexpectedTokType   : MessageEntry(MessageType.Error, "Unexpected token in Type parsing. Got %0"),
+        ExpectedIdAfterDot  : MessageEntry(MessageType.Error, "Expected identifier after '.'"),
+        ExpectedExp         : MessageEntry(MessageType.Error, "Expected expression"),
+        ExpectedCastType    : MessageEntry(MessageType.Error, "Expected cast type")
+    ];
+}
--- a/basic/SourceManager.d	Sun May 04 18:13:46 2008 +0200
+++ b/basic/SourceManager.d	Sun May 04 20:27:01 2008 +0200
@@ -88,7 +88,23 @@
             --ptr_lo;
         while (ptr != cp.data.ptr + cp.data.length && *ptr != '\n' && *ptr != '\r')
             ++ptr;
-        return ptr_lo[0 .. ptr - ptr_lo];
+        return ptr_lo[1 .. ptr - ptr_lo];
+    }
+
+    /**
+      Extracts a string containing the entire line loc appears in.
+     **/
+    int getOffsetToLine(SourceLocation loc)
+    {
+        // The line is extracted by getting two pointers to the exact location
+        // and decreasing one until the nearest newline while the other ptr is
+        // increased to the nearest newline.
+        CP* cp = &checkpoints[loc.fileID];
+        char* ptr = cp.data.ptr + loc.fileOffset;
+        char* ptr_lo = ptr;
+        while (ptr_lo != cp.data.ptr && *ptr_lo != '\n' && *ptr_lo != '\r')
+            --ptr_lo;
+        return cast(int)ptr - cast(int)ptr_lo - 1;
     }
 
     /**
--- a/dang/compiler.d	Sun May 04 18:13:46 2008 +0200
+++ b/dang/compiler.d	Sun May 04 20:27:01 2008 +0200
@@ -2,7 +2,9 @@
 
 import tango.io.Stdout,
        tango.core.Signal,
+       tango.sys.Process,
        tango.time.StopWatch,
+       tango.io.FileConduit,
        tango.io.FilePath;
 
 import lexer.Lexer,
@@ -10,6 +12,8 @@
 
 import basic.SourceManager;
 
+import basic.Message;
+
 import ast.Decl;
 
 import tools.AstPrinter,
@@ -85,6 +89,10 @@
     bool optimize = false;
     bool inline = false;
 
+
+    SourceManager src_mgr = new SourceManager;
+    MessageHandler messages = new MessageHandler(src_mgr);
+
     argParse.addOption(["-h", "--help"], Opt.Action.Help)
         .help("Show this help message");
 
@@ -94,10 +102,12 @@
     argParse.addOption(["--ast-dump-code"],
             "what-to-do", Opt.Action.StoreConst, "code")
         .help("Output the AST as code");
-
     argParse.addOption(["--gen-llvm"],
             "what-to-do", Opt.Action.StoreConst, "gen-llvm")
         .help("Compile to LLVM code (default)");
+    argParse.addOption(["-c"],
+            "what-to-do", Opt.Action.StoreConst, "compile")
+        .help("Compile to .o or executeable");
 
     argParse.addOption(
             ["-O","--optimize"], {
@@ -131,9 +141,19 @@
             (Decl[] decls, SourceManager sm) {
                 StopWatch w; w.start;
                 auto llvmGen = new CodeGen();
-                llvmGen.gen(decls, optimize, inline);
+                auto file = new FileConduit("out.bc");
+                llvmGen.gen(decls, file.fileHandle, optimize, inline);
                 timings ~= Measurement("Generating LLVM bytecode", w.stop);
             });
+    else if (what == "compile")
+        postParse.attach(
+            (Decl[] decls, SourceManager sm) {
+                StopWatch w; w.start;
+                auto llvmGen = new CodeGen();
+                auto llc = new Process("llc");
+                llvmGen.gen(decls, llc.stdin.fileHandle, optimize, inline);
+                timings ~= Measurement("Generating assemble bytecode", w.stop);
+            });
     else if (what == "dot")
         postParse.attach(
             (Decl[] decls, SourceManager sm) {
@@ -150,9 +170,6 @@
                 print.print(decls);
                 timings ~= Measurement("Converting AST to text", w.stop);
             });
-
-    SourceManager src_mgr = new SourceManager;
-
     StopWatch total;
     total.start;
     foreach (file; filesToHandle)
@@ -160,18 +177,18 @@
         preLex(file);
 
         auto start = src_mgr.addFile(file);
-        Stdout(file).newline;
-        auto lexer = new Lexer(start, src_mgr);
+        auto lexer = new Lexer(start, src_mgr, messages);
         postLex(lexer);
 
         preParse(lexer);
 
         StopWatch watch;
         watch.start;
-        auto parser = new Parser;
+        auto parser = new Parser(messages);
         auto action = new AstAction(src_mgr);
         auto decls = cast(Decl[])parser.parse(src_mgr, lexer, action);
         timings ~= Measurement("Lex + Parse", watch.stop);
+        messages.checkErrors();
 
         StopWatch watch2;
         watch.start;
--- a/gen/CodeGen.d	Sun May 04 18:13:46 2008 +0200
+++ b/gen/CodeGen.d	Sun May 04 20:27:01 2008 +0200
@@ -65,7 +65,7 @@
         b.dispose();
     }
 
-    void gen(Decl[] decls, bool optimize, bool inline)
+    void gen(Decl[] decls, uint handle, bool optimize, bool inline)
     {
         // create module
         m = new Module("main_module");
@@ -89,7 +89,7 @@
                         Type pointer = PointerType.Get(llvm(st));
                         param_types ~= pointer;
                     }
-                    if(auto ar = t.asArray)
+                    else if(auto ar = t.asArray)
                     {
                         Type pointer = PointerType.Get(llvm(ar));
                         param_types ~= pointer;
@@ -141,7 +141,7 @@
         if(optimize)
             m.optimize(inline);
 
-        m.writeBitcodeToFile("out.bc");
+        m.writeBitcodeToFileHandle(handle);
     }
 
     void genRootDecl(Decl decl)
@@ -531,7 +531,6 @@
                 else assert(0, "Can only index pointers and arrays");
             case ExpType.MemberReference:
                 auto mem = cast(MemberReference)exp;
-                Stdout(mem.target).newline;
                 switch(mem.target.expType)
                 {
                     case ExpType.Identifier:
--- a/lexer/Lexer.d	Sun May 04 18:13:46 2008 +0200
+++ b/lexer/Lexer.d	Sun May 04 20:27:01 2008 +0200
@@ -1,6 +1,6 @@
 module lexer.Lexer;
 
-import misc.Error,
+import basic.Message,
        basic.SourceManager;
 
 import lexer.Token,
@@ -21,8 +21,11 @@
     /**
       Create a new Lexer.
     */
-    this(SourceLocation start, SourceManager src_mgr)
+
+
+    this(SourceLocation start, SourceManager src_mgr, MessageHandler messages)
     {
+        this.messages = messages;
         sm = src_mgr;
         start_loc = start;
         position = 0;
@@ -112,16 +115,6 @@
         return t;
     }
 
-    /**
-      Return all errors that occurred while tokenizing the string.
-
-        TODO: Error system not implemented yet - this is a stub!
-      */
-    public Error[] getErrors()
-    {
-        return this.errors;
-    }
-
 private:
     Token eq()
     {
@@ -231,7 +224,7 @@
                         if(source[position-1] == '/')
                             return this.next;
                 }
-                throw error(__LINE__, "Unexpected end of file. Unclosed comment block");
+                messages.report(UnexpectedEOFBlock,Loc(position));
 
             case '+':
                 position += 2;
@@ -256,7 +249,7 @@
                     if(nesting == 0)
                         return this.next;
                 }
-                throw error(__LINE__, "Unexpected end of file. Unclosed comment block");
+                messages.report(UnexpectedEOFBlock,Loc(position));
 
             default:
                 return Token(Tok.Slash, Loc(position - 1), 1);
@@ -287,8 +280,7 @@
                     if(this.source[position+i] == '.')
                     {
                         if(dot)
-                            throw error(__LINE__,"Only one '.' is allowed in an floating number")
-                                .tok(Token(Tok.Float, Loc(position + i), 1));
+                            messages.report(OnlyOneDotFloating, Loc(position + i));
                         dot = true;
                         break;
                     }
@@ -301,8 +293,7 @@
                         this.source[position+i] == 'E')
                     {
                         if (e)
-                            throw error(__LINE__,"Only one '"~this.source[position+i]
-                                    ~"' is allowed in an floating number");
+                            messages.report(OnlyOneEFloating, Loc(position + i));
                         e = true;
                         break;
                     }
@@ -365,17 +356,12 @@
         CharType c = charTable[current];
 
         if(c == CharType.INVALID)
-            throw error(__LINE__, "Read invalid symbol: '%0'").arg(current);
+            messages.report(InvalidSymbol, SLoc()).arg(current);
 
         return c;
 
     }
 
-    Error error(uint line, char[] msg)
-    {
-        return (new Error(msg));//.loc(Loc(position));
-    }
-
     private final SourceLocation Loc(int pos = -1)
     {
         if (pos < 0)
@@ -387,7 +373,7 @@
     SourceLocation start_loc;
     int position;
     char[] source;
-    Error[] errors;
+    MessageHandler messages;
     CharType[] charTable;
     Token delegate()[] symbolFunctions;
 }
--- a/parser/Parser.d	Sun May 04 18:13:46 2008 +0200
+++ b/parser/Parser.d	Sun May 04 20:27:01 2008 +0200
@@ -5,7 +5,7 @@
 
 import parser.Action;
 
-import misc.Error;
+import basic.Message;
 
 import basic.SmallArray,
        basic.SourceManager;
@@ -16,11 +16,18 @@
 class Parser
 {
     Action action;
+    MessageHandler messages;
     alias Object Exp;
     alias Object Stmt;
     alias Object Decl;
 
 public:
+
+    this(MessageHandler messages)
+    {
+        this.messages = messages;
+    }
+
     Decl[] parse(SourceManager sm, Lexer lexer, Action act)
     {
         this.sm = sm;
@@ -59,9 +66,7 @@
             else if (next.type == Tok.OpenParentheses)
                 return parseFunc(type, iden);
             else
-                throw error(__LINE__, PE.UnexpectedTok)
-                    .tok(next)
-                    .arg(next.getType);
+                messages.report(UnexpectedTok, next.location).arg(next.getType);
         }
         else if (t.type == Tok.Struct)
         {
@@ -70,8 +75,7 @@
             
             return parseStruct(type, iden);
         }
-        char[] c = t.getType;
-        throw error(__LINE__, PE.UnexpectedTok).tok(t).arg(c);
+        messages.report(UnexpectedTok, t.location).arg(t.getType);
     }
 
     /**
@@ -102,8 +106,7 @@
                 action.actOnStructMember(decl, var_type, var_iden, exp);
                 continue;
             }
-            throw error(__LINE__, PE.UnexpectedTok)
-                .tok(next).arg(next.getType);
+            messages.report(UnexpectedTok, next.location).arg(next.getType);
         }
 
         require(Tok.CloseBrace);
@@ -187,7 +190,7 @@
                     if ( n.type == Tok.Star || n.type == Tok.OpenBracket)
                     {
                         int len = peekParseType;
-                        if(lexer.peek(len).type == Tok.Identifier || len == 0)
+                        if(lexer.peek(len).type == Tok.Identifier && len != 0)
                             return action.actOnDeclStmt(parseVarDecl());
 
                         Exp exp = parseExpression();
@@ -206,7 +209,7 @@
                 }
 
             case Tok.Switch:
-                throw error(__LINE__, ":(").tok(lexer.peek);
+                messages.report(UnexpectedTok, lexer.peek.location).arg(lexer.next.getType);
                 return null;
 
             default:
@@ -218,10 +221,9 @@
                     require(Tok.Seperator);
                     return action.actOnExprStmt(exp);
                 }
-                    
-                throw error(__LINE__, "Unexpexted begining of statement.").tok(lexer.peek);
+                messages.report(UnexpectedBeginStmt, lexer.peek.location).arg(lexer.next.getType);
         }
-        throw error(__LINE__, "").tok(t);
+        messages.report(UnexpectedTok, t.location);
         return null;
     }
 
@@ -313,10 +315,9 @@
         if (tok.type is Tok.Identifier)
             return Id(tok);
 
-        throw error(__LINE__, PE.UnexpectedTokSingle)
+        messages.report(UnexpectedTokSingle, tok.location)
             .arg(tok.getType)
-            .arg(Tok.Identifier)
-            .tok(tok);
+            .arg(Tok.Identifier);
     }
 
     /**
@@ -329,9 +330,8 @@
         Id currentType;
 
         if ( !(type.isBasicType || type.type == Tok.Identifier) )
-            throw error(__LINE__, "Unexpected token in Type parsing. Got %0")
-                .arg(type.getType)
-                .tok(type);
+            messages.report(UnexpectedTokSingle, type.location)
+                .arg(type.getType);
 
         currentType = Id(type);
         type = lexer.peek;
@@ -364,7 +364,7 @@
         Id currentType;
 
         if ( !(type.isBasicType || type.type == Tok.Identifier) )
-            return i;
+            return 0;
 
         currentType = Id(type);
         type = lexer.peek(++i);
@@ -407,7 +407,7 @@
                         return parsePostfixExp(exp);
                     default:
                         Token t = lexer.peek(1);
-                        throw error(__LINE__, "Expected identifier after '.'").tok(t);
+                        messages.report(ExpectedIdAfterDot, t.location);
                 }
             case Tok.OpenBracket:
                 Token open = lexer.next;
@@ -475,10 +475,9 @@
         else if (next.type == Tok.Integer)
             return action.actOnNumericConstant(next);
 
-        throw error(__LINE__, "Expected expression, not '%0'")
-            .tok(next)
-            .arg(next.getType);
-        assert(0, "Should not happen");
+        messages.report(ExpectedExp, next.location, true);
+//        assert(0, "Should not happen");
+        return null;
     }
 
     Exp parseCast(ref Token _cast)
@@ -486,9 +485,7 @@
         require(Tok.OpenParentheses);
         auto next = lexer.next;
         if(!next.isBasicType && !next.isIdentifier)
-            throw error(__LINE__, "Expected cast type, not %0")
-                .tok(next)
-                .arg(next.getType);
+            messages.report(ExpectedCastType, next.location);
         
         require(Tok.CloseParentheses);
         auto exp = P();
@@ -554,10 +551,9 @@
     Token require(Tok t)
     {
         if (lexer.peek().type != t)
-            throw error(__LINE__, PE.UnexpectedTokSingle)
+            messages.report(UnexpectedTokSingle, lexer.peek.location)
                 .arg(lexer.peek.getType)
-                .arg(t)
-                .tok(lexer.peek);
+                .arg(t);
         return lexer.next();
     }
 
@@ -569,26 +565,6 @@
         return true;
     }
 
-    Error error(uint line, char[] errMsg)
-    {
-        SLoc loc = lexer.peek.location;
-        auto e =
-            new Error("Parser.d(" ~ Integer.toString(line) ~ "): " ~errMsg);
-            //e.loc(loc);
-            return e;
-    }
-
-    struct PE
-    {
-        static char[]
-            UnexpectedTokMulti  = "Unexpected token, got %0 expected one of %1",
-            UnexpectedTokSingle = "Unexpected token, got %0 expected %1",
-            UnexpectedTok       = "Unexpected token %0";
-
-        static char[]
-            CaseValueMustBeInt  = "Cases can only be integer literals";
-    }
-
     Lexer lexer;
     SourceManager sm;
 }
--- a/tests/code/basic_2.d	Sun May 04 18:13:46 2008 +0200
+++ b/tests/code/basic_2.d	Sun May 04 20:27:01 2008 +0200
@@ -1,4 +1,4 @@
-//test fail
+//fail
 int main()
 {
     int x = y;
--- a/tests/code/func_1.d	Sun May 04 18:13:46 2008 +0200
+++ b/tests/code/func_1.d	Sun May 04 20:27:01 2008 +0200
@@ -11,6 +11,7 @@
 testStruct m(testStruct t)
 {
     t.x = t.x - 5;
+    return t;
 }
 
 struct testStruct
--- a/tests/code/math_3.d	Sun May 04 18:13:46 2008 +0200
+++ b/tests/code/math_3.d	Sun May 04 20:27:01 2008 +0200
@@ -1,4 +1,4 @@
-//test fail
+//fail
 int main()
 {
     int x = x;
--- a/tests/code/scope_1.d	Sun May 04 18:13:46 2008 +0200
+++ b/tests/code/scope_1.d	Sun May 04 20:27:01 2008 +0200
@@ -1,4 +1,4 @@
-//test fail
+//fail
 int main()
 {
     int x = y;
--- a/tests/code/scope_2.d	Sun May 04 18:13:46 2008 +0200
+++ b/tests/code/scope_2.d	Sun May 04 20:27:01 2008 +0200
@@ -1,4 +1,4 @@
-//test fail
+//fail
 int main()
 {
     int x = 10;
--- a/tests/code/switch_5.d	Sun May 04 18:13:46 2008 +0200
+++ b/tests/code/switch_5.d	Sun May 04 20:27:01 2008 +0200
@@ -1,4 +1,4 @@
-//test fail
+//fail
 int main(int x)
 {
     switch (x)
--- a/tests/code/switch_6.d	Sun May 04 18:13:46 2008 +0200
+++ b/tests/code/switch_6.d	Sun May 04 20:27:01 2008 +0200
@@ -1,4 +1,4 @@
-//test fail
+//fail
 int main(int x)
 {
     switch (x)
--- a/tests/lexer/Comments2.d	Sun May 04 18:13:46 2008 +0200
+++ b/tests/lexer/Comments2.d	Sun May 04 20:27:01 2008 +0200
@@ -1,4 +1,4 @@
-//test failure
+//fail
 
 /+ 
 
--- a/tests/run.d	Sun May 04 18:13:46 2008 +0200
+++ b/tests/run.d	Sun May 04 20:27:01 2008 +0200
@@ -13,6 +13,14 @@
        tango.sys.Process;
 
 
+enum 
+{
+    SuccessSuccess,
+    SuccessFailure,
+    FailureSuccess,
+    FailureFailure,
+}
+
 char[] prog = "./Dang";
 
 void main(char[][] args)
@@ -39,16 +47,16 @@
 
             switch(result)
             {
-                case 0:
+                case SuccessSuccess:
                     success_success++;
                     break;
-                case 1:
+                case SuccessFailure:
                     success_failure++;
                     break;
-                case 2:
+                case FailureFailure:
                     failure_failure++;
                     break;
-                case 3:
+                case FailureSuccess:
                     failure_success++;
                     break;
             }
@@ -58,15 +66,28 @@
 
     Stdout().newline.newline()
         ("Result:").newline()
-        ("  - Succes/Success:   ")(success_success).newline()
-        ("  - Succes/Failure:   ")(success_failure).newline()
+        ("  - Success/Success:   ")(success_success).newline()
+        ("  - Success/Failure:   ")(success_failure).newline()
         ("  - Failure/Failure:  ")(failure_failure).newline()
         ("  - Failure/Success:  ")(failure_success).newline;
 }
 
 class Test
 {
+    enum TestValue
+    {
+        Success = 0,
+        Lexer = 2,
+        Parser = 3,
+        Gen = 4,
+
+        Fail = 100
+    }
+
     FilePath target;
+
+    TestValue[int] testValues;
+    
     public this(FilePath target)
     {
         this.target = target;
@@ -78,10 +99,25 @@
 
         auto file = new UnicodeFile!(char)(target.path~target.file, Encoding.UTF_8);
 
-        int mode;
+        TestValue mode;
 
         char[] data = file.read;
-        if(data.length > 6 && data[0..6] == "//test")
+        char[][] commands = split(splitLines(data)[0], " ");
+        if(commands[0] == "//fail")
+        {
+            mode = TestValue.Fail;
+            if(commands.length > 1)
+            {
+                try
+                {
+                    int i = Integer.toInt(commands[1]);
+                    if(i in testValues)
+                        mode = testValues[i];
+                }
+                catch{}
+            }
+        }
+/*        if(data.length > 6 && data[0..6] == "//fail")
         {
             char[] str = data.splitLines()[0][6..$];
 
@@ -95,7 +131,7 @@
                     mode = 0;
             }
         }
-
+*/
         Stdout.format("  {,-25}", target.file);
 
         process.execute;
@@ -115,7 +151,7 @@
         return resultOf(result.status, mode);
     }
 
-    private int resultOf(int status, int mode)
+    private int resultOf(int status, TestValue mode)
     {
         char[] good(char[] s)
         {
@@ -135,28 +171,28 @@
 
         if(status == 0)
         {
-            if(mode == 0)
+            if(mode == TestValue.Success)
             {
-                Stdout(good("SUCCES")).newline;
-                return 0;
+                Stdout(good("SUCCESS")).newline;
+                return SuccessSuccess;
             }
-            if(mode == 1)
+            if(mode == TestValue.Fail)
             {
-                Stdout(bad("SUCCES - Unexpected")).newline;
-                return 3;
+                Stdout(bad("SUCCESS - Unexpected")).newline;
+                return FailureSuccess;
             }
         }
         else
         {
-            if(mode == 1)
+            if(mode == TestValue.Fail)
             {
                 Stdout(good("FAILURE")).newline;
-                return 2;
+                return FailureFailure;
             }
-            if(mode == 0)
+            if(mode == TestValue.Success)
             {
                 Stdout(bad("FAILURE - Unexpected")).newline;
-                return 1;
+                return SuccessFailure;
             }
         }
     }