Merge branch 'blueloveTH:main' into datetime-file

This commit is contained in:
M Sai Kiran 2023-10-08 12:10:45 +05:30 committed by GitHub
commit 1ad67650ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 208 additions and 67 deletions

View File

@ -1,5 +1,15 @@
name: build name: build
on: [push, pull_request] on:
push:
paths-ignore:
- 'docs/**'
- 'web/**'
- '**.md'
pull_request:
paths-ignore:
- 'docs/**'
- 'web/**'
- '**.md'
jobs: jobs:
build_win: build_win:
runs-on: windows-latest runs-on: windows-latest

View File

@ -38,7 +38,7 @@ These platforms are officially tested.
+ Android 64-bit / 32-bit + Android 64-bit / 32-bit
+ iOS 64-bit + iOS 64-bit
+ Emscripten 32-bit + Emscripten 32-bit
+ Raspberry Pi 64-bit + Raspberry Pi OS 64-bit
## Quick Start ## Quick Start
@ -56,11 +56,12 @@ To compile it with your project, these flags must be set:
+ `--std=c++17` flag must be set + `--std=c++17` flag must be set
+ Exception must be enabled + Exception must be enabled
+ For MSVC, `/utf-8` flag must be set
For development build on Linux, use this snippet. For development build on Linux, use this snippet.
```bash ```bash
# prerequisites # prerequisites
sudo apt-get install libc++-dev libc++abi-dev clang++ sudo apt-get install libc++-dev libc++abi-dev clang
# build the repo # build the repo
bash build.sh bash build.sh
# unittest # unittest
@ -114,26 +115,26 @@ for a quick overview of the supported features.
| Name | Example | Supported | | Name | Example | Supported |
| --------------- | ------------------------------- | --------- | | --------------- | ------------------------------- | --------- |
| If Else | `if..else..elif` | YES | | If Else | `if..else..elif` | |
| Loop | `for/while/break/continue` | YES | | Loop | `for/while/break/continue` | |
| Function | `def f(x,*args,y=1):` | YES | | Function | `def f(x,*args,y=1):` | |
| Subclass | `class A(B):` | YES | | Subclass | `class A(B):` | |
| List | `[1, 2, 'a']` | YES | | List | `[1, 2, 'a']` | |
| ListComp | `[i for i in range(5)]` | YES | | ListComp | `[i for i in range(5)]` | |
| Slice | `a[1:2], a[:2], a[1:]` | YES | | Slice | `a[1:2], a[:2], a[1:]` | |
| Tuple | `(1, 2, 'a')` | YES | | Tuple | `(1, 2, 'a')` | |
| Dict | `{'a': 1, 'b': 2}` | YES | | Dict | `{'a': 1, 'b': 2}` | |
| F-String | `f'value is {x}'` | YES | | F-String | `f'value is {x}'` | |
| Unpacking | `a, b = 1, 2` | YES | | Unpacking | `a, b = 1, 2` | |
| Star Unpacking | `a, *b = [1, 2, 3]` | YES | | Star Unpacking | `a, *b = [1, 2, 3]` | |
| Exception | `raise/try..catch` | YES | | Exception | `raise/try..catch` | |
| Dynamic Code | `eval()/exec()` | YES | | Dynamic Code | `eval()/exec()` | |
| Reflection | `hasattr()/getattr()/setattr()` | YES | | Reflection | `hasattr()/getattr()/setattr()` | |
| Import | `import/from..import` | YES | | Import | `import/from..import` | |
| Context Block | `with <expr> as <id>:` | YES | | Context Block | `with <expr> as <id>:` | |
| Type Annotation | `def f(a:int, b:float=1)` | YES | | Type Annotation | `def f(a:int, b:float=1)` | |
| Generator | `yield i` | YES | | Generator | `yield i` | |
| Decorator | `@cache` | YES | | Decorator | `@cache` | |
## Contribution ## Contribution

View File

@ -27,7 +27,7 @@ pkpy 支持任何拥有 C++17 编译器的平台。
+ Android 64-bit / 32-bit + Android 64-bit / 32-bit
+ iOS 64-bit + iOS 64-bit
+ Emscripten 32-bit + Emscripten 32-bit
+ Raspberry Pi 64-bit + Raspberry Pi OS 64-bit
## 快速上手 ## 快速上手
@ -76,25 +76,25 @@ int main(){
| 特性 | 示例 | 支持 | | 特性 | 示例 | 支持 |
| ------------ | ------------------------------- | ---- | | ------------ | ------------------------------- | ---- |
| 分支 | `if..else..elif` | YES | | 分支 | `if..else..elif` | |
| 循环 | `for/while/break/continue` | YES | | 循环 | `for/while/break/continue` | |
| 函数 | `def f(x,*args,y=1):` | YES | | 函数 | `def f(x,*args,y=1):` | |
| 类与继承 | `class A(B):` | YES | | 类与继承 | `class A(B):` | |
| 列表 | `[1, 2, 'a']` | YES | | 列表 | `[1, 2, 'a']` | |
| 列表生成式 | `[i for i in range(5)]` | YES | | 列表生成式 | `[i for i in range(5)]` | |
| 切片 | `a[1:2], a[:2], a[1:]` | YES | | 切片 | `a[1:2], a[:2], a[1:]` | |
| 元组 | `(1, 2, 'a')` | YES | | 元组 | `(1, 2, 'a')` | |
| 字典 | `{'a': 1, 'b': 2}` | YES | | 字典 | `{'a': 1, 'b': 2}` | |
| 格式化字符串 | `f'value is {x}'` | YES | | 格式化字符串 | `f'value is {x}'` | |
| 序列解包 | `a, b = 1, 2` | YES | | 序列解包 | `a, b = 1, 2` | |
| 异常 | `raise/try..catch` | YES | | 异常 | `raise/try..catch` | |
| 动态分发 | `eval()/exec()` | YES | | 动态分发 | `eval()/exec()` | |
| 反射 | `hasattr()/getattr()/setattr()` | YES | | 反射 | `hasattr()/getattr()/setattr()` | |
| 导入模块 | `import/from..import` | YES | | 导入模块 | `import/from..import` | |
| 上下文管理器 | `with <expr> as <id>:` | YES | | 上下文管理器 | `with <expr> as <id>:` | |
| 类型标注 | `def f(a:int, b:float=1)` | YES | | 类型标注 | `def f(a:int, b:float=1)` | ✅ |
| 生成器 | `yield i` | YES | | 生成器 | `yield i` | |
| 装饰器 | `@cache` | YES | | 装饰器 | `@cache` | ✅ |
## 参考 ## 参考

View File

@ -10,7 +10,7 @@ fi
# Check if clang++ is installed # Check if clang++ is installed
if ! type -P clang++ >/dev/null 2>&1; then if ! type -P clang++ >/dev/null 2>&1; then
echo "clang++ is required and not installed. Kindly install it." echo "clang++ is required and not installed. Kindly install it."
echo "Run: sudo apt-get install libc++-dev libc++abi-dev clang++" echo "Run: sudo apt-get install libc++-dev libc++abi-dev clang"
exit 1 exit 1
fi fi

View File

@ -160,4 +160,4 @@ For example, `vm->bind__add__` is preferred over `vm->bind_method<1>(type, "__ad
### Further reading ### Further reading
See [linalg.h](https://github.com/blueloveTH/pocketpy/blob/main/src/linalg.h) for a complete example used by `linalg` module. See [linalg.h](https://github.com/blueloveTH/pocketpy/blob/main/src/random.cpp) for a complete example used by `random` module.

View File

@ -39,7 +39,7 @@ These platforms are officially tested.
+ Android 64-bit / 32-bit + Android 64-bit / 32-bit
+ iOS 64-bit + iOS 64-bit
+ Emscripten 32-bit + Emscripten 32-bit
+ Raspberry Pi 64-bit + Raspberry Pi OS 64-bit
## Sponsor me ## Sponsor me

View File

@ -43,6 +43,7 @@ To compile it with your project, these flags must be set:
+ `--std=c++17` flag must be set + `--std=c++17` flag must be set
+ Exception must be enabled + Exception must be enabled
+ For MSVC, `/utf-8` flag must be set
For emscripten, you must enable exceptions to make pocketpy work properly. For emscripten, you must enable exceptions to make pocketpy work properly.
See https://emscripten.org/docs/porting/exceptions.html. See https://emscripten.org/docs/porting/exceptions.html.

View File

@ -96,14 +96,115 @@ def sorted(iterable, reverse=False, key=None):
return a return a
##### str ##### ##### str #####
def __f(self, *args): def __f(self: str, *args, **kwargs) -> str:
if '{}' in self: def tokenizeString(s: str):
for i in range(len(args)): tokens = []
self = self.replace('{}', str(args[i]), 1) L, R = 0,0
mode = None
curArg = 0
# lookingForKword = False
while(R<len(s)):
curChar = s[R]
nextChar = s[R+1] if R+1<len(s) else ''
# Invalid case 1: stray '}' encountered, example: "ABCD EFGH {name} IJKL}", "Hello {vv}}", "HELLO {0} WORLD}"
if curChar == '}' and nextChar != '}':
raise ValueError("Single '}' encountered in format string")
# Valid Case 1: Escaping case, we escape "{{ or "}}" to be "{" or "}", example: "{{}}", "{{My Name is {0}}}"
if (curChar == '{' and nextChar == '{') or (curChar == '}' and nextChar == '}'):
if (L<R): # Valid Case 1.1: make sure we are not adding empty string
tokens.append(s[L:R]) # add the string before the escape
tokens.append(curChar) # Valid Case 1.2: add the escape char
L = R+2 # move the left pointer to the next char
R = R+2 # move the right pointer to the next char
continue
# Valid Case 2: Regular command line arg case: example: "ABCD EFGH {} IJKL", "{}", "HELLO {} WORLD"
elif curChar == '{' and nextChar == '}':
if mode is not None and mode != 'auto':
# Invalid case 2: mixing automatic and manual field specifications -- example: "ABCD EFGH {name} IJKL {}", "Hello {vv} {}", "HELLO {0} WORLD {}"
raise ValueError("Cannot switch from manual field numbering to automatic field specification")
mode = 'auto'
if(L<R): # Valid Case 2.1: make sure we are not adding empty string
tokens.append(s[L:R]) # add the string before the special marker for the arg
tokens.append("{"+str(curArg)+"}") # Valid Case 2.2: add the special marker for the arg
curArg+=1 # increment the arg position, this will be used for referencing the arg later
L = R+2 # move the left pointer to the next char
R = R+2 # move the right pointer to the next char
continue
# Valid Case 3: Key-word arg case: example: "ABCD EFGH {name} IJKL", "Hello {vv}", "HELLO {name} WORLD"
elif (curChar == '{'):
if mode is not None and mode != 'manual':
# # Invalid case 2: mixing automatic and manual field specifications -- example: "ABCD EFGH {} IJKL {name}", "Hello {} {1}", "HELLO {} WORLD {name}"
raise ValueError("Cannot switch from automatic field specification to manual field numbering")
mode = 'manual'
if(L<R): # Valid case 3.1: make sure we are not adding empty string
tokens.append(s[L:R]) # add the string before the special marker for the arg
# We look for the end of the keyword
kwL = R # Keyword left pointer
kwR = R+1 # Keyword right pointer
while(kwR<len(s) and s[kwR]!='}'):
if s[kwR] == '{': # Invalid case 3: stray '{' encountered, example: "ABCD EFGH {n{ame} IJKL {", "Hello {vv{}}", "HELLO {0} WOR{LD}"
raise ValueError("Unexpected '{' in field name")
kwR += 1
# Valid case 3.2: We have successfully found the end of the keyword
if kwR<len(s) and s[kwR] == '}':
tokens.append(s[kwL:kwR+1]) # add the special marker for the arg
L = kwR+1
R = kwR+1
# Invalid case 4: We didn't find the end of the keyword, throw error
else: else:
for i in range(len(args)): raise ValueError("Expected '}' before end of string")
self = self.replace('{'+str(i)+'}', str(args[i])) continue
return self
R = R+1
# Valid case 4: We have reached the end of the string, add the remaining string to the tokens
if L<R:
tokens.append(s[L:R])
# print(tokens)
return tokens
tokens = tokenizeString(self)
argMap = {}
for i, a in enumerate(args):
argMap[str(i)] = a
final_tokens = []
for t in tokens:
if t[0] == '{' and t[-1] == '}':
key = t[1:-1]
argMapVal = argMap.get(key, None)
kwargsVal = kwargs.get(key, None)
if argMapVal is None and kwargsVal is None:
raise ValueError("No arg found for token: "+t)
elif argMapVal is not None:
final_tokens.append(str(argMapVal))
else:
final_tokens.append(str(kwargsVal))
else:
final_tokens.append(t)
return ''.join(final_tokens)
str.format = __f str.format = __f
def __f(self, chars=None): def __f(self, chars=None):

View File

@ -760,12 +760,6 @@ __NEXT_STEP:;
PK_UNUSED(e); PK_UNUSED(e);
PyObject* obj = POPX(); PyObject* obj = POPX();
Exception& _e = CAST(Exception&, obj); Exception& _e = CAST(Exception&, obj);
int actual_ip = frame->_ip;
if(_e._ip_on_error >= 0 && _e._code_on_error == (void*)frame->co) actual_ip = _e._ip_on_error;
int current_line = frame->co->lines[actual_ip]; // current line
auto current_f_name = frame->co->name.sv(); // current function name
if(frame->_callable == nullptr) current_f_name = ""; // not in a function
_e.st_push(frame->co->src->snapshot(current_line, nullptr, current_f_name));
_pop_frame(); _pop_frame();
if(callstack.empty()){ if(callstack.empty()){
#if PK_DEBUG_FULL_EXCEPTION #if PK_DEBUG_FULL_EXCEPTION

View File

@ -1082,13 +1082,21 @@ void VM::_error(Exception e){
} }
void VM::_raise(bool re_raise){ void VM::_raise(bool re_raise){
Frame* top = top_frame().get(); Frame* frame = top_frame().get();
if(!re_raise){
Exception& e = PK_OBJ_GET(Exception, s_data.top()); Exception& e = PK_OBJ_GET(Exception, s_data.top());
e._ip_on_error = top->_ip; if(!re_raise){
e._code_on_error = (void*)top->co; e._ip_on_error = frame->_ip;
e._code_on_error = (void*)frame->co;
} }
bool ok = top->jump_to_exception_handler(); bool ok = frame->jump_to_exception_handler();
int actual_ip = frame->_ip;
if(e._ip_on_error >= 0 && e._code_on_error == (void*)frame->co) actual_ip = e._ip_on_error;
int current_line = frame->co->lines[actual_ip]; // current line
auto current_f_name = frame->co->name.sv(); // current function name
if(frame->_callable == nullptr) current_f_name = ""; // not in a function
e.st_push(frame->co->src->snapshot(current_line, nullptr, current_f_name));
if(ok) throw HandledException(); if(ok) throw HandledException();
else throw UnhandledException(); else throw UnhandledException();
} }

View File

@ -97,6 +97,19 @@ assert "{0} {1} {2}".format("I", "love", "Python") == "I love Python"
assert "{2} {1} {0}".format("I", "love", "Python") == "Python love I" assert "{2} {1} {0}".format("I", "love", "Python") == "Python love I"
assert "{0}{1}{0}".format("abra", "cad") == "abracadabra" assert "{0}{1}{0}".format("abra", "cad") == "abracadabra"
assert "{k}={v}".format(k="key", v="value") == "key=value"
assert "{k}={k}".format(k="key") == "key=key"
assert "{0}={1}".format('{0}', '{1}') == "{0}={1}"
assert "{{{0}}}".format(1) == "{1}"
assert "{0}{1}{1}".format(1, 2, 3) == "122"
try:
"{0}={1}}".format(1, 2)
exit(1)
except ValueError:
pass
assert "{{{}xxx{}x}}".format(1, 2) == "{1xxx2x}"
assert "{{abc}}".format() == "{abc}"
# 3rd slice # 3rd slice
a = "Hello, World!" a = "Hello, World!"
assert a[::-1] == "!dlroW ,olleH" assert a[::-1] == "!dlroW ,olleH"

View File

@ -16,6 +16,15 @@ a = {
import json import json
assert json.loads("1") == 1
assert json.loads('"1"') == "1"
assert json.loads("0.0") == 0.0
assert json.loads("[1, 2]") == [1, 2]
assert json.loads("null") == None
assert json.loads("true") == True
assert json.loads("false") == False
assert json.loads("{}") == {}
_j = json.dumps(a) _j = json.dumps(a)
_a = json.loads(_j) _a = json.loads(_j)

View File

@ -6,5 +6,9 @@ try:
except KeyError: except KeyError:
s = traceback.format_exc() s = traceback.format_exc()
assert s == r'''Traceback (most recent call last): ok = s == '''Traceback (most recent call last):
File "80_traceback.py", line 5
b = a[6]
KeyError: 6''' KeyError: 6'''
assert ok, s