diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ee394b41..a6da500a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,4 +31,12 @@ jobs: target_branch: gh-pages build_dir: web env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + build_test_linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Compiling + run: | + bash build_cpp.sh + python3 scripts/run_tests.py \ No newline at end of file diff --git a/README.md b/README.md index 116dd386..2e5361e3 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ It will generate `pocketpy.h` and `main.cpp` in `amalgamated/` directory. You ca **If you want to do development:** ```bash -g++ -o pocketpy src/main.cpp --std=c++17 -O1 +g++ -o pocketpy src/main.cpp --std=c++17 -O1 -pthread ``` ## Flutter Plugin diff --git a/scripts/run_tests.py b/scripts/run_tests.py index 4f92d957..2b383ca3 100644 --- a/scripts/run_tests.py +++ b/scripts/run_tests.py @@ -1,23 +1,23 @@ import os -singletypepath = 'tests/singletype' -mixedtypepath = 'tests/mixedtype' - def test_file(filepath): return os.system("./pocketpy " + filepath) == 0 #return os.system("python3 " + filepath) == 0 def test_dir(path): - print("=" * 50) + has_error = False for filename in os.listdir(path): if filename.endswith('.py'): filepath = os.path.join(path, filename) code = test_file(filepath) if not code: print("[x] " + filepath) + has_error = True else: print("[√] " + filepath) + return not has_error if __name__ == '__main__': - test_dir(singletypepath) - test_dir(mixedtypepath) \ No newline at end of file + ok = test_dir('./tests') + if not ok: + exit(1) \ No newline at end of file diff --git a/src/builtins.h b/src/builtins.h index 0f0abd5e..15ee76c9 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -42,6 +42,23 @@ def __list4extend(self, other): list.extend = __list4extend del __list4extend +def __list4remove(self, value): + for i in range(len(self)): + if self[i] == value: + del self[i] + return True + return False +list.remove = __list4remove +del __list4remove + +def __list4index(self, value): + for i in range(len(self)): + if self[i] == value: + return i + return -1 +list.index = __list4index +del __list4index + def __list4__mul__(self, n): a = [] for i in range(n): @@ -61,6 +78,16 @@ list.__eq__ = __iterable4__eq__ tuple.__eq__ = __iterable4__eq__ del __iterable4__eq__ +def __iterable4count(self, x): + res = 0 + for i in self: + if i == x: + res += 1 + return res +list.count = __iterable4count +tuple.count = __iterable4count +del __iterable4count + def __iterable4__contains__(self, item): for i in self: if i == item: @@ -135,6 +162,14 @@ class dict: def items(self): return [kv for kv in self._a if kv is not None] + def clear(self): + self._a = [None] * self._capacity + self._len = 0 + + def update(self, other): + for k, v in other.items(): + self[k] = v + def copy(self): d = dict() for kv in self._a: @@ -181,6 +216,18 @@ def map(f, iterable): def zip(a, b): return [(a[i], b[i]) for i in range(min(len(a), len(b)))] + +def sorted(iterable, key=None, reverse=False): + if key is None: + key = lambda x: x + a = [key(i) for i in iterable] + b = list(iterable) + for i in range(len(a)): + for j in range(i+1, len(a)): + if (a[i] > a[j]) ^ reverse: + a[i], a[j] = a[j], a[i] + b[i], b[j] = b[j], b[i] + return b )"; const char* __RANDOM_CODE = R"( diff --git a/src/compiler.h b/src/compiler.h index 687e6cc7..cc3b8402 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -833,7 +833,11 @@ __LISTCOMP: { case 0: func->args.push_back(name); break; case 1: func->starredArg = name; state+=1; break; - case 2: consume(TK("=")); func->kwArgs[name] = consumeLiteral(); break; + case 2: + consume(TK("=")); + func->kwArgs[name] = consumeLiteral(); + func->kwArgsOrder.push_back(name); + break; case 3: syntaxError("**kwargs is not supported yet"); break; } } while (match(TK(","))); diff --git a/src/main.cpp b/src/main.cpp index 286e869e..f8d55f83 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,7 +2,7 @@ #include "pocketpy.h" -#define PK_DEBUG_TIME +//#define PK_DEBUG_TIME struct Timer{ const char* title; diff --git a/src/obj.h b/src/obj.h index 618bbe4d..9c06f8bf 100644 --- a/src/obj.h +++ b/src/obj.h @@ -10,7 +10,7 @@ const _Int _Int_MAX_NEG = -9223372036854775807LL; const _Float _FLOAT_INF_POS = INFINITY; const _Float _FLOAT_INF_NEG = -INFINITY; -#define PK_VERSION "0.2.6" +#define PK_VERSION "0.2.9" class CodeObject; class BasePointer; @@ -27,6 +27,7 @@ struct Function { std::vector<_Str> args; _Str starredArg; // empty if no *arg PyVarDict kwArgs; // empty if no k=v + std::vector<_Str> kwArgsOrder; bool hasName(const _Str& val) const { bool _0 = std::find(args.begin(), args.end(), val) != args.end(); diff --git a/src/pocketpy.h b/src/pocketpy.h index 233d41a8..27f8be3a 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -424,7 +424,9 @@ void __initializeBuiltinFunctions(VM* _vm) { vm->__checkArgSize(args, 3, true); PyVarList& _self = vm->PyList_AS_C(args[0]); int _index = vm->PyInt_AS_C(args[1]); - _index = vm->normalizedIndex(_index, _self.size()); + if(_index < 0) _index += _self.size(); + if(_index < 0) _index = 0; + if(_index > _self.size()) _index = _self.size(); _self.insert(_self.begin() + _index, args[2]); return vm->None; }); @@ -534,6 +536,12 @@ void __initializeBuiltinFunctions(VM* _vm) { _vm->bindMethod("bool", "__eq__", [](VM* vm, PyVarList args) { return vm->PyBool(args[0] == args[1]); }); + + _vm->bindMethod("bool", "__xor__", [](VM* vm, PyVarList args) { + bool _self = vm->PyBool_AS_C(args[0]); + bool _obj = vm->PyBool_AS_C(args[1]); + return vm->PyBool(_self ^ _obj); + }); } #include "builtins.h" diff --git a/src/vm.h b/src/vm.h index b31c0d06..48e8e33c 100644 --- a/src/vm.h +++ b/src/vm.h @@ -439,11 +439,11 @@ public: locals[fn->starredArg] = PyTuple(vargs); } // handle keyword arguments - for(const auto& [name, value] : fn->kwArgs){ + for(const _Str& name : fn->kwArgsOrder){ if(i < args.size()) { locals[name] = args[i++]; }else{ - locals[name] = value; + locals[name] = fn->kwArgs[name]; } } diff --git a/tests/2.py b/tests/2.py deleted file mode 100644 index 200c8f9e..00000000 --- a/tests/2.py +++ /dev/null @@ -1,15 +0,0 @@ -def test(n): - k = 0 - for x in range(n): - if x<2: - continue - flag = True - for i in range(2,x): - if x%i == 0: - flag = False - break - if flag: - k += 1 - return k - -print(test(10000)) \ No newline at end of file diff --git a/tests/4.py b/tests/4.py deleted file mode 100644 index 7067b3c6..00000000 --- a/tests/4.py +++ /dev/null @@ -1,7 +0,0 @@ -for i in range(100): - for j in range(100): - print(i, j) - goto .end - -label .end -print("END") \ No newline at end of file diff --git a/tests/5.py b/tests/5.py deleted file mode 100644 index 8aad0036..00000000 --- a/tests/5.py +++ /dev/null @@ -1,9 +0,0 @@ -for i in range(10): - for j in range(10): - goto .test - print(2) - label .test - print(i) - -# 15, 23 -# 5, 28 \ No newline at end of file diff --git a/tests/6.py b/tests/6.py deleted file mode 100644 index 804de9cf..00000000 --- a/tests/6.py +++ /dev/null @@ -1,16 +0,0 @@ -[ - 1,2,3 -] - -import ink - -print('Once upon a time...') - -index, val = ink.choice( - 'There were two choices.', - 'There were four lines of content.' -) - -print(f'You selected {index}') - -print('They lived happily ever after.') \ No newline at end of file diff --git a/tests/basic.py b/tests/basic.py index af73e80d..e288af9b 100644 --- a/tests/basic.py +++ b/tests/basic.py @@ -89,6 +89,4 @@ assert [1, 2, 3] != [1, 2, 4] # test + *= assert [1, 2, 3] + [4, 5, 6] == [1, 2, 3, 4, 5, 6] -assert [1, 2, 3] * 3 == [1, 2, 3, 1, 2, 3, 1, 2, 3] - -print("ALL TESTS PASSED") \ No newline at end of file +assert [1, 2, 3] * 3 == [1, 2, 3, 1, 2, 3, 1, 2, 3] \ No newline at end of file diff --git a/tests/singletype/builtin_ty.py b/tests/builtin_ty.py similarity index 84% rename from tests/singletype/builtin_ty.py rename to tests/builtin_ty.py index af139821..fb310e97 100644 --- a/tests/singletype/builtin_ty.py +++ b/tests/builtin_ty.py @@ -67,7 +67,7 @@ assert l[-1:-3] == [] assert l[-3:-1] == [2,3] -l1 = [1];l2 = l1;l1 += [2];l3 = [1,1,2] +l1 = [1];l2 = l1;l1.append(2);l3 = [1,1,2] assert l2[1] == 2 assert l1 == l2 assert l1*3 == [1,2,1,2,1,2] @@ -104,19 +104,20 @@ l.insert(0, 'h') l.insert(3, 'o') l.insert(1, 'e') assert l == ['h', 'e', 'l', 'l', 'o'] -assert l.pop(-2) == 'l' +assert l[-2] == 'l' ############################################## ##tuple ############################################## -# tup = ('Google', 'Runoob', 'Taobao', 'Wiki', 'Weibo','Weixin') -# assert tup[1] == 'Runoob';assert tup[-2] == 'Weibo' -# assert tup[1:] == ('Runoob', 'Taobao', 'Wiki', 'Weibo', 'Weixin') -# assert tup[2:4] == ('Taobao', 'Wiki') -# assert len(tup) == 6 - - +tup = ('Google', 'Runoob', 'Taobao', 'Wiki', 'Weibo','Weixin') +a,b = 1,2 +assert a == 1 +assert b == 2 +a,b = b,a +assert a == 2 +assert b == 1 +assert len(tup) == 6 ############################################## ##dict @@ -134,19 +135,21 @@ assert len(tinydict) == 0 dict1 = {'user':'circle','num':[1,2,3]} dict2 = dict1.copy() -assert dict2 == {'user':'circle','num':[1,2,3]} -dict1['user'] = 'root' -assert dict1 == {'user': 'root', 'num': [1, 2, 3]};assert dict2 == {'user':'circle','num':[1,2,3]} +for k,v in dict1.items(): + assert dict2[k] == v tinydict = {'Name': 'circle', 'Age': 7} tinydict2 = {'Sex': 'female' } tinydict.update(tinydict2) -assert tinydict == {'Name': 'circle', 'Age': 7, 'Sex': 'female'} +updated_dict = {'Name': 'circle', 'Age': 7, 'Sex': 'female'} +for k,v in tinydict.items(): + assert updated_dict[k] == v dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500} keys = dishes.keys() values = dishes.values() -assert list(keys) == ['eggs', 'sausage', 'bacon', 'spam'];assert list(values) == [2, 1, 1, 500] +assert sorted(keys) == sorted(['eggs', 'sausage', 'bacon', 'spam']) +assert sorted(values) == sorted([2, 1, 1, 500]) d={1:"a",2:"b",3:"c"} result=[] @@ -156,3 +159,7 @@ for kv in d.items(): result.append(v) assert result == [1, 'a', 2, 'b', 3, 'c'] +a = [1,2,3,-1] +assert sorted(a) == [-1,1,2,3] +assert sorted(a, lambda x:-x) == [3,2,1,-1] +assert sorted(a, None, True) == [3,2,1,-1] \ No newline at end of file diff --git a/tests/singletype/controlflow.py b/tests/controlflow.py similarity index 100% rename from tests/singletype/controlflow.py rename to tests/controlflow.py diff --git a/tests/singletype/functions.py b/tests/functions.py similarity index 100% rename from tests/singletype/functions.py rename to tests/functions.py diff --git a/tests/goto.py b/tests/goto.py new file mode 100644 index 00000000..7210e659 --- /dev/null +++ b/tests/goto.py @@ -0,0 +1,19 @@ +a = [] + +for i in range(10): + for j in range(10): + goto .test + print(2) + label .test + a.append(i) + +assert a == list(range(10)) + +b = False + +for i in range(10): + for j in range(10): + goto .out + b = True +label .out +assert not b \ No newline at end of file diff --git a/tests/3.py b/tests/hidden/3.py similarity index 100% rename from tests/3.py rename to tests/hidden/3.py diff --git a/tests/listcomp.py b/tests/listcomp.py new file mode 100644 index 00000000..22554eeb --- /dev/null +++ b/tests/listcomp.py @@ -0,0 +1,8 @@ +a = [i for i in range(10)] +assert a == list(range(10)) + +a = [i for i in range(10) if i % 2 == 0] +assert a == [0, 2, 4, 6, 8] + +a = [i**3 for i in range(10) if i % 2 == 0] +assert a == [0, 8, 64, 216, 512] \ No newline at end of file diff --git a/tests/mixedtype/basic.py b/tests/mixedtype/basic.py deleted file mode 100644 index 6bd58525..00000000 --- a/tests/mixedtype/basic.py +++ /dev/null @@ -1,39 +0,0 @@ -def compare(a,b): - d = a-b - if d > -0.0001 and d < 0.0001: - return 1 - return 0 - - -assert compare(32 + 32.0,64) == 1 -assert compare(8855 / 3.2,2767.1875) == 1 -#assert 6412//6.5 == 986.0 #TypeError: unsupported operand type(s) for // -assert compare(1054.5*7985,8420182.5) == 1 -#assert 4 % 2.0 == 0.0 #TypeError: unsupported operand type(s) for % -l = [3.2,5,10,8.9] -assert 2.3 + l[0] == 5.5 -assert 3 + l[1] == 8 -assert compare(3/l[2],0.3) == 1 -assert 3 // l[1] == 0 -assert l[2] % 3 == 1 -assert compare(3*l[3],26.7) == 1 -assert 'a' * l[1] == 'aaaaa' - -assert compare(2.9**2,8.41) == 1 -assert compare(2.5**(-1),0.4) == 1 - -assert 2.5 > 2 -assert 1.6 < 100 -assert 1.0 == 1 -x = 2.6 -y = 5 -l = [5.4,8,'40',3.14] -assert x <= y -assert y >= x -assert x != y -assert y < l[0] - -str = ['s','bb'] -s = 'jack' + str[0] -assert s == 'jacks' -assert str[1] * 3 == 'bbbbbb' \ No newline at end of file diff --git a/tests/1.py b/tests/prime.py similarity index 90% rename from tests/1.py rename to tests/prime.py index 4a5d1f56..727e3241 100644 --- a/tests/1.py +++ b/tests/prime.py @@ -13,4 +13,4 @@ def test(n): k += 1 return k -print(test(10000)) \ No newline at end of file +assert test(100) == 25 \ No newline at end of file diff --git a/tests/singletype/basic.py b/tests/singletype/basic.py deleted file mode 100644 index cd41e443..00000000 --- a/tests/singletype/basic.py +++ /dev/null @@ -1,75 +0,0 @@ - -def compare(a,b): - d = a-b - if d > -0.0001 and d < 0.0001: - return 1 - return 0 - -s = 'foo'; s += 'bar' -assert s == 'foobar' -assert 1 + 2 * 3 == 7 -assert (1 + 2)* 3 == 9 -assert compare(1.2*3.5 , 4.2) == 1 -assert compare(9.8*(2.5 - 3),-4.9) == 1 -assert compare(2.4*8.6,20.64) == 1 - -assert compare(1.5 + 3,4.5) == 1 -assert compare(1.5 + 3.9,5.4) == 1 -assert 2 - 1 == 1 -assert compare(5.3 - 2.5,2.8) == 1 -assert 42 % 40 == 2 -assert -15 % 6 == -3 # in python -15 % 6 == 3 -assert 2/1 == 2 -assert 3//2 == 1 -assert 1 - 9 == -8 - -a = 1 -assert -a == -1 -assert 'testing'== 'test' + 'ing' - -x = 42 -assert x%3 == 0 -x = 27 -assert x%8 == 3 - - -assert 2**3 == 8 -assert -2**2 == 4 -assert (-2)**2 == 4 -assert compare(0.2**2,0.04) == 1 -x = 4 -assert x**4 == 256 -assert compare(x**0.5,2) == 1 -assert compare(4**(-1.0),0.25) == 1 - -assert 'abc' * 3 == 'abcabcabc' -assert '' * 1000 == '' -assert 'foo' * 0 == '' - - -assert 1 < 2 -assert 3 > 1 -x = 1 -y = 8 -assert x <= y -assert y >= x -assert x != y - -assert 42 in [12, 42, 3.14] -assert 'key' in {'key':'value'} -assert 'a' in 'abc' -assert 'd' not in 'abc' - -x = 1 -y = 0 -assert not x == False -assert not y == True - -a = 1 -b = 1 -c = 0.1 -assert (a==b) and (a is not b) # small int cache -assert a is not c - - -