printf("*Unknown*"); // NOTEST
}
- static struct value *val_alloc(struct parse_context *c, struct type *t,
- struct value *init)
- {
- struct value *ret;
-
- if (t->prepare_type)
- t->prepare_type(c, t, 0);
-
- ret = calloc(1, t->size);
- if (init)
- memcpy(ret, init, t->size);
- else
- val_init(t, ret);
- return ret;
- }
-
###### forward decls
static void free_value(struct type *type, struct value *v);
struct variable {
struct variable *previous;
struct type *type;
- struct value *val;
struct binding *name;
struct exec *where_decl;// where name was declared
struct exec *where_set; // where type was set
is found. Instead, they are detected and ignored when considering the
list of in_scope names.
+The storage of the value of a variable will be described later. For now
+we just need to know that when a variable goes out of scope, it might
+need to be freed. For this we need to be able to find it, so assume that
+`var_value()` will provide that.
+
###### variable fields
struct variable *merged;
}
}
+###### forward decls
+ static struct value *var_value(struct parse_context *c, struct variable *v);
+
###### free context vars
while (context.varlist) {
struct variable *t = v;
v = t->previous;
- free_value(t->type, t->val);
- free(t->val);
+ free_value(t->type, var_value(&context, t));
if (t->depth == 0)
// This is a global constant
free_exec(t->where_decl);
v->scope = InScope;
v->in_scope = c->in_scope;
c->in_scope = v;
- v->val = NULL;
return v;
}
}
}
+#### Storing Values
+
+The value of a variable is store separately from the variable, on an
+analogue of a stack frame. There are (currently) two frames that can be
+active. A global frame which currently only stores constants, and a
+stacked frame which stores local variables. Each variable knows if it
+is global or not, and what its index into the frame is.
+
+Values in the global frame are known immediately they are relevant, so
+the frame needs to be reallocated as it grows so it can store those
+values. The local frame doesn't get values until the interpreted phase
+is started, so there is no need to allocate until the size is known.
+
+###### variable fields
+ short frame_pos;
+ short global;
+
+###### parse context
+
+ short global_size, global_alloc;
+ short local_size;
+ void *global, *local;
+
+###### ast functions
+
+ static struct value *var_value(struct parse_context *c, struct variable *v)
+ {
+ if (!v->global) {
+ if (!c->local || !v->type)
+ return NULL;
+ if (v->frame_pos + v->type->size > c->local_size) {
+ printf("INVALID frame_pos\n"); // NOTEST
+ exit(2);
+ }
+ return c->local + v->frame_pos;
+ }
+ if (c->global_size > c->global_alloc) {
+ int old = c->global_alloc;
+ c->global_alloc = (c->global_size | 1023) + 1024;
+ c->global = realloc(c->global, c->global_alloc);
+ memset(c->global + old, 0, c->global_alloc - old);
+ }
+ return c->global + v->frame_pos;
+ }
+
+ static struct value *global_alloc(struct parse_context *c, struct type *t,
+ struct variable *v, struct value *init)
+ {
+ struct value *ret;
+ struct variable scratch;
+
+ if (t->prepare_type)
+ t->prepare_type(c, t, 1);
+
+ if (c->global_size & (t->align - 1))
+ c->global_size = (c->global_size + t->align) & ~(t->align-1);
+ if (!v) {
+ v = &scratch;
+ v->type = t;
+ }
+ v->frame_pos = c->global_size;
+ v->global = 1;
+ c->global_size += v->type->size;
+ ret = var_value(c, v);
+ if (init)
+ memcpy(ret, init, t->size);
+ else
+ val_init(t, ret);
+ return ret;
+ }
+
+As global values are found -- struct field initializers, labels etc --
+`global_alloc()` is called to record the value in the global frame.
+
+When the program is fully parsed, we need to walk the list of variables
+to find any that weren't merged away and that aren't global, and to
+calculate the frame size and assign a frame position for each variable.
+For this we have `scope_finalize()`.
+
+###### ast functions
+
+ static void scope_finalize(struct parse_context *c)
+ {
+ struct binding *b;
+
+ for (b = c->varlist; b; b = b->next) {
+ struct variable *v;
+ for (v = b->var; v; v = v->previous) {
+ struct type *t = v->type;
+ if (v->merged && v->merged != v)
+ continue;
+ if (v->global)
+ continue;
+ if (c->local_size & (t->align - 1))
+ c->local_size = (c->local_size + t->align) & ~(t->align-1);
+ v->frame_pos = c->local_size;
+ c->local_size += v->type->size;
+ }
+ }
+ c->local = calloc(1, c->local_size);
+ }
+
+###### free context vars
+ free(context.global);
+ free(context.local);
+
### Executables
Executables can be lots of different things. In many cases an
static void array_prepare_type(struct parse_context *c, struct type *type,
int parse_time)
{
+ struct value *vsize;
mpz_t q;
if (!type->array.vsize || type->array.static_size)
return;
+ vsize = var_value(c, type->array.vsize);
mpz_init(q);
- mpz_tdiv_q(q, mpq_numref(type->array.vsize->val->num),
- mpq_denref(type->array.vsize->val->num));
+ mpz_tdiv_q(q, mpq_numref(vsize->num), mpq_denref(vsize->num));
type->array.size = mpz_get_si(q);
mpz_clear(q);
if (t->structure.fields[i].init) {
free_value(t->structure.fields[i].type,
t->structure.fields[i].init);
- free(t->structure.fields[i].init);
}
free(t->structure.fields);
}
c->parse_error = 1;
else {
struct value vl = interp_exec(c, $5, NULL);
- $0->f.init = val_alloc(c, $0->f.type, &vl);
+ $0->f.init = global_alloc(c, $0->f.type, NULL, &vl);
}
} }$
| IDENTIFIER : Type ${
v->where_decl = $0;
v->where_set = $0;
v->type = $<Type;
- v->val = NULL;
} else {
v = var_ref(c, $1.txt);
$0->var = v;
v->where_decl = $0;
v->where_set = $0;
v->type = $<Type;
- v->val = NULL;
v->constant = 1;
} else {
v = var_ref(c, $1.txt);
/* This might be a label - allocate a var just in case */
v = var_decl(c, $1.txt);
if (v) {
- v->val = NULL;
v->type = Tnone;
v->where_decl = $0;
v->where_set = $0;
if (v->type == NULL) {
if (type && *ok != 0) {
v->type = type;
- v->val = NULL;
v->where_set = prog;
*ok = 2;
}
if (v->merged)
v = v->merged;
- lrv = v->val;
+ lrv = var_value(c, v);
rvtype = v->type;
break;
}
case Declare:
{
struct variable *v = cast(var, b->left)->var;
+ struct value *val;
if (v->merged)
v = v->merged;
- free_value(v->type, v->val);
- free(v->val);
+ val = var_value(c, v);
+ free_value(v->type, val);
+ if (v->type->prepare_type)
+ v->type->prepare_type(c, v->type, 0);
if (b->right) {
right = interp_exec(c, b->right, &rtype);
- v->val = val_alloc(c, v->type, &right);
+ memcpy(val, &right, rtype->size);
rtype = Tnone;
} else {
- v->val = val_alloc(c, v->type, NULL);
+ val_init(v->type, val);
}
break;
}
struct var *v = cast(var, $0->right);
if (v->var->type == Tnone) {
/* Convert this to a label */
+ struct value *val;
+
v->var->type = Tlabel;
- v->var->val = val_alloc(c, Tlabel, NULL);
- v->var->val->label = v->var->val;
+ val = global_alloc(c, Tlabel, v->var, NULL);
+ val->label = val;
}
}
}$
c->parse_error = 1;
else if (v) {
struct value res = interp_exec(c, $5, &v->type);
- v->val = val_alloc(c, v->type, &res);
+ global_alloc(c, v->type, v, &res);
}
} }$
printf("const\n");
target = i;
} else {
+ struct value *val = var_value(&context, v);
printf(" %.*s :: ", v->name->name.len, v->name->name.txt);
type_print(v->type, stdout);
printf(" = ");
if (v->type == Tstr)
printf("\"");
- print_value(v->type, v->val);
+ print_value(v->type, val);
if (v->type == Tstr)
printf("\"");
printf("\n");
if (!v->var->type) {
v->var->where_set = b;
v->var->type = Tstr;
- v->var->val = NULL;
}
}
b = cast(binode, prog);
/* Make sure everything is still consistent */
propagate_types(b->right, c, &ok, Tnone, 0);
- return !!ok;
+ if (!ok)
+ return 0;
+ scope_finalize(c);
+ return 1;
}
static void interp_prog(struct parse_context *c, struct exec *prog, char **argv)
al = cast(binode, p->left);
while (al) {
struct var *v = cast(var, al->left);
- struct value *vl = v->var->val;
+ struct value *vl = var_value(c, v->var);
struct value arg;
if (argv[0] == NULL) {
printf("Arg not string!!\n"); // NOTEST
exit(2); // NOTEST
}
- if (!vl) {
- vl = val_alloc(c, v->var->type, NULL);
- v->var->val = vl;
- }
arg.str.txt = argv[0];
arg.str.len = strlen(argv[0]);