mirror of
https://github.com/pocketpy/pocketpy
synced 2026-03-24 06:00:25 +00:00
Compare commits
11 Commits
e51f86c599
...
3d5a59bd15
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d5a59bd15 | ||
|
|
3cc46df47b | ||
|
|
0844dbaff3 | ||
|
|
35eb793a08 | ||
|
|
d709d7b531 | ||
|
|
5f7006e2cb | ||
|
|
13f691bc37 | ||
|
|
d08ecbffd8 | ||
|
|
6ba0aff3a4 | ||
|
|
bc991249b9 | ||
|
|
b94b535de8 |
201
.github/workflows/main.yml
vendored
Normal file
201
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- 'docs/**'
|
||||||
|
- 'web/**'
|
||||||
|
- '**.md'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- 'docs/**'
|
||||||
|
- '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
|
||||||
|
build_win32:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
- name: Compile
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir -p output/x86_64
|
||||||
|
python cmake_build.py
|
||||||
|
cp main.exe output/x86_64
|
||||||
|
cp pocketpy.dll output/x86_64
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: windows
|
||||||
|
path: output
|
||||||
|
- name: Unit Test
|
||||||
|
run: python scripts/run_tests.py
|
||||||
|
- name: Benchmark
|
||||||
|
run: python scripts/run_tests.py benchmark
|
||||||
|
build_linux:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Clang
|
||||||
|
uses: egor-tensin/setup-clang@v1
|
||||||
|
with:
|
||||||
|
version: 15
|
||||||
|
platform: x64
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt-get install -y 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 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:
|
||||||
|
CC: clang
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: linux
|
||||||
|
path: output
|
||||||
|
- name: Benchmark
|
||||||
|
run: python scripts/run_tests.py benchmark
|
||||||
|
build_linux_x86:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Alpine Linux for aarch64
|
||||||
|
uses: jirutka/setup-alpine@v1
|
||||||
|
with:
|
||||||
|
arch: x86
|
||||||
|
packages: gcc g++ make cmake libc-dev linux-headers python3
|
||||||
|
- name: Build and Test
|
||||||
|
run: |
|
||||||
|
uname -m
|
||||||
|
python cmake_build.py
|
||||||
|
python scripts/run_tests.py
|
||||||
|
python scripts/run_tests.py benchmark
|
||||||
|
shell: alpine.sh --root {0}
|
||||||
|
build_darwin:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Compile and Test
|
||||||
|
run: |
|
||||||
|
python cmake_build.py
|
||||||
|
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
|
||||||
|
build_android:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: nttld/setup-ndk@v1
|
||||||
|
id: setup-ndk
|
||||||
|
with:
|
||||||
|
ndk-version: r23
|
||||||
|
local-cache: false
|
||||||
|
add-to-path: false
|
||||||
|
- name: Compile Shared Library
|
||||||
|
run: |
|
||||||
|
bash build_android.sh arm64-v8a
|
||||||
|
bash build_android.sh armeabi-v7a
|
||||||
|
bash build_android.sh x86_64
|
||||||
|
|
||||||
|
mkdir -p output/arm64-v8a
|
||||||
|
mkdir -p output/armeabi-v7a
|
||||||
|
mkdir -p output/x86_64
|
||||||
|
|
||||||
|
cp build/android/arm64-v8a/libpocketpy.so output/arm64-v8a
|
||||||
|
cp build/android/armeabi-v7a/libpocketpy.so output/armeabi-v7a
|
||||||
|
cp build/android/x86_64/libpocketpy.so output/x86_64
|
||||||
|
env:
|
||||||
|
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: android
|
||||||
|
path: output
|
||||||
|
build_ios:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Compile Frameworks
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/leetal/ios-cmake --depth 1 ~/ios-cmake
|
||||||
|
bash build_ios.sh
|
||||||
|
mkdir -p output
|
||||||
|
cp -r build/pocketpy.xcframework output/pocketpy.xcframework
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ios
|
||||||
|
path: output
|
||||||
|
|
||||||
|
merge:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [ build_win32, build_linux, build_darwin, build_android, build_ios ]
|
||||||
|
steps:
|
||||||
|
- name: "Create output directory"
|
||||||
|
run: "mkdir $GITHUB_WORKSPACE/output"
|
||||||
|
|
||||||
|
- name: "Merge win32"
|
||||||
|
uses: actions/download-artifact@v4.1.7
|
||||||
|
with:
|
||||||
|
name: windows
|
||||||
|
path: $GITHUB_WORKSPACE/output/windows
|
||||||
|
|
||||||
|
- name: "Merge linux"
|
||||||
|
uses: actions/download-artifact@v4.1.7
|
||||||
|
with:
|
||||||
|
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 android"
|
||||||
|
uses: actions/download-artifact@v4.1.7
|
||||||
|
with:
|
||||||
|
name: android
|
||||||
|
path: $GITHUB_WORKSPACE/output/android
|
||||||
|
|
||||||
|
- name: "Merge ios"
|
||||||
|
uses: actions/download-artifact@v4.1.7
|
||||||
|
with:
|
||||||
|
name: ios
|
||||||
|
path: $GITHUB_WORKSPACE/output/ios
|
||||||
|
|
||||||
|
- name: "Upload merged artifact"
|
||||||
|
uses: actions/upload-artifact@v4.3.3
|
||||||
|
with:
|
||||||
|
name: all-in-one
|
||||||
|
path: $GITHUB_WORKSPACE/output
|
||||||
BIN
.github/workflows/main.zip
vendored
BIN
.github/workflows/main.zip
vendored
Binary file not shown.
2
.gitignore
vendored
2
.gitignore
vendored
@ -34,3 +34,5 @@ docs/references.md
|
|||||||
|
|
||||||
.xmake
|
.xmake
|
||||||
.vs
|
.vs
|
||||||
|
|
||||||
|
tests/00_tmp.py
|
||||||
|
|||||||
@ -11,12 +11,12 @@ if(MSVC)
|
|||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox")
|
||||||
|
add_definitions(-DNDEBUG)
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
|
||||||
|
add_definitions(-DNDEBUG)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# disable -Wshorten-64-to-32 for apple
|
# disable -Wshorten-64-to-32 for apple
|
||||||
@ -28,12 +28,6 @@ endif()
|
|||||||
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
|
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
|
||||||
file(GLOB_RECURSE POCKETPY_SRC ${CMAKE_CURRENT_LIST_DIR}/src/*.c)
|
file(GLOB_RECURSE POCKETPY_SRC ${CMAKE_CURRENT_LIST_DIR}/src/*.c)
|
||||||
|
|
||||||
# option(PK_USE_CJSON "" OFF)
|
|
||||||
# if(PK_USE_CJSON)
|
|
||||||
# add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/3rd/cjson)
|
|
||||||
# add_definitions(-DPK_USE_CJSON)
|
|
||||||
# endif()
|
|
||||||
|
|
||||||
option(PK_ENABLE_OS "" OFF)
|
option(PK_ENABLE_OS "" OFF)
|
||||||
if(PK_ENABLE_OS)
|
if(PK_ENABLE_OS)
|
||||||
add_definitions(-DPK_ENABLE_OS=1)
|
add_definitions(-DPK_ENABLE_OS=1)
|
||||||
@ -80,15 +74,11 @@ elseif(PK_BUILD_STATIC_LIB)
|
|||||||
else()
|
else()
|
||||||
set(PROJECT_EXE_NAME main)
|
set(PROJECT_EXE_NAME main)
|
||||||
add_executable(${PROJECT_EXE_NAME} src2/main.c)
|
add_executable(${PROJECT_EXE_NAME} src2/main.c)
|
||||||
# static linked main
|
# shared linked main
|
||||||
add_library(${PROJECT_NAME} STATIC ${POCKETPY_SRC})
|
add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC})
|
||||||
target_link_libraries(${PROJECT_EXE_NAME} ${PROJECT_NAME})
|
target_link_libraries(${PROJECT_EXE_NAME} ${PROJECT_NAME})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(PK_USE_CJSON)
|
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE cjson)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# link math library
|
# link math library
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE m)
|
target_link_libraries(${PROJECT_NAME} PRIVATE m)
|
||||||
|
|||||||
@ -1,46 +0,0 @@
|
|||||||
try:
|
|
||||||
import os
|
|
||||||
except ImportError:
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
import sys
|
|
||||||
is_pkpy = not hasattr(sys, 'getrefcount')
|
|
||||||
|
|
||||||
os.chdir('benchmarks')
|
|
||||||
|
|
||||||
if is_pkpy:
|
|
||||||
try:
|
|
||||||
import cjson as json
|
|
||||||
except ImportError:
|
|
||||||
print('[cJSON not Enabled]')
|
|
||||||
exit(0)
|
|
||||||
else:
|
|
||||||
import json
|
|
||||||
|
|
||||||
_2489KB = 'WorldMap_GridVania_layout.ldtk'
|
|
||||||
_1093KB = 'WorldMap_Free_layout.ldtk'
|
|
||||||
_339KB = 'Typical_2D_platformer_example.ldtk'
|
|
||||||
|
|
||||||
with open(f'res/{_2489KB}', 'r') as f:
|
|
||||||
json_content = f.read()
|
|
||||||
|
|
||||||
data: dict = json.loads(json_content)
|
|
||||||
assert isinstance(data, dict)
|
|
||||||
|
|
||||||
# serialize and deserialize
|
|
||||||
dumped: str = json.dumps(data)
|
|
||||||
for _ in range(10):
|
|
||||||
loaded: dict = json.loads(dumped)
|
|
||||||
assert len(data) == len(loaded)
|
|
||||||
assert data == loaded
|
|
||||||
|
|
||||||
#### very very slow!!
|
|
||||||
import pickle
|
|
||||||
|
|
||||||
with open(f'res/{_339KB}', 'r') as f:
|
|
||||||
json_content = f.read()
|
|
||||||
data: dict = json.loads(json_content)
|
|
||||||
|
|
||||||
data_pickled: bytes = pickle.dumps(data)
|
|
||||||
assert isinstance(data_pickled, bytes)
|
|
||||||
assert pickle.loads(data_pickled) == data
|
|
||||||
@ -1,7 +1,4 @@
|
|||||||
try:
|
exit(0)
|
||||||
import os
|
|
||||||
except ImportError:
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
os.chdir('benchmarks')
|
os.chdir('benchmarks')
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,15 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
is_cpython = hasattr(sys, 'getrefcount')
|
class vec2:
|
||||||
|
def __init__(self, x, y):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
if is_cpython:
|
def __add__(self, other):
|
||||||
class vec2:
|
return vec2(self.x + other.x, self.y + other.y)
|
||||||
def __init__(self, x, y):
|
|
||||||
self.x = x
|
|
||||||
self.y = y
|
|
||||||
|
|
||||||
def __add__(self, other):
|
def __eq__(self, other):
|
||||||
return vec2(self.x + other.x, self.y + other.y)
|
return self.x == other.x and self.y == other.y
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return self.x == other.x and self.y == other.y
|
|
||||||
else:
|
|
||||||
from linalg import vec2
|
|
||||||
|
|
||||||
x = vec2(0, 0)
|
x = vec2(0, 0)
|
||||||
for i in range(10000000):
|
for i in range(10000000):
|
||||||
|
|||||||
@ -25,10 +25,10 @@ assert code == 0
|
|||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
shutil.copy(f"{config}/main.exe", "../main.exe")
|
shutil.copy(f"{config}/main.exe", "../main.exe")
|
||||||
# shutil.copy(f"{config}/pocketpy.dll", "../pocketpy.dll")
|
shutil.copy(f"{config}/pocketpy.dll", "../pocketpy.dll")
|
||||||
elif sys.platform == "darwin":
|
elif sys.platform == "darwin":
|
||||||
shutil.copy("main", "../main")
|
shutil.copy("main", "../main")
|
||||||
# shutil.copy("libpocketpy.dylib", "../libpocketpy.dylib")
|
shutil.copy("libpocketpy.dylib", "../libpocketpy.dylib")
|
||||||
else:
|
else:
|
||||||
shutil.copy("main", "../main")
|
shutil.copy("main", "../main")
|
||||||
# shutil.copy("libpocketpy.so", "../libpocketpy.so")
|
shutil.copy("libpocketpy.so", "../libpocketpy.so")
|
||||||
|
|||||||
@ -10,7 +10,7 @@ extern const char* TokenSymbols[];
|
|||||||
|
|
||||||
typedef enum TokenIndex{
|
typedef enum TokenIndex{
|
||||||
TK_EOF, TK_EOL, TK_SOF,
|
TK_EOF, TK_EOL, TK_SOF,
|
||||||
TK_ID, TK_NUM, TK_STR, TK_FSTR, TK_BYTES, TK_IMAG,
|
TK_ID, TK_NUM, TK_STR, TK_FSTR_BEGIN, TK_FSTR_CPNT, TK_FSTR_SPEC, TK_FSTR_END, TK_BYTES, TK_IMAG,
|
||||||
TK_INDENT, TK_DEDENT,
|
TK_INDENT, TK_DEDENT,
|
||||||
/***************/
|
/***************/
|
||||||
TK_IS_NOT, TK_NOT_IN, TK_YIELD_FROM,
|
TK_IS_NOT, TK_NOT_IN, TK_YIELD_FROM,
|
||||||
@ -87,7 +87,6 @@ enum Precedence {
|
|||||||
typedef c11_array TokenArray;
|
typedef c11_array TokenArray;
|
||||||
|
|
||||||
Error* Lexer__process(SourceData_ src, TokenArray* out_tokens);
|
Error* Lexer__process(SourceData_ src, TokenArray* out_tokens);
|
||||||
Error* Lexer__process_and_dump(SourceData_ src, c11_string** out_string);
|
|
||||||
void TokenArray__dtor(TokenArray* self);
|
void TokenArray__dtor(TokenArray* self);
|
||||||
|
|
||||||
#define Token__sv(self) (c11_sv){(self)->start, (self)->length}
|
#define Token__sv(self) (c11_sv){(self)->start, (self)->length}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "pocketpy/objects/codeobject.h"
|
#include "pocketpy/objects/codeobject.h"
|
||||||
#include "pocketpy/pocketpy.h"
|
#include "pocketpy/pocketpy.h"
|
||||||
#include "pocketpy/interpreter/gc.h"
|
#include "pocketpy/interpreter/heap.h"
|
||||||
#include "pocketpy/interpreter/frame.h"
|
#include "pocketpy/interpreter/frame.h"
|
||||||
#include "pocketpy/interpreter/modules.h"
|
#include "pocketpy/interpreter/modules.h"
|
||||||
|
|
||||||
|
|||||||
@ -9,14 +9,12 @@
|
|||||||
struct SourceData {
|
struct SourceData {
|
||||||
RefCounted rc;
|
RefCounted rc;
|
||||||
enum py_CompileMode mode;
|
enum py_CompileMode mode;
|
||||||
bool is_precompiled;
|
|
||||||
bool is_dynamic; // for exec() and eval()
|
bool is_dynamic; // for exec() and eval()
|
||||||
|
|
||||||
c11_string* filename;
|
c11_string* filename;
|
||||||
c11_string* source;
|
c11_string* source;
|
||||||
|
|
||||||
c11_vector /*T=const char* */ line_starts;
|
c11_vector /*T=const char* */ line_starts;
|
||||||
c11_vector /*T=c11_string* */ _precompiled_tokens;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct SourceData* SourceData_;
|
typedef struct SourceData* SourceData_;
|
||||||
|
|||||||
@ -67,7 +67,6 @@ OPCODE(LOOP_BREAK)
|
|||||||
OPCODE(JUMP_ABSOLUTE_TOP)
|
OPCODE(JUMP_ABSOLUTE_TOP)
|
||||||
OPCODE(GOTO)
|
OPCODE(GOTO)
|
||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(REPR)
|
|
||||||
OPCODE(CALL)
|
OPCODE(CALL)
|
||||||
OPCODE(CALL_VARGS)
|
OPCODE(CALL_VARGS)
|
||||||
OPCODE(RETURN_VALUE)
|
OPCODE(RETURN_VALUE)
|
||||||
@ -108,7 +107,6 @@ OPCODE(PUSH_EXCEPTION)
|
|||||||
OPCODE(BEGIN_EXC_HANDLING)
|
OPCODE(BEGIN_EXC_HANDLING)
|
||||||
OPCODE(END_EXC_HANDLING)
|
OPCODE(END_EXC_HANDLING)
|
||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(FSTRING_EVAL)
|
|
||||||
OPCODE(FORMAT_STRING)
|
OPCODE(FORMAT_STRING)
|
||||||
/**************************/
|
/**************************/
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -4,7 +4,7 @@ python prebuild.py
|
|||||||
|
|
||||||
SRC=$(find src/ -name "*.c")
|
SRC=$(find src/ -name "*.c")
|
||||||
|
|
||||||
clang -std=c11 --coverage -O1 -Wfatal-errors -o main src2/main.c $SRC -Iinclude -DPK_ENABLE_OS=1 -DPK_DEBUG_PRECOMPILED_EXEC=1 -DPK_ENABLE_PROFILER=1
|
clang -std=c11 --coverage -O1 -Wfatal-errors -o main src2/main.c $SRC -Iinclude -DPK_ENABLE_OS=1 -DPK_ENABLE_PROFILER=1 -lm -DNDEBUG
|
||||||
|
|
||||||
python scripts/run_tests.py
|
python scripts/run_tests.py
|
||||||
|
|
||||||
@ -15,7 +15,6 @@ fi
|
|||||||
|
|
||||||
rm -rf .coverage
|
rm -rf .coverage
|
||||||
mkdir .coverage
|
mkdir .coverage
|
||||||
rm pocketpy_c.gcno
|
|
||||||
|
|
||||||
UNITS=$(find ./ -name "*.gcno")
|
UNITS=$(find ./ -name "*.gcno")
|
||||||
llvm-cov-15 gcov ${UNITS} -r -s include/ -r -s src/ >> .coverage/coverage.txt
|
llvm-cov-15 gcov ${UNITS} -r -s include/ -r -s src/ >> .coverage/coverage.txt
|
||||||
|
|||||||
@ -56,12 +56,9 @@ def add(a, b):
|
|||||||
class A:
|
class A:
|
||||||
def __init__(self, x):
|
def __init__(self, x):
|
||||||
self.x = x
|
self.x = x
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
return self.x
|
return self.x
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
print('ans_1:', add(1, 2))
|
print('ans_1:', add(1, 2))
|
||||||
print('ans_2:', A('abc').get())
|
print('ans_2:', A('abc').get())
|
||||||
exit()
|
exit()
|
||||||
|
|||||||
@ -5,14 +5,13 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static void SourceData__ctor(struct SourceData* self,
|
static void SourceData__ctor(struct SourceData* self,
|
||||||
const char* source,
|
const char* source,
|
||||||
const char* filename,
|
const char* filename,
|
||||||
enum py_CompileMode mode,
|
enum py_CompileMode mode,
|
||||||
bool is_dynamic) {
|
bool is_dynamic) {
|
||||||
self->filename = c11_string__new(filename);
|
self->filename = c11_string__new(filename);
|
||||||
self->mode = mode;
|
self->mode = mode;
|
||||||
c11_vector__ctor(&self->line_starts, sizeof(const char*));
|
c11_vector__ctor(&self->line_starts, sizeof(const char*));
|
||||||
c11_vector__ctor(&self->_precompiled_tokens, sizeof(c11_string*));
|
|
||||||
|
|
||||||
// Skip utf8 BOM if there is any.
|
// Skip utf8 BOM if there is any.
|
||||||
if(strncmp(source, "\xEF\xBB\xBF", 3) == 0) source += 3;
|
if(strncmp(source, "\xEF\xBB\xBF", 3) == 0) source += 3;
|
||||||
@ -26,7 +25,6 @@ static void SourceData__ctor(struct SourceData* self,
|
|||||||
source++;
|
source++;
|
||||||
}
|
}
|
||||||
self->source = c11_sbuf__submit(&ss);
|
self->source = c11_sbuf__submit(&ss);
|
||||||
self->is_precompiled = (strncmp(source, "pkpy:", 5) == 0);
|
|
||||||
self->is_dynamic = is_dynamic;
|
self->is_dynamic = is_dynamic;
|
||||||
c11_vector__push(const char*, &self->line_starts, self->source->data);
|
c11_vector__push(const char*, &self->line_starts, self->source->data);
|
||||||
}
|
}
|
||||||
@ -34,19 +32,13 @@ static void SourceData__ctor(struct SourceData* self,
|
|||||||
static void SourceData__dtor(struct SourceData* self) {
|
static void SourceData__dtor(struct SourceData* self) {
|
||||||
c11_string__delete(self->filename);
|
c11_string__delete(self->filename);
|
||||||
c11_string__delete(self->source);
|
c11_string__delete(self->source);
|
||||||
|
|
||||||
c11_vector__dtor(&self->line_starts);
|
c11_vector__dtor(&self->line_starts);
|
||||||
|
|
||||||
for(int i = 0; i < self->_precompiled_tokens.count; i++) {
|
|
||||||
c11_string__delete(c11__getitem(c11_string*, &self->_precompiled_tokens, i));
|
|
||||||
}
|
|
||||||
c11_vector__dtor(&self->_precompiled_tokens);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceData_ SourceData__rcnew(const char* source,
|
SourceData_ SourceData__rcnew(const char* source,
|
||||||
const char* filename,
|
const char* filename,
|
||||||
enum py_CompileMode mode,
|
enum py_CompileMode mode,
|
||||||
bool is_dynamic) {
|
bool is_dynamic) {
|
||||||
SourceData_ self = malloc(sizeof(struct SourceData));
|
SourceData_ self = malloc(sizeof(struct SourceData));
|
||||||
SourceData__ctor(self, source, filename, mode, is_dynamic);
|
SourceData__ctor(self, source, filename, mode, is_dynamic);
|
||||||
self->rc.count = 1;
|
self->rc.count = 1;
|
||||||
@ -55,10 +47,10 @@ SourceData_ SourceData__rcnew(const char* source,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool SourceData__get_line(const struct SourceData* self,
|
bool SourceData__get_line(const struct SourceData* self,
|
||||||
int lineno,
|
int lineno,
|
||||||
const char** st,
|
const char** st,
|
||||||
const char** ed) {
|
const char** ed) {
|
||||||
if(self->is_precompiled || lineno == -1) { return false; }
|
if(lineno < 0) return false;
|
||||||
lineno -= 1;
|
lineno -= 1;
|
||||||
if(lineno < 0) lineno = 0;
|
if(lineno < 0) lineno = 0;
|
||||||
const char* _start = c11__getitem(const char*, &self->line_starts, lineno);
|
const char* _start = c11__getitem(const char*, &self->line_starts, lineno);
|
||||||
@ -72,10 +64,10 @@ bool SourceData__get_line(const struct SourceData* self,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SourceData__snapshot(const struct SourceData* self,
|
void SourceData__snapshot(const struct SourceData* self,
|
||||||
c11_sbuf* ss,
|
c11_sbuf* ss,
|
||||||
int lineno,
|
int lineno,
|
||||||
const char* cursor,
|
const char* cursor,
|
||||||
const char* name) {
|
const char* name) {
|
||||||
pk_sprintf(ss, " File \"%s\", line %d", self->filename->data, lineno);
|
pk_sprintf(ss, " File \"%s\", line %d", self->filename->data, lineno);
|
||||||
|
|
||||||
if(name && *name) {
|
if(name && *name) {
|
||||||
@ -83,26 +75,24 @@ void SourceData__snapshot(const struct SourceData* self,
|
|||||||
c11_sbuf__write_cstr(ss, name);
|
c11_sbuf__write_cstr(ss, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!self->is_precompiled) {
|
c11_sbuf__write_char(ss, '\n');
|
||||||
c11_sbuf__write_char(ss, '\n');
|
const char *st = NULL, *ed;
|
||||||
const char *st = NULL, *ed;
|
if(SourceData__get_line(self, lineno, &st, &ed)) {
|
||||||
if(SourceData__get_line(self, lineno, &st, &ed)) {
|
while(st < ed && isblank(*st))
|
||||||
while(st < ed && isblank(*st))
|
++st;
|
||||||
++st;
|
if(st < ed) {
|
||||||
if(st < ed) {
|
c11_sbuf__write_cstr(ss, " ");
|
||||||
c11_sbuf__write_cstr(ss, " ");
|
c11_sbuf__write_cstrn(ss, st, ed - st);
|
||||||
c11_sbuf__write_cstrn(ss, st, ed - st);
|
if(cursor && st <= cursor && cursor <= ed) {
|
||||||
if(cursor && st <= cursor && cursor <= ed) {
|
c11_sbuf__write_cstr(ss, "\n ");
|
||||||
c11_sbuf__write_cstr(ss, "\n ");
|
for(int i = 0; i < (cursor - st); ++i)
|
||||||
for(int i = 0; i < (cursor - st); ++i)
|
c11_sbuf__write_char(ss, ' ');
|
||||||
c11_sbuf__write_char(ss, ' ');
|
c11_sbuf__write_cstr(ss, "^");
|
||||||
c11_sbuf__write_cstr(ss, "^");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
st = NULL;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
st = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!st) { c11_sbuf__write_cstr(ss, " <?>"); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!st) { c11_sbuf__write_cstr(ss, " <?>"); }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include "pocketpy/common/sstream.h"
|
#include "pocketpy/common/sstream.h"
|
||||||
#include "pocketpy/common/config.h"
|
#include "pocketpy/common/config.h"
|
||||||
#include "pocketpy/common/memorypool.h"
|
#include "pocketpy/common/memorypool.h"
|
||||||
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
@ -227,6 +228,30 @@ UnaryExpr* UnaryExpr__new(int line, Expr* child, Opcode opcode) {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct FStringSpecExpr {
|
||||||
|
EXPR_COMMON_HEADER
|
||||||
|
Expr* child;
|
||||||
|
c11_sv spec;
|
||||||
|
} FStringSpecExpr;
|
||||||
|
|
||||||
|
void FStringSpecExpr__emit_(Expr* self_, Ctx* ctx) {
|
||||||
|
FStringSpecExpr* self = (FStringSpecExpr*)self_;
|
||||||
|
vtemit_(self->child, ctx);
|
||||||
|
int index = Ctx__add_const_string(ctx, self->spec);
|
||||||
|
Ctx__emit_(ctx, OP_FORMAT_STRING, index, self->line);
|
||||||
|
}
|
||||||
|
|
||||||
|
FStringSpecExpr* FStringSpecExpr__new(int line, Expr* child, c11_sv spec) {
|
||||||
|
const static ExprVt Vt = {.emit_ = FStringSpecExpr__emit_, .dtor = UnaryExpr__dtor};
|
||||||
|
static_assert_expr_size(FStringSpecExpr);
|
||||||
|
FStringSpecExpr* self = PoolExpr_alloc();
|
||||||
|
self->vt = &Vt;
|
||||||
|
self->line = line;
|
||||||
|
self->child = child;
|
||||||
|
self->spec = spec;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct RawStringExpr {
|
typedef struct RawStringExpr {
|
||||||
EXPR_COMMON_HEADER
|
EXPR_COMMON_HEADER
|
||||||
c11_sv value;
|
c11_sv value;
|
||||||
@ -236,8 +261,7 @@ typedef struct RawStringExpr {
|
|||||||
void RawStringExpr__emit_(Expr* self_, Ctx* ctx) {
|
void RawStringExpr__emit_(Expr* self_, Ctx* ctx) {
|
||||||
RawStringExpr* self = (RawStringExpr*)self_;
|
RawStringExpr* self = (RawStringExpr*)self_;
|
||||||
int index = Ctx__add_const_string(ctx, self->value);
|
int index = Ctx__add_const_string(ctx, self->value);
|
||||||
Ctx__emit_(ctx, OP_LOAD_CONST, index, self->line);
|
Ctx__emit_(ctx, self->opcode, index, self->line);
|
||||||
Ctx__emit_(ctx, self->opcode, BC_NOARG, self->line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RawStringExpr* RawStringExpr__new(int line, c11_sv value, Opcode opcode) {
|
RawStringExpr* RawStringExpr__new(int line, c11_sv value, Opcode opcode) {
|
||||||
@ -505,6 +529,11 @@ static SequenceExpr* SequenceExpr__new(int line, const ExprVt* vt, int count, Op
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SequenceExpr* FStringExpr__new(int line, int count) {
|
||||||
|
const static ExprVt ListExprVt = {.dtor = SequenceExpr__dtor, .emit_ = SequenceExpr__emit_};
|
||||||
|
return SequenceExpr__new(line, &ListExprVt, count, OP_BUILD_STRING);
|
||||||
|
}
|
||||||
|
|
||||||
SequenceExpr* ListExpr__new(int line, int count) {
|
SequenceExpr* ListExpr__new(int line, int count) {
|
||||||
const static ExprVt ListExprVt = {.dtor = SequenceExpr__dtor, .emit_ = SequenceExpr__emit_};
|
const static ExprVt ListExprVt = {.dtor = SequenceExpr__dtor, .emit_ = SequenceExpr__emit_};
|
||||||
return SequenceExpr__new(line, &ListExprVt, count, OP_BUILD_LIST);
|
return SequenceExpr__new(line, &ListExprVt, count, OP_BUILD_LIST);
|
||||||
@ -611,162 +640,6 @@ LambdaExpr* LambdaExpr__new(int line, int index) {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct FStringExpr {
|
|
||||||
EXPR_COMMON_HEADER
|
|
||||||
c11_sv src;
|
|
||||||
} FStringExpr;
|
|
||||||
|
|
||||||
static bool is_fmt_valid_char(char c) {
|
|
||||||
switch(c) {
|
|
||||||
// clang-format off
|
|
||||||
case '-': case '=': case '*': case '#': case '@': case '!': case '~':
|
|
||||||
case '<': case '>': case '^':
|
|
||||||
case '.': case 'f': case 'd': case 's':
|
|
||||||
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
|
||||||
return true;
|
|
||||||
default: return false;
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _load_expr(Ctx* ctx, c11_sv expr, int line) {
|
|
||||||
bool repr = false;
|
|
||||||
const char* expr_end = expr.data + expr.size;
|
|
||||||
if(expr.size >= 2 && expr_end[-2] == '!') {
|
|
||||||
switch(expr_end[-1]) {
|
|
||||||
case 'r':
|
|
||||||
repr = true;
|
|
||||||
expr.size -= 2; // expr[:-2]
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
repr = false;
|
|
||||||
expr.size -= 2; // expr[:-2]
|
|
||||||
break;
|
|
||||||
default: break; // nothing happens
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c11_string* source = c11_string__new2(expr.data, expr.size);
|
|
||||||
bool ok = py_compile(source->data, "<f-string>", EVAL_MODE, true);
|
|
||||||
if(!ok) {
|
|
||||||
py_printexc();
|
|
||||||
c11__abort("f-string: invalid expression");
|
|
||||||
}
|
|
||||||
int index = Ctx__add_const(ctx, py_retval());
|
|
||||||
c11_string__delete(source);
|
|
||||||
Ctx__emit_(ctx, OP_FSTRING_EVAL, index, line);
|
|
||||||
|
|
||||||
if(repr) Ctx__emit_(ctx, OP_REPR, BC_NOARG, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FStringExpr__emit_(Expr* self_, Ctx* ctx) {
|
|
||||||
FStringExpr* self = (FStringExpr*)self_;
|
|
||||||
int i = 0; // left index
|
|
||||||
int j = 0; // right index
|
|
||||||
int count = 0; // how many string parts
|
|
||||||
bool flag = false; // true if we are in a expression
|
|
||||||
|
|
||||||
const char* src = self->src.data;
|
|
||||||
while(j < self->src.size) {
|
|
||||||
if(flag) {
|
|
||||||
if(src[j] == '}') {
|
|
||||||
// add expression
|
|
||||||
c11_sv expr = {src + i, j - i}; // src[i:j]
|
|
||||||
// BUG: ':' is not a format specifier in f"{stack[2:]}"
|
|
||||||
int conon = c11_sv__index(expr, ':');
|
|
||||||
if(conon >= 0) {
|
|
||||||
c11_sv spec = {expr.data + (conon + 1),
|
|
||||||
expr.size - (conon + 1)}; // expr[conon+1:]
|
|
||||||
// filter some invalid spec
|
|
||||||
bool ok = true;
|
|
||||||
for(int k = 0; k < spec.size; k++) {
|
|
||||||
char c = spec.data[k];
|
|
||||||
if(!is_fmt_valid_char(c)) {
|
|
||||||
ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(ok) {
|
|
||||||
expr.size = conon; // expr[:conon]
|
|
||||||
_load_expr(ctx, expr, self->line);
|
|
||||||
Ctx__emit_(ctx,
|
|
||||||
OP_FORMAT_STRING,
|
|
||||||
Ctx__add_const_string(ctx, spec),
|
|
||||||
self->line);
|
|
||||||
} else {
|
|
||||||
// ':' is not a spec indicator
|
|
||||||
_load_expr(ctx, expr, self->line);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_load_expr(ctx, expr, self->line);
|
|
||||||
}
|
|
||||||
flag = false;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(src[j] == '{') {
|
|
||||||
// look at next char
|
|
||||||
if(j + 1 < self->src.size && src[j + 1] == '{') {
|
|
||||||
// {{ -> {
|
|
||||||
j++;
|
|
||||||
Ctx__emit_(ctx,
|
|
||||||
OP_LOAD_CONST,
|
|
||||||
Ctx__add_const_string(ctx, (c11_sv){"{", 1}),
|
|
||||||
self->line);
|
|
||||||
count++;
|
|
||||||
} else {
|
|
||||||
// { -> }
|
|
||||||
flag = true;
|
|
||||||
i = j + 1;
|
|
||||||
}
|
|
||||||
} else if(src[j] == '}') {
|
|
||||||
// look at next char
|
|
||||||
if(j + 1 < self->src.size && src[j + 1] == '}') {
|
|
||||||
// }} -> }
|
|
||||||
j++;
|
|
||||||
Ctx__emit_(ctx,
|
|
||||||
OP_LOAD_CONST,
|
|
||||||
Ctx__add_const_string(ctx, (c11_sv){"}", 1}),
|
|
||||||
self->line);
|
|
||||||
count++;
|
|
||||||
} else {
|
|
||||||
// } -> error
|
|
||||||
// throw std::runtime_error("f-string: unexpected }");
|
|
||||||
// just ignore
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// literal
|
|
||||||
i = j;
|
|
||||||
while(j < self->src.size && src[j] != '{' && src[j] != '}')
|
|
||||||
j++;
|
|
||||||
c11_sv literal = {src + i, j - i}; // src[i:j]
|
|
||||||
Ctx__emit_(ctx, OP_LOAD_CONST, Ctx__add_const_string(ctx, literal), self->line);
|
|
||||||
count++;
|
|
||||||
continue; // skip j++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(flag) {
|
|
||||||
// literal
|
|
||||||
c11_sv literal = {src + i, self->src.size - i}; // src[i:]
|
|
||||||
Ctx__emit_(ctx, OP_LOAD_CONST, Ctx__add_const_string(ctx, literal), self->line);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
Ctx__emit_(ctx, OP_BUILD_STRING, count, self->line);
|
|
||||||
}
|
|
||||||
|
|
||||||
FStringExpr* FStringExpr__new(int line, c11_sv src) {
|
|
||||||
const static ExprVt Vt = {.emit_ = FStringExpr__emit_};
|
|
||||||
static_assert_expr_size(FStringExpr);
|
|
||||||
FStringExpr* self = PoolExpr_alloc();
|
|
||||||
self->vt = &Vt;
|
|
||||||
self->line = line;
|
|
||||||
self->src = src;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
// AndExpr, OrExpr
|
// AndExpr, OrExpr
|
||||||
typedef struct LogicBinaryExpr {
|
typedef struct LogicBinaryExpr {
|
||||||
EXPR_COMMON_HEADER
|
EXPR_COMMON_HEADER
|
||||||
@ -1669,9 +1542,39 @@ static Error* exprBytes(Compiler* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Error* exprFString(Compiler* self) {
|
static Error* exprFString(Compiler* self) {
|
||||||
c11_sv sv = c11_string__sv(prev()->value._str);
|
// @fstr-begin, [@fstr-cpnt | <expr>]*, @fstr-end
|
||||||
Ctx__s_push(ctx(), (Expr*)FStringExpr__new(prev()->line, sv));
|
int count = 0;
|
||||||
return NULL;
|
int line = prev()->line;
|
||||||
|
while(true) {
|
||||||
|
if(match(TK_FSTR_END)) {
|
||||||
|
SequenceExpr* e = FStringExpr__new(line, count);
|
||||||
|
for(int i = count - 1; i >= 0; i--) {
|
||||||
|
Expr* item = Ctx__s_popx(ctx());
|
||||||
|
c11__setitem(Expr*, &e->items, i, item);
|
||||||
|
}
|
||||||
|
Ctx__s_push(ctx(), (Expr*)e);
|
||||||
|
return NULL;
|
||||||
|
} else if(match(TK_FSTR_CPNT)) {
|
||||||
|
// OP_LOAD_CONST
|
||||||
|
LiteralExpr* e = LiteralExpr__new(prev()->line, &prev()->value);
|
||||||
|
Ctx__s_push(ctx(), (Expr*)e);
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
// {a!r:.2f}
|
||||||
|
Error* err = EXPR(self);
|
||||||
|
if(err) return err;
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if(match(TK_FSTR_SPEC)) {
|
||||||
|
c11_sv spec = Token__sv(prev());
|
||||||
|
// ':.2f}' -> ':.2f'
|
||||||
|
spec.size--;
|
||||||
|
Expr* child = Ctx__s_popx(ctx());
|
||||||
|
FStringSpecExpr* e = FStringSpecExpr__new(prev()->line, child, spec);
|
||||||
|
Ctx__s_push(ctx(), (Expr*)e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Error* exprImag(Compiler* self) {
|
static Error* exprImag(Compiler* self) {
|
||||||
@ -2766,14 +2669,25 @@ Error* pk_compile(SourceData_ src, CodeObject* out) {
|
|||||||
Error* err = Lexer__process(src, &tokens);
|
Error* err = Lexer__process(src, &tokens);
|
||||||
if(err) return err;
|
if(err) return err;
|
||||||
|
|
||||||
// Token* data = (Token*)tokens.data;
|
#if 0
|
||||||
// printf("%s\n", src->filename->data);
|
Token* data = (Token*)tokens.data;
|
||||||
// for(int i = 0; i < tokens.count; i++) {
|
printf("%s\n", src->filename->data);
|
||||||
// Token* t = data + i;
|
for(int i = 0; i < tokens.count; i++) {
|
||||||
// c11_string* tmp = c11_string__new2(t->start, t->length);
|
Token* t = data + i;
|
||||||
// printf("[%d] %s: %s\n", t->line, TokenSymbols[t->type], tmp->data);
|
c11_string* tmp = c11_string__new2(t->start, t->length);
|
||||||
// c11_string__delete(tmp);
|
if(t->value.index == TokenValue_STR) {
|
||||||
// }
|
const char* value_str = t->value._str->data;
|
||||||
|
printf("[%d] %s: %s (value._str=%s)\n",
|
||||||
|
t->line,
|
||||||
|
TokenSymbols[t->type],
|
||||||
|
tmp->data,
|
||||||
|
value_str);
|
||||||
|
} else {
|
||||||
|
printf("[%d] %s: %s\n", t->line, TokenSymbols[t->type], tmp->data);
|
||||||
|
}
|
||||||
|
c11_string__delete(tmp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Compiler compiler;
|
Compiler compiler;
|
||||||
Compiler__ctor(&compiler, src, tokens);
|
Compiler__ctor(&compiler, src, tokens);
|
||||||
@ -2829,7 +2743,7 @@ const static PrattRule rules[TK__COUNT__] = {
|
|||||||
[TK_ID] = { exprName, },
|
[TK_ID] = { exprName, },
|
||||||
[TK_NUM] = { exprLiteral, },
|
[TK_NUM] = { exprLiteral, },
|
||||||
[TK_STR] = { exprLiteral, },
|
[TK_STR] = { exprLiteral, },
|
||||||
[TK_FSTR] = { exprFString, },
|
[TK_FSTR_BEGIN] = { exprFString, },
|
||||||
[TK_IMAG] = { exprImag, },
|
[TK_IMAG] = { exprImag, },
|
||||||
[TK_BYTES] = { exprBytes, },
|
[TK_BYTES] = { exprBytes, },
|
||||||
[TK_LBRACE] = { exprMap },
|
[TK_LBRACE] = { exprMap },
|
||||||
|
|||||||
@ -36,6 +36,8 @@ double TokenDeserializer__read_float(TokenDeserializer* self, char c);
|
|||||||
|
|
||||||
const static TokenValue EmptyTokenValue;
|
const static TokenValue EmptyTokenValue;
|
||||||
|
|
||||||
|
static Error* lex_one_token(Lexer* self, bool* eof, bool is_fstring);
|
||||||
|
|
||||||
static void Lexer__ctor(Lexer* self, SourceData_ src) {
|
static void Lexer__ctor(Lexer* self, SourceData_ src) {
|
||||||
PK_INCREF(src);
|
PK_INCREF(src);
|
||||||
self->src = src;
|
self->src = src;
|
||||||
@ -267,18 +269,24 @@ static Error* eat_name(Lexer* self) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Error* eat_string_until(Lexer* self, char quote, bool raw, c11_string** out) {
|
enum StringType { NORMAL_STRING, RAW_STRING, F_STRING, NORMAL_BYTES };
|
||||||
|
|
||||||
|
static Error* _eat_string(Lexer* self, c11_sbuf* buff, char quote, enum StringType type) {
|
||||||
|
bool is_raw = type == RAW_STRING;
|
||||||
|
bool is_fstring = type == F_STRING;
|
||||||
|
|
||||||
|
if(is_fstring) { add_token(self, TK_FSTR_BEGIN); }
|
||||||
|
|
||||||
// previous char is quote
|
// previous char is quote
|
||||||
bool quote3 = match_n_chars(self, 2, quote);
|
bool quote3 = match_n_chars(self, 2, quote);
|
||||||
c11_sbuf buff;
|
|
||||||
c11_sbuf__ctor(&buff);
|
|
||||||
while(true) {
|
while(true) {
|
||||||
char c = eatchar_include_newline(self);
|
char c = eatchar_include_newline(self);
|
||||||
if(c == quote) {
|
if(c == quote) {
|
||||||
if(quote3 && !match_n_chars(self, 2, quote)) {
|
if(quote3 && !match_n_chars(self, 2, quote)) {
|
||||||
c11_sbuf__write_char(&buff, c);
|
c11_sbuf__write_char(buff, c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// end of string
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(c == '\0') { return SyntaxError(self, "EOL while scanning string literal"); }
|
if(c == '\0') { return SyntaxError(self, "EOL while scanning string literal"); }
|
||||||
@ -286,47 +294,88 @@ static Error* eat_string_until(Lexer* self, char quote, bool raw, c11_string** o
|
|||||||
if(!quote3)
|
if(!quote3)
|
||||||
return SyntaxError(self, "EOL while scanning string literal");
|
return SyntaxError(self, "EOL while scanning string literal");
|
||||||
else {
|
else {
|
||||||
c11_sbuf__write_char(&buff, c);
|
c11_sbuf__write_char(buff, c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!raw && c == '\\') {
|
if(!is_raw && c == '\\') {
|
||||||
switch(eatchar_include_newline(self)) {
|
switch(eatchar_include_newline(self)) {
|
||||||
case '"': c11_sbuf__write_char(&buff, '"'); break;
|
case '"': c11_sbuf__write_char(buff, '"'); break;
|
||||||
case '\'': c11_sbuf__write_char(&buff, '\''); break;
|
case '\'': c11_sbuf__write_char(buff, '\''); break;
|
||||||
case '\\': c11_sbuf__write_char(&buff, '\\'); break;
|
case '\\': c11_sbuf__write_char(buff, '\\'); break;
|
||||||
case 'n': c11_sbuf__write_char(&buff, '\n'); break;
|
case 'n': c11_sbuf__write_char(buff, '\n'); break;
|
||||||
case 'r': c11_sbuf__write_char(&buff, '\r'); break;
|
case 'r': c11_sbuf__write_char(buff, '\r'); break;
|
||||||
case 't': c11_sbuf__write_char(&buff, '\t'); break;
|
case 't': c11_sbuf__write_char(buff, '\t'); break;
|
||||||
case 'b': c11_sbuf__write_char(&buff, '\b'); break;
|
case 'b': c11_sbuf__write_char(buff, '\b'); break;
|
||||||
case 'x': {
|
case 'x': {
|
||||||
char hex[3] = {eatchar(self), eatchar(self), '\0'};
|
char hex[3] = {eatchar(self), eatchar(self), '\0'};
|
||||||
int code;
|
int code;
|
||||||
if(sscanf(hex, "%x", &code) != 1) {
|
if(sscanf(hex, "%x", &code) != 1) {
|
||||||
return SyntaxError(self, "invalid hex char");
|
return SyntaxError(self, "invalid hex char");
|
||||||
}
|
}
|
||||||
c11_sbuf__write_char(&buff, (char)code);
|
c11_sbuf__write_char(buff, (char)code);
|
||||||
} break;
|
} break;
|
||||||
default: return SyntaxError(self, "invalid escape char");
|
default: return SyntaxError(self, "invalid escape char");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c11_sbuf__write_char(&buff, c);
|
if(is_fstring) {
|
||||||
|
if(c == '{') {
|
||||||
|
if(matchchar(self, '{')) {
|
||||||
|
// '{{' -> '{'
|
||||||
|
c11_sbuf__write_char(buff, '{');
|
||||||
|
} else {
|
||||||
|
// submit previous string
|
||||||
|
c11_string* res = c11_sbuf__submit(buff);
|
||||||
|
if(res->size > 0) {
|
||||||
|
TokenValue value = {TokenValue_STR, ._str = res};
|
||||||
|
add_token_with_value(self, TK_FSTR_CPNT, value);
|
||||||
|
} else {
|
||||||
|
c11_string__delete(res);
|
||||||
|
}
|
||||||
|
c11_sbuf__ctor(buff); // re-init buffer
|
||||||
|
|
||||||
|
// submit {expr} tokens
|
||||||
|
bool eof = false;
|
||||||
|
int token_count = self->nexts.count;
|
||||||
|
while(!eof) {
|
||||||
|
Error* err = lex_one_token(self, &eof, true);
|
||||||
|
if(err) return err;
|
||||||
|
}
|
||||||
|
if(self->nexts.count == token_count) {
|
||||||
|
// f'{}' is not allowed
|
||||||
|
return SyntaxError(self, "f-string: empty expression not allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(c == '}') {
|
||||||
|
if(matchchar(self, '}')) {
|
||||||
|
// '}}' -> '}'
|
||||||
|
c11_sbuf__write_char(buff, '}');
|
||||||
|
} else {
|
||||||
|
return SyntaxError(self, "f-string: single '}' is not allowed");
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
c11_sbuf__write_char(buff, c);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c11_sbuf__write_char(buff, c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*out = c11_sbuf__submit(&buff);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum StringType { NORMAL_STRING, RAW_STRING, F_STRING, NORMAL_BYTES };
|
c11_string* res = c11_sbuf__submit(buff);
|
||||||
|
TokenValue value = {TokenValue_STR, ._str = res};
|
||||||
|
|
||||||
static Error* eat_string(Lexer* self, char quote, enum StringType type) {
|
if(is_fstring) {
|
||||||
c11_string* s;
|
if(res->size > 0) {
|
||||||
Error* err = eat_string_until(self, quote, type == RAW_STRING, &s);
|
add_token_with_value(self, TK_FSTR_CPNT, value);
|
||||||
if(err) return err;
|
} else {
|
||||||
TokenValue value = {TokenValue_STR, ._str = s};
|
c11_string__delete(res);
|
||||||
if(type == F_STRING) {
|
}
|
||||||
add_token_with_value(self, TK_FSTR, value);
|
add_token(self, TK_FSTR_END);
|
||||||
} else if(type == NORMAL_BYTES) {
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type == NORMAL_BYTES) {
|
||||||
add_token_with_value(self, TK_BYTES, value);
|
add_token_with_value(self, TK_BYTES, value);
|
||||||
} else {
|
} else {
|
||||||
add_token_with_value(self, TK_STR, value);
|
add_token_with_value(self, TK_STR, value);
|
||||||
@ -334,6 +383,14 @@ static Error* eat_string(Lexer* self, char quote, enum StringType type) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Error* eat_string(Lexer* self, char quote, enum StringType type) {
|
||||||
|
c11_sbuf buff;
|
||||||
|
c11_sbuf__ctor(&buff);
|
||||||
|
Error* err = _eat_string(self, &buff, quote, type);
|
||||||
|
c11_sbuf__dtor(&buff);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static Error* eat_number(Lexer* self) {
|
static Error* eat_number(Lexer* self) {
|
||||||
const char* i = self->token_start;
|
const char* i = self->token_start;
|
||||||
while(is_possible_number_char(*i))
|
while(is_possible_number_char(*i))
|
||||||
@ -380,7 +437,22 @@ static Error* eat_number(Lexer* self) {
|
|||||||
return SyntaxError(self, "invalid number literal");
|
return SyntaxError(self, "invalid number literal");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Error* lex_one_token(Lexer* self, bool* eof) {
|
static Error* eat_fstring_spec(Lexer* self, bool* eof) {
|
||||||
|
while(true) {
|
||||||
|
char c = eatchar_include_newline(self);
|
||||||
|
if(c == '\n' || c == '\0') {
|
||||||
|
return SyntaxError(self, "EOL while scanning f-string format spec");
|
||||||
|
}
|
||||||
|
if(c == '}') {
|
||||||
|
add_token(self, TK_FSTR_SPEC);
|
||||||
|
*eof = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Error* lex_one_token(Lexer* self, bool* eof, bool is_fstring) {
|
||||||
*eof = false;
|
*eof = false;
|
||||||
while(*self->curr_char) {
|
while(*self->curr_char) {
|
||||||
self->token_start = self->curr_char;
|
self->token_start = self->curr_char;
|
||||||
@ -395,9 +467,20 @@ static Error* lex_one_token(Lexer* self, bool* eof) {
|
|||||||
case '#': skip_line_comment(self); break;
|
case '#': skip_line_comment(self); break;
|
||||||
case '~': add_token(self, TK_INVERT); return NULL;
|
case '~': add_token(self, TK_INVERT); return NULL;
|
||||||
case '{': add_token(self, TK_LBRACE); return NULL;
|
case '{': add_token(self, TK_LBRACE); return NULL;
|
||||||
case '}': add_token(self, TK_RBRACE); return NULL;
|
case '}': {
|
||||||
|
if(is_fstring) {
|
||||||
|
*eof = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
add_token(self, TK_RBRACE);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
case ',': add_token(self, TK_COMMA); return NULL;
|
case ',': add_token(self, TK_COMMA); return NULL;
|
||||||
case ':': add_token(self, TK_COLON); return NULL;
|
case ':': {
|
||||||
|
if(is_fstring && self->brackets_level == 0) { return eat_fstring_spec(self, eof); }
|
||||||
|
add_token(self, TK_COLON);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
case ';': add_token(self, TK_SEMICOLON); return NULL;
|
case ';': add_token(self, TK_SEMICOLON); return NULL;
|
||||||
case '(': add_token(self, TK_LPAREN); return NULL;
|
case '(': add_token(self, TK_LPAREN); return NULL;
|
||||||
case ')': add_token(self, TK_RPAREN); return NULL;
|
case ')': add_token(self, TK_RPAREN); return NULL;
|
||||||
@ -465,13 +548,15 @@ static Error* lex_one_token(Lexer* self, bool* eof) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
case '!':
|
case '!':
|
||||||
|
if(is_fstring && self->brackets_level == 0) {
|
||||||
|
if(matchchar(self, 'r')) { return eat_fstring_spec(self, eof); }
|
||||||
|
}
|
||||||
if(matchchar(self, '=')) {
|
if(matchchar(self, '=')) {
|
||||||
add_token(self, TK_NE);
|
add_token(self, TK_NE);
|
||||||
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
Error* err = SyntaxError(self, "expected '=' after '!'");
|
return SyntaxError(self, "expected '=' after '!'");
|
||||||
if(err) return err;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case '*':
|
case '*':
|
||||||
if(matchchar(self, '*')) {
|
if(matchchar(self, '*')) {
|
||||||
add_token(self, TK_POW); // '**'
|
add_token(self, TK_POW); // '**'
|
||||||
@ -512,6 +597,8 @@ static Error* lex_one_token(Lexer* self, bool* eof) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(is_fstring) return SyntaxError(self, "unterminated f-string expression");
|
||||||
|
|
||||||
self->token_start = self->curr_char;
|
self->token_start = self->curr_char;
|
||||||
while(self->indents.count > 1) {
|
while(self->indents.count > 1) {
|
||||||
c11_vector__pop(&self->indents);
|
c11_vector__pop(&self->indents);
|
||||||
@ -523,85 +610,10 @@ static Error* lex_one_token(Lexer* self, bool* eof) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Error* from_precompiled(Lexer* self) {
|
|
||||||
TokenDeserializer deserializer;
|
|
||||||
TokenDeserializer__ctor(&deserializer, self->src->source->data);
|
|
||||||
|
|
||||||
deserializer.curr += 5; // skip "pkpy:"
|
|
||||||
c11_sv version = TokenDeserializer__read_string(&deserializer, '\n');
|
|
||||||
|
|
||||||
if(c11_sv__cmp2(version, PK_VERSION) != 0) {
|
|
||||||
return SyntaxError(self, "precompiled version mismatch");
|
|
||||||
}
|
|
||||||
if(TokenDeserializer__read_uint(&deserializer, '\n') != (int64_t)self->src->mode) {
|
|
||||||
return SyntaxError(self, "precompiled mode mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = TokenDeserializer__read_count(&deserializer);
|
|
||||||
c11_vector* precompiled_tokens = &self->src->_precompiled_tokens;
|
|
||||||
for(int i = 0; i < count; i++) {
|
|
||||||
c11_sv item = TokenDeserializer__read_string(&deserializer, '\n');
|
|
||||||
c11_string* copied_item = c11_string__new2(item.data, item.size);
|
|
||||||
c11_vector__push(c11_string*, precompiled_tokens, copied_item);
|
|
||||||
}
|
|
||||||
|
|
||||||
count = TokenDeserializer__read_count(&deserializer);
|
|
||||||
for(int i = 0; i < count; i++) {
|
|
||||||
Token t;
|
|
||||||
t.type = (TokenIndex)TokenDeserializer__read_uint(&deserializer, ',');
|
|
||||||
if(is_raw_string_used(t.type)) {
|
|
||||||
int64_t index = TokenDeserializer__read_uint(&deserializer, ',');
|
|
||||||
c11_string* p = c11__getitem(c11_string*, precompiled_tokens, index);
|
|
||||||
t.start = p->data;
|
|
||||||
t.length = p->size;
|
|
||||||
} else {
|
|
||||||
t.start = NULL;
|
|
||||||
t.length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(TokenDeserializer__match_char(&deserializer, ',')) {
|
|
||||||
t.line = c11_vector__back(Token, &self->nexts).line;
|
|
||||||
} else {
|
|
||||||
t.line = (int)TokenDeserializer__read_uint(&deserializer, ',');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(TokenDeserializer__match_char(&deserializer, ',')) {
|
|
||||||
t.brackets_level = c11_vector__back(Token, &self->nexts).brackets_level;
|
|
||||||
} else {
|
|
||||||
t.brackets_level = (int)TokenDeserializer__read_uint(&deserializer, ',');
|
|
||||||
}
|
|
||||||
|
|
||||||
char type = (*deserializer.curr++); // read_char
|
|
||||||
switch(type) {
|
|
||||||
case 'I': {
|
|
||||||
int64_t res = TokenDeserializer__read_uint(&deserializer, '\n');
|
|
||||||
t.value = (TokenValue){TokenValue_I64, ._i64 = res};
|
|
||||||
} break;
|
|
||||||
case 'F': {
|
|
||||||
double res = TokenDeserializer__read_float(&deserializer, '\n');
|
|
||||||
t.value = (TokenValue){TokenValue_F64, ._f64 = res};
|
|
||||||
} break;
|
|
||||||
case 'S': {
|
|
||||||
c11_string* res = TokenDeserializer__read_string_from_hex(&deserializer, '\n');
|
|
||||||
t.value = (TokenValue){TokenValue_STR, ._str = res};
|
|
||||||
} break;
|
|
||||||
default: t.value = EmptyTokenValue; break;
|
|
||||||
}
|
|
||||||
c11_vector__push(Token, &self->nexts, t);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Error* Lexer__process(SourceData_ src, TokenArray* out_tokens) {
|
Error* Lexer__process(SourceData_ src, TokenArray* out_tokens) {
|
||||||
Lexer lexer;
|
Lexer lexer;
|
||||||
Lexer__ctor(&lexer, src);
|
Lexer__ctor(&lexer, src);
|
||||||
|
|
||||||
if(src->is_precompiled) {
|
|
||||||
Error* err = from_precompiled(&lexer);
|
|
||||||
// TODO: set out tokens
|
|
||||||
Lexer__dtor(&lexer);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
// push initial tokens
|
// push initial tokens
|
||||||
Token sof =
|
Token sof =
|
||||||
{TK_SOF, lexer.token_start, 0, lexer.current_line, lexer.brackets_level, EmptyTokenValue};
|
{TK_SOF, lexer.token_start, 0, lexer.current_line, lexer.brackets_level, EmptyTokenValue};
|
||||||
@ -610,7 +622,7 @@ Error* Lexer__process(SourceData_ src, TokenArray* out_tokens) {
|
|||||||
|
|
||||||
bool eof = false;
|
bool eof = false;
|
||||||
while(!eof) {
|
while(!eof) {
|
||||||
void* err = lex_one_token(&lexer, &eof);
|
void* err = lex_one_token(&lexer, &eof, false);
|
||||||
if(err) {
|
if(err) {
|
||||||
Lexer__dtor(&lexer);
|
Lexer__dtor(&lexer);
|
||||||
return err;
|
return err;
|
||||||
@ -623,102 +635,6 @@ Error* Lexer__process(SourceData_ src, TokenArray* out_tokens) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error* Lexer__process_and_dump(SourceData_ src, c11_string** out) {
|
|
||||||
assert(!src->is_precompiled);
|
|
||||||
TokenArray nexts; // output tokens
|
|
||||||
Error* err = Lexer__process(src, &nexts);
|
|
||||||
if(err) return err;
|
|
||||||
|
|
||||||
c11_sbuf ss;
|
|
||||||
c11_sbuf__ctor(&ss);
|
|
||||||
|
|
||||||
// L1: version string
|
|
||||||
c11_sbuf__write_cstr(&ss, "pkpy:" PK_VERSION "\n");
|
|
||||||
// L2: mode
|
|
||||||
c11_sbuf__write_int(&ss, (int)src->mode);
|
|
||||||
c11_sbuf__write_char(&ss, '\n');
|
|
||||||
|
|
||||||
c11_smallmap_s2n token_indices;
|
|
||||||
c11_smallmap_s2n__ctor(&token_indices);
|
|
||||||
|
|
||||||
c11__foreach(Token, &nexts, token) {
|
|
||||||
if(is_raw_string_used(token->type)) {
|
|
||||||
c11_sv token_sv = {token->start, token->length};
|
|
||||||
if(!c11_smallmap_s2n__contains(&token_indices, token_sv)) {
|
|
||||||
c11_smallmap_s2n__set(&token_indices, token_sv, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// L3: raw string count
|
|
||||||
c11_sbuf__write_char(&ss, '=');
|
|
||||||
c11_sbuf__write_int(&ss, token_indices.count);
|
|
||||||
c11_sbuf__write_char(&ss, '\n');
|
|
||||||
|
|
||||||
uint16_t index = 0;
|
|
||||||
for(int i = 0; i < token_indices.count; i++) {
|
|
||||||
c11_smallmap_s2n_KV* kv = c11__at(c11_smallmap_s2n_KV, &token_indices, i);
|
|
||||||
// L4: raw strings
|
|
||||||
c11_sbuf__write_cstrn(&ss, kv->key.data, kv->key.size);
|
|
||||||
kv->value = index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// L5: token count
|
|
||||||
c11_sbuf__write_char(&ss, '=');
|
|
||||||
c11_sbuf__write_int(&ss, nexts.count);
|
|
||||||
c11_sbuf__write_char(&ss, '\n');
|
|
||||||
|
|
||||||
for(int i = 0; i < nexts.count; i++) {
|
|
||||||
const Token* token = c11__at(Token, &nexts, i);
|
|
||||||
c11_sbuf__write_int(&ss, (int)token->type);
|
|
||||||
c11_sbuf__write_char(&ss, ',');
|
|
||||||
|
|
||||||
if(is_raw_string_used(token->type)) {
|
|
||||||
uint16_t* p =
|
|
||||||
c11_smallmap_s2n__try_get(&token_indices, (c11_sv){token->start, token->length});
|
|
||||||
assert(p != NULL);
|
|
||||||
c11_sbuf__write_int(&ss, (int)*p);
|
|
||||||
c11_sbuf__write_char(&ss, ',');
|
|
||||||
}
|
|
||||||
if(i > 0 && c11__getitem(Token, &nexts, i - 1).line == token->line) {
|
|
||||||
c11_sbuf__write_char(&ss, ',');
|
|
||||||
} else {
|
|
||||||
c11_sbuf__write_int(&ss, token->line);
|
|
||||||
c11_sbuf__write_char(&ss, ',');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i > 0 && c11__getitem(Token, &nexts, i - 1).brackets_level == token->brackets_level) {
|
|
||||||
c11_sbuf__write_char(&ss, ',');
|
|
||||||
} else {
|
|
||||||
c11_sbuf__write_int(&ss, token->brackets_level);
|
|
||||||
c11_sbuf__write_char(&ss, ',');
|
|
||||||
}
|
|
||||||
// visit token value
|
|
||||||
switch(token->value.index) {
|
|
||||||
case TokenValue_EMPTY: break;
|
|
||||||
case TokenValue_I64:
|
|
||||||
c11_sbuf__write_char(&ss, 'I');
|
|
||||||
c11_sbuf__write_int(&ss, token->value._i64);
|
|
||||||
break;
|
|
||||||
case TokenValue_F64:
|
|
||||||
c11_sbuf__write_char(&ss, 'F');
|
|
||||||
c11_sbuf__write_f64(&ss, token->value._f64, -1);
|
|
||||||
break;
|
|
||||||
case TokenValue_STR: {
|
|
||||||
c11_sbuf__write_char(&ss, 'S');
|
|
||||||
c11_sv sv = c11_string__sv(token->value._str);
|
|
||||||
for(int i = 0; i < sv.size; i++) {
|
|
||||||
c11_sbuf__write_hex(&ss, sv.data[i], false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c11_sbuf__write_char(&ss, '\n');
|
|
||||||
}
|
|
||||||
*out = c11_sbuf__submit(&ss);
|
|
||||||
c11_smallmap_s2n__dtor(&token_indices);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TokenArray__dtor(TokenArray* self) {
|
void TokenArray__dtor(TokenArray* self) {
|
||||||
Token* data = self->data;
|
Token* data = self->data;
|
||||||
for(int i = 0; i < self->count; i++) {
|
for(int i = 0; i < self->count; i++) {
|
||||||
@ -734,7 +650,10 @@ const char* TokenSymbols[] = {
|
|||||||
"@id",
|
"@id",
|
||||||
"@num",
|
"@num",
|
||||||
"@str",
|
"@str",
|
||||||
"@fstr",
|
"@fstr-begin", // TK_FSTR_BEGIN
|
||||||
|
"@fstr-cpnt", // TK_FSTR_CPNT
|
||||||
|
"@fstr-spec", // TK_FSTR_SPEC
|
||||||
|
"@fstr-end", // TK_FSTR_END
|
||||||
"@bytes",
|
"@bytes",
|
||||||
"@imag",
|
"@imag",
|
||||||
"@indent",
|
"@indent",
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
#include "pocketpy/common/config.h"
|
#include "pocketpy/common/config.h"
|
||||||
|
#include "pocketpy/common/str.h"
|
||||||
#include "pocketpy/common/utils.h"
|
#include "pocketpy/common/utils.h"
|
||||||
#include "pocketpy/interpreter/frame.h"
|
#include "pocketpy/interpreter/frame.h"
|
||||||
#include "pocketpy/interpreter/vm.h"
|
#include "pocketpy/interpreter/vm.h"
|
||||||
@ -10,7 +11,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
static bool stack_unpack_sequence(VM* self, uint16_t arg);
|
static bool stack_unpack_sequence(VM* self, uint16_t arg);
|
||||||
static bool format_object(py_Ref obj, c11_sv spec);
|
static bool stack_format_object(VM* self, c11_sv spec);
|
||||||
|
|
||||||
#define DISPATCH() \
|
#define DISPATCH() \
|
||||||
do { \
|
do { \
|
||||||
@ -469,8 +470,9 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
}
|
}
|
||||||
case OP_BUILD_BYTES: {
|
case OP_BUILD_BYTES: {
|
||||||
int size;
|
int size;
|
||||||
const char* data = py_tostrn(TOP(), &size);
|
py_Ref string = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
||||||
unsigned char* p = py_newbytes(TOP(), size);
|
const char* data = py_tostrn(string, &size);
|
||||||
|
unsigned char* p = py_newbytes(SP()++, size);
|
||||||
memcpy(p, data, size);
|
memcpy(p, data, size);
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
@ -660,11 +662,6 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
DISPATCH_JUMP_ABSOLUTE(target);
|
DISPATCH_JUMP_ABSOLUTE(target);
|
||||||
}
|
}
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
case OP_REPR: {
|
|
||||||
if(!py_repr(TOP())) goto __ERROR;
|
|
||||||
py_assign(TOP(), py_retval());
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
case OP_CALL: {
|
case OP_CALL: {
|
||||||
ManagedHeap__collect_if_needed(&self->heap);
|
ManagedHeap__collect_if_needed(&self->heap);
|
||||||
vectorcall_opcall(byte.arg & 0xFF, byte.arg >> 8);
|
vectorcall_opcall(byte.arg & 0xFF, byte.arg >> 8);
|
||||||
@ -1022,21 +1019,10 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
//////////////////
|
//////////////////
|
||||||
case OP_FSTRING_EVAL: {
|
|
||||||
py_TValue* code = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
|
||||||
assert(py_istype(code, tp_code));
|
|
||||||
py_newglobals(SP()++);
|
|
||||||
py_newlocals(SP()++);
|
|
||||||
PUSH(code);
|
|
||||||
if(!pk_exec(py_touserdata(code), frame->module)) goto __ERROR;
|
|
||||||
PUSH(py_retval());
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
case OP_FORMAT_STRING: {
|
case OP_FORMAT_STRING: {
|
||||||
py_Ref spec = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
py_Ref spec = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
||||||
bool ok = format_object(TOP(), py_tosv(spec));
|
bool ok = stack_format_object(self, py_tosv(spec));
|
||||||
if(!ok) goto __ERROR;
|
if(!ok) goto __ERROR;
|
||||||
py_assign(TOP(), py_retval());
|
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
default: c11__unreachedable();
|
default: c11__unreachedable();
|
||||||
@ -1132,9 +1118,31 @@ static bool stack_unpack_sequence(VM* self, uint16_t arg) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool format_object(py_Ref val, c11_sv spec) {
|
static bool stack_format_object(VM* self, c11_sv spec) {
|
||||||
|
// format TOS via `spec` inplace
|
||||||
|
// spec: '!r:.2f', '.2f'
|
||||||
|
py_StackRef val = TOP();
|
||||||
if(spec.size == 0) return py_str(val);
|
if(spec.size == 0) return py_str(val);
|
||||||
|
|
||||||
|
if(spec.data[0] == '!'){
|
||||||
|
if(c11_sv__startswith(spec, (c11_sv){"!r", 2})){
|
||||||
|
spec.data += 2;
|
||||||
|
spec.size -= 2;
|
||||||
|
if(!py_repr(val)) return false;
|
||||||
|
py_assign(val, py_retval());
|
||||||
|
if(spec.size == 0) return true;
|
||||||
|
}else{
|
||||||
|
return ValueError("invalid conversion specifier (only !r is supported)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(spec.size > 0);
|
||||||
|
|
||||||
|
if(spec.data[0] == ':'){
|
||||||
|
spec.data++;
|
||||||
|
spec.size--;
|
||||||
|
}
|
||||||
|
|
||||||
char type;
|
char type;
|
||||||
switch(spec.data[spec.size - 1]) {
|
switch(spec.data[spec.size - 1]) {
|
||||||
case 'f':
|
case 'f':
|
||||||
@ -1253,6 +1261,7 @@ static bool format_object(py_Ref val, c11_sv spec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c11_string__delete(body);
|
c11_string__delete(body);
|
||||||
c11_sbuf__py_submit(&buf, py_retval());
|
// inplace update
|
||||||
|
c11_sbuf__py_submit(&buf, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
#include "pocketpy/interpreter/gc.h"
|
#include "pocketpy/interpreter/heap.h"
|
||||||
#include "pocketpy/common/memorypool.h"
|
#include "pocketpy/common/memorypool.h"
|
||||||
#include "pocketpy/objects/base.h"
|
#include "pocketpy/objects/base.h"
|
||||||
|
|
||||||
@ -6,10 +6,12 @@
|
|||||||
|
|
||||||
static bool get_ns(int64_t* out) {
|
static bool get_ns(int64_t* out) {
|
||||||
struct timespec tms;
|
struct timespec tms;
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
clock_gettime(CLOCK_REALTIME, &tms);
|
||||||
|
#else
|
||||||
/* The C11 way */
|
/* The C11 way */
|
||||||
if(!timespec_get(&tms, TIME_UTC)) {
|
timespec_get(&tms, TIME_UTC);
|
||||||
return py_exception(tp_OSError, "%s", "timespec_get() failed");
|
#endif
|
||||||
}
|
|
||||||
/* seconds, multiplied with 1 billion */
|
/* seconds, multiplied with 1 billion */
|
||||||
int64_t nanos = tms.tv_sec * NANOS_PER_SEC;
|
int64_t nanos = tms.tv_sec * NANOS_PER_SEC;
|
||||||
/* Add full nanoseconds */
|
/* Add full nanoseconds */
|
||||||
|
|||||||
@ -646,6 +646,10 @@ py_Type pk_bytes__register() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool py_str(py_Ref val) {
|
bool py_str(py_Ref val) {
|
||||||
|
if(val->type == tp_str) {
|
||||||
|
py_assign(py_retval(), val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
py_Ref tmp = py_tpfindmagic(val->type, __str__);
|
py_Ref tmp = py_tpfindmagic(val->type, __str__);
|
||||||
if(!tmp) return py_repr(val);
|
if(!tmp) return py_repr(val);
|
||||||
return py_call(tmp, 1, val);
|
return py_call(tmp, 1, val);
|
||||||
|
|||||||
@ -20,6 +20,9 @@ assert s == 'asdasd\nasds1321321321测试\\测试'
|
|||||||
t = 4
|
t = 4
|
||||||
assert f'123{t}56789' == '123456789'
|
assert f'123{t}56789' == '123456789'
|
||||||
|
|
||||||
|
assert f'{{' == '{'
|
||||||
|
assert f'}}' == '}'
|
||||||
|
|
||||||
b = 123
|
b = 123
|
||||||
s = f'''->->{s}<-<-
|
s = f'''->->{s}<-<-
|
||||||
{b}
|
{b}
|
||||||
@ -117,10 +120,11 @@ class A:
|
|||||||
return 'A'
|
return 'A'
|
||||||
|
|
||||||
a = A()
|
a = A()
|
||||||
|
assert f'{a!r}' == 'A()'
|
||||||
assert f'{a!r:10}' == 'A() '
|
assert f'{a!r:10}' == 'A() '
|
||||||
assert f'{a!s:10}' == 'A '
|
assert f'{a:10}' == 'A '
|
||||||
assert f'{a:10}' == 'A '
|
assert f'{a:10}' == 'A '
|
||||||
|
|
||||||
assert f'{A()!r:10}' == 'A() '
|
assert f'{A()!r:10}' == 'A() '
|
||||||
assert f'{A()!s:10}' == 'A '
|
assert f'{A():10}' == 'A '
|
||||||
assert f'{A():10}' == 'A '
|
assert f'{A():10}' == 'A '
|
||||||
@ -89,8 +89,6 @@ try:
|
|||||||
exit(1)
|
exit(1)
|
||||||
except UnboundLocalError:
|
except UnboundLocalError:
|
||||||
pass
|
pass
|
||||||
except NameError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
g = 1
|
g = 1
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user