From 1ed52120bbdb3f9dfbe62d3bff71d7a3482d509d Mon Sep 17 00:00:00 2001 From: mtardy Date: Fri, 28 Aug 2020 10:06:53 +0200 Subject: [PATCH] Add slice '[n:m]' for strings and array-like maps --- object.c | 59 +++++++++++++++++++++++++------ parse.leg | 91 ++++++++++++++++++++++++++++++++++++++---------- test-module.txt | 3 +- test-require.txt | 3 +- test-slice.txt | 10 ++++++ 5 files changed, 135 insertions(+), 31 deletions(-) create mode 100644 test-slice.txt diff --git a/object.c b/object.c index 051bb60..0518095 100644 --- a/object.c +++ b/object.c @@ -205,6 +205,7 @@ oop makeInteger(int value) #endif } +// value will be copied oop makeString(char *value) { oop newString = memcheck(malloc(sizeof(union object))); @@ -214,11 +215,37 @@ oop makeString(char *value) return newString; } +// value will be used directly +oop makeStringFrom(char *value, size_t l) +{ + oop newString = memcheck(malloc(sizeof(union object))); + newString->type = String; + newString->String.value = value; + newString->String.size = l; + return newString; +} + size_t string_size(oop s) { return get(s, String, size); } +oop string_slice(oop str, ssize_t start, ssize_t stop) { + assert(is(String, str)); + size_t len = string_size(str); + if (start < 0) start= start + len; + if (stop < 0) stop= stop + len; + if (start < 0 || start > len) return NULL; + if (stop < 0 || stop > len) return NULL; + if (start > stop) return NULL; + + size_t cpylen = stop - start; + char *slice= memcheck(malloc(sizeof(char) * (cpylen + 1))); + memcpy(slice, get(str, String, value) + start, cpylen); + slice[cpylen]= '\0'; + return makeStringFrom(slice, cpylen); +} + oop string_concat(oop str1, oop str2) { size_t len = string_size(str1) + string_size(str2); @@ -226,11 +253,7 @@ oop string_concat(oop str1, oop str2) memcpy(concat, get(str1, String, value), string_size(str1)); memcpy(concat + string_size(str1), get(str2, String, value), string_size(str2)); concat[len]= '\0'; - oop newString = memcheck(malloc(sizeof(union object))); - newString->type = String; - newString->String.value = concat; - newString->String.size = len; - return newString; + return makeStringFrom(concat, len); } oop string_mul(oop str, oop factor) @@ -242,11 +265,7 @@ oop string_mul(oop str, oop factor) memcpy(concat + (i * string_size(str)), get(str, String, value), string_size(str)); } concat[len]= '\0'; - oop newString = memcheck(malloc(sizeof(union object))); - newString->type = String; - newString->String.value = concat; - newString->String.size = len; - return newString; + return makeStringFrom(concat, len); } oop makeSymbol(char *name) @@ -459,6 +478,26 @@ oop map_values(oop map) return values; } +oop map_slice(oop map, ssize_t start, ssize_t stop) { + assert(is(Map, map)); + size_t len = map_size(map); + if (start < 0) start= start + len; + if (stop < 0) stop= stop + len; + if (start < 0 || start > len) return NULL; + if (stop < 0 || stop > len) return NULL; + if (start > stop) return NULL; + + oop slice= makeMap(); + if (start < stop) { + if (!map_hasIntegerKey(map, start )) return NULL; + if (!map_hasIntegerKey(map, stop - 1)) return NULL; + for (size_t i= start; i < stop; ++i) { + map_append(slice, get(map, Map, elements)[i].value); + } + } + return slice; +} + DECLARE_BUFFER(oop, OopStack); OopStack printing = BUFFER_INITIALISER; diff --git a/parse.leg b/parse.leg index d44a924..1b66bac 100644 --- a/parse.leg +++ b/parse.leg @@ -17,7 +17,7 @@ _DO(PostIncVariable) _DO(PostIncMember) _DO(PostIncIndex) \ _DO(PreDecVariable) _DO(PreDecMember) _DO(PreDecIndex) \ _DO(PostDecVariable) _DO(PostDecMember) _DO(PostDecIndex) \ - _DO(GetVariable) _DO(GetMember) _DO(SetMember) _DO(GetIndex) _DO(SetIndex) \ + _DO(GetVariable) _DO(GetMember) _DO(SetMember) _DO(GetIndex) _DO(SetIndex) _DO(Slice) \ _DO(Return) _DO(Break) _DO(Continue) _DO(Throw) _DO(Try) \ _DO(Quasiquote) _DO(Unquote) @@ -75,7 +75,8 @@ oop globals= 0; _DO(lhs) _DO(rhs) _DO(scope) _DO(args) _DO(expression) _DO(labels) _DO(statements) _DO(initialise) \ _DO(update) _DO(this) _DO(fixed) _DO(operator) _DO(map) _DO(func) \ _DO(try) _DO(catch) _DO(finally) _DO(exception) \ - _DO(__line__) _DO(__file__) + _DO(__line__) _DO(__file__) \ + _DO(start) _DO(stop) #define _DO(NAME) oop NAME##_symbol; DO_SYMBOLS() @@ -547,6 +548,15 @@ oop newContinue(void) return obj; } +oop newSlice(oop value, oop start, oop stop) +{ + oop obj= newObject(Slice_proto); + map_set(obj, value_symbol, value); + map_set(obj, start_symbol, start); + map_set(obj, stop_symbol, stop); + return obj; +} + oop newTry(oop try, oop exception, oop catch, oop finally) { oop obj = newObject(Try_proto); @@ -664,24 +674,24 @@ cond = c:logor QUERY t:exp COLON f:cond { $$ = newIf(c, t, | logor logor = l:logand - ( LOGOR r:logand { l = newBinary(Logor_proto, l, r) } - )* { $$ = l } + ( LOGOR r:logand { l = newBinary(Logor_proto, l, r) } + )* { $$ = l } logand = l:bitor - ( LOGAND r:bitor { l = newBinary(Logand_proto, l, r) } - )* { $$ = l } + ( LOGAND r:bitor { l = newBinary(Logand_proto, l, r) } + )* { $$ = l } bitor = l:bitxor - ( BITOR r:bitxor { l = newBinary(Bitor_proto, l, r) } - )* { $$ = l } + ( BITOR r:bitxor { l = newBinary(Bitor_proto, l, r) } + )* { $$ = l } bitxor = l:bitand - ( BITXOR r:bitand { l = newBinary(Bitxor_proto, l, r) } - )* { $$ = l } + ( BITXOR r:bitand { l = newBinary(Bitxor_proto, l, r) } + )* { $$ = l } bitand = l:eq - ( BITAND r:eq { l = newBinary(Bitand_proto, l, r) } - )* { $$ = l } + ( BITAND r:eq { l = newBinary(Bitand_proto, l, r) } + )* { $$ = l } eq = l:ineq ( EQUAL r:ineq { l = newBinary(Equal_proto, l, r) } @@ -701,8 +711,8 @@ shift = l:sum )* { $$ = l } sum = l:prod - ( PLUS r:prod { l = newBinary(Add_proto, l, r) } - | MINUS r:prod { l = newBinary(Sub_proto, l, r) } + ( PLUS r:prod { l = newBinary(Add_proto, l, r) } + | MINUS r:prod { l = newBinary(Sub_proto, l, r) } )* { $$ = l } prod = l:prefix @@ -723,6 +733,10 @@ prefix = PLUS n:prefix { $$= n } postfix = i:value ( DOT s:IDENT a:argumentList { i = newInvoke(i, s, a) } | DOT s:IDENT !assignOp { i = newGetMap(GetMember_proto, i, s) } + | LBRAC e1:exp COLON e2:exp RBRAC !assignOp { i = newSlice(i, e1, e2) } + | LBRAC e1:exp COLON RBRAC !assignOp { i = newSlice(i, e1, null) } + | LBRAC COLON e2:exp RBRAC !assignOp { i = newSlice(i, null, e2) } + | LBRAC COLON RBRAC !assignOp { i = newSlice(i, null, null) } | LBRAC p:exp RBRAC !assignOp { i = newGetMap(GetIndex_proto, i, p) } | a:argumentList { i = (null != getSyntax(1, i)) ? apply(globals, getSyntax(1, i), a) : newCall(i, a) } | PLUSPLUS { i = newPostIncrement(i) } @@ -1556,11 +1570,23 @@ oop eval(oop scope, oop ast) oop key = eval(scope, map_get(ast, key_symbol)); switch (getType(map)) { case String: - if (getInteger(key) >= get(map, String, size)) { - runtimeError("GetIndex out of range on String"); + if (!isInteger(key)) { + runtimeError("non-integer index"); + } + ssize_t i= getInteger(key); + size_t len= string_size(map); + if (i < 0) i+= len; + if (i < 0 || i >= len) { + runtimeError("GetIndex out of bounds on String"); } - return makeInteger(unescape(get(map, String, value))[getInteger(key)]); + return makeInteger(get(map, String, value)[i]); case Map: + if (isInteger(key) && getInteger(key) < 0) { + size_t size= map_size(map); + if (size > 0 && map_hasIntegerKey(map, size - 1)) { + key= makeInteger(getInteger(key) + size); + } + } return map_get(map, key); default: runtimeError("GetIndex on non Map or String"); @@ -1574,7 +1600,7 @@ oop eval(oop scope, oop ast) switch (getType(map)) { case String: if (getInteger(key) >= get(map, String, size)) { - runtimeError("SetIndex out of range on String"); + runtimeError("SetIndex out of bounds on String"); } get(map, String, value)[getInteger(key)] = getInteger(value); return value; @@ -1586,6 +1612,35 @@ oop eval(oop scope, oop ast) } } + case t_Slice: { + oop pre= eval(scope, map_get(ast, value_symbol)); + oop start= eval(scope, map_get(ast, start_symbol)); + oop stop= eval(scope, map_get(ast, stop_symbol)); + ssize_t first= start == null ? 0 : getInteger(start); + + if (start == null) { + start= makeInteger(0); + } + switch (getType(pre)) { + case String: { + ssize_t last= stop == null ? string_size(pre) : getInteger(stop); + oop res= string_slice(pre, first, last); + if (NULL == res) { + runtimeError("index out of bounds"); + } + return res; + } + case Map: { + ssize_t last= stop == null ? map_size(pre) : getInteger(stop); + oop res= map_slice(pre, first, last); + if (NULL == res) { + runtimeError("index out of bounds"); + } + return res; + } + } + runtimeError("slicing a non-String or non-Map"); + } case t_Symbol: case t_Integer: case t_String: { diff --git a/test-module.txt b/test-module.txt index 2f88091..df0954f 100644 --- a/test-module.txt +++ b/test-module.txt @@ -2,5 +2,4 @@ var pi = 3 fun sum(a, b) { return a+b; -} -println("Hello from test-module.txt"); \ No newline at end of file +} \ No newline at end of file diff --git a/test-require.txt b/test-require.txt index d76d436..7c78098 100644 --- a/test-require.txt +++ b/test-require.txt @@ -3,4 +3,5 @@ var mod = require("test-module.txt"); println(mod.sum(40, 2)); println(mod.pi); var mod2 = require("test-module.txt"); -println(mod2.pi); \ No newline at end of file +println(mod2.pi); +println(invoke(mod2, mod2.pi, [2, 4])) \ No newline at end of file diff --git a/test-slice.txt b/test-slice.txt new file mode 100644 index 0000000..23313f2 --- /dev/null +++ b/test-slice.txt @@ -0,0 +1,10 @@ +s = "hello" +b = [2, 4, 6] +b.pizza= "ok" +println(s[-4:-3]) +println(s[:-3]) +println(s[-4:]) +println(s[:]) +println(s[:]) +m = { 3: "three", 1: "four", 2: "five" } +println({}[-0]); \ No newline at end of file