From 9541a962799a21d92336281a543608ae9d5b9ec3 Mon Sep 17 00:00:00 2001 From: mtardy Date: Mon, 7 Sep 2020 10:30:15 +0200 Subject: [PATCH] Correctly add slice '[n:m]' for strings and array-like maps --- object.c | 59 ++++++++++++++++++++++++++++------ parse.leg | 87 ++++++++++++++++++++++++++++++++++++++++---------- test-slice.txt | 6 ++-- 3 files changed, 123 insertions(+), 29 deletions(-) diff --git a/object.c b/object.c index d59348f..b79ba08 100644 --- a/object.c +++ b/object.c @@ -236,6 +236,7 @@ oop makeInteger(int_t value) return newInt; } +// value will be copied oop makeString(char *value) { oop newString = malloc(sizeof(struct String)); @@ -245,11 +246,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); @@ -257,11 +284,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 = malloc(sizeof(struct String)); - newString->type = String; - newString->String.value = concat; - newString->String.size = len; - return newString; + return makeStringFrom(concat, len); } oop string_mul(oop str, oop factor) @@ -273,11 +296,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 = malloc(sizeof(struct String)); - newString->type = String; - newString->String.value = concat; - newString->String.size = len; - return newString; + return makeStringFrom(concat, len); } oop makeSymbol(char *name) @@ -498,6 +517,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 668cb08..8d36795 100644 --- a/parse.leg +++ b/parse.leg @@ -19,7 +19,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) @@ -70,7 +70,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() @@ -560,6 +561,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); @@ -678,23 +688,23 @@ cond = c:logor QUERY t:exp COLON f:cond { $$ = newIf(c, t, logor = l:logand ( LOGOR r:logand { l = newBinary(Logor_proto, l, r) } - )* { $$ = l } + )* { $$ = l } logand = l:bitor ( LOGAND r:bitor { l = newBinary(Logand_proto, l, r) } - )* { $$ = l } + )* { $$ = l } bitor = l:bitxor ( BITOR r:bitxor { l = newBinary(Bitor_proto, l, r) } - )* { $$ = l } + )* { $$ = 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) } @@ -714,8 +724,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 @@ -736,6 +746,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, globals, getSyntax(1, i), a, i) : newCall(i, a) } | PLUSPLUS { i = newPostIncrement(i) } @@ -1559,12 +1573,24 @@ 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: - return getVariable(map, key); + 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"); } @@ -1577,7 +1603,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; @@ -1589,6 +1615,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-slice.txt b/test-slice.txt index 23313f2..921c9ac 100644 --- a/test-slice.txt +++ b/test-slice.txt @@ -1,10 +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 +m = { 1: "three", 2: "four", 3: "five" } +println(m) +println(b[-2]); \ No newline at end of file