mirror of
https://github.com/janet-lang/janet
synced 2024-11-28 19:19:53 +00:00
Merge pull request #913 from ishehadeh/feature/destructuring-rest
Support for `& rest` pattern in destructure and match
This commit is contained in:
commit
56357699cb
@ -1678,6 +1678,7 @@
|
||||
|
||||
* array or bracket tuple -- an array or bracket tuple will match only if
|
||||
all of its elements match the corresponding elements in `x`.
|
||||
Use `& rest` at the end of an array or bracketed tuple to bind all remaining values to `rest`.
|
||||
|
||||
* table or struct -- a table or struct will match if all values match with
|
||||
the corresponding values in `x`.
|
||||
@ -1751,10 +1752,26 @@
|
||||
(break)
|
||||
|
||||
# match data structure template
|
||||
(or isarr (= t :struct) (= t :table))
|
||||
(or (= t :struct) (= t :table))
|
||||
(eachp [i sub-pattern] pattern
|
||||
(visit-pattern-1 b2g s i sub-pattern))
|
||||
|
||||
isarr
|
||||
(do
|
||||
(when isarr (get-length-sym s))
|
||||
(get-length-sym s)
|
||||
(eachp [i sub-pattern] pattern
|
||||
(when (= sub-pattern '&)
|
||||
(when (<= (length pattern) (inc i))
|
||||
(errorf "expected symbol following & in pattern"))
|
||||
|
||||
(when (< (+ i 2) (length pattern))
|
||||
(errorf "expected a single symbol follow '& in pattern, found %q" (slice pattern (inc i))))
|
||||
|
||||
(when (not= (type (pattern (inc i))) :symbol)
|
||||
(errorf "expected symbol following & in pattern, found %q" (pattern (inc i))))
|
||||
|
||||
(put b2g (pattern (inc i)) @[[slice s i]])
|
||||
(break))
|
||||
(visit-pattern-1 b2g s i sub-pattern)))
|
||||
|
||||
# match global unification
|
||||
@ -1774,14 +1791,24 @@
|
||||
(def isarr (or (= t :array) (and (= t :tuple) (= (tuple/type pattern) :brackets))))
|
||||
(when isarr
|
||||
(array/push anda (get-length-sym s))
|
||||
(array/push anda [<= (length pattern) (get-length-sym s)]))
|
||||
(def pattern-len
|
||||
(if-let [ rest-idx (find-index (fn [x] (= x '&)) pattern) ]
|
||||
rest-idx
|
||||
(length pattern)))
|
||||
(array/push anda [<= pattern-len (get-length-sym s)]))
|
||||
(cond
|
||||
|
||||
# match data structure template
|
||||
(or isarr (= t :struct) (= t :table))
|
||||
(or (= t :struct) (= t :table))
|
||||
(eachp [i sub-pattern] pattern
|
||||
(when (not isarr)
|
||||
(array/push anda [not= nil (get-sym s i)]))
|
||||
(array/push anda [not= nil (get-sym s i)])
|
||||
(visit-pattern-2 anda gun preds s i sub-pattern))
|
||||
|
||||
isarr
|
||||
(eachp [i sub-pattern] pattern
|
||||
# stop recursing to sub-patterns if the rest sigil is found
|
||||
(when (= sub-pattern '&)
|
||||
(break))
|
||||
(visit-pattern-2 anda gun preds s i sub-pattern))
|
||||
|
||||
# match local binding
|
||||
|
@ -154,6 +154,69 @@ static int destructure(JanetCompiler *c,
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
JanetSlot nextright = janetc_farslot(c);
|
||||
Janet subval = values[i];
|
||||
|
||||
if (janet_checktype(subval, JANET_SYMBOL) && !janet_cstrcmp(janet_unwrap_symbol(subval), "&")) {
|
||||
if (i + 1 >= len) {
|
||||
janetc_cerror(c, "expected symbol following '& in destructuring pattern");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (i + 2 < len) {
|
||||
int32_t num_extra = len - i - 1;
|
||||
Janet* extra = janet_tuple_begin(num_extra);
|
||||
janet_tuple_flag(extra) |= JANET_TUPLE_FLAG_BRACKETCTOR;
|
||||
|
||||
for (int32_t j = 0; j < num_extra; ++j) {
|
||||
extra[j] = values[j + i + 1];
|
||||
}
|
||||
|
||||
janetc_error(c, janet_formatc("expected a single symbol follow '& in destructuring pattern, found %q", janet_wrap_tuple(janet_tuple_end(extra))));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if (!janet_checktype(values[i + 1], JANET_SYMBOL)) {
|
||||
janetc_error(c, janet_formatc("expected symbol following '& in destructuring pattern, found %q", values[i + 1]));
|
||||
return 1;
|
||||
}
|
||||
|
||||
JanetSlot argi = janetc_farslot(c);
|
||||
JanetSlot arg = janetc_farslot(c);
|
||||
JanetSlot len = janetc_farslot(c);
|
||||
|
||||
janetc_emit_si(c, JOP_LOAD_INTEGER, argi, i, 0);
|
||||
janetc_emit_ss(c, JOP_LENGTH, len, right, 0);
|
||||
|
||||
// loop condition
|
||||
// reuse arg slot for the condition result
|
||||
int32_t label_loop_start = janetc_emit_sss(c, JOP_EQUALS, arg, argi, len, 0);
|
||||
int32_t label_loop_cond_jump = janetc_emit_si(c, JOP_JUMP_IF, arg, 0, 0);
|
||||
|
||||
// loop body
|
||||
janetc_emit_sss(c, JOP_GET, arg, right, argi, 0);
|
||||
janetc_emit_s(c, JOP_PUSH, arg, 0);
|
||||
janetc_emit_ssi(c, JOP_ADD_IMMEDIATE, argi, argi, 1, 0);
|
||||
|
||||
// loop
|
||||
// jump back to the start of the loop
|
||||
int32_t label_loop_loop = janet_v_count(c->buffer);
|
||||
janetc_emit(c, JOP_JUMP);
|
||||
int32_t label_loop_exit = janet_v_count(c->buffer);
|
||||
|
||||
c->buffer[label_loop_cond_jump] |= (label_loop_exit - label_loop_cond_jump) << 16;
|
||||
c->buffer[label_loop_loop] |= (label_loop_start - label_loop_loop) << 8;
|
||||
|
||||
janetc_freeslot(c, argi);
|
||||
janetc_freeslot(c, arg);
|
||||
janetc_freeslot(c, len);
|
||||
|
||||
janetc_emit_s(c, JOP_MAKE_TUPLE, nextright, 1);
|
||||
|
||||
leaf(c, janet_unwrap_symbol(values[i + 1]), nextright, attr);
|
||||
janetc_freeslot(c, nextright);
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < 0x100) {
|
||||
janetc_emit_ssu(c, JOP_GET_INDEX, nextright, right, (uint8_t) i, 1);
|
||||
} else {
|
||||
|
@ -137,6 +137,39 @@
|
||||
(assert (= a 1) "dictionary destructuring 3")
|
||||
(assert (= b 2) "dictionary destructuring 4")
|
||||
(assert (= c 4) "dictionary destructuring 5 - expression as key"))
|
||||
(let [test-tuple [:a :b 1 2]]
|
||||
(def [a b one two] test-tuple)
|
||||
(assert (= a :a) "tuple destructuring 1")
|
||||
(assert (= b :b) "tuple destructuring 2")
|
||||
(assert (= two 2) "tuple destructuring 3"))
|
||||
(let [test-tuple [:a :b 1 2]]
|
||||
(def [a & rest] test-tuple)
|
||||
(assert (= a :a) "tuple destructuring 4 - rest")
|
||||
(assert (= rest [:b 1 2]) "tuple destructuring 5 - rest"))
|
||||
(do
|
||||
(def [a b & rest] [:a :b nil :d])
|
||||
(assert (= a :a) "tuple destructuring 6 - rest")
|
||||
(assert (= b :b) "tuple destructuring 7 - rest")
|
||||
(assert (= rest [nil :d]) "tuple destructuring 8 - rest"))
|
||||
(do
|
||||
(def [[a b] x & rest] [[1 2] :a :c :b :a])
|
||||
(assert (= a 1) "tuple destructuring 9 - rest")
|
||||
(assert (= b 2) "tuple destructuring 10 - rest")
|
||||
(assert (= x :a) "tuple destructuring 11 - rest")
|
||||
(assert (= rest [:c :b :a]) "tuple destructuring 12 - rest"))
|
||||
(do
|
||||
(def [a b & rest] [:a :b])
|
||||
(assert (= a :a) "tuple destructuring 13 - rest")
|
||||
(assert (= b :b) "tuple destructuring 14 - rest")
|
||||
(assert (= rest []) "tuple destructuring 15 - rest"))
|
||||
|
||||
(do
|
||||
(def [[a b & r1] c & r2] [[:a :b 1 2] :c 3 4])
|
||||
(assert (= a :a) "tuple destructuring 16 - rest")
|
||||
(assert (= b :b) "tuple destructuring 17 - rest")
|
||||
(assert (= c :c) "tuple destructuring 18 - rest")
|
||||
(assert (= r1 [1 2]) "tuple destructuring 19 - rest")
|
||||
(assert (= r2 [3 4]) "tuple destructuring 20 - rest"))
|
||||
|
||||
# Marshal
|
||||
|
||||
|
@ -106,6 +106,10 @@
|
||||
(assert (= nil (match {:a :hi} {:a a :b b} a)) "match 3")
|
||||
(assert (= nil (match [1 2] [a b c] a)) "match 4")
|
||||
(assert (= 2 (match [1 2] [a b] b)) "match 5")
|
||||
(assert (= [2 :a :b] (match [1 2 :a :b] [o & rest] rest)) "match 6")
|
||||
(assert (= [] (match @[:a] @[x & r] r :fallback)) "match 7")
|
||||
(assert (= :fallback (match @[1] @[x y & r] r :fallback)) "match 8")
|
||||
(assert (= [1 2 3 4] (match @[1 2 3 4] @[x y z & r] [x y z ;r] :fallback)) "match 9")
|
||||
|
||||
# And/or checks
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user