negative index, range slice and more

... string concat with .. operator
    exponent operator with ** operator
This commit is contained in:
Thakee Nathees 2022-05-08 15:32:51 +05:30
parent b908f85338
commit f5e2f15d23
12 changed files with 291 additions and 91 deletions

View File

@ -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. which could be used to create a new fiber to "re-start" the fiber.
```ruby ```ruby
fb = Fiber foo() fb = Fiber fn()
for i in 0..5 for i in 0..5
yield(i) yield(i)
end end

View File

@ -11,7 +11,7 @@ which is less than <strong>300KB</strong> and the language itself can be compile
</p> </p>
<div class="center"> <div class="center">
<a class="button" target="_blank" href="/try-online.html"> Try Online </a> <a class="button" target="_blank" href="./try-online.html"> Try Online </a>
<a class="button" target="_blank" href="https://www.github.com/ThakeeNathees/pocketlang/"> GitHub </a> <a class="button" target="_blank" href="https://www.github.com/ThakeeNathees/pocketlang/"> GitHub </a>
</div> </div>

View File

@ -21,6 +21,7 @@ TEST_SUITE = {
"Unit Tests": ( "Unit Tests": (
"lang/basics.pk", "lang/basics.pk",
"lang/builtin_fn.pk", "lang/builtin_fn.pk",
"lang/builtin_ty.pk",
"lang/class.pk", "lang/class.pk",
"lang/closure.pk", "lang/closure.pk",
"lang/core.pk", "lang/core.pk",

View File

@ -81,6 +81,7 @@ typedef enum {
TK_MINUS, // - TK_MINUS, // -
TK_STAR, // * TK_STAR, // *
TK_FSLASH, // / TK_FSLASH, // /
TK_STARSTAR, // **
TK_BSLASH, // \. TK_BSLASH, // \.
TK_EQ, // = TK_EQ, // =
TK_GT, // > TK_GT, // >
@ -96,6 +97,7 @@ typedef enum {
TK_STAREQ, // *= TK_STAREQ, // *=
TK_DIVEQ, // /= TK_DIVEQ, // /=
TK_MODEQ, // %= TK_MODEQ, // %=
TK_POWEQ, // **=
TK_ANDEQ, // &= TK_ANDEQ, // &=
TK_OREQ, // |= TK_OREQ, // |=
@ -226,6 +228,7 @@ typedef enum {
PREC_TERM, // + - PREC_TERM, // + -
PREC_FACTOR, // * / % PREC_FACTOR, // * / %
PREC_UNARY, // - ! ~ not PREC_UNARY, // - ! ~ not
PREC_EXPONENT, // **
PREC_CALL, // () PREC_CALL, // ()
PREC_SUBSCRIPT, // [] PREC_SUBSCRIPT, // []
PREC_ATTRIB, // .index PREC_ATTRIB, // .index
@ -1101,11 +1104,7 @@ static void lexToken(Compiler* compiler) {
case '>': case '>':
if (matchChar(parser, '>')) { if (matchChar(parser, '>')) {
if (matchChar(parser, '=')) { setNextTwoCharToken(parser, '=', TK_SRIGHT, TK_SRIGHTEQ);
setNextToken(parser, TK_SRIGHTEQ);
} else {
setNextToken(parser, TK_SRIGHT);
}
} else { } else {
setNextTwoCharToken(parser, '=', TK_GT, TK_GTEQ); setNextTwoCharToken(parser, '=', TK_GT, TK_GTEQ);
} }
@ -1113,11 +1112,7 @@ static void lexToken(Compiler* compiler) {
case '<': case '<':
if (matchChar(parser, '<')) { if (matchChar(parser, '<')) {
if (matchChar(parser, '=')) { setNextTwoCharToken(parser, '=', TK_SLEFT, TK_SLEFTEQ);
setNextToken(parser, TK_SLEFTEQ);
} else {
setNextToken(parser, TK_SLEFT);
}
} else { } else {
setNextTwoCharToken(parser, '=', TK_LT, TK_LTEQ); setNextTwoCharToken(parser, '=', TK_LT, TK_LTEQ);
} }
@ -1138,7 +1133,11 @@ static void lexToken(Compiler* compiler) {
return; return;
case '*': case '*':
if (matchChar(parser, '*')) {
setNextTwoCharToken(parser, '=', TK_STARSTAR, TK_POWEQ);
} else {
setNextTwoCharToken(parser, '=', TK_STAR, TK_STAREQ); setNextTwoCharToken(parser, '=', TK_STAR, TK_STAREQ);
}
return; return;
case '/': case '/':
@ -1298,6 +1297,7 @@ static bool matchAssignment(Compiler* compiler) {
if (match(compiler, TK_STAREQ)) return true; if (match(compiler, TK_STAREQ)) return true;
if (match(compiler, TK_DIVEQ)) return true; if (match(compiler, TK_DIVEQ)) return true;
if (match(compiler, TK_MODEQ)) 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_ANDEQ)) return true;
if (match(compiler, TK_OREQ)) return true; if (match(compiler, TK_OREQ)) return true;
if (match(compiler, TK_XOREQ)) 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_MINUS */ { exprUnaryOp, exprBinaryOp, PREC_TERM },
/* TK_STAR */ { NULL, exprBinaryOp, PREC_FACTOR }, /* TK_STAR */ { NULL, exprBinaryOp, PREC_FACTOR },
/* TK_FSLASH */ { NULL, exprBinaryOp, PREC_FACTOR }, /* TK_FSLASH */ { NULL, exprBinaryOp, PREC_FACTOR },
/* TK_STARSTAR */ { NULL, exprBinaryOp, PREC_EXPONENT },
/* TK_BSLASH */ NO_RULE, /* TK_BSLASH */ NO_RULE,
/* TK_EQ */ NO_RULE, /* TK_EQ */ NO_RULE,
/* TK_GT */ { NULL, exprBinaryOp, PREC_COMPARISION }, /* TK_GT */ { NULL, exprBinaryOp, PREC_COMPARISION },
@ -1582,6 +1583,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
/* TK_STAREQ */ NO_RULE, /* TK_STAREQ */ NO_RULE,
/* TK_DIVEQ */ NO_RULE, /* TK_DIVEQ */ NO_RULE,
/* TK_MODEQ */ NO_RULE, /* TK_MODEQ */ NO_RULE,
/* TK_POWEQ */ NO_RULE,
/* TK_ANDEQ */ NO_RULE, /* TK_ANDEQ */ NO_RULE,
/* TK_OREQ */ NO_RULE, /* TK_OREQ */ NO_RULE,
/* TK_XOREQ */ NO_RULE, /* TK_XOREQ */ NO_RULE,
@ -2004,6 +2006,7 @@ static void exprBinaryOp(Compiler* compiler) {
case TK_MINUS: EMIT_BINARY_OP_INPLACE(OP_SUBTRACT); break; case TK_MINUS: EMIT_BINARY_OP_INPLACE(OP_SUBTRACT); break;
case TK_STAR: EMIT_BINARY_OP_INPLACE(OP_MULTIPLY); break; case TK_STAR: EMIT_BINARY_OP_INPLACE(OP_MULTIPLY); break;
case TK_FSLASH: EMIT_BINARY_OP_INPLACE(OP_DIVIDE); 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_AMP: EMIT_BINARY_OP_INPLACE(OP_BIT_AND); break;
case TK_PIPE: EMIT_BINARY_OP_INPLACE(OP_BIT_OR); 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_CARET: EMIT_BINARY_OP_INPLACE(OP_BIT_XOR); break;
@ -2238,6 +2241,10 @@ static void parsePrecedence(Compiler* compiler, Precedence precedence) {
return; 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; compiler->l_value = precedence <= PREC_LOWEST;
prefix(compiler); prefix(compiler);
@ -2258,8 +2265,9 @@ static void parsePrecedence(Compiler* compiler, Precedence precedence) {
// TK_LPARAN '(' as infix is the call operator. // TK_LPARAN '(' as infix is the call operator.
compiler->is_last_call = (op == TK_LPARAN); 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_STAREQ: EMIT_BINARY_OP_INPLACE(OP_MULTIPLY); break;
case TK_DIVEQ: EMIT_BINARY_OP_INPLACE(OP_DIVIDE); break; case TK_DIVEQ: EMIT_BINARY_OP_INPLACE(OP_DIVIDE); break;
case TK_MODEQ: EMIT_BINARY_OP_INPLACE(OP_MOD); 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_ANDEQ: EMIT_BINARY_OP_INPLACE(OP_BIT_AND); break;
case TK_OREQ: EMIT_BINARY_OP_INPLACE(OP_BIT_OR); break; case TK_OREQ: EMIT_BINARY_OP_INPLACE(OP_BIT_OR); break;
case TK_XOREQ: EMIT_BINARY_OP_INPLACE(OP_BIT_XOR); 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_STAR)) _RET("*", 1);
if (match(compiler, TK_STAREQ)) _RET("*=", 1); if (match(compiler, TK_STAREQ)) _RET("*=", 1);
if (match(compiler, TK_FSLASH)) _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_DIVEQ)) _RET("/=", 1);
if (match(compiler, TK_PERCENT)) _RET("%", 1); if (match(compiler, TK_PERCENT)) _RET("%", 1);
if (match(compiler, TK_MODEQ)) _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_AMP)) _RET("&", 1);
if (match(compiler, TK_ANDEQ)) _RET("&=", 1); if (match(compiler, TK_ANDEQ)) _RET("&=", 1);
if (match(compiler, TK_PIPE)) _RET("|", 1); if (match(compiler, TK_PIPE)) _RET("|", 1);

View File

@ -251,7 +251,7 @@ DEF(coreTypeName,
DEF(coreHelp, DEF(coreHelp,
"help([fn:Closure]) -> null\n" "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; int argc = ARGC;
if (argc != 0 && argc != 1) { if (argc != 0 && argc != 1) {
@ -919,7 +919,6 @@ static void initializePrimitiveClasses(PKVM* vm) {
ADD_CTOR(PK_LIST, "@ctorList", _ctorList, -1); ADD_CTOR(PK_LIST, "@ctorList", _ctorList, -1);
ADD_CTOR(PK_MAP, "@ctorMap", _ctorMap, 0); ADD_CTOR(PK_MAP, "@ctorMap", _ctorMap, 0);
ADD_CTOR(PK_FIBER, "@ctorFiber", _ctorFiber, 1); ADD_CTOR(PK_FIBER, "@ctorFiber", _ctorFiber, 1);
#undef ADD_CTOR #undef ADD_CTOR
#define ADD_METHOD(type, name, ptr, arity_) \ #define ADD_METHOD(type, name, ptr, arity_) \
@ -1222,6 +1221,20 @@ Var varDivide(PKVM* vm, Var v1, Var v2, bool inplace) {
return VAR_NULL; 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) { Var varBitAnd(PKVM* vm, Var v1, Var v2, bool inplace) {
CHECK_BITWISE_OP(&); CHECK_BITWISE_OP(&);
CHECK_INST_BINARY_OP("&"); CHECK_INST_BINARY_OP("&");
@ -1283,6 +1296,14 @@ Var varOpRange(PKVM* vm, Var v1, Var v2) {
if (IS_NUM(v1) && IS_NUM(v2)) { if (IS_NUM(v1) && IS_NUM(v2)) {
return VAR_OBJ(newRange(vm, AS_NUM(v1), AS_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; const bool inplace = false;
CHECK_INST_BINARY_OP(".."); CHECK_INST_BINARY_OP("..");
UNSUPPORTED_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 '$'", \ "'$' object has no mutable attribute named '$'", \
varTypeName(on), attrib->data)) 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)) { if (!IS_OBJ(on)) {
ERR_NO_ATTRIB(vm, on, attrib); ERR_NO_ATTRIB(vm, on, attrib);
return; return;
@ -1527,30 +1540,6 @@ do { \
Object* obj = AS_OBJ(on); Object* obj = AS_OBJ(on);
switch (obj->type) { 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: { case OBJ_MODULE: {
Module* module = (Module*)obj; Module* module = (Module*)obj;
@ -1563,26 +1552,9 @@ do { \
} break; } break;
case OBJ_FUNC: 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: case OBJ_UPVALUE:
UNREACHABLE(); // Upvalues aren't first class objects. UNREACHABLE(); // Functions aren't first class objects.
return; break;
case OBJ_FIBER:
ERR_NO_ATTRIB(vm, on, attrib);
return;
case OBJ_CLASS:
ERR_NO_ATTRIB(vm, on, attrib);
return;
case OBJ_INST: { case OBJ_INST: {
@ -1612,6 +1584,9 @@ do { \
return; return;
} break; } break;
default:
break;
} }
ERR_NO_ATTRIB(vm, on, attrib); ERR_NO_ATTRIB(vm, on, attrib);
@ -1621,6 +1596,101 @@ do { \
#undef ERR_NO_ATTRIB #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) { Var varGetSubscript(PKVM* vm, Var on, Var key) {
if (!IS_OBJ(on)) { if (!IS_OBJ(on)) {
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", 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); Object* obj = AS_OBJ(on);
switch (obj->type) { switch (obj->type) {
case OBJ_STRING: { case OBJ_STRING: {
int64_t index; int64_t index;
String* str = ((String*)obj); String* str = ((String*)obj);
if (!validateInteger(vm, key, &index, "List index")) {
return VAR_NULL; if (isInteger(key, &index)) {
} // Normalize index.
if (!validateIndex(vm, index, str->length, "String")) { 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; return VAR_NULL;
} }
// FIXME: Add static VM characters instead of allocating here.
String* c = newStringLength(vm, str->data + index, 1); String* c = newStringLength(vm, str->data + index, 1);
return VAR_OBJ(c); 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;
}
} break; } break;
case OBJ_LIST: { case OBJ_LIST: {
int64_t index; int64_t index;
pkVarBuffer* elems = &((List*)obj)->elements; pkVarBuffer* elems = &((List*)obj)->elements;
if (!validateInteger(vm, key, &index, "List index")) {
return VAR_NULL; if (isInteger(key, &index)) {
} // Normalize index.
if (!validateIndex(vm, index, elems->count, "List")) { 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 VAR_NULL;
} }
return elems->data[index]; 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;
}
} break; } break;
case OBJ_MAP: { case OBJ_MAP: {
@ -1676,6 +1768,9 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
case OBJ_FUNC: case OBJ_FUNC:
case OBJ_UPVALUE: case OBJ_UPVALUE:
UNREACHABLE(); // Not first class objects. UNREACHABLE(); // Not first class objects.
default:
break;
} }
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", 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; int64_t index;
pkVarBuffer* elems = &((List*)obj)->elements; pkVarBuffer* elems = &((List*)obj)->elements;
if (!validateInteger(vm, key, &index, "List index")) return; 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; elems->data[index] = value;
return; return;
} break; } break;
case OBJ_MAP: { case OBJ_MAP: {
@ -1714,6 +1816,9 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
case OBJ_FUNC: case OBJ_FUNC:
case OBJ_UPVALUE: case OBJ_UPVALUE:
UNREACHABLE(); UNREACHABLE();
default:
break;
} }
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.",

View File

@ -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 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 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 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 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. Var varBitAnd(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 & v2.

View File

@ -44,7 +44,12 @@ void reportCompileTimeError(PKVM* vm, const char* path, int line,
// Print the error message. // Print the error message.
buff.count = 0; 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."); ASSERT(size >= 0, "vnsprintf() failed.");
pkByteBufferReserve(&buff, vm, size); pkByteBufferReserve(&buff, vm, size);
vsnprintf((char*)buff.data, size, fmt, args); vsnprintf((char*)buff.data, size, fmt, args);
@ -641,6 +646,7 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
case OP_SUBTRACT: case OP_SUBTRACT:
case OP_MULTIPLY: case OP_MULTIPLY:
case OP_DIVIDE: case OP_DIVIDE:
case OP_EXPONENT:
case OP_MOD: case OP_MOD:
case OP_BIT_AND: case OP_BIT_AND:
case OP_BIT_OR: case OP_BIT_OR:

View File

@ -242,6 +242,7 @@ OPCODE(ADD, 1, -1)
OPCODE(SUBTRACT, 1, -1) OPCODE(SUBTRACT, 1, -1)
OPCODE(MULTIPLY, 1, -1) OPCODE(MULTIPLY, 1, -1)
OPCODE(DIVIDE, 1, -1) OPCODE(DIVIDE, 1, -1)
OPCODE(EXPONENT, 1, -1)
OPCODE(MOD, 1, -1) OPCODE(MOD, 1, -1)
OPCODE(BIT_AND, 1, -1) OPCODE(BIT_AND, 1, -1)

View File

@ -1579,6 +1579,19 @@ L_do_call:
DISPATCH(); 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): OPCODE(MOD):
{ {
// Don't pop yet, we need the reference for gc. // Don't pop yet, we need the reference for gc.

View File

@ -136,6 +136,13 @@ x = 99
x <<= 2 x <<= 2
assert(x == 99 << 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(.5 == 0.5)
assert(.333 == .333) assert(.333 == .333)
assert(.1 + 1 == 1.1) assert(.1 + 1 == 1.1)

View File

@ -5,3 +5,5 @@ assert(list_join([1, 2, 3]) == "123")
assert(list_join(["hello", " world"]) == "hello world") assert(list_join(["hello", " world"]) == "hello world")
assert(list_join([[], []]) == "[][]") assert(list_join([[], []]) == "[][]")
## If we got here, that means all test were passed.
print('All TESTS PASSED')

53
tests/lang/builtin_ty.pk Normal file
View File

@ -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')