mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-03-04 05:05:57 +08:00
Merge pull request #235 from ThakeeNathees/more-methods
Some builtin class methods were added
This commit is contained in:
commit
e16debcff0
@ -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="./tryonline.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>
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ TEST_SUITE = {
|
|||||||
"lang/builtin_ty.pk",
|
"lang/builtin_ty.pk",
|
||||||
"lang/class.pk",
|
"lang/class.pk",
|
||||||
"lang/closure.pk",
|
"lang/closure.pk",
|
||||||
"lang/core.pk",
|
|
||||||
"lang/controlflow.pk",
|
"lang/controlflow.pk",
|
||||||
"lang/fibers.pk",
|
"lang/fibers.pk",
|
||||||
"lang/functions.pk",
|
"lang/functions.pk",
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
if (self->capacity < size) { \
|
if (self->capacity < size) { \
|
||||||
int capacity = utilPowerOf2Ceil((int)size); \
|
int capacity = utilPowerOf2Ceil((int)size); \
|
||||||
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; \
|
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; \
|
||||||
self->data = (m_type*)vmRealloc(vm, self->data, \
|
self->data = (m_type*) vmRealloc(vm, self->data, \
|
||||||
self->capacity * sizeof(m_type), capacity * sizeof(m_type)); \
|
self->capacity * sizeof(m_type), capacity * sizeof(m_type)); \
|
||||||
self->capacity = capacity; \
|
self->capacity = capacity; \
|
||||||
} \
|
} \
|
||||||
|
388
src/core/core.c
388
src/core/core.c
@ -489,34 +489,6 @@ DEF(coreExit,
|
|||||||
exit((int)value);
|
exit((int)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// String functions.
|
|
||||||
// -----------------
|
|
||||||
|
|
||||||
DEF(coreStrSub,
|
|
||||||
"str_sub(str:string, pos:num, len:num) -> string\n"
|
|
||||||
"Returns a substring from a given string supplied. In addition, "
|
|
||||||
"the position and length of the substring are provided when this "
|
|
||||||
"function is called. For example: `str_sub(str, pos, len)`.") {
|
|
||||||
|
|
||||||
String* str;
|
|
||||||
int64_t pos, len;
|
|
||||||
|
|
||||||
if (!validateArgString(vm, 1, &str)) return;
|
|
||||||
if (!validateInteger(vm, ARG(2), &pos, "Argument 2")) return;
|
|
||||||
if (!validateInteger(vm, ARG(3), &len, "Argument 3")) return;
|
|
||||||
|
|
||||||
if (pos < 0 || str->length < pos)
|
|
||||||
RET_ERR(newString(vm, "Index out of range."));
|
|
||||||
|
|
||||||
if (str->length < pos + len)
|
|
||||||
RET_ERR(newString(vm, "Substring length exceeded the limit."));
|
|
||||||
|
|
||||||
// Edge case, empty string.
|
|
||||||
if (len == 0) RET(VAR_OBJ(newStringLength(vm, "", 0)));
|
|
||||||
|
|
||||||
RET(VAR_OBJ(newStringLength(vm, str->data + pos, (uint32_t)len)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// List functions.
|
// List functions.
|
||||||
// ---------------
|
// ---------------
|
||||||
|
|
||||||
@ -557,21 +529,6 @@ DEF(coreListJoin,
|
|||||||
RET(VAR_OBJ(str));
|
RET(VAR_OBJ(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map functions.
|
|
||||||
// --------------
|
|
||||||
|
|
||||||
DEF(coreMapRemove,
|
|
||||||
"map_remove(self:map, key:var) -> var\n"
|
|
||||||
"Remove the [key] from the map [self] and return it's value if the key "
|
|
||||||
"exists, otherwise it'll return null.") {
|
|
||||||
|
|
||||||
Map* map;
|
|
||||||
if (!validateArgMap(vm, 1, &map)) return;
|
|
||||||
Var key = ARG(2);
|
|
||||||
|
|
||||||
RET(mapRemoveKey(vm, map, key));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void initializeBuiltinFN(PKVM* vm, Closure** bfn, const char* name,
|
static void initializeBuiltinFN(PKVM* vm, Closure** bfn, const char* name,
|
||||||
int length, int arity, pkNativeFn ptr,
|
int length, int arity, pkNativeFn ptr,
|
||||||
const char* docstring) {
|
const char* docstring) {
|
||||||
@ -601,19 +558,10 @@ static void initializeBuiltinFunctions(PKVM* vm) {
|
|||||||
INITIALIZE_BUILTIN_FN("input", coreInput, -1);
|
INITIALIZE_BUILTIN_FN("input", coreInput, -1);
|
||||||
INITIALIZE_BUILTIN_FN("exit", coreExit, -1);
|
INITIALIZE_BUILTIN_FN("exit", coreExit, -1);
|
||||||
|
|
||||||
// FIXME:
|
|
||||||
// move this functions as methods. and make "append()" a builtin.
|
|
||||||
|
|
||||||
// String functions.
|
|
||||||
INITIALIZE_BUILTIN_FN("str_sub", coreStrSub, 3);
|
|
||||||
|
|
||||||
// List functions.
|
// List functions.
|
||||||
INITIALIZE_BUILTIN_FN("list_append", coreListAppend, 2);
|
INITIALIZE_BUILTIN_FN("list_append", coreListAppend, 2);
|
||||||
INITIALIZE_BUILTIN_FN("list_join", coreListJoin, 1);
|
INITIALIZE_BUILTIN_FN("list_join", coreListJoin, 1);
|
||||||
|
|
||||||
// Map functions.
|
|
||||||
INITIALIZE_BUILTIN_FN("map_remove", coreMapRemove, 2);
|
|
||||||
|
|
||||||
#undef INITIALIZE_BUILTIN_FN
|
#undef INITIALIZE_BUILTIN_FN
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -850,23 +798,293 @@ DEF(_numberIsbyte,
|
|||||||
RET(VAR_BOOL((floor(n) == n) && (0x00 <= n && n <= 0xff)));
|
RET(VAR_BOOL((floor(n) == n) && (0x00 <= n && n <= 0xff)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEF(_stringFind,
|
||||||
|
"String.find(sub:String[, start:Number=0]) -> Number\n"
|
||||||
|
"Returns the first index of the substring [sub] found from the "
|
||||||
|
"[start] index") {
|
||||||
|
|
||||||
|
if (!pkCheckArgcRange(vm, ARGC, 1, 2)) return;
|
||||||
|
|
||||||
|
String* sub;
|
||||||
|
if (!validateArgString(vm, 1, &sub)) return;
|
||||||
|
|
||||||
|
int64_t start = 0;
|
||||||
|
if (ARGC == 2) {
|
||||||
|
if (!validateInteger(vm, ARG(2), &start, "Argument 1")) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String* self = (String*) AS_OBJ(SELF);
|
||||||
|
|
||||||
|
if (self->length <= start) {
|
||||||
|
RET(VAR_NUM((double) -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Pocketlang strings can contain \x00 ie. NULL byte and strstr
|
||||||
|
// doesn't support them. However pocketlang strings always ends with a null
|
||||||
|
// byte so the match won't go outside of the string.
|
||||||
|
const char* match = strstr(self->data + start, sub->data);
|
||||||
|
|
||||||
|
if (match == NULL) RET(VAR_NUM((double) -1));
|
||||||
|
|
||||||
|
ASSERT_INDEX(match - self->data, self->capacity);
|
||||||
|
RET(VAR_NUM((double) (match - self->data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_stringReplace,
|
||||||
|
"String.replace(old:Sttring, new:String[, count:Number=-1]) -> String\n"
|
||||||
|
"Returns a copy of the string where [count] occurrence of the substring "
|
||||||
|
"[old] will be replaced with [new]. If [count] == -1 all the occurrence "
|
||||||
|
"will be replaced.") {
|
||||||
|
|
||||||
|
if (!pkCheckArgcRange(vm, ARGC, 2, 3)) return;
|
||||||
|
|
||||||
|
String *old, *new_;
|
||||||
|
if (!validateArgString(vm, 1, &old)) return;
|
||||||
|
if (!validateArgString(vm, 2, &new_)) return;
|
||||||
|
|
||||||
|
String* self = (String*) AS_OBJ(SELF);
|
||||||
|
|
||||||
|
int64_t count = -1;
|
||||||
|
if (ARGC == 3) {
|
||||||
|
if (!validateInteger(vm, ARG(3), &count, "Argument 3")) return;
|
||||||
|
if (count < 0 && count != -1) {
|
||||||
|
RET_ERR(newString(vm, "count should either be >= 0 or -1"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RET(VAR_OBJ(stringReplace(vm, self, old, new_, (int32_t) count)));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_stringSplit,
|
||||||
|
"String.split(sep:String) -> List\n"
|
||||||
|
"Split the string into a list of string seperated by [sep] delimeter.") {
|
||||||
|
|
||||||
|
String* sep;
|
||||||
|
if (!validateArgString(vm, 1, &sep)) return;
|
||||||
|
|
||||||
|
if (sep->length == 0) {
|
||||||
|
RET_ERR(newString(vm, "Cannot use empty string as a seperator."));
|
||||||
|
}
|
||||||
|
|
||||||
|
RET(VAR_OBJ(stringSplit(vm, (String*)AS_OBJ(SELF), sep)));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_stringStrip,
|
||||||
|
"String.strip() -> String\n"
|
||||||
|
"Returns a copy of the string where the leading and trailing whitespace "
|
||||||
|
"removed.") {
|
||||||
|
RET(VAR_OBJ(stringStrip(vm, (String*) AS_OBJ(SELF))));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_stringLower,
|
||||||
|
"String.lower() -> String\n"
|
||||||
|
"Returns a copy of the string where all the characters are converted to "
|
||||||
|
"lower case letters.") {
|
||||||
|
RET(VAR_OBJ(stringLower(vm, (String*) AS_OBJ(SELF))));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_stringUpper,
|
||||||
|
"String.lower() -> String\n"
|
||||||
|
"Returns a copy of the string where all the characters are converted to "
|
||||||
|
"upper case letters.") {
|
||||||
|
RET(VAR_OBJ(stringUpper(vm, (String*) AS_OBJ(SELF))));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_stingStartswith,
|
||||||
|
"String.startswith(prefix: String | List) -> Bool\n"
|
||||||
|
"Returns true if the string starts the specified prefix.") {
|
||||||
|
|
||||||
|
Var prefix = ARG(1);
|
||||||
|
String* self = (String*) AS_OBJ(SELF);
|
||||||
|
|
||||||
|
if (IS_OBJ_TYPE(prefix, OBJ_STRING)) {
|
||||||
|
String* pre = (String*) AS_OBJ(prefix);
|
||||||
|
if (pre->length > self->length) RET(VAR_FALSE);
|
||||||
|
RET(VAR_BOOL((strncmp(self->data, pre->data, pre->length) == 0)));
|
||||||
|
|
||||||
|
} else if (IS_OBJ_TYPE(prefix, OBJ_LIST)) {
|
||||||
|
|
||||||
|
List* prefixes = (List*) AS_OBJ(prefix);
|
||||||
|
for (uint32_t i = 0; i < prefixes->elements.count; i++) {
|
||||||
|
Var pre_var = prefixes->elements.data[i];
|
||||||
|
if (!IS_OBJ_TYPE(pre_var, OBJ_STRING)) {
|
||||||
|
RET_ERR(newString(vm, "Expected a String for prefix."));
|
||||||
|
}
|
||||||
|
String* pre = (String*) AS_OBJ(pre_var);
|
||||||
|
if (pre->length > self->length) RET(VAR_FALSE);
|
||||||
|
if (strncmp(self->data, pre->data, pre->length) == 0) RET(VAR_TRUE);
|
||||||
|
}
|
||||||
|
RET(VAR_FALSE);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
RET_ERR(newString(vm, "Expected a String or a List of prifiexes."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_stingEndswith,
|
||||||
|
"String.endswith(suffix: String | List) -> Bool\n"
|
||||||
|
"Returns true if the string ends with the specified suffix.") {
|
||||||
|
|
||||||
|
Var suffix = ARG(1);
|
||||||
|
String* self = (String*)AS_OBJ(SELF);
|
||||||
|
|
||||||
|
if (IS_OBJ_TYPE(suffix, OBJ_STRING)) {
|
||||||
|
String* suf = (String*)AS_OBJ(suffix);
|
||||||
|
if (suf->length > self->length) RET(VAR_FALSE);
|
||||||
|
|
||||||
|
const char* start = (self->data + (self->length - suf->length));
|
||||||
|
RET(VAR_BOOL((strncmp(start, suf->data, suf->length) == 0)));
|
||||||
|
|
||||||
|
} else if (IS_OBJ_TYPE(suffix, OBJ_LIST)) {
|
||||||
|
|
||||||
|
List* suffixes = (List*)AS_OBJ(suffix);
|
||||||
|
for (uint32_t i = 0; i < suffixes->elements.count; i++) {
|
||||||
|
Var suff_var = suffixes->elements.data[i];
|
||||||
|
if (!IS_OBJ_TYPE(suff_var, OBJ_STRING)) {
|
||||||
|
RET_ERR(newString(vm, "Expected a String for suffix."));
|
||||||
|
}
|
||||||
|
String* suf = (String*)AS_OBJ(suff_var);
|
||||||
|
if (suf->length > self->length) RET(VAR_FALSE);
|
||||||
|
|
||||||
|
const char* start = (self->data + (self->length - suf->length));
|
||||||
|
if (strncmp(start, suf->data, suf->length) == 0) RET(VAR_TRUE);
|
||||||
|
}
|
||||||
|
RET(VAR_FALSE);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
RET_ERR(newString(vm, "Expected a String or a List of suffixes."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DEF(_listAppend,
|
DEF(_listAppend,
|
||||||
"List.append(value:var) -> List\n"
|
"List.append(value:var) -> List\n"
|
||||||
"Append the [value] to the list and return the list.") {
|
"Append the [value] to the list and return the List.") {
|
||||||
|
|
||||||
ASSERT(IS_OBJ_TYPE(SELF, OBJ_LIST), OOPS);
|
ASSERT(IS_OBJ_TYPE(SELF, OBJ_LIST), OOPS);
|
||||||
|
|
||||||
listAppend(vm, ((List*)AS_OBJ(SELF)), ARG(1));
|
listAppend(vm, ((List*) AS_OBJ(SELF)), ARG(1));
|
||||||
RET(SELF);
|
RET(SELF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEF(_listInsert,
|
||||||
|
"List.insert(index:Number, value:var) -> null\n"
|
||||||
|
"Insert the element at the given index. The index should be "
|
||||||
|
"0 <= index <= list.length.") {
|
||||||
|
|
||||||
|
List* self = (List*)AS_OBJ(SELF);
|
||||||
|
|
||||||
|
int64_t index;
|
||||||
|
if (!validateInteger(vm, ARG(1), &index, "Argument 1")) return;
|
||||||
|
|
||||||
|
if (index < 0 || index > self->elements.count) {
|
||||||
|
RET_ERR(newString(vm, "List.insert index out of bounds."));
|
||||||
|
}
|
||||||
|
|
||||||
|
listInsert(vm, self, (uint32_t) index, ARG(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_listPop,
|
||||||
|
"List.pop(index=-1) -> var\n"
|
||||||
|
"Removes the last element of the list and return it.") {
|
||||||
|
|
||||||
|
ASSERT(IS_OBJ_TYPE(SELF, OBJ_LIST), OOPS);
|
||||||
|
List* self = (List*) AS_OBJ(SELF);
|
||||||
|
|
||||||
|
if (!pkCheckArgcRange(vm, ARGC, 0, 1)) return;
|
||||||
|
|
||||||
|
if (self->elements.count == 0) {
|
||||||
|
RET_ERR(newString(vm, "Cannot pop from an empty list."));
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t index = -1;
|
||||||
|
if (ARGC == 1) {
|
||||||
|
if (!validateInteger(vm, ARG(1), &index, "Argument 1")) return;
|
||||||
|
}
|
||||||
|
if (index < 0) index = self->elements.count + index;
|
||||||
|
|
||||||
|
if (index < 0 || index >= self->elements.count) {
|
||||||
|
RET_ERR(newString(vm, "List.pop index out of bounds."));
|
||||||
|
}
|
||||||
|
RET(listRemoveAt(vm, self, (uint32_t) index));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_listFind,
|
||||||
|
"List.find(value:var) -> Number\n"
|
||||||
|
"Find the value and return its index. If the vlaue not exists "
|
||||||
|
"it'll return -1.") {
|
||||||
|
|
||||||
|
ASSERT(IS_OBJ_TYPE(SELF, OBJ_LIST), OOPS);
|
||||||
|
List* self = (List*)AS_OBJ(SELF);
|
||||||
|
|
||||||
|
Var* it = self->elements.data;
|
||||||
|
if (it == NULL) RET(VAR_NUM(-1)); // Empty list.
|
||||||
|
|
||||||
|
for (; it < self->elements.data + self->elements.count; it++) {
|
||||||
|
if (isValuesEqual(*it, ARG(1))) {
|
||||||
|
RET(VAR_NUM((double) (it - self->elements.data)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RET(VAR_NUM(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_listClear,
|
||||||
|
"List.clear() -> null\n"
|
||||||
|
"Removes all the entries in the list.") {
|
||||||
|
listClear(vm, (List*) AS_OBJ(SELF));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_mapClear,
|
||||||
|
"Map.clear() -> null\n"
|
||||||
|
"Removes all the entries in the map.") {
|
||||||
|
Map* self = (Map*) AS_OBJ(SELF);
|
||||||
|
mapClear(vm, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_mapGet,
|
||||||
|
"Map.get(key:var, default=null) -> var\n"
|
||||||
|
"Returns the key if its in the map, otherwise the default value will "
|
||||||
|
"be returned.") {
|
||||||
|
|
||||||
|
if (!pkCheckArgcRange(vm, ARGC, 1, 2)) return;
|
||||||
|
|
||||||
|
Var default_ = (ARGC == 1) ? VAR_NULL : ARG(2);
|
||||||
|
|
||||||
|
Map* self = (Map*) AS_OBJ(SELF);
|
||||||
|
|
||||||
|
Var value = mapGet(self, ARG(1));
|
||||||
|
if (IS_UNDEF(value)) RET(default_);
|
||||||
|
RET(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_mapHas,
|
||||||
|
"Map.has(key:var) -> Bool\n"
|
||||||
|
"Returns true if the key exists.") {
|
||||||
|
|
||||||
|
Map* self = (Map*)AS_OBJ(SELF);
|
||||||
|
Var value = mapGet(self, ARG(1));
|
||||||
|
RET(VAR_BOOL(!IS_UNDEF(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_mapPop,
|
||||||
|
"Map.pop(key:var) -> var\n"
|
||||||
|
"Pops the value at the key and return it.") {
|
||||||
|
|
||||||
|
Map* self = (Map*)AS_OBJ(SELF);
|
||||||
|
Var value = mapRemoveKey(vm, self, ARG(1));
|
||||||
|
if (IS_UNDEF(value)) {
|
||||||
|
RET_ERR(stringFormat(vm, "Key '@' does not exists.", toRepr(vm, ARG(1))));
|
||||||
|
}
|
||||||
|
RET(value);
|
||||||
|
}
|
||||||
|
|
||||||
DEF(_fiberRun,
|
DEF(_fiberRun,
|
||||||
"Fiber.run(...) -> var\n"
|
"Fiber.run(...) -> var\n"
|
||||||
"Runs the fiber's function with the provided arguments and returns it's "
|
"Runs the fiber's function with the provided arguments and returns it's "
|
||||||
"return value or the yielded value if it's yielded.") {
|
"return value or the yielded value if it's yielded.") {
|
||||||
|
|
||||||
ASSERT(IS_OBJ_TYPE(SELF, OBJ_FIBER), OOPS);
|
ASSERT(IS_OBJ_TYPE(SELF, OBJ_FIBER), OOPS);
|
||||||
Fiber* self = (Fiber*)AS_OBJ(SELF);
|
Fiber* self = (Fiber*) AS_OBJ(SELF);
|
||||||
|
|
||||||
// Switch fiber and start execution. New fibers are marked as running in
|
// Switch fiber and start execution. New fibers are marked as running in
|
||||||
// either it's stats running with vmRunFiber() or here -- inserting a
|
// either it's stats running with vmRunFiber() or here -- inserting a
|
||||||
@ -884,7 +1102,7 @@ DEF(_fiberResume,
|
|||||||
"Return it's return value or the yielded value if it's yielded.") {
|
"Return it's return value or the yielded value if it's yielded.") {
|
||||||
|
|
||||||
ASSERT(IS_OBJ_TYPE(SELF, OBJ_FIBER), OOPS);
|
ASSERT(IS_OBJ_TYPE(SELF, OBJ_FIBER), OOPS);
|
||||||
Fiber* self = (Fiber*)AS_OBJ(SELF);
|
Fiber* self = (Fiber*) AS_OBJ(SELF);
|
||||||
|
|
||||||
if (!pkCheckArgcRange(vm, ARGC, 0, 1)) return;
|
if (!pkCheckArgcRange(vm, ARGC, 0, 1)) return;
|
||||||
|
|
||||||
@ -948,12 +1166,32 @@ static void initializePrimitiveClasses(PKVM* vm) {
|
|||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
// TODO: write docs.
|
// TODO: write docs.
|
||||||
ADD_METHOD(PK_NUMBER, "times", _numberTimes, 1);
|
ADD_METHOD(PK_NUMBER, "times", _numberTimes, 1);
|
||||||
ADD_METHOD(PK_NUMBER, "isint", _numberIsint, 0);
|
ADD_METHOD(PK_NUMBER, "isint", _numberIsint, 0);
|
||||||
ADD_METHOD(PK_NUMBER, "isbyte", _numberIsbyte, 0);
|
ADD_METHOD(PK_NUMBER, "isbyte", _numberIsbyte, 0);
|
||||||
ADD_METHOD(PK_LIST, "append", _listAppend, 1);
|
|
||||||
ADD_METHOD(PK_FIBER, "run", _fiberRun, -1);
|
ADD_METHOD(PK_STRING, "strip", _stringStrip, 0);
|
||||||
ADD_METHOD(PK_FIBER, "resume", _fiberResume, -1);
|
ADD_METHOD(PK_STRING, "lower", _stringLower, 0);
|
||||||
|
ADD_METHOD(PK_STRING, "upper", _stringUpper, 0);
|
||||||
|
ADD_METHOD(PK_STRING, "find", _stringFind, -1);
|
||||||
|
ADD_METHOD(PK_STRING, "replace", _stringReplace, -1);
|
||||||
|
ADD_METHOD(PK_STRING, "split", _stringSplit, 1);
|
||||||
|
ADD_METHOD(PK_STRING, "startswith", _stingStartswith, 1);
|
||||||
|
ADD_METHOD(PK_STRING, "endswith", _stingEndswith, 1);
|
||||||
|
|
||||||
|
ADD_METHOD(PK_LIST, "clear", _listClear, 0);
|
||||||
|
ADD_METHOD(PK_LIST, "find", _listFind, 1);
|
||||||
|
ADD_METHOD(PK_LIST, "append", _listAppend, 1);
|
||||||
|
ADD_METHOD(PK_LIST, "pop", _listPop, -1);
|
||||||
|
ADD_METHOD(PK_LIST, "insert", _listInsert, 2);
|
||||||
|
|
||||||
|
ADD_METHOD(PK_MAP, "clear", _mapClear, 0);
|
||||||
|
ADD_METHOD(PK_MAP, "get", _mapGet, -1);
|
||||||
|
ADD_METHOD(PK_MAP, "has", _mapHas, 1);
|
||||||
|
ADD_METHOD(PK_MAP, "pop", _mapPop, 1);
|
||||||
|
|
||||||
|
ADD_METHOD(PK_FIBER, "run", _fiberRun, -1);
|
||||||
|
ADD_METHOD(PK_FIBER, "resume", _fiberResume, -1);
|
||||||
|
|
||||||
#undef ADD_METHOD
|
#undef ADD_METHOD
|
||||||
}
|
}
|
||||||
@ -1347,11 +1585,15 @@ bool varContains(PKVM* vm, Var elem, Var container) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String* sub = (String*)AS_OBJ(elem);
|
String* sub = (String*) AS_OBJ(elem);
|
||||||
String* str = (String*)AS_OBJ(container);
|
String* str = (String*) AS_OBJ(container);
|
||||||
if (sub->length > str->length) return false;
|
if (sub->length > str->length) return false;
|
||||||
|
|
||||||
TODO;
|
// FIXME: strstr function can only be used for null terminated strings.
|
||||||
|
// But pocket lang strings can contain any byte values including a null
|
||||||
|
// byte \x00.
|
||||||
|
const char* match = strstr(str->data, sub->data);
|
||||||
|
return match != NULL;
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -1419,15 +1661,6 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
|||||||
|
|
||||||
case CHECK_HASH("length", 0x83d03615):
|
case CHECK_HASH("length", 0x83d03615):
|
||||||
return VAR_NUM((double)(str->length));
|
return VAR_NUM((double)(str->length));
|
||||||
|
|
||||||
case CHECK_HASH("lower", 0xb51d04ba):
|
|
||||||
return VAR_OBJ(stringLower(vm, str));
|
|
||||||
|
|
||||||
case CHECK_HASH("upper", 0xa8c6a47):
|
|
||||||
return VAR_OBJ(stringUpper(vm, str));
|
|
||||||
|
|
||||||
case CHECK_HASH("strip", 0xfd1b18d1):
|
|
||||||
return VAR_OBJ(stringStrip(vm, str));
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -1441,10 +1674,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case OBJ_MAP: {
|
case OBJ_MAP: {
|
||||||
// map = { "foo" : 42, "can't access" : 32 }
|
// TODO:
|
||||||
// val = map.foo ## <-- This should be error
|
|
||||||
// Only the map's attributes are accessed here.
|
|
||||||
TODO;
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case OBJ_RANGE: {
|
case OBJ_RANGE: {
|
||||||
@ -1509,7 +1739,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case OBJ_CLASS:
|
case OBJ_CLASS:
|
||||||
TODO;
|
// TODO:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OBJ_INST: {
|
case OBJ_INST: {
|
||||||
|
154
src/core/value.c
154
src/core/value.c
@ -23,6 +23,9 @@
|
|||||||
// capacity by the GROW_FACTOR.
|
// capacity by the GROW_FACTOR.
|
||||||
#define GROW_FACTOR 2
|
#define GROW_FACTOR 2
|
||||||
|
|
||||||
|
#define _MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||||
|
#define _MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
// Buffer implementations.
|
// Buffer implementations.
|
||||||
DEFINE_BUFFER(Uint, uint32_t)
|
DEFINE_BUFFER(Uint, uint32_t)
|
||||||
DEFINE_BUFFER(Byte, uint8_t)
|
DEFINE_BUFFER(Byte, uint8_t)
|
||||||
@ -544,7 +547,7 @@ String* stringLower(PKVM* vm, String* self) {
|
|||||||
|
|
||||||
// Start where the first upper case letter found.
|
// Start where the first upper case letter found.
|
||||||
char* _c = lower->data + (c - self->data);
|
char* _c = lower->data + (c - self->data);
|
||||||
for (; *_c != '\0'; _c++) *_c = (char)tolower(*_c);
|
for (; *_c != '\0'; _c++) *_c = (char) tolower(*_c);
|
||||||
|
|
||||||
// Since the string is modified re-hash it.
|
// Since the string is modified re-hash it.
|
||||||
lower->hash = utilHashString(lower->data);
|
lower->hash = utilHashString(lower->data);
|
||||||
@ -565,7 +568,7 @@ String* stringUpper(PKVM* vm, String* self) {
|
|||||||
|
|
||||||
// Start where the first lower case letter found.
|
// Start where the first lower case letter found.
|
||||||
char* _c = upper->data + (c - self->data);
|
char* _c = upper->data + (c - self->data);
|
||||||
for (; *_c != '\0'; _c++) *_c = (char)toupper(*_c);
|
for (; *_c != '\0'; _c++) *_c = (char) toupper(*_c);
|
||||||
|
|
||||||
// Since the string is modified re-hash it.
|
// Since the string is modified re-hash it.
|
||||||
upper->hash = utilHashString(upper->data);
|
upper->hash = utilHashString(upper->data);
|
||||||
@ -608,6 +611,139 @@ String* stringStrip(PKVM* vm, String* self) {
|
|||||||
return newStringLength(vm, start, (uint32_t)(end - start + 1));
|
return newStringLength(vm, start, (uint32_t)(end - start + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String* stringReplace(PKVM* vm, String* self,
|
||||||
|
String* old, String* new_, int32_t count) {
|
||||||
|
// The algorithm:
|
||||||
|
//
|
||||||
|
// We'll first deduce the maximum possible occurence of the old string.
|
||||||
|
//
|
||||||
|
// max_count = floor(self.length / old.length)
|
||||||
|
//
|
||||||
|
// If count == -1 we'll set it to max_count, otherwise we can update our
|
||||||
|
// count as follows.
|
||||||
|
//
|
||||||
|
// count = min(count, min_count)
|
||||||
|
//
|
||||||
|
// Now we know the maximum possible length of the new string.
|
||||||
|
//
|
||||||
|
// length = max(self.length,
|
||||||
|
// self.length + (new.length - old.length) * count)
|
||||||
|
//
|
||||||
|
// Finally we use "C" functions strstr() and memcpy() to find and replace.
|
||||||
|
|
||||||
|
// FIXME: this function use strstr() which only supports null terminated
|
||||||
|
// strings, if our string contain any \x00 the replacement will stop.
|
||||||
|
|
||||||
|
ASSERT(count >= 0 || count == -1, OOPS);
|
||||||
|
|
||||||
|
// Optimize case.
|
||||||
|
if (self->length == 0 || old->length == 0 || count == 0) return self;
|
||||||
|
if (IS_STR_EQ(old, new_)) return self;
|
||||||
|
|
||||||
|
int32_t max_count = self->length / old->length;
|
||||||
|
count = (count == -1)
|
||||||
|
? max_count
|
||||||
|
: _MIN(count, max_count);
|
||||||
|
|
||||||
|
// TODO: New length can be overflow if the string is too large
|
||||||
|
// we should handle it here.
|
||||||
|
|
||||||
|
uint32_t length = _MAX(self->length,
|
||||||
|
self->length + (new_->length - old->length) * count);
|
||||||
|
|
||||||
|
String* replaced = self; // Will be allocated if any match found.
|
||||||
|
int32_t replacedc = 0; // Replaced count so far.
|
||||||
|
|
||||||
|
const char* s = self->data; // Source: current position in self.
|
||||||
|
char* d = NULL; // Destination pointer in replaced.
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (replacedc == count) break;
|
||||||
|
|
||||||
|
const char* match = strstr(s, old->data);
|
||||||
|
if (match == NULL) break;
|
||||||
|
|
||||||
|
// Note that since we're not allocating anything else here, this string
|
||||||
|
// doesn't needs to pushed to VM's temp references.
|
||||||
|
if (replacedc == 0) {
|
||||||
|
replaced = newStringLength(vm, "", length);
|
||||||
|
d = replaced->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy everything from [s] till [match].
|
||||||
|
memcpy(d, s, match - s);
|
||||||
|
d += match - s;
|
||||||
|
s = match;
|
||||||
|
|
||||||
|
// Copy the replace string.
|
||||||
|
memcpy(d, new_->data, new_->length);
|
||||||
|
d += new_->length;
|
||||||
|
s += old->length;
|
||||||
|
replacedc++;
|
||||||
|
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
// Copy the rest of the string from [s] till the end.
|
||||||
|
if (d != NULL) {
|
||||||
|
uint32_t tail_length = self->length - (int32_t) (s - self->data);
|
||||||
|
memcpy(d, s, tail_length);
|
||||||
|
d += tail_length;
|
||||||
|
|
||||||
|
// Update the string.
|
||||||
|
replaced->length = (int32_t) (d - replaced->data);
|
||||||
|
ASSERT(replaced->length < replaced->capacity, OOPS);
|
||||||
|
replaced->data[replaced->length] = '\0';
|
||||||
|
replaced->hash = utilHashString(replaced->data);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ASSERT(self == replaced, OOPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return replaced;
|
||||||
|
}
|
||||||
|
|
||||||
|
List* stringSplit(PKVM* vm, String* self, String* sep) {
|
||||||
|
|
||||||
|
ASSERT(sep->length != 0, OOPS);
|
||||||
|
|
||||||
|
const char* s = self->data; // Current position in self.
|
||||||
|
|
||||||
|
List* list = newList(vm, 0);
|
||||||
|
vmPushTempRef(vm, &list->_super); // list.
|
||||||
|
do {
|
||||||
|
const char* match = strstr(s, sep->data);
|
||||||
|
if (match == NULL) {
|
||||||
|
|
||||||
|
// Add the tail string from [s] till the end. Optimize case: if the
|
||||||
|
// string doesn't have any match we can reuse self.
|
||||||
|
if (s == self->data) {
|
||||||
|
ASSERT(list->elements.count == 0, OOPS);
|
||||||
|
listAppend(vm, list, VAR_OBJ(self));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
String* tail = newStringLength(vm, s,
|
||||||
|
(uint32_t)(self->length - (s - self->data)));
|
||||||
|
vmPushTempRef(vm, &tail->_super); // tail.
|
||||||
|
listAppend(vm, list, VAR_OBJ(tail));
|
||||||
|
vmPopTempRef(vm); // tail.
|
||||||
|
}
|
||||||
|
|
||||||
|
break; // We're done.
|
||||||
|
}
|
||||||
|
|
||||||
|
String* split = newStringLength(vm, s, (uint32_t)(match - s));
|
||||||
|
vmPushTempRef(vm, &split->_super); // split.
|
||||||
|
listAppend(vm, list, VAR_OBJ(split));
|
||||||
|
vmPopTempRef(vm); // split.
|
||||||
|
|
||||||
|
s = match + sep->length;
|
||||||
|
|
||||||
|
} while (true);
|
||||||
|
vmPopTempRef(vm); // list.
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
String* stringFormat(PKVM* vm, const char* fmt, ...) {
|
String* stringFormat(PKVM* vm, const char* fmt, ...) {
|
||||||
va_list arg_list;
|
va_list arg_list;
|
||||||
|
|
||||||
@ -698,6 +834,8 @@ void listInsert(PKVM* vm, List* self, uint32_t index, Var value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Var listRemoveAt(PKVM* vm, List* self, uint32_t index) {
|
Var listRemoveAt(PKVM* vm, List* self, uint32_t index) {
|
||||||
|
ASSERT_INDEX(index, self->elements.count);
|
||||||
|
|
||||||
Var removed = self->elements.data[index];
|
Var removed = self->elements.data[index];
|
||||||
if (IS_OBJ(removed)) vmPushTempRef(vm, AS_OBJ(removed));
|
if (IS_OBJ(removed)) vmPushTempRef(vm, AS_OBJ(removed));
|
||||||
|
|
||||||
@ -708,7 +846,7 @@ Var listRemoveAt(PKVM* vm, List* self, uint32_t index) {
|
|||||||
|
|
||||||
// Shrink the size if it's too much excess.
|
// Shrink the size if it's too much excess.
|
||||||
if (self->elements.capacity / GROW_FACTOR >= self->elements.count) {
|
if (self->elements.capacity / GROW_FACTOR >= self->elements.count) {
|
||||||
self->elements.data = (Var*)vmRealloc(vm, self->elements.data,
|
self->elements.data = (Var*) vmRealloc(vm, self->elements.data,
|
||||||
sizeof(Var) * self->elements.capacity,
|
sizeof(Var) * self->elements.capacity,
|
||||||
sizeof(Var) * self->elements.capacity / GROW_FACTOR);
|
sizeof(Var) * self->elements.capacity / GROW_FACTOR);
|
||||||
self->elements.capacity /= GROW_FACTOR;
|
self->elements.capacity /= GROW_FACTOR;
|
||||||
@ -720,6 +858,10 @@ Var listRemoveAt(PKVM* vm, List* self, uint32_t index) {
|
|||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void listClear(PKVM* vm, List* self) {
|
||||||
|
pkVarBufferClear(&self->elements, vm);
|
||||||
|
}
|
||||||
|
|
||||||
List* listAdd(PKVM* vm, List* l1, List* l2) {
|
List* listAdd(PKVM* vm, List* l1, List* l2) {
|
||||||
|
|
||||||
// Optimize end case.
|
// Optimize end case.
|
||||||
@ -898,7 +1040,7 @@ void mapClear(PKVM* vm, Map* self) {
|
|||||||
|
|
||||||
Var mapRemoveKey(PKVM* vm, Map* self, Var key) {
|
Var mapRemoveKey(PKVM* vm, Map* self, Var key) {
|
||||||
MapEntry* entry;
|
MapEntry* entry;
|
||||||
if (!_mapFindEntry(self, key, &entry)) return VAR_NULL;
|
if (!_mapFindEntry(self, key, &entry)) return VAR_UNDEFINED;
|
||||||
|
|
||||||
// Set the key as VAR_UNDEFINED to mark is as an available slow and set it's
|
// Set the key as VAR_UNDEFINED to mark is as an available slow and set it's
|
||||||
// value to VAR_TRUE for tombstone.
|
// value to VAR_TRUE for tombstone.
|
||||||
@ -1609,3 +1751,7 @@ bool toBool(Var v) {
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Undefining for amalgamation to let othre libraries also define _MIN, _MAX.
|
||||||
|
#undef _MAX
|
||||||
|
#undef _MIN
|
||||||
|
@ -643,6 +643,16 @@ String* stringUpper(PKVM* vm, String* self);
|
|||||||
// If the string is already trimmed it'll return the same string.
|
// If the string is already trimmed it'll return the same string.
|
||||||
String* stringStrip(PKVM* vm, String* self);
|
String* stringStrip(PKVM* vm, String* self);
|
||||||
|
|
||||||
|
// Replace the [count] occurence of the [old] string with [new_] string. If
|
||||||
|
// [count] == -1. It'll replace all the occurences. If nothing is replaced
|
||||||
|
// the original string will be returned.
|
||||||
|
String* stringReplace(PKVM* vm, String* self,
|
||||||
|
String* old, String* new_, int count);
|
||||||
|
|
||||||
|
// Split the string into a list of string separated by [sep]. String [sep] must
|
||||||
|
// not be of length 0 otherwise an assertion will fail.
|
||||||
|
List* stringSplit(PKVM* vm, String* self, String* sep);
|
||||||
|
|
||||||
// Creates a new string from the arguments. This is intended for internal
|
// Creates a new string from the arguments. This is intended for internal
|
||||||
// usage and it has 2 formated characters (just like wren does).
|
// usage and it has 2 formated characters (just like wren does).
|
||||||
// $ - a C string
|
// $ - a C string
|
||||||
@ -672,6 +682,9 @@ void listInsert(PKVM* vm, List* self, uint32_t index, Var value);
|
|||||||
// Remove and return element at [index].
|
// Remove and return element at [index].
|
||||||
Var listRemoveAt(PKVM* vm, List* self, uint32_t index);
|
Var listRemoveAt(PKVM* vm, List* self, uint32_t index);
|
||||||
|
|
||||||
|
// Remove all the elements of the list.
|
||||||
|
void listClear(PKVM* vm, List* self);
|
||||||
|
|
||||||
// Create a new list by joining the 2 given list and return the result.
|
// Create a new list by joining the 2 given list and return the result.
|
||||||
List* listAdd(PKVM* vm, List* l1, List* l2);
|
List* listAdd(PKVM* vm, List* l1, List* l2);
|
||||||
|
|
||||||
@ -686,7 +699,7 @@ void mapSet(PKVM* vm, Map* self, Var key, Var value);
|
|||||||
void mapClear(PKVM* vm, Map* self);
|
void mapClear(PKVM* vm, Map* self);
|
||||||
|
|
||||||
// Remove the [key] from the map. If the key exists return it's value
|
// Remove the [key] from the map. If the key exists return it's value
|
||||||
// otherwise return VAR_NULL.
|
// otherwise return VAR_UNDEFINED.
|
||||||
Var mapRemoveKey(PKVM* vm, Map* self, Var key);
|
Var mapRemoveKey(PKVM* vm, Map* self, Var key);
|
||||||
|
|
||||||
// Returns true if the fiber has error, and if it has any the fiber cannot be
|
// Returns true if the fiber has error, and if it has any the fiber cannot be
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
## Number
|
## Number
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
assert(hex(12648430) == '0xc0ffee')
|
||||||
|
assert(hex(255) == '0xff' and hex(10597059) == '0xa1b2c3')
|
||||||
|
assert(hex(-4294967295) == '-0xffffffff') ## the largest.
|
||||||
|
|
||||||
sum = 0
|
sum = 0
|
||||||
5.times fn(i)
|
5.times fn(i)
|
||||||
sum += i
|
sum += i
|
||||||
@ -15,10 +19,27 @@ assert(Number("-.23e+6") == -.23e6)
|
|||||||
assert(Number("0b10100101") == 0b10100101)
|
assert(Number("0b10100101") == 0b10100101)
|
||||||
assert(Number("-0Xabcdef123") == -0xabcdef123)
|
assert(Number("-0Xabcdef123") == -0xabcdef123)
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## RANGE
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
r = 1..5
|
||||||
|
assert(r.as_list == [1, 2, 3, 4])
|
||||||
|
assert(r.first == 1)
|
||||||
|
assert(r.last == 5)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
## STRING
|
## STRING
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
assert(''.length == 0)
|
||||||
|
assert('test'.length == 4)
|
||||||
|
assert(''.lower() == '' and ''.upper() == '')
|
||||||
|
assert('already+lower '.lower() == 'already+lower ')
|
||||||
|
assert('ALREADY+UPPER '.upper() == 'ALREADY+UPPER ')
|
||||||
|
assert('tEST+InG'.lower() == 'test+ing')
|
||||||
|
assert('tEST+InG'.upper() == 'TEST+ING')
|
||||||
|
|
||||||
s = "foobar"
|
s = "foobar"
|
||||||
assert(s[-3] == 'b')
|
assert(s[-3] == 'b')
|
||||||
|
|
||||||
@ -33,6 +54,39 @@ assert("abcd"[-1..-3] == "dcb")
|
|||||||
assert(""[0..-1] == "")
|
assert(""[0..-1] == "")
|
||||||
assert(""[-1..0] == "")
|
assert(""[-1..0] == "")
|
||||||
|
|
||||||
|
assert(' trim '.strip() == 'trim')
|
||||||
|
assert(''.strip() == '')
|
||||||
|
|
||||||
|
lorem = "\
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
|
||||||
|
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nost
|
||||||
|
rud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis
|
||||||
|
aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fug
|
||||||
|
iat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in cu
|
||||||
|
lpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
|
||||||
|
assert("sint" in lorem)
|
||||||
|
ut = lorem.find('ut'); assert(ut != -1)
|
||||||
|
assert(lorem[ut+3 .. ut+8] == 'labore')
|
||||||
|
|
||||||
|
assert("foobar".replace("foo", "baz") == "bazbar")
|
||||||
|
assert("abcdefabdcefabaeef".replace("ab", "") == "cdefdcefaeef")
|
||||||
|
assert("xx.xx.xx.xx".replace("xx", "yy", 2) == "yy.yy.xx.xx")
|
||||||
|
assert("aaaaaaaaaaaaa".replace('a', '!b', 5) == "!b!b!b!b!baaaaaaaa")
|
||||||
|
assert("aaaaaaaaaaaaa".replace('a', 'b', 1000) == "bbbbbbbbbbbbb")
|
||||||
|
|
||||||
|
assert("a,b,c".split(',') == ['a', 'b', 'c'])
|
||||||
|
assert('a,'.split(',') == ['a', ''])
|
||||||
|
assert('foo!!bar!!baz'.split('!!') == ['foo', 'bar', 'baz'])
|
||||||
|
|
||||||
|
assert('abcdef'.startswith('abc'))
|
||||||
|
assert(not 'x'.startswith('abc'))
|
||||||
|
assert(not 'pqr'.startswith(['abc', 'def', 'gh']))
|
||||||
|
|
||||||
|
assert('foobar'.endswith('obar'))
|
||||||
|
assert(not 'foobar'.endswith('rfoo'))
|
||||||
|
assert('image.png'.endswith(['.jpg', '.jpeg', '.png', '.ppm']))
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
## LIST
|
## LIST
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@ -52,6 +106,67 @@ assert(l[-1..-3] == [4, 3, 2])
|
|||||||
assert([][0..0] == [])
|
assert([][0..0] == [])
|
||||||
assert([][-1..-1] == [])
|
assert([][-1..-1] == [])
|
||||||
|
|
||||||
|
assert([].length == 0)
|
||||||
|
assert([1, 2, 3].length == 3)
|
||||||
|
|
||||||
|
assert(['a'].append('b') == ['a', 'b'])
|
||||||
|
|
||||||
|
assert(l.find(2) == 1)
|
||||||
|
assert(l.find(4) == 3)
|
||||||
|
assert(l.find(7) == -1)
|
||||||
|
|
||||||
|
assert(l.pop(-2) == 3 and l == [1, 2, 4])
|
||||||
|
assert(l.pop() == 4)
|
||||||
|
assert(l.pop() == 2)
|
||||||
|
assert(l == [1])
|
||||||
|
|
||||||
|
l = []
|
||||||
|
l.insert(0, 'l')
|
||||||
|
l.insert(1, 'l')
|
||||||
|
l.insert(0, 'h')
|
||||||
|
l.insert(3, 'o')
|
||||||
|
l.insert(1, 'e')
|
||||||
|
assert(l == ['h', 'e', 'l', 'l', 'o'])
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## MAP
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
swtich = {
|
||||||
|
0 : fn return 1 end,
|
||||||
|
1 : fn return 2 end,
|
||||||
|
2 : fn return 3 end,
|
||||||
|
3 : fn return 4 end,
|
||||||
|
4 : fn return 5 end,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..5
|
||||||
|
assert(swtich.get(i)() == i+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
vars = {
|
||||||
|
'os' : 'mint',
|
||||||
|
'version' : '20.3',
|
||||||
|
'name' : 'Una',
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(vars.get('os') == 'mint')
|
||||||
|
assert(vars.get('family', 'linux') == 'linux')
|
||||||
|
assert(vars.has('version'))
|
||||||
|
assert(!vars.has('build'))
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## FUNCTIONS
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
assert(print.arity == -1)
|
||||||
|
assert(hex.arity == 1)
|
||||||
|
assert(fn(a, b)end .arity == 2)
|
||||||
|
assert(print.name == "print")
|
||||||
|
def foo(p1, p2, p3) end
|
||||||
|
assert(foo.name == "foo")
|
||||||
|
assert(foo.arity == 3)
|
||||||
|
|
||||||
## If we got here, that means all test were passed.
|
## If we got here, that means all test were passed.
|
||||||
print('All TESTS PASSED')
|
print('All TESTS PASSED')
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
## Core builtin functions and attribute tests.
|
|
||||||
|
|
||||||
assert(hex(12648430) == '0xc0ffee')
|
|
||||||
assert(hex(255) == '0xff' and hex(10597059) == '0xa1b2c3')
|
|
||||||
assert(hex(-4294967295) == '-0xffffffff') ## the largest.
|
|
||||||
|
|
||||||
## string attributes.
|
|
||||||
assert(''.length == 0)
|
|
||||||
assert('test'.length == 4)
|
|
||||||
assert(''.lower == '' and ''.upper == '')
|
|
||||||
assert('already+lower '.lower == 'already+lower ')
|
|
||||||
assert('ALREADY+UPPER '.upper == 'ALREADY+UPPER ')
|
|
||||||
assert('tEST+InG'.lower == 'test+ing')
|
|
||||||
assert('tEST+InG'.upper == 'TEST+ING')
|
|
||||||
|
|
||||||
assert(' trim '.strip == 'trim')
|
|
||||||
assert(''.strip == '')
|
|
||||||
|
|
||||||
## List attribute
|
|
||||||
assert([].length == 0)
|
|
||||||
assert([1, 2, 3].length == 3)
|
|
||||||
|
|
||||||
## Function
|
|
||||||
assert(print.arity == -1)
|
|
||||||
assert(hex.arity == 1)
|
|
||||||
assert(fn(a, b)end .arity == 2)
|
|
||||||
assert(print.name == "print")
|
|
||||||
def foo(p1, p2, p3) end
|
|
||||||
assert(foo.name == "foo")
|
|
||||||
assert(foo.arity == 3)
|
|
||||||
|
|
||||||
## String functions
|
|
||||||
assert(str_sub('c programming', 2, 11) == 'programming')
|
|
||||||
assert(str_sub('foobarbaz', 2, 3) == 'oba')
|
|
||||||
assert(str_sub('abcdef', 1, 2) == 'bc')
|
|
||||||
assert(str_sub('I am a boy', 0, 4) == 'I am')
|
|
||||||
assert(str_sub('programming', 2, 0) == '')
|
|
||||||
assert(str_sub('foobar', 5, 0) == '')
|
|
||||||
assert(str_sub('foobar', 0, 6) == 'foobar')
|
|
||||||
assert(str_sub('', 0, 0) == '')
|
|
||||||
|
|
||||||
## range
|
|
||||||
r = 1..5
|
|
||||||
assert(r.as_list == [1, 2, 3, 4])
|
|
||||||
assert(r.first == 1)
|
|
||||||
assert(r.last == 5)
|
|
||||||
|
|
||||||
# If we got here, that means all test were passed.
|
|
||||||
print('All TESTS PASSED')
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user