From 0fef360caf43278c6536e08312a1a0f876581674 Mon Sep 17 00:00:00 2001 From: "S. Mahmudul Hasan" Date: Tue, 3 Oct 2023 13:45:21 -0400 Subject: [PATCH 01/13] Added better string formatting as required by Issue#138 --- python/builtins.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/python/builtins.py b/python/builtins.py index 4abb8187..19193a58 100644 --- a/python/builtins.py +++ b/python/builtins.py @@ -96,13 +96,20 @@ def sorted(iterable, reverse=False, key=None): return a ##### str ##### -def __f(self, *args): +def __f(self, *args, **kwargs): if '{}' in self: for i in range(len(args)): self = self.replace('{}', str(args[i]), 1) else: - for i in range(len(args)): - self = self.replace('{'+str(i)+'}', str(args[i])) + # Positional arguments will be followed by keyword arguments + # 1. Replace the positional arguments + for i,a in enumerate(args): + self = self.replace('{'+str(i)+'}', str(a)) + + # 2. Replace the keyword arguments + for k,v in kwargs.items(): + self = self.replace('{'+k+'}', str(v)) + return self str.format = __f From ee548164218d0f1c22759d6d6cd7ca5e42f297b8 Mon Sep 17 00:00:00 2001 From: "S. Mahmudul Hasan" Date: Thu, 5 Oct 2023 20:04:21 -0400 Subject: [PATCH 02/13] Updated str.format to tokenization-based-replacement operation --- python/builtins.py | 119 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 14 deletions(-) diff --git a/python/builtins.py b/python/builtins.py index 19193a58..1f096cc1 100644 --- a/python/builtins.py +++ b/python/builtins.py @@ -96,21 +96,112 @@ def sorted(iterable, reverse=False, key=None): return a ##### str ##### -def __f(self, *args, **kwargs): - if '{}' in self: - for i in range(len(args)): - self = self.replace('{}', str(args[i]), 1) - else: - # Positional arguments will be followed by keyword arguments - # 1. Replace the positional arguments - for i,a in enumerate(args): - self = self.replace('{'+str(i)+'}', str(a)) - - # 2. Replace the keyword arguments - for k,v in kwargs.items(): - self = self.replace('{'+k+'}', str(v)) +def tokenize(s:str) -> list: + tokens = [] + L, R = 0,0 - return self + mode = None + curPos = 0 + lookingForKword = False + + while(R str: + tokens = tokenize(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) + + # if '{}' in self: + # for i in range(len(args)): + # self = self.replace('{}', str(args[i]), 1) + # else: + # # Positional arguments will be followed by keyword arguments + # # 1. Replace the positional arguments + # for i,a in enumerate(args): + # self = self.replace('{'+str(i)+'}', str(a)) + + # # 2. Replace the keyword arguments + # for k,v in kwargs.items(): + # self = self.replace('{'+k+'}', str(v)) + + # return self str.format = __f def __f(self, chars=None): From 151e9467ae717c0dd9acc4fcfb5adbe24fed3279 Mon Sep 17 00:00:00 2001 From: "S. Mahmudul Hasan" Date: Fri, 6 Oct 2023 16:56:19 -0400 Subject: [PATCH 03/13] fixed the error case for escaped curly braces, and converted the tokenize function into a inner function --- python/builtins.py | 161 +++++++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 72 deletions(-) diff --git a/python/builtins.py b/python/builtins.py index 1f096cc1..9528160b 100644 --- a/python/builtins.py +++ b/python/builtins.py @@ -96,77 +96,94 @@ def sorted(iterable, reverse=False, key=None): return a ##### str ##### -def tokenize(s:str) -> list: - tokens = [] - L, R = 0,0 - - mode = None - curPos = 0 - lookingForKword = False - - while(R str: - tokens = tokenize(self) + def tokenizeString(s:str): + tokens = [] + L, R = 0,0 + + mode = None + curArg = 0 + # lookingForKword = False + + while(R str: 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: @@ -187,7 +204,7 @@ def __f(self:str, *args, **kwargs) -> str: final_tokens.append(t) return ''.join(final_tokens) - + # if '{}' in self: # for i in range(len(args)): # self = self.replace('{}', str(args[i]), 1) From 2f525617090d57274c7fcf615f5ea76938d7444e Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sat, 7 Oct 2023 11:37:36 +0800 Subject: [PATCH 04/13] add more json test cases --- tests/80_json.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/80_json.py b/tests/80_json.py index d821ab92..1e0f1ccc 100644 --- a/tests/80_json.py +++ b/tests/80_json.py @@ -16,6 +16,15 @@ a = { 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) _a = json.loads(_j) From 098b85f89b90dfbd15e24fbc83dc181951d58263 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sat, 7 Oct 2023 11:55:57 +0800 Subject: [PATCH 05/13] ... --- python/builtins.py | 18 ++---------------- tests/04_str.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/python/builtins.py b/python/builtins.py index 9528160b..436cd39c 100644 --- a/python/builtins.py +++ b/python/builtins.py @@ -96,8 +96,8 @@ def sorted(iterable, reverse=False, key=None): return a ##### str ##### -def __f(self:str, *args, **kwargs) -> str: - def tokenizeString(s:str): +def __f(self: str, *args, **kwargs) -> str: + def tokenizeString(s: str): tokens = [] L, R = 0,0 @@ -205,20 +205,6 @@ def __f(self:str, *args, **kwargs) -> str: return ''.join(final_tokens) - # if '{}' in self: - # for i in range(len(args)): - # self = self.replace('{}', str(args[i]), 1) - # else: - # # Positional arguments will be followed by keyword arguments - # # 1. Replace the positional arguments - # for i,a in enumerate(args): - # self = self.replace('{'+str(i)+'}', str(a)) - - # # 2. Replace the keyword arguments - # for k,v in kwargs.items(): - # self = self.replace('{'+k+'}', str(v)) - - # return self str.format = __f def __f(self, chars=None): diff --git a/tests/04_str.py b/tests/04_str.py index dd4a1587..6a2e1a44 100644 --- a/tests/04_str.py +++ b/tests/04_str.py @@ -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 "{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 a = "Hello, World!" assert a[::-1] == "!dlroW ,olleH" From 7312afdad24bb308037088d34b32ed0078fed7d2 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sat, 7 Oct 2023 13:28:20 +0800 Subject: [PATCH 06/13] fix a term --- README.md | 2 +- README_zh.md | 2 +- docs/index.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f0063c8d..18095dd3 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ These platforms are officially tested. + Android 64-bit / 32-bit + iOS 64-bit + Emscripten 32-bit -+ Raspberry Pi 64-bit ++ Raspberry Pi OS 64-bit ## Quick Start diff --git a/README_zh.md b/README_zh.md index 9e1394b8..7a6f33b6 100644 --- a/README_zh.md +++ b/README_zh.md @@ -27,7 +27,7 @@ pkpy 支持任何拥有 C++17 编译器的平台。 + Android 64-bit / 32-bit + iOS 64-bit + Emscripten 32-bit -+ Raspberry Pi 64-bit ++ Raspberry Pi OS 64-bit ## 快速上手 diff --git a/docs/index.md b/docs/index.md index 673dc2ee..08ed87c3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -39,7 +39,7 @@ These platforms are officially tested. + Android 64-bit / 32-bit + iOS 64-bit + Emscripten 32-bit -+ Raspberry Pi 64-bit ++ Raspberry Pi OS 64-bit ## Sponsor me From b29801bbe8dc03ceca3475a828dd6143585b8887 Mon Sep 17 00:00:00 2001 From: Non <43197300+nonperforming@users.noreply.github.com> Date: Sun, 8 Oct 2023 00:11:20 +1300 Subject: [PATCH 07/13] Use emojis in feature list --- README.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 18095dd3..027bd9ed 100644 --- a/README.md +++ b/README.md @@ -114,26 +114,26 @@ for a quick overview of the supported features. | Name | Example | Supported | | --------------- | ------------------------------- | --------- | -| If Else | `if..else..elif` | YES | -| Loop | `for/while/break/continue` | YES | -| Function | `def f(x,*args,y=1):` | YES | -| Subclass | `class A(B):` | YES | -| List | `[1, 2, 'a']` | YES | -| ListComp | `[i for i in range(5)]` | YES | -| Slice | `a[1:2], a[:2], a[1:]` | YES | -| Tuple | `(1, 2, 'a')` | YES | -| Dict | `{'a': 1, 'b': 2}` | YES | -| F-String | `f'value is {x}'` | YES | -| Unpacking | `a, b = 1, 2` | YES | -| Star Unpacking | `a, *b = [1, 2, 3]` | YES | -| Exception | `raise/try..catch` | YES | -| Dynamic Code | `eval()/exec()` | YES | -| Reflection | `hasattr()/getattr()/setattr()` | YES | -| Import | `import/from..import` | YES | -| Context Block | `with as :` | YES | -| Type Annotation | `def f(a:int, b:float=1)` | YES | -| Generator | `yield i` | YES | -| Decorator | `@cache` | YES | +| If Else | `if..else..elif` | ✅ | +| Loop | `for/while/break/continue` | ✅ | +| Function | `def f(x,*args,y=1):` | ✅ | +| Subclass | `class A(B):` | ✅ | +| List | `[1, 2, 'a']` | ✅ | +| ListComp | `[i for i in range(5)]` | ✅ | +| Slice | `a[1:2], a[:2], a[1:]` | ✅ | +| Tuple | `(1, 2, 'a')` | ✅ | +| Dict | `{'a': 1, 'b': 2}` | ✅ | +| F-String | `f'value is {x}'` | ✅ | +| Unpacking | `a, b = 1, 2` | ✅ | +| Star Unpacking | `a, *b = [1, 2, 3]` | ✅ | +| Exception | `raise/try..catch` | ✅ | +| Dynamic Code | `eval()/exec()` | ✅ | +| Reflection | `hasattr()/getattr()/setattr()` | ✅ | +| Import | `import/from..import` | ✅ | +| Context Block | `with as :` | ✅ | +| Type Annotation | `def f(a:int, b:float=1)` | ✅ | +| Generator | `yield i` | ✅ | +| Decorator | `@cache` | ✅ | ## Contribution From 2b72428931fda01a8d58bec46137a9fa1c46960c Mon Sep 17 00:00:00 2001 From: Non <43197300+nonperforming@users.noreply.github.com> Date: Sun, 8 Oct 2023 00:13:25 +1300 Subject: [PATCH 08/13] Use emojis in feature list (zh) --- README_zh.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/README_zh.md b/README_zh.md index 7a6f33b6..007ab2f4 100644 --- a/README_zh.md +++ b/README_zh.md @@ -76,25 +76,25 @@ int main(){ | 特性 | 示例 | 支持 | | ------------ | ------------------------------- | ---- | -| 分支 | `if..else..elif` | YES | -| 循环 | `for/while/break/continue` | YES | -| 函数 | `def f(x,*args,y=1):` | YES | -| 类与继承 | `class A(B):` | YES | -| 列表 | `[1, 2, 'a']` | YES | -| 列表生成式 | `[i for i in range(5)]` | YES | -| 切片 | `a[1:2], a[:2], a[1:]` | YES | -| 元组 | `(1, 2, 'a')` | YES | -| 字典 | `{'a': 1, 'b': 2}` | YES | -| 格式化字符串 | `f'value is {x}'` | YES | -| 序列解包 | `a, b = 1, 2` | YES | -| 异常 | `raise/try..catch` | YES | -| 动态分发 | `eval()/exec()` | YES | -| 反射 | `hasattr()/getattr()/setattr()` | YES | -| 导入模块 | `import/from..import` | YES | -| 上下文管理器 | `with as :` | YES | -| 类型标注 | `def f(a:int, b:float=1)` | YES | -| 生成器 | `yield i` | YES | -| 装饰器 | `@cache` | YES | +| 分支 | `if..else..elif` | ✅ | +| 循环 | `for/while/break/continue` | ✅ | +| 函数 | `def f(x,*args,y=1):` | ✅ | +| 类与继承 | `class A(B):` | ✅ | +| 列表 | `[1, 2, 'a']` | ✅ | +| 列表生成式 | `[i for i in range(5)]` | ✅ | +| 切片 | `a[1:2], a[:2], a[1:]` | ✅ | +| 元组 | `(1, 2, 'a')` | ✅ | +| 字典 | `{'a': 1, 'b': 2}` | ✅ | +| 格式化字符串 | `f'value is {x}'` | ✅ | +| 序列解包 | `a, b = 1, 2` | ✅ | +| 异常 | `raise/try..catch` | ✅ | +| 动态分发 | `eval()/exec()` | ✅ | +| 反射 | `hasattr()/getattr()/setattr()` | ✅ | +| 导入模块 | `import/from..import` | ✅ | +| 上下文管理器 | `with as :` | ✅ | +| 类型标注 | `def f(a:int, b:float=1)` | ✅ | +| 生成器 | `yield i` | ✅ | +| 装饰器 | `@cache` | ✅ | ## 参考 From 85eefaab5d4805de4e6eb008737a023e271e0298 Mon Sep 17 00:00:00 2001 From: Non <43197300+nonperforming@users.noreply.github.com> Date: Sun, 8 Oct 2023 00:20:51 +1300 Subject: [PATCH 09/13] Do not compile on docs change --- .github/workflows/main.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 99bddbf9..4b48e6f2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,5 +1,15 @@ name: build -on: [push, pull_request] +on: + push: + paths-ignore: + - 'docs/**' + - 'web/**' + - '**.md' + pull_request: + paths-ignore: + - 'docs/**' + - 'web/**' + - '**.md' jobs: build_win: runs-on: windows-latest From 4df6d4fdb1689734064c94b3e9ee9ae8b88fab15 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 8 Oct 2023 01:36:11 +0800 Subject: [PATCH 10/13] fix a bug of `traceback` --- src/ceval.cpp | 6 ------ src/vm.cpp | 18 +++++++++++++----- tests/80_traceback.py | 8 ++++++-- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/ceval.cpp b/src/ceval.cpp index e867f2cd..bd867b2b 100644 --- a/src/ceval.cpp +++ b/src/ceval.cpp @@ -760,12 +760,6 @@ __NEXT_STEP:; PK_UNUSED(e); PyObject* obj = POPX(); 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(); if(callstack.empty()){ #if PK_DEBUG_FULL_EXCEPTION diff --git a/src/vm.cpp b/src/vm.cpp index 5a016938..5ee9913d 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -1082,13 +1082,21 @@ void VM::_error(Exception e){ } void VM::_raise(bool re_raise){ - Frame* top = top_frame().get(); + Frame* frame = top_frame().get(); + Exception& e = PK_OBJ_GET(Exception, s_data.top()); if(!re_raise){ - Exception& e = PK_OBJ_GET(Exception, s_data.top()); - e._ip_on_error = top->_ip; - 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(); else throw UnhandledException(); } diff --git a/tests/80_traceback.py b/tests/80_traceback.py index 25fbfec7..b1df66fd 100644 --- a/tests/80_traceback.py +++ b/tests/80_traceback.py @@ -6,5 +6,9 @@ try: except KeyError: s = traceback.format_exc() -assert s == r'''Traceback (most recent call last): -KeyError: 6''' \ No newline at end of file +ok = s == '''Traceback (most recent call last): + File "80_traceback.py", line 5 + b = a[6] +KeyError: 6''' + +assert ok, s \ No newline at end of file From 9c5d231fb6614cb523a14e83b6fcdd5420edfe8c Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 8 Oct 2023 11:33:12 +0800 Subject: [PATCH 11/13] fix a broken link --- docs/bindings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/bindings.md b/docs/bindings.md index 33968d5c..328b3c28 100644 --- a/docs/bindings.md +++ b/docs/bindings.md @@ -160,4 +160,4 @@ For example, `vm->bind__add__` is preferred over `vm->bind_method<1>(type, "__ad ### Further reading -See [linalg.h](https://github.com/blueloveTH/pocketpy/blob/main/src/linalg.h) for a complete example used by `linalg` module. \ No newline at end of file +See [linalg.h](https://github.com/blueloveTH/pocketpy/blob/main/src/random.cpp) for a complete example used by `random` module. \ No newline at end of file From 9a17085b875085c40d8799fca03f03253c7a00eb Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 8 Oct 2023 11:45:42 +0800 Subject: [PATCH 12/13] fix a doc issue to add `/utf-8` on MSVC --- README.md | 1 + docs/quick-start/installation.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 027bd9ed..ebce1fe3 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ To compile it with your project, these flags must be set: + `--std=c++17` flag must be set + Exception must be enabled ++ For MSVC, `/utf-8` flag must be set For development build on Linux, use this snippet. ```bash diff --git a/docs/quick-start/installation.md b/docs/quick-start/installation.md index 4332c228..9a0f4f38 100644 --- a/docs/quick-start/installation.md +++ b/docs/quick-start/installation.md @@ -43,6 +43,7 @@ To compile it with your project, these flags must be set: + `--std=c++17` flag must be set + Exception must be enabled ++ For MSVC, `/utf-8` flag must be set For emscripten, you must enable exceptions to make pocketpy work properly. See https://emscripten.org/docs/porting/exceptions.html. From 3478ddeeb0764acfd8975e73f4287891e76d3179 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 8 Oct 2023 11:59:01 +0800 Subject: [PATCH 13/13] change `clang++` into `clang` --- README.md | 2 +- build.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ebce1fe3..bb9875f6 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ To compile it with your project, these flags must be set: For development build on Linux, use this snippet. ```bash # prerequisites -sudo apt-get install libc++-dev libc++abi-dev clang++ +sudo apt-get install libc++-dev libc++abi-dev clang # build the repo bash build.sh # unittest diff --git a/build.sh b/build.sh index 473b1f7d..0a2303f4 100644 --- a/build.sh +++ b/build.sh @@ -10,7 +10,7 @@ fi # Check if clang++ is installed if ! type -P clang++ >/dev/null 2>&1; then 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 fi