diff --git a/amalgamate.py b/amalgamate.py index ebbdcf68..990b99a0 100644 --- a/amalgamate.py +++ b/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"] ] diff --git a/src/compiler.h b/src/compiler.h index a482270d..bee2c8b9 100644 --- a/src/compiler.h +++ b/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) { diff --git a/src/error.h b/src/error.h index 5a246ac1..e141bf1f 100644 --- a/src/error.h +++ b/src/error.h @@ -3,9 +3,8 @@ #include #include #include -#include -#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; - - SyntaxError(const _Str& path, Token tk, const char* msg, ...) { - va_list args; - va_start(args, msg); - vsnprintf(message, 100, msg, args); - va_end(args); - - this->path = path; - lineno = tk.line; - - _StrStream ss; - ss << " File '" << path << "', line " << std::to_string(lineno) << std::endl; - ss << _Str("SyntaxError: ") << message; - _what = ss.str(); + _Error(_Str type, _Str msg, _Str desc){ + _what = desc + type + ": " + msg; } const char* what() const noexcept override { - return _what.str().c_str(); + return _what; } +}; + +struct LineSnapshot { + _Str filename; + int lineno; + _Str source; + + _Str str() const { + _StrStream ss; + ss << " " << "File \"" << filename << "\", line " << lineno << '\n'; + ss << " " << source << '\n'; + return ss.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 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 snapshots) + : _Error(type, msg, __concat(snapshots)) {} +}; + +class UnexpectedError : public _Error { +public: + UnexpectedError(_Str msg) + : _Error("UnexpectedError", msg, "") {} }; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index aa8e9d0f..a8f53abc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,8 +4,8 @@ #include #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(&e)){ + NeedMoreLines* ne = dynamic_cast(&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(file)), std::istreambuf_iterator()); 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 diff --git a/src/str.h b/src/str.h index 8d347d4c..64818473 100644 --- a/src/str.h +++ b/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()) {} diff --git a/src/vm.h b/src/vm.h index 59630126..02905ea5 100644 --- a/src/vm.h +++ b/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) { diff --git a/tests/singletype/basic.py b/tests/singletype/basic.py index cb709c12..cd41e443 100644 --- a/tests/singletype/basic.py +++ b/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