From 9528bb145193159fa3e697da642e32a0877af5fb Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Tue, 1 Apr 2014 22:50:55 -0500 Subject: All: pass stepA tests, in particular with correct conj behavior. --- bash/types.sh | 22 +++++--- c/types.c | 102 ++++++++++++++++++++++++++++---------- docs/step_notes.txt | 12 +++-- java/src/main/java/mal/types.java | 19 +++++-- js/types.js | 15 ++++-- make/types.mk | 40 ++++++++++----- php/types.php | 13 ++--- python/mal_types.py | 5 +- tests/stepA_more.mal | 28 ++++++++--- 9 files changed, 184 insertions(+), 72 deletions(-) diff --git a/bash/types.sh b/bash/types.sh index e678321..33278da 100644 --- a/bash/types.sh +++ b/bash/types.sh @@ -128,7 +128,7 @@ true? () { _true? "${1}" && r="${__true}" || r="${__false}"; } true_pr_str () { r="true"; } _false? () { [[ ${1} =~ ^fals_ ]]; } -false? () { _false? "${1}" && r="${__false}" || r="${__false}"; } +false? () { _false? "${1}" && r="${__true}" || r="${__false}"; } false_pr_str () { r="false"; } @@ -516,10 +516,18 @@ conj () { local obj="${1}"; shift local obj_data="${ANON["${obj}"]}" __new_obj_like "${obj}" - ANON["${r}"]="${obj_data:+${obj_data} }${*}" + if _list? "${obj}"; then + ANON["${r}"]="${obj_data:+${obj_data}}" + for elem in ${@}; do + ANON["${r}"]="${elem} ${ANON["${r}"]}" + done + + else + ANON["${r}"]="${obj_data:+${obj_data} }${*}" + fi } -# conj that mutates in place +# conj that mutates in place (and always appends) conj! () { local obj="${1}"; shift local obj_data="${ANON["${obj}"]}" @@ -541,6 +549,7 @@ count () { first () { local temp="${ANON["${1}"]}" r="${temp%% *}" + [ "${r}" ] || r="${__nil}" } last () { @@ -559,7 +568,7 @@ _slice () { # element rest () { local temp="${ANON["${1}"]}" - __new_obj_like "${1}" + __new_obj list if [[ "${temp#* }" == "${temp}" ]]; then ANON["${r}"]= else @@ -568,9 +577,8 @@ rest () { } apply () { - local f="${ANON["${1}"]}" - local args="${2}" - local items="${ANON["${2}"]}" + local f="${ANON["${1}"]}"; shift + local items="${@:1:$(( ${#@} -1 ))} ${ANON["${!#}"]}" eval ${f%%@*} ${items} } diff --git a/c/types.c b/c/types.c index 1308aac..a6bfbf6 100644 --- a/c/types.c +++ b/c/types.c @@ -556,44 +556,62 @@ MalVal *_hash_map(int count, ...) { return hm; } -MalVal *hash_map(MalVal *args) { - assert_type(args, MAL_LIST|MAL_VECTOR, - "hash-map called with non-sequential arguments"); - assert((args->val.array->len % 2) == 0, - "odd number of parameters to hash-map"); - GHashTable *htable = g_hash_table_new(g_str_hash, g_str_equal); - MalVal *hm = malval_new_hash_map(htable); +MalVal *_assoc_BANG(MalVal* hm, MalVal *args) { + assert((_count(args) % 2) == 0, + "odd number of parameters to assoc!"); + GHashTable *htable = hm->val.hash_table; int i; MalVal *k, *v; - for(i=0; i< args->val.array->len; i+=2) { + for (i=0; i<_count(args); i+=2) { k = g_array_index(args->val.array, MalVal*, i); assert_type(k, MAL_STRING, - "hash-map called with non-string key"); + "assoc! called with non-string key"); v = g_array_index(args->val.array, MalVal*, i+1); g_hash_table_insert(htable, k->val.string, v); } return hm; } +MalVal *_dissoc_BANG(MalVal* hm, MalVal *args) { + GHashTable *htable = hm->val.hash_table; + int i; + MalVal *k, *v; + for (i=0; i<_count(args); i++) { + k = g_array_index(args->val.array, MalVal*, i); + assert_type(k, MAL_STRING, + "dissoc! called with non-string key"); + g_hash_table_remove(htable, k->val.string); + } + return hm; +} + +MalVal *hash_map(MalVal *args) { + assert_type(args, MAL_LIST|MAL_VECTOR, + "hash-map called with non-sequential arguments"); + GHashTable *htable = g_hash_table_new(g_str_hash, g_str_equal); + MalVal *hm = malval_new_hash_map(htable); + return _assoc_BANG(hm, args); +} + int _hash_map_Q(MalVal *seq) { return seq->type & MAL_HASH_MAP; } MalVal *hash_map_Q(MalVal *seq) { return _hash_map_Q(seq) ? &mal_true : &mal_false; } -// TODO: support multiple key/values -MalVal *assoc(MalVal *hm, MalVal *key, MalVal *val) { - GHashTable *htable = g_hash_table_copy(hm->val.hash_table); - MalVal *new_hm = malval_new_hash_map(htable); - g_hash_table_insert(htable, key->val.string, val); - return new_hm; +MalVal *assoc(MalVal *args) { + assert_type(args, MAL_LIST|MAL_VECTOR, + "assoc called with non-sequential arguments"); + assert(_count(args) >= 2, + "assoc needs at least 2 arguments"); + GHashTable *htable = g_hash_table_copy(_nth(args,0)->val.hash_table); + MalVal *hm = malval_new_hash_map(htable); + return _assoc_BANG(hm, rest(args)); } -// TODO: support multiple keys -MalVal *dissoc(MalVal *hm, MalVal *key) { - GHashTable *htable = g_hash_table_copy(hm->val.hash_table); - MalVal *new_hm = malval_new_hash_map(htable); - g_hash_table_remove(htable, key->val.string); - return new_hm; +MalVal *dissoc(MalVal* args) { + GHashTable *htable = g_hash_table_copy(_nth(args,0)->val.hash_table); + MalVal *hm = malval_new_hash_map(htable); + return _dissoc_BANG(hm, rest(args)); } MalVal *keys(MalVal *obj) { @@ -849,10 +867,19 @@ MalVal *sconj(MalVal *args) { int i, len = _count(src_lst) + _count(args) - 1; GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), len); - for (i=1; ival.array, MalVal*, i)); + // Copy in src_lst + for (i=0; i<_count(src_lst); i++) { + g_array_append_val(new_arr, g_array_index(src_lst->val.array, MalVal*, i)); } - return malval_new_list(MAL_LIST, new_arr); + // Conj extra args + for (i=1; i<_count(args); i++) { + if (src_lst->type & MAL_LIST) { + g_array_prepend_val(new_arr, g_array_index(args->val.array, MalVal*, i)); + } else { + g_array_append_val(new_arr, g_array_index(args->val.array, MalVal*, i)); + } + } + return malval_new_list(src_lst->type, new_arr); } MalVal *first(MalVal *seq) { @@ -889,6 +916,27 @@ MalVal *nth(MalVal *seq, MalVal *idx) { return _nth(seq, idx->val.intnum); } +MalVal *sapply(MalVal *args) { + assert_type(args, MAL_LIST|MAL_VECTOR, + "apply called with non-sequential"); + MalVal *f = _nth(args, 0); + MalVal *last_arg = _nth(args, _count(args)-1); + assert_type(last_arg, MAL_LIST|MAL_VECTOR, + "last argument to apply is non-sequential"); + int i, len = _count(args) - 2 + _count(last_arg); + GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), + len); + // Initial arguments + for (i=1; i<_count(args)-1; i++) { + g_array_append_val(new_arr, g_array_index(args->val.array, MalVal*, i)); + } + // Add arguments from last_arg + for (i=0; i<_count(last_arg); i++) { + g_array_append_val(new_arr, g_array_index(last_arg->val.array, MalVal*, i)); + } + return apply(f, malval_new_list(MAL_LIST, new_arr)); +} + MalVal *_map2(MalVal *(*func)(void*, void*), MalVal *lst, void *arg2) { MalVal *e, *el; assert_type(lst, MAL_LIST|MAL_VECTOR, @@ -1007,8 +1055,8 @@ types_ns_entry types_ns[49] = { {"<=", (void*(*)(void*))int_lte, 2}, {"hash-map", (void*(*)(void*))hash_map, -1}, {"map?", (void*(*)(void*))hash_map_Q, 1}, - {"assoc", (void*(*)(void*))assoc, 3}, - {"dissoc", (void*(*)(void*))dissoc, 2}, + {"assoc", (void*(*)(void*))assoc, -1}, + {"dissoc", (void*(*)(void*))dissoc, -1}, {"get", (void*(*)(void*))get, 2}, {"contains?", (void*(*)(void*))contains_Q, 2}, {"keys", (void*(*)(void*))keys, 1}, @@ -1033,6 +1081,6 @@ types_ns_entry types_ns[49] = { {"last", (void*(*)(void*))last, 1}, {"rest", (void*(*)(void*))rest, 1}, {"nth", (void*(*)(void*))nth, 2}, - {"apply", (void*(*)(void*))apply, 2}, + {"apply", (void*(*)(void*))sapply, -1}, {"map", (void*(*)(void*))map, 2}, }; diff --git a/docs/step_notes.txt b/docs/step_notes.txt index 63e7a76..b5b1c6f 100644 --- a/docs/step_notes.txt +++ b/docs/step_notes.txt @@ -168,12 +168,18 @@ Step Notes: - throw function - apply, map functions: should not directly call EVAL, which requires the function object to be runnable - - symbol?, nil?, true?, false?, sequential? (if not already) - - conj, first, rest - EVAL: - try*/catch*: for normal exceptions, extracts string otherwise extracts full value - - define cond and or macros using rep() + +- Extra defintions needed for self-hosting + - types module: + - symbol?, nil?, true?, false?, sequential? (if not already) + - first, rest + - define cond and or macros using REP/RE + +- Other misc: + - conj function - atoms - reader module: diff --git a/java/src/main/java/mal/types.java b/java/src/main/java/mal/types.java index 1e9bb34..8a4910b 100644 --- a/java/src/main/java/mal/types.java +++ b/java/src/main/java/mal/types.java @@ -716,12 +716,21 @@ public class types { static MalFunction conj = new MalFunction() { public MalVal apply(MalList a) throws MalThrowable { - MalList lst = new MalList(); - lst.value.addAll(((MalList)a.nth(0)).value); - for(Integer i=1; igetArrayCopy(); - foreach ($args as $arg) { - $tmp[] = $arg; - } if (list_Q($src)) { + foreach ($args as $arg) { array_unshift($tmp, $arg); } $s = new ListClass(); } else { + foreach ($args as $arg) { $tmp[] = $arg; } $s = new VectorClass(); } $s->exchangeArray($tmp); @@ -368,8 +367,10 @@ function nth($seq, $idx) { return $seq[$idx]; } -function apply($f, $args) { - return $f->apply($args->getArrayCopy()); +function apply($f) { + $args = array_slice(func_get_args(), 1); + $last_arg = array_pop($args)->getArrayCopy(); + return $f->apply(array_merge($args, $last_arg)); } function map($f, $seq) { @@ -480,7 +481,7 @@ $types_ns = array( 'first'=> function ($a) { return first($a); }, 'rest'=> function ($a) { return rest($a); }, 'nth'=> function ($a, $b) { return nth($a, $b); }, - 'apply'=> function ($a, $b) { return apply($a, $b); }, + 'apply'=> function () { return call_user_func_array('apply', func_get_args()); }, 'map'=> function ($a, $b) { return map($a, $b); } ); diff --git a/python/mal_types.py b/python/mal_types.py index fa0a11e..401a03b 100644 --- a/python/mal_types.py +++ b/python/mal_types.py @@ -200,7 +200,10 @@ def concat(*lsts): return List(chain(*lsts)) # retains metadata def conj(lst, *args): - new_lst = List(lst + list(args)) + if list_Q(lst): + new_lst = List(list(reversed(list(args))) + lst) + else: + new_lst = Vector(lst + list(args)) if hasattr(lst, "__meta__"): new_lst.__meta__ = lst.__meta__ return new_lst diff --git a/tests/stepA_more.mal b/tests/stepA_more.mal index bae226d..31eb2bb 100644 --- a/tests/stepA_more.mal +++ b/tests/stepA_more.mal @@ -103,15 +103,24 @@ (conj (list) 1) ;=>(1) (conj (list 1) 2) -;=>(1 2) +;=>(2 1) (conj (list 2 3) 4) -;=>(2 3 4) +;=>(4 2 3) (conj (list 2 3) 4 5 6) -;=>(2 3 4 5 6) +;=>(6 5 4 2 3) (conj (list 1) (list 2 3)) -;=>(1 (2 3)) -(conj [1 2] [3 4] ) -;=>(1 2 [3 4]) +;=>((2 3) 1) + +(conj [] 1) +;=>[1] +(conj [1] 2) +;=>[1 2] +(conj [2 3] 4) +;=>[2 3 4] +(conj [2 3] 4 5 6) +;=>[2 3 4 5 6] +(conj [1] [2 3]) +;=>[1 [2 3]] ;; Testing first/rest functions (first '()) @@ -190,6 +199,9 @@ (vals hm2) ;=>(1) +(count (keys (assoc hm2 "b" 2 "c" 3))) +;=>3 + (def! hm3 (assoc hm2 "b" 2)) (count (keys hm3)) ;=>2 @@ -263,8 +275,8 @@ ;; ;; Testing read-str and eval -(read-string "[1 2 (3 4) nil]") -;=>[1 2 (3 4) nil] +(read-string "(1 2 (3 4) nil)") +;=>(1 2 (3 4) nil) (eval (read-string "(+ 4 5)")) ;=>9 -- cgit v1.2.3