A few more tests, now passes valgrind.
#include "builtin.h"
static void f_false(jv input[], jv output[]) {
+ jv_free(input[0]);
output[0] = jv_false();
}
static void f_true(jv input[], jv output[]) {
+ jv_free(input[0]);
output[0] = jv_true();
}
static void f_plus(jv input[], jv output[]) {
+ jv_free(input[0]);
jv a = input[2];
jv b = input[1];
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
output[0] = jv_array_concat(a, b);
} else {
output[0] = jv_string("wtf gaize");
+ jv_free(a);
+ jv_free(b);
}
}
// FIXME mem
int path_push(stackval sv, jv val) {
+ return 0;
int pos = sv.pathidx;
assert(pos <= pathsize);
assert(pos >= 0);
stackval sv;
} data_stk_elem;
-void stk_push(stackval val) {
+void stack_push(stackval val) {
data_stk_elem* s = forkable_stack_push(&data_stk, sizeof(data_stk_elem));
s->sv = val;
}
-stackval stk_pop() {
+stackval stack_pop() {
data_stk_elem* s = forkable_stack_peek(&data_stk);
stackval sv = s->sv;
- if (!forkable_stack_pop(&data_stk)) {
+ if (!forkable_stack_pop_will_free(&data_stk)) {
sv.value = jv_copy(sv.value);
}
+ forkable_stack_pop(&data_stk);
return sv;
}
}
void stack_restore(){
+ while (!forkable_stack_empty(&data_stk) &&
+ forkable_stack_pop_will_free(&data_stk)) {
+ jv_free(stack_pop().value);
+ }
+ while (!forkable_stack_empty(&frame_stk) &&
+ forkable_stack_pop_will_free(&frame_stk)) {
+ frame_pop(&frame_stk);
+ }
struct forkpoint* fork = forkable_stack_peek(&fork_stk);
forkable_stack_restore(&data_stk, &fork->saved_data_stack);
forkable_stack_restore(&frame_stk, &fork->saved_call_stack);
}
-#define stack_push stk_push
-#define stack_pop stk_pop
-
#define ON_BACKTRACK(op) ((op)+NUM_OPCODES)
jv jq_next() {
if (i == 0) {
param = forkable_stack_peek(&data_stk);
} else {
+ printf(" | ");
param = forkable_stack_peek_next(&data_stk, param);
}
+ if (!param) break;
jv_dump(jv_copy(param->sv.value));
- if (i < opdesc->stack_in-1) printf(" | ");
+ printf("<%d>", jv_get_refcnt(param->sv.value));
}
if (backtracking) {
int idx = jv_number_value(stack_pop().value);
stackval array = stack_pop();
if (idx >= jv_array_length(jv_copy(array.value))) {
+ jv_free(array.value);
goto do_backtrack;
} else {
stack_save();
do_backtrack:
case BACKTRACK: {
if (forkable_stack_empty(&fork_stk)) {
- // FIXME: invalid
+ // FIXME: invalid jv value
return jv_null();
}
stack_restore();
forkable_stack_init(&data_stk, sizeof(stackval) * 1000); // FIXME: lower this number, see if it breaks
forkable_stack_init(&frame_stk, 10240); // FIXME: lower this number, see if it breaks
forkable_stack_init(&fork_stk, 10240); // FIXME: lower this number, see if it breaks
+ stack_save();
stack_push(stackval_root(input));
struct closure top = {bc, -1};
frame_push_backtrack(&frame_stk, bc->code);
}
+void jq_teardown() {
+ while (!forkable_stack_empty(&fork_stk)) {
+ stack_restore();
+ }
+ assert(forkable_stack_empty(&fork_stk));
+ assert(forkable_stack_empty(&data_stk));
+ assert(forkable_stack_empty(&frame_stk));
+ forkable_stack_free(&fork_stk);
+ forkable_stack_free(&data_stk);
+ forkable_stack_free(&frame_stk);
+}
+
void run_program(struct bytecode* bc) {
char buf[4096];
fgets(buf, sizeof(buf), stdin);
forkable_stack_check(s);
}
+static void forkable_stack_free(struct forkable_stack* s) {
+ assert(forkable_stack_empty(s));
+ assert(s->savedlimit == s->length);
+ free(s->stk);
+ s->stk = 0;
+}
+
static void* forkable_stack_push(struct forkable_stack* s, size_t size) {
forkable_stack_check(s);
int curr = s->pos < s->savedlimit ? s->pos : s->savedlimit;
}
}
-// Returns 1 if the object being popped can not be accessed again
-// (i.e. was not saved with a fork)
-static int forkable_stack_pop(struct forkable_stack* s) {
+// Returns 1 if the next forkable_stack_pop will permanently remove an
+// object from the stack (i.e. the top object was not saved with a fork)
+static int forkable_stack_pop_will_free(struct forkable_stack* s) {
+ return s->pos < s->savedlimit ? 1 : 0;
+}
+
+static void forkable_stack_pop(struct forkable_stack* s) {
forkable_stack_check(s);
- int finalpop = s->pos < s->savedlimit ? 1 : 0;
struct forkable_stack_header* elem = forkable_stack_peek(s);
s->pos = elem->next;
- return finalpop;
}
int curr_limit = s->savedlimit;
if (curr_pos < curr_limit) s->savedlimit = curr_pos;
- state->prevlimit = curr_limit;
+ //state->prevlimit = curr_limit; FIXME clean up
forkable_stack_check(s);
}
static void forkable_stack_restore(struct forkable_stack* s, struct forkable_stack_state* state) {
forkable_stack_check(s);
+ assert(s->savedlimit <= state->prevpos);
+ assert(s->savedlimit <= state->prevlimit);
s->pos = state->prevpos;
s->savedlimit = state->prevlimit;
forkable_stack_check(s);
struct bytecode* bc;
stack_idx env;
uint16_t* retaddr;
+ // FIXME: probably not necessary as a separate field
+ int is_backtrack_frame;
};
typedef union frame_elem {
cc->bc = cl.bc;
cc->env = cl.env;
cc->retaddr = retaddr;
+ cc->is_backtrack_frame = 0;
for (int i=0; i<cl.bc->nlocals; i++) {
*frame_local_var(fp, i) = jv_null();
}
static frame_ptr frame_push_backtrack(struct forkable_stack* stk, uint16_t* retaddr) {
struct continuation cc = *frame_self(frame_current(stk));
frame_ptr fp = forkable_stack_push(stk, sizeof(union frame_elem) * 2);
+ assert(!cc.is_backtrack_frame);
+ cc.is_backtrack_frame = 1;
cc.retaddr = retaddr;
*frame_self(fp) = cc;
return fp;
}
static void frame_pop(struct forkable_stack* stk) {
+ frame_ptr fp = frame_current(stk);
+ if (forkable_stack_pop_will_free(stk) &&
+ !frame_self(fp)->is_backtrack_frame) {
+ int nlocals = frame_self(fp)->bc->nlocals;
+ for (int i=0; i<nlocals; i++) {
+ jv_free(*frame_local_var(fp, i));
+ }
+ }
forkable_stack_pop(stk);
}
#include <stddef.h>
#include <assert.h>
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include "jv.h"
return n;
}
+static int jvp_object_equal(jv_complex* o1, jv_complex* o2) {
+ int len2 = jvp_object_length(o2);
+ int len1 = 0;
+ for (int i=0; i<jvp_object_size(o1); i++) {
+ struct object_slot* slot = jvp_object_get_slot(o1, i);
+ if (!slot->string) continue;
+ jv* slot2 = jvp_object_read(o2, slot->string);
+ if (!slot2) return 0;
+ // FIXME: do less refcounting here
+ if (!jv_equal(jv_copy(slot->value), jv_copy(*slot2))) return 0;
+ len1++;
+ }
+ return len1 == len2;
+}
+
/*
* Objects (public interface)
*/
}
}
+int jv_get_refcnt(jv j) {
+ switch (jv_get_kind(j)) {
+ case JV_KIND_ARRAY:
+ case JV_KIND_STRING:
+ case JV_KIND_OBJECT:
+ return j.val.complex.ptr->count;
+ default:
+ return 1;
+ }
+}
+
/*
* Higher-level operations
*/
case JV_KIND_STRING: {
r = jvp_string_equal(&a.val.complex, &b.val.complex);
break;
- }
+ }
+ case JV_KIND_OBJECT: {
+ r = jvp_object_equal(&a.val.complex, &b.val.complex);
+ break;
+ }
default:
r = 1;
break;
jv jv_object_iter_value(jv, int);
+int jv_get_refcnt(jv);
+
void jv_dump(jv);
jv jv_parse(const char* string);
void jq_init(struct bytecode* bc, jv value);
jv jq_next();
+void jq_teardown();
void run_program(struct bytecode* bc);
pass = 0;
}*/
}
+ jq_teardown();
bytecode_free(bc);
tests++;
passed+=pass;
2
3
+1,1
+[]
+1
+1
+
+[.]
+[2]
+[[2]]
+
+[[2]]
+[3]
+[[2]]
+
+[{}]
+[2]
+[{}]
+
+[.[]]
+["a"]
+["a"]
+
[(.,1),((.,.[]),(2,3))]
["a","b"]
[["a","b"],1,["a","b"],"a","b",2,3]
1+1
null
-2.0
+2
.+4
15
1000
2000
-# test backtracking through function calls and returns
-# this test is *evil*
-[[20,10][1,0] as $x | def f: (100,200) as $y | def g: [$x + $y, .]; . + $x | g; f[0] | [f][0][1] | f]
-"woo, testing!"
-[[110.0, 130.0], [210.0, 130.0], [110.0, 230.0], [210.0, 230.0], [120.0, 160.0], [220.0, 160.0], [120.0, 260.0], [220.0, 260.0]]
+([1,2] + [4,5])
+[1,2,3]
+[1,2,4,5]
+
+true
+[1]
+true
+
+[1,2,3]
+[5,6]
+[1,2,3]
def f(x): x | x; f([.], . + [42])
[1,2,3]
def id(x):x; 2000 as $x | def f(x):1 as $x | id([$x, x, x]); def g(x): 100 as $x | f($x,$x+x); g($x)
"more testing"
[1,100,2100.0,100,2100.0]
+
+# test backtracking through function calls and returns
+# this test is *evil*
+[[20,10][1,0] as $x | def f: (100,200) as $y | def g: [$x + $y, .]; . + $x | g; f[0] | [f][0][1] | f]
+"woo, testing!"
+[[110.0, 130.0], [210.0, 130.0], [110.0, 230.0], [210.0, 230.0], [120.0, 160.0], [220.0, 160.0], [120.0, 260.0], [220.0, 260.0]]