diff --git a/src/compiler.h b/src/compiler.h index 3c286204..ea73bf3c 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -887,6 +887,11 @@ __LITERAL_EXIT: }; _Code compile(VM* vm, const char* source, _Str filename, CompileMode mode=EXEC_MODE) { - Compiler compiler(vm, source, filename, mode); - return compiler.__fillCode(); + try{ + Compiler compiler(vm, source, filename, mode); + return compiler.__fillCode(); + }catch(std::exception& e){ + REDIRECT_ERROR() + return nullptr; + } } \ No newline at end of file diff --git a/src/error.h b/src/error.h index d3358a46..c83b3521 100644 --- a/src/error.h +++ b/src/error.h @@ -6,7 +6,7 @@ #include "str.h" -class NeedMoreLines : public std::exception { +class NeedMoreLines { public: NeedMoreLines(bool isClassDef) : isClassDef(isClassDef) {} bool isClassDef; @@ -92,4 +92,13 @@ class UnexpectedError : public _Error { public: UnexpectedError(_Str msg) : _Error("UnexpectedError", msg, "") {} -}; \ No newline at end of file +}; + +#define REDIRECT_ERROR() \ + if(const _Error* _ = dynamic_cast(&e)){ \ + vm->_stderr(e.what()); \ + }else{ \ + vm->_stderr(UnexpectedError(e.what()).what()); \ + } \ + vm->_stderr("\n"); + diff --git a/src/main.cpp b/src/main.cpp index 4172c9bc..81770522 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,22 +7,15 @@ //#define PK_DEBUG //#define PK_DEBUG_TIME -class Timer{ -private: - std::chrono::time_point start; - std::string title; -public: - Timer(const std::string& title){ -#ifdef PK_DEBUG_TIME - start = std::chrono::high_resolution_clock::now(); - this->title = title; -#endif - } - - void stop(){ -#ifdef PK_DEBUG_TIME +struct Timer{ + const char* title; + Timer(const char* title) : title(title) {} + void run(std::function f){ + auto start = std::chrono::high_resolution_clock::now(); + f(); auto end = std::chrono::high_resolution_clock::now(); double elapsed = std::chrono::duration_cast(end - start).count() / 1000000.0; +#ifdef PK_DEBUG_TIME std::cout << title << ": " << elapsed << " s" << std::endl; #endif } @@ -32,6 +25,9 @@ VM* newVM(){ VM* vm = createVM([](const char* str) { std::cout << str; std::cout.flush(); + }, [](const char* str) { + std::cerr << str; + std::cerr.flush(); }); return vm; } @@ -40,12 +36,10 @@ void REPL(){ std::cout << "pocketpy 0.1.0" << std::endl; std::cout << "https://github.com/blueloveTH/pocketpy" << std::endl; #ifdef PK_DEBUG - std::cout << "[ DEBUG MODE ENABLED ]" << std::endl; + std::cout << "[DEBUG MODE ENABLED]" << std::endl; #endif - int need_more_lines = 0; - std::string buffer; VM* vm = newVM(); @@ -75,24 +69,14 @@ __NOT_ENOUGH_LINES: if(line == "exit()") break; if(line.empty()) continue; } + try{ _Code code = compile(vm, line.c_str(), "", mode); - vm->exec(code); -#ifdef PK_DEBUG - }catch(NeedMoreLines& e){ -#else - }catch(std::exception& e){ -#endif - NeedMoreLines* ne = dynamic_cast(&e); - if(ne){ - buffer += line; - buffer += '\n'; - need_more_lines = ne->isClassDef ? 3 : 2; - }else{ - vm->_stdout(e.what()); - vm->_stdout("\n"); - vm->cleanError(); - } + if(code != nullptr) vm->exec(code); + }catch(NeedMoreLines& ne){ + buffer += line; + buffer += '\n'; + need_more_lines = ne.isClassDef ? 3 : 2; } } } @@ -101,9 +85,6 @@ int main(int argc, char** argv){ if(argc == 1){ REPL(); return 0; - - // argc = 2; - // argv = new char*[2]{argv[0], (char*)"../tests/singletype/basic.py"}; } if(argc == 2){ @@ -112,24 +93,26 @@ int main(int argc, char** argv){ std::cout << "Usage: pocketpy [filename]" << std::endl; return 0; } -#ifndef PK_DEBUG - try{ -#endif - std::ifstream file(filename); - std::string src((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - VM* vm = newVM(); - Timer timer("Compile time"); - _Code code = compile(vm, src.c_str(), filename); - timer.stop(); - //std::cout << code->toString() << std::endl; - Timer timer2("Running time"); - vm->exec(code); - timer2.stop(); -#ifndef PK_DEBUG - }catch(std::exception& e){ - std::cout << e.what() << std::endl; + + std::ifstream file(filename); + if(!file.is_open()){ + std::cerr << "File not found: " << filename << std::endl; + return 1; } -#endif + std::string src((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + + VM* vm = newVM(); + _Code code; + Timer("Compile time").run([&]{ + code = compile(vm, src.c_str(), filename); + }); + if(code == nullptr) return 1; + + //std::cout << code->toString() << std::endl; + + Timer("Running time").run([=]{ + vm->exec(code); + }); return 0; } } \ No newline at end of file diff --git a/src/pocketpy.h b/src/pocketpy.h index 3504422d..36d03468 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -414,13 +414,24 @@ void __runCodeBuiltins(VM* vm, const char* src){ #define __EXPORT #endif +#include +void __addModuleTime(VM* vm){ + PyVar mod = vm->newModule("time"); + vm->bindFunc(mod, "time", [](VM* vm, PyVarList args) { + return vm->PyInt((int)std::time(nullptr)); + }); +} + + extern "C" { __EXPORT - VM* createVM(PrintFn _stdout){ + VM* createVM(PrintFn _stdout, PrintFn _stderr){ VM* vm = new VM(); __initializeBuiltinFunctions(vm); __runCodeBuiltins(vm, __BUILTINS_CODE); + __addModuleTime(vm); vm->_stdout = _stdout; + vm->_stderr = _stderr; return vm; } @@ -431,14 +442,8 @@ extern "C" { __EXPORT void exec(VM* vm, const char* source){ - try{ - _Code code = compile(vm, source, "main.py"); - vm->exec(code); - }catch(std::exception& e){ - vm->_stdout(e.what()); - vm->_stdout("\n"); - vm->cleanError(); - } + _Code code = compile(vm, source, "main.py"); + if(code != nullptr) vm->exec(code); } __EXPORT diff --git a/src/vm.h b/src/vm.h index 3be44c2c..f0d6b307 100644 --- a/src/vm.h +++ b/src/vm.h @@ -27,123 +27,8 @@ class VM{ private: std::stack< std::shared_ptr > callstack; std::vector numPool; -public: - PyVarDict _types; // builtin types - PyVar None, True, False; - - PrintFn _stdout = [](auto s){}; - PrintFn _stderr = [](auto s){}; - - PyVar builtins; // builtins module - PyVar _main; // __main__ module PyVarDict _modules; // 3rd modules - VM(){ - initializeBuiltinClasses(); - } - - PyVar asStr(const PyVar& obj){ - PyVarOrNull str_fn = getAttr(obj, __str__, false); - if(str_fn != nullptr) return call(str_fn, {}); - return asRepr(obj); - } - - PyVar asRepr(const PyVar& obj){ - if(obj->isType(_tp_type)) return PyStr("getName() + "'>"); - return call(obj, __repr__, {}); - } - - PyVar asBool(const PyVar& obj){ - if(obj == None) return False; - PyVar tp = obj->attribs[__class__]; - if(tp == _tp_bool) return obj; - if(tp == _tp_int) return PyBool(PyInt_AS_C(obj) != 0); - if(tp == _tp_float) return PyBool(PyFloat_AS_C(obj) != 0.0f); - PyVarOrNull len_fn = getAttr(obj, "__len__", false); - if(len_fn != nullptr){ - PyVar ret = call(len_fn, {}); - return PyBool(PyInt_AS_C(ret) > 0); - } - return True; - } - - PyVar fastCall(const PyVar& obj, const _Str& name, PyVarList args){ - PyVar cls = obj->attribs[__class__]; - while(cls != None) { - auto it = cls->attribs.find(name); - if(it != cls->attribs.end()){ - return call(it->second, args); - } - cls = cls->attribs[__base__]; - } - attributeError(obj, name); - return nullptr; - } - - PyVar call(PyVar callable, PyVarList args){ - if(callable->isType(_tp_type)){ - auto it = callable->attribs.find(__new__); - PyVar obj; - if(it != callable->attribs.end()){ - obj = call(it->second, args); - }else{ - obj = newObject(callable, -1); - } - if(obj->isType(callable)){ - PyVarOrNull init_fn = getAttr(obj, __init__, false); - if (init_fn != nullptr) call(init_fn, args); - } - return obj; - } - - if(callable->isType(_tp_bounded_method)){ - auto& bm = PyBoundedMethod_AS_C(callable); - args.insert(args.begin(), bm.obj); - callable = bm.method; - } - - if(callable->isType(_tp_native_function)){ - auto f = std::get<_CppFunc>(callable->_native); - return f(this, args); - } else if(callable->isType(_tp_function)){ - _Func fn = PyFunction_AS_C(callable); - PyVarDict locals; - int i = 0; - for(const auto& name : fn.args){ - if(i < args.size()) { - locals[name] = args[i++]; - }else{ - typeError("missing positional argument '" + name + "'"); - } - } - // handle *args - if(!fn.starredArg.empty()){ - PyVarList vargs; - while(i < args.size()) vargs.push_back(args[i++]); - locals[fn.starredArg] = PyTuple(vargs); - } - // handle keyword arguments - for(const auto& [name, value] : fn.kwArgs){ - if(i < args.size()) { - locals[name] = args[i++]; - }else{ - locals[name] = value; - } - } - - if(i < args.size()) typeError("too many arguments"); - - // TODO: handle **kwargs - return exec(fn.code, locals); - } - typeError("'" + callable->getTypeName() + "' object is not callable"); - return None; - } - - inline PyVar call(const PyVar& obj, const _Str& func, PyVarList args){ - return call(getAttr(obj, func), args); - } - PyVar runFrame(std::shared_ptr frame){ callstack.push(frame); while(!frame->isEnd()){ @@ -380,11 +265,8 @@ public: { const _Str& name = frame->code->co_names[byte.arg]->name; auto it = _modules.find(name); - if(it == _modules.end()){ - _error("ImportError", "module '" + name + "' not found"); - }else{ - frame->push(it->second); - } + if(it == _modules.end()) _error("ImportError", "module '" + name + "' not found"); + else frame->push(it->second); } break; default: systemError(_Str("opcode ") + OP_NAMES[byte.op] + " is not implemented"); @@ -404,14 +286,139 @@ public: return None; } +public: + PyVarDict _types; // builtin types + PyVar None, True, False; + + PrintFn _stdout = [](auto s){}; + PrintFn _stderr = [](auto s){}; + + PyVar builtins; // builtins module + PyVar _main; // __main__ module + + VM(){ + initializeBuiltinClasses(); + } + + PyVar asStr(const PyVar& obj){ + PyVarOrNull str_fn = getAttr(obj, __str__, false); + if(str_fn != nullptr) return call(str_fn, {}); + return asRepr(obj); + } + + PyVar asRepr(const PyVar& obj){ + if(obj->isType(_tp_type)) return PyStr("getName() + "'>"); + return call(obj, __repr__, {}); + } + + PyVar asBool(const PyVar& obj){ + if(obj == None) return False; + PyVar tp = obj->attribs[__class__]; + if(tp == _tp_bool) return obj; + if(tp == _tp_int) return PyBool(PyInt_AS_C(obj) != 0); + if(tp == _tp_float) return PyBool(PyFloat_AS_C(obj) != 0.0f); + PyVarOrNull len_fn = getAttr(obj, "__len__", false); + if(len_fn != nullptr){ + PyVar ret = call(len_fn, {}); + return PyBool(PyInt_AS_C(ret) > 0); + } + return True; + } + + PyVar fastCall(const PyVar& obj, const _Str& name, PyVarList args){ + PyVar cls = obj->attribs[__class__]; + while(cls != None) { + auto it = cls->attribs.find(name); + if(it != cls->attribs.end()){ + return call(it->second, args); + } + cls = cls->attribs[__base__]; + } + attributeError(obj, name); + return nullptr; + } + + PyVar call(PyVar callable, PyVarList args){ + if(callable->isType(_tp_type)){ + auto it = callable->attribs.find(__new__); + PyVar obj; + if(it != callable->attribs.end()){ + obj = call(it->second, args); + }else{ + obj = newObject(callable, -1); + } + if(obj->isType(callable)){ + PyVarOrNull init_fn = getAttr(obj, __init__, false); + if (init_fn != nullptr) call(init_fn, args); + } + return obj; + } + + if(callable->isType(_tp_bounded_method)){ + auto& bm = PyBoundedMethod_AS_C(callable); + args.insert(args.begin(), bm.obj); + callable = bm.method; + } + + if(callable->isType(_tp_native_function)){ + auto f = std::get<_CppFunc>(callable->_native); + return f(this, args); + } else if(callable->isType(_tp_function)){ + _Func fn = PyFunction_AS_C(callable); + PyVarDict locals; + int i = 0; + for(const auto& name : fn.args){ + if(i < args.size()) { + locals[name] = args[i++]; + }else{ + typeError("missing positional argument '" + name + "'"); + } + } + // handle *args + if(!fn.starredArg.empty()){ + PyVarList vargs; + while(i < args.size()) vargs.push_back(args[i++]); + locals[fn.starredArg] = PyTuple(vargs); + } + // handle keyword arguments + for(const auto& [name, value] : fn.kwArgs){ + if(i < args.size()) { + locals[name] = args[i++]; + }else{ + locals[name] = value; + } + } + + if(i < args.size()) typeError("too many arguments"); + + // TODO: handle **kwargs + return exec(fn.code, locals); + } + typeError("'" + callable->getTypeName() + "' object is not callable"); + return None; + } + + inline PyVar call(const PyVar& obj, const _Str& func, PyVarList args){ + return call(getAttr(obj, func), args); + } + PyVar exec(const _Code& code, const PyVarDict& locals={}, PyVar _module=nullptr){ + if(code == nullptr) UNREACHABLE(); if(_module == nullptr) _module = _main; auto frame = std::make_shared( code.get(), locals, &_module->attribs ); - return runFrame(frame); + + try { + return runFrame(frame); + } catch (const std::exception& e) { + while(!callstack.empty()) callstack.pop(); + VM* vm = this; + REDIRECT_ERROR() + return None; + } } PyVar newUserClassType(_Str name, PyVar base){ @@ -456,8 +463,9 @@ public: } PyVar newModule(_Str name) { - PyVar obj = newObject(_tp_module, 0); + PyVar obj = newObject(_tp_module, -2); setAttr(obj, "__name__", PyStr(name)); + _modules[name] = obj; return obj; } @@ -628,7 +636,6 @@ public: void registerCompiledModule(_Str name, _Code code){ PyVar _m = newModule(name); exec(code, {}, _m); - _modules[name] = _m; } /***** Error Reporter *****/ @@ -644,10 +651,6 @@ private: } public: - void cleanError(){ - while(!callstack.empty()) callstack.pop(); - } - void typeError(const _Str& msg){ typeError(msg); }