소스 검색

adjust errors

blueloveTH 3 년 전
부모
커밋
5c7f1e6dd8
7개의 변경된 파일128개의 추가작업 그리고 95개의 파일을 삭제
  1. 8 16
      src/compiler.h
  2. 3 1
      src/error.h
  3. 3 3
      src/main.cpp
  4. 4 4
      src/parser.h
  5. 20 20
      src/pocketpy.h
  6. 73 51
      src/vm.h
  7. 17 0
      tests/errors/1.py

+ 8 - 16
src/compiler.h

@@ -593,6 +593,7 @@ __LISTCOMP:
         }
         consume(TK("@indent"));
         while (peek() != TK("@dedent")) {
+            matchNewLines();
             (this->*action)();
             matchNewLines();
         }
@@ -624,17 +625,13 @@ __LISTCOMP:
     void parsePrecedence(Precedence precedence) {
         lexToken();
         GrammarFn prefix = rules[parser->previous.type].prefix;
-
-        if (prefix == nullptr) syntaxError("expected an expression");
-
+        if (prefix == nullptr) syntaxError(_Str("expected an expression, but got ") + TK_STR(parser->previous.type));
         (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");
-            }
+            if(infix == nullptr) throw UnexpectedError("(infix == nullptr) is true");
             (this->*infix)();
         }
     }
@@ -874,23 +871,18 @@ __LITERAL_EXIT:
         }
     }
 
-    /**** Error Reporter ***/
+    /***** Error Reporter *****/
     LineSnapshot getLineSnapshot(){
-        LineSnapshot snapshot;
-        snapshot.filename = path;
-        snapshot.lineno = parser->previous.line;
-        snapshot.source = "<?>";
-        return snapshot;
+        const char* line_start = parser->line_starts.at(parser->previous.line-1);
+        const char* i = line_start;
+        while(*i != '\n' && *i != '\0') i++;
+        return LineSnapshot(path, parser->previous.line, _Str(line_start, i-line_start));
     }
 
     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());
     }

+ 3 - 1
src/error.h

@@ -25,10 +25,12 @@ public:
     }
 };
 
-struct LineSnapshot {
+class LineSnapshot {
     _Str filename;
     int lineno;
     _Str source;
+public:
+    LineSnapshot(_Str filename, int lineno, _Str source="<?>") : filename(filename), lineno(lineno), source(source) {}
 
     _Str str() const {
         _StrStream ss;

+ 3 - 3
src/main.cpp

@@ -48,7 +48,7 @@ void REPL(){
 
     while(true){
         CompileMode mode = SINGLE_MODE;
-        vm->printFn(need_more_lines ? "... " : ">>> ");
+        vm->_stdout(need_more_lines ? "... " : ">>> ");
         std::string line;
         std::getline(std::cin, line);
 
@@ -86,8 +86,8 @@ __NOT_ENOUGH_LINES:
                 buffer += '\n';
                 need_more_lines = ne->isClassDef ? 3 : 2;
             }else{
-                vm->printFn(e.what());
-                vm->printFn("\n");
+                vm->_stdout(e.what());
+                vm->_stdout("\n");
                 vm->cleanError();
             }
         }

+ 4 - 4
src/parser.h

@@ -87,10 +87,11 @@ struct Parser {
     const char* source;         //< Currently compiled source.
     const char* token_start;    //< Start of the currently parsed token.
     const char* current_char;   //< Current char position in the source.
-    const char* line_start;     //< Start of the current line.
 
     int current_line = 1;
 
+    std::vector<const char*> line_starts;
+
     Token previous, current;
     std::queue<Token> nexts;
 
@@ -159,7 +160,7 @@ struct Parser {
         current_char++;
         if (c == '\n'){
             current_line++;
-            line_start = current_char;
+            line_starts.push_back(current_char);
         }
         return c;
     }
@@ -235,8 +236,7 @@ struct Parser {
         this->source = source;
         this->token_start = source;
         this->current_char = source;
-        this->line_start = source;
-
+        this->line_starts.push_back(source);
         this->nexts.push(Token{TK("@sof"), token_start, 0, current_line});
         this->indents.push(0);
     }

+ 20 - 20
src/pocketpy.h

@@ -11,7 +11,7 @@ inline int _round(float f){
 #define BIND_NUM_ARITH_OPT(name, op)                                                                    \
     _vm->bindMethodMulti({"int","float"}, #name, [](VM* vm, PyVarList args){               \
         if(!vm->isIntOrFloat(args[0], args[1]))                                                         \
-            vm->_error("TypeError", "unsupported operand type(s) for " #op );                        \
+            vm->typeError("unsupported operand type(s) for " #op );                        \
         if(args[0]->isType(vm->_tp_int) && args[1]->isType(vm->_tp_int)){                               \
             return vm->PyInt(vm->PyInt_AS_C(args[0]) op vm->PyInt_AS_C(args[1]));                       \
         }else{                                                                                          \
@@ -23,7 +23,7 @@ inline int _round(float f){
     _vm->bindMethodMulti({"int","float"}, #name, [](VM* vm, PyVarList args){               \
         if(!vm->isIntOrFloat(args[0], args[1])){                                                        \
             if constexpr(fallback) return vm->PyBool(args[0] op args[1]);                               \
-            vm->_error("TypeError", "unsupported operand type(s) for " #op );                        \
+            vm->typeError("unsupported operand type(s) for " #op );                        \
         }                                                                                               \
         return vm->PyBool(vm->numToFloat(args[0]) op vm->numToFloat(args[1]));                          \
     });
@@ -44,14 +44,14 @@ void __initializeBuiltinFunctions(VM* _vm) {
 #undef BIND_NUM_LOGICAL_OPT
 
     _vm->bindBuiltinFunc("print", [](VM* vm, PyVarList args) {
-        for (auto& arg : args) vm->printFn(vm->PyStr_AS_C(vm->asStr(arg)) + " ");
-        vm->printFn("\n");
+        for (auto& arg : args) vm->_stdout(vm->PyStr_AS_C(vm->asStr(arg)) + " ");
+        vm->_stdout("\n");
         return vm->None;
     });
 
     _vm->bindBuiltinFunc("eval", [](VM* vm, PyVarList args) {
-        if (args.size() != 1) vm->_error("TypeError", "eval() takes exactly one argument");
-        if (!args[0]->isType(vm->_tp_str)) vm->_error("TypeError", "eval() argument must be a string");
+        if (args.size() != 1) vm->typeError("eval() takes exactly one argument");
+        if (!args[0]->isType(vm->_tp_str)) vm->typeError("eval() argument must be a string");
         const _Str& expr = vm->PyStr_AS_C(args[0]);
         _Code code = compile(vm, expr, "<f-string>", EVAL_MODE);
         return vm->exec(code);      // not working in function
@@ -67,7 +67,7 @@ void __initializeBuiltinFunctions(VM* _vm) {
 
     _vm->bindBuiltinFunc("chr", [](VM* vm, PyVarList args) {
         int i = vm->PyInt_AS_C(args.at(0));
-        if (i < 0 || i > 128) vm->_error("ValueError", "chr() arg not in range(128)");
+        if (i < 0 || i > 128) vm->valueError("chr() arg not in range(128)");
         return vm->PyStr(_Str(1, (char)i));
     });
 
@@ -77,7 +77,7 @@ void __initializeBuiltinFunctions(VM* _vm) {
 
     _vm->bindBuiltinFunc("ord", [](VM* vm, PyVarList args) {
         _Str s = vm->PyStr_AS_C(args.at(0));
-        if (s.size() != 1) vm->_error("TypeError", "ord() expected an ASCII character");
+        if (s.size() != 1) vm->typeError("ord() expected an ASCII character");
         return vm->PyInt((int)s[0]);
     });
 
@@ -104,7 +104,7 @@ void __initializeBuiltinFunctions(VM* _vm) {
             case 1: r.stop = vm->PyInt_AS_C(args[0]); break;
             case 2: r.start = vm->PyInt_AS_C(args[0]); r.stop = vm->PyInt_AS_C(args[1]); break;
             case 3: r.start = vm->PyInt_AS_C(args[0]); r.stop = vm->PyInt_AS_C(args[1]); r.step = vm->PyInt_AS_C(args[2]); break;
-            default: vm->_error("TypeError", "range expected 1-3 arguments, got " + std::to_string(args.size()));
+            default: vm->typeError("range expected 1-3 arguments, got " + std::to_string(args.size()));
         }
         return vm->PyRange(r);
     });
@@ -121,13 +121,13 @@ void __initializeBuiltinFunctions(VM* _vm) {
 
     _vm->bindMethodMulti({"int", "float"}, "__truediv__", [](VM* vm, PyVarList args) {
         if(!vm->isIntOrFloat(args[0], args[1]))
-            vm->_error("TypeError", "unsupported operand type(s) for " "/" );
+            vm->typeError("unsupported operand type(s) for " "/" );
         return vm->PyFloat(vm->numToFloat(args[0]) / vm->numToFloat(args[1]));
     });
 
     _vm->bindMethodMulti({"int", "float"}, "__pow__", [](VM* vm, PyVarList args) {
         if(!vm->isIntOrFloat(args[0], args[1]))
-            vm->_error("TypeError", "unsupported operand type(s) for " "**" );
+            vm->typeError("unsupported operand type(s) for " "**" );
         if(args[0]->isType(vm->_tp_int) && args[1]->isType(vm->_tp_int)){
             return vm->PyInt(_round(pow(vm->PyInt_AS_C(args[0]), vm->PyInt_AS_C(args[1]))));
         }else{
@@ -138,19 +138,19 @@ void __initializeBuiltinFunctions(VM* _vm) {
     /************ PyInt ************/
     _vm->bindMethod("int", "__floordiv__", [](VM* vm, PyVarList args) {
         if(!args[0]->isType(vm->_tp_int) || !args[1]->isType(vm->_tp_int))
-            vm->_error("TypeError", "unsupported operand type(s) for " "//" );
+            vm->typeError("unsupported operand type(s) for " "//" );
         return vm->PyInt(vm->PyInt_AS_C(args[0]) / vm->PyInt_AS_C(args[1]));
     });
 
     _vm->bindMethod("int", "__mod__", [](VM* vm, PyVarList args) {
         if(!args[0]->isType(vm->_tp_int) || !args[1]->isType(vm->_tp_int))
-            vm->_error("TypeError", "unsupported operand type(s) for " "%" );
+            vm->typeError("unsupported operand type(s) for " "%" );
         return vm->PyInt(vm->PyInt_AS_C(args[0]) % vm->PyInt_AS_C(args[1]));
     });
 
     _vm->bindMethod("int", "__neg__", [](VM* vm, PyVarList args) {
         if(!args[0]->isType(vm->_tp_int))
-            vm->_error("TypeError", "unsupported operand type(s) for " "-" );
+            vm->typeError("unsupported operand type(s) for " "-" );
         return vm->PyInt(-1 * vm->PyInt_AS_C(args[0]));
     });
 
@@ -175,7 +175,7 @@ void __initializeBuiltinFunctions(VM* _vm) {
 
     _vm->bindMethod("str", "__add__", [](VM* vm, PyVarList args) {
         if(!args[0]->isType(vm->_tp_str) || !args[1]->isType(vm->_tp_str))
-            vm->_error("TypeError", "unsupported operand type(s) for " "+" );
+            vm->typeError("unsupported operand type(s) for " "+" );
         const _Str& lhs = vm->PyStr_AS_C(args[0]);
         const _Str& rhs = vm->PyStr_AS_C(args[1]);
         return vm->PyStr(lhs + rhs);
@@ -317,7 +317,7 @@ void __initializeBuiltinFunctions(VM* _vm) {
 
     _vm->bindMethod("list", "pop", [](VM* vm, PyVarList args) {
         PyVarList& _self = vm->PyList_AS_C(args[0]);
-        if(_self.empty()) vm->_error("IndexError", "pop from empty list");
+        if(_self.empty()) vm->indexError("pop from empty list");
         PyVar ret = _self.back();
         _self.pop_back();
         return ret;
@@ -428,12 +428,12 @@ void __addModuleRandom(VM* vm){
 
 extern "C" {
     __EXPORT
-    VM* createVM(PrintFn printFn){
+    VM* createVM(PrintFn _stdout){
         VM* vm = new VM();
         __initializeBuiltinFunctions(vm);
         __runCodeBuiltins(vm, __BUILTINS_CODE);
         __addModuleRandom(vm);
-        vm->printFn = printFn;
+        vm->_stdout = _stdout;
         return vm;
     }
 
@@ -448,8 +448,8 @@ extern "C" {
             _Code code = compile(vm, source, "main.py");
             vm->exec(code);
         }catch(std::exception& e){
-            vm->printFn(e.what());
-            vm->printFn("\n");
+            vm->_stdout(e.what());
+            vm->_stdout("\n");
             vm->cleanError();
         }
     }

+ 73 - 51
src/vm.h

@@ -2,6 +2,7 @@
 
 #include "codeobject.h"
 #include "iter.h"
+#include "error.h"
 
 #define __DEF_PY_AS_C(type, ctype, ptype)                       \
     inline ctype& Py##type##_AS_C(const PyVar& obj) {           \
@@ -18,7 +19,6 @@
     __DEF_PY(type, ctype, ptype)                                \
     __DEF_PY_AS_C(type, ctype, ptype)
 
-// TODO: we should split this into stdout and stderr
 typedef void(*PrintFn)(const char*);
 
 #define NUM_POOL_MAX_SIZE 1024
@@ -31,7 +31,8 @@ public:
     PyVarDict _types;         // builtin types
     PyVar None, True, False;
 
-    PrintFn printFn = [](auto s){};
+    PrintFn _stdout = [](auto s){};
+    PrintFn _stderr = [](auto s){};
     
     PyVar builtins;         // builtins module
     PyVar _main;            // __main__ module
@@ -41,24 +42,6 @@ public:
         initializeBuiltinClasses();
     }
 
-    void cleanError(){
-        while(!callstack.empty()) callstack.pop();
-    }
-
-    void nameError(const _Str& name){
-        _error("NameError", "name '" + name + "' is not defined");
-    }
-
-    void attributeError(PyVar obj, const _Str& name){
-        _error("AttributeError", "type '" + obj->getTypeName() + "' has no attribute '" + name + "'");
-    }
-
-    inline void __checkType(const PyVar& obj, const PyVar& type){
-        if(!obj->isType(type)){
-            _error("TypeError", "expected '" + type->getName() + "', but got '" + obj->getTypeName() + "'");
-        }
-    }
-
     PyVar asStr(const PyVar& obj){
         PyVarOrNull str_fn = getAttr(obj, __str__, false);
         if(str_fn != nullptr) return call(str_fn, {});
@@ -130,7 +113,7 @@ public:
                 if(i < args.size()) {
                     locals[name] = args[i++];
                 }else{
-                    _error("TypeError", "missing positional argument '" + name + "'");
+                    typeError("missing positional argument '" + name + "'");
                 }
             }
             // handle *args
@@ -148,12 +131,12 @@ public:
                 }
             }
 
-            if(i < args.size()) _error("TypeError", "too many arguments");
+            if(i < args.size()) typeError("too many arguments");
 
             // TODO: handle **kwargs
             return exec(fn.code, locals);
         }
-        _error("TypeError", "'" + callable->getTypeName() + "' object is not callable");
+        typeError("'" + callable->getTypeName() + "' object is not callable");
         return None;
     }
 
@@ -264,8 +247,8 @@ public:
                 {
                     const PyVar& expr = frame->topValue(this);
                     if(expr == None) break;
-                    printFn(PyStr_AS_C(asRepr(expr)));
-                    printFn("\n");
+                    _stdout(PyStr_AS_C(asRepr(expr)));
+                    _stdout("\n");
                 } break;
             case OP_POP_TOP: frame->popValue(this); break;
             case OP_BINARY_OP:
@@ -318,7 +301,7 @@ public:
             case OP_ASSERT:
                 {
                     PyVar expr = frame->popValue(this);
-                    if(!PyBool_AS_C(expr)) _error("AssertionError", "assertion failed");
+                    _assert(PyBool_AS_C(expr), "assertion failed");
                 } break;
             case OP_RAISE_ERROR:
                 {
@@ -357,7 +340,7 @@ public:
                         PyIter_AS_C(tmp)->var = PyPointer_AS_C(frame->__pop());
                         frame->push(tmp);
                     }else{
-                        _error("TypeError", "'" + obj->getTypeName() + "' object is not iterable");
+                        typeError("'" + obj->getTypeName() + "' object is not iterable");
                     }
                 } break;
             case OP_FOR_ITER:
@@ -404,19 +387,19 @@ public:
                     }
                 } break;
             default:
-                _error("SystemError", _Str("opcode ") + OP_NAMES[byte.op] + " is not implemented");
+                systemError(_Str("opcode ") + OP_NAMES[byte.op] + " is not implemented");
                 break;
             }
         }
 
         if(frame->code->mode == EVAL_MODE) {
             if(frame->stackSize() != 1) {
-                _error("SystemError", "stack size is not 1 in EVAL_MODE");
+                systemError("stack size is not 1 in EVAL_MODE");
             }
             return frame->popValue(this);
         }
 
-        if(frame->stackSize() != 0) _error("SystemError", "stack not empty in EXEC_MODE");
+        if(frame->stackSize() != 0) systemError("stack not empty in EXEC_MODE");
         callstack.pop();
         return None;
     }
@@ -431,19 +414,6 @@ public:
         return runFrame(frame);
     }
 
-    void _assert(bool val, const _Str& msg){
-        if (!val) _error("AssertionError", msg);
-    }
-
-    void _error(const _Str& name, const _Str& msg){
-        _StrStream ss;
-        auto frame = callstack.top();
-        ss << "Traceback (most recent call last):" << std::endl;
-        ss << "  File '" << frame->code->co_filename << "', line ";
-        ss << frame->currentLine() << '\n' << name << ": " << msg;
-        throw std::runtime_error(ss.str());
-    }
-
     PyVar newUserClassType(_Str name, PyVar base){
         PyVar obj = newClassType(name, base);
         setAttr(obj, "__name__", PyStr(name));
@@ -469,7 +439,7 @@ public:
 
     PyVar newNumber(PyVar type, _Value _native) {
         if(type != _tp_int && type != _tp_float)
-            _error("SystemError", "type is not a number type");
+            systemError("type is not a number type");
         PyObject* _raw = nullptr;
         if(numPool.size() > 0) {
             _raw = numPool.back();
@@ -567,7 +537,7 @@ public:
     int normalizedIndex(int index, int size){
         if(index < 0) index += size;
         if(index < 0 || index >= size){
-            _error("IndexError", "index out of range, " + std::to_string(index) + " not in [0, " + std::to_string(size) + ")");
+            indexError("index out of range, " + std::to_string(index) + " not in [0, " + std::to_string(size) + ")");
         }
         return index;
     }
@@ -651,7 +621,7 @@ public:
         }
         if (obj->isType(_tp_str)) return PyStr_AS_C(obj).hash();
         if (obj->isType(_tp_type)) return (int64_t)obj.get();
-        _error("TypeError", "unhashable type: " + obj->getTypeName());
+        typeError("unhashable type: " + obj->getTypeName());
         return 0;
     }
 
@@ -660,9 +630,61 @@ public:
         exec(code, {}, _m);
         _modules[name] = _m;
     }
+
+    /***** Error Reporter *****/
+private:
+    void _error(const _Str& name, const _Str& msg){
+        std::stack<LineSnapshot> snapshots;
+        while (!callstack.empty()){
+            auto frame = callstack.top();
+            snapshots.push(LineSnapshot(
+                frame->code->co_filename,
+                frame->currentLine()
+            ));
+            callstack.pop();
+        }
+        throw RuntimeError(name, msg, snapshots);
+    }
+
+public:
+    void cleanError(){
+        while(!callstack.empty()) callstack.pop();
+    }
+
+    void typeError(const _Str& msg){
+        typeError(msg);
+    }
+
+    void systemError(const _Str& msg){
+        systemError(msg);
+    }
+
+    void indexError(const _Str& msg){
+        _error("IndexError", msg);
+    }
+
+    void valueError(const _Str& msg){
+        _error("ValueError", msg);
+    }
+
+    void nameError(const _Str& name){
+        _error("NameError", "name '" + name + "' is not defined");
+    }
+
+    void attributeError(PyVar obj, const _Str& name){
+        _error("AttributeError", "type '" + obj->getTypeName() + "' has no attribute '" + name + "'");
+    }
+
+    inline void __checkType(const PyVar& obj, const PyVar& type){
+        if(!obj->isType(type)) typeError("expected '" + type->getName() + "', but got '" + obj->getTypeName() + "'");
+    }
+
+    void _assert(bool val, const _Str& msg){
+        if (!val) _error("AssertionError", msg);
+    }
 };
 
-/**************** Pointers' Impl ****************/
+/***** Pointers' Impl *****/
 
 PyVar NamePointer::get(VM* vm, Frame* frame) const{
     auto it = frame->f_locals.find(name);
@@ -724,7 +746,7 @@ void AttrPointer::set(VM* vm, Frame* frame, PyVar val) const{
 }
 
 void AttrPointer::del(VM* vm, Frame* frame) const{
-    vm->_error("AttributeError", "can't delete attribute");
+    vm->typeError("cannot delete attribute");
 }
 
 PyVar IndexPointer::get(VM* vm, Frame* frame) const{
@@ -749,11 +771,11 @@ PyVar CompoundPointer::get(VM* vm, Frame* frame) const{
 
 void CompoundPointer::set(VM* vm, Frame* frame, PyVar val) const{
     if(!val->isType(vm->_tp_tuple) && !val->isType(vm->_tp_list)){
-        vm->_error("TypeError", "only tuple or list can be unpacked");
+        vm->typeError("only tuple or list can be unpacked");
     }
     const PyVarList& args = std::get<PyVarList>(val->_native);
-    if(args.size() > pointers.size()) vm->_error("ValueError", "too many values to unpack");
-    if(args.size() < pointers.size()) vm->_error("ValueError", "not enough values to unpack");
+    if(args.size() > pointers.size()) vm->valueError("too many values to unpack");
+    if(args.size() < pointers.size()) vm->valueError("not enough values to unpack");
     for (int i = 0; i < pointers.size(); i++) {
         pointers[i]->set(vm, frame, args[i]);
     }

+ 17 - 0
tests/errors/1.py

@@ -0,0 +1,17 @@
+def is_prime(x):
+  if x<2:
+    return False
+  
+  for i in range(2,x):
+    if x%i == 0:
+      return False1
+  return True
+
+def test(n):
+  k = 0
+  for i in range(n):
+    if is_prime(i):
+      k += 1
+  return k
+
+print(test(100))