Browse Source

Sol Part 53: You Should Get Yourself Tested!

Graham Northup 5 years ago
parent
commit
5fe1f453a7
Signed by: Grissess <grissess@nexusg.org> GPG Key ID: 5D000E6F539376FB
14 changed files with 250 additions and 5 deletions
  1. 40
    0
      builtins.c
  2. 21
    0
      object.c
  3. 53
    0
      sol.h
  4. 1
    0
      state.c
  5. 11
    5
      tests/_lib.sol
  6. 38
    0
      tests/basic_control.sol
  7. 9
    0
      tests/basic_equality.sol
  8. 7
    0
      tests/basic_len.sol
  9. 10
    0
      tests/basic_vars.sol
  10. 5
    0
      tests/bt_range.sol
  11. 0
    0
      tests/fails.sol.disabled
  12. 4
    0
      tests/lang_forex.sol
  13. 4
    0
      tests/lang_ifex.sol
  14. 47
    0
      tests/lang_scoping.sol

+ 40
- 0
builtins.c View File

@@ -1085,6 +1085,46 @@ sol_object_t *sol_f_list_mul(sol_state_t *state, sol_object_t *args) {
1085 1085
 	return ls;
1086 1086
 }
1087 1087
 
1088
+sol_object_t *sol_f_list_cmp(sol_state_t *state, sol_object_t *args) {
1089
+	sol_object_t *a = sol_list_get_index(state, args, 0), *b = sol_list_get_index(state, args, 1), *item, *ls, *tmp;
1090
+	int i, alen, blen;
1091
+	if(!sol_is_list(b)) {
1092
+		sol_obj_free(b);
1093
+		sol_obj_free(a);
1094
+		return sol_new_int(state, 1);  // XXX lists always greater than non-lists?
1095
+	}
1096
+	alen = sol_list_len(state, a);
1097
+	blen = sol_list_len(state, b);
1098
+	if(alen != blen) {
1099
+		sol_obj_free(b);
1100
+		sol_obj_free(a);
1101
+		return sol_new_int(state, alen > blen ? 1 : -1);
1102
+	}
1103
+	ls = sol_new_list(state);
1104
+	sol_list_insert(state, ls, 0, state->None);
1105
+	sol_list_insert(state, ls, 1, state->None);
1106
+	for(i = 0; i < alen; i++) {
1107
+		tmp = sol_list_get_index(state, a, i);
1108
+		sol_list_set_index(state, ls, 0, tmp);
1109
+		item = sol_list_get_index(state, b, i);
1110
+		sol_list_set_index(state, ls, 1, item);
1111
+		sol_obj_free(item);
1112
+		item = CALL_METHOD(state, tmp, cmp, ls);
1113
+		sol_obj_free(tmp);
1114
+		if(item->ival != 0) {
1115
+			sol_obj_free(ls);
1116
+			sol_obj_free(b);
1117
+			sol_obj_free(a);
1118
+			return item;
1119
+		}
1120
+		sol_obj_free(item);
1121
+	}
1122
+	sol_obj_free(ls);
1123
+	sol_obj_free(b);
1124
+	sol_obj_free(a);
1125
+	return sol_new_int(state, 0);
1126
+}
1127
+
1088 1128
 sol_object_t *sol_f_list_index(sol_state_t *state, sol_object_t *args) {
1089 1129
 	sol_object_t *ls = sol_list_get_index(state, args, 0), *b = sol_list_get_index(state, args, 1), *ival;
1090 1130
 	sol_object_t *res, *funcs;

+ 21
- 0
object.c View File

@@ -308,6 +308,14 @@ int sol_map_len(sol_state_t *state, sol_object_t *map) {
308 308
 	return dsl_seq_len(map->seq);
309 309
 }
310 310
 
311
+sol_object_t *sol_map_mcell_index(sol_state_t *state, sol_object_t *map, int index) {
312
+	sol_object_t *res = dsl_seq_get(map->seq, index);
313
+	if(res) {
314
+		return sol_incref(res);
315
+	}
316
+	return sol_incref(state->None);
317
+}
318
+
311 319
 sol_object_t *sol_map_mcell(sol_state_t *state, sol_object_t *map, sol_object_t *key) {
312 320
 	sol_object_t *list, *cmp, *icmp, *res = NULL;
313 321
 	dsl_seq_iter *iter;
@@ -375,6 +383,19 @@ sol_object_t *sol_map_get_name(sol_state_t *state, sol_object_t *map, char *name
375 383
 
376 384
 void sol_map_set(sol_state_t *state, sol_object_t *map, sol_object_t *key, sol_object_t *val) {
377 385
 	sol_object_t *mcell = sol_map_mcell(state, map, key), *newcell, *temp;
386
+	if(sol_is_none(state, val)) {
387
+		if(!sol_is_none(state, mcell)) {
388
+			// XXX hacky
389
+			dsl_seq_iter *iter = dsl_new_seq_iter(map->seq);
390
+			while(!dsl_seq_iter_is_invalid(iter)) {
391
+				if(mcell == dsl_seq_iter_at(iter)) {
392
+					dsl_seq_iter_delete_at(iter);
393
+					break;
394
+				}
395
+			}
396
+		}
397
+		return;
398
+	} 
378 399
 	if(sol_is_none(state, mcell)) {
379 400
 		newcell = sol_alloc_object(state);
380 401
 		newcell->type = SOL_MCELL;

+ 53
- 0
sol.h View File

@@ -765,6 +765,7 @@ sol_object_t *sol_f_str_find(sol_state_t *, sol_object_t *);
765 765
 
766 766
 sol_object_t *sol_f_list_add(sol_state_t *, sol_object_t *);
767 767
 sol_object_t *sol_f_list_mul(sol_state_t *, sol_object_t *);
768
+sol_object_t *sol_f_list_cmp(sol_state_t *, sol_object_t *);
768 769
 sol_object_t *sol_f_list_index(sol_state_t *, sol_object_t *);
769 770
 sol_object_t *sol_f_list_setindex(sol_state_t *, sol_object_t *);
770 771
 sol_object_t *sol_f_list_len(sol_state_t *, sol_object_t *);
@@ -931,28 +932,80 @@ void sol_list_append(sol_state_t *, sol_object_t *, sol_object_t *);
931 932
  *   list. */
932 933
 #define sol_list_pop(st, ls) sol_list_remove(st, ls, 0);
933 934
 
935
+/** Creates a new empty Sol map. */
934 936
 sol_object_t *sol_new_map(sol_state_t *);
937
+/** Internal routine to get the length (number of associations) in a Sol map. */
935 938
 int sol_map_len(sol_state_t *, sol_object_t *);
939
+/** Internal routine to get an MCELL by index.
940
+ *
941
+ * This is most typically used to iterate over the associations in a map in an
942
+ * arbitrary order.
943
+ */
944
+sol_object_t *sol_map_mcell_index(sol_state_t *, sol_object_t *, int);
945
+/** Internal routine to get an MCELL with key equal to `key`, or `None`.
946
+ *
947
+ * This does most of the work of association lookup; many other functions in
948
+ * the map internal API are built upon this one.
949
+ */
936 950
 sol_object_t *sol_map_mcell(sol_state_t *, sol_object_t *, sol_object_t *);
951
+/** Internal routine to determine if a key is in a map. */
937 952
 int sol_map_has(sol_state_t *, sol_object_t *, sol_object_t *);
953
+/** Internal routine to get the value associated with a key in a map, or `None`
954
+ *   if there is no association.. */
938 955
 sol_object_t *sol_map_get(sol_state_t *, sol_object_t *, sol_object_t *);
956
+/** Internal routine to get the value associated with a string key (specified
957
+ *   as a C string) in a map, or `None` if there is no association. */
939 958
 sol_object_t *sol_map_get_name(sol_state_t *, sol_object_t *, char *);
959
+/** Internal routine to set an association in a map.
960
+ *
961
+ * If the key had a previous association, it is lost. If the value is `None`,
962
+ * any existing association is deleted; this is consistent with a return of
963
+ * `None` for any map get for which no association exists.
964
+ */
940 965
 void sol_map_set(sol_state_t *, sol_object_t *, sol_object_t *, sol_object_t *);
966
+/** Internal routine to set an association, borrowing a reference to the value
967
+ *   instead of owning a reference.
968
+ *
969
+ * This is mostly used in `sol_state_init`, where it avoids having to
970
+ * `sol_obj_free` after creating a new object just to associate it.
971
+ *
972
+ * Note that this actually decrements the reference count on your pointer--the
973
+ * map still increments the reference count, but this effect is nullified, so
974
+ * it works out the same way.
975
+ */
941 976
 #define sol_map_borrow(state, map, key, object) do {\
942 977
 	sol_object_t *__obj = (object);\
943 978
 	sol_map_set((state), (map), (key), __obj);\
944 979
 	sol_obj_free(__obj);\
945 980
 } while(0)
981
+/** Internal routine to set a map association with a C-string key. */
946 982
 void sol_map_set_name(sol_state_t *, sol_object_t *, char *, sol_object_t *);
983
+/** Internal routine to set a map association with a C-string key, and
984
+ *   borrowing a reference to the value. */
947 985
 #define sol_map_borrow_name(state, map, str, object) do {\
948 986
 	sol_object_t *__obj = (object);\
949 987
 	sol_map_set_name((state), (map), (str), __obj);\
950 988
 	sol_obj_free(__obj);\
951 989
 } while(0)
990
+/** Internal routine to set a map associaiton to a new value only if the key
991
+ *   was associated with a value (other than `None`) previously.
992
+ *
993
+ * This is mostly used in the end of `sol_f_func_call` to update the closure.
994
+ */
952 995
 void sol_map_set_existing(sol_state_t *, sol_object_t *, sol_object_t *, sol_object_t *);
996
+/** Creates a new copy of an existing Sol map. */
953 997
 sol_object_t *sol_map_copy(sol_state_t *, sol_object_t *);
998
+/** Merges the associations of the source map into the destination map.
999
+ *
1000
+ * Associations in the source map take precedence if the same key exists in
1001
+ * both.
1002
+ */
954 1003
 void sol_map_merge(sol_state_t *, sol_object_t *, sol_object_t *);
1004
+/** Merges the associations of the source map into the destination map, but
1005
+ *   only for keys already in the destination map. */
955 1006
 void sol_map_merge_existing(sol_state_t *, sol_object_t *, sol_object_t *);
1007
+/** Updates a map to contain an association from value to key for every key and
1008
+ *   value already within. */
956 1009
 void sol_map_invert(sol_state_t *, sol_object_t *);
957 1010
 
958 1011
 // Defined in ast.h

+ 1
- 0
state.c View File

@@ -126,6 +126,7 @@ int sol_state_init(sol_state_t *state) {
126 126
 	state->ListOps.tname = "list";
127 127
 	state->ListOps.add = sol_f_list_add;
128 128
 	state->ListOps.mul = sol_f_list_mul;
129
+	state->ListOps.cmp = sol_f_list_cmp;
129 130
 	state->ListOps.call = sol_f_not_impl;
130 131
 	state->ListOps.index = sol_f_list_index;
131 132
 	state->ListOps.setindex = sol_f_list_setindex;

+ 11
- 5
tests/_lib.sol View File

@@ -1,19 +1,27 @@
1 1
 func assert(x, msg)
2
+	_test_count += 1
3
+	io.stdout:write('Test ' + tostring(_test_count) + ': ' + msg)
2 4
 	if !x then
3 5
 		error("Assertion failed: " + tostring(msg))
4 6
 	end
7
+	print("...passed")
5 8
 end
9
+assert.closure._test_count = 0
6 10
 
7 11
 func assert_eq(x, y, msg)
8
-	assert(x == y, msg)
12
+	assert(x == y, "equality: " + tostring(x) + " == " + tostring(y) + ": " + msg)
13
+end
14
+
15
+func assert_neq(x, y, msg)
16
+	assert(x != y, "inequality: " + tostring(x) + " != " + tostring(y) + ": " + msg)
9 17
 end
10 18
 
11 19
 func assert_none(x, msg)
12
-	assert_eq(None, x, msg)
20
+	assert(None == x, "None: " + tostring(x) + ": " + msg)
13 21
 end
14 22
 
15 23
 func assert_not_none(x, msg)
16
-	assert(None != x, msg)
24
+	assert(None != x, "Not None: " + tostring(x) + ": " + msg)
17 25
 end
18 26
 
19 27
 func warn(msg)
@@ -25,5 +33,3 @@ func warn_if(cond, msg)
25 33
 		warn(msg)
26 34
 	end
27 35
 end
28
-
29
-print("(test library loaded successfully)")

+ 38
- 0
tests/basic_control.sol View File

@@ -0,0 +1,38 @@
1
+execfile("tests/_lib.sol")
2
+
3
+if 0 then
4
+	error("Iftrue on a false constant")
5
+else
6
+	print("Iffalse on a false contant (pass)")
7
+end
8
+
9
+if 1 then
10
+	print("iftrue on a true constant (pass)")
11
+else
12
+	error("iffalse on a true constant")
13
+end
14
+
15
+iters = 0
16
+for i in range(5) do iters += 1 end
17
+assert_eq(iters, 5, "for over range")
18
+
19
+func count(start, stop, step)
20
+	if None == step then step = 1 end
21
+	func inner()
22
+		if i >= stop then return StopIteration end
23
+		i += step
24
+		return i - 1
25
+	end
26
+	inner.closure.stop = stop
27
+	inner.closure.i = start
28
+	inner.closure.step = step
29
+	return inner
30
+end
31
+
32
+iters = 0
33
+for i in count(0, 10) do iters += 1 end
34
+assert_eq(iters, 10, "for over iterator function")
35
+
36
+iters = 0
37
+for i in count(0, 10, 2) do iters += 1 end
38
+assert_eq(iters, 5, "for over iterator function")

+ 9
- 0
tests/basic_equality.sol View File

@@ -0,0 +1,9 @@
1
+execfile("tests/_lib.sol")
2
+
3
+assert_eq(1, 1, "int cmp")
4
+assert_eq(-3695, -3695, "int cmp")
5
+assert_eq([1, 2, 3], [1, 2, 3], "list cmp")
6
+assert_neq([1, 2, 3], [4, 5, 6], "list cmp (inverse same len)")
7
+assert_neq([1, 2, 3], [], "list cmp (with empty)")
8
+assert_neq([1, 2, 3], [1, 2], "list cmp (with prefix)")
9
+assert_neq([1, 2, 3], [1, 2, 3, 4], "list cmp (as prefix)")

+ 7
- 0
tests/basic_len.sol View File

@@ -0,0 +1,7 @@
1
+execfile("tests/_lib.sol")
2
+
3
+assert_eq(0, #[], "empty list")
4
+assert_eq(0, #{}, "empty map")
5
+
6
+assert_eq(2, #[1, 2], "list len")
7
+assert_eq(3, #{a=1, b=2, c=[3, 4, 5]}, "map len")

+ 10
- 0
tests/basic_vars.sol View File

@@ -0,0 +1,10 @@
1
+execfile("tests/_lib.sol")
2
+
3
+a = 3
4
+assert_eq(a, a, "a (reflexive)")
5
+assert_eq(3, a, "a")
6
+assert_eq(a, 3, "a")
7
+
8
+l = [0 9 3 1]
9
+assert_eq(l, l, "l (reflexive)")
10
+assert_eq([0, 9, 3, 1], l, "l")

+ 5
- 0
tests/bt_range.sol View File

@@ -0,0 +1,5 @@
1
+execfile("tests/_lib.sol")
2
+
3
+assert_eq([0, 1, 2, 3, 4], range(5), "range 5")
4
+assert_eq(50, #range(50), "len range 50")
5
+assert_eq(0, #range(0), "Empty range")

tests/fails.sol → tests/fails.sol.disabled View File


+ 4
- 0
tests/lang_forex.sol View File

@@ -0,0 +1,4 @@
1
+execfile("tests/_lib.sol")
2
+
3
+assert_eq(5, for i in range(10) do if i == 5 then break i end end, "break with value")
4
+assert_eq([0, 2, 4, 6, 8], for i in range(5) do continue 2 * i end, "continue with value")

+ 4
- 0
tests/lang_ifex.sol View File

@@ -0,0 +1,4 @@
1
+execfile("tests/_lib.sol")
2
+
3
+assert_eq("true", if 1 then "true" else "false" end, "iftrue")
4
+assert_eq("false", if 0 then "true" else "false" end, "iffalse")

+ 47
- 0
tests/lang_scoping.sol View File

@@ -0,0 +1,47 @@
1
+execfile("tests/_lib.sol")
2
+
3
+_G = debug.globals()
4
+_G.a = 10
5
+assert_eq(a, 10, "debug.globals write")
6
+
7
+b = 7
8
+assert_eq(_G.b, 7, "debug.globals read")
9
+
10
+func()
11
+	assert_eq(a, 10, "scope inheritance (a)")
12
+	assert_eq(b, 7, "scope inheritance (b)")
13
+
14
+	_L = debug.locals()
15
+	_L.a = 17
16
+	assert_eq(a, 17, "debug.locals write")
17
+
18
+	b = 3
19
+	assert_eq(_L.b, 3, "debug.locals read")
20
+
21
+	assert_eq(#debug.scopes(), 2, "debug scope count")
22
+end()
23
+
24
+assert_eq(a, 10, "scope leak (direct)")
25
+assert_eq(b, 7, "scope leak (direct)")
26
+assert_eq(_G.a, 10, "scope leak (indirect)")
27
+assert_eq(_G.b, 7, "scope leak (indirect)")
28
+
29
+func()
30
+	_LG = debug.globals()
31
+	_LG.a = 13
32
+	_LG.b = 14
33
+	assert_eq(a, 13, "scope inheritance after outer write")
34
+	assert_eq(b, 14, "scope inheritance after outer write")
35
+
36
+	a = 9
37
+	b = 7
38
+	assert_eq(a, 9, "local write")
39
+	assert_eq(b, 7, "local write")
40
+end()
41
+
42
+assert_eq(a, 13, "outer write persistence")
43
+assert_eq(b, 14, "outer write persistence")
44
+
45
+
46
+-- FIXME: Attempting to repr these scopes causes an inf recursion
47
+assert(debug.locals() == debug.globals(), "root scope locals == globals")

Loading…
Cancel
Save