1 //: So far we have local variables, and we can nest local variables of short
 2 //: lifetimes inside longer ones. Now let's support 'global' variables that
 3 //: last for the life of a routine. If we create multiple routines they won't
 4 //: have access to each other's globals.
 5 //:
 6 //: This feature is still experimental and half-baked. You can't name global
 7 //: variables, and they don't get checked for types (the only known hole in
 8 //: the type system, can cause memory corruption). We might fix these issues
 9 //: if we ever use globals. Or we might just drop the feature entirely.
10 //: [tag: todo]
11 
12 :(scenario global_space)
13 def main [
14   # pretend address:array:location; in practice we'll use new
15   10:num <- copy 0  # refcount
16   11:num <- copy 5  # length
17   # pretend address:array:location; in practice we'll use new
18   20:num <- copy 0  # refcount
19   21:num <- copy 5  # length
20   # actual start of this recipe
21   global-space:space <- copy 20/unsafe
22   default-space:space <- copy 10/unsafe
23   1:num <- copy 23
24   1:num/space:global <- copy 24
25 ]
26 # store to default space: 10 + (skip refcount and length) 2 + (index) 1
27 +mem: storing 23 in location 13
28 # store to chained space: (contents of location 12) 20 + (refcount and length) 2 + (index) 1
29 +mem: storing 24 in location 23
30 
31 //: to support it, create another special variable called global space
32 :(before "End is_disqualified Special-cases")
33 if (x.name == "global-space")
34   x.initialized = true;
35 :(before "End is_special_name Special-cases")
36 if (s == "global-space") return true;
37 
38 //: writes to this variable go to a field in the current routine
39 :(before "End routine Fields")
40 int global_space;
41 :(before "End routine Constructor")
42 global_space = 0;
43 :(after "Begin Preprocess write_memory(x, data)")
44 if (x.name == "global-space") {
45   if (!scalar(data) || !is_space(x))
46     raise << maybe(current_recipe_name()) << "'global-space' should be of type address:array:location, but tried to write '" << to_string(x.type) << "'\n" << end();
47   if (Current_routine->global_space)
48     raise << "routine already has a global-space; you can't over-write your globals" << end();
49   Current_routine->global_space = data.at(0);
50   return;
51 }
52 
53 //: now marking variables as /space:global looks them up inside this field
54 :(after "int space_base(const reagent& x)")
55   if (is_global(x)) {
56     if (!Current_routine->global_space)
57       raise << "routine has no global space\n" << end();
58     return Current_routine->global_space + /*skip refcount*/1;
59   }
60 
61 //: for now let's not bother giving global variables names.
62 //: don't want to make them too comfortable to use.
63 
64 :(scenario global_space_with_names)
65 def main [
66   global-space:space <- new location:type, 10
67   x:num <- copy 23
68   1:num/space:global <- copy 24
69 ]
70 # don't complain about mixing numeric addresses and names
71 $error: 0
72 
73 :(after "bool is_numeric_location(const reagent& x)")
74   if (is_global(x)) return false;
75 
76 //: helpers
77 
78 :(code)
79 bool is_global(const reagent& x) {
80   string_tree* s = property(x, "space");
81   return s && s->atom && s->value == "global";
82 }