mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-06 12:46:53 +08:00
negative index, range slice and more
... string concat with .. operator exponent operator with ** operator
This commit is contained in:
parent
b908f85338
commit
f5e2f15d23
@ -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
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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",
|
||||||
|
@ -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);
|
||||||
|
229
src/pk_core.c
229
src/pk_core.c
@ -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.",
|
||||||
|
@ -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.
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
13
src/pk_vm.c
13
src/pk_vm.c
@ -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.
|
||||||
|
@ -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)
|
||||||
|
@ -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
53
tests/lang/builtin_ty.pk
Normal 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')
|
Loading…
Reference in New Issue
Block a user