mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-21 12:00:18 +00:00
Merge branch 'v2.0-c11' into v2.0
This commit is contained in:
commit
e239c216b7
75
.github/workflows/main.yml
vendored
75
.github/workflows/main.yml
vendored
@ -12,21 +12,17 @@ on:
|
||||
- 'web/**'
|
||||
- '**.md'
|
||||
jobs:
|
||||
build_win32_amalgamated:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: Compile
|
||||
shell: powershell
|
||||
run: |
|
||||
python amalgamate.py
|
||||
cd amalgamated
|
||||
cl.exe /std:c++17 /EHsc /utf-8 /Ox /I. /DPK_ENABLE_OS=1 main.cpp /link /out:pkpy.exe
|
||||
# - uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: amalgamated
|
||||
# path: amalgamated/pkpy.exe
|
||||
# build_win32_amalgamated:
|
||||
# runs-on: windows-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# - uses: ilammy/msvc-dev-cmd@v1
|
||||
# - name: Compile
|
||||
# shell: powershell
|
||||
# run: |
|
||||
# python amalgamate.py
|
||||
# cd amalgamated
|
||||
# cl.exe /std:c++17 /EHsc /utf-8 /Ox /I. /DPK_ENABLE_OS=1 main.cpp /link /out:pkpy.exe
|
||||
build_win32:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
@ -58,18 +54,19 @@ jobs:
|
||||
platform: x64
|
||||
- name: Install libc++
|
||||
run: sudo apt-get install -y libc++-15-dev libc++1-15 libc++abi-15-dev libc++abi1-15 libclang-rt-15-dev
|
||||
- name: Unit Test with Coverage
|
||||
run: bash run_tests.sh
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
directory: .coverage
|
||||
if: github.ref == 'refs/heads/main'
|
||||
- name: Compile
|
||||
# - name: Unit Test with Coverage
|
||||
# run: bash run_tests.sh
|
||||
# - name: Upload coverage reports to Codecov
|
||||
# uses: codecov/codecov-action@v4
|
||||
# with:
|
||||
# token: ${{ secrets.CODECOV_TOKEN }}
|
||||
# directory: .coverage
|
||||
# if: github.ref == 'refs/heads/main'
|
||||
- name: Compile and Test
|
||||
run: |
|
||||
mkdir -p output/x86_64
|
||||
python cmake_build.py
|
||||
python scripts/run_tests.py
|
||||
cp main output/x86_64
|
||||
cp libpocketpy.so output/x86_64
|
||||
env:
|
||||
@ -109,16 +106,16 @@ jobs:
|
||||
python scripts/run_tests.py
|
||||
- name: Benchmark
|
||||
run: python scripts/run_tests.py benchmark
|
||||
- run: |
|
||||
python amalgamate.py
|
||||
cd plugins/macos/pocketpy
|
||||
mkdir output
|
||||
xcodebuild clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
|
||||
cp -r build/Release/pocketpy.bundle output
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos
|
||||
path: plugins/macos/pocketpy/output
|
||||
# - run: |
|
||||
# python amalgamate.py
|
||||
# cd plugins/macos/pocketpy
|
||||
# mkdir output
|
||||
# xcodebuild clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
|
||||
# cp -r build/Release/pocketpy.bundle output
|
||||
# - uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: macos
|
||||
# path: plugins/macos/pocketpy/output
|
||||
build_android:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@ -182,11 +179,11 @@ jobs:
|
||||
name: linux
|
||||
path: $GITHUB_WORKSPACE/output/linux
|
||||
|
||||
- name: "Merge darwin"
|
||||
uses: actions/download-artifact@v4.1.7
|
||||
with:
|
||||
name: macos
|
||||
path: $GITHUB_WORKSPACE/output/macos
|
||||
# - name: "Merge darwin"
|
||||
# uses: actions/download-artifact@v4.1.7
|
||||
# with:
|
||||
# name: macos
|
||||
# path: $GITHUB_WORKSPACE/output/macos
|
||||
|
||||
- name: "Merge android"
|
||||
uses: actions/download-artifact@v4.1.7
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -31,3 +31,6 @@ libpocketpy.dylib.dSYM/
|
||||
main.dSYM/
|
||||
|
||||
docs/references.md
|
||||
|
||||
.xmake
|
||||
.vs
|
||||
|
@ -2,23 +2,40 @@ cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(pocketpy)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /utf-8 /Ox /jumptablerdata /GS-")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /utf-8 /jumptablerdata /GS-")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /utf-8 /jumptablerdata /GS-")
|
||||
add_compile_options(/wd4267 /wd4244)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Ox")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox")
|
||||
endif()
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -frtti -O2")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -frtti")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
|
||||
endif()
|
||||
|
||||
# disable -Wshorten-64-to-32 for apple
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-shorten-64-to-32")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-shorten-64-to-32")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
file(GLOB_RECURSE POCKETPY_SRC ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp)
|
||||
file(GLOB_RECURSE POCKETPY_SRC_CPP ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp)
|
||||
file(GLOB_RECURSE POCKETPY_SRC_C ${CMAKE_CURRENT_LIST_DIR}/src/*.c)
|
||||
set(POCKETPY_SRC ${POCKETPY_SRC_CPP} ${POCKETPY_SRC_C})
|
||||
|
||||
option(PK_USE_CJSON "" OFF)
|
||||
if(PK_USE_CJSON)
|
||||
|
4
build.sh
4
build.sh
@ -18,7 +18,9 @@ if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SRC=$(find src/ -name "*.cpp")
|
||||
SRC_C=$(find src/ -name "*.c")
|
||||
SRC_CPP=$(find src/ -name "*.cpp")
|
||||
SRC="$SRC_C $SRC_CPP"
|
||||
|
||||
echo "> Compiling and linking source files... "
|
||||
|
||||
|
19
build_g.sh
19
build_g.sh
@ -1,7 +1,20 @@
|
||||
set -e
|
||||
|
||||
python prebuild.py
|
||||
|
||||
SRC=$(find src/ -name "*.cpp")
|
||||
SRC_C=$(find src/ -name "*.c")
|
||||
SRC_CPP=$(find src/ -name "*.cpp")
|
||||
|
||||
FLAGS="-std=c++17 -O0 -stdlib=libc++ -Iinclude -frtti -Wfatal-errors -g -DDEBUG"
|
||||
COMMON_FLAGS="-Iinclude -O0 -Wfatal-errors -g -DDEBUG -DPK_ENABLE_OS=1"
|
||||
|
||||
clang++ $FLAGS -o main src2/main.cpp $SRC
|
||||
FLAGS_C="-std=c11 $COMMON_FLAGS"
|
||||
FLAGS_CPP="-std=c++17 -stdlib=libc++ -frtti $COMMON_FLAGS"
|
||||
|
||||
echo "Compiling C files..."
|
||||
clang $FLAGS_C -c $SRC_C
|
||||
ar rcs libpocketpy_c.a *.o
|
||||
rm *.o
|
||||
|
||||
echo "Compiling C++ files..."
|
||||
clang++ $FLAGS_CPP -o main src2/main.cpp $SRC_CPP libpocketpy_c.a
|
||||
rm libpocketpy_c.a
|
||||
|
@ -3,5 +3,8 @@ python prebuild.py
|
||||
rm -rf web/lib
|
||||
mkdir web/lib
|
||||
|
||||
SRC=$(find src/ -name "*.cpp")
|
||||
SRC_C=$(find src/ -name "*.c")
|
||||
SRC_CPP=$(find src/ -name "*.cpp")
|
||||
SRC="$SRC_C $SRC_CPP"
|
||||
|
||||
em++ $SRC -Iinclude/ -fexceptions -frtti -s -Os -sEXPORTED_FUNCTIONS=_pkpy_new_repl,_pkpy_repl_input,_pkpy_new_vm -sEXPORTED_RUNTIME_METHODS=ccall -o web/lib/pocketpy.js
|
||||
|
@ -1,10 +1,8 @@
|
||||
-xc++
|
||||
|
||||
-Wall
|
||||
-W*
|
||||
|
||||
-std=c++17
|
||||
-stdlib=libc++
|
||||
-std=c++17
|
||||
|
||||
-Iinclude/
|
||||
-I3rd/cjson/include/
|
||||
|
@ -92,13 +92,6 @@ vm->bind(obj, "f() -> int", [](VM* vm, ArgsView args){
|
||||
}, x); // capture x
|
||||
```
|
||||
|
||||
The 3rd way is to change the macro `PK_ENABLE_STD_FUNCTION` in `config.h`:
|
||||
```cpp
|
||||
#define PK_ENABLE_STD_FUNCTION 0 // => 1
|
||||
```
|
||||
|
||||
Then you can use standard capture list in lambda.
|
||||
|
||||
## Bind a class or struct
|
||||
|
||||
Assume you have a struct `Point` declared as follows.
|
||||
|
27
include/pocketpy/common/_generated.h
Normal file
27
include/pocketpy/common/_generated.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
// generated by prebuild.py
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const char kPythonLibs__enum[];
|
||||
extern const char kPythonLibs__long[];
|
||||
extern const char kPythonLibs__set[];
|
||||
extern const char kPythonLibs_bisect[];
|
||||
extern const char kPythonLibs_builtins[];
|
||||
extern const char kPythonLibs_cmath[];
|
||||
extern const char kPythonLibs_collections[];
|
||||
extern const char kPythonLibs_colorsys[];
|
||||
extern const char kPythonLibs_datetime[];
|
||||
extern const char kPythonLibs_functools[];
|
||||
extern const char kPythonLibs_heapq[];
|
||||
extern const char kPythonLibs_itertools[];
|
||||
extern const char kPythonLibs_operator[];
|
||||
extern const char kPythonLibs_pickle[];
|
||||
extern const char kPythonLibs_this[];
|
||||
extern const char kPythonLibs_typing[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
// generated by prebuild.py
|
||||
|
||||
namespace pkpy{
|
||||
extern const char kPythonLibs__enum[];
|
||||
extern const char kPythonLibs__long[];
|
||||
extern const char kPythonLibs__set[];
|
||||
extern const char kPythonLibs_bisect[];
|
||||
extern const char kPythonLibs_builtins[];
|
||||
extern const char kPythonLibs_cmath[];
|
||||
extern const char kPythonLibs_collections[];
|
||||
extern const char kPythonLibs_colorsys[];
|
||||
extern const char kPythonLibs_datetime[];
|
||||
extern const char kPythonLibs_functools[];
|
||||
extern const char kPythonLibs_heapq[];
|
||||
extern const char kPythonLibs_itertools[];
|
||||
extern const char kPythonLibs_operator[];
|
||||
extern const char kPythonLibs_pickle[];
|
||||
extern const char kPythonLibs_this[];
|
||||
extern const char kPythonLibs_typing[];
|
||||
} // namespace pkpy
|
18
include/pocketpy/common/algorithm.h
Normal file
18
include/pocketpy/common/algorithm.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void *c11__lower_bound(const void *key, const void *ptr, int count, int size,
|
||||
bool (*less)(const void *, const void *));
|
||||
|
||||
int *c11__lower_bound_int(int key, const int *ptr, int count);
|
||||
double *c11__lower_bound_double(double key, const double *ptr, int count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
#endif
|
33
include/pocketpy/common/any.h
Normal file
33
include/pocketpy/common/any.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct c11_userdata{
|
||||
void* _0;
|
||||
void* _1;
|
||||
} c11_userdata;
|
||||
|
||||
void c11_userdata__ctor(c11_userdata* self, void* ptr, int size);
|
||||
#define c11_userdata__as(T, self) (*( (T*)(self) ))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
namespace pkpy{
|
||||
struct any: c11_userdata{
|
||||
template<typename T>
|
||||
any(T value){
|
||||
c11_userdata__ctor(this, &value, sizeof(T));
|
||||
}
|
||||
|
||||
any(){ }
|
||||
|
||||
template<typename T>
|
||||
T as(){
|
||||
return c11_userdata__as(T, this);
|
||||
}
|
||||
};
|
||||
} // namespace pkpy
|
||||
#endif
|
@ -1,113 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/common/traits.hpp"
|
||||
|
||||
#include <typeindex>
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool is_any_sso_v = is_pod_v<T> && sizeof(T) <= sizeof(void*);
|
||||
|
||||
struct any {
|
||||
struct vtable {
|
||||
const std::type_index type;
|
||||
void (*deleter)(void*);
|
||||
|
||||
template <typename T>
|
||||
inline static vtable* get() {
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
if constexpr(is_any_sso_v<T>) {
|
||||
static vtable vt{typeid(T), nullptr};
|
||||
return &vt;
|
||||
} else {
|
||||
static vtable vt{typeid(T), [](void* ptr) {
|
||||
delete static_cast<T*>(ptr);
|
||||
}};
|
||||
return &vt;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void* data;
|
||||
vtable* _vt;
|
||||
|
||||
any() : data(nullptr), _vt(nullptr) {}
|
||||
|
||||
explicit operator bool () const { return _vt != nullptr; }
|
||||
|
||||
template <typename T>
|
||||
any(T&& value) {
|
||||
using U = std::decay_t<T>;
|
||||
static_assert(!std::is_same_v<U, any>, "any(const any&) is deleted");
|
||||
static_assert(sizeof(U) == sizeof(T));
|
||||
if constexpr(is_any_sso_v<U>) {
|
||||
std::memcpy(&data, &value, sizeof(U));
|
||||
} else {
|
||||
data = new U(std::forward<T>(value));
|
||||
}
|
||||
_vt = vtable::get<U>();
|
||||
}
|
||||
|
||||
any(any&& other) noexcept;
|
||||
any& operator= (any&& other) noexcept;
|
||||
|
||||
const std::type_index type_id() const { return _vt ? _vt->type : typeid(void); }
|
||||
|
||||
any(const any& other) = delete;
|
||||
any& operator= (const any& other) = delete;
|
||||
|
||||
~any() {
|
||||
if(_vt && _vt->deleter) _vt->deleter(data);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& _cast() const noexcept {
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
if constexpr(is_any_sso_v<T>) {
|
||||
return *((T*)(&data));
|
||||
} else {
|
||||
return *(static_cast<T*>(data));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& cast() const {
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
if(type_id() != typeid(T)) __bad_any_cast(typeid(T), type_id());
|
||||
return _cast<T>();
|
||||
}
|
||||
|
||||
static void __bad_any_cast(const std::type_index expected, const std::type_index actual);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct function;
|
||||
|
||||
template <typename Ret, typename... Params>
|
||||
struct function<Ret(Params...)> {
|
||||
any _impl;
|
||||
Ret (*_wrapper)(const any&, Params...);
|
||||
|
||||
function() : _impl(), _wrapper(nullptr) {}
|
||||
|
||||
explicit operator bool () const { return _wrapper != nullptr; }
|
||||
|
||||
template <typename F>
|
||||
function(F&& f) : _impl(std::forward<F>(f)) {
|
||||
_wrapper = [](const any& impl, Params... params) -> Ret {
|
||||
return impl._cast<std::decay_t<F>>()(std::forward<Params>(params)...);
|
||||
};
|
||||
}
|
||||
|
||||
Ret operator() (Params... params) const {
|
||||
assert(_wrapper);
|
||||
return _wrapper(_impl, std::forward<Params>(params)...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
@ -24,13 +24,6 @@
|
||||
#define PK_GC_MIN_THRESHOLD 16384
|
||||
#endif
|
||||
|
||||
// Whether to use `pkpy::function<>` to do bindings or not
|
||||
// By default, functions to be binded must be a C function pointer without capture
|
||||
// However, someone thinks it's not convenient.
|
||||
// By setting this to 1, capturing lambdas can be binded,
|
||||
// but it's slower and may cause "code bloat", it also needs more time to compile.
|
||||
#define PK_ENABLE_STD_FUNCTION 0
|
||||
|
||||
/*************** debug settings ***************/
|
||||
// Do not edit the following settings unless you know what you are doing
|
||||
#define PK_DEBUG_CEVAL_STEP 0
|
||||
|
86
include/pocketpy/common/sstream.h
Normal file
86
include/pocketpy/common/sstream.h
Normal file
@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/common/vector.h"
|
||||
#include "pocketpy/common/str.h"
|
||||
#include "pocketpy/common/utils.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct pkpy_SStream {
|
||||
c11_vector data;
|
||||
} pkpy_SStream;
|
||||
|
||||
typedef struct pkpy_AnyStr {
|
||||
int type;
|
||||
union {
|
||||
int _int;
|
||||
int64_t _i64;
|
||||
float _float;
|
||||
double _double;
|
||||
char _char;
|
||||
const pkpy_Str* _str;
|
||||
c11_string _sv;
|
||||
const char* _cstr;
|
||||
void* _ptr;
|
||||
};
|
||||
} pkpy_AnyStr;
|
||||
|
||||
PK_INLINE pkpy_AnyStr pkpy_AnyStr__int(int x) { pkpy_AnyStr s; s.type = 1; s._int = x; return s; }
|
||||
PK_INLINE pkpy_AnyStr pkpy_AnyStr__i64(int64_t x) { pkpy_AnyStr s; s.type = 2; s._i64 = x; return s; }
|
||||
PK_INLINE pkpy_AnyStr pkpy_AnyStr__float(float x) { pkpy_AnyStr s; s.type = 3; s._float = x; return s; }
|
||||
PK_INLINE pkpy_AnyStr pkpy_AnyStr__double(double x) { pkpy_AnyStr s; s.type = 4; s._double = x; return s; }
|
||||
PK_INLINE pkpy_AnyStr pkpy_AnyStr__char(char x) { pkpy_AnyStr s; s.type = 5; s._char = x; return s; }
|
||||
PK_INLINE pkpy_AnyStr pkpy_AnyStr__str(const pkpy_Str* x) { pkpy_AnyStr s; s.type = 6; s._str = x; return s; }
|
||||
PK_INLINE pkpy_AnyStr pkpy_AnyStr__sv(c11_string x) { pkpy_AnyStr s; s.type = 7; s._sv = x; return s; }
|
||||
PK_INLINE pkpy_AnyStr pkpy_AnyStr__cstr(const char* x) { pkpy_AnyStr s; s.type = 8; s._cstr = x; return s; }
|
||||
PK_INLINE pkpy_AnyStr pkpy_AnyStr__ptr(void* x) { pkpy_AnyStr s; s.type = 9; s._ptr = x; return s; }
|
||||
|
||||
void pkpy_SStream__ctor(pkpy_SStream* self);
|
||||
void pkpy_SStream__ctor2(pkpy_SStream* self, int capacity);
|
||||
void pkpy_SStream__dtor(pkpy_SStream* self);
|
||||
|
||||
void pkpy_SStream__write_int(pkpy_SStream* self, int);
|
||||
void pkpy_SStream__write_i64(pkpy_SStream* self, int64_t);
|
||||
void pkpy_SStream__write_float(pkpy_SStream* self, float, int precision);
|
||||
void pkpy_SStream__write_double(pkpy_SStream* self, double, int precision);
|
||||
void pkpy_SStream__write_char(pkpy_SStream* self, char);
|
||||
void pkpy_SStream__write_Str(pkpy_SStream* self, const pkpy_Str*);
|
||||
void pkpy_SStream__write_sv(pkpy_SStream* self, c11_string);
|
||||
void pkpy_SStream__write_cstr(pkpy_SStream* self, const char*);
|
||||
void pkpy_SStream__write_cstrn(pkpy_SStream* self, const char*, int);
|
||||
void pkpy_SStream__write_hex(pkpy_SStream* self, unsigned char, bool non_zero);
|
||||
void pkpy_SStream__write_ptr(pkpy_SStream* self, void*);
|
||||
void pkpy_SStream__write_any(pkpy_SStream* self, const char* fmt, const pkpy_AnyStr* args, int n);
|
||||
|
||||
// Submit the stream and return the final string. The stream becomes invalid after this call
|
||||
pkpy_Str pkpy_SStream__submit(pkpy_SStream* self);
|
||||
|
||||
#define pkpy__anystr(x) _Generic((x), \
|
||||
int: pkpy_AnyStr__int, \
|
||||
int64_t: pkpy_AnyStr__i64, \
|
||||
float: pkpy_AnyStr__float, \
|
||||
double: pkpy_AnyStr__double, \
|
||||
char: pkpy_AnyStr__char, \
|
||||
const pkpy_Str*: pkpy_AnyStr__str, \
|
||||
c11_string: pkpy_AnyStr__sv, \
|
||||
const char*: pkpy_AnyStr__cstr, \
|
||||
void*: pkpy_AnyStr__ptr \
|
||||
)(x)
|
||||
|
||||
#define pkpy__anystr_list_1(a) (pkpy_AnyStr[]){pkpy__anystr(a)}, 1
|
||||
#define pkpy__anystr_list_2(a, b) (pkpy_AnyStr[]){pkpy__anystr(a), pkpy__anystr(b)}, 2
|
||||
#define pkpy__anystr_list_3(a, b, c) (pkpy_AnyStr[]){pkpy__anystr(a), pkpy__anystr(b), pkpy__anystr(c)}, 3
|
||||
#define pkpy__anystr_list_4(a, b, c, d) (pkpy_AnyStr[]){pkpy__anystr(a), pkpy__anystr(b), pkpy__anystr(c), pkpy__anystr(d)}, 4
|
||||
|
||||
#define pkpy__anystr_list_dispatcher(...) PK_NARGS_SEQ(__VA_ARGS__, pkpy__anystr_list_4, pkpy__anystr_list_3, pkpy__anystr_list_2, pkpy__anystr_list_1, 0)
|
||||
#define pkpy__anystr_list(...) pkpy__anystr_list_dispatcher(__VA_ARGS__)(__VA_ARGS__)
|
||||
|
||||
#define pkpy_SStream__write(self, fmt, ...) pkpy_SStream__write_any(self, fmt, pkpy__anystr_list(__VA_ARGS__))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
64
include/pocketpy/common/str.h
Normal file
64
include/pocketpy/common/str.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "pocketpy/common/vector.h"
|
||||
#include "pocketpy/common/utils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* string_view */
|
||||
typedef struct c11_string{
|
||||
const char* data;
|
||||
int size;
|
||||
} c11_string;
|
||||
|
||||
typedef struct pkpy_Str{
|
||||
int size;
|
||||
bool is_ascii;
|
||||
bool is_sso;
|
||||
union{
|
||||
char* _ptr;
|
||||
char _inlined[16];
|
||||
};
|
||||
} pkpy_Str;
|
||||
|
||||
PK_INLINE const char* pkpy_Str__data(const pkpy_Str* self){
|
||||
return self->is_sso ? self->_inlined : self->_ptr;
|
||||
}
|
||||
|
||||
void pkpy_Str__ctor(pkpy_Str* self, const char* data);
|
||||
void pkpy_Str__ctor2(pkpy_Str* self, const char* data, int size);
|
||||
void pkpy_Str__dtor(pkpy_Str* self);
|
||||
pkpy_Str pkpy_Str__copy(const pkpy_Str* self);
|
||||
pkpy_Str pkpy_Str__concat(const pkpy_Str* self, const pkpy_Str* other);
|
||||
pkpy_Str pkpy_Str__concat2(const pkpy_Str* self, const char* other, int size);
|
||||
pkpy_Str pkpy_Str__slice(const pkpy_Str* self, int start);
|
||||
pkpy_Str pkpy_Str__slice2(const pkpy_Str* self, int start, int stop);
|
||||
pkpy_Str pkpy_Str__lower(const pkpy_Str* self);
|
||||
pkpy_Str pkpy_Str__upper(const pkpy_Str* self);
|
||||
pkpy_Str pkpy_Str__escape(const pkpy_Str* self, char quote);
|
||||
pkpy_Str pkpy_Str__strip(const pkpy_Str* self, bool left, bool right);
|
||||
pkpy_Str pkpy_Str__strip2(const pkpy_Str* self, bool left, bool right, const pkpy_Str* chars);
|
||||
pkpy_Str pkpy_Str__replace(const pkpy_Str* self, char old, char new_);
|
||||
pkpy_Str pkpy_Str__replace2(const pkpy_Str* self, const pkpy_Str* old, const pkpy_Str* new_);
|
||||
pkpy_Str pkpy_Str__u8_getitem(const pkpy_Str* self, int i);
|
||||
pkpy_Str pkpy_Str__u8_slice(const pkpy_Str* self, int start, int stop, int step);
|
||||
int pkpy_Str__u8_length(const pkpy_Str* self);
|
||||
int pkpy_Str__cmp(const pkpy_Str* self, const pkpy_Str* other);
|
||||
int pkpy_Str__cmp2(const pkpy_Str* self, const char* other, int size);
|
||||
int pkpy_Str__unicode_index_to_byte(const pkpy_Str* self, int i);
|
||||
int pkpy_Str__byte_index_to_unicode(const pkpy_Str* self, int n);
|
||||
int pkpy_Str__index(const pkpy_Str* self, const pkpy_Str* sub, int start);
|
||||
int pkpy_Str__count(const pkpy_Str* self, const pkpy_Str* sub);
|
||||
c11_vector/* T=c11_string */ pkpy_Str__split(const pkpy_Str* self, char sep);
|
||||
c11_vector/* T=c11_string */ pkpy_Str__split2(const pkpy_Str* self, const pkpy_Str* sep);
|
||||
|
||||
bool c11__isascii(const char* p, int size);
|
||||
bool c11__is_unicode_Lo_char(int c);
|
||||
int c11__u8_header(unsigned char c, bool suppress);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,104 +1,246 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/common/utils.hpp"
|
||||
#include "pocketpy/common/sstream.h"
|
||||
#include "pocketpy/common/utils.h"
|
||||
#include "pocketpy/common/memorypool.hpp"
|
||||
#include "pocketpy/common/vector.h"
|
||||
#include "pocketpy/common/vector.hpp"
|
||||
#include "pocketpy/common/str.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <string_view>
|
||||
#include <ostream>
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
int utf8len(unsigned char c, bool suppress = false);
|
||||
struct SStream;
|
||||
struct Str: pkpy_Str {
|
||||
bool is_inlined() const { return is_sso; }
|
||||
|
||||
struct Str {
|
||||
int size;
|
||||
bool is_ascii;
|
||||
char* data;
|
||||
char _inlined[16];
|
||||
Str(){
|
||||
pkpy_Str__ctor2(this, "", 0);
|
||||
}
|
||||
|
||||
bool is_inlined() const { return data == _inlined; }
|
||||
Str(pkpy_Str&& s){
|
||||
std::memcpy(this, &s, sizeof(pkpy_Str));
|
||||
}
|
||||
|
||||
Str(const std::string& s){
|
||||
pkpy_Str__ctor2(this, s.data(), s.size());
|
||||
}
|
||||
|
||||
Str(std::string_view s){
|
||||
pkpy_Str__ctor2(this, s.data(), s.size());
|
||||
}
|
||||
|
||||
Str(const char* s){
|
||||
pkpy_Str__ctor2(this, s, strlen(s));
|
||||
}
|
||||
|
||||
Str(const char* s, int len){
|
||||
pkpy_Str__ctor2(this, s, len);
|
||||
}
|
||||
|
||||
Str();
|
||||
Str(int size, bool is_ascii);
|
||||
Str(const std::string& s);
|
||||
Str(std::string_view s);
|
||||
Str(const char* s);
|
||||
Str(const char* s, int len);
|
||||
Str(pair<char*, int>); // take ownership
|
||||
Str(const Str& other);
|
||||
Str(Str&& other);
|
||||
|
||||
Str(const Str& other){
|
||||
pkpy_Str__ctor2(this, pkpy_Str__data(&other), other.size);
|
||||
}
|
||||
|
||||
Str(Str&& other){
|
||||
std::memcpy(this, &other, sizeof(pkpy_Str));
|
||||
other.size = 0;
|
||||
other.is_sso = true;
|
||||
}
|
||||
|
||||
operator std::string_view () const { return sv(); }
|
||||
|
||||
const char* begin() const { return data; }
|
||||
|
||||
const char* end() const { return data + size; }
|
||||
|
||||
char operator[] (int idx) const { return data[idx]; }
|
||||
|
||||
const char* begin() const { return pkpy_Str__data(this); }
|
||||
const char* end() const { return pkpy_Str__data(this) + size; }
|
||||
int length() const { return size; }
|
||||
|
||||
char operator[] (int idx) const { return pkpy_Str__data(this)[idx]; }
|
||||
bool empty() const { return size == 0; }
|
||||
|
||||
size_t hash() const { return std::hash<std::string_view>()(sv()); }
|
||||
|
||||
Str& operator= (const Str&);
|
||||
Str operator+ (const Str&) const;
|
||||
Str operator+ (const char*) const;
|
||||
friend Str operator+ (const char*, const Str&);
|
||||
Str& operator= (const Str& other){
|
||||
pkpy_Str__dtor(this);
|
||||
pkpy_Str__ctor2(this, pkpy_Str__data(&other), other.size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator== (const std::string_view other) const;
|
||||
bool operator!= (const std::string_view other) const;
|
||||
bool operator< (const std::string_view other) const;
|
||||
friend bool operator< (const std::string_view other, const Str& str);
|
||||
Str operator+ (const Str& other) const{
|
||||
return pkpy_Str__concat(this, &other);
|
||||
}
|
||||
|
||||
bool operator== (const char* p) const;
|
||||
bool operator!= (const char* p) const;
|
||||
Str operator+ (const char* other) const{
|
||||
return pkpy_Str__concat2(this, other, strlen(other));
|
||||
}
|
||||
|
||||
bool operator== (const Str& other) const;
|
||||
bool operator!= (const Str& other) const;
|
||||
bool operator< (const Str& other) const;
|
||||
bool operator> (const Str& other) const;
|
||||
bool operator<= (const Str& other) const;
|
||||
bool operator>= (const Str& other) const;
|
||||
friend Str operator+ (const char* self, const Str& other){
|
||||
pkpy_Str tmp;
|
||||
pkpy_Str__ctor2(&tmp, self, strlen(self));
|
||||
pkpy_Str retval = pkpy_Str__concat(&tmp, &other);
|
||||
pkpy_Str__dtor(&tmp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
~Str();
|
||||
bool operator== (const std::string_view other) const{
|
||||
int res = pkpy_Str__cmp2(this, other.data(), other.size());
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<< (std::ostream& os, const Str& str);
|
||||
bool operator!= (const std::string_view other) const{
|
||||
int res = pkpy_Str__cmp2(this, other.data(), other.size());
|
||||
return res != 0;
|
||||
}
|
||||
|
||||
const char* c_str() const { return data; }
|
||||
bool operator< (const std::string_view other) const{
|
||||
int res = pkpy_Str__cmp2(this, other.data(), other.size());
|
||||
return res < 0;
|
||||
}
|
||||
|
||||
std::string_view sv() const { return std::string_view(data, size); }
|
||||
friend bool operator< (const std::string_view other, const Str& str){
|
||||
int res = pkpy_Str__cmp2(&str, other.data(), other.size());
|
||||
return res > 0;
|
||||
}
|
||||
|
||||
std::string str() const { return std::string(data, size); }
|
||||
bool operator== (const char* p) const{
|
||||
int res = pkpy_Str__cmp2(this, p, strlen(p));
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
Str substr(int start, int len) const;
|
||||
Str substr(int start) const;
|
||||
Str strip(bool left, bool right, const Str& chars) const;
|
||||
Str strip(bool left = true, bool right = true) const;
|
||||
bool operator!= (const char* p) const{
|
||||
int res = pkpy_Str__cmp2(this, p, strlen(p));
|
||||
return res != 0;
|
||||
}
|
||||
|
||||
bool operator== (const Str& other) const{
|
||||
return pkpy_Str__cmp(this, &other) == 0;
|
||||
}
|
||||
bool operator!= (const Str& other) const{
|
||||
return pkpy_Str__cmp(this, &other) != 0;
|
||||
}
|
||||
bool operator< (const Str& other) const{
|
||||
return pkpy_Str__cmp(this, &other) < 0;
|
||||
}
|
||||
bool operator> (const Str& other) const{
|
||||
return pkpy_Str__cmp(this, &other) > 0;
|
||||
}
|
||||
bool operator<= (const Str& other) const{
|
||||
return pkpy_Str__cmp(this, &other) <= 0;
|
||||
}
|
||||
bool operator>= (const Str& other) const{
|
||||
return pkpy_Str__cmp(this, &other) >= 0;
|
||||
}
|
||||
|
||||
~Str(){
|
||||
pkpy_Str__dtor(this);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<< (std::ostream& os, const Str& self){
|
||||
os.write(pkpy_Str__data(&self), self.size);
|
||||
return os;
|
||||
}
|
||||
|
||||
const char* c_str() const { return pkpy_Str__data(this); }
|
||||
|
||||
std::string_view sv() const {
|
||||
return std::string_view(pkpy_Str__data(this), size);
|
||||
}
|
||||
|
||||
std::string str() const {
|
||||
return std::string(pkpy_Str__data(this), size);
|
||||
}
|
||||
|
||||
Str slice(int start, int stop) const{
|
||||
return pkpy_Str__slice2(this, start, stop);
|
||||
}
|
||||
|
||||
Str slice(int start) const{
|
||||
return pkpy_Str__slice(this, start);
|
||||
}
|
||||
|
||||
Str substr(int start) const{
|
||||
return pkpy_Str__slice(this, start);
|
||||
}
|
||||
|
||||
Str strip(bool left, bool right, const Str& chars) const{
|
||||
return pkpy_Str__strip2(this, left, right, &chars);
|
||||
}
|
||||
|
||||
Str strip(bool left = true, bool right = true) const{
|
||||
return pkpy_Str__strip(this, left, right);
|
||||
}
|
||||
|
||||
Str lstrip() const { return strip(true, false); }
|
||||
|
||||
Str rstrip() const { return strip(false, true); }
|
||||
|
||||
Str lower() const;
|
||||
Str upper() const;
|
||||
Str escape(bool single_quote = true) const;
|
||||
void escape_(SStream& ss, bool single_quote = true) const;
|
||||
int index(const Str& sub, int start = 0) const;
|
||||
Str replace(char old, char new_) const;
|
||||
Str replace(const Str& old, const Str& new_, int count = -1) const;
|
||||
vector<std::string_view> split(const Str& sep) const;
|
||||
vector<std::string_view> split(char sep) const;
|
||||
int count(const Str& sub) const;
|
||||
Str lower() const{
|
||||
return pkpy_Str__lower(this);
|
||||
}
|
||||
Str upper() const{
|
||||
return pkpy_Str__upper(this);
|
||||
}
|
||||
Str replace(char old, char new_) const{
|
||||
return pkpy_Str__replace(this, old, new_);
|
||||
}
|
||||
Str replace(const Str& old, const Str& new_) const{
|
||||
return pkpy_Str__replace2(this, &old, &new_);
|
||||
}
|
||||
|
||||
Str escape(char quote='\'') const{
|
||||
return pkpy_Str__escape(this, quote);
|
||||
}
|
||||
|
||||
vector<std::string_view> split(const Str& sep) const{
|
||||
c11_vector/* T=c11_string */ res = pkpy_Str__split2(this, &sep);
|
||||
vector<std::string_view> retval(res.count);
|
||||
for(int i = 0; i < res.count; i++){
|
||||
c11_string tmp = c11__getitem(c11_string, &res, i);
|
||||
retval[i] = std::string_view(tmp.data, tmp.size);
|
||||
}
|
||||
c11_vector__dtor(&res);
|
||||
return retval;
|
||||
}
|
||||
|
||||
vector<std::string_view> split(char sep) const{
|
||||
c11_vector/* T=c11_string */ res = pkpy_Str__split(this, sep);
|
||||
vector<std::string_view> retval(res.count);
|
||||
for(int i = 0; i < res.count; i++){
|
||||
c11_string tmp = c11__getitem(c11_string, &res, i);
|
||||
retval[i] = std::string_view(tmp.data, tmp.size);
|
||||
}
|
||||
c11_vector__dtor(&res);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int index(const Str& sub, int start = 0) const{
|
||||
return pkpy_Str__index(this, &sub, start);
|
||||
}
|
||||
|
||||
int count(const Str& sub) const{
|
||||
return pkpy_Str__count(this, &sub);
|
||||
}
|
||||
|
||||
/*************unicode*************/
|
||||
int _unicode_index_to_byte(int i) const;
|
||||
int _byte_index_to_unicode(int n) const;
|
||||
Str u8_getitem(int i) const;
|
||||
Str u8_slice(int start, int stop, int step) const;
|
||||
int u8_length() const;
|
||||
int _unicode_index_to_byte(int i) const{
|
||||
return pkpy_Str__unicode_index_to_byte(this, i);
|
||||
}
|
||||
|
||||
int _byte_index_to_unicode(int n) const{
|
||||
return pkpy_Str__byte_index_to_unicode(this, n);
|
||||
}
|
||||
|
||||
Str u8_getitem(int i) const{
|
||||
return pkpy_Str__u8_getitem(this, i);
|
||||
}
|
||||
|
||||
Str u8_slice(int start, int stop, int step) const{
|
||||
return pkpy_Str__u8_slice(this, start, stop, step);
|
||||
}
|
||||
|
||||
int u8_length() const{
|
||||
return pkpy_Str__u8_length(this);
|
||||
}
|
||||
};
|
||||
|
||||
struct StrName {
|
||||
@ -131,36 +273,91 @@ struct StrName {
|
||||
static uint32_t _pesudo_random_index;
|
||||
};
|
||||
|
||||
struct SStream {
|
||||
struct SStream: pkpy_SStream {
|
||||
PK_ALWAYS_PASS_BY_POINTER(SStream)
|
||||
|
||||
vector<char> buffer;
|
||||
int _precision = -1;
|
||||
|
||||
bool empty() const { return buffer.empty(); }
|
||||
bool _submited = false;
|
||||
bool empty() const { return data.count == 0; }
|
||||
|
||||
void setprecision(int precision) { _precision = precision; }
|
||||
|
||||
SStream() {}
|
||||
SStream() {
|
||||
pkpy_SStream__ctor(this);
|
||||
}
|
||||
|
||||
SStream(int guess_size) { buffer.reserve(guess_size); }
|
||||
SStream(int guess_size) { c11_vector__reserve(&data, guess_size); }
|
||||
|
||||
Str str();
|
||||
~SStream() {
|
||||
// in case of error
|
||||
if(!_submited) pkpy_SStream__dtor(this);
|
||||
}
|
||||
|
||||
SStream& operator<< (const Str&);
|
||||
SStream& operator<< (const char*);
|
||||
SStream& operator<< (int);
|
||||
SStream& operator<< (size_t);
|
||||
SStream& operator<< (i64);
|
||||
SStream& operator<< (f64);
|
||||
SStream& operator<< (const std::string&);
|
||||
SStream& operator<< (std::string_view);
|
||||
SStream& operator<< (char);
|
||||
SStream& operator<< (StrName);
|
||||
Str str(){
|
||||
assert(!_submited);
|
||||
_submited = true;
|
||||
return pkpy_SStream__submit(this);
|
||||
}
|
||||
|
||||
void write_hex(unsigned char, bool non_zero = false);
|
||||
void write_hex(void*);
|
||||
void write_hex(i64);
|
||||
SStream& operator<< (const Str& val){
|
||||
pkpy_SStream__write_Str(this, &val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& operator<< (const char* val){
|
||||
pkpy_SStream__write_cstr(this, val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& operator<< (int val){
|
||||
pkpy_SStream__write_int(this, val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& operator<< (size_t val){
|
||||
// size_t could overflow int64, but nevermind...
|
||||
pkpy_SStream__write_i64(this, val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& operator<< (i64 val){
|
||||
pkpy_SStream__write_i64(this, val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& operator<< (f64 val){
|
||||
pkpy_SStream__write_double(this, val, _precision);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& operator<< (const std::string& val){
|
||||
pkpy_SStream__write_cstrn(this, val.data(), val.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& operator<< (std::string_view val){
|
||||
pkpy_SStream__write_cstrn(this, val.data(), val.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& operator<< (char val){
|
||||
pkpy_SStream__write_char(this, val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& operator<< (StrName name){
|
||||
std::string_view sv = name.sv();
|
||||
pkpy_SStream__write_cstrn(this, sv.data(), sv.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
void write_hex(unsigned char val, bool non_zero = false){
|
||||
pkpy_SStream__write_hex(this, val, non_zero);
|
||||
}
|
||||
|
||||
void write_ptr(void* p){
|
||||
pkpy_SStream__write_ptr(this, p);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef _S
|
||||
|
@ -37,4 +37,11 @@ struct has_gc_marker<T, std::void_t<decltype(&T::_gc_mark)>> : std::true_type {}
|
||||
|
||||
template <typename T>
|
||||
constexpr inline int py_sizeof = 16 + sizeof(T);
|
||||
|
||||
#define PK_ALWAYS_PASS_BY_POINTER(T) \
|
||||
T(const T&) = delete; \
|
||||
T& operator= (const T&) = delete; \
|
||||
T(T&&) = delete; \
|
||||
T& operator= (T&&) = delete;
|
||||
|
||||
} // namespace pkpy
|
||||
|
40
include/pocketpy/common/utils.h
Normal file
40
include/pocketpy/common/utils.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define PK_INLINE inline
|
||||
#else
|
||||
#define PK_INLINE static inline
|
||||
#endif
|
||||
|
||||
#define PK_REGION(name) 1
|
||||
|
||||
#define PK_SLICE_LOOP(i, start, stop, step) for(int i = start; step > 0 ? i < stop : i > stop; i += step)
|
||||
|
||||
// global constants
|
||||
#define PK_HEX_TABLE "0123456789abcdef"
|
||||
|
||||
extern const char* kPlatformStrings[];
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define PK_UNREACHABLE() __assume(0);
|
||||
#else
|
||||
#define PK_UNREACHABLE() __builtin_unreachable();
|
||||
#endif
|
||||
|
||||
#define PK_FATAL_ERROR(...) { fprintf(stderr, __VA_ARGS__); abort(); }
|
||||
|
||||
#define PK_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define PK_MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
// NARGS
|
||||
#define PK_NARGS_SEQ(_1, _2, _3, _4, N, ...) N
|
||||
#define PK_NARGS(...) PK_NARGS_SEQ(__VA_ARGS__, 4, 3, 2, 1, 0)
|
||||
#define PK_NPTRS(...) PK_NARGS_SEQ(__VA_ARGS__, int****, int***, int**, int*, int)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define PK_REGION(name) 1
|
||||
|
||||
#define PK_ALWAYS_PASS_BY_POINTER(T) \
|
||||
T(const T&) = delete; \
|
||||
T& operator= (const T&) = delete; \
|
||||
T(T&&) = delete; \
|
||||
T& operator= (T&&) = delete;
|
||||
|
||||
#define PK_SLICE_LOOP(i, start, stop, step) for(int i = start; step > 0 ? i < stop : i > stop; i += step)
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
// global constants
|
||||
const inline char* PK_HEX_TABLE = "0123456789abcdef";
|
||||
|
||||
const inline char* kPlatformStrings[] = {
|
||||
"win32", // 0
|
||||
"emscripten", // 1
|
||||
"ios", // 2
|
||||
"darwin", // 3
|
||||
"android", // 4
|
||||
"linux", // 5
|
||||
"unknown" // 6
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define PK_UNREACHABLE() __assume(0);
|
||||
#else
|
||||
#define PK_UNREACHABLE() __builtin_unreachable();
|
||||
#endif
|
||||
|
||||
#define PK_FATAL_ERROR(...) { fprintf(stderr, __VA_ARGS__); std::abort(); }
|
||||
|
||||
} // namespace pkpy
|
87
include/pocketpy/common/vector.h
Normal file
87
include/pocketpy/common/vector.h
Normal file
@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "pocketpy/common/algorithm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct c11_array{
|
||||
void* data;
|
||||
int count;
|
||||
int elem_size;
|
||||
} c11_array;
|
||||
|
||||
void c11_array__ctor(c11_array* self, int elem_size, int count);
|
||||
void c11_array__dtor(c11_array* self);
|
||||
c11_array c11_array__copy(const c11_array* self);
|
||||
void* c11_array__at(c11_array* self, int index);
|
||||
|
||||
typedef struct c11_vector{
|
||||
void* data;
|
||||
int count;
|
||||
int capacity;
|
||||
int elem_size;
|
||||
} c11_vector;
|
||||
|
||||
void c11_vector__ctor(c11_vector* self, int elem_size);
|
||||
void c11_vector__dtor(c11_vector* self);
|
||||
c11_vector c11_vector__copy(const c11_vector* self);
|
||||
void* c11_vector__at(c11_vector* self, int index);
|
||||
void c11_vector__reserve(c11_vector* self, int capacity);
|
||||
void c11_vector__clear(c11_vector* self);
|
||||
|
||||
#define c11__getitem(T, self, index) ((T*)(self)->data)[index]
|
||||
#define c11__setitem(T, self, index, value) ((T*)(self)->data)[index] = value;
|
||||
|
||||
#define c11_vector__push(T, self, elem) \
|
||||
do{ \
|
||||
if((self)->count == (self)->capacity) c11_vector__reserve((self), (self)->capacity*2); \
|
||||
((T*)(self)->data)[(self)->count] = (elem); \
|
||||
(self)->count++; \
|
||||
}while(0)
|
||||
|
||||
#define c11_vector__pop(T, self) \
|
||||
do{ \
|
||||
(self)->count--; \
|
||||
}while(0)
|
||||
|
||||
#define c11_vector__extend(T, self, p, size) \
|
||||
do{ \
|
||||
c11_vector__reserve((self), (self)->count + (size)); \
|
||||
memcpy((T*)(self)->data + (self)->count, (p), (size) * sizeof(T)); \
|
||||
(self)->count += (size); \
|
||||
}while(0)
|
||||
|
||||
|
||||
#define c11_vector__insert(T, self, index, elem) \
|
||||
do{ \
|
||||
if((self)->count == (self)->capacity) c11_vector__reserve((self), (self)->capacity*2); \
|
||||
T* p = (T*)(self)->data + (index); \
|
||||
memmove(p + 1, p, ((self)->count - (index)) * sizeof(T)); \
|
||||
*p = (elem); \
|
||||
(self)->count++; \
|
||||
}while(0)
|
||||
|
||||
#define c11_vector__erase(T, self, index) \
|
||||
do{ \
|
||||
T* p = (T*)(self)->data + (index); \
|
||||
memmove(p, p + 1, ((self)->count - (index) - 1) * sizeof(T)); \
|
||||
(self)->count--; \
|
||||
}while(0)
|
||||
|
||||
#define c11_vector__reverse(T, self, start, end) \
|
||||
do{ \
|
||||
T* p = (T*)(self)->data + (start); \
|
||||
T* q = (T*)(self)->data + (end); \
|
||||
while(p < q){ \
|
||||
T tmp = *p; *p = *q; *q = tmp; \
|
||||
p++; q--; \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "pocketpy/common/traits.hpp"
|
||||
#include "pocketpy/common/types.hpp"
|
||||
#include "pocketpy/common/algorithm.h"
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
|
@ -41,7 +41,7 @@ struct Compiler {
|
||||
#if PK_DEBUG_COMPILER
|
||||
if(__i>=0 && __i<lexer.nexts.size()){
|
||||
printf("%s:%d %s %s\n",
|
||||
lexer.src->filename.c_str(),
|
||||
lexer.src.filename().c_str(),
|
||||
curr().line,
|
||||
TK_STR(curr().type),
|
||||
curr().str().escape().c_str()
|
||||
|
@ -29,7 +29,7 @@ struct VoidP {
|
||||
|
||||
Str hex() const {
|
||||
SStream ss;
|
||||
ss.write_hex(ptr);
|
||||
ss.write_ptr(ptr);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include "pocketpy/common/config.h"
|
||||
#include "pocketpy/common/vector.hpp"
|
||||
#include "pocketpy/common/utils.hpp"
|
||||
#include "pocketpy/common/utils.h"
|
||||
#include "pocketpy/objects/object.hpp"
|
||||
#include "pocketpy/objects/namedict.hpp"
|
||||
|
||||
|
@ -74,9 +74,9 @@ struct Generator {
|
||||
|
||||
struct DictItemsIter {
|
||||
PyVar ref;
|
||||
int i;
|
||||
pkpy_DictIter it;
|
||||
|
||||
DictItemsIter(PyVar ref) : ref(ref) { i = PK_OBJ_GET(Dict, ref)._head_idx; }
|
||||
DictItemsIter(PyVar ref) : ref(ref) { it = PK_OBJ_GET(Dict, ref).iter(); }
|
||||
|
||||
void _gc_mark(VM* vm) const { vm->obj_gc_mark(ref); }
|
||||
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "pocketpy/interpreter/frame.hpp"
|
||||
#include "pocketpy/interpreter/profiler.hpp"
|
||||
|
||||
#include <typeindex>
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
/* Stack manipulation macros */
|
||||
@ -319,8 +321,8 @@ public:
|
||||
#endif
|
||||
|
||||
#if PK_REGION("Logging Methods")
|
||||
virtual void stdout_write(const Str& s){ _stdout(s.data, s.size); }
|
||||
virtual void stderr_write(const Str& s){ _stderr(s.data, s.size); }
|
||||
virtual void stdout_write(const Str& s){ _stdout(s.c_str(), s.size); }
|
||||
virtual void stderr_write(const Str& s){ _stderr(s.c_str(), s.size); }
|
||||
#endif
|
||||
|
||||
#if PK_REGION("Magic Bindings")
|
||||
|
@ -1,17 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/common/any.hpp"
|
||||
#include "pocketpy/common/any.h"
|
||||
#include "pocketpy/objects/tuplelist.hpp"
|
||||
#include "pocketpy/objects/namedict.hpp"
|
||||
#include "pocketpy/objects/sourcedata.hpp"
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
#if PK_ENABLE_STD_FUNCTION
|
||||
using NativeFuncC = function<PyVar(VM*, ArgsView)>;
|
||||
#else
|
||||
typedef PyVar (*NativeFuncC)(VM*, ArgsView);
|
||||
#endif
|
||||
|
||||
enum class BindType {
|
||||
DEFAULT,
|
||||
@ -171,10 +167,12 @@ struct Function {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T& lambda_get_userdata(PyVar* p) {
|
||||
T lambda_get_userdata(PyVar* p) {
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
static_assert(is_pod_v<T>);
|
||||
int offset = p[-1] != nullptr ? -1 : -2;
|
||||
return p[offset].obj_get<NativeFunc>()._userdata.cast<T>();
|
||||
NativeFunc& nf = p[offset].obj_get<NativeFunc>();
|
||||
return nf._userdata.as<T>();
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
|
113
include/pocketpy/objects/dict.h
Normal file
113
include/pocketpy/objects/dict.h
Normal file
@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "pocketpy/objects/pyvar.h"
|
||||
#include "pocketpy/common/vector.h"
|
||||
|
||||
/** @brief `pkpy_Dict` is the Dict type in Python */
|
||||
typedef struct {
|
||||
int count; /** number of elements in the dictionary */
|
||||
c11_vector _entries; /** contains `pkpy_DictEntry` (hidden type) */
|
||||
int _htcap; /** capacity of the hashtable, always a power of 2 */
|
||||
void* _hashtable; /** contains indecies, can be `u8`, `u16` or `u32` according to size*/
|
||||
} pkpy_Dict;
|
||||
|
||||
/** @brief `pkpy_DictIter` is used to iterate over a `pkpy_Dict` */
|
||||
typedef struct {
|
||||
const pkpy_Dict* _dict;
|
||||
int _index;
|
||||
} pkpy_DictIter;
|
||||
|
||||
/**
|
||||
* @brief `pkpy_Dict` constructor
|
||||
* @param self `pkpy_Dict` instance
|
||||
*/
|
||||
void pkpy_Dict__ctor(pkpy_Dict* self);
|
||||
|
||||
/**
|
||||
* @brief `pkpy_Dict` destructor
|
||||
* @param self `pkpy_Dict` instance
|
||||
*/
|
||||
void pkpy_Dict__dtor(pkpy_Dict* self);
|
||||
|
||||
/**
|
||||
* @brief Copy a `pkpy_Dict`
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @return a new `pkpy_Dict` instance, must be destructed by the caller
|
||||
*/
|
||||
pkpy_Dict pkpy_Dict__copy(const pkpy_Dict* self);
|
||||
|
||||
/**
|
||||
* @brief Set a key-value pair into the `pkpy_Dict`
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @param vm __eq__ and __hash__ context
|
||||
* @param key key to set
|
||||
* @param val value to set
|
||||
* @return `true` if the key is newly added, `false` if the key already exists
|
||||
*/
|
||||
bool pkpy_Dict__set(pkpy_Dict* self, void* vm, pkpy_Var key, pkpy_Var val);
|
||||
|
||||
/**
|
||||
* @brief Check if a key exists in the `pkpy_Dict`
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @param vm __eq__ and __hash__ context
|
||||
* @param key key to check
|
||||
* @return `true` if the key exists, `false` otherwise
|
||||
*/
|
||||
bool pkpy_Dict__contains(const pkpy_Dict* self, void* vm, pkpy_Var key);
|
||||
|
||||
/**
|
||||
* @brief Remove a key from the `pkpy_Dict`
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @param vm __eq__ and __hash__ context
|
||||
* @param key key to remove
|
||||
* @return `true` if the key was found and removed, `false` if the key doesn't exist
|
||||
*/
|
||||
bool pkpy_Dict__del(pkpy_Dict* self, void* vm, pkpy_Var key);
|
||||
|
||||
/**
|
||||
* @brief Try to get a value from the `pkpy_Dict`
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @param vm __eq__ and __hash__ context
|
||||
* @param key key to get
|
||||
* @return the value associated with the key, `NULL` if the key doesn't exist
|
||||
*/
|
||||
const pkpy_Var* pkpy_Dict__try_get(const pkpy_Dict* self, void* vm, pkpy_Var key);
|
||||
|
||||
/**
|
||||
* @brief Update the `pkpy_Dict` with another one
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @param vm __eq__ and __hash__ context
|
||||
* @param other `pkpy_Dict` instance to update with
|
||||
*/
|
||||
void pkpy_Dict__update(pkpy_Dict* self, void *vm, const pkpy_Dict* other);
|
||||
|
||||
/**
|
||||
* @brief Clear the `pkpy_Dict`
|
||||
* @param self `pkpy_Dict` instance
|
||||
*/
|
||||
void pkpy_Dict__clear(pkpy_Dict* self);
|
||||
|
||||
/**
|
||||
* @brief Iterate over the `pkpy_Dict`
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @return an iterator over the `pkpy_Dict`
|
||||
*/
|
||||
pkpy_DictIter pkpy_Dict__iter(const pkpy_Dict* self);
|
||||
|
||||
/**
|
||||
* @brief Iterate over the `pkpy_Dict`
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @param key key will be filled with the current key, can be `NULL` if not needed
|
||||
* @param value value will be filled with the current value, can be `NULL` if not needed
|
||||
* @return `true` if the iteration is still valid, `false` otherwise
|
||||
*/
|
||||
bool pkpy_DictIter__next(pkpy_DictIter* self, pkpy_Var* key, pkpy_Var* value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -2,63 +2,95 @@
|
||||
|
||||
#include "pocketpy/objects/base.hpp"
|
||||
#include "pocketpy/objects/tuplelist.hpp"
|
||||
#include "pocketpy/objects/dict.h"
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
struct Dict {
|
||||
struct Item {
|
||||
PyVar first;
|
||||
PyVar second;
|
||||
int prev;
|
||||
int next;
|
||||
};
|
||||
struct Dict : private pkpy_Dict {
|
||||
Dict() {
|
||||
pkpy_Dict__ctor(this);
|
||||
}
|
||||
|
||||
constexpr static int __Capacity = 8;
|
||||
constexpr static float __LoadFactor = 0.67f;
|
||||
Dict(Dict&& other) {
|
||||
std::memcpy(this, &other, sizeof(Dict));
|
||||
pkpy_Dict__ctor(&other);
|
||||
}
|
||||
|
||||
int _capacity;
|
||||
int _mask;
|
||||
int _size;
|
||||
int _critical_size;
|
||||
int _head_idx; // for order preserving
|
||||
int _tail_idx; // for order preserving
|
||||
Item* _items;
|
||||
Dict(const Dict& other) {
|
||||
// OPTIMIZEME: reduce copy
|
||||
auto clone = pkpy_Dict__copy(&other);
|
||||
std::memcpy(this, &clone, sizeof(Dict));
|
||||
}
|
||||
|
||||
Dict();
|
||||
Dict(Dict&& other);
|
||||
Dict(const Dict& other);
|
||||
Dict& operator= (const Dict&) = delete;
|
||||
Dict& operator= (Dict&&) = delete;
|
||||
|
||||
int size() const { return _size; }
|
||||
int size() const { return count; }
|
||||
|
||||
void _probe_0(VM* vm, PyVar key, bool& ok, int& i) const;
|
||||
void _probe_1(VM* vm, PyVar key, bool& ok, int& i) const;
|
||||
void set(VM* vm, PyVar key, PyVar val) {
|
||||
pkpy_Dict__set(this, vm, *(pkpy_Var*)(&key), *(pkpy_Var*)(&val));
|
||||
}
|
||||
|
||||
void set(VM* vm, PyVar key, PyVar val);
|
||||
void _rehash(VM* vm);
|
||||
PyVar try_get(VM* vm, PyVar key) const {
|
||||
auto res = pkpy_Dict__try_get(this, vm, *(pkpy_Var*)(&key));
|
||||
if (!res) return nullptr;
|
||||
return *(const PyVar*)(res);
|
||||
}
|
||||
|
||||
PyVar try_get(VM* vm, PyVar key) const;
|
||||
bool contains(VM* vm, PyVar key) const {
|
||||
return pkpy_Dict__contains(this, vm, *(pkpy_Var*)(&key));
|
||||
}
|
||||
|
||||
bool contains(VM* vm, PyVar key) const;
|
||||
bool del(VM* vm, PyVar key);
|
||||
void update(VM* vm, const Dict& other);
|
||||
bool del(VM* vm, PyVar key) {
|
||||
return pkpy_Dict__del(this, vm, *(pkpy_Var*)(&key));
|
||||
}
|
||||
|
||||
void update(VM* vm, const Dict& other) {
|
||||
pkpy_Dict__update(this, vm, &other);
|
||||
}
|
||||
|
||||
template <typename __Func>
|
||||
void apply(__Func f) const {
|
||||
int i = _head_idx;
|
||||
while(i != -1) {
|
||||
f(_items[i].first, _items[i].second);
|
||||
i = _items[i].next;
|
||||
pkpy_DictIter it = iter();
|
||||
PyVar key, val;
|
||||
while(pkpy_DictIter__next(&it, (pkpy_Var*)(&key), (pkpy_Var*)(&val))) {
|
||||
f(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
Tuple keys() const;
|
||||
Tuple values() const;
|
||||
void clear();
|
||||
~Dict();
|
||||
Tuple keys() const {
|
||||
Tuple res(count);
|
||||
pkpy_DictIter it = iter();
|
||||
PyVar key, val;
|
||||
int i = 0;
|
||||
while(pkpy_DictIter__next(&it, (pkpy_Var*)(&key), (pkpy_Var*)(&val))) {
|
||||
res[i++] = key;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void __alloc_items();
|
||||
Tuple values() const {
|
||||
Tuple res(count);
|
||||
pkpy_DictIter it = iter();
|
||||
PyVar key, val;
|
||||
int i = 0;
|
||||
while(pkpy_DictIter__next(&it, (pkpy_Var*)(&key), (pkpy_Var*)(&val))) {
|
||||
res[i++] = val;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
pkpy_DictIter iter() const {
|
||||
return pkpy_Dict__iter(this);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
pkpy_Dict__clear(this);
|
||||
}
|
||||
|
||||
~Dict() {
|
||||
pkpy_Dict__dtor(this);
|
||||
}
|
||||
|
||||
void _gc_mark(VM*) const;
|
||||
};
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include "pocketpy/common/config.h"
|
||||
#include "pocketpy/common/str.hpp"
|
||||
#include "pocketpy/common/utils.hpp"
|
||||
#include "pocketpy/common/utils.h"
|
||||
#include "pocketpy/objects/object.hpp"
|
||||
|
||||
namespace pkpy {
|
||||
|
51
include/pocketpy/objects/pyvar.h
Normal file
51
include/pocketpy/objects/pyvar.h
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief A python value in pocketpy.
|
||||
*/
|
||||
typedef struct {
|
||||
int type;
|
||||
int _0;
|
||||
int64_t _1;
|
||||
} pkpy_Var;
|
||||
|
||||
/**
|
||||
* @brief Check if the pkpy_Var is null.
|
||||
* @param self The variable to check.
|
||||
* @return True if the variable is null, false otherwise.
|
||||
*/
|
||||
#define pkpy_Var__is_null(self) ((self)->type == 0)
|
||||
|
||||
/**
|
||||
* @brief Set the variable to null.
|
||||
* @param self The variable to set.
|
||||
*/
|
||||
#define pkpy_Var__set_null(self) do { (self)->type = 0; } while(0)
|
||||
|
||||
/**
|
||||
* @brief Check if two pkpy_Vars are equal, respects to __eq__ method.
|
||||
* @param vm The virtual machine.
|
||||
* @param a The first pkpy_Var.
|
||||
* @param b The second pkpy_Var.
|
||||
* @return True if the pkpy_Vars are equal, false otherwise.
|
||||
*/
|
||||
bool pkpy_Var__eq__(void *vm, pkpy_Var a, pkpy_Var b);
|
||||
|
||||
/**
|
||||
* @brief Get the hash of the pkpy_Var, respects to __hash__ method.
|
||||
* @param vm The virtual machine.
|
||||
* @param a The pkpy_Var to hash.
|
||||
* @return The hash of the pkpy_Var.
|
||||
*/
|
||||
int64_t pkpy_Var__hash__(void *vm, pkpy_Var a);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
32
include/pocketpy/objects/sourcedata.h
Normal file
32
include/pocketpy/objects/sourcedata.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "pocketpy/common/str.h"
|
||||
#include "pocketpy/common/vector.h"
|
||||
|
||||
enum CompileMode { EXEC_MODE, EVAL_MODE, REPL_MODE, JSON_MODE, CELL_MODE };
|
||||
|
||||
struct pkpy_SourceData {
|
||||
enum CompileMode mode;
|
||||
bool is_precompiled;
|
||||
|
||||
pkpy_Str filename;
|
||||
pkpy_Str source;
|
||||
|
||||
c11_vector/*T=const char* */ line_starts;
|
||||
c11_vector/*T=pkpy_Str*/ _precompiled_tokens;
|
||||
};
|
||||
|
||||
void pkpy_SourceData__ctor(struct pkpy_SourceData *self, c11_string source, const pkpy_Str *filename, enum CompileMode mode);
|
||||
void pkpy_SourceData__dtor(struct pkpy_SourceData* self);
|
||||
|
||||
bool pkpy_SourceData__get_line(const struct pkpy_SourceData *self, int lineno, const char **st, const char **ed);
|
||||
pkpy_Str pkpy_SourceData__snapshot(const struct pkpy_SourceData *self, int lineno, const char *cursor, const char *name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,29 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/common/utils.hpp"
|
||||
#include "pocketpy/common/utils.h"
|
||||
#include "pocketpy/common/str.hpp"
|
||||
#include "pocketpy/objects/sourcedata.h"
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
enum CompileMode { EXEC_MODE, EVAL_MODE, REPL_MODE, JSON_MODE, CELL_MODE };
|
||||
struct SourceData : public pkpy_SourceData {
|
||||
SourceData(std::string_view source, const Str& filename, CompileMode mode) {
|
||||
pkpy_SourceData__ctor(this, {source.data(), (int)source.size()}, &filename, mode);
|
||||
}
|
||||
|
||||
struct SourceData {
|
||||
PK_ALWAYS_PASS_BY_POINTER(SourceData)
|
||||
~SourceData() {
|
||||
pkpy_SourceData__dtor(this);
|
||||
}
|
||||
|
||||
Str filename;
|
||||
CompileMode mode;
|
||||
std::string_view get_line(int lineno) const {
|
||||
const char *st, *ed;
|
||||
if (pkpy_SourceData__get_line(this, lineno, &st, &ed)) {
|
||||
return std::string_view(st, ed - st);
|
||||
}
|
||||
return "<?>";
|
||||
}
|
||||
|
||||
Str source;
|
||||
vector<const char*> line_starts;
|
||||
|
||||
bool is_precompiled;
|
||||
vector<Str> _precompiled_tokens;
|
||||
|
||||
SourceData(std::string_view source, const Str& filename, CompileMode mode);
|
||||
SourceData(const Str& filename, CompileMode mode);
|
||||
pair<const char*, const char*> _get_line(int lineno) const;
|
||||
std::string_view get_line(int lineno) const;
|
||||
Str snapshot(int lineno, const char* cursor, std::string_view name) const;
|
||||
Str snapshot(int lineno, const char* cursor, std::string_view name) const {
|
||||
return pkpy_SourceData__snapshot(this, lineno, cursor, name.empty() ? nullptr : name.data());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
|
23
prebuild.py
23
prebuild.py
@ -23,26 +23,31 @@ def get_sources():
|
||||
sources = get_sources()
|
||||
|
||||
# use LF line endings instead of CRLF
|
||||
with open("include/pocketpy/common/_generated.hpp", "wt", encoding='utf-8', newline='\n') as f:
|
||||
with open("include/pocketpy/common/_generated.h", "wt", encoding='utf-8', newline='\n') as f:
|
||||
data = '''#pragma once
|
||||
// generated by prebuild.py
|
||||
|
||||
namespace pkpy{
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
'''
|
||||
for key in sorted(sources.keys()):
|
||||
value = sources[key]
|
||||
data += f' extern const char kPythonLibs_{key}[];\n'
|
||||
data += '} // namespace pkpy\n'
|
||||
data += f'extern const char kPythonLibs_{key}[];\n'
|
||||
data += '''
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
'''
|
||||
f.write(data)
|
||||
|
||||
with open("src/common/_generated.cpp", "wt", encoding='utf-8', newline='\n') as f:
|
||||
with open("src/common/_generated.c", "wt", encoding='utf-8', newline='\n') as f:
|
||||
data = '''// generated by prebuild.py
|
||||
#include "pocketpy/common/_generated.hpp"
|
||||
#include "pocketpy/common/_generated.h"
|
||||
|
||||
namespace pkpy{
|
||||
'''
|
||||
for key in sorted(sources.keys()):
|
||||
value = sources[key]
|
||||
data += f' const char kPythonLibs_{key}[] = {value};\n'
|
||||
data += '} // namespace pkpy\n'
|
||||
data += f'const char kPythonLibs_{key}[] = {value};\n'
|
||||
f.write(data)
|
||||
|
@ -1,5 +1,9 @@
|
||||
python prebuild.py
|
||||
SRC=$(find src/ -name "*.cpp")
|
||||
|
||||
SRC_C=$(find src/ -name "*.c")
|
||||
SRC_CPP=$(find src/ -name "*.cpp")
|
||||
SRC="$SRC_C $SRC_CPP"
|
||||
|
||||
g++ -pg -Og -std=c++17 -frtti -Wfatal-errors -o main $SRC src2/main.cpp -Iinclude
|
||||
./main benchmarks/fib.py
|
||||
gprof main gmon.out > gprof.txt
|
||||
|
@ -1,5 +1,9 @@
|
||||
python prebuild.py
|
||||
SRC=$(find src/ -name "*.cpp")
|
||||
|
||||
SRC_C=$(find src/ -name "*.c")
|
||||
SRC_CPP=$(find src/ -name "*.cpp")
|
||||
SRC="$SRC_C $SRC_CPP"
|
||||
|
||||
clang++ -std=c++17 --coverage -O1 -stdlib=libc++ -frtti -Wfatal-errors -o main src2/main.cpp $SRC -Iinclude -DPK_ENABLE_OS=1 -DPK_DEBUG_PRECOMPILED_EXEC=1 -DPK_ENABLE_PROFILER=1
|
||||
|
||||
python scripts/run_tests.py
|
||||
|
19
src/common/_generated.c
Normal file
19
src/common/_generated.c
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
39
src/common/algorithm.c
Normal file
39
src/common/algorithm.c
Normal file
@ -0,0 +1,39 @@
|
||||
#include "pocketpy/common/algorithm.h"
|
||||
|
||||
void *c11__lower_bound(const void *key, const void *ptr, int count, int size,
|
||||
bool (*less)(const void *, const void *)) {
|
||||
char* __first = (char*)ptr;
|
||||
int __len = count;
|
||||
|
||||
while(__len != 0){
|
||||
int __l2 = (int)((unsigned int)__len / 2);
|
||||
char* __m = __first + __l2 * size;
|
||||
if(less(__m, key)){
|
||||
__m += size;
|
||||
__first = __m;
|
||||
__len -= __l2 + 1;
|
||||
}else{
|
||||
__len = __l2;
|
||||
}
|
||||
}
|
||||
return __first;
|
||||
}
|
||||
|
||||
static bool c11__less_int(const void* a, const void* b){
|
||||
return *(int*)a < *(int*)b;
|
||||
}
|
||||
|
||||
static bool c11__less_double(const void* a, const void* b){
|
||||
return *(double*)a < *(double*)b;
|
||||
}
|
||||
|
||||
int *c11__lower_bound_int(int key, const int *ptr, int count) {
|
||||
void* res = c11__lower_bound(&key, ptr, count, sizeof(int), c11__less_int);
|
||||
return (int*)res;
|
||||
}
|
||||
|
||||
double *c11__lower_bound_double(double key, const double *ptr, int count) {
|
||||
void* res = c11__lower_bound(&key, ptr, count, sizeof(double), c11__less_double);
|
||||
return (double*)res;
|
||||
}
|
||||
|
7
src/common/any.c
Normal file
7
src/common/any.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include "pocketpy/common/any.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void c11_userdata__ctor(c11_userdata* self, void* ptr, int size){
|
||||
memcpy(self, ptr, size);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#include "pocketpy/common/any.hpp"
|
||||
#include "pocketpy/common/utils.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
void any::__bad_any_cast(const std::type_index expected, const std::type_index actual) {
|
||||
PK_FATAL_ERROR("bad_any_cast: expected %s, got %s\n", expected.name(), actual.name())
|
||||
}
|
||||
|
||||
any::any(any&& other) noexcept : data(other.data), _vt(other._vt) {
|
||||
other.data = nullptr;
|
||||
other._vt = nullptr;
|
||||
}
|
||||
|
||||
any& any::operator= (any&& other) noexcept {
|
||||
if(data) _vt->deleter(data);
|
||||
data = other.data;
|
||||
_vt = other._vt;
|
||||
other.data = nullptr;
|
||||
other._vt = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
96
src/common/sourcedata.c
Normal file
96
src/common/sourcedata.c
Normal file
@ -0,0 +1,96 @@
|
||||
#include "pocketpy/objects/sourcedata.h"
|
||||
#include "pocketpy/common/sstream.h"
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void pkpy_SourceData__ctor(struct pkpy_SourceData* self,
|
||||
c11_string source, // may not be null-terminated
|
||||
const pkpy_Str* filename,
|
||||
enum CompileMode mode) {
|
||||
self->filename = pkpy_Str__copy(filename); // OPTIMIZEME?
|
||||
self->mode = mode;
|
||||
c11_vector__ctor(&self->line_starts, sizeof(const char*));
|
||||
c11_vector__ctor(&self->_precompiled_tokens, sizeof(pkpy_Str));
|
||||
|
||||
int index = 0;
|
||||
// Skip utf8 BOM if there is any.
|
||||
if (source.size >= 3 && strncmp(source.data, "\xEF\xBB\xBF", 3) == 0) index += 3;
|
||||
// Drop all '\r'
|
||||
pkpy_SStream ss;
|
||||
pkpy_SStream__ctor2(&ss, source.size + 1);
|
||||
while(index < source.size){
|
||||
char c = source.data[index];
|
||||
if(c != '\r') pkpy_SStream__write_char(&ss, c);
|
||||
index++;
|
||||
}
|
||||
self->source = pkpy_SStream__submit(&ss);
|
||||
self->is_precompiled = (strncmp(pkpy_Str__data(&self->source), "pkpy:", 5) == 0);
|
||||
c11_vector__push(const char*, &self->line_starts, pkpy_Str__data(&self->source));
|
||||
}
|
||||
|
||||
void pkpy_SourceData__dtor(struct pkpy_SourceData* self) {
|
||||
pkpy_Str__dtor(&self->filename);
|
||||
pkpy_Str__dtor(&self->source);
|
||||
c11_vector__dtor(&self->line_starts);
|
||||
c11_vector__dtor(&self->_precompiled_tokens);
|
||||
}
|
||||
|
||||
bool pkpy_SourceData__get_line(const struct pkpy_SourceData* self, int lineno, const char** st, const char** ed) {
|
||||
if(self->is_precompiled || lineno == -1) { return false; }
|
||||
lineno -= 1;
|
||||
if(lineno < 0) lineno = 0;
|
||||
const char* _start = c11__getitem(const char*, &self->line_starts, lineno);
|
||||
const char* i = _start;
|
||||
// max 300 chars
|
||||
while(*i != '\n' && *i != '\0' && i - _start < 300)
|
||||
i++;
|
||||
*st = _start;
|
||||
*ed = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_SourceData__snapshot(const struct pkpy_SourceData* self, int lineno, const char* cursor, const char* name) {
|
||||
pkpy_SStream ss;
|
||||
pkpy_SStream__ctor(&ss);
|
||||
|
||||
// pkpy_SStream__write_cstr(&ss, " File \"");
|
||||
// pkpy_SStream__write_Str(&ss, &self->filename);
|
||||
// pkpy_SStream__write_cstr(&ss, "\", line ");
|
||||
// pkpy_SStream__write_int(&ss, lineno);
|
||||
|
||||
pkpy_SStream__write(&ss,
|
||||
" File \"{}\", line {}",
|
||||
&self->filename,
|
||||
lineno
|
||||
);
|
||||
|
||||
if(name) {
|
||||
pkpy_SStream__write_cstr(&ss, ", in ");
|
||||
pkpy_SStream__write_cstr(&ss, name);
|
||||
}
|
||||
|
||||
if(!self->is_precompiled) {
|
||||
pkpy_SStream__write_char(&ss, '\n');
|
||||
const char *st = NULL, *ed;
|
||||
if(pkpy_SourceData__get_line(self, lineno, &st, &ed)) {
|
||||
while(st < ed && isblank(*st))
|
||||
++st;
|
||||
if(st < ed) {
|
||||
pkpy_SStream__write_cstr(&ss, " ");
|
||||
pkpy_SStream__write_cstrn(&ss, st, ed - st);
|
||||
if(cursor && st <= cursor && cursor <= ed) {
|
||||
pkpy_SStream__write_cstr(&ss, "\n ");
|
||||
for(int i = 0; i < (cursor - st); ++i)
|
||||
pkpy_SStream__write_char(&ss, ' ');
|
||||
pkpy_SStream__write_cstr(&ss, "^");
|
||||
}
|
||||
} else {
|
||||
st = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if(!st) { pkpy_SStream__write_cstr(&ss, " <?>"); }
|
||||
}
|
||||
return pkpy_SStream__submit(&ss);
|
||||
}
|
161
src/common/sstream.c
Normal file
161
src/common/sstream.c
Normal file
@ -0,0 +1,161 @@
|
||||
#include "pocketpy/common/sstream.h"
|
||||
#include "pocketpy/common/utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
|
||||
void pkpy_SStream__ctor(pkpy_SStream* self) {
|
||||
c11_vector__ctor(&self->data, sizeof(char));
|
||||
}
|
||||
|
||||
void pkpy_SStream__ctor2(pkpy_SStream* self, int capacity) {
|
||||
c11_vector__ctor(&self->data, sizeof(char));
|
||||
c11_vector__reserve(&self->data, capacity);
|
||||
}
|
||||
|
||||
void pkpy_SStream__dtor(pkpy_SStream* self) {
|
||||
c11_vector__dtor(&self->data);
|
||||
}
|
||||
|
||||
void pkpy_SStream__write_char(pkpy_SStream* self, char c) {
|
||||
c11_vector__push(char, &self->data, c);
|
||||
}
|
||||
|
||||
void pkpy_SStream__write_int(pkpy_SStream* self, int i) {
|
||||
char buf[12]; // sign + 10 digits + null terminator
|
||||
snprintf(buf, sizeof(buf), "%d", i);
|
||||
pkpy_SStream__write_cstr(self, buf);
|
||||
}
|
||||
|
||||
void pkpy_SStream__write_i64(pkpy_SStream* self, int64_t val) {
|
||||
// sign + 21 digits + null terminator
|
||||
// str(-2**64).__len__() == 21
|
||||
c11_vector__reserve(&self->data, self->data.count + 23);
|
||||
if(val == 0){
|
||||
pkpy_SStream__write_char(self, '0');
|
||||
return;
|
||||
}
|
||||
if(val < 0){
|
||||
pkpy_SStream__write_char(self, '-');
|
||||
val = -val;
|
||||
}
|
||||
int start = self->data.count;
|
||||
while(val){
|
||||
c11_vector__push(char, &self->data, '0' + val % 10);
|
||||
val /= 10;
|
||||
}
|
||||
int end = self->data.count - 1;
|
||||
c11_vector__reverse(char, &self->data, start, end);
|
||||
}
|
||||
|
||||
void pkpy_SStream__write_float(pkpy_SStream* self, float val, int precision){
|
||||
return pkpy_SStream__write_double(self, val, precision);
|
||||
}
|
||||
|
||||
void pkpy_SStream__write_double(pkpy_SStream* self, double val, int precision){
|
||||
if(isinf(val)) {
|
||||
pkpy_SStream__write_cstr(self, val > 0 ? "inf" : "-inf");
|
||||
return;
|
||||
}
|
||||
if(isnan(val)) {
|
||||
pkpy_SStream__write_cstr(self, "nan");
|
||||
return;
|
||||
}
|
||||
char b[32];
|
||||
int size;
|
||||
if(precision < 0) {
|
||||
int prec = 17 - 1; // std::numeric_limits<double>::max_digits10 == 17
|
||||
size = snprintf(b, sizeof(b), "%.*g", prec, val);
|
||||
} else {
|
||||
int prec = precision;
|
||||
size = snprintf(b, sizeof(b), "%.*f", prec, val);
|
||||
}
|
||||
pkpy_SStream__write_cstr(self, b);
|
||||
bool all_is_digit = true;
|
||||
for(int i = 1; i < size; i++){
|
||||
if(!isdigit(b[i])){ all_is_digit = false; break; }
|
||||
}
|
||||
if(all_is_digit) pkpy_SStream__write_cstr(self, ".0");
|
||||
}
|
||||
|
||||
void pkpy_SStream__write_Str(pkpy_SStream* self, const pkpy_Str* str) {
|
||||
pkpy_SStream__write_cstrn(self, pkpy_Str__data(str), str->size);
|
||||
}
|
||||
|
||||
void pkpy_SStream__write_sv(pkpy_SStream* self, c11_string sv) {
|
||||
pkpy_SStream__write_cstrn(self, sv.data, sv.size);
|
||||
}
|
||||
|
||||
void pkpy_SStream__write_cstr(pkpy_SStream* self, const char* str) {
|
||||
pkpy_SStream__write_cstrn(self, str, strlen(str));
|
||||
}
|
||||
|
||||
void pkpy_SStream__write_cstrn(pkpy_SStream* self, const char* str, int n) {
|
||||
c11_vector__extend(char, &self->data, str, n);
|
||||
}
|
||||
|
||||
void pkpy_SStream__write_hex(pkpy_SStream* self, unsigned char c, bool non_zero) {
|
||||
unsigned char high = c >> 4;
|
||||
unsigned char low = c & 0xf;
|
||||
if(non_zero) {
|
||||
if(high) pkpy_SStream__write_char(self, PK_HEX_TABLE[high]);
|
||||
if(high || low) pkpy_SStream__write_char(self, PK_HEX_TABLE[low]);
|
||||
} else {
|
||||
pkpy_SStream__write_char(self, PK_HEX_TABLE[high]);
|
||||
pkpy_SStream__write_char(self, PK_HEX_TABLE[low]);
|
||||
}
|
||||
}
|
||||
|
||||
void pkpy_SStream__write_ptr(pkpy_SStream* self, void* p) {
|
||||
if(p == NULL) {
|
||||
pkpy_SStream__write_cstr(self, "0x0");
|
||||
return;
|
||||
}
|
||||
pkpy_SStream__write_cstr(self, "0x");
|
||||
uintptr_t p_t = (uintptr_t)(p);
|
||||
bool non_zero = true;
|
||||
for(int i = sizeof(void*) - 1; i >= 0; i--) {
|
||||
unsigned char cpnt = (p_t >> (i * 8)) & 0xff;
|
||||
pkpy_SStream__write_hex(self, cpnt, non_zero);
|
||||
if(cpnt != 0) non_zero = false;
|
||||
}
|
||||
}
|
||||
|
||||
void pkpy_SStream__write_any(pkpy_SStream* self, const char* fmt, const pkpy_AnyStr* args, int n){
|
||||
int i = 0;
|
||||
while(*fmt){
|
||||
if(*fmt == '{' && fmt[1] == '}'){
|
||||
assert(i < n);
|
||||
switch(args[i].type){
|
||||
case 1: pkpy_SStream__write_int(self, args[i]._int); break;
|
||||
case 2: pkpy_SStream__write_i64(self, args[i]._i64); break;
|
||||
case 3: pkpy_SStream__write_float(self, args[i]._float, -1); break;
|
||||
case 4: pkpy_SStream__write_double(self, args[i]._double, -1); break;
|
||||
case 5: pkpy_SStream__write_char(self, args[i]._char); break;
|
||||
case 6: pkpy_SStream__write_Str(self, args[i]._str); break;
|
||||
case 7: pkpy_SStream__write_sv(self, args[i]._sv); break;
|
||||
case 8: pkpy_SStream__write_cstr(self, args[i]._cstr); break;
|
||||
case 9: assert(0); break;
|
||||
default: assert(0); break;
|
||||
}
|
||||
fmt += 2;
|
||||
i++;
|
||||
}else{
|
||||
pkpy_SStream__write_char(self, *fmt);
|
||||
fmt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_SStream__submit(pkpy_SStream* self) {
|
||||
c11_vector__push(char, &self->data, '\0');
|
||||
pkpy_Str retval = {
|
||||
.size = self->data.count - 1,
|
||||
.is_ascii = false, // need to check
|
||||
.is_sso = false,
|
||||
._ptr = (char*)self->data.data
|
||||
};
|
||||
return retval;
|
||||
}
|
405
src/common/str.c
Normal file
405
src/common/str.c
Normal file
@ -0,0 +1,405 @@
|
||||
#include "pocketpy/common/str.h"
|
||||
#include "pocketpy/common/utils.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void pkpy_Str__ctor(pkpy_Str *self, const char *data){
|
||||
pkpy_Str__ctor2(self, data, strlen(data));
|
||||
}
|
||||
|
||||
void pkpy_Str__ctor2(pkpy_Str *self, const char *data, int size){
|
||||
self->size = size;
|
||||
self->is_ascii = c11__isascii(data, size);
|
||||
self->is_sso = size < sizeof(self->_inlined);
|
||||
char* p;
|
||||
if(self->is_sso){
|
||||
p = self->_inlined;
|
||||
}else{
|
||||
self->_ptr = (char*)malloc(size + 1);
|
||||
p = self->_ptr;
|
||||
}
|
||||
memcpy(p, data, size);
|
||||
p[size] = '\0';
|
||||
}
|
||||
|
||||
void pkpy_Str__dtor(pkpy_Str *self){
|
||||
if(!self->is_sso){
|
||||
free(self->_ptr);
|
||||
self->is_sso = true;
|
||||
self->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__copy(const pkpy_Str *self){
|
||||
pkpy_Str retval = *self;
|
||||
if(!self->is_sso){
|
||||
retval._ptr = (char*)malloc(self->size + 1);
|
||||
// '\0' is copied
|
||||
memcpy(retval._ptr, self->_ptr, self->size + 1);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__concat(const pkpy_Str *self, const pkpy_Str *other){
|
||||
pkpy_Str retval = {
|
||||
.size = self->size + other->size,
|
||||
.is_ascii = self->is_ascii && other->is_ascii,
|
||||
.is_sso = self->size + other->size < sizeof(retval._inlined),
|
||||
};
|
||||
char* p;
|
||||
if(retval.is_sso){
|
||||
p = retval._inlined;
|
||||
}else{
|
||||
retval._ptr = (char*)malloc(retval.size + 1);
|
||||
p = retval._ptr;
|
||||
}
|
||||
memcpy(p, pkpy_Str__data(self), self->size);
|
||||
memcpy(p + self->size, pkpy_Str__data(other), other->size);
|
||||
p[retval.size] = '\0';
|
||||
return retval;
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__concat2(const pkpy_Str *self, const char *other, int size){
|
||||
pkpy_Str tmp;
|
||||
pkpy_Str__ctor2(&tmp, other, size);
|
||||
pkpy_Str retval = pkpy_Str__concat(self, &tmp);
|
||||
pkpy_Str__dtor(&tmp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__slice(const pkpy_Str *self, int start){
|
||||
return pkpy_Str__slice2(self, start, self->size);
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__slice2(const pkpy_Str *self, int start, int stop){
|
||||
pkpy_Str retval;
|
||||
if(stop < start) stop = start;
|
||||
pkpy_Str__ctor2(&retval, pkpy_Str__data(self) + start, stop - start);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__lower(const pkpy_Str *self){
|
||||
pkpy_Str retval = pkpy_Str__copy(self);
|
||||
char* p = (char*)pkpy_Str__data(&retval);
|
||||
for(int i = 0; i < retval.size; i++){
|
||||
if('A' <= p[i] && p[i] <= 'Z') p[i] += 32;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__upper(const pkpy_Str *self){
|
||||
pkpy_Str retval = pkpy_Str__copy(self);
|
||||
char* p = (char*)pkpy_Str__data(&retval);
|
||||
for(int i = 0; i < retval.size; i++){
|
||||
if('a' <= p[i] && p[i] <= 'z') p[i] -= 32;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__escape(const pkpy_Str* self, char quote){
|
||||
assert(quote == '"' || quote == '\'');
|
||||
c11_vector buffer;
|
||||
c11_vector__ctor(&buffer, sizeof(char));
|
||||
c11_vector__reserve(&buffer, self->size);
|
||||
c11_vector__push(char, &buffer, quote);
|
||||
const char* data = pkpy_Str__data(self);
|
||||
for(int i = 0; i < self->size; i++) {
|
||||
char c = data[i];
|
||||
switch(c) {
|
||||
case '"': case '\'':
|
||||
if(c == quote) c11_vector__push(char, &buffer, '\\');
|
||||
c11_vector__push(char, &buffer, c);
|
||||
break;
|
||||
case '\\':
|
||||
c11_vector__push(char, &buffer, '\\');
|
||||
c11_vector__push(char, &buffer, '\\');
|
||||
break;
|
||||
case '\n':
|
||||
c11_vector__push(char, &buffer, '\\');
|
||||
c11_vector__push(char, &buffer, 'n');
|
||||
break;
|
||||
case '\r':
|
||||
c11_vector__push(char, &buffer, '\\');
|
||||
c11_vector__push(char, &buffer, 'r');
|
||||
break;
|
||||
case '\t':
|
||||
c11_vector__push(char, &buffer, '\\');
|
||||
c11_vector__push(char, &buffer, 't');
|
||||
break;
|
||||
case '\b':
|
||||
c11_vector__push(char, &buffer, '\\');
|
||||
c11_vector__push(char, &buffer, 'b');
|
||||
break;
|
||||
default:
|
||||
if('\x00' <= c && c <= '\x1f') {
|
||||
c11_vector__push(char, &buffer, '\\');
|
||||
c11_vector__push(char, &buffer, 'x');
|
||||
c11_vector__push(char, &buffer, PK_HEX_TABLE[c >> 4]);
|
||||
c11_vector__push(char, &buffer, PK_HEX_TABLE[c & 0xf]);
|
||||
} else {
|
||||
c11_vector__push(char, &buffer, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
c11_vector__push(char, &buffer, quote);
|
||||
c11_vector__push(char, &buffer, '\0');
|
||||
pkpy_Str retval = {
|
||||
.size = buffer.count - 1,
|
||||
.is_ascii = self->is_ascii,
|
||||
.is_sso = false,
|
||||
._ptr = (char*)buffer.data,
|
||||
};
|
||||
return retval;
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__strip(const pkpy_Str *self, bool left, bool right){
|
||||
const char* data = pkpy_Str__data(self);
|
||||
if(self->is_ascii) {
|
||||
int L = 0;
|
||||
int R = self->size;
|
||||
if(left) {
|
||||
while(L < R && (data[L] == ' ' || data[L] == '\t' || data[L] == '\n' || data[L] == '\r'))
|
||||
L++;
|
||||
}
|
||||
if(right) {
|
||||
while(L < R && (data[R - 1] == ' ' || data[R - 1] == '\t' || data[R - 1] == '\n' || data[R - 1] == '\r'))
|
||||
R--;
|
||||
}
|
||||
return pkpy_Str__slice2(self, L, R);
|
||||
} else {
|
||||
pkpy_Str tmp;
|
||||
pkpy_Str__ctor(&tmp, " \t\n\r");
|
||||
pkpy_Str retval = pkpy_Str__strip2(self, left, right, &tmp);
|
||||
pkpy_Str__dtor(&tmp);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__strip2(const pkpy_Str *self, bool left, bool right, const pkpy_Str *chars){
|
||||
int L = 0;
|
||||
int R = pkpy_Str__u8_length(self);
|
||||
if(left) {
|
||||
while(L < R){
|
||||
pkpy_Str tmp = pkpy_Str__u8_getitem(self, L);
|
||||
bool found = pkpy_Str__index(chars, &tmp, 0) != -1;
|
||||
pkpy_Str__dtor(&tmp);
|
||||
if(!found) break;
|
||||
L++;
|
||||
}
|
||||
}
|
||||
if(right) {
|
||||
while(L < R){
|
||||
pkpy_Str tmp = pkpy_Str__u8_getitem(self, R - 1);
|
||||
bool found = pkpy_Str__index(chars, &tmp, 0) != -1;
|
||||
pkpy_Str__dtor(&tmp);
|
||||
if(!found) break;
|
||||
R--;
|
||||
}
|
||||
}
|
||||
return pkpy_Str__u8_slice(self, L, R, 1);
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__replace(const pkpy_Str *self, char old, char new_){
|
||||
pkpy_Str retval = pkpy_Str__copy(self);
|
||||
char* p = (char*)pkpy_Str__data(&retval);
|
||||
for(int i = 0; i < retval.size; i++){
|
||||
if(p[i] == old) p[i] = new_;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__replace2(const pkpy_Str *self, const pkpy_Str *old, const pkpy_Str *new_){
|
||||
c11_vector buffer;
|
||||
c11_vector__ctor(&buffer, sizeof(char));
|
||||
int start = 0;
|
||||
while(true) {
|
||||
int i = pkpy_Str__index(self, old, start);
|
||||
if(i == -1) break;
|
||||
pkpy_Str tmp = pkpy_Str__slice2(self, start, i);
|
||||
c11_vector__extend(char, &buffer, pkpy_Str__data(&tmp), tmp.size);
|
||||
pkpy_Str__dtor(&tmp);
|
||||
c11_vector__extend(char, &buffer, pkpy_Str__data(new_), new_->size);
|
||||
start = i + old->size;
|
||||
}
|
||||
pkpy_Str tmp = pkpy_Str__slice2(self, start, self->size);
|
||||
c11_vector__extend(char, &buffer, pkpy_Str__data(&tmp), tmp.size);
|
||||
pkpy_Str__dtor(&tmp);
|
||||
c11_vector__push(char, &buffer, '\0');
|
||||
pkpy_Str retval = {
|
||||
.size = buffer.count - 1,
|
||||
.is_ascii = self->is_ascii && old->is_ascii && new_->is_ascii,
|
||||
.is_sso = false,
|
||||
._ptr = (char*)buffer.data,
|
||||
};
|
||||
return retval;
|
||||
}
|
||||
|
||||
int pkpy_Str__cmp(const pkpy_Str *self, const pkpy_Str *other){
|
||||
return pkpy_Str__cmp2(self, pkpy_Str__data(other), other->size);
|
||||
}
|
||||
|
||||
int pkpy_Str__cmp2(const pkpy_Str *self, const char *other, int size){
|
||||
int res = strncmp(pkpy_Str__data(self), other, PK_MIN(self->size, size));
|
||||
if(res != 0) return res;
|
||||
return self->size - size;
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__u8_getitem(const pkpy_Str *self, int i){
|
||||
i = pkpy_Str__unicode_index_to_byte(self, i);
|
||||
int size = c11__u8_header(pkpy_Str__data(self)[i], false);
|
||||
return pkpy_Str__slice2(self, i, i + size);
|
||||
}
|
||||
|
||||
pkpy_Str pkpy_Str__u8_slice(const pkpy_Str *self, int start, int stop, int step){
|
||||
c11_vector buffer;
|
||||
c11_vector__ctor(&buffer, sizeof(char));
|
||||
assert(step != 0);
|
||||
if(self->is_ascii){
|
||||
const char* p = pkpy_Str__data(self);
|
||||
for (int i=start; step>0 ? i<stop : i>stop; i+=step) {
|
||||
c11_vector__push(char, &buffer, p[i]);
|
||||
}
|
||||
}else{
|
||||
for (int i=start; step>0 ? i<stop : i>stop; i+=step) {
|
||||
pkpy_Str unicode = pkpy_Str__u8_getitem(self, i);
|
||||
const char* p = pkpy_Str__data(&unicode);
|
||||
for(int j = 0; j < unicode.size; j++){
|
||||
c11_vector__push(char, &buffer, p[j]);
|
||||
}
|
||||
pkpy_Str__dtor(&unicode);
|
||||
}
|
||||
}
|
||||
c11_vector__push(char, &buffer, '\0');
|
||||
pkpy_Str retval = {
|
||||
.size = buffer.count - 1,
|
||||
.is_ascii = self->is_ascii,
|
||||
.is_sso = false,
|
||||
._ptr = (char*)buffer.data,
|
||||
};
|
||||
return retval;
|
||||
}
|
||||
|
||||
int pkpy_Str__u8_length(const pkpy_Str *self){
|
||||
return pkpy_Str__byte_index_to_unicode(self, self->size);
|
||||
}
|
||||
|
||||
int pkpy_Str__unicode_index_to_byte(const pkpy_Str* self, int i) {
|
||||
if(self->is_ascii) return i;
|
||||
const char* p = pkpy_Str__data(self);
|
||||
int j = 0;
|
||||
while(i > 0) {
|
||||
j += c11__u8_header(p[j], false);
|
||||
i--;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
int pkpy_Str__byte_index_to_unicode(const pkpy_Str* self, int n) {
|
||||
if(self->is_ascii) return n;
|
||||
const char* p = pkpy_Str__data(self);
|
||||
int cnt = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
if((p[i] & 0xC0) != 0x80) cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int pkpy_Str__index(const pkpy_Str *self, const pkpy_Str *sub, int start){
|
||||
if(sub->size == 0) return start;
|
||||
int max_end = self->size - sub->size;
|
||||
const char* self_data = pkpy_Str__data(self);
|
||||
const char* sub_data = pkpy_Str__data(sub);
|
||||
for(int i=start; i<=max_end; i++){
|
||||
int res = memcmp(self_data + i, sub_data, sub->size);
|
||||
if(res == 0) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pkpy_Str__count(const pkpy_Str *self, const pkpy_Str *sub){
|
||||
if(sub->size == 0) return self->size + 1;
|
||||
int cnt = 0;
|
||||
int start = 0;
|
||||
while(true) {
|
||||
int i = pkpy_Str__index(self, sub, start);
|
||||
if(i == -1) break;
|
||||
cnt++;
|
||||
start = i + sub->size;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
c11_vector/* T=c11_string */ pkpy_Str__split(const pkpy_Str *self, char sep){
|
||||
c11_vector retval;
|
||||
c11_vector__ctor(&retval, sizeof(c11_string));
|
||||
const char* data = pkpy_Str__data(self);
|
||||
int i = 0;
|
||||
for(int j = 0; j < self->size; j++) {
|
||||
if(data[j] == sep) {
|
||||
if(j > i){
|
||||
c11_string tmp = {data + i, j - i};
|
||||
c11_vector__push(c11_string, &retval, tmp);
|
||||
}
|
||||
i = j + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(self->size > i){
|
||||
c11_string tmp = {data + i, self->size - i};
|
||||
c11_vector__push(c11_string, &retval, tmp);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
c11_vector/* T=c11_string */ pkpy_Str__split2(const pkpy_Str *self, const pkpy_Str *sep){
|
||||
c11_vector retval;
|
||||
c11_vector__ctor(&retval, sizeof(c11_string));
|
||||
int start = 0;
|
||||
const char* data = pkpy_Str__data(self);
|
||||
while(true) {
|
||||
int i = pkpy_Str__index(self, sep, start);
|
||||
if(i == -1) break;
|
||||
c11_string tmp = {data + start, i - start};
|
||||
if(tmp.size != 0) c11_vector__push(c11_string, &retval, tmp);
|
||||
start = i + sep->size;
|
||||
}
|
||||
c11_string tmp = {data + start, self->size - start};
|
||||
if(tmp.size != 0) c11_vector__push(c11_string, &retval, tmp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool c11__isascii(const char* p, int size){
|
||||
for(int i = 0; i < size; i++)
|
||||
if((unsigned char)p[i] > 127)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static const int kLoRangeA[] = {170,186,443,448,660,1488,1519,1568,1601,1646,1649,1749,1774,1786,1791,1808,1810,1869,1969,1994,2048,2112,2144,2208,2230,2308,2365,2384,2392,2418,2437,2447,2451,2474,2482,2486,2493,2510,2524,2527,2544,2556,2565,2575,2579,2602,2610,2613,2616,2649,2654,2674,2693,2703,2707,2730,2738,2741,2749,2768,2784,2809,2821,2831,2835,2858,2866,2869,2877,2908,2911,2929,2947,2949,2958,2962,2969,2972,2974,2979,2984,2990,3024,3077,3086,3090,3114,3133,3160,3168,3200,3205,3214,3218,3242,3253,3261,3294,3296,3313,3333,3342,3346,3389,3406,3412,3423,3450,3461,3482,3507,3517,3520,3585,3634,3648,3713,3716,3718,3724,3749,3751,3762,3773,3776,3804,3840,3904,3913,3976,4096,4159,4176,4186,4193,4197,4206,4213,4238,4352,4682,4688,4696,4698,4704,4746,4752,4786,4792,4800,4802,4808,4824,4882,4888,4992,5121,5743,5761,5792,5873,5888,5902,5920,5952,5984,5998,6016,6108,6176,6212,6272,6279,6314,6320,6400,6480,6512,6528,6576,6656,6688,6917,6981,7043,7086,7098,7168,7245,7258,7401,7406,7413,7418,8501,11568,11648,11680,11688,11696,11704,11712,11720,11728,11736,12294,12348,12353,12447,12449,12543,12549,12593,12704,12784,13312,19968,40960,40982,42192,42240,42512,42538,42606,42656,42895,42999,43003,43011,43015,43020,43072,43138,43250,43259,43261,43274,43312,43360,43396,43488,43495,43514,43520,43584,43588,43616,43633,43642,43646,43697,43701,43705,43712,43714,43739,43744,43762,43777,43785,43793,43808,43816,43968,44032,55216,55243,63744,64112,64285,64287,64298,64312,64318,64320,64323,64326,64467,64848,64914,65008,65136,65142,65382,65393,65440,65474,65482,65490,65498,65536,65549,65576,65596,65599,65616,65664,66176,66208,66304,66349,66370,66384,66432,66464,66504,66640,66816,66864,67072,67392,67424,67584,67592,67594,67639,67644,67647,67680,67712,67808,67828,67840,67872,67968,68030,68096,68112,68117,68121,68192,68224,68288,68297,68352,68416,68448,68480,68608,68864,69376,69415,69424,69600,69635,69763,69840,69891,69956,69968,70006,70019,70081,70106,70108,70144,70163,70272,70280,70282,70287,70303,70320,70405,70415,70419,70442,70450,70453,70461,70480,70493,70656,70727,70751,70784,70852,70855,71040,71128,71168,71236,71296,71352,71424,71680,71935,72096,72106,72161,72163,72192,72203,72250,72272,72284,72349,72384,72704,72714,72768,72818,72960,72968,72971,73030,73056,73063,73066,73112,73440,73728,74880,77824,82944,92160,92736,92880,92928,93027,93053,93952,94032,94208,100352,110592,110928,110948,110960,113664,113776,113792,113808,123136,123214,123584,124928,126464,126469,126497,126500,126503,126505,126516,126521,126523,126530,126535,126537,126539,126541,126545,126548,126551,126553,126555,126557,126559,126561,126564,126567,126572,126580,126585,126590,126592,126603,126625,126629,126635,131072,173824,177984,178208,183984,194560};
|
||||
static const int kLoRangeB[] = {170,186,443,451,660,1514,1522,1599,1610,1647,1747,1749,1775,1788,1791,1808,1839,1957,1969,2026,2069,2136,2154,2228,2237,2361,2365,2384,2401,2432,2444,2448,2472,2480,2482,2489,2493,2510,2525,2529,2545,2556,2570,2576,2600,2608,2611,2614,2617,2652,2654,2676,2701,2705,2728,2736,2739,2745,2749,2768,2785,2809,2828,2832,2856,2864,2867,2873,2877,2909,2913,2929,2947,2954,2960,2965,2970,2972,2975,2980,2986,3001,3024,3084,3088,3112,3129,3133,3162,3169,3200,3212,3216,3240,3251,3257,3261,3294,3297,3314,3340,3344,3386,3389,3406,3414,3425,3455,3478,3505,3515,3517,3526,3632,3635,3653,3714,3716,3722,3747,3749,3760,3763,3773,3780,3807,3840,3911,3948,3980,4138,4159,4181,4189,4193,4198,4208,4225,4238,4680,4685,4694,4696,4701,4744,4749,4784,4789,4798,4800,4805,4822,4880,4885,4954,5007,5740,5759,5786,5866,5880,5900,5905,5937,5969,5996,6000,6067,6108,6210,6264,6276,6312,6314,6389,6430,6509,6516,6571,6601,6678,6740,6963,6987,7072,7087,7141,7203,7247,7287,7404,7411,7414,7418,8504,11623,11670,11686,11694,11702,11710,11718,11726,11734,11742,12294,12348,12438,12447,12538,12543,12591,12686,12730,12799,19893,40943,40980,42124,42231,42507,42527,42539,42606,42725,42895,42999,43009,43013,43018,43042,43123,43187,43255,43259,43262,43301,43334,43388,43442,43492,43503,43518,43560,43586,43595,43631,43638,43642,43695,43697,43702,43709,43712,43714,43740,43754,43762,43782,43790,43798,43814,43822,44002,55203,55238,55291,64109,64217,64285,64296,64310,64316,64318,64321,64324,64433,64829,64911,64967,65019,65140,65276,65391,65437,65470,65479,65487,65495,65500,65547,65574,65594,65597,65613,65629,65786,66204,66256,66335,66368,66377,66421,66461,66499,66511,66717,66855,66915,67382,67413,67431,67589,67592,67637,67640,67644,67669,67702,67742,67826,67829,67861,67897,68023,68031,68096,68115,68119,68149,68220,68252,68295,68324,68405,68437,68466,68497,68680,68899,69404,69415,69445,69622,69687,69807,69864,69926,69956,70002,70006,70066,70084,70106,70108,70161,70187,70278,70280,70285,70301,70312,70366,70412,70416,70440,70448,70451,70457,70461,70480,70497,70708,70730,70751,70831,70853,70855,71086,71131,71215,71236,71338,71352,71450,71723,71935,72103,72144,72161,72163,72192,72242,72250,72272,72329,72349,72440,72712,72750,72768,72847,72966,72969,73008,73030,73061,73064,73097,73112,73458,74649,75075,78894,83526,92728,92766,92909,92975,93047,93071,94026,94032,100343,101106,110878,110930,110951,111355,113770,113788,113800,113817,123180,123214,123627,125124,126467,126495,126498,126500,126503,126514,126519,126521,126523,126530,126535,126537,126539,126543,126546,126548,126551,126553,126555,126557,126559,126562,126564,126570,126578,126583,126588,126590,126601,126619,126627,126633,126651,173782,177972,178205,183969,191456,195101};
|
||||
// clang-format on
|
||||
|
||||
bool c11__is_unicode_Lo_char(int c){
|
||||
if(c == 0x1f955) return true;
|
||||
int index = c11__lower_bound_int(c, kLoRangeA, 476) - kLoRangeA;
|
||||
if(c == kLoRangeA[index]) return true;
|
||||
index -= 1;
|
||||
if(index < 0) return false;
|
||||
return c >= kLoRangeA[index] && c <= kLoRangeB[index];
|
||||
}
|
||||
|
||||
int c11__u8_header(unsigned char c, bool suppress) {
|
||||
if((c & 0b10000000) == 0) return 1;
|
||||
if((c & 0b11100000) == 0b11000000) return 2;
|
||||
if((c & 0b11110000) == 0b11100000) return 3;
|
||||
if((c & 0b11111000) == 0b11110000) return 4;
|
||||
if((c & 0b11111100) == 0b11111000) return 5;
|
||||
if((c & 0b11111110) == 0b11111100) return 6;
|
||||
if(!suppress) PK_FATAL_ERROR("invalid utf8 char\n")
|
||||
return 0;
|
||||
}
|
@ -9,352 +9,18 @@
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
int utf8len(unsigned char c, bool suppress) {
|
||||
if((c & 0b10000000) == 0) return 1;
|
||||
if((c & 0b11100000) == 0b11000000) return 2;
|
||||
if((c & 0b11110000) == 0b11100000) return 3;
|
||||
if((c & 0b11111000) == 0b11110000) return 4;
|
||||
if((c & 0b11111100) == 0b11111000) return 5;
|
||||
if((c & 0b11111110) == 0b11111100) return 6;
|
||||
if(!suppress) PK_FATAL_ERROR("invalid utf8 char\n")
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PK_STR_ALLOCATE() \
|
||||
if(this->size < (int)sizeof(this->_inlined)) { \
|
||||
this->data = this->_inlined; \
|
||||
} else { \
|
||||
this->data = (char*)std::malloc(this->size + 1); \
|
||||
}
|
||||
|
||||
#define PK_STR_COPY_INIT(__s) \
|
||||
for(int i = 0; i < this->size; i++) { \
|
||||
this->data[i] = __s[i]; \
|
||||
if(!isascii(__s[i])) is_ascii = false; \
|
||||
} \
|
||||
this->data[this->size] = '\0';
|
||||
|
||||
Str::Str() : size(0), is_ascii(true), data(_inlined) { _inlined[0] = '\0'; }
|
||||
|
||||
Str::Str(int size, bool is_ascii) :
|
||||
size(size), is_ascii(is_ascii){PK_STR_ALLOCATE()}
|
||||
|
||||
Str::Str(const std::string& s) :
|
||||
size(s.size()), is_ascii(true){PK_STR_ALLOCATE() PK_STR_COPY_INIT(s)}
|
||||
|
||||
Str::Str(std::string_view s) :
|
||||
size(s.size()), is_ascii(true){PK_STR_ALLOCATE() PK_STR_COPY_INIT(s)}
|
||||
|
||||
Str::Str(const char* s) :
|
||||
size(strlen(s)), is_ascii(true){PK_STR_ALLOCATE() PK_STR_COPY_INIT(s)}
|
||||
|
||||
Str::Str(const char* s, int len) :
|
||||
size(len), is_ascii(true){PK_STR_ALLOCATE() PK_STR_COPY_INIT(s)}
|
||||
|
||||
Str::Str(pair<char*, int> detached) : size(detached.second), is_ascii(true) {
|
||||
this->data = detached.first;
|
||||
Str::Str(pair<char*, int> detached) {
|
||||
this->size = detached.second;
|
||||
this->is_ascii = true;
|
||||
this->is_sso = false;
|
||||
this->_ptr = detached.first;
|
||||
for(int i = 0; i < size; i++) {
|
||||
if(!isascii(data[i])) {
|
||||
if(!isascii(_ptr[i])) {
|
||||
is_ascii = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(data[size] == '\0');
|
||||
}
|
||||
|
||||
Str::Str(const Str& other) : size(other.size), is_ascii(other.is_ascii) {
|
||||
PK_STR_ALLOCATE()
|
||||
std::memcpy(data, other.data, size);
|
||||
data[size] = '\0';
|
||||
}
|
||||
|
||||
Str::Str(Str&& other) : size(other.size), is_ascii(other.is_ascii) {
|
||||
if(other.is_inlined()) {
|
||||
data = _inlined;
|
||||
for(int i = 0; i < size; i++)
|
||||
_inlined[i] = other._inlined[i];
|
||||
data[size] = '\0';
|
||||
} else {
|
||||
data = other.data;
|
||||
// zero out `other`
|
||||
other.data = other._inlined;
|
||||
other.data[0] = '\0';
|
||||
other.size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Str operator+ (const char* p, const Str& str) {
|
||||
Str other(p);
|
||||
return other + str;
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& os, const Str& str) { return os << str.sv(); }
|
||||
|
||||
bool operator< (const std::string_view other, const Str& str) { return other < str.sv(); }
|
||||
|
||||
Str& Str::operator= (const Str& other) {
|
||||
if(!is_inlined()) std::free(data);
|
||||
size = other.size;
|
||||
is_ascii = other.is_ascii;
|
||||
PK_STR_ALLOCATE()
|
||||
std::memcpy(data, other.data, size);
|
||||
data[size] = '\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
Str Str::operator+ (const Str& other) const {
|
||||
Str ret(size + other.size, is_ascii && other.is_ascii);
|
||||
std::memcpy(ret.data, data, size);
|
||||
std::memcpy(ret.data + size, other.data, other.size);
|
||||
ret.data[ret.size] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
Str Str::operator+ (const char* p) const {
|
||||
Str other(p);
|
||||
return *this + other;
|
||||
}
|
||||
|
||||
bool Str::operator== (const Str& other) const {
|
||||
if(size != other.size) return false;
|
||||
return memcmp(data, other.data, size) == 0;
|
||||
}
|
||||
|
||||
bool Str::operator!= (const Str& other) const {
|
||||
if(size != other.size) return true;
|
||||
return memcmp(data, other.data, size) != 0;
|
||||
}
|
||||
|
||||
bool Str::operator== (const std::string_view other) const {
|
||||
if(size != (int)other.size()) return false;
|
||||
return memcmp(data, other.data(), size) == 0;
|
||||
}
|
||||
|
||||
bool Str::operator!= (const std::string_view other) const {
|
||||
if(size != (int)other.size()) return true;
|
||||
return memcmp(data, other.data(), size) != 0;
|
||||
}
|
||||
|
||||
bool Str::operator== (const char* p) const { return *this == std::string_view(p); }
|
||||
|
||||
bool Str::operator!= (const char* p) const { return *this != std::string_view(p); }
|
||||
|
||||
bool Str::operator< (const Str& other) const { return this->sv() < other.sv(); }
|
||||
|
||||
bool Str::operator< (const std::string_view other) const { return this->sv() < other; }
|
||||
|
||||
bool Str::operator> (const Str& other) const { return this->sv() > other.sv(); }
|
||||
|
||||
bool Str::operator<= (const Str& other) const { return this->sv() <= other.sv(); }
|
||||
|
||||
bool Str::operator>= (const Str& other) const { return this->sv() >= other.sv(); }
|
||||
|
||||
Str::~Str() {
|
||||
if(!is_inlined()) std::free(data);
|
||||
}
|
||||
|
||||
Str Str::substr(int start, int len) const {
|
||||
Str ret(len, is_ascii);
|
||||
std::memcpy(ret.data, data + start, len);
|
||||
ret.data[len] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
Str Str::substr(int start) const { return substr(start, size - start); }
|
||||
|
||||
Str Str::strip(bool left, bool right, const Str& chars) const {
|
||||
int L = 0;
|
||||
int R = u8_length();
|
||||
if(left) {
|
||||
while(L < R && chars.index(u8_getitem(L)) != -1)
|
||||
L++;
|
||||
}
|
||||
if(right) {
|
||||
while(L < R && chars.index(u8_getitem(R - 1)) != -1)
|
||||
R--;
|
||||
}
|
||||
return u8_slice(L, R, 1);
|
||||
}
|
||||
|
||||
Str Str::strip(bool left, bool right) const {
|
||||
if(is_ascii) {
|
||||
int L = 0;
|
||||
int R = size;
|
||||
if(left) {
|
||||
while(L < R && (data[L] == ' ' || data[L] == '\t' || data[L] == '\n' || data[L] == '\r'))
|
||||
L++;
|
||||
}
|
||||
if(right) {
|
||||
while(L < R && (data[R - 1] == ' ' || data[R - 1] == '\t' || data[R - 1] == '\n' || data[R - 1] == '\r'))
|
||||
R--;
|
||||
}
|
||||
return substr(L, R - L);
|
||||
} else {
|
||||
return strip(left, right, " \t\n\r");
|
||||
}
|
||||
}
|
||||
|
||||
Str Str::lower() const {
|
||||
std::string copy(data, size);
|
||||
std::transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c) {
|
||||
if('A' <= c && c <= 'Z') return c + ('a' - 'A');
|
||||
return (int)c;
|
||||
});
|
||||
return Str(copy);
|
||||
}
|
||||
|
||||
Str Str::upper() const {
|
||||
std::string copy(data, size);
|
||||
std::transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c) {
|
||||
if('a' <= c && c <= 'z') return c - ('a' - 'A');
|
||||
return (int)c;
|
||||
});
|
||||
return Str(copy);
|
||||
}
|
||||
|
||||
Str Str::escape(bool single_quote) const {
|
||||
SStream ss;
|
||||
escape_(ss, single_quote);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void Str::escape_(SStream& ss, bool single_quote) const {
|
||||
ss << (single_quote ? '\'' : '"');
|
||||
for(int i = 0; i < length(); i++) {
|
||||
char c = this->operator[] (i);
|
||||
switch(c) {
|
||||
case '"':
|
||||
if(!single_quote) ss << '\\';
|
||||
ss << '"';
|
||||
break;
|
||||
case '\'':
|
||||
if(single_quote) ss << '\\';
|
||||
ss << '\'';
|
||||
break;
|
||||
case '\\': ss << '\\' << '\\'; break;
|
||||
case '\n': ss << "\\n"; break;
|
||||
case '\r': ss << "\\r"; break;
|
||||
case '\t': ss << "\\t"; break;
|
||||
case '\b': ss << "\\b"; break;
|
||||
default:
|
||||
if('\x00' <= c && c <= '\x1f') {
|
||||
ss << "\\x"; // << std::hex << std::setw(2) << std::setfill('0') << (int)c;
|
||||
ss << PK_HEX_TABLE[c >> 4];
|
||||
ss << PK_HEX_TABLE[c & 0xf];
|
||||
} else {
|
||||
ss << c;
|
||||
}
|
||||
}
|
||||
}
|
||||
ss << (single_quote ? '\'' : '"');
|
||||
}
|
||||
|
||||
int Str::index(const Str& sub, int start) const {
|
||||
auto p = std::search(data + start, data + size, sub.data, sub.data + sub.size);
|
||||
if(p == data + size) return -1;
|
||||
return p - data;
|
||||
}
|
||||
|
||||
Str Str::replace(char old, char new_) const {
|
||||
Str copied = *this;
|
||||
for(int i = 0; i < copied.size; i++) {
|
||||
if(copied.data[i] == old) copied.data[i] = new_;
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
||||
Str Str::replace(const Str& old, const Str& new_, int count) const {
|
||||
SStream ss;
|
||||
int start = 0;
|
||||
while(true) {
|
||||
int i = index(old, start);
|
||||
if(i == -1) break;
|
||||
ss << substr(start, i - start);
|
||||
ss << new_;
|
||||
start = i + old.size;
|
||||
if(count != -1 && --count == 0) break;
|
||||
}
|
||||
ss << substr(start, size - start);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int Str::_unicode_index_to_byte(int i) const {
|
||||
if(is_ascii) return i;
|
||||
int j = 0;
|
||||
while(i > 0) {
|
||||
j += utf8len(data[j]);
|
||||
i--;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
int Str::_byte_index_to_unicode(int n) const {
|
||||
if(is_ascii) return n;
|
||||
int cnt = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
if((data[i] & 0xC0) != 0x80) cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
Str Str::u8_getitem(int i) const {
|
||||
i = _unicode_index_to_byte(i);
|
||||
return substr(i, utf8len(data[i]));
|
||||
}
|
||||
|
||||
Str Str::u8_slice(int start, int stop, int step) const {
|
||||
SStream ss;
|
||||
if(is_ascii) {
|
||||
PK_SLICE_LOOP(i, start, stop, step) ss << data[i];
|
||||
} else {
|
||||
PK_SLICE_LOOP(i, start, stop, step) ss << u8_getitem(i);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int Str::u8_length() const { return _byte_index_to_unicode(size); }
|
||||
|
||||
vector<std::string_view> Str::split(const Str& sep) const {
|
||||
vector<std::string_view> result;
|
||||
std::string_view tmp;
|
||||
int start = 0;
|
||||
while(true) {
|
||||
int i = index(sep, start);
|
||||
if(i == -1) break;
|
||||
tmp = sv().substr(start, i - start);
|
||||
if(!tmp.empty()) result.push_back(tmp);
|
||||
start = i + sep.size;
|
||||
}
|
||||
tmp = sv().substr(start, size - start);
|
||||
if(!tmp.empty()) result.push_back(tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<std::string_view> Str::split(char sep) const {
|
||||
vector<std::string_view> result;
|
||||
int i = 0;
|
||||
for(int j = 0; j < size; j++) {
|
||||
if(data[j] == sep) {
|
||||
if(j > i) result.emplace_back(data + i, j - i);
|
||||
i = j + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(size > i) result.emplace_back(data + i, size - i);
|
||||
return result;
|
||||
}
|
||||
|
||||
int Str::count(const Str& sub) const {
|
||||
if(sub.empty()) return size + 1;
|
||||
int cnt = 0;
|
||||
int start = 0;
|
||||
while(true) {
|
||||
int i = index(sub, start);
|
||||
if(i == -1) break;
|
||||
cnt++;
|
||||
start = i + sub.size;
|
||||
}
|
||||
return cnt;
|
||||
assert(_ptr[size] == '\0');
|
||||
}
|
||||
|
||||
static std::map<std::string_view, uint16_t>& _interned() {
|
||||
@ -388,136 +54,6 @@ StrName StrName::get(std::string_view s) {
|
||||
return StrName(index);
|
||||
}
|
||||
|
||||
Str SStream::str() {
|
||||
// after this call, the buffer is no longer valid
|
||||
buffer.push_back('\0');
|
||||
auto detached = buffer.detach();
|
||||
detached.second--; // remove the last '\0'
|
||||
return Str(detached);
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (const Str& s) {
|
||||
for(char c: s)
|
||||
buffer.push_back(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (const char* s) {
|
||||
while(*s)
|
||||
buffer.push_back(*s++);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (const std::string& s) {
|
||||
for(char c: s)
|
||||
buffer.push_back(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (std::string_view s) {
|
||||
for(char c: s)
|
||||
buffer.push_back(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (char c) {
|
||||
buffer.push_back(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (StrName sn) { return *this << sn.sv(); }
|
||||
|
||||
SStream& SStream::operator<< (size_t val) {
|
||||
// size_t could be out of range of `i64`, use `std::to_string` instead
|
||||
return (*this) << std::to_string(val);
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (int val) { return (*this) << static_cast<i64>(val); }
|
||||
|
||||
SStream& SStream::operator<< (i64 val) {
|
||||
// str(-2**64).__len__() == 21
|
||||
buffer.reserve(buffer.size() + 24);
|
||||
if(val == 0) {
|
||||
buffer.push_back('0');
|
||||
return *this;
|
||||
}
|
||||
if(val < 0) {
|
||||
buffer.push_back('-');
|
||||
val = -val;
|
||||
}
|
||||
auto begin = buffer.end();
|
||||
while(val) {
|
||||
buffer.push_back('0' + val % 10);
|
||||
val /= 10;
|
||||
}
|
||||
std::reverse(begin, buffer.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (f64 val) {
|
||||
if(std::isinf(val)) { return (*this) << (val > 0 ? "inf" : "-inf"); }
|
||||
if(std::isnan(val)) { return (*this) << "nan"; }
|
||||
char b[32];
|
||||
if(_precision == -1) {
|
||||
int prec = std::numeric_limits<f64>::max_digits10 - 1;
|
||||
snprintf(b, sizeof(b), "%.*g", prec, val);
|
||||
} else {
|
||||
int prec = _precision;
|
||||
snprintf(b, sizeof(b), "%.*f", prec, val);
|
||||
}
|
||||
(*this) << b;
|
||||
if(std::all_of(b + 1, b + strlen(b), isdigit)) (*this) << ".0";
|
||||
return *this;
|
||||
}
|
||||
|
||||
void SStream::write_hex(unsigned char c, bool non_zero) {
|
||||
unsigned char high = c >> 4;
|
||||
unsigned char low = c & 0xf;
|
||||
if(non_zero) {
|
||||
if(high) (*this) << PK_HEX_TABLE[high];
|
||||
if(high || low) (*this) << PK_HEX_TABLE[low];
|
||||
} else {
|
||||
(*this) << PK_HEX_TABLE[high];
|
||||
(*this) << PK_HEX_TABLE[low];
|
||||
}
|
||||
}
|
||||
|
||||
void SStream::write_hex(void* p) {
|
||||
if(p == nullptr) {
|
||||
(*this) << "0x0";
|
||||
return;
|
||||
}
|
||||
(*this) << "0x";
|
||||
uintptr_t p_t = reinterpret_cast<uintptr_t>(p);
|
||||
bool non_zero = true;
|
||||
for(int i = sizeof(void*) - 1; i >= 0; i--) {
|
||||
unsigned char cpnt = (p_t >> (i * 8)) & 0xff;
|
||||
write_hex(cpnt, non_zero);
|
||||
if(cpnt != 0) non_zero = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SStream::write_hex(i64 val) {
|
||||
if(val == 0) {
|
||||
(*this) << "0x0";
|
||||
return;
|
||||
}
|
||||
if(val < 0) {
|
||||
(*this) << "-";
|
||||
val = -val;
|
||||
}
|
||||
(*this) << "0x";
|
||||
bool non_zero = true;
|
||||
for(int i = 56; i >= 0; i -= 8) {
|
||||
unsigned char cpnt = (val >> i) & 0xff;
|
||||
write_hex(cpnt, non_zero);
|
||||
if(cpnt != 0) non_zero = false;
|
||||
}
|
||||
}
|
||||
|
||||
#undef PK_STR_ALLOCATE
|
||||
#undef PK_STR_COPY_INIT
|
||||
|
||||
// unary operators
|
||||
const StrName __repr__ = StrName::get("__repr__");
|
||||
const StrName __str__ = StrName::get("__str__");
|
||||
|
9
src/common/utils.c
Normal file
9
src/common/utils.c
Normal file
@ -0,0 +1,9 @@
|
||||
const char* kPlatformStrings[] = {
|
||||
"win32", // 0
|
||||
"emscripten", // 1
|
||||
"ios", // 2
|
||||
"darwin", // 3
|
||||
"android", // 4
|
||||
"linux", // 5
|
||||
"unknown" // 6
|
||||
};
|
65
src/common/vector.c
Normal file
65
src/common/vector.c
Normal file
@ -0,0 +1,65 @@
|
||||
#include "pocketpy/common/vector.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void c11_array__ctor(c11_array* self, int elem_size, int count){
|
||||
self->data = malloc(elem_size * count);
|
||||
self->count = count;
|
||||
self->elem_size = elem_size;
|
||||
}
|
||||
|
||||
void c11_array__dtor(c11_array* self){
|
||||
free(self->data);
|
||||
self->data = NULL;
|
||||
self->count = 0;
|
||||
}
|
||||
|
||||
c11_array c11_array__copy(const c11_array* self){
|
||||
c11_array retval;
|
||||
c11_array__ctor(&retval, self->elem_size, self->count);
|
||||
memcpy(retval.data, self->data, self->elem_size * self->count);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void* c11_array__at(c11_array* self, int index){
|
||||
return (char*)self->data + self->elem_size * index;
|
||||
}
|
||||
|
||||
void c11_vector__ctor(c11_vector* self, int elem_size){
|
||||
self->data = NULL;
|
||||
self->count = 0;
|
||||
self->capacity = 0;
|
||||
self->elem_size = elem_size;
|
||||
}
|
||||
|
||||
void c11_vector__dtor(c11_vector* self){
|
||||
if(self->data) free(self->data);
|
||||
self->data = NULL;
|
||||
self->count = 0;
|
||||
self->capacity = 0;
|
||||
}
|
||||
|
||||
c11_vector c11_vector__copy(const c11_vector* self){
|
||||
c11_vector retval;
|
||||
c11_vector__ctor(&retval, self->elem_size);
|
||||
c11_vector__reserve(&retval, self->capacity);
|
||||
memcpy(retval.data, self->data, self->elem_size * self->count);
|
||||
retval.count = self->count;
|
||||
return retval;
|
||||
}
|
||||
|
||||
void* c11_vector__at(c11_vector* self, int index){
|
||||
return (char*)self->data + self->elem_size * index;
|
||||
}
|
||||
|
||||
void c11_vector__reserve(c11_vector* self, int capacity){
|
||||
if(capacity < 4) capacity = 4;
|
||||
if(capacity <= self->capacity) return;
|
||||
self->capacity = capacity;
|
||||
self->data = realloc(self->data, self->elem_size * self->capacity);
|
||||
}
|
||||
|
||||
void c11_vector__clear(c11_vector* self){
|
||||
self->count = 0;
|
||||
}
|
@ -20,7 +20,7 @@ NameScope Compiler::name_scope() const noexcept{
|
||||
}
|
||||
|
||||
CodeObject_ Compiler::push_global_context() noexcept{
|
||||
CodeObject_ co = std::make_shared<CodeObject>(lexer.src, lexer.src->filename);
|
||||
CodeObject_ co = std::make_shared<CodeObject>(lexer.src, static_cast<const Str&>(lexer.src->filename));
|
||||
co->start_line = __i == 0 ? 1 : prev().line;
|
||||
contexts.push_back(CodeEmitContext(vm, co, contexts.size()));
|
||||
return co;
|
||||
@ -1293,8 +1293,8 @@ Error* Compiler::compile(CodeObject_* out) noexcept{
|
||||
Error* err;
|
||||
check(lexer.run());
|
||||
|
||||
// if(lexer.src->filename[0] != '<'){
|
||||
// printf("%s\n", lexer.src->filename.c_str());
|
||||
// if(lexer.src.filename()[0] != '<'){
|
||||
// printf("%s\n", lexer.src.filename().c_str());
|
||||
// for(int i=0; i<lexer.nexts.size(); i++){
|
||||
// printf("%s: %s\n", TK_STR(tk(i).type), tk(i).str().escape().c_str());
|
||||
// }
|
||||
|
@ -414,11 +414,11 @@ void FStringExpr::_load_simple_expr(CodeEmitContext* ctx, Str expr) {
|
||||
switch(expr.end()[-1]) {
|
||||
case 'r':
|
||||
repr = true;
|
||||
expr = expr.substr(0, expr.size - 2);
|
||||
expr = expr.slice(0, expr.size - 2);
|
||||
break;
|
||||
case 's':
|
||||
repr = false;
|
||||
expr = expr.substr(0, expr.size - 2);
|
||||
expr = expr.slice(0, expr.size - 2);
|
||||
break;
|
||||
default: break; // nothing happens
|
||||
}
|
||||
@ -472,7 +472,7 @@ void FStringExpr::emit_(CodeEmitContext* ctx) {
|
||||
if(flag) {
|
||||
if(src[j] == '}') {
|
||||
// add expression
|
||||
Str expr = src.substr(i, j - i);
|
||||
Str expr = src.slice(i, j);
|
||||
// BUG: ':' is not a format specifier in f"{stack[2:]}"
|
||||
int conon = expr.index(":");
|
||||
if(conon >= 0) {
|
||||
@ -485,7 +485,7 @@ void FStringExpr::emit_(CodeEmitContext* ctx) {
|
||||
break;
|
||||
}
|
||||
if(ok) {
|
||||
_load_simple_expr(ctx, expr.substr(0, conon));
|
||||
_load_simple_expr(ctx, expr.slice(0, conon));
|
||||
ctx->emit_(OP_FORMAT_STRING, ctx->add_const_string(spec.sv()), line);
|
||||
} else {
|
||||
// ':' is not a spec indicator
|
||||
@ -527,7 +527,7 @@ void FStringExpr::emit_(CodeEmitContext* ctx) {
|
||||
i = j;
|
||||
while(j < src.size && src[j] != '{' && src[j] != '}')
|
||||
j++;
|
||||
Str literal = src.substr(i, j - i);
|
||||
Str literal = src.slice(i, j);
|
||||
ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(literal.sv()), line);
|
||||
count++;
|
||||
continue; // skip j++
|
||||
@ -538,7 +538,7 @@ void FStringExpr::emit_(CodeEmitContext* ctx) {
|
||||
|
||||
if(flag) {
|
||||
// literal
|
||||
Str literal = src.substr(i, src.size - i);
|
||||
Str literal = src.slice(i, src.size);
|
||||
ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(literal.sv()), line);
|
||||
count++;
|
||||
}
|
||||
|
@ -1,16 +1,12 @@
|
||||
#include "pocketpy/compiler/lexer.hpp"
|
||||
#include "pocketpy/common/gil.hpp"
|
||||
#include "pocketpy/common/version.h"
|
||||
#include "pocketpy/common/str.h"
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
// clang-format off
|
||||
static const uint32_t kLoRangeA[] = {170,186,443,448,660,1488,1519,1568,1601,1646,1649,1749,1774,1786,1791,1808,1810,1869,1969,1994,2048,2112,2144,2208,2230,2308,2365,2384,2392,2418,2437,2447,2451,2474,2482,2486,2493,2510,2524,2527,2544,2556,2565,2575,2579,2602,2610,2613,2616,2649,2654,2674,2693,2703,2707,2730,2738,2741,2749,2768,2784,2809,2821,2831,2835,2858,2866,2869,2877,2908,2911,2929,2947,2949,2958,2962,2969,2972,2974,2979,2984,2990,3024,3077,3086,3090,3114,3133,3160,3168,3200,3205,3214,3218,3242,3253,3261,3294,3296,3313,3333,3342,3346,3389,3406,3412,3423,3450,3461,3482,3507,3517,3520,3585,3634,3648,3713,3716,3718,3724,3749,3751,3762,3773,3776,3804,3840,3904,3913,3976,4096,4159,4176,4186,4193,4197,4206,4213,4238,4352,4682,4688,4696,4698,4704,4746,4752,4786,4792,4800,4802,4808,4824,4882,4888,4992,5121,5743,5761,5792,5873,5888,5902,5920,5952,5984,5998,6016,6108,6176,6212,6272,6279,6314,6320,6400,6480,6512,6528,6576,6656,6688,6917,6981,7043,7086,7098,7168,7245,7258,7401,7406,7413,7418,8501,11568,11648,11680,11688,11696,11704,11712,11720,11728,11736,12294,12348,12353,12447,12449,12543,12549,12593,12704,12784,13312,19968,40960,40982,42192,42240,42512,42538,42606,42656,42895,42999,43003,43011,43015,43020,43072,43138,43250,43259,43261,43274,43312,43360,43396,43488,43495,43514,43520,43584,43588,43616,43633,43642,43646,43697,43701,43705,43712,43714,43739,43744,43762,43777,43785,43793,43808,43816,43968,44032,55216,55243,63744,64112,64285,64287,64298,64312,64318,64320,64323,64326,64467,64848,64914,65008,65136,65142,65382,65393,65440,65474,65482,65490,65498,65536,65549,65576,65596,65599,65616,65664,66176,66208,66304,66349,66370,66384,66432,66464,66504,66640,66816,66864,67072,67392,67424,67584,67592,67594,67639,67644,67647,67680,67712,67808,67828,67840,67872,67968,68030,68096,68112,68117,68121,68192,68224,68288,68297,68352,68416,68448,68480,68608,68864,69376,69415,69424,69600,69635,69763,69840,69891,69956,69968,70006,70019,70081,70106,70108,70144,70163,70272,70280,70282,70287,70303,70320,70405,70415,70419,70442,70450,70453,70461,70480,70493,70656,70727,70751,70784,70852,70855,71040,71128,71168,71236,71296,71352,71424,71680,71935,72096,72106,72161,72163,72192,72203,72250,72272,72284,72349,72384,72704,72714,72768,72818,72960,72968,72971,73030,73056,73063,73066,73112,73440,73728,74880,77824,82944,92160,92736,92880,92928,93027,93053,93952,94032,94208,100352,110592,110928,110948,110960,113664,113776,113792,113808,123136,123214,123584,124928,126464,126469,126497,126500,126503,126505,126516,126521,126523,126530,126535,126537,126539,126541,126545,126548,126551,126553,126555,126557,126559,126561,126564,126567,126572,126580,126585,126590,126592,126603,126625,126629,126635,131072,173824,177984,178208,183984,194560};
|
||||
static const uint32_t kLoRangeB[] = {170,186,443,451,660,1514,1522,1599,1610,1647,1747,1749,1775,1788,1791,1808,1839,1957,1969,2026,2069,2136,2154,2228,2237,2361,2365,2384,2401,2432,2444,2448,2472,2480,2482,2489,2493,2510,2525,2529,2545,2556,2570,2576,2600,2608,2611,2614,2617,2652,2654,2676,2701,2705,2728,2736,2739,2745,2749,2768,2785,2809,2828,2832,2856,2864,2867,2873,2877,2909,2913,2929,2947,2954,2960,2965,2970,2972,2975,2980,2986,3001,3024,3084,3088,3112,3129,3133,3162,3169,3200,3212,3216,3240,3251,3257,3261,3294,3297,3314,3340,3344,3386,3389,3406,3414,3425,3455,3478,3505,3515,3517,3526,3632,3635,3653,3714,3716,3722,3747,3749,3760,3763,3773,3780,3807,3840,3911,3948,3980,4138,4159,4181,4189,4193,4198,4208,4225,4238,4680,4685,4694,4696,4701,4744,4749,4784,4789,4798,4800,4805,4822,4880,4885,4954,5007,5740,5759,5786,5866,5880,5900,5905,5937,5969,5996,6000,6067,6108,6210,6264,6276,6312,6314,6389,6430,6509,6516,6571,6601,6678,6740,6963,6987,7072,7087,7141,7203,7247,7287,7404,7411,7414,7418,8504,11623,11670,11686,11694,11702,11710,11718,11726,11734,11742,12294,12348,12438,12447,12538,12543,12591,12686,12730,12799,19893,40943,40980,42124,42231,42507,42527,42539,42606,42725,42895,42999,43009,43013,43018,43042,43123,43187,43255,43259,43262,43301,43334,43388,43442,43492,43503,43518,43560,43586,43595,43631,43638,43642,43695,43697,43702,43709,43712,43714,43740,43754,43762,43782,43790,43798,43814,43822,44002,55203,55238,55291,64109,64217,64285,64296,64310,64316,64318,64321,64324,64433,64829,64911,64967,65019,65140,65276,65391,65437,65470,65479,65487,65495,65500,65547,65574,65594,65597,65613,65629,65786,66204,66256,66335,66368,66377,66421,66461,66499,66511,66717,66855,66915,67382,67413,67431,67589,67592,67637,67640,67644,67669,67702,67742,67826,67829,67861,67897,68023,68031,68096,68115,68119,68149,68220,68252,68295,68324,68405,68437,68466,68497,68680,68899,69404,69415,69445,69622,69687,69807,69864,69926,69956,70002,70006,70066,70084,70106,70108,70161,70187,70278,70280,70285,70301,70312,70366,70412,70416,70440,70448,70451,70457,70461,70480,70497,70708,70730,70751,70831,70853,70855,71086,71131,71215,71236,71338,71352,71450,71723,71935,72103,72144,72161,72163,72192,72242,72250,72272,72329,72349,72440,72712,72750,72768,72847,72966,72969,73008,73030,73061,73064,73097,73112,73458,74649,75075,78894,83526,92728,92766,92909,92975,93047,93071,94026,94032,100343,101106,110878,110930,110951,111355,113770,113788,113800,113817,123180,123214,123627,125124,126467,126495,126498,126500,126503,126514,126519,126521,126523,126530,126535,126537,126539,126543,126546,126548,126551,126553,126555,126557,126559,126562,126564,126570,126578,126583,126588,126590,126601,126619,126627,126633,126651,173782,177972,178205,183969,191456,195101};
|
||||
// clang-format on
|
||||
|
||||
static bool is_possible_number_char(char c) noexcept{
|
||||
switch(c) {
|
||||
// clang-format off
|
||||
@ -24,16 +20,6 @@ static bool is_possible_number_char(char c) noexcept{
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_unicode_Lo_char(uint32_t c) noexcept{
|
||||
// open a hole for carrot
|
||||
if(c == U'🥕') return true;
|
||||
auto index = std::lower_bound(kLoRangeA, kLoRangeA + 476, c) - kLoRangeA;
|
||||
if(c == kLoRangeA[index]) return true;
|
||||
index -= 1;
|
||||
if(index < 0) return false;
|
||||
return c >= kLoRangeA[index] && c <= kLoRangeB[index];
|
||||
}
|
||||
|
||||
bool Lexer::match_n_chars(int n, char c0) noexcept{
|
||||
const char* c = curr_char;
|
||||
for(int i = 0; i < n; i++) {
|
||||
@ -98,7 +84,7 @@ char Lexer::eatchar_include_newline() noexcept{
|
||||
curr_char++;
|
||||
if(c == '\n') {
|
||||
current_line++;
|
||||
src->line_starts.push_back(curr_char);
|
||||
c11_vector__push(const char*, &src->line_starts, curr_char);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@ -107,7 +93,7 @@ Error* Lexer::eat_name() noexcept{
|
||||
curr_char--;
|
||||
while(true) {
|
||||
unsigned char c = peekchar();
|
||||
int u8bytes = utf8len(c, true);
|
||||
int u8bytes = c11__u8_header(c, true);
|
||||
if(u8bytes == 0) return SyntaxError("invalid char: %c", c);
|
||||
if(u8bytes == 1) {
|
||||
if(isalpha(c) || c == '_' || isdigit(c)) {
|
||||
@ -134,7 +120,7 @@ Error* Lexer::eat_name() noexcept{
|
||||
value |= (b & 0b00111111) << (6 * (u8bytes - k - 1));
|
||||
}
|
||||
}
|
||||
if(is_unicode_Lo_char(value))
|
||||
if(c11__is_unicode_Lo_char(value))
|
||||
curr_char += u8bytes;
|
||||
else
|
||||
break;
|
||||
@ -160,7 +146,7 @@ Error* Lexer::eat_name() noexcept{
|
||||
const auto KW_BEGIN = kTokens + TK("False");
|
||||
const auto KW_END = kTokens + kTokenCount;
|
||||
|
||||
auto it = std::lower_bound(KW_BEGIN, KW_END, name);
|
||||
auto it = lower_bound(KW_BEGIN, KW_END, name);
|
||||
if(it != KW_END && *it == name) {
|
||||
add_token(it - kTokens);
|
||||
} else {
|
||||
@ -533,8 +519,8 @@ Error* Lexer::SyntaxError(const char* fmt, ...) noexcept{
|
||||
}
|
||||
|
||||
Lexer::Lexer(VM* vm, std::shared_ptr<SourceData> src) noexcept : vm(vm), src(src){
|
||||
this->token_start = src->source.c_str();
|
||||
this->curr_char = src->source.c_str();
|
||||
this->token_start = pkpy_Str__data(&src->source);
|
||||
this->curr_char = pkpy_Str__data(&src->source);
|
||||
}
|
||||
|
||||
Error* Lexer::run() noexcept{
|
||||
@ -556,7 +542,7 @@ Error* Lexer::run() noexcept{
|
||||
}
|
||||
|
||||
Error* Lexer::from_precompiled() noexcept{
|
||||
TokenDeserializer deserializer(src->source.c_str());
|
||||
TokenDeserializer deserializer(pkpy_Str__data(&src->source));
|
||||
deserializer.curr += 5; // skip "pkpy:"
|
||||
std::string_view version = deserializer.read_string('\n');
|
||||
|
||||
@ -568,9 +554,9 @@ Error* Lexer::from_precompiled() noexcept{
|
||||
}
|
||||
|
||||
int count = deserializer.read_count();
|
||||
vector<Str>& precompiled_tokens = src->_precompiled_tokens;
|
||||
auto precompiled_tokens = &src->_precompiled_tokens;
|
||||
for(int i = 0; i < count; i++) {
|
||||
precompiled_tokens.push_back(deserializer.read_string('\n'));
|
||||
c11_vector__push(Str, precompiled_tokens, Str(deserializer.read_string('\n')));
|
||||
}
|
||||
|
||||
count = deserializer.read_count();
|
||||
@ -579,8 +565,8 @@ Error* Lexer::from_precompiled() noexcept{
|
||||
t.type = (unsigned char)deserializer.read_uint(',');
|
||||
if(is_raw_string_used(t.type)) {
|
||||
i64 index = deserializer.read_uint(',');
|
||||
t.start = precompiled_tokens[index].c_str();
|
||||
t.length = precompiled_tokens[index].size;
|
||||
t.start = c11__getitem(Str, precompiled_tokens, index).c_str();
|
||||
t.length = c11__getitem(Str, precompiled_tokens, index).size;
|
||||
} else {
|
||||
t.start = nullptr;
|
||||
t.length = 0;
|
||||
|
@ -446,7 +446,7 @@ PyVar VM::__run_top_frame() {
|
||||
case OP_BUILD_BYTES: {
|
||||
const Str& s = CAST(Str&, TOP());
|
||||
unsigned char* p = (unsigned char*)std::malloc(s.size);
|
||||
std::memcpy(p, s.data, s.size);
|
||||
std::memcpy(p, s.c_str(), s.size);
|
||||
TOP() = VAR(Bytes(p, s.size));
|
||||
}
|
||||
DISPATCH()
|
||||
|
@ -49,9 +49,9 @@ void StringIter::_register(VM* vm, PyObject* mod, PyObject* type) {
|
||||
Str& s = PK_OBJ_GET(Str, self.ref);
|
||||
if(self.i == s.size) return 0;
|
||||
int start = self.i;
|
||||
int len = utf8len(s.data[self.i]);
|
||||
int len = c11__u8_header(s[self.i], false);
|
||||
self.i += len;
|
||||
vm->s_data.push(VAR(s.substr(start, len)));
|
||||
vm->s_data.push(VAR(s.slice(start, self.i)));
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
@ -117,12 +117,13 @@ void DictItemsIter::_register(VM* vm, PyObject* mod, PyObject* type) {
|
||||
});
|
||||
vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
|
||||
DictItemsIter& self = _CAST(DictItemsIter&, _0);
|
||||
Dict& d = PK_OBJ_GET(Dict, self.ref);
|
||||
if(self.i == -1) return 0;
|
||||
vm->s_data.push(d._items[self.i].first);
|
||||
vm->s_data.push(d._items[self.i].second);
|
||||
self.i = d._items[self.i].next;
|
||||
PyVar key, val;
|
||||
if (pkpy_DictIter__next(&self.it, (pkpy_Var*)(&key), (pkpy_Var*)(&val))) {
|
||||
vm->s_data.push(key);
|
||||
vm->s_data.push(val);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ void LineProfiler::begin() { frames.clear(); }
|
||||
void LineProfiler::_step(int callstack_size, Frame* frame) {
|
||||
auto line_info = frame->co->lines[frame->ip()];
|
||||
if(line_info.is_virtual) return;
|
||||
std::string_view filename = frame->co->src->filename.sv();
|
||||
std::string_view filename = frame->co->src.filename().sv();
|
||||
int line = line_info.lineno;
|
||||
|
||||
if(frames.empty()) {
|
||||
@ -87,7 +87,7 @@ Str LineProfiler::stats() {
|
||||
int start_line = decl->code->start_line;
|
||||
int end_line = decl->code->end_line;
|
||||
if(start_line == -1 || end_line == -1) continue;
|
||||
std::string_view filename = decl->code->src->filename.sv();
|
||||
std::string_view filename = decl->code->src.filename().sv();
|
||||
const _LineRecord* file_records = records[filename];
|
||||
clock_t total_time = 0;
|
||||
for(int line = start_line; line <= end_line; line++) {
|
||||
|
@ -4,6 +4,10 @@
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
#if PK_DEBUG_CEVAL_STEP
|
||||
#include <map>
|
||||
#endif
|
||||
|
||||
const static char* OP_NAMES[] = {
|
||||
#define OPCODE(name) #name,
|
||||
#include "pocketpy/opcodes.h"
|
||||
@ -38,7 +42,7 @@ struct JsonSerializer {
|
||||
if(!is_type(k, VM::tp_str)) {
|
||||
vm->TypeError(_S("json keys must be string, got ", _type_name(vm, vm->_tp(k))));
|
||||
}
|
||||
ss << _CAST(Str&, k).escape(false) << ": ";
|
||||
ss << _CAST(Str&, k).escape('"') << ": ";
|
||||
write_object(v);
|
||||
});
|
||||
ss << '}';
|
||||
@ -57,7 +61,7 @@ struct JsonSerializer {
|
||||
} else if(obj_t == vm->tp_bool) {
|
||||
ss << (obj == vm->True ? "true" : "false");
|
||||
} else if(obj_t == vm->tp_str) {
|
||||
_CAST(Str&, obj).escape_(ss, false);
|
||||
ss << _CAST(Str&, obj).escape('"');
|
||||
} else if(obj_t == vm->tp_list) {
|
||||
write_array<List>(_CAST(List&, obj));
|
||||
} else if(obj_t == vm->tp_tuple) {
|
||||
@ -117,7 +121,7 @@ Str VM::py_repr(PyVar obj) {
|
||||
}
|
||||
|
||||
Str VM::py_json(PyVar obj) {
|
||||
auto j = JsonSerializer(this, obj);
|
||||
JsonSerializer j(this, obj);
|
||||
return j.serialize();
|
||||
}
|
||||
|
||||
@ -623,7 +627,7 @@ PyVar VM::__format_object(PyVar obj, Str spec) {
|
||||
case 'd':
|
||||
case 's':
|
||||
type = spec.end()[-1];
|
||||
spec = spec.substr(0, spec.length() - 1);
|
||||
spec = spec.slice(0, spec.length() - 1);
|
||||
break;
|
||||
default: type = ' '; break;
|
||||
}
|
||||
@ -660,9 +664,9 @@ PyVar VM::__format_object(PyVar obj, Str spec) {
|
||||
if(dot == 0) {
|
||||
width = -1;
|
||||
} else {
|
||||
width = std::stoi(spec.substr(0, dot).str());
|
||||
width = std::stoi(spec.slice(0, dot).str());
|
||||
}
|
||||
precision = std::stoi(spec.substr(dot + 1).str());
|
||||
precision = std::stoi(spec.slice(dot + 1).str());
|
||||
} else {
|
||||
width = std::stoi(spec.str());
|
||||
precision = -1;
|
||||
@ -761,7 +765,7 @@ static std::string _opcode_argstr(VM* vm, int i, Bytecode byte, const CodeObject
|
||||
|
||||
Str VM::disassemble(CodeObject_ co) {
|
||||
auto pad = [](const Str& s, const int n) {
|
||||
if(s.length() >= n) return s.substr(0, n);
|
||||
if(s.length() >= n) return s.slice(0, n);
|
||||
return s + std::string(n - s.length(), ' ');
|
||||
};
|
||||
|
||||
@ -808,7 +812,7 @@ Str VM::disassemble(CodeObject_ co) {
|
||||
|
||||
#if PK_DEBUG_CEVAL_STEP
|
||||
void VM::__log_s_data(const char* title) {
|
||||
if(_main == nullptr) return;
|
||||
// if(_main == nullptr) return;
|
||||
if(callstack.empty()) return;
|
||||
SStream ss;
|
||||
if(title) ss << title << " | ";
|
||||
@ -1624,37 +1628,6 @@ BIND_BINARY_SPECIAL(__xor__)
|
||||
|
||||
#undef BIND_BINARY_SPECIAL
|
||||
|
||||
void Dict::_probe_0(VM* vm, PyVar key, bool& ok, int& i) const {
|
||||
ok = false;
|
||||
i64 hash = vm->py_hash(key);
|
||||
i = hash & _mask;
|
||||
for(int j = 0; j < _capacity; j++) {
|
||||
if(_items[i].first != nullptr) {
|
||||
if(vm->py_eq(_items[i].first, key)) {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(_items[i].second == nullptr) break;
|
||||
}
|
||||
// https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166
|
||||
i = ((5 * i) + 1) & _mask;
|
||||
}
|
||||
}
|
||||
|
||||
void Dict::_probe_1(VM* vm, PyVar key, bool& ok, int& i) const {
|
||||
ok = false;
|
||||
i = vm->py_hash(key) & _mask;
|
||||
while(_items[i].first != nullptr) {
|
||||
if(vm->py_eq(_items[i].first, key)) {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
// https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166
|
||||
i = ((5 * i) + 1) & _mask;
|
||||
}
|
||||
}
|
||||
|
||||
#if PK_ENABLE_PROFILER
|
||||
void NextBreakpoint::_step(VM* vm) {
|
||||
int curr_callstack_size = vm->callstack.size();
|
||||
@ -1706,7 +1679,7 @@ void VM::__breakpoint() {
|
||||
SStream ss;
|
||||
Frame* frame = &frames[i]->frame;
|
||||
int lineno = frame->curr_lineno();
|
||||
ss << "File \"" << frame->co->src->filename << "\", line " << lineno;
|
||||
ss << "File \"" << frame->co->src.filename() << "\", line " << lineno;
|
||||
if(frame->_callable) {
|
||||
ss << ", in ";
|
||||
ss << frame->_callable->as<Function>().decl->code->name;
|
||||
|
@ -85,7 +85,7 @@ void FileIO::_register(VM* vm, PyObject* mod, PyObject* type) {
|
||||
FileIO& io = PK_OBJ_GET(FileIO, args[0]);
|
||||
if(io.is_text) {
|
||||
Str& s = CAST(Str&, args[1]);
|
||||
fwrite(s.data, 1, s.length(), io.fp);
|
||||
fwrite(s.c_str(), 1, s.length(), io.fp);
|
||||
} else {
|
||||
Bytes& buffer = CAST(Bytes&, args[1]);
|
||||
fwrite(buffer.data(), 1, buffer.size(), io.fp);
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "pocketpy/common/version.h"
|
||||
#include "pocketpy/common/export.h"
|
||||
|
||||
#include "pocketpy/common/_generated.hpp"
|
||||
#include "pocketpy/common/_generated.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
@ -199,7 +199,7 @@ struct Random {
|
||||
List result(k);
|
||||
for(int i = 0; i < k; i++) {
|
||||
f64 r = self.gen.uniform(0.0, cum_weights[size - 1]);
|
||||
int idx = std::lower_bound(cum_weights.begin(), cum_weights.end(), r) - cum_weights.begin();
|
||||
int idx = c11__lower_bound_double(r, cum_weights.begin(), cum_weights.size()) - cum_weights.begin();
|
||||
result[i] = data[idx];
|
||||
}
|
||||
return VAR(std::move(result));
|
||||
|
260
src/objects/dict.c
Normal file
260
src/objects/dict.c
Normal file
@ -0,0 +1,260 @@
|
||||
#include "pocketpy/objects/dict.h"
|
||||
#include "pocketpy/common/utils.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DICT_MAX_LOAD 0.75
|
||||
#define DICT_HASH_NEXT(h) ((h) * 5 + 1)
|
||||
#define DICT_HASH_TRANS(h) ((int)((h) & 0xffffffff)) // used for tansform value from __hash__
|
||||
#define PK_DICT_COMPACT_MODE 1
|
||||
|
||||
struct pkpy_DictEntry {
|
||||
pkpy_Var key;
|
||||
pkpy_Var val;
|
||||
};
|
||||
|
||||
inline extern int pkpy_Dict__idx_size(const pkpy_Dict* self) {
|
||||
#if PK_DICT_COMPACT_MODE
|
||||
if(self->_htcap < 255) return 1;
|
||||
if(self->_htcap < 65535) return 2;
|
||||
#endif
|
||||
return 4;
|
||||
}
|
||||
|
||||
inline extern int pkpy_Dict__idx_null(const pkpy_Dict* self) {
|
||||
#if PK_DICT_COMPACT_MODE
|
||||
if(self->_htcap < 255) return 255;
|
||||
if(self->_htcap < 65535) return 65535;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline extern int pkpy_Dict__ht_byte_size(const pkpy_Dict* self) { return self->_htcap * pkpy_Dict__idx_size(self); }
|
||||
|
||||
void pkpy_Dict__ctor(pkpy_Dict* self) {
|
||||
self->count = 0;
|
||||
c11_vector__ctor(&self->_entries, sizeof(struct pkpy_DictEntry));
|
||||
self->_htcap = 16;
|
||||
self->_hashtable = malloc(pkpy_Dict__ht_byte_size(self));
|
||||
memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self));
|
||||
}
|
||||
|
||||
void pkpy_Dict__dtor(pkpy_Dict* self) {
|
||||
c11_vector__dtor(&self->_entries);
|
||||
free(self->_hashtable);
|
||||
}
|
||||
|
||||
pkpy_Dict pkpy_Dict__copy(const pkpy_Dict* self) {
|
||||
int ht_size = pkpy_Dict__ht_byte_size(self);
|
||||
void* ht_clone = malloc(ht_size);
|
||||
memcpy(ht_clone, self->_hashtable, ht_size);
|
||||
return (pkpy_Dict){.count = self->count,
|
||||
._entries = c11_vector__copy(&self->_entries),
|
||||
._htcap = self->_htcap,
|
||||
._hashtable = ht_clone};
|
||||
}
|
||||
|
||||
static int pkpy_Dict__htget(const pkpy_Dict* self, int h) {
|
||||
#if PK_DICT_COMPACT_MODE
|
||||
const int *p = (const int*)(((const char*)self->_hashtable) + h * pkpy_Dict__idx_size(self));
|
||||
return (*p) & pkpy_Dict__idx_null(self);
|
||||
#else
|
||||
return ((const int*)self->_hashtable)[h];
|
||||
#endif
|
||||
}
|
||||
|
||||
static void pkpy_Dict__htset(pkpy_Dict* self, int h, int v) {
|
||||
#if PK_DICT_COMPACT_MODE
|
||||
int *p = (int*)(((char*)self->_hashtable) + h * pkpy_Dict__idx_size(self));
|
||||
*p = v | (*p & ~pkpy_Dict__idx_null(self));
|
||||
#else
|
||||
((int*)self->_hashtable)[h] = v;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int pkpy_Dict__probe0(const pkpy_Dict* self, void* vm, pkpy_Var key, int hash) {
|
||||
const int null = pkpy_Dict__idx_null(self);
|
||||
const int mask = self->_htcap - 1;
|
||||
for(int h = hash & mask;; h = DICT_HASH_NEXT(h) & mask) {
|
||||
int idx = pkpy_Dict__htget(self, h);
|
||||
if(idx == null) return h;
|
||||
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
|
||||
if(pkpy_Var__is_null(&entry->key)) return h;
|
||||
}
|
||||
PK_UNREACHABLE();
|
||||
}
|
||||
|
||||
static int pkpy_Dict__probe1(const pkpy_Dict* self, void* vm, pkpy_Var key, int hash) {
|
||||
const int null = pkpy_Dict__idx_null(self);
|
||||
const int mask = self->_htcap - 1;
|
||||
for(int h = hash & mask;; h = DICT_HASH_NEXT(h) & mask) {
|
||||
int idx = pkpy_Dict__htget(self, h);
|
||||
if(idx == null) return h;
|
||||
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
|
||||
if(pkpy_Var__is_null(&entry->key)) continue;
|
||||
if(pkpy_Var__eq__(vm, entry->key, key)) return h;
|
||||
}
|
||||
PK_UNREACHABLE();
|
||||
}
|
||||
|
||||
static void pkpy_Dict__extendht(pkpy_Dict* self, void* vm) {
|
||||
free(self->_hashtable);
|
||||
self->_htcap *= 2;
|
||||
self->_hashtable = malloc(pkpy_Dict__ht_byte_size(self));
|
||||
memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self));
|
||||
|
||||
for(int i = 0; i < self->_entries.count; i++) {
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, i);
|
||||
if(pkpy_Var__is_null(&entry->key)) continue;
|
||||
|
||||
int rhash = DICT_HASH_TRANS(pkpy_Var__hash__(vm, entry->key));
|
||||
int h = pkpy_Dict__probe0(self, vm, entry->key, rhash);
|
||||
pkpy_Dict__htset(self, h, i);
|
||||
}
|
||||
}
|
||||
|
||||
bool pkpy_Dict__set(pkpy_Dict* self, void* vm, pkpy_Var key, pkpy_Var val) {
|
||||
int hash = DICT_HASH_TRANS(pkpy_Var__hash__(vm, key));
|
||||
int h = pkpy_Dict__probe1(self, vm, key, hash);
|
||||
|
||||
int idx = pkpy_Dict__htget(self, h);
|
||||
if(idx == pkpy_Dict__idx_null(self)) {
|
||||
idx = self->_entries.count;
|
||||
c11_vector__push(struct pkpy_DictEntry,
|
||||
&self->_entries,
|
||||
((struct pkpy_DictEntry){
|
||||
.key = key,
|
||||
.val = val,
|
||||
}));
|
||||
h = pkpy_Dict__probe0(self, vm, key, hash);
|
||||
pkpy_Dict__htset(self, h, idx);
|
||||
self->count += 1;
|
||||
if(self->count >= self->_htcap * DICT_MAX_LOAD) pkpy_Dict__extendht(self, vm);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
|
||||
|
||||
if(pkpy_Var__eq__(vm, entry->key, key)) {
|
||||
entry->val = val;
|
||||
} else {
|
||||
self->count += 1;
|
||||
h = pkpy_Dict__probe0(self, vm, key, hash);
|
||||
idx = pkpy_Dict__htget(self, h);
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
|
||||
entry->key = key;
|
||||
entry->val = val;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pkpy_Dict__contains(const pkpy_Dict* self, void* vm, pkpy_Var key) {
|
||||
int hash = DICT_HASH_TRANS(pkpy_Var__hash__(vm, key));
|
||||
int h = pkpy_Dict__probe1(self, vm, key, hash);
|
||||
|
||||
int idx = pkpy_Dict__htget(self, h);
|
||||
if(idx == pkpy_Dict__idx_null(self)) return false;
|
||||
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pkpy_Dict__refactor(pkpy_Dict* self, void* vm) {
|
||||
int deleted_slots = self->_entries.count - self->count;
|
||||
if(deleted_slots <= 8 || deleted_slots < self->_entries.count * (1 - DICT_MAX_LOAD)) return false;
|
||||
|
||||
// shrink
|
||||
// free(self->_hashtable);
|
||||
// while(self->_htcap * DICT_MAX_LOAD / 2 > self->count && self->_htcap >= 32)
|
||||
// self->_htcap /= 2;
|
||||
// self->_hashtable = malloc(pkpy_Dict__ht_byte_size(self));
|
||||
memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self));
|
||||
|
||||
int new_cnt = 0;
|
||||
for (int i = 0; i < self->_entries.count; ++i) {
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, i);
|
||||
if(pkpy_Var__is_null(&entry->key)) continue;
|
||||
if (i > new_cnt) c11__setitem(struct pkpy_DictEntry, &self->_entries, new_cnt, *entry);
|
||||
new_cnt += 1;
|
||||
}
|
||||
|
||||
self->_entries.count = new_cnt;
|
||||
for(int i = 0; i < self->_entries.count; i++) {
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, i);
|
||||
if(pkpy_Var__is_null(&entry->key)) continue;
|
||||
|
||||
int rhash = DICT_HASH_TRANS(pkpy_Var__hash__(vm, entry->key));
|
||||
int h = pkpy_Dict__probe0(self, vm, entry->key, rhash);
|
||||
pkpy_Dict__htset(self, h, i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pkpy_Dict__del(pkpy_Dict* self, void* vm, pkpy_Var key) {
|
||||
int hash = DICT_HASH_TRANS(pkpy_Var__hash__(vm, key));
|
||||
int h = pkpy_Dict__probe1(self, vm, key, hash);
|
||||
int idx = pkpy_Dict__htget(self, h), null = pkpy_Dict__idx_null(self);
|
||||
if(idx == null) return false;
|
||||
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
|
||||
pkpy_Var__set_null(&entry->key);
|
||||
self->count -= 1;
|
||||
pkpy_Dict__refactor(self, vm);
|
||||
return true;
|
||||
}
|
||||
|
||||
const pkpy_Var *pkpy_Dict__try_get(const pkpy_Dict* self, void* vm, pkpy_Var key) {
|
||||
int hash = DICT_HASH_TRANS(pkpy_Var__hash__(vm, key));
|
||||
int h = pkpy_Dict__probe1(self, vm, key, hash);
|
||||
|
||||
int idx = pkpy_Dict__htget(self, h);
|
||||
if(idx == pkpy_Dict__idx_null(self)) return NULL;
|
||||
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
|
||||
return &entry->val;
|
||||
}
|
||||
|
||||
void pkpy_Dict__update(pkpy_Dict *self, void *vm, const pkpy_Dict *other) {
|
||||
for(int i = 0; i < other->_entries.count; i++) {
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &other->_entries, i);
|
||||
if(pkpy_Var__is_null(&entry->key)) continue;
|
||||
pkpy_Dict__set(self, vm, entry->key, entry->val);
|
||||
}
|
||||
}
|
||||
|
||||
void pkpy_Dict__clear(pkpy_Dict *self) {
|
||||
self->count = 0;
|
||||
self->_entries.count = 0;
|
||||
memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self));
|
||||
}
|
||||
|
||||
static int pkpy_Dict__next_entry_idx(const pkpy_Dict* self, int idx) {
|
||||
while (idx < self->_entries.count) {
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
|
||||
if(!pkpy_Var__is_null(&entry->key)) break;
|
||||
idx++;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
pkpy_DictIter pkpy_Dict__iter(const pkpy_Dict *self) {
|
||||
return (pkpy_DictIter){
|
||||
._dict = self,
|
||||
._index = pkpy_Dict__next_entry_idx(self, 0),
|
||||
};
|
||||
}
|
||||
|
||||
bool pkpy_DictIter__next(pkpy_DictIter *self, pkpy_Var *key, pkpy_Var *val) {
|
||||
if(self->_index >= self->_dict->_entries.count) return false;
|
||||
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_dict->_entries, self->_index);
|
||||
if(pkpy_Var__is_null(&entry->key)) return false;
|
||||
if (key) *key = entry->key;
|
||||
if (val) *val = entry->val;
|
||||
|
||||
self->_index = pkpy_Dict__next_entry_idx(self->_dict, self->_index + 1);
|
||||
return true;
|
||||
}
|
@ -1,180 +0,0 @@
|
||||
#include "pocketpy/objects/dict.hpp"
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
Dict::Dict() :
|
||||
_capacity(__Capacity), _mask(__Capacity - 1), _size(0), _critical_size(__Capacity * __LoadFactor + 0.5f),
|
||||
_head_idx(-1), _tail_idx(-1) {
|
||||
__alloc_items();
|
||||
}
|
||||
|
||||
void Dict::__alloc_items() {
|
||||
_items = (Item*)std::malloc(_capacity * sizeof(Item));
|
||||
for(int i = 0; i < _capacity; i++) {
|
||||
_items[i].first = nullptr;
|
||||
_items[i].second = nullptr;
|
||||
_items[i].prev = -1;
|
||||
_items[i].next = -1;
|
||||
}
|
||||
}
|
||||
|
||||
Dict::Dict(Dict&& other) {
|
||||
_capacity = other._capacity;
|
||||
_mask = other._mask;
|
||||
_size = other._size;
|
||||
_critical_size = other._critical_size;
|
||||
_head_idx = other._head_idx;
|
||||
_tail_idx = other._tail_idx;
|
||||
_items = other._items;
|
||||
other._items = nullptr;
|
||||
}
|
||||
|
||||
Dict::Dict(const Dict& other) {
|
||||
_capacity = other._capacity;
|
||||
_mask = other._mask;
|
||||
_size = other._size;
|
||||
_critical_size = other._critical_size;
|
||||
_head_idx = other._head_idx;
|
||||
_tail_idx = other._tail_idx;
|
||||
// copy items
|
||||
_items = (Item*)std::malloc(_capacity * sizeof(Item));
|
||||
std::memcpy(_items, other._items, _capacity * sizeof(Item));
|
||||
}
|
||||
|
||||
void Dict::set(VM* vm, PyVar key, PyVar val) {
|
||||
// do possible rehash
|
||||
if(_size + 1 > _critical_size) _rehash(vm);
|
||||
bool ok;
|
||||
int i;
|
||||
_probe_1(vm, key, ok, i);
|
||||
if(!ok) {
|
||||
_size++;
|
||||
_items[i].first = key;
|
||||
|
||||
// append to tail
|
||||
if(_size == 0 + 1) {
|
||||
_head_idx = i;
|
||||
_tail_idx = i;
|
||||
} else {
|
||||
_items[i].prev = _tail_idx;
|
||||
_items[_tail_idx].next = i;
|
||||
_tail_idx = i;
|
||||
}
|
||||
}
|
||||
_items[i].second = val;
|
||||
}
|
||||
|
||||
void Dict::_rehash(VM* vm) {
|
||||
Item* old_items = _items;
|
||||
int old_head_idx = _head_idx;
|
||||
|
||||
_capacity *= 4;
|
||||
_mask = _capacity - 1;
|
||||
_size = 0;
|
||||
_critical_size = _capacity * __LoadFactor + 0.5f;
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
|
||||
__alloc_items();
|
||||
|
||||
// copy old items to new dict
|
||||
int i = old_head_idx;
|
||||
while(i != -1) {
|
||||
set(vm, old_items[i].first, old_items[i].second);
|
||||
i = old_items[i].next;
|
||||
}
|
||||
|
||||
std::free(old_items);
|
||||
}
|
||||
|
||||
PyVar Dict::try_get(VM* vm, PyVar key) const {
|
||||
bool ok;
|
||||
int i;
|
||||
_probe_0(vm, key, ok, i);
|
||||
if(!ok) return nullptr;
|
||||
return _items[i].second;
|
||||
}
|
||||
|
||||
bool Dict::contains(VM* vm, PyVar key) const {
|
||||
bool ok;
|
||||
int i;
|
||||
_probe_0(vm, key, ok, i);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Dict::del(VM* vm, PyVar key) {
|
||||
bool ok;
|
||||
int i;
|
||||
_probe_0(vm, key, ok, i);
|
||||
if(!ok) return false;
|
||||
_items[i].first = nullptr;
|
||||
// _items[i].second = PY_DELETED_SLOT; // do not change .second if it is not NULL, it means the slot is occupied by
|
||||
// a deleted item
|
||||
_size--;
|
||||
|
||||
if(_size == 0) {
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
} else {
|
||||
if(_head_idx == i) {
|
||||
_head_idx = _items[i].next;
|
||||
_items[_head_idx].prev = -1;
|
||||
} else if(_tail_idx == i) {
|
||||
_tail_idx = _items[i].prev;
|
||||
_items[_tail_idx].next = -1;
|
||||
} else {
|
||||
_items[_items[i].prev].next = _items[i].next;
|
||||
_items[_items[i].next].prev = _items[i].prev;
|
||||
}
|
||||
}
|
||||
_items[i].prev = -1;
|
||||
_items[i].next = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Dict::update(VM* vm, const Dict& other) {
|
||||
other.apply([&](PyVar k, PyVar v) {
|
||||
set(vm, k, v);
|
||||
});
|
||||
}
|
||||
|
||||
Tuple Dict::keys() const {
|
||||
Tuple t(_size);
|
||||
int i = _head_idx;
|
||||
int j = 0;
|
||||
while(i != -1) {
|
||||
t[j++] = _items[i].first;
|
||||
i = _items[i].next;
|
||||
}
|
||||
assert(j == _size);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tuple Dict::values() const {
|
||||
Tuple t(_size);
|
||||
int i = _head_idx;
|
||||
int j = 0;
|
||||
while(i != -1) {
|
||||
t[j++] = _items[i].second;
|
||||
i = _items[i].next;
|
||||
}
|
||||
assert(j == _size);
|
||||
return t;
|
||||
}
|
||||
|
||||
void Dict::clear() {
|
||||
_size = 0;
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
for(int i = 0; i < _capacity; i++) {
|
||||
_items[i].first = nullptr;
|
||||
_items[i].second = nullptr;
|
||||
_items[i].prev = -1;
|
||||
_items[i].next = -1;
|
||||
}
|
||||
}
|
||||
|
||||
Dict::~Dict() {
|
||||
if(_items) std::free(_items);
|
||||
}
|
||||
} // namespace pkpy
|
17
src/objects/pyvar.cpp
Normal file
17
src/objects/pyvar.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "pocketpy/objects/base.hpp"
|
||||
#include "pocketpy/objects/pyvar.h"
|
||||
#include "pocketpy/interpreter/vm.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool pkpy_Var__eq__(void* vm_, pkpy_Var a, pkpy_Var b) {
|
||||
auto vm = (pkpy::VM*)(vm_);
|
||||
return vm->py_eq(*(pkpy::PyVar*)(&a), *(pkpy::PyVar*)(&b));
|
||||
}
|
||||
|
||||
int64_t pkpy_Var__hash__(void* vm_, pkpy_Var a) {
|
||||
auto vm = (pkpy::VM*)(vm_);
|
||||
return vm->py_hash(*(pkpy::PyVar*)(&a));
|
||||
}
|
||||
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
#include "pocketpy/objects/sourcedata.hpp"
|
||||
|
||||
namespace pkpy {
|
||||
SourceData::SourceData(std::string_view source, const Str& filename, CompileMode mode) :
|
||||
filename(filename), mode(mode) {
|
||||
int index = 0;
|
||||
// Skip utf8 BOM if there is any.
|
||||
if(strncmp(source.data(), "\xEF\xBB\xBF", 3) == 0) index += 3;
|
||||
// Drop all '\r'
|
||||
SStream ss(source.size() + 1);
|
||||
while(index < source.size()) {
|
||||
if(source[index] != '\r') ss << source[index];
|
||||
index++;
|
||||
}
|
||||
this->source = ss.str();
|
||||
if(this->source.size > 5 && this->source.sv().substr(0, 5) == "pkpy:") {
|
||||
this->is_precompiled = true;
|
||||
} else {
|
||||
this->is_precompiled = false;
|
||||
}
|
||||
line_starts.push_back(this->source.c_str());
|
||||
}
|
||||
|
||||
SourceData::SourceData(const Str& filename, CompileMode mode) : filename(filename), mode(mode) {
|
||||
line_starts.push_back(this->source.c_str());
|
||||
}
|
||||
|
||||
pair<const char*, const char*> SourceData::_get_line(int lineno) const {
|
||||
if(is_precompiled || lineno == -1) return {nullptr, nullptr};
|
||||
lineno -= 1;
|
||||
if(lineno < 0) lineno = 0;
|
||||
const char* _start = line_starts[lineno];
|
||||
const char* i = _start;
|
||||
// max 300 chars
|
||||
while(*i != '\n' && *i != '\0' && i - _start < 300)
|
||||
i++;
|
||||
return {_start, i};
|
||||
}
|
||||
|
||||
std::string_view SourceData::get_line(int lineno) const {
|
||||
auto [_0, _1] = _get_line(lineno);
|
||||
if(_0 && _1) return std::string_view(_0, _1 - _0);
|
||||
return "<?>";
|
||||
}
|
||||
|
||||
Str SourceData::snapshot(int lineno, const char* cursor, std::string_view name) const {
|
||||
SStream ss;
|
||||
ss << " " << "File \"" << filename << "\", line " << lineno;
|
||||
if(!name.empty()) ss << ", in " << name;
|
||||
if(!is_precompiled) {
|
||||
ss << '\n';
|
||||
pair<const char*, const char*> pair = _get_line(lineno);
|
||||
Str line = "<?>";
|
||||
int removed_spaces = 0;
|
||||
if(pair.first && pair.second) {
|
||||
line = Str(pair.first, pair.second - pair.first).lstrip();
|
||||
removed_spaces = pair.second - pair.first - line.length();
|
||||
if(line.empty()) line = "<?>";
|
||||
}
|
||||
ss << " " << line;
|
||||
if(cursor && line != "<?>" && cursor >= pair.first && cursor <= pair.second) {
|
||||
auto column = cursor - pair.first - removed_spaces;
|
||||
if(column >= 0) ss << "\n " << std::string(column, ' ') << "^";
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
} // namespace pkpy
|
@ -1,6 +1,6 @@
|
||||
#include "pocketpy/pocketpy.hpp"
|
||||
|
||||
#include "pocketpy/common/_generated.hpp"
|
||||
#include "pocketpy/common/_generated.h"
|
||||
|
||||
#include "pocketpy/modules/array2d.hpp"
|
||||
#include "pocketpy/modules/base64.hpp"
|
||||
@ -296,7 +296,22 @@ void __init_builtins(VM* _vm) {
|
||||
|
||||
_vm->bind_func(_vm->builtins, "hex", 1, [](VM* vm, ArgsView args) {
|
||||
SStream ss;
|
||||
ss.write_hex(CAST(i64, args[0]));
|
||||
i64 val = CAST(i64, args[0]);
|
||||
if(val == 0) {
|
||||
ss << "0x0";
|
||||
return VAR(ss.str());
|
||||
}
|
||||
if(val < 0) {
|
||||
ss << "-";
|
||||
val = -val;
|
||||
}
|
||||
ss << "0x";
|
||||
bool non_zero = true;
|
||||
for(int i = 56; i >= 0; i -= 8) {
|
||||
unsigned char cpnt = (val >> i) & 0xff;
|
||||
ss.write_hex(cpnt, non_zero);
|
||||
if(cpnt != 0) non_zero = false;
|
||||
}
|
||||
return VAR(ss.str());
|
||||
});
|
||||
|
||||
@ -353,7 +368,7 @@ void __init_builtins(VM* _vm) {
|
||||
assert(!is_tagged(obj));
|
||||
SStream ss;
|
||||
ss << "<" << _type_name(vm, vm->_tp(obj)) << " object at ";
|
||||
ss.write_hex(obj.get());
|
||||
ss.write_ptr(obj.get());
|
||||
ss << ">";
|
||||
return ss.str();
|
||||
});
|
||||
@ -539,7 +554,7 @@ void __init_builtins(VM* _vm) {
|
||||
double float_out;
|
||||
char* p_end;
|
||||
try {
|
||||
float_out = std::strtod(s.data, &p_end);
|
||||
float_out = std::strtod(s.c_str(), &p_end);
|
||||
if(p_end != s.end()) throw 1;
|
||||
} catch(...) { vm->ValueError("invalid literal for float(): " + s.escape()); }
|
||||
return VAR(float_out);
|
||||
@ -636,13 +651,12 @@ void __init_builtins(VM* _vm) {
|
||||
return VAR(self.u8_getitem(i));
|
||||
});
|
||||
|
||||
_vm->bind(_vm->_t(VM::tp_str), "replace(self, old, new, count=-1)", [](VM* vm, ArgsView args) {
|
||||
_vm->bind(_vm->_t(VM::tp_str), "replace(self, old, new)", [](VM* vm, ArgsView args) {
|
||||
const Str& self = _CAST(Str&, args[0]);
|
||||
const Str& old = CAST(Str&, args[1]);
|
||||
if(old.empty()) vm->ValueError("empty substring");
|
||||
const Str& new_ = CAST(Str&, args[2]);
|
||||
int count = CAST(int, args[3]);
|
||||
return VAR(self.replace(old, new_, count));
|
||||
return VAR(self.replace(old, new_));
|
||||
});
|
||||
|
||||
_vm->bind(_vm->_t(VM::tp_str), "split(self, sep=' ')", [](VM* vm, ArgsView args) {
|
||||
@ -705,14 +719,14 @@ void __init_builtins(VM* _vm) {
|
||||
const Str& suffix = CAST(Str&, args[1]);
|
||||
int offset = self.length() - suffix.length();
|
||||
if(offset < 0) return vm->False;
|
||||
bool ok = memcmp(self.data + offset, suffix.data, suffix.length()) == 0;
|
||||
bool ok = memcmp(self.c_str() + offset, suffix.c_str(), suffix.length()) == 0;
|
||||
return VAR(ok);
|
||||
});
|
||||
|
||||
_vm->bind_func(VM::tp_str, "encode", 1, [](VM* vm, ArgsView args) {
|
||||
const Str& self = _CAST(Str&, args[0]);
|
||||
Bytes retval(self.length());
|
||||
std::memcpy(retval.data(), self.data, self.length());
|
||||
std::memcpy(retval.data(), self.c_str(), self.length());
|
||||
return VAR(std::move(retval));
|
||||
});
|
||||
|
||||
@ -1479,12 +1493,12 @@ void __init_builtins(VM* _vm) {
|
||||
if(!vm->isinstance(_1, vm->tp_dict)) return vm->NotImplemented;
|
||||
Dict& other = _CAST(Dict&, _1);
|
||||
if(self.size() != other.size()) return vm->False;
|
||||
for(int i = 0; i < self._capacity; i++) {
|
||||
auto item = self._items[i];
|
||||
if(item.first == nullptr) continue;
|
||||
PyVar value = other.try_get(vm, item.first);
|
||||
if(value == nullptr) return vm->False;
|
||||
if(!vm->py_eq(item.second, value)) return vm->False;
|
||||
pkpy_DictIter it = self.iter();
|
||||
PyVar key, val;
|
||||
while(pkpy_DictIter__next(&it, (pkpy_Var*)(&key), (pkpy_Var*)(&val))) {
|
||||
PyVar other_val = other.try_get(vm, key);
|
||||
if(other_val == nullptr) return vm->False;
|
||||
if(!vm->py_eq(val, other_val)) return vm->False;
|
||||
}
|
||||
return vm->True;
|
||||
});
|
||||
|
@ -39,8 +39,8 @@ assert t[-5:] == 'ow!!!'
|
||||
assert t[3:-3] == 's is string example....wow'
|
||||
assert s > q;assert s < r
|
||||
assert s.replace("o","") == "ftball"
|
||||
assert s.replace("o","O",1) == "fOotball"
|
||||
assert s.replace("foo","ball",1) == "balltball"
|
||||
assert s.replace("o","O") == "fOOtball"
|
||||
assert s.replace("foo","ball") == "balltball"
|
||||
assert s.startswith('f') == True;assert s.endswith('o') == False
|
||||
assert t.startswith('this') == True;
|
||||
|
||||
|
@ -159,6 +159,17 @@ try:
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
n = 2 ** 17
|
||||
a = {}
|
||||
for i in range(n):
|
||||
a[str(i)] = i
|
||||
|
||||
for i in range(n):
|
||||
y = a[str(i)]
|
||||
|
||||
for i in range(n):
|
||||
del a[str(i)]
|
||||
|
||||
a = {1: 2, 3: 4}
|
||||
a['a'] = a
|
||||
assert repr(a) == "{1: 2, 3: 4, 'a': {...}}"
|
||||
@ -169,4 +180,3 @@ gc.collect()
|
||||
for k, v in a.items():
|
||||
pass
|
||||
assert gc.collect() == 1
|
||||
|
||||
|
@ -4,11 +4,16 @@ try:
|
||||
a = {'123': 4}
|
||||
b = a[6]
|
||||
except KeyError:
|
||||
s = traceback.format_exc()
|
||||
actual = traceback.format_exc()
|
||||
|
||||
ok = s == '''Traceback (most recent call last):
|
||||
expected = '''Traceback (most recent call last):
|
||||
File "80_traceback.py", line 5
|
||||
b = a[6]
|
||||
KeyError: 6'''
|
||||
|
||||
assert ok, s
|
||||
if actual != expected:
|
||||
print('--- ACTUAL RESULT -----')
|
||||
print(actual)
|
||||
print('--- EXPECTED RESULT ---')
|
||||
print(expected)
|
||||
exit(1)
|
@ -146,10 +146,13 @@ except:
|
||||
pass
|
||||
|
||||
# test chr
|
||||
l = []
|
||||
actual = []
|
||||
for i in range(128):
|
||||
l.append(f'{i} {chr(i)}')
|
||||
assert l == ['0 \x00', '1 \x01', '2 \x02', '3 \x03', '4 \x04', '5 \x05', '6 \x06', '7 \x07', '8 \x08', '9 \t', '10 \n', '11 \x0b', '12 \x0c', '13 \r', '14 \x0e', '15 \x0f', '16 \x10', '17 \x11', '18 \x12', '19 \x13', '20 \x14', '21 \x15', '22 \x16', '23 \x17', '24 \x18', '25 \x19', '26 \x1a', '27 \x1b', '28 \x1c', '29 \x1d', '30 \x1e', '31 \x1f', '32 ', '33 !', '34 "', '35 #', '36 $', '37 %', '38 &', "39 '", '40 (', '41 )', '42 *', '43 +', '44 ,', '45 -', '46 .', '47 /', '48 0', '49 1', '50 2', '51 3', '52 4', '53 5', '54 6', '55 7', '56 8', '57 9', '58 :', '59 ;', '60 <', '61 =', '62 >', '63 ?', '64 @', '65 A', '66 B', '67 C', '68 D', '69 E', '70 F', '71 G', '72 H', '73 I', '74 J', '75 K', '76 L', '77 M', '78 N', '79 O', '80 P', '81 Q', '82 R', '83 S', '84 T', '85 U', '86 V', '87 W', '88 X', '89 Y', '90 Z', '91 [', '92 \\', '93 ]', '94 ^', '95 _', '96 `', '97 a', '98 b', '99 c', '100 d', '101 e', '102 f', '103 g', '104 h', '105 i', '106 j', '107 k', '108 l', '109 m', '110 n', '111 o', '112 p', '113 q', '114 r', '115 s', '116 t', '117 u', '118 v', '119 w', '120 x', '121 y', '122 z', '123 {', '124 |', '125 }', '126 ~', '127 \x7f']
|
||||
actual.append(f'{i} {chr(i)}')
|
||||
expected = ['0 \x00', '1 \x01', '2 \x02', '3 \x03', '4 \x04', '5 \x05', '6 \x06', '7 \x07', '8 \x08', '9 \t', '10 \n', '11 \x0b', '12 \x0c', '13 \r', '14 \x0e', '15 \x0f', '16 \x10', '17 \x11', '18 \x12', '19 \x13', '20 \x14', '21 \x15', '22 \x16', '23 \x17', '24 \x18', '25 \x19', '26 \x1a', '27 \x1b', '28 \x1c', '29 \x1d', '30 \x1e', '31 \x1f', '32 ', '33 !', '34 "', '35 #', '36 $', '37 %', '38 &', "39 '", '40 (', '41 )', '42 *', '43 +', '44 ,', '45 -', '46 .', '47 /', '48 0', '49 1', '50 2', '51 3', '52 4', '53 5', '54 6', '55 7', '56 8', '57 9', '58 :', '59 ;', '60 <', '61 =', '62 >', '63 ?', '64 @', '65 A', '66 B', '67 C', '68 D', '69 E', '70 F', '71 G', '72 H', '73 I', '74 J', '75 K', '76 L', '77 M', '78 N', '79 O', '80 P', '81 Q', '82 R', '83 S', '84 T', '85 U', '86 V', '87 W', '88 X', '89 Y', '90 Z', '91 [', '92 \\', '93 ]', '94 ^', '95 _', '96 `', '97 a', '98 b', '99 c', '100 d', '101 e', '102 f', '103 g', '104 h', '105 i', '106 j', '107 k', '108 l', '109 m', '110 n', '111 o', '112 p', '113 q', '114 r', '115 s', '116 t', '117 u', '118 v', '119 w', '120 x', '121 y', '122 z', '123 {', '124 |', '125 }', '126 ~', '127 \x7f']
|
||||
assert len(actual) == len(expected)
|
||||
for i in range(len(actual)):
|
||||
assert (actual[i] == expected[i]), (actual[i], expected[i])
|
||||
|
||||
assert type(bin(1234)) is str
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user