This commit is contained in:
blueloveTH 2023-07-02 03:52:44 +08:00
parent f3ac21ccc2
commit 656d1d7291
26 changed files with 2204 additions and 2032 deletions

3
.gitignore vendored
View File

@ -26,4 +26,5 @@ pocketpy.exe
main.obj
pocketpy.exp
pocketpy.lib
APPS
APPS
build

43
CMakeLists.txt Normal file
View File

@ -0,0 +1,43 @@
cmake_minimum_required(VERSION 3.10)
project(pocketpy)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
execute_process(
COMMAND python prebuild.py
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
RESULT_VARIABLE PREBUILD_RESULT
)
if(NOT ${PREBUILD_RESULT} EQUAL 0)
message(FATAL_ERROR "Prebuild failed with code ${PREBUILD_RESULT}")
endif()
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR- /EHsc /utf-8 /O2")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fexceptions -O2")
endif()
find_program(CLANGPP clang++)
if(CLANGPP)
message(STATUS "Using clang with libc++")
set(CMAKE_CXX_COMPILER ${CLANGPP})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/src POCKETPY_SRC)
option(BUILD_EXE "Build executable" ON)
if(BUILD_EXE)
message(STATUS "Building executable")
add_executable(${PROJECT_NAME} ${POCKETPY_SRC} src2/main.cpp)
else()
message(STATUS "Building library")
add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC} src2/lib.cpp)
endif()

View File

@ -1,14 +1,14 @@
import os
os.system("python3 preprocess.py")
os.system("python3 prebuild.py")
with open("src/opcodes.h", "rt", encoding='utf-8') as f:
OPCODES_TEXT = f.read()
with open("include/pocketpy/opcodes.h", "rt", encoding='utf-8') as f:
OPCODES_TEXT = '\n' + f.read() + '\n'
pipeline = [
["config.h", "common.h", "memory.h", "vector.h", "str.h", "tuplelist.h", "namedict.h", "error.h", "lexer.h"],
["obj.h", "dict.h", "codeobject.h", "frame.h"],
["gc.h", "vm.h", "expr.h", "compiler.h", "repl.h"],
["gc.h", "vm.h", "ceval.h", "expr.h", "compiler.h", "repl.h"],
["_generated.h", "cffi.h", "iter.h", "base64.h", "random.h", "re.h", "linalg.h", "easing.h", "io.h"],
["export.h", "pocketpy.h"]
]
@ -24,24 +24,41 @@ import time
if os.path.exists("amalgamated"):
shutil.rmtree("amalgamated")
time.sleep(0.6)
time.sleep(0.5)
os.mkdir("amalgamated")
def remove_copied_include(text):
text = text.replace("#pragma once", "")
def _replace(m):
key = m.group(1)
if key.startswith("pocketpy/"):
key = key[9:]
if key == "user_config.h":
return m.group(0)
if key == "opcodes.h":
return OPCODES_TEXT
assert key in copied, f"include {key} not found"
return ""
text = re.sub(
r'#include\s+"(.+)"\s*',
lambda m: "" if m.group(1) in copied else m.group(0),
_replace,
text
)
text = text.replace('#include "opcodes.h"', OPCODES_TEXT)
return text
for seq in pipeline:
for j in seq:
with open("src/"+j, "rt", encoding='utf-8') as f:
print(j)
with open("include/pocketpy/"+j, "rt", encoding='utf-8') as f:
text += remove_copied_include(f.read()) + '\n'
copied.add(j)
j = j.replace(".h", ".cpp")
if os.path.exists("src/"+j):
with open("src/"+j, "rt", encoding='utf-8') as f:
text += remove_copied_include(f.read()) + '\n'
copied.add(j)
with open("amalgamated/pocketpy.h", "wt", encoding='utf-8') as f:
final_text = \
@ -56,7 +73,12 @@ r'''/*
''' + text + '\n#endif // POCKETPY_H'
f.write(final_text)
shutil.copy("src/main.cpp", "amalgamated/main.cpp")
shutil.copy("src2/main.cpp", "amalgamated/main.cpp")
with open("amalgamated/main.cpp", "rt", encoding='utf-8') as f:
text = f.read()
text = text.replace('#include "pocketpy/pocketpy.h"', '#include "pocketpy.h"')
with open("amalgamated/main.cpp", "wt", encoding='utf-8') as f:
f.write(text)
if sys.platform == 'linux':
ok = os.system("clang++ -o pocketpy amalgamated/main.cpp --std=c++17 -stdlib=libc++")
@ -67,7 +89,7 @@ if sys.platform == 'linux':
print("amalgamated/pocketpy.h")
content = []
for i in ["src/export.h", "c_bindings/pocketpy_c.h", "c_bindings/pocketpy_c.cpp"]:
for i in ["include/pocketpy/export.h", "c_bindings/pocketpy_c.h", "c_bindings/pocketpy_c.cpp"]:
with open(i, "rt", encoding='utf-8') as g:
content.append(g.read())
@ -90,11 +112,4 @@ if os.path.exists(unity_ios_root):
shutil.copy("amalgamated/pocketpy.h", unity_ios_root)
shutil.copy("amalgamated/pocketpy.cpp", unity_ios_root)
# my custom things...
if os.path.exists("/mnt/e/PainterEngine/project/pocketpy.h"):
shutil.copy("amalgamated/pocketpy.h", "/mnt/e/PainterEngine/project/pocketpy.h")
shutil.copy("src/easing.pyi", "/mnt/e/PainterEngine/game/pype/easing.pyi")
shutil.copy("src/linalg.pyi", "/mnt/e/PainterEngine/game/pype/linalg.pyi")
shutil.copy("src/c.pyi", "/mnt/e/PainterEngine/game/pype/c.pyi")

View File

@ -1,20 +1,9 @@
#pragma once
#include "common.h"
#if PK_MODULE_BASE64
#include "cffi.h"
namespace pkpy {
void add_module_base64(VM* vm);
} // namespace pkpy
#else
ADD_MODULE_PLACEHOLDER(base64)
#endif
} // namespace pkpy

4
include/pocketpy/ceval.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include "vm.h"
// dummy header for ceval.cpp

View File

@ -29,7 +29,7 @@ namespace pkpy {
#define VAR_T(T, ...) vm->heap.gcnew<T>(T::_type(vm), T(__VA_ARGS__))
static int c99_sizeof(VM*, const Str&);
int c99_sizeof(VM*, const Str&);
inline PyObject* py_var(VM* vm, void* p);
inline PyObject* py_var(VM* vm, char* p);
@ -60,126 +60,7 @@ struct VoidP{
return "0x" + ss.str();
}
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_default_constructor<VoidP>(type);
vm->bind_func<1>(type, "from_hex", [](VM* vm, ArgsView args){
std::string s = CAST(Str&, args[0]).str();
size_t size;
intptr_t ptr = std::stoll(s, &size, 16);
if(size != s.size()) vm->ValueError("invalid literal for void_p(): " + s);
return VAR_T(VoidP, (void*)ptr);
});
vm->bind_method<0>(type, "hex", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
return VAR(self.hex());
});
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
VoidP& self = _CAST(VoidP&, obj);
std::stringstream ss;
ss << "<void* at " << self.hex();
if(self.base_offset != 1) ss << ", base_offset=" << self.base_offset;
ss << ">";
return VAR(ss.str());
});
#define BIND_CMP(name, op) \
vm->bind##name(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){ \
if(!is_non_tagged_type(rhs, VoidP::_type(vm))) return vm->NotImplemented; \
return VAR(_CAST(VoidP&, lhs) op _CAST(VoidP&, rhs)); \
});
BIND_CMP(__eq__, ==)
BIND_CMP(__lt__, <)
BIND_CMP(__le__, <=)
BIND_CMP(__gt__, >)
BIND_CMP(__ge__, >=)
#undef BIND_CMP
vm->bind__hash__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
VoidP& self = _CAST(VoidP&, obj);
return reinterpret_cast<i64>(self.ptr);
});
vm->bind_method<1>(type, "set_base_offset", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
if(is_non_tagged_type(args[1], vm->tp_str)){
const Str& type = _CAST(Str&, args[1]);
self.base_offset = c99_sizeof(vm, type);
}else{
self.base_offset = CAST(int, args[1]);
}
return vm->None;
});
vm->bind_method<0>(type, "get_base_offset", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
return VAR(self.base_offset);
});
vm->bind_method<1>(type, "offset", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
i64 offset = CAST(i64, args[1]);
return VAR_T(VoidP, (char*)self.ptr + offset * self.base_offset);
});
vm->bind__add__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
VoidP& self = _CAST(VoidP&, lhs);
i64 offset = CAST(i64, rhs);
return VAR_T(VoidP, (char*)self.ptr + offset);
});
vm->bind__sub__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
VoidP& self = _CAST(VoidP&, lhs);
i64 offset = CAST(i64, rhs);
return VAR_T(VoidP, (char*)self.ptr - offset);
});
#define BIND_SETGET(T, name) \
vm->bind_method<0>(type, "read_" name, [](VM* vm, ArgsView args){ \
VoidP& self = _CAST(VoidP&, args[0]); \
return VAR(*(T*)self.ptr); \
}); \
vm->bind_method<1>(type, "write_" name, [](VM* vm, ArgsView args){ \
VoidP& self = _CAST(VoidP&, args[0]); \
*(T*)self.ptr = CAST(T, args[1]); \
return vm->None; \
});
BIND_SETGET(char, "char")
BIND_SETGET(unsigned char, "uchar")
BIND_SETGET(short, "short")
BIND_SETGET(unsigned short, "ushort")
BIND_SETGET(int, "int")
BIND_SETGET(unsigned int, "uint")
BIND_SETGET(long, "long")
BIND_SETGET(unsigned long, "ulong")
BIND_SETGET(long long, "longlong")
BIND_SETGET(unsigned long long, "ulonglong")
BIND_SETGET(float, "float")
BIND_SETGET(double, "double")
BIND_SETGET(bool, "bool")
BIND_SETGET(void*, "void_p")
vm->bind_method<1>(type, "read_bytes", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
i64 size = CAST(i64, args[1]);
std::vector<char> buffer(size);
memcpy(buffer.data(), self.ptr, size);
return VAR(Bytes(std::move(buffer)));
});
vm->bind_method<1>(type, "write_bytes", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
Bytes& bytes = CAST(Bytes&, args[1]);
memcpy(self.ptr, bytes.data(), bytes.size());
return vm->None;
});
}
#undef BIND_SETGET
static void _register(VM* vm, PyObject* mod, PyObject* type);
};
struct C99Struct{
@ -215,116 +96,7 @@ struct C99Struct{
~C99Struct(){ if(p!=_inlined) free(p); }
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_constructor<-1>(type, [](VM* vm, ArgsView args){
if(args.size() == 1+1){
if(is_int(args[1])){
int size = _CAST(int, args[1]);
return VAR_T(C99Struct, size);
}
if(is_non_tagged_type(args[1], vm->tp_str)){
const Str& s = _CAST(Str&, args[1]);
return VAR_T(C99Struct, (void*)s.data, s.size);
}
if(is_non_tagged_type(args[1], vm->tp_bytes)){
const Bytes& b = _CAST(Bytes&, args[1]);
return VAR_T(C99Struct, (void*)b.data(), b.size());
}
vm->TypeError("expected int, str or bytes");
return vm->None;
}
if(args.size() == 1+2){
void* p = CAST(void*, args[1]);
int size = CAST(int, args[2]);
return VAR_T(C99Struct, p, size);
}
vm->TypeError("expected 1 or 2 arguments");
return vm->None;
});
vm->bind_method<0>(type, "addr", [](VM* vm, ArgsView args){
C99Struct& self = _CAST(C99Struct&, args[0]);
return VAR_T(VoidP, self.p);
});
vm->bind_method<0>(type, "size", [](VM* vm, ArgsView args){
C99Struct& self = _CAST(C99Struct&, args[0]);
return VAR(self.size);
});
vm->bind_method<0>(type, "copy", [](VM* vm, ArgsView args){
const C99Struct& self = _CAST(C99Struct&, args[0]);
return VAR_T(C99Struct, self);
});
vm->bind_method<0>(type, "to_string", [](VM* vm, ArgsView args){
C99Struct& self = _CAST(C99Struct&, args[0]);
return VAR(Str(self.p, self.size));
});
vm->bind_method<0>(type, "to_bytes", [](VM* vm, ArgsView args){
C99Struct& self = _CAST(C99Struct&, args[0]);
std::vector<char> buffer(self.size);
memcpy(buffer.data(), self.p, self.size);
return VAR(Bytes(std::move(buffer)));
});
vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
C99Struct& self = _CAST(C99Struct&, lhs);
if(!is_non_tagged_type(rhs, C99Struct::_type(vm))) return vm->NotImplemented;
C99Struct& other = _CAST(C99Struct&, rhs);
bool ok = self.size == other.size && memcmp(self.p, other.p, self.size) == 0;
return VAR(ok);
});
#define BIND_SETGET(T, name) \
vm->bind(type, "read_" name "(self, offset=0)", [](VM* vm, ArgsView args){ \
C99Struct& self = _CAST(C99Struct&, args[0]); \
i64 offset = CAST(i64, args[1]); \
void* ptr = self.p + offset; \
return VAR(*(T*)ptr); \
}); \
vm->bind(type, "write_" name "(self, value, offset=0)", [](VM* vm, ArgsView args){ \
C99Struct& self = _CAST(C99Struct&, args[0]); \
i64 offset = CAST(i64, args[2]); \
void* ptr = self.p + offset; \
*(T*)ptr = CAST(T, args[1]); \
return vm->None; \
});
BIND_SETGET(char, "char")
BIND_SETGET(unsigned char, "uchar")
BIND_SETGET(short, "short")
BIND_SETGET(unsigned short, "ushort")
BIND_SETGET(int, "int")
BIND_SETGET(unsigned int, "uint")
BIND_SETGET(long, "long")
BIND_SETGET(unsigned long, "ulong")
BIND_SETGET(long long, "longlong")
BIND_SETGET(unsigned long long, "ulonglong")
BIND_SETGET(float, "float")
BIND_SETGET(double, "double")
BIND_SETGET(bool, "bool")
BIND_SETGET(void*, "void_p")
#undef BIND_SETGET
// patch VoidP
type = vm->_t(VoidP::_type(vm));
vm->bind_method<1>(type, "read_struct", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
const Str& type = CAST(Str&, args[1]);
int size = c99_sizeof(vm, type);
return VAR_T(C99Struct, self.ptr, size);
});
vm->bind_method<1>(type, "write_struct", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
C99Struct& other = CAST(C99Struct&, args[1]);
memcpy(self.ptr, other.p, other.size);
return vm->None;
});
}
static void _register(VM* vm, PyObject* mod, PyObject* type);
};
struct ReflField{
@ -351,13 +123,6 @@ inline void add_refl_type(std::string_view name, size_t size, std::vector<ReflFi
_refl_types[name] = std::move(type);
}
inline static int c99_sizeof(VM* vm, const Str& type){
auto it = _refl_types.find(type.sv());
if(it != _refl_types.end()) return it->second.size;
vm->ValueError("not a valid c99 type");
return 0;
}
struct C99ReflType final: ReflType{
PY_CLASS(C99ReflType, c, _refl)
@ -367,40 +132,7 @@ struct C99ReflType final: ReflType{
this->fields = type.fields;
}
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_notimplemented_constructor<C99ReflType>(type);
vm->bind_method<0>(type, "__call__", [](VM* vm, ArgsView args){
C99ReflType& self = _CAST(C99ReflType&, args[0]);
return VAR_T(C99Struct, nullptr, self.size);
});
vm->bind_method<0>(type, "__repr__", [](VM* vm, ArgsView args){
C99ReflType& self = _CAST(C99ReflType&, args[0]);
return VAR("<ctype '" + Str(self.name) + "'>");
});
vm->bind_method<0>(type, "name", [](VM* vm, ArgsView args){
C99ReflType& self = _CAST(C99ReflType&, args[0]);
return VAR(self.name);
});
vm->bind_method<0>(type, "size", [](VM* vm, ArgsView args){
C99ReflType& self = _CAST(C99ReflType&, args[0]);
return VAR(self.size);
});
vm->bind__getitem__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj, PyObject* key){
C99ReflType& self = _CAST(C99ReflType&, obj);
const Str& name = CAST(Str&, key);
auto it = std::lower_bound(self.fields.begin(), self.fields.end(), name.sv());
if(it == self.fields.end() || it->name != name.sv()){
vm->KeyError(key);
return vm->None;
}
return VAR(it->offset);
});
}
static void _register(VM* vm, PyObject* mod, PyObject* type);
};
static_assert(sizeof(Py_<C99Struct>) <= 64);
@ -483,73 +215,6 @@ inline void bind_any_c_fp(VM* vm, PyObject* obj, Str name, T fp){
obj->attr().set(name, func);
}
inline void add_module_c(VM* vm){
PyObject* mod = vm->new_module("c");
vm->bind_func<1>(mod, "malloc", [](VM* vm, ArgsView args){
i64 size = CAST(i64, args[0]);
return VAR(malloc(size));
});
vm->bind_func<1>(mod, "free", [](VM* vm, ArgsView args){
void* p = CAST(void*, args[0]);
free(p);
return vm->None;
});
vm->bind_func<1>(mod, "sizeof", [](VM* vm, ArgsView args){
const Str& type = CAST(Str&, args[0]);
i64 size = c99_sizeof(vm, type);
return VAR(size);
});
vm->bind_func<1>(mod, "refl", [](VM* vm, ArgsView args){
const Str& key = CAST(Str&, args[0]);
auto it = _refl_types.find(key.sv());
if(it == _refl_types.end()) vm->ValueError("reflection type not found");
const ReflType& rt = it->second;
return VAR_T(C99ReflType, rt);
});
vm->bind_func<3>(mod, "memset", [](VM* vm, ArgsView args){
void* p = CAST(void*, args[0]);
memset(p, CAST(int, args[1]), CAST(size_t, args[2]));
return vm->None;
});
vm->bind_func<3>(mod, "memcpy", [](VM* vm, ArgsView args){
void* dst = CAST(void*, args[0]);
void* src = CAST(void*, args[1]);
i64 size = CAST(i64, args[2]);
memcpy(dst, src, size);
return vm->None;
});
VoidP::register_class(vm, mod);
C99Struct::register_class(vm, mod);
C99ReflType::register_class(vm, mod);
mod->attr().set("NULL", VAR_T(VoidP, nullptr));
add_refl_type("char", sizeof(char), {});
add_refl_type("uchar", sizeof(unsigned char), {});
add_refl_type("short", sizeof(short), {});
add_refl_type("ushort", sizeof(unsigned short), {});
add_refl_type("int", sizeof(int), {});
add_refl_type("uint", sizeof(unsigned int), {});
add_refl_type("long", sizeof(long), {});
add_refl_type("ulong", sizeof(unsigned long), {});
add_refl_type("longlong", sizeof(long long), {});
add_refl_type("ulonglong", sizeof(unsigned long long), {});
add_refl_type("float", sizeof(float), {});
add_refl_type("double", sizeof(double), {});
add_refl_type("bool", sizeof(bool), {});
add_refl_type("void_p", sizeof(void*), {});
PyObject* void_p_t = mod->attr("void_p");
for(const char* t: {"char", "uchar", "short", "ushort", "int", "uint", "long", "ulong", "longlong", "ulonglong", "float", "double", "bool"}){
mod->attr().set(Str(t) + "_", VAR_T(C99ReflType, _refl_types[t]));
mod->attr().set(Str(t) + "_p", void_p_t);
}
}
void add_module_c(VM* vm);
} // namespace pkpy

View File

@ -33,8 +33,8 @@ enum CodeBlockType {
TRY_EXCEPT,
};
#define BC_NOARG -1
#define BC_KEEPLINE -1
inline const int BC_NOARG = -1;
inline const int BC_KEEPLINE = -1;
struct CodeBlock {
CodeBlockType type;

File diff suppressed because it is too large Load Diff

View File

@ -88,8 +88,6 @@ namespace pkpy{
#define PK_MODULE_RE 1
#define PK_MODULE_RANDOM 1
#define PK_MODULE_BASE64 1
#define PK_MODULE_LINALG 1
#define PK_MODULE_EASING 1
#endif

View File

@ -1,19 +1,9 @@
#pragma once
#include "common.h"
#if PK_MODULE_EASING
#include "cffi.h"
namespace pkpy{
void add_module_easing(VM* vm);
} // namespace pkpy
#else
ADD_MODULE_PLACEHOLDER(easing)
#endif
} // namespace pkpy

View File

@ -2,6 +2,12 @@
#include "cffi.h"
namespace pkpy{
Bytes _default_import_handler(const Str& name);
void add_module_os(VM* vm);
void add_module_io(VM* vm);
}
#if PK_ENABLE_OS
#include <filesystem>
@ -9,21 +15,6 @@
namespace pkpy{
inline Bytes _default_import_handler(const Str& name){
std::filesystem::path path(name.sv());
bool exists = std::filesystem::exists(path);
if(!exists) return Bytes();
std::string cname = name.str();
FILE* fp = fopen(cname.c_str(), "rb");
if(!fp) return Bytes();
fseek(fp, 0, SEEK_END);
std::vector<char> buffer(ftell(fp));
fseek(fp, 0, SEEK_SET);
fread(buffer.data(), 1, buffer.size(), fp);
fclose(fp);
return Bytes(std::move(buffer));
};
struct FileIO {
PY_CLASS(FileIO, io, FileIO)
@ -32,156 +23,11 @@ struct FileIO {
FILE* fp;
bool is_text() const { return mode != "rb" && mode != "wb" && mode != "ab"; }
FileIO(VM* vm, std::string file, std::string mode): file(file), mode(mode) {
fp = fopen(file.c_str(), mode.c_str());
if(!fp) vm->IOError(strerror(errno));
}
void close(){
if(fp == nullptr) return;
fclose(fp);
fp = nullptr;
}
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_constructor<3>(type, [](VM* vm, ArgsView args){
return VAR_T(FileIO,
vm, CAST(Str&, args[1]).str(), CAST(Str&, args[2]).str()
);
});
vm->bind_method<0>(type, "read", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]);
fseek(io.fp, 0, SEEK_END);
std::vector<char> buffer(ftell(io.fp));
fseek(io.fp, 0, SEEK_SET);
fread(buffer.data(), 1, buffer.size(), io.fp);
Bytes b(std::move(buffer));
if(io.is_text()) return VAR(Str(b.str()));
return VAR(std::move(b));
});
vm->bind_method<1>(type, "write", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]);
if(io.is_text()){
Str& s = CAST(Str&, args[1]);
fwrite(s.data, 1, s.length(), io.fp);
}else{
Bytes& buffer = CAST(Bytes&, args[1]);
fwrite(buffer.data(), 1, buffer.size(), io.fp);
}
return vm->None;
});
vm->bind_method<0>(type, "close", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]);
io.close();
return vm->None;
});
vm->bind_method<0>(type, "__exit__", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]);
io.close();
return vm->None;
});
vm->bind_method<0>(type, "__enter__", PK_LAMBDA(vm->None));
}
FileIO(VM* vm, std::string file, std::string mode);
void close();
static void _register(VM* vm, PyObject* mod, PyObject* type);
};
inline void add_module_io(VM* vm){
PyObject* mod = vm->new_module("io");
FileIO::register_class(vm, mod);
vm->bind_builtin_func<2>("open", [](VM* vm, ArgsView args){
static StrName m_io("io");
static StrName m_FileIO("FileIO");
return vm->call(vm->_modules[m_io]->attr(m_FileIO), args[0], args[1]);
});
}
#endif
inline void add_module_os(VM* vm){
PyObject* mod = vm->new_module("os");
PyObject* path_obj = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
mod->attr().set("path", path_obj);
// Working directory is shared by all VMs!!
vm->bind_func<0>(mod, "getcwd", [](VM* vm, ArgsView args){
return VAR(std::filesystem::current_path().string());
});
vm->bind_func<1>(mod, "chdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
std::filesystem::current_path(path);
return vm->None;
});
vm->bind_func<1>(mod, "listdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
std::filesystem::directory_iterator di;
try{
di = std::filesystem::directory_iterator(path);
}catch(std::filesystem::filesystem_error& e){
std::string msg = e.what();
auto pos = msg.find_last_of(":");
if(pos != std::string::npos) msg = msg.substr(pos + 1);
vm->IOError(Str(msg).lstrip());
}
List ret;
for(auto& p: di) ret.push_back(VAR(p.path().filename().string()));
return VAR(ret);
});
vm->bind_func<1>(mod, "remove", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool ok = std::filesystem::remove(path);
if(!ok) vm->IOError("operation failed");
return vm->None;
});
vm->bind_func<1>(mod, "mkdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool ok = std::filesystem::create_directory(path);
if(!ok) vm->IOError("operation failed");
return vm->None;
});
vm->bind_func<1>(mod, "rmdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool ok = std::filesystem::remove(path);
if(!ok) vm->IOError("operation failed");
return vm->None;
});
vm->bind_func<-1>(path_obj, "join", [](VM* vm, ArgsView args){
std::filesystem::path path;
for(int i=0; i<args.size(); i++){
path /= CAST(Str&, args[i]).sv();
}
return VAR(path.string());
});
vm->bind_func<1>(path_obj, "exists", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool exists = std::filesystem::exists(path);
return VAR(exists);
});
vm->bind_func<1>(path_obj, "basename", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
return VAR(path.filename().string());
});
}
} // namespace pkpy
#else
namespace pkpy{
inline void add_module_io(void* vm){}
inline void add_module_os(void* vm){}
inline Bytes _default_import_handler(const Str& name) { return Bytes(); }
} // namespace pkpy
#endif
} // namespace pkpy

View File

@ -113,449 +113,32 @@ struct Lexer {
bool used = false;
char peekchar() const{ return *curr_char; }
bool match_n_chars(int n, char c0);
bool match_string(const char* s);
int eat_spaces();
bool match_n_chars(int n, char c0){
const char* c = curr_char;
for(int i=0; i<n; i++){
if(*c == '\0') return false;
if(*c != c0) return false;
c++;
}
for(int i=0; i<n; i++) eatchar_include_newline();
return true;
}
bool eat_indentation();
char eatchar();
char eatchar_include_newline();
int eat_name();
void skip_line_comment();
bool matchchar(char c);
void add_token(TokenIndex type, TokenValue value={});
void add_token_2(char c, TokenIndex one, TokenIndex two);
Str eat_string_until(char quote, bool raw);
void eat_string(char quote, StringType type);
bool match_string(const char* s){
int s_len = strlen(s);
bool ok = strncmp(curr_char, s, s_len) == 0;
if(ok) for(int i=0; i<s_len; i++) eatchar_include_newline();
return ok;
}
int eat_spaces(){
int count = 0;
while (true) {
switch (peekchar()) {
case ' ' : count+=1; break;
case '\t': count+=4; break;
default: return count;
}
eatchar();
}
}
bool eat_indentation(){
if(brackets_level > 0) return true;
int spaces = eat_spaces();
if(peekchar() == '#') skip_line_comment();
if(peekchar() == '\0' || peekchar() == '\n') return true;
// https://docs.python.org/3/reference/lexical_analysis.html#indentation
if(spaces > indents.top()){
indents.push(spaces);
nexts.push_back(Token{TK("@indent"), token_start, 0, current_line, brackets_level});
} else if(spaces < indents.top()){
while(spaces < indents.top()){
indents.pop();
nexts.push_back(Token{TK("@dedent"), token_start, 0, current_line, brackets_level});
}
if(spaces != indents.top()){
return false;
}
}
return true;
}
char eatchar() {
char c = peekchar();
if(c == '\n') throw std::runtime_error("eatchar() cannot consume a newline");
curr_char++;
return c;
}
char eatchar_include_newline() {
char c = peekchar();
curr_char++;
if (c == '\n'){
current_line++;
src->line_starts.push_back(curr_char);
}
return c;
}
int eat_name() {
curr_char--;
while(true){
unsigned char c = peekchar();
int u8bytes = utf8len(c, true);
if(u8bytes == 0) return 1;
if(u8bytes == 1){
if(isalpha(c) || c=='_' || isdigit(c)) {
curr_char++;
continue;
}else{
break;
}
}
// handle multibyte char
std::string u8str(curr_char, u8bytes);
if(u8str.size() != u8bytes) return 2;
uint32_t value = 0;
for(int k=0; k < u8bytes; k++){
uint8_t b = u8str[k];
if(k==0){
if(u8bytes == 2) value = (b & 0b00011111) << 6;
else if(u8bytes == 3) value = (b & 0b00001111) << 12;
else if(u8bytes == 4) value = (b & 0b00000111) << 18;
}else{
value |= (b & 0b00111111) << (6*(u8bytes-k-1));
}
}
if(is_unicode_Lo_char(value)) curr_char += u8bytes;
else break;
}
int length = (int)(curr_char - token_start);
if(length == 0) return 3;
std::string_view name(token_start, length);
if(src->mode == JSON_MODE){
if(name == "true"){
add_token(TK("True"));
} else if(name == "false"){
add_token(TK("False"));
} else if(name == "null"){
add_token(TK("None"));
} else {
return 4;
}
return 0;
}
if(kTokenKwMap.count(name)){
add_token(kTokenKwMap.at(name));
} else {
add_token(TK("@id"));
}
return 0;
}
void skip_line_comment() {
char c;
while ((c = peekchar()) != '\0') {
if (c == '\n') return;
eatchar();
}
}
bool matchchar(char c) {
if (peekchar() != c) return false;
eatchar_include_newline();
return true;
}
void add_token(TokenIndex type, TokenValue value={}) {
switch(type){
case TK("{"): case TK("["): case TK("("): brackets_level++; break;
case TK(")"): case TK("]"): case TK("}"): brackets_level--; break;
}
auto token = Token{
type,
token_start,
(int)(curr_char - token_start),
current_line - ((type == TK("@eol")) ? 1 : 0),
brackets_level,
value
};
// handle "not in", "is not", "yield from"
if(!nexts.empty()){
auto& back = nexts.back();
if(back.type == TK("not") && type == TK("in")){
back.type = TK("not in");
return;
}
if(back.type == TK("is") && type == TK("not")){
back.type = TK("is not");
return;
}
if(back.type == TK("yield") && type == TK("from")){
back.type = TK("yield from");
return;
}
nexts.push_back(token);
}
}
void add_token_2(char c, TokenIndex one, TokenIndex two) {
if (matchchar(c)) add_token(two);
else add_token(one);
}
Str eat_string_until(char quote, bool raw) {
bool quote3 = match_n_chars(2, quote);
std::vector<char> buff;
while (true) {
char c = eatchar_include_newline();
if (c == quote){
if(quote3 && !match_n_chars(2, quote)){
buff.push_back(c);
continue;
}
break;
}
if (c == '\0'){
if(quote3 && src->mode == REPL_MODE){
throw NeedMoreLines(false);
}
SyntaxError("EOL while scanning string literal");
}
if (c == '\n'){
if(!quote3) SyntaxError("EOL while scanning string literal");
else{
buff.push_back(c);
continue;
}
}
if (!raw && c == '\\') {
switch (eatchar_include_newline()) {
case '"': buff.push_back('"'); break;
case '\'': buff.push_back('\''); break;
case '\\': buff.push_back('\\'); break;
case 'n': buff.push_back('\n'); break;
case 'r': buff.push_back('\r'); break;
case 't': buff.push_back('\t'); break;
case 'x': {
char hex[3] = {eatchar(), eatchar(), '\0'};
size_t parsed;
char code;
try{
code = (char)Number::stoi(hex, &parsed, 16);
}catch(std::invalid_argument&){
SyntaxError("invalid hex char");
}
if (parsed != 2) SyntaxError("invalid hex char");
buff.push_back(code);
} break;
default: SyntaxError("invalid escape char");
}
} else {
buff.push_back(c);
}
}
return Str(buff.data(), buff.size());
}
void eat_string(char quote, StringType type) {
Str s = eat_string_until(quote, type == RAW_STRING);
if(type == F_STRING){
add_token(TK("@fstr"), s);
}else{
add_token(TK("@str"), s);
}
}
void eat_number() {
static const std::regex pattern("^(0x)?[0-9a-fA-F]+(\\.[0-9]+)?(L)?");
std::smatch m;
const char* i = token_start;
while(*i != '\n' && *i != '\0') i++;
std::string s = std::string(token_start, i);
bool ok = std::regex_search(s, m, pattern);
PK_ASSERT(ok);
// here is m.length()-1, since the first char was eaten by lex_token()
for(int j=0; j<m.length()-1; j++) eatchar();
if(m[3].matched){
add_token(TK("@long"));
return;
}
try{
int base = 10;
size_t size;
if (m[1].matched) base = 16;
if (m[2].matched) {
if(base == 16) SyntaxError("hex literal should not contain a dot");
add_token(TK("@num"), Number::stof(m[0], &size));
} else {
add_token(TK("@num"), Number::stoi(m[0], &size, base));
}
PK_ASSERT((int)size == (int)m.length());
}catch(std::exception& e){
PK_UNUSED(e);
SyntaxError("invalid number literal");
}
}
bool lex_one_token() {
while (peekchar() != '\0') {
token_start = curr_char;
char c = eatchar_include_newline();
switch (c) {
case '\'': case '"': eat_string(c, NORMAL_STRING); return true;
case '#': skip_line_comment(); break;
case '{': add_token(TK("{")); return true;
case '}': add_token(TK("}")); return true;
case ',': add_token(TK(",")); return true;
case ':': add_token(TK(":")); return true;
case ';': add_token(TK(";")); return true;
case '(': add_token(TK("(")); return true;
case ')': add_token(TK(")")); return true;
case '[': add_token(TK("[")); return true;
case ']': add_token(TK("]")); return true;
case '@': add_token(TK("@")); return true;
case '$': {
for(int i=TK("$goto"); i<=TK("$label"); i++){
// +1 to skip the '$'
if(match_string(TK_STR(i) + 1)){
add_token((TokenIndex)i);
return true;
}
}
SyntaxError("invalid special token");
} return false;
case '%': add_token_2('=', TK("%"), TK("%=")); return true;
case '&': add_token_2('=', TK("&"), TK("&=")); return true;
case '|': add_token_2('=', TK("|"), TK("|=")); return true;
case '^': add_token_2('=', TK("^"), TK("^=")); return true;
case '?': add_token(TK("?")); return true;
case '.': {
if(matchchar('.')) {
if(matchchar('.')) {
add_token(TK("..."));
} else {
SyntaxError("invalid token '..'");
}
} else {
add_token(TK("."));
}
return true;
}
case '=': add_token_2('=', TK("="), TK("==")); return true;
case '+':
if(matchchar('+')){
add_token(TK("++"));
}else{
add_token_2('=', TK("+"), TK("+="));
}
return true;
case '>': {
if(matchchar('=')) add_token(TK(">="));
else if(matchchar('>')) add_token_2('=', TK(">>"), TK(">>="));
else add_token(TK(">"));
return true;
}
case '<': {
if(matchchar('=')) add_token(TK("<="));
else if(matchchar('<')) add_token_2('=', TK("<<"), TK("<<="));
else add_token(TK("<"));
return true;
}
case '-': {
if(matchchar('-')){
add_token(TK("--"));
}else{
if(matchchar('=')) add_token(TK("-="));
else if(matchchar('>')) add_token(TK("->"));
else add_token(TK("-"));
}
return true;
}
case '!':
if(matchchar('=')) add_token(TK("!="));
else SyntaxError("expected '=' after '!'");
break;
case '*':
if (matchchar('*')) {
add_token(TK("**")); // '**'
} else {
add_token_2('=', TK("*"), TK("*="));
}
return true;
case '/':
if(matchchar('/')) {
add_token_2('=', TK("//"), TK("//="));
} else {
add_token_2('=', TK("/"), TK("/="));
}
return true;
case ' ': case '\t': eat_spaces(); break;
case '\n': {
add_token(TK("@eol"));
if(!eat_indentation()) IndentationError("unindent does not match any outer indentation level");
return true;
}
default: {
if(c == 'f'){
if(matchchar('\'')) {eat_string('\'', F_STRING); return true;}
if(matchchar('"')) {eat_string('"', F_STRING); return true;}
}else if(c == 'r'){
if(matchchar('\'')) {eat_string('\'', RAW_STRING); return true;}
if(matchchar('"')) {eat_string('"', RAW_STRING); return true;}
}
if (c >= '0' && c <= '9') {
eat_number();
return true;
}
switch (eat_name())
{
case 0: break;
case 1: SyntaxError("invalid char: " + std::string(1, c)); break;
case 2: SyntaxError("invalid utf8 sequence: " + std::string(1, c)); break;
case 3: SyntaxError("@id contains invalid char"); break;
case 4: SyntaxError("invalid JSON token"); break;
default: FATAL_ERROR();
}
return true;
}
}
}
token_start = curr_char;
while(indents.size() > 1){
indents.pop();
add_token(TK("@dedent"));
return true;
}
add_token(TK("@eof"));
return false;
}
void eat_number();
bool lex_one_token();
/***** Error Reporter *****/
void throw_err(Str type, Str msg){
int lineno = current_line;
const char* cursor = curr_char;
if(peekchar() == '\n'){
lineno--;
cursor--;
}
throw_err(type, msg, lineno, cursor);
}
void throw_err(Str type, Str msg, int lineno, const char* cursor){
auto e = Exception(type, msg);
e.st_push(src->snapshot(lineno, cursor));
throw e;
}
void throw_err(Str type, Str msg);
void throw_err(Str type, Str msg, int lineno, const char* cursor);
void SyntaxError(Str msg){ throw_err("SyntaxError", msg); }
void SyntaxError(){ throw_err("SyntaxError", "invalid syntax"); }
void IndentationError(Str msg){ throw_err("IndentationError", msg); }
Lexer(shared_ptr<SourceData> src) {
this->src = src;
this->token_start = src->source.c_str();
this->curr_char = src->source.c_str();
this->nexts.push_back(Token{TK("@sof"), token_start, 0, current_line, brackets_level});
this->indents.push(0);
}
std::vector<Token> run() {
if(used) FATAL_ERROR();
used = true;
while (lex_one_token());
return std::move(nexts);
}
Lexer(shared_ptr<SourceData> src);
std::vector<Token> run();
};
} // namespace pkpy

View File

@ -1,9 +1,5 @@
#pragma once
#include "common.h"
#if PK_MODULE_LINALG
#include "cffi.h"
namespace pkpy{
@ -378,10 +374,4 @@ inline void add_module_linalg(VM* vm){
static_assert(sizeof(Py_<PyMat3x3>) <= 64);
} // namespace pkpy
#else
ADD_MODULE_PLACEHOLDER(linalg)
#endif
} // namespace pkpy

View File

@ -1,6 +1,4 @@
#include "pocketpy/common.h"
#include "pocketpy/namedict.h"
#include "pocketpy/vm.h"
#include "pocketpy/ceval.h"
namespace pkpy{

348
src/cffi.cpp Normal file
View File

@ -0,0 +1,348 @@
#include "pocketpy/cffi.h"
namespace pkpy{
void VoidP::_register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_default_constructor<VoidP>(type);
vm->bind_func<1>(type, "from_hex", [](VM* vm, ArgsView args){
std::string s = CAST(Str&, args[0]).str();
size_t size;
intptr_t ptr = std::stoll(s, &size, 16);
if(size != s.size()) vm->ValueError("invalid literal for void_p(): " + s);
return VAR_T(VoidP, (void*)ptr);
});
vm->bind_method<0>(type, "hex", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
return VAR(self.hex());
});
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
VoidP& self = _CAST(VoidP&, obj);
std::stringstream ss;
ss << "<void* at " << self.hex();
if(self.base_offset != 1) ss << ", base_offset=" << self.base_offset;
ss << ">";
return VAR(ss.str());
});
#define BIND_CMP(name, op) \
vm->bind##name(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){ \
if(!is_non_tagged_type(rhs, VoidP::_type(vm))) return vm->NotImplemented; \
return VAR(_CAST(VoidP&, lhs) op _CAST(VoidP&, rhs)); \
});
BIND_CMP(__eq__, ==)
BIND_CMP(__lt__, <)
BIND_CMP(__le__, <=)
BIND_CMP(__gt__, >)
BIND_CMP(__ge__, >=)
#undef BIND_CMP
vm->bind__hash__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
VoidP& self = _CAST(VoidP&, obj);
return reinterpret_cast<i64>(self.ptr);
});
vm->bind_method<1>(type, "set_base_offset", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
if(is_non_tagged_type(args[1], vm->tp_str)){
const Str& type = _CAST(Str&, args[1]);
self.base_offset = c99_sizeof(vm, type);
}else{
self.base_offset = CAST(int, args[1]);
}
return vm->None;
});
vm->bind_method<0>(type, "get_base_offset", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
return VAR(self.base_offset);
});
vm->bind_method<1>(type, "offset", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
i64 offset = CAST(i64, args[1]);
return VAR_T(VoidP, (char*)self.ptr + offset * self.base_offset);
});
vm->bind__add__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
VoidP& self = _CAST(VoidP&, lhs);
i64 offset = CAST(i64, rhs);
return VAR_T(VoidP, (char*)self.ptr + offset);
});
vm->bind__sub__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
VoidP& self = _CAST(VoidP&, lhs);
i64 offset = CAST(i64, rhs);
return VAR_T(VoidP, (char*)self.ptr - offset);
});
#define BIND_SETGET(T, name) \
vm->bind_method<0>(type, "read_" name, [](VM* vm, ArgsView args){ \
VoidP& self = _CAST(VoidP&, args[0]); \
return VAR(*(T*)self.ptr); \
}); \
vm->bind_method<1>(type, "write_" name, [](VM* vm, ArgsView args){ \
VoidP& self = _CAST(VoidP&, args[0]); \
*(T*)self.ptr = CAST(T, args[1]); \
return vm->None; \
});
BIND_SETGET(char, "char")
BIND_SETGET(unsigned char, "uchar")
BIND_SETGET(short, "short")
BIND_SETGET(unsigned short, "ushort")
BIND_SETGET(int, "int")
BIND_SETGET(unsigned int, "uint")
BIND_SETGET(long, "long")
BIND_SETGET(unsigned long, "ulong")
BIND_SETGET(long long, "longlong")
BIND_SETGET(unsigned long long, "ulonglong")
BIND_SETGET(float, "float")
BIND_SETGET(double, "double")
BIND_SETGET(bool, "bool")
BIND_SETGET(void*, "void_p")
vm->bind_method<1>(type, "read_bytes", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
i64 size = CAST(i64, args[1]);
std::vector<char> buffer(size);
memcpy(buffer.data(), self.ptr, size);
return VAR(Bytes(std::move(buffer)));
});
vm->bind_method<1>(type, "write_bytes", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
Bytes& bytes = CAST(Bytes&, args[1]);
memcpy(self.ptr, bytes.data(), bytes.size());
return vm->None;
});
#undef BIND_SETGET
}
void C99Struct::_register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_constructor<-1>(type, [](VM* vm, ArgsView args){
if(args.size() == 1+1){
if(is_int(args[1])){
int size = _CAST(int, args[1]);
return VAR_T(C99Struct, size);
}
if(is_non_tagged_type(args[1], vm->tp_str)){
const Str& s = _CAST(Str&, args[1]);
return VAR_T(C99Struct, (void*)s.data, s.size);
}
if(is_non_tagged_type(args[1], vm->tp_bytes)){
const Bytes& b = _CAST(Bytes&, args[1]);
return VAR_T(C99Struct, (void*)b.data(), b.size());
}
vm->TypeError("expected int, str or bytes");
return vm->None;
}
if(args.size() == 1+2){
void* p = CAST(void*, args[1]);
int size = CAST(int, args[2]);
return VAR_T(C99Struct, p, size);
}
vm->TypeError("expected 1 or 2 arguments");
return vm->None;
});
vm->bind_method<0>(type, "addr", [](VM* vm, ArgsView args){
C99Struct& self = _CAST(C99Struct&, args[0]);
return VAR_T(VoidP, self.p);
});
vm->bind_method<0>(type, "size", [](VM* vm, ArgsView args){
C99Struct& self = _CAST(C99Struct&, args[0]);
return VAR(self.size);
});
vm->bind_method<0>(type, "copy", [](VM* vm, ArgsView args){
const C99Struct& self = _CAST(C99Struct&, args[0]);
return VAR_T(C99Struct, self);
});
vm->bind_method<0>(type, "to_string", [](VM* vm, ArgsView args){
C99Struct& self = _CAST(C99Struct&, args[0]);
return VAR(Str(self.p, self.size));
});
vm->bind_method<0>(type, "to_bytes", [](VM* vm, ArgsView args){
C99Struct& self = _CAST(C99Struct&, args[0]);
std::vector<char> buffer(self.size);
memcpy(buffer.data(), self.p, self.size);
return VAR(Bytes(std::move(buffer)));
});
vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
C99Struct& self = _CAST(C99Struct&, lhs);
if(!is_non_tagged_type(rhs, C99Struct::_type(vm))) return vm->NotImplemented;
C99Struct& other = _CAST(C99Struct&, rhs);
bool ok = self.size == other.size && memcmp(self.p, other.p, self.size) == 0;
return VAR(ok);
});
#define BIND_SETGET(T, name) \
vm->bind(type, "read_" name "(self, offset=0)", [](VM* vm, ArgsView args){ \
C99Struct& self = _CAST(C99Struct&, args[0]); \
i64 offset = CAST(i64, args[1]); \
void* ptr = self.p + offset; \
return VAR(*(T*)ptr); \
}); \
vm->bind(type, "write_" name "(self, value, offset=0)", [](VM* vm, ArgsView args){ \
C99Struct& self = _CAST(C99Struct&, args[0]); \
i64 offset = CAST(i64, args[2]); \
void* ptr = self.p + offset; \
*(T*)ptr = CAST(T, args[1]); \
return vm->None; \
});
BIND_SETGET(char, "char")
BIND_SETGET(unsigned char, "uchar")
BIND_SETGET(short, "short")
BIND_SETGET(unsigned short, "ushort")
BIND_SETGET(int, "int")
BIND_SETGET(unsigned int, "uint")
BIND_SETGET(long, "long")
BIND_SETGET(unsigned long, "ulong")
BIND_SETGET(long long, "longlong")
BIND_SETGET(unsigned long long, "ulonglong")
BIND_SETGET(float, "float")
BIND_SETGET(double, "double")
BIND_SETGET(bool, "bool")
BIND_SETGET(void*, "void_p")
#undef BIND_SETGET
// patch VoidP
type = vm->_t(VoidP::_type(vm));
vm->bind_method<1>(type, "read_struct", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
const Str& type = CAST(Str&, args[1]);
int size = c99_sizeof(vm, type);
return VAR_T(C99Struct, self.ptr, size);
});
vm->bind_method<1>(type, "write_struct", [](VM* vm, ArgsView args){
VoidP& self = _CAST(VoidP&, args[0]);
C99Struct& other = CAST(C99Struct&, args[1]);
memcpy(self.ptr, other.p, other.size);
return vm->None;
});
}
void C99ReflType::_register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_notimplemented_constructor<C99ReflType>(type);
vm->bind_method<0>(type, "__call__", [](VM* vm, ArgsView args){
C99ReflType& self = _CAST(C99ReflType&, args[0]);
return VAR_T(C99Struct, nullptr, self.size);
});
vm->bind_method<0>(type, "__repr__", [](VM* vm, ArgsView args){
C99ReflType& self = _CAST(C99ReflType&, args[0]);
return VAR("<ctype '" + Str(self.name) + "'>");
});
vm->bind_method<0>(type, "name", [](VM* vm, ArgsView args){
C99ReflType& self = _CAST(C99ReflType&, args[0]);
return VAR(self.name);
});
vm->bind_method<0>(type, "size", [](VM* vm, ArgsView args){
C99ReflType& self = _CAST(C99ReflType&, args[0]);
return VAR(self.size);
});
vm->bind__getitem__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj, PyObject* key){
C99ReflType& self = _CAST(C99ReflType&, obj);
const Str& name = CAST(Str&, key);
auto it = std::lower_bound(self.fields.begin(), self.fields.end(), name.sv());
if(it == self.fields.end() || it->name != name.sv()){
vm->KeyError(key);
return vm->None;
}
return VAR(it->offset);
});
}
void add_module_c(VM* vm){
PyObject* mod = vm->new_module("c");
vm->bind_func<1>(mod, "malloc", [](VM* vm, ArgsView args){
i64 size = CAST(i64, args[0]);
return VAR(malloc(size));
});
vm->bind_func<1>(mod, "free", [](VM* vm, ArgsView args){
void* p = CAST(void*, args[0]);
free(p);
return vm->None;
});
vm->bind_func<1>(mod, "sizeof", [](VM* vm, ArgsView args){
const Str& type = CAST(Str&, args[0]);
i64 size = c99_sizeof(vm, type);
return VAR(size);
});
vm->bind_func<1>(mod, "refl", [](VM* vm, ArgsView args){
const Str& key = CAST(Str&, args[0]);
auto it = _refl_types.find(key.sv());
if(it == _refl_types.end()) vm->ValueError("reflection type not found");
const ReflType& rt = it->second;
return VAR_T(C99ReflType, rt);
});
vm->bind_func<3>(mod, "memset", [](VM* vm, ArgsView args){
void* p = CAST(void*, args[0]);
memset(p, CAST(int, args[1]), CAST(size_t, args[2]));
return vm->None;
});
vm->bind_func<3>(mod, "memcpy", [](VM* vm, ArgsView args){
void* dst = CAST(void*, args[0]);
void* src = CAST(void*, args[1]);
i64 size = CAST(i64, args[2]);
memcpy(dst, src, size);
return vm->None;
});
VoidP::register_class(vm, mod);
C99Struct::register_class(vm, mod);
C99ReflType::register_class(vm, mod);
mod->attr().set("NULL", VAR_T(VoidP, nullptr));
add_refl_type("char", sizeof(char), {});
add_refl_type("uchar", sizeof(unsigned char), {});
add_refl_type("short", sizeof(short), {});
add_refl_type("ushort", sizeof(unsigned short), {});
add_refl_type("int", sizeof(int), {});
add_refl_type("uint", sizeof(unsigned int), {});
add_refl_type("long", sizeof(long), {});
add_refl_type("ulong", sizeof(unsigned long), {});
add_refl_type("longlong", sizeof(long long), {});
add_refl_type("ulonglong", sizeof(unsigned long long), {});
add_refl_type("float", sizeof(float), {});
add_refl_type("double", sizeof(double), {});
add_refl_type("bool", sizeof(bool), {});
add_refl_type("void_p", sizeof(void*), {});
PyObject* void_p_t = mod->attr("void_p");
for(const char* t: {"char", "uchar", "short", "ushort", "int", "uint", "long", "ulong", "longlong", "ulonglong", "float", "double", "bool"}){
mod->attr().set(Str(t) + "_", VAR_T(C99ReflType, _refl_types[t]));
mod->attr().set(Str(t) + "_p", void_p_t);
}
}
int c99_sizeof(VM* vm, const Str& type){
auto it = _refl_types.find(type.sv());
if(it != _refl_types.end()) return it->second.size;
vm->ValueError("not a valid c99 type");
return 0;
}
} // namespace pkpy

1047
src/compiler.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,8 @@
namespace pkpy{
#if PK_MODULE_EASING
// https://easings.net/
static const double PI = 3.1415926545;
@ -250,4 +252,13 @@ void add_module_easing(VM* vm){
#undef EASE
}
#else
void add_module_easing(VM* vm){
PK_UNUSED(vm);
}
#endif
} // namespace pkpy

174
src/io.cpp Normal file
View File

@ -0,0 +1,174 @@
#include "pocketpy/io.h"
#include "pocketpy/common.h"
namespace pkpy{
Bytes _default_import_handler(const Str& name){
#if PK_ENABLE_OS
std::filesystem::path path(name.sv());
bool exists = std::filesystem::exists(path);
if(!exists) return Bytes();
std::string cname = name.str();
FILE* fp = fopen(cname.c_str(), "rb");
if(!fp) return Bytes();
fseek(fp, 0, SEEK_END);
std::vector<char> buffer(ftell(fp));
fseek(fp, 0, SEEK_SET);
size_t sz = fread(buffer.data(), 1, buffer.size(), fp);
PK_UNUSED(sz);
fclose(fp);
return Bytes(std::move(buffer));
#else
return Bytes();
#endif
};
#if PK_ENABLE_OS
void FileIO::_register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_constructor<3>(type, [](VM* vm, ArgsView args){
return VAR_T(FileIO,
vm, CAST(Str&, args[1]).str(), CAST(Str&, args[2]).str()
);
});
vm->bind_method<0>(type, "read", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]);
fseek(io.fp, 0, SEEK_END);
std::vector<char> buffer(ftell(io.fp));
fseek(io.fp, 0, SEEK_SET);
size_t sz = fread(buffer.data(), 1, buffer.size(), io.fp);
PK_UNUSED(sz);
Bytes b(std::move(buffer));
if(io.is_text()) return VAR(Str(b.str()));
return VAR(std::move(b));
});
vm->bind_method<1>(type, "write", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]);
if(io.is_text()){
Str& s = CAST(Str&, args[1]);
fwrite(s.data, 1, s.length(), io.fp);
}else{
Bytes& buffer = CAST(Bytes&, args[1]);
fwrite(buffer.data(), 1, buffer.size(), io.fp);
}
return vm->None;
});
vm->bind_method<0>(type, "close", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]);
io.close();
return vm->None;
});
vm->bind_method<0>(type, "__exit__", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]);
io.close();
return vm->None;
});
vm->bind_method<0>(type, "__enter__", PK_LAMBDA(vm->None));
}
FileIO::FileIO(VM* vm, std::string file, std::string mode): file(file), mode(mode) {
fp = fopen(file.c_str(), mode.c_str());
if(!fp) vm->IOError(strerror(errno));
}
void FileIO::close(){
if(fp == nullptr) return;
fclose(fp);
fp = nullptr;
}
#endif
void add_module_io(VM* vm){
#if PK_ENABLE_OS
PyObject* mod = vm->new_module("io");
FileIO::register_class(vm, mod);
vm->bind_builtin_func<2>("open", [](VM* vm, ArgsView args){
static StrName m_io("io");
static StrName m_FileIO("FileIO");
return vm->call(vm->_modules[m_io]->attr(m_FileIO), args[0], args[1]);
});
#endif
}
void add_module_os(VM* vm){
#if PK_ENABLE_OS
PyObject* mod = vm->new_module("os");
PyObject* path_obj = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
mod->attr().set("path", path_obj);
// Working directory is shared by all VMs!!
vm->bind_func<0>(mod, "getcwd", [](VM* vm, ArgsView args){
return VAR(std::filesystem::current_path().string());
});
vm->bind_func<1>(mod, "chdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
std::filesystem::current_path(path);
return vm->None;
});
vm->bind_func<1>(mod, "listdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
std::filesystem::directory_iterator di;
try{
di = std::filesystem::directory_iterator(path);
}catch(std::filesystem::filesystem_error& e){
std::string msg = e.what();
auto pos = msg.find_last_of(":");
if(pos != std::string::npos) msg = msg.substr(pos + 1);
vm->IOError(Str(msg).lstrip());
}
List ret;
for(auto& p: di) ret.push_back(VAR(p.path().filename().string()));
return VAR(ret);
});
vm->bind_func<1>(mod, "remove", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool ok = std::filesystem::remove(path);
if(!ok) vm->IOError("operation failed");
return vm->None;
});
vm->bind_func<1>(mod, "mkdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool ok = std::filesystem::create_directory(path);
if(!ok) vm->IOError("operation failed");
return vm->None;
});
vm->bind_func<1>(mod, "rmdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool ok = std::filesystem::remove(path);
if(!ok) vm->IOError("operation failed");
return vm->None;
});
vm->bind_func<-1>(path_obj, "join", [](VM* vm, ArgsView args){
std::filesystem::path path;
for(int i=0; i<args.size(); i++){
path /= CAST(Str&, args[i]).sv();
}
return VAR(path.string());
});
vm->bind_func<1>(path_obj, "exists", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool exists = std::filesystem::exists(path);
return VAR(exists);
});
vm->bind_func<1>(path_obj, "basename", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
return VAR(path.filename().string());
});
#endif
}
} // namespace pkpy

444
src/lexer.cpp Normal file
View File

@ -0,0 +1,444 @@
#include "pocketpy/lexer.h"
namespace pkpy{
bool Lexer::match_n_chars(int n, char c0){
const char* c = curr_char;
for(int i=0; i<n; i++){
if(*c == '\0') return false;
if(*c != c0) return false;
c++;
}
for(int i=0; i<n; i++) eatchar_include_newline();
return true;
}
bool Lexer::match_string(const char* s){
int s_len = strlen(s);
bool ok = strncmp(curr_char, s, s_len) == 0;
if(ok) for(int i=0; i<s_len; i++) eatchar_include_newline();
return ok;
}
int Lexer::eat_spaces(){
int count = 0;
while (true) {
switch (peekchar()) {
case ' ' : count+=1; break;
case '\t': count+=4; break;
default: return count;
}
eatchar();
}
}
bool Lexer::eat_indentation(){
if(brackets_level > 0) return true;
int spaces = eat_spaces();
if(peekchar() == '#') skip_line_comment();
if(peekchar() == '\0' || peekchar() == '\n') return true;
// https://docs.python.org/3/reference/lexical_analysis.html#indentation
if(spaces > indents.top()){
indents.push(spaces);
nexts.push_back(Token{TK("@indent"), token_start, 0, current_line, brackets_level});
} else if(spaces < indents.top()){
while(spaces < indents.top()){
indents.pop();
nexts.push_back(Token{TK("@dedent"), token_start, 0, current_line, brackets_level});
}
if(spaces != indents.top()){
return false;
}
}
return true;
}
char Lexer::eatchar() {
char c = peekchar();
if(c == '\n') throw std::runtime_error("eatchar() cannot consume a newline");
curr_char++;
return c;
}
char Lexer::eatchar_include_newline() {
char c = peekchar();
curr_char++;
if (c == '\n'){
current_line++;
src->line_starts.push_back(curr_char);
}
return c;
}
int Lexer::eat_name() {
curr_char--;
while(true){
unsigned char c = peekchar();
int u8bytes = utf8len(c, true);
if(u8bytes == 0) return 1;
if(u8bytes == 1){
if(isalpha(c) || c=='_' || isdigit(c)) {
curr_char++;
continue;
}else{
break;
}
}
// handle multibyte char
std::string u8str(curr_char, u8bytes);
if(u8str.size() != u8bytes) return 2;
uint32_t value = 0;
for(int k=0; k < u8bytes; k++){
uint8_t b = u8str[k];
if(k==0){
if(u8bytes == 2) value = (b & 0b00011111) << 6;
else if(u8bytes == 3) value = (b & 0b00001111) << 12;
else if(u8bytes == 4) value = (b & 0b00000111) << 18;
}else{
value |= (b & 0b00111111) << (6*(u8bytes-k-1));
}
}
if(is_unicode_Lo_char(value)) curr_char += u8bytes;
else break;
}
int length = (int)(curr_char - token_start);
if(length == 0) return 3;
std::string_view name(token_start, length);
if(src->mode == JSON_MODE){
if(name == "true"){
add_token(TK("True"));
} else if(name == "false"){
add_token(TK("False"));
} else if(name == "null"){
add_token(TK("None"));
} else {
return 4;
}
return 0;
}
if(kTokenKwMap.count(name)){
add_token(kTokenKwMap.at(name));
} else {
add_token(TK("@id"));
}
return 0;
}
void Lexer::skip_line_comment() {
char c;
while ((c = peekchar()) != '\0') {
if (c == '\n') return;
eatchar();
}
}
bool Lexer::matchchar(char c) {
if (peekchar() != c) return false;
eatchar_include_newline();
return true;
}
void Lexer::add_token(TokenIndex type, TokenValue value) {
switch(type){
case TK("{"): case TK("["): case TK("("): brackets_level++; break;
case TK(")"): case TK("]"): case TK("}"): brackets_level--; break;
}
auto token = Token{
type,
token_start,
(int)(curr_char - token_start),
current_line - ((type == TK("@eol")) ? 1 : 0),
brackets_level,
value
};
// handle "not in", "is not", "yield from"
if(!nexts.empty()){
auto& back = nexts.back();
if(back.type == TK("not") && type == TK("in")){
back.type = TK("not in");
return;
}
if(back.type == TK("is") && type == TK("not")){
back.type = TK("is not");
return;
}
if(back.type == TK("yield") && type == TK("from")){
back.type = TK("yield from");
return;
}
nexts.push_back(token);
}
}
void Lexer::add_token_2(char c, TokenIndex one, TokenIndex two) {
if (matchchar(c)) add_token(two);
else add_token(one);
}
Str Lexer::eat_string_until(char quote, bool raw) {
bool quote3 = match_n_chars(2, quote);
std::vector<char> buff;
while (true) {
char c = eatchar_include_newline();
if (c == quote){
if(quote3 && !match_n_chars(2, quote)){
buff.push_back(c);
continue;
}
break;
}
if (c == '\0'){
if(quote3 && src->mode == REPL_MODE){
throw NeedMoreLines(false);
}
SyntaxError("EOL while scanning string literal");
}
if (c == '\n'){
if(!quote3) SyntaxError("EOL while scanning string literal");
else{
buff.push_back(c);
continue;
}
}
if (!raw && c == '\\') {
switch (eatchar_include_newline()) {
case '"': buff.push_back('"'); break;
case '\'': buff.push_back('\''); break;
case '\\': buff.push_back('\\'); break;
case 'n': buff.push_back('\n'); break;
case 'r': buff.push_back('\r'); break;
case 't': buff.push_back('\t'); break;
case 'x': {
char hex[3] = {eatchar(), eatchar(), '\0'};
size_t parsed;
char code;
try{
code = (char)Number::stoi(hex, &parsed, 16);
}catch(std::invalid_argument&){
SyntaxError("invalid hex char");
}
if (parsed != 2) SyntaxError("invalid hex char");
buff.push_back(code);
} break;
default: SyntaxError("invalid escape char");
}
} else {
buff.push_back(c);
}
}
return Str(buff.data(), buff.size());
}
void Lexer::eat_string(char quote, StringType type) {
Str s = eat_string_until(quote, type == RAW_STRING);
if(type == F_STRING){
add_token(TK("@fstr"), s);
}else{
add_token(TK("@str"), s);
}
}
void Lexer::eat_number() {
static const std::regex pattern("^(0x)?[0-9a-fA-F]+(\\.[0-9]+)?(L)?");
std::smatch m;
const char* i = token_start;
while(*i != '\n' && *i != '\0') i++;
std::string s = std::string(token_start, i);
bool ok = std::regex_search(s, m, pattern);
PK_ASSERT(ok);
// here is m.length()-1, since the first char was eaten by lex_token()
for(int j=0; j<m.length()-1; j++) eatchar();
if(m[3].matched){
add_token(TK("@long"));
return;
}
try{
int base = 10;
size_t size;
if (m[1].matched) base = 16;
if (m[2].matched) {
if(base == 16) SyntaxError("hex literal should not contain a dot");
add_token(TK("@num"), Number::stof(m[0], &size));
} else {
add_token(TK("@num"), Number::stoi(m[0], &size, base));
}
PK_ASSERT((int)size == (int)m.length());
}catch(std::exception& e){
PK_UNUSED(e);
SyntaxError("invalid number literal");
}
}
bool Lexer::lex_one_token() {
while (peekchar() != '\0') {
token_start = curr_char;
char c = eatchar_include_newline();
switch (c) {
case '\'': case '"': eat_string(c, NORMAL_STRING); return true;
case '#': skip_line_comment(); break;
case '{': add_token(TK("{")); return true;
case '}': add_token(TK("}")); return true;
case ',': add_token(TK(",")); return true;
case ':': add_token(TK(":")); return true;
case ';': add_token(TK(";")); return true;
case '(': add_token(TK("(")); return true;
case ')': add_token(TK(")")); return true;
case '[': add_token(TK("[")); return true;
case ']': add_token(TK("]")); return true;
case '@': add_token(TK("@")); return true;
case '$': {
for(int i=TK("$goto"); i<=TK("$label"); i++){
// +1 to skip the '$'
if(match_string(TK_STR(i) + 1)){
add_token((TokenIndex)i);
return true;
}
}
SyntaxError("invalid special token");
} return false;
case '%': add_token_2('=', TK("%"), TK("%=")); return true;
case '&': add_token_2('=', TK("&"), TK("&=")); return true;
case '|': add_token_2('=', TK("|"), TK("|=")); return true;
case '^': add_token_2('=', TK("^"), TK("^=")); return true;
case '?': add_token(TK("?")); return true;
case '.': {
if(matchchar('.')) {
if(matchchar('.')) {
add_token(TK("..."));
} else {
SyntaxError("invalid token '..'");
}
} else {
add_token(TK("."));
}
return true;
}
case '=': add_token_2('=', TK("="), TK("==")); return true;
case '+':
if(matchchar('+')){
add_token(TK("++"));
}else{
add_token_2('=', TK("+"), TK("+="));
}
return true;
case '>': {
if(matchchar('=')) add_token(TK(">="));
else if(matchchar('>')) add_token_2('=', TK(">>"), TK(">>="));
else add_token(TK(">"));
return true;
}
case '<': {
if(matchchar('=')) add_token(TK("<="));
else if(matchchar('<')) add_token_2('=', TK("<<"), TK("<<="));
else add_token(TK("<"));
return true;
}
case '-': {
if(matchchar('-')){
add_token(TK("--"));
}else{
if(matchchar('=')) add_token(TK("-="));
else if(matchchar('>')) add_token(TK("->"));
else add_token(TK("-"));
}
return true;
}
case '!':
if(matchchar('=')) add_token(TK("!="));
else SyntaxError("expected '=' after '!'");
break;
case '*':
if (matchchar('*')) {
add_token(TK("**")); // '**'
} else {
add_token_2('=', TK("*"), TK("*="));
}
return true;
case '/':
if(matchchar('/')) {
add_token_2('=', TK("//"), TK("//="));
} else {
add_token_2('=', TK("/"), TK("/="));
}
return true;
case ' ': case '\t': eat_spaces(); break;
case '\n': {
add_token(TK("@eol"));
if(!eat_indentation()) IndentationError("unindent does not match any outer indentation level");
return true;
}
default: {
if(c == 'f'){
if(matchchar('\'')) {eat_string('\'', F_STRING); return true;}
if(matchchar('"')) {eat_string('"', F_STRING); return true;}
}else if(c == 'r'){
if(matchchar('\'')) {eat_string('\'', RAW_STRING); return true;}
if(matchchar('"')) {eat_string('"', RAW_STRING); return true;}
}
if (c >= '0' && c <= '9') {
eat_number();
return true;
}
switch (eat_name())
{
case 0: break;
case 1: SyntaxError("invalid char: " + std::string(1, c)); break;
case 2: SyntaxError("invalid utf8 sequence: " + std::string(1, c)); break;
case 3: SyntaxError("@id contains invalid char"); break;
case 4: SyntaxError("invalid JSON token"); break;
default: FATAL_ERROR();
}
return true;
}
}
}
token_start = curr_char;
while(indents.size() > 1){
indents.pop();
add_token(TK("@dedent"));
return true;
}
add_token(TK("@eof"));
return false;
}
void Lexer::throw_err(Str type, Str msg){
int lineno = current_line;
const char* cursor = curr_char;
if(peekchar() == '\n'){
lineno--;
cursor--;
}
throw_err(type, msg, lineno, cursor);
}
void Lexer::throw_err(Str type, Str msg, int lineno, const char* cursor){
auto e = Exception(type, msg);
e.st_push(src->snapshot(lineno, cursor));
throw e;
}
Lexer::Lexer(shared_ptr<SourceData> src) {
this->src = src;
this->token_start = src->source.c_str();
this->curr_char = src->source.c_str();
this->nexts.push_back(Token{TK("@sof"), token_start, 0, current_line, brackets_level});
this->indents.push(0);
}
std::vector<Token> Lexer::run() {
if(used) FATAL_ERROR();
used = true;
while (lex_one_token());
return std::move(nexts);
}
} // namespace pkpy

View File

@ -1,4 +1,4 @@
#include "pocketpy/pocketpy.h"
#include "pocketpy/linalg.h"
namespace pkpy{

1
src2/lib.cpp Normal file
View File

@ -0,0 +1 @@
#include "pocketpy/pocketpy.h"