mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
...
This commit is contained in:
parent
f3ac21ccc2
commit
656d1d7291
1
.gitignore
vendored
1
.gitignore
vendored
@ -27,3 +27,4 @@ main.obj
|
||||
pocketpy.exp
|
||||
pocketpy.lib
|
||||
APPS
|
||||
build
|
43
CMakeLists.txt
Normal file
43
CMakeLists.txt
Normal 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()
|
@ -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")
|
||||
|
||||
|
||||
|
@ -1,9 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if PK_MODULE_BASE64
|
||||
|
||||
#include "cffi.h"
|
||||
|
||||
namespace pkpy {
|
||||
@ -11,10 +7,3 @@ namespace pkpy {
|
||||
void add_module_base64(VM* vm);
|
||||
|
||||
} // namespace pkpy
|
||||
|
||||
|
||||
#else
|
||||
|
||||
ADD_MODULE_PLACEHOLDER(base64)
|
||||
|
||||
#endif
|
4
include/pocketpy/ceval.h
Normal file
4
include/pocketpy/ceval.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include "vm.h"
|
||||
// dummy header for ceval.cpp
|
@ -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
|
@ -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
@ -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
|
@ -1,9 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if PK_MODULE_EASING
|
||||
|
||||
#include "cffi.h"
|
||||
|
||||
namespace pkpy{
|
||||
@ -11,9 +7,3 @@ namespace pkpy{
|
||||
void add_module_easing(VM* vm);
|
||||
|
||||
} // namespace pkpy
|
||||
|
||||
#else
|
||||
|
||||
ADD_MODULE_PLACEHOLDER(easing)
|
||||
|
||||
#endif
|
@ -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]);
|
||||
});
|
||||
}
|
||||
|
||||
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
|
@ -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
|
||||
|
@ -1,9 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if PK_MODULE_LINALG
|
||||
|
||||
#include "cffi.h"
|
||||
|
||||
namespace pkpy{
|
||||
@ -379,9 +375,3 @@ inline void add_module_linalg(VM* vm){
|
||||
static_assert(sizeof(Py_<PyMat3x3>) <= 64);
|
||||
|
||||
} // namespace pkpy
|
||||
|
||||
#else
|
||||
|
||||
ADD_MODULE_PLACEHOLDER(linalg)
|
||||
|
||||
#endif
|
@ -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
348
src/cffi.cpp
Normal 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
1047
src/compiler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
174
src/io.cpp
Normal 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
444
src/lexer.cpp
Normal 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
|
@ -1,4 +1,4 @@
|
||||
#include "pocketpy/pocketpy.h"
|
||||
#include "pocketpy/linalg.h"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
|
1
src2/lib.cpp
Normal file
1
src2/lib.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "pocketpy/pocketpy.h"
|
Loading…
x
Reference in New Issue
Block a user