diff --git a/docs/GettingStarted/LanguageManual.md b/docs/GettingStarted/LanguageManual.md index a80ef0a..f3fb937 100644 --- a/docs/GettingStarted/LanguageManual.md +++ b/docs/GettingStarted/LanguageManual.md @@ -224,7 +224,7 @@ fiber is finished check its attribute `is_done` and use the attribute `function` which could be used to create a new fiber to "re-start" the fiber. ```ruby -fb = Fiber foo() +fb = Fiber fn() for i in 0..5 yield(i) end diff --git a/docs/README.md b/docs/README.md index 078c2e9..c38385a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,7 +11,7 @@ which is less than 300KB and the language itself can be compile
diff --git a/scripts/run_tests.py b/scripts/run_tests.py index 02eea24..2c099d3 100644 --- a/scripts/run_tests.py +++ b/scripts/run_tests.py @@ -21,6 +21,7 @@ TEST_SUITE = { "Unit Tests": ( "lang/basics.pk", "lang/builtin_fn.pk", + "lang/builtin_ty.pk", "lang/class.pk", "lang/closure.pk", "lang/core.pk", diff --git a/src/pk_compiler.c b/src/pk_compiler.c index 2ebd6e4..bbd82e6 100644 --- a/src/pk_compiler.c +++ b/src/pk_compiler.c @@ -81,6 +81,7 @@ typedef enum { TK_MINUS, // - TK_STAR, // * TK_FSLASH, // / + TK_STARSTAR, // ** TK_BSLASH, // \. TK_EQ, // = TK_GT, // > @@ -96,6 +97,7 @@ typedef enum { TK_STAREQ, // *= TK_DIVEQ, // /= TK_MODEQ, // %= + TK_POWEQ, // **= TK_ANDEQ, // &= TK_OREQ, // |= @@ -226,6 +228,7 @@ typedef enum { PREC_TERM, // + - PREC_FACTOR, // * / % PREC_UNARY, // - ! ~ not + PREC_EXPONENT, // ** PREC_CALL, // () PREC_SUBSCRIPT, // [] PREC_ATTRIB, // .index @@ -1101,11 +1104,7 @@ static void lexToken(Compiler* compiler) { case '>': if (matchChar(parser, '>')) { - if (matchChar(parser, '=')) { - setNextToken(parser, TK_SRIGHTEQ); - } else { - setNextToken(parser, TK_SRIGHT); - } + setNextTwoCharToken(parser, '=', TK_SRIGHT, TK_SRIGHTEQ); } else { setNextTwoCharToken(parser, '=', TK_GT, TK_GTEQ); } @@ -1113,11 +1112,7 @@ static void lexToken(Compiler* compiler) { case '<': if (matchChar(parser, '<')) { - if (matchChar(parser, '=')) { - setNextToken(parser, TK_SLEFTEQ); - } else { - setNextToken(parser, TK_SLEFT); - } + setNextTwoCharToken(parser, '=', TK_SLEFT, TK_SLEFTEQ); } else { setNextTwoCharToken(parser, '=', TK_LT, TK_LTEQ); } @@ -1138,7 +1133,11 @@ static void lexToken(Compiler* compiler) { return; case '*': - setNextTwoCharToken(parser, '=', TK_STAR, TK_STAREQ); + if (matchChar(parser, '*')) { + setNextTwoCharToken(parser, '=', TK_STARSTAR, TK_POWEQ); + } else { + setNextTwoCharToken(parser, '=', TK_STAR, TK_STAREQ); + } return; case '/': @@ -1298,6 +1297,7 @@ static bool matchAssignment(Compiler* compiler) { if (match(compiler, TK_STAREQ)) return true; if (match(compiler, TK_DIVEQ)) return true; if (match(compiler, TK_MODEQ)) return true; + if (match(compiler, TK_POWEQ)) return true; if (match(compiler, TK_ANDEQ)) return true; if (match(compiler, TK_OREQ)) return true; if (match(compiler, TK_XOREQ)) return true; @@ -1569,6 +1569,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence /* TK_MINUS */ { exprUnaryOp, exprBinaryOp, PREC_TERM }, /* TK_STAR */ { NULL, exprBinaryOp, PREC_FACTOR }, /* TK_FSLASH */ { NULL, exprBinaryOp, PREC_FACTOR }, + /* TK_STARSTAR */ { NULL, exprBinaryOp, PREC_EXPONENT }, /* TK_BSLASH */ NO_RULE, /* TK_EQ */ NO_RULE, /* TK_GT */ { NULL, exprBinaryOp, PREC_COMPARISION }, @@ -1582,6 +1583,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence /* TK_STAREQ */ NO_RULE, /* TK_DIVEQ */ NO_RULE, /* TK_MODEQ */ NO_RULE, + /* TK_POWEQ */ NO_RULE, /* TK_ANDEQ */ NO_RULE, /* TK_OREQ */ NO_RULE, /* TK_XOREQ */ NO_RULE, @@ -1998,17 +2000,18 @@ static void exprBinaryOp(Compiler* compiler) { do { emitOpcode(compiler, opcode); emitByte(compiler, 0); } while (false) switch (op) { - case TK_DOTDOT: emitOpcode(compiler, OP_RANGE); break; - case TK_PERCENT: EMIT_BINARY_OP_INPLACE(OP_MOD); break; - case TK_PLUS: EMIT_BINARY_OP_INPLACE(OP_ADD); break; - case TK_MINUS: EMIT_BINARY_OP_INPLACE(OP_SUBTRACT); break; - case TK_STAR: EMIT_BINARY_OP_INPLACE(OP_MULTIPLY); break; - case TK_FSLASH: EMIT_BINARY_OP_INPLACE(OP_DIVIDE); break; - case TK_AMP: EMIT_BINARY_OP_INPLACE(OP_BIT_AND); break; - case TK_PIPE: EMIT_BINARY_OP_INPLACE(OP_BIT_OR); break; - case TK_CARET: EMIT_BINARY_OP_INPLACE(OP_BIT_XOR); break; - case TK_SRIGHT: EMIT_BINARY_OP_INPLACE(OP_BIT_RSHIFT); break; - case TK_SLEFT: EMIT_BINARY_OP_INPLACE(OP_BIT_LSHIFT); break; + case TK_DOTDOT: emitOpcode(compiler, OP_RANGE); break; + case TK_PERCENT: EMIT_BINARY_OP_INPLACE(OP_MOD); break; + case TK_PLUS: EMIT_BINARY_OP_INPLACE(OP_ADD); break; + case TK_MINUS: EMIT_BINARY_OP_INPLACE(OP_SUBTRACT); break; + case TK_STAR: EMIT_BINARY_OP_INPLACE(OP_MULTIPLY); break; + case TK_FSLASH: EMIT_BINARY_OP_INPLACE(OP_DIVIDE); break; + case TK_STARSTAR: EMIT_BINARY_OP_INPLACE(OP_EXPONENT); break; + case TK_AMP: EMIT_BINARY_OP_INPLACE(OP_BIT_AND); break; + case TK_PIPE: EMIT_BINARY_OP_INPLACE(OP_BIT_OR); break; + case TK_CARET: EMIT_BINARY_OP_INPLACE(OP_BIT_XOR); break; + case TK_SRIGHT: EMIT_BINARY_OP_INPLACE(OP_BIT_RSHIFT); break; + case TK_SLEFT: EMIT_BINARY_OP_INPLACE(OP_BIT_LSHIFT); break; #undef EMIT_BINARY_OP_INPLACE case TK_GT: emitOpcode(compiler, OP_GT); break; @@ -2238,6 +2241,10 @@ static void parsePrecedence(Compiler* compiler, Precedence precedence) { return; } + // Make a "backup" of the l value before parsing next operators to + // reset once it done. + bool l_value = compiler->l_value; + compiler->l_value = precedence <= PREC_LOWEST; prefix(compiler); @@ -2258,8 +2265,9 @@ static void parsePrecedence(Compiler* compiler, Precedence precedence) { // TK_LPARAN '(' as infix is the call operator. compiler->is_last_call = (op == TK_LPARAN); - } + + compiler->l_value = l_value; } /*****************************************************************************/ @@ -2458,6 +2466,7 @@ static void emitAssignedOp(Compiler* compiler, TokenType assignment) { case TK_STAREQ: EMIT_BINARY_OP_INPLACE(OP_MULTIPLY); break; case TK_DIVEQ: EMIT_BINARY_OP_INPLACE(OP_DIVIDE); break; case TK_MODEQ: EMIT_BINARY_OP_INPLACE(OP_MOD); break; + case TK_POWEQ: EMIT_BINARY_OP_INPLACE(OP_EXPONENT); break; case TK_ANDEQ: EMIT_BINARY_OP_INPLACE(OP_BIT_AND); break; case TK_OREQ: EMIT_BINARY_OP_INPLACE(OP_BIT_OR); break; case TK_XOREQ: EMIT_BINARY_OP_INPLACE(OP_BIT_XOR); break; @@ -2627,9 +2636,11 @@ static bool matchOperatorMethod(Compiler* compiler, if (match(compiler, TK_STAR)) _RET("*", 1); if (match(compiler, TK_STAREQ)) _RET("*=", 1); if (match(compiler, TK_FSLASH)) _RET("/", 1); + if (match(compiler, TK_STARSTAR)) _RET("**", 1); if (match(compiler, TK_DIVEQ)) _RET("/=", 1); if (match(compiler, TK_PERCENT)) _RET("%", 1); if (match(compiler, TK_MODEQ)) _RET("%=", 1); + if (match(compiler, TK_POWEQ)) _RET("**=", 1); if (match(compiler, TK_AMP)) _RET("&", 1); if (match(compiler, TK_ANDEQ)) _RET("&=", 1); if (match(compiler, TK_PIPE)) _RET("|", 1); diff --git a/src/pk_core.c b/src/pk_core.c index ac450a2..52eb001 100644 --- a/src/pk_core.c +++ b/src/pk_core.c @@ -251,7 +251,7 @@ DEF(coreTypeName, DEF(coreHelp, "help([fn:Closure]) -> null\n" - "This will write an error message to stdout and return null.") { + "It'll print the docstring the object and return.") { int argc = ARGC; if (argc != 0 && argc != 1) { @@ -919,7 +919,6 @@ static void initializePrimitiveClasses(PKVM* vm) { ADD_CTOR(PK_LIST, "@ctorList", _ctorList, -1); ADD_CTOR(PK_MAP, "@ctorMap", _ctorMap, 0); ADD_CTOR(PK_FIBER, "@ctorFiber", _ctorFiber, 1); - #undef ADD_CTOR #define ADD_METHOD(type, name, ptr, arity_) \ @@ -1222,6 +1221,20 @@ Var varDivide(PKVM* vm, Var v1, Var v2, bool inplace) { return VAR_NULL; } +Var varExponent(PKVM* vm, Var v1, Var v2, bool inplace) { + double n1, n2; + if (isNumeric(v1, &n1)) { + if (validateNumeric(vm, v2, &n2, RIGHT_OPERAND)) { + return VAR_NUM(pow(n1, n2)); + } + return VAR_NULL; + } + + CHECK_INST_BINARY_OP("**"); + UNSUPPORTED_BINARY_OP("**"); + return VAR_NULL; +} + Var varBitAnd(PKVM* vm, Var v1, Var v2, bool inplace) { CHECK_BITWISE_OP(&); CHECK_INST_BINARY_OP("&"); @@ -1283,6 +1296,14 @@ Var varOpRange(PKVM* vm, Var v1, Var v2) { if (IS_NUM(v1) && IS_NUM(v2)) { return VAR_OBJ(newRange(vm, AS_NUM(v1), AS_NUM(v2))); } + + if (IS_OBJ_TYPE(v1, OBJ_STRING)) { + String* str = _varToString(vm, v2); + if (str == NULL) return VAR_NULL; + String* concat = stringJoin(vm, (String*) AS_OBJ(v1), str); + return VAR_OBJ(concat); + } + const bool inplace = false; CHECK_INST_BINARY_OP(".."); UNSUPPORTED_BINARY_OP(".."); @@ -1512,14 +1533,6 @@ void varSetAttrib(PKVM* vm, Var on, String* attrib, Var value) { "'$' object has no mutable attribute named '$'", \ varTypeName(on), attrib->data)) -#define ATTRIB_IMMUTABLE(name) \ -do { \ - if ((attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)) { \ - VM_SET_ERROR(vm, stringFormat(vm, "'$' attribute is immutable.", name)); \ - return; \ - } \ -} while (false) - if (!IS_OBJ(on)) { ERR_NO_ATTRIB(vm, on, attrib); return; @@ -1527,30 +1540,6 @@ do { \ Object* obj = AS_OBJ(on); switch (obj->type) { - case OBJ_STRING: - ATTRIB_IMMUTABLE("length"); - ATTRIB_IMMUTABLE("lower"); - ATTRIB_IMMUTABLE("upper"); - ATTRIB_IMMUTABLE("strip"); - ERR_NO_ATTRIB(vm, on, attrib); - return; - - case OBJ_LIST: - ATTRIB_IMMUTABLE("length"); - ERR_NO_ATTRIB(vm, on, attrib); - return; - - case OBJ_MAP: - TODO; - ERR_NO_ATTRIB(vm, on, attrib); - return; - - case OBJ_RANGE: - ATTRIB_IMMUTABLE("as_list"); - ATTRIB_IMMUTABLE("first"); - ATTRIB_IMMUTABLE("last"); - ERR_NO_ATTRIB(vm, on, attrib); - return; case OBJ_MODULE: { Module* module = (Module*)obj; @@ -1563,26 +1552,9 @@ do { \ } break; case OBJ_FUNC: - UNREACHABLE(); // Functions aren't first class objects. - return; - - case OBJ_CLOSURE: - ATTRIB_IMMUTABLE("arity"); - ATTRIB_IMMUTABLE("name"); - ERR_NO_ATTRIB(vm, on, attrib); - return; - case OBJ_UPVALUE: - UNREACHABLE(); // Upvalues aren't first class objects. - return; - - case OBJ_FIBER: - ERR_NO_ATTRIB(vm, on, attrib); - return; - - case OBJ_CLASS: - ERR_NO_ATTRIB(vm, on, attrib); - return; + UNREACHABLE(); // Functions aren't first class objects. + break; case OBJ_INST: { @@ -1612,6 +1584,9 @@ do { \ return; } break; + + default: + break; } ERR_NO_ATTRIB(vm, on, attrib); @@ -1621,6 +1596,101 @@ do { \ #undef ERR_NO_ATTRIB } +// Given a range. It'll "normalize" the range to slice an object (string or +// list) set the [start] index [length] and [reversed]. On success it'll return +// true. +static bool _normalizeSliceRange(PKVM* vm, Range* range, uint32_t count, + int32_t* start, int32_t* length, bool* reversed) { + if ((floor(range->from) != range->from) || + (floor(range->to) != range->to)) { + VM_SET_ERROR(vm, newString(vm, "Expected a whole number.")); + return false; + } + + int32_t from = (int32_t)range->from; + int32_t to = (int32_t)range->to; + + if (from < 0) from = count + from; + if (to < 0) to = count + to; + + *reversed = false; + if (to < from) { + int32_t tmp = to; + to = from; + from = tmp; + *reversed = true; + } + + if (from < 0 || count <= (uint32_t) to) { + + // Special case we allow 0..0 or 0..-1, -1..0, -1..-1 to be valid slice + // ranges for empty string/list, and will gives an empty string/list. + if (count == 0 && (from == 0 || from == -1) && (to == 0 || to == -1)) { + *start = 0; + *length = 0; + *reversed = false; + return true; + } + + VM_SET_ERROR(vm, newString(vm, "Index out of bound.")); + return false; + } + + *start = from; + *length = to - from + 1; + + return true; +} + +// Slice the string with the [range] and reutrn it. On error it'll set +// an error and return NULL. +static String* _sliceString(PKVM* vm, String* str, Range* range) { + + int32_t start, length; bool reversed; + if (!_normalizeSliceRange(vm, range, str->length, + &start, &length, &reversed)) { + return NULL; + } + + // Optimize case. + if (start == 0 && length == str->length && !reversed) return str; + + // TODO: check if length is 1 and return pre allocated character string. + + String* slice = newStringLength(vm, str->data + start, length); + if (!reversed) return slice; + + for (int32_t i = 0; i < length / 2; i++) { + char tmp = slice->data[i]; + slice->data[i] = slice->data[length - i - 1]; + slice->data[length - i - 1] = tmp; + } + slice->hash = utilHashString(slice->data); + return slice; +} + +// Slice the list with the [range] and reutrn it. On error it'll set +// an error and return NULL. +static List* _sliceList(PKVM* vm, List* list, Range* range) { + + int32_t start, length; bool reversed; + if (!_normalizeSliceRange(vm, range, list->elements.count, + &start, &length, &reversed)) { + return NULL; + } + + List* slice = newList(vm, length); + vmPushTempRef(vm, &slice->_super); // slice. + + for (int32_t i = 0; i < length; i++) { + int32_t ind = (reversed) ? start + length - 1 - i : start + i; + listAppend(vm, slice, list->elements.data[ind]); + } + + vmPopTempRef(vm); // slice. + return slice; +} + Var varGetSubscript(PKVM* vm, Var on, Var key) { if (!IS_OBJ(on)) { VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", @@ -1631,28 +1701,50 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) { Object* obj = AS_OBJ(on); switch (obj->type) { case OBJ_STRING: { + int64_t index; String* str = ((String*)obj); - if (!validateInteger(vm, key, &index, "List index")) { + + if (isInteger(key, &index)) { + // Normalize index. + if (index < 0) index = str->length + index; + if (index >= str->length || index < 0) { + VM_SET_ERROR(vm, newString(vm, "String index out of bound.")); + return VAR_NULL; + } + // FIXME: Add static VM characters instead of allocating here. + String* c = newStringLength(vm, str->data + index, 1); + return VAR_OBJ(c); + } + + if (IS_OBJ_TYPE(key, OBJ_RANGE)) { + String* subs = _sliceString(vm, str, (Range*) AS_OBJ(key)); + if (subs != NULL) return VAR_OBJ(subs); return VAR_NULL; } - if (!validateIndex(vm, index, str->length, "String")) { - return VAR_NULL; - } - String* c = newStringLength(vm, str->data + index, 1); - return VAR_OBJ(c); + } break; case OBJ_LIST: { int64_t index; pkVarBuffer* elems = &((List*)obj)->elements; - if (!validateInteger(vm, key, &index, "List index")) { + + if (isInteger(key, &index)) { + // Normalize index. + if (index < 0) index = elems->count + index; + if (index >= elems->count || index < 0) { + VM_SET_ERROR(vm, newString(vm, "List index out of bound.")); + return VAR_NULL; + } + return elems->data[index]; + } + + if (IS_OBJ_TYPE(key, OBJ_RANGE)) { + List* sublist = _sliceList(vm, (List*)obj, (Range*)AS_OBJ(key)); + if (sublist != NULL) return VAR_OBJ(sublist); return VAR_NULL; } - if (!validateIndex(vm, index, elems->count, "List")) { - return VAR_NULL; - } - return elems->data[index]; + } break; case OBJ_MAP: { @@ -1676,6 +1768,9 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) { case OBJ_FUNC: case OBJ_UPVALUE: UNREACHABLE(); // Not first class objects. + + default: + break; } VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", @@ -1696,9 +1791,16 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) { int64_t index; pkVarBuffer* elems = &((List*)obj)->elements; if (!validateInteger(vm, key, &index, "List index")) return; - if (!validateIndex(vm, index, elems->count, "List")) return; + + // Normalize index. + if (index < 0) index = elems->count + index; + if (index >= elems->count || index < 0) { + VM_SET_ERROR(vm, newString(vm, "List index out of bound.")); + return; + } elems->data[index] = value; return; + } break; case OBJ_MAP: { @@ -1714,6 +1816,9 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) { case OBJ_FUNC: case OBJ_UPVALUE: UNREACHABLE(); + + default: + break; } VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", diff --git a/src/pk_core.h b/src/pk_core.h index b85fdf4..5a1d95e 100644 --- a/src/pk_core.h +++ b/src/pk_core.h @@ -98,6 +98,7 @@ Var varAdd(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 + v2. Var varSubtract(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 - v2. Var varMultiply(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 * v2. Var varDivide(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 / v2. +Var varExponent(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 ** v2. Var varModulo(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 % v2. Var varBitAnd(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 & v2. diff --git a/src/pk_debug.c b/src/pk_debug.c index 042f508..db83dc6 100644 --- a/src/pk_debug.c +++ b/src/pk_debug.c @@ -44,7 +44,12 @@ void reportCompileTimeError(PKVM* vm, const char* path, int line, // Print the error message. buff.count = 0; - int size = vsnprintf(NULL, 0, fmt, args) + 1; + + va_list args_copy; + va_copy(args_copy, args); + int size = vsnprintf(NULL, 0, fmt, args_copy) + 1; + va_end(args_copy); + ASSERT(size >= 0, "vnsprintf() failed."); pkByteBufferReserve(&buff, vm, size); vsnprintf((char*)buff.data, size, fmt, args); @@ -641,6 +646,7 @@ void dumpFunctionCode(PKVM* vm, Function* func) { case OP_SUBTRACT: case OP_MULTIPLY: case OP_DIVIDE: + case OP_EXPONENT: case OP_MOD: case OP_BIT_AND: case OP_BIT_OR: diff --git a/src/pk_opcodes.h b/src/pk_opcodes.h index 4e79e5d..e0cb890 100644 --- a/src/pk_opcodes.h +++ b/src/pk_opcodes.h @@ -242,6 +242,7 @@ OPCODE(ADD, 1, -1) OPCODE(SUBTRACT, 1, -1) OPCODE(MULTIPLY, 1, -1) OPCODE(DIVIDE, 1, -1) +OPCODE(EXPONENT, 1, -1) OPCODE(MOD, 1, -1) OPCODE(BIT_AND, 1, -1) diff --git a/src/pk_vm.c b/src/pk_vm.c index c96be40..ae7c1ff 100644 --- a/src/pk_vm.c +++ b/src/pk_vm.c @@ -1579,6 +1579,19 @@ L_do_call: DISPATCH(); } + OPCODE(EXPONENT): + { + // Don't pop yet, we need the reference for gc. + Var r = PEEK(-1), l = PEEK(-2); + uint8_t inplace = READ_BYTE(); ASSERT(inplace <= 1, OOPS); + Var result = varExponent(vm, l, r, inplace); + DROP(); DROP(); // r, l + PUSH(result); + + CHECK_ERROR(); + DISPATCH(); + } + OPCODE(MOD): { // Don't pop yet, we need the reference for gc. diff --git a/tests/lang/basics.pk b/tests/lang/basics.pk index 445f182..5e68ac4 100644 --- a/tests/lang/basics.pk +++ b/tests/lang/basics.pk @@ -136,6 +136,13 @@ x = 99 x <<= 2 assert(x == 99 << 2) +x = 4 +assert(2**3 == 8) +assert(-2**2 == -4 and (-2)**2 == 4) +assert((x**=4) == 256) +assert(x**.5 == 16) +assert(4**-1 == .25) + assert(.5 == 0.5) assert(.333 == .333) assert(.1 + 1 == 1.1) diff --git a/tests/lang/builtin_fn.pk b/tests/lang/builtin_fn.pk index 709fd4c..31541c8 100644 --- a/tests/lang/builtin_fn.pk +++ b/tests/lang/builtin_fn.pk @@ -5,3 +5,5 @@ assert(list_join([1, 2, 3]) == "123") assert(list_join(["hello", " world"]) == "hello world") assert(list_join([[], []]) == "[][]") +## If we got here, that means all test were passed. +print('All TESTS PASSED') diff --git a/tests/lang/builtin_ty.pk b/tests/lang/builtin_ty.pk new file mode 100644 index 0000000..3ab39ae --- /dev/null +++ b/tests/lang/builtin_ty.pk @@ -0,0 +1,53 @@ + +## Builtin types attributes and methods tests. + +############################################################################### +## INTEGER +############################################################################### + +sum = 0 +5.times fn(i) + sum += i +end +assert(sum == 0 + 1 + 2 + 3 + 4) + +############################################################################### +## STRING +############################################################################### + +s = "foobar" +assert(s[-3] == 'b') + +assert("foo" .. 42 == "foo42") +assert("" .. 1 .. 2 .. 3 == "123") + +assert("abcd"[1..1] == "b") +assert("abcd"[-1..0] == "dcba") +assert("abcd"[-3..3] == "bcd") +assert("abcd"[-1..-3] == "dcb") + +assert(""[0..-1] == "") +assert(""[-1..0] == "") + +############################################################################### +## LIST +############################################################################### + +l = [1, 2, 3, 4] +assert (l[2] == 3) +assert (l[-1] == 4) +l[-2] = 42 +assert (l[2] == 42) + +l = [1, 2, 3, 4] +assert(l[1..1] == [2]) +assert(l[-1..0] == [4, 3, 2, 1]) +assert(l[-3..3] == [2, 3, 4]) +assert(l[-1..-3] == [4, 3, 2]) + +assert([][0..0] == []) +assert([][-1..-1] == []) + + +## If we got here, that means all test were passed. +print('All TESTS PASSED')