blueloveTH 3 лет назад
Родитель
Сommit
01bbda85ec
7 измененных файлов с 100 добавлено и 59 удалено
  1. 2 2
      amalgamate.py
  2. 44 29
      src/compiler.h
  3. 43 19
      src/error.h
  4. 8 6
      src/main.cpp
  5. 1 1
      src/str.h
  6. 1 1
      src/vm.h
  7. 1 1
      tests/singletype/basic.py

+ 2 - 2
amalgamate.py

@@ -2,9 +2,9 @@ with open("src/opcodes.h", "rt", encoding='utf-8') as f:
 	OPCODES_TEXT = f.read()
 
 pipeline = [
-	["str.h", "builtins.h"],
+	["str.h", "builtins.h", "error.h"],
 	["obj.h", "iter.h", "parser.h", "pointer.h", "codeobject.h"],
-	["error.h", "vm.h", "compiler.h"],
+	["vm.h", "compiler.h"],
 	["pocketpy.h"]
 ]
 

+ 44 - 29
src/compiler.h

@@ -124,8 +124,7 @@ public:
         while (true) {
             char c = parser->eatChar();
             if (c == quote) break;
-            if (c == '\0')
-                throw SyntaxError(path, parser->makeErrToken(), "EOL while scanning string literal");
+            if (c == '\0') syntaxError("EOL while scanning string literal");
             if (c == '\\') {
                 switch (parser->eatCharIncludeNewLine()) {
                     case '"':  buff.push_back('"');  break;
@@ -135,7 +134,7 @@ public:
                     case 'r':  buff.push_back('\r'); break;
                     case 't':  buff.push_back('\t'); break;
                     case '\n': case '\r': break;
-                    default: throw SyntaxError(path, parser->makeErrToken(), "invalid escape character");
+                    default: syntaxError("invalid escape character");
                 }
             } else {
                 buff.push_back(c);
@@ -172,7 +171,7 @@ public:
                 }  
             }
         }catch(std::exception& e){
-            throw SyntaxError(path, parser->makeErrToken(), "invalid number (%s)", e.what());
+            syntaxError("invalid number literal");
         }
     }
 
@@ -210,7 +209,7 @@ public:
                 }
                 case '!':
                     if(parser->matchChar('=')) parser->setNextToken(TK("!="));
-                    else SyntaxError(path, parser->makeErrToken(), "expected '=' after '!'");
+                    else syntaxError("expected '=' after '!'");
                     break;
                 case '*':
                     if (parser->matchChar('*')) {
@@ -229,10 +228,8 @@ public:
                 case '\r': break;       // just ignore '\r'
                 case ' ': case '\t': parser->eatSpaces(); break;
                 case '\n': {
-                    parser->setNextToken(TK("@eol"));
-                    while(parser->matchChar('\n'));
-                    if(!parser->eatIndentation())
-                        throw SyntaxError(path, parser->makeErrToken(), "unindent does not match any outer indentation level");
+                    parser->setNextToken(TK("@eol")); while(parser->matchChar('\n'));
+                    if(!parser->eatIndentation()) indentationError("unindent does not match any outer indentation level");
                     return;
                 }
                 default: {
@@ -245,7 +242,7 @@ public:
                         }
                         parser->eatName();
                     } else {
-                        throw SyntaxError(path, parser->makeErrToken(), "unknown character: %c", c);
+                        syntaxError("unknown character: " + _Str(1, c));
                     }
                     return;
                 }
@@ -270,7 +267,9 @@ public:
         lexToken();
         Token prev = parser->previous;
         if (prev.type != expected){
-            throw SyntaxError(path, prev, "expected '%s', but got '%s'", TK_STR(expected), TK_STR(prev.type));
+            _StrStream ss;
+            ss << "expected '" << TK_STR(expected) << "', but got '" << TK_STR(prev.type) << "'";
+            syntaxError(ss.str());
         }
     }
 
@@ -298,8 +297,7 @@ public:
     }
 
     void consumeEndStatement() {
-        if (!matchEndStatement())
-            throw SyntaxError(path, parser->current, "expected statement end");
+        if (!matchEndStatement()) syntaxError("expected statement end");
     }
 
     void exprLiteral() {
@@ -591,7 +589,7 @@ __LISTCOMP:
     void __compileBlockBody(CompilerAction action) {
         consume(TK(":"));
         if(!matchNewLines(mode==SINGLE_MODE)){
-            throw SyntaxError(path, parser->previous, "expected a new line after ':'");
+            syntaxError("expected a new line after ':'");
         }
         consume(TK("@indent"));
         while (peek() != TK("@dedent")) {
@@ -627,15 +625,16 @@ __LISTCOMP:
         lexToken();
         GrammarFn prefix = rules[parser->previous.type].prefix;
 
-        if (prefix == nullptr) {
-            throw SyntaxError(path, parser->previous, "expected an expression");
-        }
+        if (prefix == nullptr) syntaxError("expected an expression");
 
         (this->*prefix)();
         while (rules[peek()].precedence >= precedence) {
             lexToken();
             _TokenType op = parser->previous.type;
             GrammarFn infix = rules[op].infix;
+            if(infix == nullptr) {
+                throw UnexpectedError("(infix == nullptr) is true");
+            }
             (this->*infix)();
         }
     }
@@ -706,20 +705,18 @@ __LISTCOMP:
 
     void compileStatement() {
         if (match(TK("break"))) {
-            if (loops.empty()) throw SyntaxError(path, parser->previous, "'break' outside loop");
+            if (loops.empty()) syntaxError("'break' outside loop");
             consumeEndStatement();
             if(getLoop().forLoop) emitCode(OP_POP_TOP); // pop the iterator of for loop.
             int patch = emitCode(OP_JUMP_ABSOLUTE);
             getLoop().breaks.push_back(patch);
         } else if (match(TK("continue"))) {
-            if (loops.empty()) {
-                throw SyntaxError(path, parser->previous, "'continue' not properly in loop");
-            }
+            if (loops.empty()) syntaxError("'continue' not properly in loop");
             consumeEndStatement();
             emitCode(OP_JUMP_ABSOLUTE, getLoop().start);
         } else if (match(TK("return"))) {
             if (codes.size() == 1)
-                throw SyntaxError(path, parser->previous, "'return' outside function");
+                syntaxError("'return' outside function");
             if(matchEndStatement()){
                 emitCode(OP_LOAD_NONE);
             }else{
@@ -786,14 +783,11 @@ __LISTCOMP:
     void __compileFunctionArgs(_Func& func){
         int state = 0;      // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs
         do {
-            if(state == 3){
-                throw SyntaxError(path, parser->previous, "**kwargs should be the last argument");
-            }
-
+            if(state == 3) syntaxError("**kwargs should be the last argument");
             matchNewLines();
             if(match(TK("*"))){
                 if(state < 1) state = 1;
-                else throw SyntaxError(path, parser->previous, "*args should be placed before **kwargs");
+                else syntaxError("*args should be placed before **kwargs");
             }
             else if(match(TK("**"))){
                 state = 3;
@@ -801,7 +795,7 @@ __LISTCOMP:
 
             consume(TK("@id"));
             const _Str& name = parser->previous.str();
-            if(func.hasName(name)) throw SyntaxError(path, parser->previous, "duplicate argument name");
+            if(func.hasName(name)) syntaxError("duplicate argument name");
 
             if(state == 0 && peek() == TK("=")) state = 2;
 
@@ -845,7 +839,7 @@ __LISTCOMP:
         if(match(TK("True"))) goto __LITERAL_EXIT;
         if(match(TK("False"))) goto __LITERAL_EXIT;
         if(match(TK("None"))) goto __LITERAL_EXIT;
-        throw SyntaxError(path, parser->previous, "expect a literal, not %s", TK_STR(parser->current.type));
+        syntaxError(_Str("expect a literal, not ") + TK_STR(parser->current.type));
 __LITERAL_EXIT:
         return parser->previous.value;
     }
@@ -879,6 +873,27 @@ __LITERAL_EXIT:
             matchNewLines();
         }
     }
+
+    /**** Error Reporter ***/
+    LineSnapshot getLineSnapshot(){
+        LineSnapshot snapshot;
+        snapshot.filename = path;
+        snapshot.lineno = parser->previous.line;
+        snapshot.source = "<?>";
+        return snapshot;
+    }
+
+    void syntaxError(_Str msg){
+        throw CompileError("SyntaxError", msg, getLineSnapshot());
+    }
+
+    void unknownSyntaxError(){
+        throw CompileError("SyntaxError", "invalid syntax", getLineSnapshot());
+    }
+
+    void indentationError(_Str msg){
+        throw CompileError("IndentationError", msg, getLineSnapshot());
+    }
 };
 
 _Code compile(VM* vm, const char* source, _Str filename, CompileMode mode=EXEC_MODE) {

+ 43 - 19
src/error.h

@@ -3,9 +3,8 @@
 #include <string>
 #include <vector>
 #include <stdexcept>
-#include <stdarg.h>
 
-#include "parser.h"
+#include "str.h"
 
 class NeedMoreLines : public std::exception {
 public:
@@ -13,31 +12,56 @@ public:
     bool isClassDef;
 };
 
-class SyntaxError : public std::exception {
+class _Error : public std::exception {
 private:
     _Str _what;
-
 public:
-    char message[100];
-    _Str path;
-    int lineno;
+    _Error(_Str type, _Str msg, _Str desc){
+        _what = desc + type + ": " + msg;
+    }
 
-    SyntaxError(const _Str& path, Token tk, const char* msg, ...) {
-        va_list args;
-        va_start(args, msg);
-        vsnprintf(message, 100, msg, args);
-        va_end(args);
+    const char* what() const noexcept override {
+        return _what;
+    }
+};
 
-        this->path = path;
-        lineno = tk.line;
+struct LineSnapshot {
+    _Str filename;
+    int lineno;
+    _Str source;
 
+    _Str str() const {
         _StrStream ss;
-        ss << "  File '" << path << "', line " << std::to_string(lineno) << std::endl;
-        ss << _Str("SyntaxError: ") << message;
-        _what = ss.str();
+        ss << "  " << "File \"" << filename << "\", line " << lineno << '\n';
+        ss << "    " << source << '\n';
+        return ss.str();
     }
+};
 
-    const char* what() const noexcept override {
-        return _what.str().c_str();
+class CompileError : public _Error {
+public:
+    CompileError(_Str type, _Str msg, const LineSnapshot& snapshot)
+        : _Error(type, msg, snapshot.str()) {}
+};
+
+class RuntimeError : public _Error {
+private:
+    static _Str __concat(std::stack<LineSnapshot> snapshots){
+        _StrStream ss;
+        ss << "Traceback (most recent call last):" << '\n';
+        while(!snapshots.empty()){
+            ss << snapshots.top().str();
+            snapshots.pop();
+        }
+        return ss.str();
     }
+public:
+    RuntimeError(_Str type, _Str msg, std::stack<LineSnapshot> snapshots)
+        : _Error(type, msg, __concat(snapshots)) {}
+};
+
+class UnexpectedError : public _Error {
+public:
+    UnexpectedError(_Str msg)
+        : _Error("UnexpectedError", msg, "") {}
 };

+ 8 - 6
src/main.cpp

@@ -4,8 +4,8 @@
 #include <chrono>
 #include "pocketpy.h"
 
-#define PK_DEBUG
-#define PK_DEBUG_TIME
+//#define PK_DEBUG
+//#define PK_DEBUG_TIME
 
 class Timer{
 private:
@@ -39,6 +39,7 @@ VM* newVM(){
 
 void REPL(){
     std::cout << "pocketpy 0.1.0" << std::endl;
+    std::cout << "https://github.com/blueloveTH/pocketpy" << std::endl;
 
     int need_more_lines = 0;
 
@@ -79,10 +80,11 @@ __NOT_ENOUGH_LINES:
 #else
         }catch(std::exception& e){
 #endif
-            if(dynamic_cast<NeedMoreLines*>(&e)){
+            NeedMoreLines* ne = dynamic_cast<NeedMoreLines*>(&e);
+            if(ne){
                 buffer += line;
                 buffer += '\n';
-                need_more_lines = e.isClassDef ? 3 : 2;
+                need_more_lines = ne->isClassDef ? 3 : 2;
             }else{
                 vm->printFn(e.what());
                 vm->printFn("\n");
@@ -113,11 +115,11 @@ int main(int argc, char** argv){
             std::ifstream file(filename);
             std::string src((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
             VM* vm = newVM();
-            Timer timer("编译时间");
+            Timer timer("Compile time");
             _Code code = compile(vm, src.c_str(), filename);
             timer.stop();
             //std::cout << code->toString() << std::endl;
-            Timer timer2("运行时间");
+            Timer timer2("Running time");
             vm->exec(code);
             timer2.stop();
 #ifndef PK_DEBUG

+ 1 - 1
src/str.h

@@ -29,7 +29,7 @@ private:
 public:
     _Str(const char* s): _s(s) {}
     _Str(const char* s, size_t len): _s(s, len) {}
-    _Str(int n, char fill = ' '): _s(n, fill) {}
+    _Str(int n, char fill): _s(n, fill) {}
     _Str(const std::string& s): _s(s) {}
     _Str(std::string&& s): _s(std::move(s)) {}
     _Str(const _StrStream& ss): _s(ss.str()) {}

+ 1 - 1
src/vm.h

@@ -165,7 +165,7 @@ public:
         callstack.push(frame);
         while(!frame->isEnd()){
             const ByteCode& byte = frame->readCode();
-            printf("%s (%d) stack_size: %d\n", OP_NAMES[byte.op], byte.arg, frame->stackSize());
+            //printf("%s (%d) stack_size: %d\n", OP_NAMES[byte.op], byte.arg, frame->stackSize());
 
             switch (byte.op)
             {

+ 1 - 1
tests/singletype/basic.py

@@ -34,7 +34,7 @@ assert x%8 == 3
 
 
 assert 2**3 == 8 
-assert -2**2 == -4 
+assert -2**2 == 4 
 assert (-2)**2 == 4
 assert compare(0.2**2,0.04) == 1    
 x = 4