4266 - space for alloc-id in heap allocations
This has taken me almost 6 weeks :(
This commit is contained in:
parent
377b00b045
commit
23d3a02226
39
020run.cc
39
020run.cc
|
@ -87,6 +87,10 @@ void run_current_routine() {
|
|||
// Primitive Recipe Implementations
|
||||
case COPY: {
|
||||
copy(ingredients.begin(), ingredients.end(), inserter(products, products.begin()));
|
||||
for (int i = 0; i < SIZE(current_instruction().products); ++i) {
|
||||
if (is_mu_scalar(current_instruction().products.at(i)) && is_mu_address(current_instruction().ingredients.at(i)))
|
||||
products.at(i).erase(products.at(i).begin()); // ignore alloc id
|
||||
}
|
||||
break;
|
||||
}
|
||||
// End Primitive Recipe Implementations
|
||||
|
@ -125,6 +129,33 @@ bool should_copy_ingredients() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool is_mu_scalar(reagent/*copy*/ r) {
|
||||
return is_mu_scalar(r.type);
|
||||
}
|
||||
bool is_mu_scalar(const type_tree* type) {
|
||||
if (!type) return false;
|
||||
if (is_mu_address(type)) return false;
|
||||
if (!type->atom) return false;
|
||||
if (is_literal(type))
|
||||
return type->name != "literal-string";
|
||||
return size_of(type) == 1;
|
||||
}
|
||||
|
||||
bool is_mu_address(reagent/*copy*/ r) {
|
||||
// End Preprocess is_mu_address(reagent r)
|
||||
return is_mu_address(r.type);
|
||||
}
|
||||
bool is_mu_address(const type_tree* type) {
|
||||
if (!type) return false;
|
||||
if (is_literal(type)) return false;
|
||||
if (type->atom) return false;
|
||||
if (!type->left->atom) {
|
||||
raise << "invalid type " << to_string(type) << '\n' << end();
|
||||
return false;
|
||||
}
|
||||
return type->left->value == Address_type_ordinal;
|
||||
}
|
||||
|
||||
//: Some helpers.
|
||||
//: Important that they return references into the current routine.
|
||||
|
||||
|
@ -299,6 +330,7 @@ bool ends_with(const string& s, const string& pat) {
|
|||
vector<double> read_memory(reagent/*copy*/ x) {
|
||||
// Begin Preprocess read_memory(x)
|
||||
vector<double> result;
|
||||
if (x.name == "null") result.push_back(/*alloc id*/0);
|
||||
if (is_literal(x)) {
|
||||
result.push_back(x.value);
|
||||
return result;
|
||||
|
@ -358,7 +390,7 @@ int size_of(const type_tree* type) {
|
|||
raise << "invalid type " << to_string(type) << '\n' << end();
|
||||
return 0;
|
||||
}
|
||||
if (type->left->value == Address_type_ordinal) return 1;
|
||||
if (type->left->value == Address_type_ordinal) return 2; // address and alloc id
|
||||
// End size_of(type) Non-atom Special-cases
|
||||
}
|
||||
// End size_of(type) Special-cases
|
||||
|
@ -421,6 +453,11 @@ def main [
|
|||
]
|
||||
+run: _ <- copy {0: "literal"}
|
||||
|
||||
:(scenario run_null)
|
||||
def main [
|
||||
1:&:num <- copy null
|
||||
]
|
||||
|
||||
:(scenario write_to_0_disallowed)
|
||||
% Hide_errors = true;
|
||||
def main [
|
||||
|
|
|
@ -88,14 +88,15 @@ $error: 0
|
|||
|
||||
:(code)
|
||||
// types_match with some leniency
|
||||
bool types_coercible(const reagent& to, const reagent& from) {
|
||||
if (types_match(to, from)) return true;
|
||||
bool types_coercible(reagent/*copy*/ to, reagent/*copy*/ from) {
|
||||
// Begin types_coercible(reagent to, reagent from)
|
||||
if (types_match_sub(to, from)) return true;
|
||||
if (is_real_mu_number(from) && is_mu_character(to)) return true;
|
||||
// End types_coercible Special-cases
|
||||
return false;
|
||||
}
|
||||
|
||||
bool types_match(const reagent& to, const reagent& from) {
|
||||
bool types_match_sub(const reagent& to, const reagent& from) {
|
||||
// to sidestep type-checking, use /unsafe in the source.
|
||||
// this will be highlighted in red inside vim. just for setting up some tests.
|
||||
if (is_unsafe(from)) return true;
|
||||
|
@ -103,15 +104,20 @@ bool types_match(const reagent& to, const reagent& from) {
|
|||
if (is_mu_array(to)) return false;
|
||||
// End Matching Types For Literal(to)
|
||||
if (!to.type) return false;
|
||||
// allow writing null to any address
|
||||
if (is_mu_address(to)) return from.name == "null";
|
||||
return size_of(to) == 1; // literals are always scalars
|
||||
}
|
||||
return types_strictly_match(to, from);
|
||||
return types_strictly_match_sub(to, from);
|
||||
}
|
||||
// variant for others to call
|
||||
bool types_match(reagent/*copy*/ to, reagent/*copy*/ from) {
|
||||
// Begin types_match(reagent to, reagent from)
|
||||
return types_match_sub(to, from);
|
||||
}
|
||||
|
||||
//: copy arguments for later layers
|
||||
bool types_strictly_match(reagent/*copy*/ to, reagent/*copy*/ from) {
|
||||
// End Preprocess types_strictly_match(reagent to, reagent from)
|
||||
bool types_strictly_match_sub(const reagent& to, const reagent& from) {
|
||||
if (to.type == NULL) return false; // error
|
||||
if (is_literal(from) && to.type->value == Number_type_ordinal) return true;
|
||||
// to sidestep type-checking, use /unsafe in the source.
|
||||
|
@ -122,6 +128,11 @@ bool types_strictly_match(reagent/*copy*/ to, reagent/*copy*/ from) {
|
|||
if (!to.type) return !from.type;
|
||||
return types_strictly_match(to.type, from.type);
|
||||
}
|
||||
// variant for others to call
|
||||
bool types_strictly_match(reagent/*copy*/ to, reagent/*copy*/ from) {
|
||||
// Begin types_strictly_match(reagent to, reagent from)
|
||||
return types_strictly_match_sub(to, from);
|
||||
}
|
||||
|
||||
bool types_strictly_match(const type_tree* to, const type_tree* from) {
|
||||
if (from == to) return true;
|
||||
|
@ -185,21 +196,6 @@ bool is_mu_array(const type_tree* type) {
|
|||
return type->left->value == Array_type_ordinal;
|
||||
}
|
||||
|
||||
bool is_mu_address(reagent/*copy*/ r) {
|
||||
// End Preprocess is_mu_address(reagent r)
|
||||
return is_mu_address(r.type);
|
||||
}
|
||||
bool is_mu_address(const type_tree* type) {
|
||||
if (!type) return false;
|
||||
if (is_literal(type)) return false;
|
||||
if (type->atom) return false;
|
||||
if (!type->left->atom) {
|
||||
raise << "invalid type " << to_string(type) << '\n' << end();
|
||||
return false;
|
||||
}
|
||||
return type->left->value == Address_type_ordinal;
|
||||
}
|
||||
|
||||
bool is_mu_boolean(reagent/*copy*/ r) {
|
||||
// End Preprocess is_mu_boolean(reagent r)
|
||||
if (!r.type) return false;
|
||||
|
@ -234,15 +230,3 @@ bool is_mu_character(const type_tree* type) {
|
|||
if (is_literal(type)) return false;
|
||||
return type->value == Character_type_ordinal;
|
||||
}
|
||||
|
||||
bool is_mu_scalar(reagent/*copy*/ r) {
|
||||
return is_mu_scalar(r.type);
|
||||
}
|
||||
bool is_mu_scalar(const type_tree* type) {
|
||||
if (!type) return false;
|
||||
if (is_mu_address(type)) return true;
|
||||
if (!type->atom) return false;
|
||||
if (is_literal(type))
|
||||
return type->name != "literal-string";
|
||||
return size_of(type) == 1;
|
||||
}
|
||||
|
|
|
@ -79,7 +79,6 @@ case SUBTRACT: {
|
|||
break;
|
||||
}
|
||||
for (int i = 0; i < SIZE(inst.ingredients); ++i) {
|
||||
if (is_raw(inst.ingredients.at(i))) continue; // permit address offset computations in tests
|
||||
if (!is_mu_number(inst.ingredients.at(i))) {
|
||||
raise << maybe(get(Recipe, r).name) << "'subtract' requires number ingredients, but got '" << inst.ingredients.at(i).original_string << "'\n" << end();
|
||||
goto finish_checking_instruction;
|
||||
|
@ -104,10 +103,6 @@ case SUBTRACT: {
|
|||
products.at(0).push_back(result);
|
||||
break;
|
||||
}
|
||||
:(code)
|
||||
bool is_raw(const reagent& r) {
|
||||
return has_property(r, "raw");
|
||||
}
|
||||
|
||||
:(scenario subtract_literal)
|
||||
def main [
|
||||
|
|
|
@ -26,11 +26,17 @@ case AND: {
|
|||
case AND: {
|
||||
bool result = true;
|
||||
for (int i = 0; i < SIZE(ingredients); ++i)
|
||||
result = result && ingredients.at(i).at(0);
|
||||
result = result && scalar_ingredient(ingredients, i);
|
||||
products.resize(1);
|
||||
products.at(0).push_back(result);
|
||||
break;
|
||||
}
|
||||
:(code)
|
||||
double scalar_ingredient(const vector<vector<double> >& ingredients, int i) {
|
||||
if (is_mu_address(current_instruction().ingredients.at(i)))
|
||||
return ingredients.at(i).at(/*skip alloc id*/1);
|
||||
return ingredients.at(i).at(0);
|
||||
}
|
||||
|
||||
:(scenario and)
|
||||
def main [
|
||||
|
@ -84,7 +90,7 @@ case OR: {
|
|||
case OR: {
|
||||
bool result = false;
|
||||
for (int i = 0; i < SIZE(ingredients); ++i)
|
||||
result = result || ingredients.at(i).at(0);
|
||||
result = result || scalar_ingredient(ingredients, i);
|
||||
products.resize(1);
|
||||
products.at(0).push_back(result);
|
||||
break;
|
||||
|
@ -127,8 +133,8 @@ case NOT: {
|
|||
break;
|
||||
}
|
||||
for (int i = 0; i < SIZE(inst.ingredients); ++i) {
|
||||
if (!is_mu_scalar(inst.ingredients.at(i))) {
|
||||
raise << maybe(get(Recipe, r).name) << "'not' requires boolean ingredients, but got '" << inst.ingredients.at(i).original_string << "'\n" << end();
|
||||
if (!is_mu_scalar(inst.ingredients.at(i)) && !is_mu_address(inst.ingredients.at(i))) {
|
||||
raise << maybe(get(Recipe, r).name) << "'not' requires ingredients that can be interpreted as boolean, but got '" << inst.ingredients.at(i).original_string << "'\n" << end();
|
||||
goto finish_checking_instruction;
|
||||
}
|
||||
}
|
||||
|
@ -145,7 +151,7 @@ case NOT: {
|
|||
case NOT: {
|
||||
products.resize(SIZE(ingredients));
|
||||
for (int i = 0; i < SIZE(ingredients); ++i) {
|
||||
products.at(i).push_back(!ingredients.at(i).at(0));
|
||||
products.at(i).push_back(!scalar_ingredient(ingredients, i));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
21
024jump.cc
21
024jump.cc
|
@ -72,7 +72,7 @@ case JUMP_IF: {
|
|||
raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' should get exactly two ingredients\n" << end();
|
||||
break;
|
||||
}
|
||||
if (!is_mu_scalar(inst.ingredients.at(0))) {
|
||||
if (!is_mu_address(inst.ingredients.at(0)) && !is_mu_scalar(inst.ingredients.at(0))) {
|
||||
raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' requires a boolean for its first ingredient, but '" << inst.ingredients.at(0).name << "' has type '" << names_to_string_without_quotes(inst.ingredients.at(0).type) << "'\n" << end();
|
||||
break;
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ case JUMP_IF: {
|
|||
:(before "End Primitive Recipe Implementations")
|
||||
case JUMP_IF: {
|
||||
assert(current_instruction().ingredients.at(1).initialized);
|
||||
if (!ingredients.at(0).at(0)) {
|
||||
if (!scalar_ingredient(ingredients, 0)) {
|
||||
trace(9998, "run") << "jump-if fell through" << end();
|
||||
break;
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ def main [
|
|||
]
|
||||
+run: jump-if {999: "literal"}, {1: "offset"}
|
||||
+run: jumping to instruction 2
|
||||
-run: {1: "number"} <- copy {1: "literal"}
|
||||
-run: {123: "number"} <- copy {1: "literal"}
|
||||
-mem: storing 1 in location 123
|
||||
|
||||
:(scenario jump_if_fallthrough)
|
||||
|
@ -122,6 +122,17 @@ def main [
|
|||
+run: {123: "number"} <- copy {1: "literal"}
|
||||
+mem: storing 1 in location 123
|
||||
|
||||
:(scenario jump_if_on_address)
|
||||
def main [
|
||||
10:num/alloc-id, 11:num <- copy 0, 999
|
||||
jump-if 10:&:number, 1:offset
|
||||
123:num <- copy 1
|
||||
]
|
||||
+run: jump-if {10: ("address" "number")}, {1: "offset"}
|
||||
+run: jumping to instruction 3
|
||||
-run: {123: "number"} <- copy {1: "literal"}
|
||||
-mem: storing 1 in location 123
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
JUMP_UNLESS,
|
||||
:(before "End Primitive Recipe Numbers")
|
||||
|
@ -132,7 +143,7 @@ case JUMP_UNLESS: {
|
|||
raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' should get exactly two ingredients\n" << end();
|
||||
break;
|
||||
}
|
||||
if (!is_mu_scalar(inst.ingredients.at(0))) {
|
||||
if (!is_mu_address(inst.ingredients.at(0)) && !is_mu_scalar(inst.ingredients.at(0))) {
|
||||
raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' requires a boolean for its first ingredient, but '" << inst.ingredients.at(0).name << "' has type '" << names_to_string_without_quotes(inst.ingredients.at(0).type) << "'\n" << end();
|
||||
break;
|
||||
}
|
||||
|
@ -150,7 +161,7 @@ case JUMP_UNLESS: {
|
|||
:(before "End Primitive Recipe Implementations")
|
||||
case JUMP_UNLESS: {
|
||||
assert(current_instruction().ingredients.at(1).initialized);
|
||||
if (ingredients.at(0).at(0)) {
|
||||
if (scalar_ingredient(ingredients, 0)) {
|
||||
trace(9998, "run") << "jump-unless fell through" << end();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,10 @@ case EQUAL: {
|
|||
vector<double>& exemplar = ingredients.at(0);
|
||||
bool result = true;
|
||||
for (int i = /*skip exemplar*/1; i < SIZE(ingredients); ++i) {
|
||||
if (SIZE(ingredients.at(i)) != SIZE(exemplar)) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
if (!equal(ingredients.at(i).begin(), ingredients.at(i).end(), exemplar.begin())) {
|
||||
result = false;
|
||||
break;
|
||||
|
@ -103,6 +107,10 @@ case NOT_EQUAL: {
|
|||
case NOT_EQUAL: {
|
||||
vector<double>& exemplar = ingredients.at(0);
|
||||
products.resize(1);
|
||||
if (SIZE(ingredients.at(1)) != SIZE(exemplar)) {
|
||||
products.at(0).push_back(true);
|
||||
break;
|
||||
}
|
||||
bool equal_ingredients = equal(ingredients.at(1).begin(), ingredients.at(1).end(), exemplar.begin());
|
||||
products.at(0).push_back(!equal_ingredients);
|
||||
break;
|
||||
|
|
|
@ -124,6 +124,8 @@ case REWIND_INGREDIENTS: {
|
|||
break;
|
||||
}
|
||||
|
||||
//: another primitive: 'ingredient' for random access
|
||||
|
||||
:(scenario ingredient)
|
||||
def main [
|
||||
f 1, 2
|
||||
|
|
101
032array.cc
101
032array.cc
|
@ -11,7 +11,7 @@ def main [
|
|||
# create an array occupying locations 1 (for the size) and 2-4 (for the elements)
|
||||
1:array:num:3 <- create-array
|
||||
]
|
||||
+run: creating array of size 4
|
||||
+run: creating array from 4 locations
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
CREATE_ARRAY,
|
||||
|
@ -60,7 +60,7 @@ case CREATE_ARRAY: {
|
|||
trace("mem") << "storing " << array_length << " in location " << base_address << end();
|
||||
put(Memory, base_address, array_length); // in array elements
|
||||
int size = size_of(product); // in locations
|
||||
trace(9998, "run") << "creating array of size " << size << end();
|
||||
trace(9998, "run") << "creating array from " << size << " locations" << end();
|
||||
// initialize array
|
||||
for (int i = 1; i <= size_of(product); ++i)
|
||||
put(Memory, base_address+i, 0);
|
||||
|
@ -208,19 +208,23 @@ def main [
|
|||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
5:num <- index 1:array:num:3, 0/index # the index must be a non-negative whole number
|
||||
10:num <- index 1:array:num:3, 0/index # the index must be a non-negative whole number
|
||||
]
|
||||
+mem: storing 14 in location 5
|
||||
+mem: storing 14 in location 10
|
||||
|
||||
:(scenario index_compound_element)
|
||||
def main [
|
||||
{1: (array (address number) 3)} <- create-array
|
||||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
5:&:num <- index {1: (array (address number) 3)}, 0
|
||||
# skip alloc id
|
||||
3:num <- copy 14
|
||||
# skip alloc id
|
||||
5:num <- copy 15
|
||||
# skip alloc id
|
||||
7:num <- copy 16
|
||||
10:address:num <- index {1: (array (address number) 3)}, 0
|
||||
]
|
||||
+mem: storing 14 in location 5
|
||||
# skip alloc id
|
||||
+mem: storing 14 in location 11
|
||||
|
||||
:(scenario index_direct_offset)
|
||||
def main [
|
||||
|
@ -228,10 +232,10 @@ def main [
|
|||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
5:num <- copy 0
|
||||
6:num <- index 1:array:num, 5:num
|
||||
10:num <- copy 0
|
||||
20:num <- index 1:array:num, 10:num
|
||||
]
|
||||
+mem: storing 14 in location 6
|
||||
+mem: storing 14 in location 20
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
INDEX,
|
||||
|
@ -330,7 +334,7 @@ void test_array_length_compound() {
|
|||
put(Memory, 2, 14);
|
||||
put(Memory, 3, 15);
|
||||
put(Memory, 4, 16);
|
||||
reagent x("1:array:&:num"); // 3 types, but not a static array
|
||||
reagent x("1:array:address:num"); // 3 types, but not a static array
|
||||
populate_value(x);
|
||||
CHECK_EQ(array_length(x), 3);
|
||||
}
|
||||
|
@ -346,61 +350,40 @@ def main [
|
|||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
5:num <- index 1:array:num:3, 1.5 # non-whole number
|
||||
10:num <- index 1:array:num:3, 1.5 # non-whole number
|
||||
]
|
||||
# fraction is truncated away
|
||||
+mem: storing 15 in location 5
|
||||
+mem: storing 15 in location 10
|
||||
|
||||
:(scenario index_out_of_bounds)
|
||||
% Hide_errors = true;
|
||||
def main [
|
||||
1:array:num:3 <- create-array
|
||||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
5:num <- copy 14
|
||||
6:num <- copy 15
|
||||
7:num <- copy 16
|
||||
index 1:array:num:3, 4 # less than size of array in locations, but larger than its length in elements
|
||||
1:array:point:3 <- create-array
|
||||
index 1:array:point:3, 4 # less than size of array in locations, but larger than its length in elements
|
||||
]
|
||||
+error: main: invalid index 4 in 'index 1:array:num:3, 4'
|
||||
+error: main: invalid index 4 in 'index 1:array:point:3, 4'
|
||||
|
||||
:(scenario index_out_of_bounds_2)
|
||||
% Hide_errors = true;
|
||||
def main [
|
||||
1:array:point:3 <- create-array
|
||||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
5:num <- copy 14
|
||||
6:num <- copy 15
|
||||
7:num <- copy 16
|
||||
index 1:array:point, -1
|
||||
1:array:num:3 <- create-array
|
||||
index 1:array:num, -1
|
||||
]
|
||||
+error: main: invalid index -1 in 'index 1:array:point, -1'
|
||||
+error: main: invalid index -1 in 'index 1:array:num, -1'
|
||||
|
||||
:(scenario index_product_type_mismatch)
|
||||
% Hide_errors = true;
|
||||
def main [
|
||||
1:array:point:3 <- create-array
|
||||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
5:num <- copy 14
|
||||
6:num <- copy 15
|
||||
7:num <- copy 16
|
||||
9:num <- index 1:array:point, 0
|
||||
10:num <- index 1:array:point, 0
|
||||
]
|
||||
+error: main: 'index' on '1:array:point' can't be saved in '9:num'; type should be 'point'
|
||||
+error: main: 'index' on '1:array:point' can't be saved in '10:num'; type should be 'point'
|
||||
|
||||
//: we might want to call 'index' without saving the results, say in a sandbox
|
||||
|
||||
:(scenario index_without_product)
|
||||
def main [
|
||||
1:array:num:3 <- create-array
|
||||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
index 1:array:num:3, 0
|
||||
]
|
||||
# just don't die
|
||||
|
@ -410,9 +393,6 @@ def main [
|
|||
:(scenario put_index)
|
||||
def main [
|
||||
1:array:num:3 <- create-array
|
||||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
1:array:num <- put-index 1:array:num, 1, 34
|
||||
]
|
||||
+mem: storing 34 in location 3
|
||||
|
@ -488,12 +468,6 @@ case PUT_INDEX: {
|
|||
% Hide_errors = true;
|
||||
def main [
|
||||
1:array:point:3 <- create-array
|
||||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
5:num <- copy 14
|
||||
6:num <- copy 15
|
||||
7:num <- copy 16
|
||||
8:point <- merge 34, 35
|
||||
1:array:point <- put-index 1:array:point, 4, 8:point # '4' is less than size of array in locations, but larger than its length in elements
|
||||
]
|
||||
|
@ -503,22 +477,14 @@ def main [
|
|||
% Hide_errors = true;
|
||||
def main [
|
||||
1:array:point:3 <- create-array
|
||||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
5:num <- copy 14
|
||||
6:num <- copy 15
|
||||
7:num <- copy 16
|
||||
8:point <- merge 34, 35
|
||||
1:array:point <- put-index 1:array:point, -1, 8:point
|
||||
10:point <- merge 34, 35
|
||||
1:array:point <- put-index 1:array:point, -1, 10:point
|
||||
]
|
||||
+error: main: invalid index -1 in '1:array:point <- put-index 1:array:point, -1, 8:point'
|
||||
+error: main: invalid index -1 in '1:array:point <- put-index 1:array:point, -1, 10:point'
|
||||
|
||||
:(scenario put_index_product_error)
|
||||
% Hide_errors = true;
|
||||
def main [
|
||||
local-scope
|
||||
load-ingredients
|
||||
1:array:num:3 <- create-array
|
||||
4:array:num:3 <- put-index 1:array:num:3, 0, 34
|
||||
]
|
||||
|
@ -529,12 +495,9 @@ def main [
|
|||
:(scenario array_length)
|
||||
def main [
|
||||
1:array:num:3 <- create-array
|
||||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
5:num <- length 1:array:num:3
|
||||
10:num <- length 1:array:num
|
||||
]
|
||||
+mem: storing 3 in location 5
|
||||
+mem: storing 3 in location 10
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
LENGTH,
|
||||
|
|
|
@ -457,39 +457,3 @@ def main [
|
|||
+mem: storing 1 in location 6
|
||||
+mem: storing 34 in location 7
|
||||
+mem: storing 35 in location 8
|
||||
|
||||
//: a little helper: convert address to number
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
DEADDRESS,
|
||||
:(before "End Primitive Recipe Numbers")
|
||||
put(Recipe_ordinal, "deaddress", DEADDRESS);
|
||||
:(before "End Primitive Recipe Checks")
|
||||
case DEADDRESS: {
|
||||
// primary goal of these checks is to forbid address arithmetic
|
||||
for (int i = 0; i < SIZE(inst.ingredients); ++i) {
|
||||
if (!is_mu_address(inst.ingredients.at(i))) {
|
||||
raise << maybe(get(Recipe, r).name) << "'deaddress' requires address ingredients, but got '" << inst.ingredients.at(i).original_string << "'\n" << end();
|
||||
goto finish_checking_instruction;
|
||||
}
|
||||
}
|
||||
if (SIZE(inst.products) > SIZE(inst.ingredients)) {
|
||||
raise << maybe(get(Recipe, r).name) << "too many products in '" << to_original_string(inst) << "'\n" << end();
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < SIZE(inst.products); ++i) {
|
||||
if (!is_real_mu_number(inst.products.at(i))) {
|
||||
raise << maybe(get(Recipe, r).name) << "'deaddress' requires number products, but got '" << inst.products.at(i).original_string << "'\n" << end();
|
||||
goto finish_checking_instruction;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
:(before "End Primitive Recipe Implementations")
|
||||
case DEADDRESS: {
|
||||
products.resize(SIZE(ingredients));
|
||||
for (int i = 0; i < SIZE(ingredients); ++i) {
|
||||
products.at(i).push_back(ingredients.at(i).at(0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
160
034address.cc
160
034address.cc
|
@ -18,6 +18,37 @@
|
|||
//: write to the payload of an ingredient rather than its value, simply add
|
||||
//: the /lookup property to it. Modern computers provide efficient support for
|
||||
//: addresses and lookups, making this a realistic feature.
|
||||
//:
|
||||
//: To create addresses and allocate memory exclusively for their use, use
|
||||
//: 'new'. Memory is a finite resource so if the computer can't satisfy your
|
||||
//: request, 'new' may return a 0 (null) address.
|
||||
//:
|
||||
//: Computers these days have lots of memory so in practice we can often
|
||||
//: assume we'll never run out. If you start running out however, say in a
|
||||
//: long-running program, you'll need to switch mental gears and start
|
||||
//: husbanding our memory more carefully. The most important tool to avoid
|
||||
//: wasting memory is to 'abandon' an address when you don't need it anymore.
|
||||
//: That frees up the memory allocated to it to be reused in future calls to
|
||||
//: 'new'.
|
||||
|
||||
//: Since memory can be reused multiple times, it can happen that you have a
|
||||
//: stale copy to an address that has since been abandoned and reused. Using
|
||||
//: the stale address is almost never safe, but it can be very hard to track
|
||||
//: down such copies because any errors caused by them may occur even millions
|
||||
//: of instructions after the copy or abandon instruction. To help track down
|
||||
//: such issues, Mu tracks an 'alloc id' for each allocation it makes. The
|
||||
//: first call to 'new' has an alloc id of 1, the second gets 2, and so on.
|
||||
//: The alloc id is never reused.
|
||||
:(before "End Globals")
|
||||
long long Next_alloc_id = 0;
|
||||
:(before "End Reset")
|
||||
Next_alloc_id = 0;
|
||||
|
||||
//: The 'new' instruction records alloc ids both in the memory being allocated
|
||||
//: and *also* in the address. The 'abandon' instruction clears alloc ids in
|
||||
//: both places as well. Tracking alloc ids in this manner allows us to raise
|
||||
//: errors about stale addresses much earlier: 'lookup' operations always
|
||||
//: compare alloc ids between the address and its payload.
|
||||
|
||||
//: todo: give 'new' a custodian ingredient. Following malloc/free is a temporary hack.
|
||||
|
||||
|
@ -25,29 +56,33 @@
|
|||
# call 'new' two times with identical types without modifying the results; you
|
||||
# should get back different results
|
||||
def main [
|
||||
1:&:num/raw <- new num:type
|
||||
2:&:num/raw <- new num:type
|
||||
3:bool/raw <- equal 1:&:num/raw, 2:&:num/raw
|
||||
10:&:num <- new num:type
|
||||
12:&:num <- new num:type
|
||||
20:bool <- equal 10:&:num, 12:&:num
|
||||
]
|
||||
+mem: storing 0 in location 3
|
||||
+mem: storing 1000 in location 11
|
||||
+mem: storing 0 in location 20
|
||||
|
||||
:(scenario new_array)
|
||||
# call 'new' with a second ingredient to allocate an array of some type rather than a single copy
|
||||
def main [
|
||||
1:&:@:num/raw <- new num:type, 5
|
||||
2:&:num/raw <- new num:type
|
||||
3:num/raw <- subtract 2:&:num/raw, 1:&:@:num/raw
|
||||
10:&:@:num <- new num:type, 5
|
||||
12:&:num <- new num:type
|
||||
20:num/alloc2, 21:num/alloc1 <- deaddress 10:&:@:num, 12:&:num
|
||||
30:num <- subtract 21:num/alloc2, 20:num/alloc1
|
||||
]
|
||||
+run: {1: ("address" "array" "number"), "raw": ()} <- new {num: "type"}, {5: "literal"}
|
||||
+run: {10: ("address" "array" "number")} <- new {num: "type"}, {5: "literal"}
|
||||
+mem: array length is 5
|
||||
# don't forget the extra location for array length
|
||||
+mem: storing 6 in location 3
|
||||
# skip alloc id in allocation
|
||||
+mem: storing 1000 in location 11
|
||||
# don't forget the extra locations for alloc id and array length
|
||||
+mem: storing 7 in location 30
|
||||
|
||||
:(scenario dilated_reagent_with_new)
|
||||
def main [
|
||||
1:&:&:num <- new {(& num): type}
|
||||
10:&:&:num <- new {(& num): type}
|
||||
]
|
||||
+new: size of '(& num)' is 1
|
||||
+new: size of '(& num)' is 2
|
||||
|
||||
//: 'new' takes a weird 'type' as its first ingredient; don't error on it
|
||||
:(before "End Mu Types Initialization")
|
||||
|
@ -135,22 +170,29 @@ def main [
|
|||
:(scenario new_discerns_singleton_list_from_atom_container)
|
||||
% Hide_errors = true;
|
||||
def main [
|
||||
1:&:num/raw <- new {(num): type} # should be '{num: type}'
|
||||
1:&:num <- new {(num): type} # should be '{num: type}'
|
||||
]
|
||||
+error: main: product of 'new' has incorrect type: '1:&:num/raw <- new {(num): type}'
|
||||
+error: main: product of 'new' has incorrect type: '1:&:num <- new {(num): type}'
|
||||
|
||||
:(scenario new_with_type_abbreviation)
|
||||
def main [
|
||||
1:&:num/raw <- new num:type
|
||||
1:&:num <- new num:type
|
||||
]
|
||||
$error: 0
|
||||
|
||||
:(scenario new_with_type_abbreviation_inside_compound)
|
||||
def main [
|
||||
{1: (& & num), raw: ()} <- new {(& num): type}
|
||||
{1: (address address number), raw: ()} <- new {(& num): type}
|
||||
]
|
||||
$error: 0
|
||||
|
||||
:(scenario equal_result_of_new_with_null)
|
||||
def main [
|
||||
1:&:num <- new num:type
|
||||
10:bool <- equal 1:&:num, null
|
||||
]
|
||||
+mem: storing 0 in location 10
|
||||
|
||||
//: To implement 'new', a Mu transform turns all 'new' instructions into
|
||||
//: 'allocate' instructions that precompute the amount of memory they want to
|
||||
//: allocate.
|
||||
|
@ -221,15 +263,18 @@ case ALLOCATE: {
|
|||
int result = allocate(size);
|
||||
if (SIZE(current_instruction().ingredients) > 1) {
|
||||
// initialize array length
|
||||
trace("mem") << "storing " << ingredients.at(1).at(0) << " in location " << result << end();
|
||||
put(Memory, result, ingredients.at(1).at(0));
|
||||
trace("mem") << "storing array length " << ingredients.at(1).at(0) << " in location " << result+/*skip alloc id*/1 << end();
|
||||
put(Memory, result+/*skip alloc id*/1, ingredients.at(1).at(0));
|
||||
}
|
||||
products.resize(1);
|
||||
products.at(0).push_back(/*alloc id*/0);
|
||||
products.at(0).push_back(result);
|
||||
break;
|
||||
}
|
||||
:(code)
|
||||
int allocate(int size) {
|
||||
// include space for alloc id
|
||||
++size;
|
||||
trace("mem") << "allocating size " << size << end();
|
||||
//? Total_alloc += size;
|
||||
//? ++Num_alloc;
|
||||
|
@ -289,42 +334,45 @@ def main [
|
|||
|
||||
:(scenario new_size)
|
||||
def main [
|
||||
11:&:num/raw <- new num:type
|
||||
12:&:num/raw <- new num:type
|
||||
13:num/raw <- subtract 12:&:num/raw, 11:&:num/raw
|
||||
10:&:num <- new num:type
|
||||
12:&:num <- new num:type
|
||||
20:num/alloc1, 21:num/alloc2 <- deaddress 10:&:num, 12:&:num
|
||||
30:num <- subtract 21:num/alloc2, 20:num/alloc1
|
||||
]
|
||||
# size of number
|
||||
+mem: storing 1 in location 13
|
||||
# size of number + alloc id
|
||||
+mem: storing 2 in location 30
|
||||
|
||||
:(scenario new_array_size)
|
||||
def main [
|
||||
1:&:@:num/raw <- new num:type, 5
|
||||
2:&:num/raw <- new num:type
|
||||
3:num/raw <- subtract 2:&:num/raw, 1:&:@:num/raw
|
||||
10:&:@:num <- new num:type, 5
|
||||
12:&:num <- new num:type
|
||||
20:num/alloc1, 21:num/alloc2 <- deaddress 10:&:num, 12:&:num
|
||||
30:num <- subtract 21:num/alloc2, 20:num/alloc1
|
||||
]
|
||||
# 5 locations for array contents + array length
|
||||
+mem: storing 6 in location 3
|
||||
# 5 locations for array contents + array length + alloc id
|
||||
+mem: storing 7 in location 30
|
||||
|
||||
:(scenario new_empty_array)
|
||||
def main [
|
||||
1:&:@:num/raw <- new num:type, 0
|
||||
2:&:num/raw <- new num:type
|
||||
3:num/raw <- subtract 2:&:num/raw, 1:&:@:num/raw
|
||||
10:&:@:num <- new num:type, 0
|
||||
12:&:num <- new num:type
|
||||
20:num/alloc1, 21:num/alloc2 <- deaddress 10:&:@:num, 12:&:num
|
||||
30:num <- subtract 21:num/alloc2, 20:num/alloc1
|
||||
]
|
||||
+run: {1: ("address" "array" "number"), "raw": ()} <- new {num: "type"}, {0: "literal"}
|
||||
+run: {10: ("address" "array" "number")} <- new {num: "type"}, {0: "literal"}
|
||||
+mem: array length is 0
|
||||
# one location for array length
|
||||
+mem: storing 1 in location 3
|
||||
+mem: storing 2 in location 30
|
||||
|
||||
//: If a routine runs out of its initial allocation, it should allocate more.
|
||||
:(scenario new_overflow)
|
||||
% Initial_memory_per_routine = 2; // barely enough room for point allocation below
|
||||
% Initial_memory_per_routine = 3; // barely enough room for point allocation below
|
||||
def main [
|
||||
1:&:num/raw <- new num:type
|
||||
2:&:point/raw <- new point:type # not enough room in initial page
|
||||
10:&:num <- new num:type
|
||||
12:&:point <- new point:type # not enough room in initial page
|
||||
]
|
||||
+new: routine allocated memory from 1000 to 1002
|
||||
+new: routine allocated memory from 1002 to 1004
|
||||
+new: routine allocated memory from 1000 to 1003
|
||||
+new: routine allocated memory from 1003 to 1006
|
||||
|
||||
:(scenario new_without_ingredient)
|
||||
% Hide_errors = true;
|
||||
|
@ -332,3 +380,39 @@ def main [
|
|||
1:&:num <- new # missing ingredient
|
||||
]
|
||||
+error: main: 'new' requires one or two ingredients, but got '1:&:num <- new'
|
||||
|
||||
//: a little helper: convert address to number
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
DEADDRESS,
|
||||
:(before "End Primitive Recipe Numbers")
|
||||
put(Recipe_ordinal, "deaddress", DEADDRESS);
|
||||
:(before "End Primitive Recipe Checks")
|
||||
case DEADDRESS: {
|
||||
// primary goal of these checks is to forbid address arithmetic
|
||||
for (int i = 0; i < SIZE(inst.ingredients); ++i) {
|
||||
if (!is_mu_address(inst.ingredients.at(i))) {
|
||||
raise << maybe(get(Recipe, r).name) << "'deaddress' requires address ingredients, but got '" << inst.ingredients.at(i).original_string << "'\n" << end();
|
||||
goto finish_checking_instruction;
|
||||
}
|
||||
}
|
||||
if (SIZE(inst.products) > SIZE(inst.ingredients)) {
|
||||
raise << maybe(get(Recipe, r).name) << "too many products in '" << to_original_string(inst) << "'\n" << end();
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < SIZE(inst.products); ++i) {
|
||||
if (!is_real_mu_number(inst.products.at(i))) {
|
||||
raise << maybe(get(Recipe, r).name) << "'deaddress' requires number products, but got '" << inst.products.at(i).original_string << "'\n" << end();
|
||||
goto finish_checking_instruction;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
:(before "End Primitive Recipe Implementations")
|
||||
case DEADDRESS: {
|
||||
products.resize(SIZE(ingredients));
|
||||
for (int i = 0; i < SIZE(ingredients); ++i) {
|
||||
products.at(i).push_back(ingredients.at(i).at(/*skip alloc id*/1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
304
035lookup.cc
304
035lookup.cc
|
@ -5,12 +5,15 @@
|
|||
|
||||
:(scenario copy_indirect)
|
||||
def main [
|
||||
1:&:num <- copy 10/unsafe
|
||||
10:num <- copy 34
|
||||
# This loads location 1 as an address and looks up *that* location.
|
||||
2:num <- copy 1:&:num/lookup
|
||||
# skip alloc id for 10:&:num
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:num <- copy 94
|
||||
# Treat locations 10 and 11 as an address to look up, pointing at the
|
||||
# payload in locations 20 and 21.
|
||||
30:num <- copy 10:&:num/lookup
|
||||
]
|
||||
+mem: storing 34 in location 2
|
||||
+mem: storing 94 in location 30
|
||||
|
||||
:(before "End Preprocess read_memory(x)")
|
||||
canonize(x);
|
||||
|
@ -19,10 +22,11 @@ canonize(x);
|
|||
//: 'lookup' property
|
||||
:(scenario store_indirect)
|
||||
def main [
|
||||
1:&:num <- copy 10/unsafe
|
||||
1:&:num/lookup <- copy 34
|
||||
# skip alloc id for 10:&:num
|
||||
11:num <- copy 10
|
||||
10:&:num/lookup <- copy 94
|
||||
]
|
||||
+mem: storing 34 in location 10
|
||||
+mem: storing 94 in location 11
|
||||
|
||||
:(before "End Preprocess write_memory(x, data)")
|
||||
canonize(x);
|
||||
|
@ -31,20 +35,20 @@ canonize(x);
|
|||
:(scenario store_to_0_fails)
|
||||
% Hide_errors = true;
|
||||
def main [
|
||||
1:&:num <- copy null
|
||||
1:&:num/lookup <- copy 34
|
||||
10:&:num <- copy null
|
||||
10:&:num/lookup <- copy 94
|
||||
]
|
||||
-mem: storing 34 in location 0
|
||||
+error: can't write to location 0 in '1:&:num/lookup <- copy 34'
|
||||
-mem: storing 94 in location 0
|
||||
+error: main: tried to lookup 0 in '10:&:num/lookup <- copy 94'
|
||||
|
||||
//: attempts to /lookup address 0 always loudly fail
|
||||
:(scenario lookup_0_fails)
|
||||
% Hide_errors = true;
|
||||
def main [
|
||||
1:&:num <- copy null
|
||||
2:num <- copy 1:&:num/lookup
|
||||
10:&:num <- copy null
|
||||
20:num <- copy 10:&:num/lookup
|
||||
]
|
||||
+error: main: tried to lookup 0 in '2:num <- copy 1:&:num/lookup'
|
||||
+error: main: tried to lookup 0 in '20:num <- copy 10:&:num/lookup'
|
||||
|
||||
:(scenario lookup_0_dumps_callstack)
|
||||
% Hide_errors = true;
|
||||
|
@ -52,10 +56,10 @@ def main [
|
|||
foo null
|
||||
]
|
||||
def foo [
|
||||
1:&:num <- next-input
|
||||
2:num <- copy 1:&:num/lookup
|
||||
10:&:num <- next-input
|
||||
20:num <- copy 10:&:num/lookup
|
||||
]
|
||||
+error: foo: tried to lookup 0 in '2:num <- copy 1:&:num/lookup'
|
||||
+error: foo: tried to lookup 0 in '20:num <- copy 10:&:num/lookup'
|
||||
+error: called from main: foo null
|
||||
|
||||
:(code)
|
||||
|
@ -82,7 +86,7 @@ void lookup_memory(reagent& x) {
|
|||
}
|
||||
|
||||
void lookup_memory_core(reagent& x, bool check_for_null) {
|
||||
double address = x.value;
|
||||
double address = x.value + /*skip alloc id in address*/1;
|
||||
double new_value = get_or_insert(Memory, address);
|
||||
trace("mem") << "location " << address << " contains " << no_scientific(new_value) << end();
|
||||
if (check_for_null && new_value == 0) {
|
||||
|
@ -94,12 +98,18 @@ void lookup_memory_core(reagent& x, bool check_for_null) {
|
|||
raise << "tried to lookup 0\n" << end();
|
||||
}
|
||||
}
|
||||
x.set_value(new_value);
|
||||
x.set_value(new_value+/*skip alloc id in payload*/1);
|
||||
drop_from_type(x, "address");
|
||||
drop_one_lookup(x);
|
||||
}
|
||||
|
||||
:(before "End Preprocess types_strictly_match(reagent to, reagent from)")
|
||||
:(after "Begin types_coercible(reagent to, reagent from)")
|
||||
if (!canonize_type(to)) return false;
|
||||
if (!canonize_type(from)) return false;
|
||||
:(after "Begin types_match(reagent to, reagent from)")
|
||||
if (!canonize_type(to)) return false;
|
||||
if (!canonize_type(from)) return false;
|
||||
:(after "Begin types_strictly_match(reagent to, reagent from)")
|
||||
if (!canonize_type(to)) return false;
|
||||
if (!canonize_type(from)) return false;
|
||||
|
||||
|
@ -156,31 +166,38 @@ void drop_one_lookup(reagent& r) {
|
|||
|
||||
:(scenario get_indirect)
|
||||
def main [
|
||||
1:&:point <- copy 10/unsafe
|
||||
10:num <- copy 34
|
||||
11:num <- copy 35
|
||||
2:num <- get 1:&:point/lookup, 0:offset
|
||||
# skip alloc id for 10:&:point
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:num <- copy 94
|
||||
22:num <- copy 95
|
||||
30:num <- get 10:&:point/lookup, 0:offset
|
||||
]
|
||||
+mem: storing 34 in location 2
|
||||
+mem: storing 94 in location 30
|
||||
|
||||
:(scenario get_indirect2)
|
||||
def main [
|
||||
1:&:point <- copy 10/unsafe
|
||||
10:num <- copy 34
|
||||
11:num <- copy 35
|
||||
2:&:num <- copy 20/unsafe
|
||||
2:&:num/lookup <- get 1:&:point/lookup, 0:offset
|
||||
# skip alloc id for 10:&:point
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:num <- copy 94
|
||||
22:num <- copy 95
|
||||
# skip alloc id for destination
|
||||
31:num <- copy 40
|
||||
30:&:num/lookup <- get 10:&:point/lookup, 0:offset
|
||||
]
|
||||
+mem: storing 34 in location 20
|
||||
+mem: storing 94 in location 41
|
||||
|
||||
:(scenario include_nonlookup_properties)
|
||||
def main [
|
||||
1:&:point <- copy 10/unsafe
|
||||
10:num <- copy 34
|
||||
11:num <- copy 35
|
||||
2:num <- get 1:&:point/lookup/foo, 0:offset
|
||||
# skip alloc id for 10:&:point
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:num <- copy 94
|
||||
22:num <- copy 95
|
||||
30:num <- get 10:&:point/lookup/foo, 0:offset
|
||||
]
|
||||
+mem: storing 34 in location 2
|
||||
+mem: storing 94 in location 30
|
||||
|
||||
:(after "Update GET base in Check")
|
||||
if (!canonize_type(base)) break;
|
||||
|
@ -191,12 +208,14 @@ canonize(base);
|
|||
|
||||
:(scenario put_indirect)
|
||||
def main [
|
||||
1:&:point <- copy 10/unsafe
|
||||
10:num <- copy 34
|
||||
11:num <- copy 35
|
||||
1:&:point/lookup <- put 1:&:point/lookup, 0:offset, 36
|
||||
# skip alloc id for 10:&:point
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:num <- copy 94
|
||||
22:num <- copy 95
|
||||
10:&:point/lookup <- put 10:&:point/lookup, 0:offset, 96
|
||||
]
|
||||
+mem: storing 36 in location 10
|
||||
+mem: storing 96 in location 21
|
||||
|
||||
:(after "Update PUT base in Check")
|
||||
if (!canonize_type(base)) break;
|
||||
|
@ -208,12 +227,14 @@ canonize(base);
|
|||
:(scenario put_product_error_with_lookup)
|
||||
% Hide_errors = true;
|
||||
def main [
|
||||
1:&:point <- copy 10/unsafe
|
||||
10:num <- copy 34
|
||||
11:num <- copy 35
|
||||
1:&:point <- put 1:&:point/lookup, x:offset, 36
|
||||
# skip alloc id for 10:&:point
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:num <- copy 94
|
||||
22:num <- copy 95
|
||||
10:&:point <- put 10:&:point/lookup, x:offset, 96
|
||||
]
|
||||
+error: main: product of 'put' must be first ingredient '1:&:point/lookup', but got '1:&:point'
|
||||
+error: main: product of 'put' must be first ingredient '10:&:point/lookup', but got '10:&:point'
|
||||
|
||||
:(before "End PUT Product Checks")
|
||||
reagent/*copy*/ p = inst.products.at(0);
|
||||
|
@ -237,24 +258,27 @@ canonize_type(product);
|
|||
|
||||
:(scenario copy_array_indirect)
|
||||
def main [
|
||||
10:@:num:3 <- create-array
|
||||
11:num <- copy 14
|
||||
12:num <- copy 15
|
||||
13:num <- copy 16
|
||||
1:&:@:num <- copy 10/unsafe
|
||||
2:@:num <- copy 1:&:@:num/lookup
|
||||
# skip alloc id for 10:&:@:num
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:num <- copy 3 # array length
|
||||
22:num <- copy 94
|
||||
23:num <- copy 95
|
||||
24:num <- copy 96
|
||||
30:@:num <- copy 10:&:@:num/lookup
|
||||
]
|
||||
+mem: storing 3 in location 2
|
||||
+mem: storing 14 in location 3
|
||||
+mem: storing 15 in location 4
|
||||
+mem: storing 16 in location 5
|
||||
+mem: storing 3 in location 30
|
||||
+mem: storing 94 in location 31
|
||||
+mem: storing 95 in location 32
|
||||
+mem: storing 96 in location 33
|
||||
|
||||
:(scenario create_array_indirect)
|
||||
def main [
|
||||
1:&:@:num:3 <- copy 1000/unsafe # pretend allocation
|
||||
1:&:@:num:3/lookup <- create-array
|
||||
# skip alloc id for 10:&:@:num:3
|
||||
11:num <- copy 3000
|
||||
10:&:array:num:3/lookup <- create-array
|
||||
]
|
||||
+mem: storing 3 in location 1000
|
||||
+mem: storing 3 in location 3001
|
||||
|
||||
:(after "Update CREATE_ARRAY product in Check")
|
||||
if (!canonize_type(product)) break;
|
||||
|
@ -263,14 +287,16 @@ canonize(product);
|
|||
|
||||
:(scenario index_indirect)
|
||||
def main [
|
||||
10:@:num:3 <- create-array
|
||||
11:num <- copy 14
|
||||
12:num <- copy 15
|
||||
13:num <- copy 16
|
||||
1:&:@:num <- copy 10/unsafe
|
||||
2:num <- index 1:&:@:num/lookup, 1
|
||||
# skip alloc id for 10:&:@:num
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:num <- copy 3 # array length
|
||||
22:num <- copy 94
|
||||
23:num <- copy 95
|
||||
24:num <- copy 96
|
||||
30:num <- index 10:&:@:num/lookup, 1
|
||||
]
|
||||
+mem: storing 15 in location 2
|
||||
+mem: storing 95 in location 30
|
||||
|
||||
:(before "Update INDEX base in Check")
|
||||
if (!canonize_type(base)) break;
|
||||
|
@ -286,38 +312,44 @@ canonize(index);
|
|||
|
||||
:(scenario put_index_indirect)
|
||||
def main [
|
||||
10:@:num:3 <- create-array
|
||||
11:num <- copy 14
|
||||
12:num <- copy 15
|
||||
13:num <- copy 16
|
||||
1:&:@:num <- copy 10/unsafe
|
||||
1:&:@:num/lookup <- put-index 1:&:@:num/lookup, 1, 34
|
||||
# skip alloc id for 10:&:@:num
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:num <- copy 3 # array length
|
||||
22:num <- copy 94
|
||||
23:num <- copy 95
|
||||
24:num <- copy 96
|
||||
10:&:@:num/lookup <- put-index 10:&:@:num/lookup, 1, 97
|
||||
]
|
||||
+mem: storing 34 in location 12
|
||||
+mem: storing 97 in location 23
|
||||
|
||||
:(scenario put_index_indirect_2)
|
||||
def main [
|
||||
1:@:num:3 <- create-array
|
||||
2:num <- copy 14
|
||||
3:num <- copy 15
|
||||
4:num <- copy 16
|
||||
5:&:num <- copy 10/unsafe
|
||||
10:num <- copy 1
|
||||
1:@:num:3 <- put-index 1:@:num:3, 5:&:num/lookup, 34
|
||||
10:num <- copy 3 # array length
|
||||
11:num <- copy 94
|
||||
12:num <- copy 95
|
||||
13:num <- copy 96
|
||||
# skip alloc id for address
|
||||
21:num <- copy 30
|
||||
# skip alloc id for payload
|
||||
31:num <- copy 1 # index
|
||||
10:@:num <- put-index 10:@:num, 20:&:num/lookup, 97
|
||||
]
|
||||
+mem: storing 34 in location 3
|
||||
+mem: storing 97 in location 12
|
||||
|
||||
:(scenario put_index_product_error_with_lookup)
|
||||
% Hide_errors = true;
|
||||
def main [
|
||||
10:@:num:3 <- create-array
|
||||
11:num <- copy 14
|
||||
12:num <- copy 15
|
||||
13:num <- copy 16
|
||||
1:&:@:num <- copy 10/unsafe
|
||||
1:&:@:num <- put-index 1:&:@:num/lookup, 1, 34
|
||||
# skip alloc id for 10:&:@:num
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:num <- copy 3 # array length
|
||||
22:num <- copy 94
|
||||
23:num <- copy 95
|
||||
24:num <- copy 96
|
||||
10:&:@:num <- put-index 10:&:@:num/lookup, 1, 34
|
||||
]
|
||||
+error: main: product of 'put-index' must be first ingredient '1:&:@:num/lookup', but got '1:&:@:num'
|
||||
+error: main: product of 'put-index' must be first ingredient '10:&:@:num/lookup', but got '10:&:@:num'
|
||||
|
||||
:(before "End PUT_INDEX Product Checks")
|
||||
reagent/*copy*/ p = inst.products.at(0);
|
||||
|
@ -331,14 +363,14 @@ if (!types_strictly_match(p, i)) {
|
|||
|
||||
:(scenario dilated_reagent_in_static_array)
|
||||
def main [
|
||||
{1: (@ (& num) 3)} <- create-array
|
||||
5:&:num <- new num:type
|
||||
{1: (@ (& num) 3)} <- put-index {1: (@ (& num) 3)}, 0, 5:&:num
|
||||
*5:&:num <- copy 34
|
||||
6:num <- copy *5:&:num
|
||||
{1: (array (& num) 3)} <- create-array
|
||||
10:&:num <- new num:type
|
||||
{1: (array (& num) 3)} <- put-index {1: (array (& num) 3)}, 0, 10:&:num
|
||||
*10:&:num <- copy 94
|
||||
20:num <- copy *10:&:num
|
||||
]
|
||||
+run: creating array of size 4
|
||||
+mem: storing 34 in location 6
|
||||
+run: creating array from 7 locations
|
||||
+mem: storing 94 in location 20
|
||||
|
||||
:(before "Update PUT_INDEX base in Check")
|
||||
if (!canonize_type(base)) break;
|
||||
|
@ -354,14 +386,16 @@ canonize(index);
|
|||
|
||||
:(scenario length_indirect)
|
||||
def main [
|
||||
10:@:num:3 <- create-array
|
||||
11:num <- copy 14
|
||||
12:num <- copy 15
|
||||
13:num <- copy 16
|
||||
1:&:@:num <- copy 10/unsafe
|
||||
2:num <- length 1:&:@:num/lookup
|
||||
# skip alloc id for 10:&:@:num
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:num <- copy 3 # array length
|
||||
22:num <- copy 94
|
||||
23:num <- copy 95
|
||||
24:num <- copy 96
|
||||
30:num <- length 10:&:array:num/lookup
|
||||
]
|
||||
+mem: storing 3 in location 2
|
||||
+mem: storing 3 in location 30
|
||||
|
||||
:(before "Update LENGTH array in Check")
|
||||
if (!canonize_type(array)) break;
|
||||
|
@ -370,32 +404,40 @@ canonize(array);
|
|||
|
||||
:(scenario maybe_convert_indirect)
|
||||
def main [
|
||||
10:number-or-point <- merge 0/number, 34
|
||||
1:&:number-or-point <- copy 10/unsafe
|
||||
2:num, 3:bool <- maybe-convert 1:&:number-or-point/lookup, i:variant
|
||||
# skip alloc id for 10:&:number-or-point
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:number-or-point <- merge 0/number, 94
|
||||
30:num, 31:bool <- maybe-convert 10:&:number-or-point/lookup, i:variant
|
||||
]
|
||||
+mem: storing 1 in location 3
|
||||
+mem: storing 34 in location 2
|
||||
+mem: storing 1 in location 31
|
||||
+mem: storing 94 in location 30
|
||||
|
||||
:(scenario maybe_convert_indirect_2)
|
||||
def main [
|
||||
10:number-or-point <- merge 0/number, 34
|
||||
1:&:number-or-point <- copy 10/unsafe
|
||||
2:&:num <- copy 20/unsafe
|
||||
2:&:num/lookup, 3:bool <- maybe-convert 1:&:number-or-point/lookup, i:variant
|
||||
# skip alloc id for 10:&:number-or-point
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:number-or-point <- merge 0/number, 94
|
||||
# skip alloc id for 30:&:num
|
||||
31:num <- copy 40
|
||||
30:&:num/lookup, 50:bool <- maybe-convert 10:&:number-or-point/lookup, i:variant
|
||||
]
|
||||
+mem: storing 1 in location 3
|
||||
+mem: storing 34 in location 20
|
||||
+mem: storing 1 in location 50
|
||||
+mem: storing 94 in location 41
|
||||
|
||||
:(scenario maybe_convert_indirect_3)
|
||||
def main [
|
||||
10:number-or-point <- merge 0/number, 34
|
||||
1:&:number-or-point <- copy 10/unsafe
|
||||
2:&:bool <- copy 20/unsafe
|
||||
3:num, 2:&:bool/lookup <- maybe-convert 1:&:number-or-point/lookup, i:variant
|
||||
# skip alloc id for 10:&:number-or-point
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:number-or-point <- merge 0/number, 94
|
||||
# skip alloc id for 30:&:bool
|
||||
31:num <- copy 40
|
||||
50:num, 30:&:bool/lookup <- maybe-convert 10:&:number-or-point/lookup, i:variant
|
||||
]
|
||||
+mem: storing 1 in location 20
|
||||
+mem: storing 34 in location 3
|
||||
+mem: storing 1 in location 41
|
||||
+mem: storing 94 in location 50
|
||||
|
||||
:(before "Update MAYBE_CONVERT base in Check")
|
||||
if (!canonize_type(base)) break;
|
||||
|
@ -413,11 +455,13 @@ canonize(status);
|
|||
|
||||
:(scenario merge_exclusive_container_indirect)
|
||||
def main [
|
||||
1:&:number-or-point <- copy 10/unsafe
|
||||
1:&:number-or-point/lookup <- merge 0/number, 34
|
||||
# skip alloc id for 10:&:number-or-point
|
||||
11:num <- copy 20
|
||||
10:&:number-or-point/lookup <- merge 0/number, 34
|
||||
]
|
||||
+mem: storing 0 in location 10
|
||||
+mem: storing 34 in location 11
|
||||
# skip alloc id
|
||||
+mem: storing 0 in location 21
|
||||
+mem: storing 34 in location 22
|
||||
|
||||
:(before "Update size_mismatch Check for MERGE(x)
|
||||
canonize(x);
|
||||
|
@ -426,12 +470,14 @@ canonize(x);
|
|||
|
||||
:(scenario lookup_abbreviation)
|
||||
def main [
|
||||
1:&:num <- copy 10/unsafe
|
||||
10:num <- copy 34
|
||||
3:num <- copy *1:&:num
|
||||
# skip alloc id for 10:&:num
|
||||
11:num <- copy 20
|
||||
# skip alloc id for payload
|
||||
21:num <- copy 94
|
||||
30:num <- copy *10:&:num
|
||||
]
|
||||
+parse: ingredient: {1: ("&" "num"), "lookup": ()}
|
||||
+mem: storing 34 in location 3
|
||||
+parse: ingredient: {10: ("&" "num"), "lookup": ()}
|
||||
+mem: storing 94 in location 30
|
||||
|
||||
:(before "End Parsing reagent")
|
||||
{
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
:(scenario new_reclaim)
|
||||
def main [
|
||||
1:&:num <- new number:type
|
||||
2:num <- deaddress 1:&:num # because 1 will get reset during abandon below
|
||||
abandon 1:&:num
|
||||
3:&:num <- new number:type # must be same size as abandoned memory to reuse
|
||||
4:num <- deaddress 3:&:num
|
||||
5:bool <- equal 2:num, 4:num
|
||||
10:&:num <- new number:type
|
||||
20:num <- deaddress 10:&:num
|
||||
abandon 10:&:num
|
||||
30:&:num <- new number:type # must be same size as abandoned memory to reuse
|
||||
40:num <- deaddress 30:&:num
|
||||
50:bool <- equal 20:num, 40:num
|
||||
]
|
||||
# both allocations should have returned the same address
|
||||
+mem: storing 1 in location 5
|
||||
+mem: storing 1 in location 50
|
||||
|
||||
//: When abandoning addresses we'll save them to a 'free list', segregated by size.
|
||||
|
||||
|
@ -39,7 +39,7 @@ case ABANDON: {
|
|||
for (int i = 0; i < SIZE(current_instruction().ingredients); ++i) {
|
||||
reagent/*copy*/ ingredient = current_instruction().ingredients.at(i);
|
||||
canonize(ingredient);
|
||||
abandon(get_or_insert(Memory, ingredient.value), payload_size(ingredient));
|
||||
abandon(get_or_insert(Memory, ingredient.value+/*skip alloc id*/1), payload_size(ingredient));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ void abandon(int address, int payload_size) {
|
|||
int payload_size(reagent/*copy*/ x) {
|
||||
x.properties.push_back(pair<string, string_tree*>("lookup", NULL));
|
||||
lookup_memory_core(x, /*check_for_null*/false);
|
||||
return size_of(x);
|
||||
return size_of(x)+/*alloc id*/1;
|
||||
}
|
||||
|
||||
:(after "Allocate Special-cases")
|
||||
|
@ -91,12 +91,12 @@ def main [
|
|||
|
||||
:(scenario new_reclaim_array)
|
||||
def main [
|
||||
1:&:@:num <- new number:type, 2
|
||||
2:num <- deaddress 1:&:@:num
|
||||
abandon 1:&:@:num
|
||||
3:&:@:num <- new number:type, 2 # same size
|
||||
4:num <- deaddress 3:&:@:num
|
||||
5:bool <- equal 2:num, 4:num
|
||||
10:&:@:num <- new number:type, 2
|
||||
20:num <- deaddress 10:&:@:num
|
||||
abandon 10:&:@:num
|
||||
30:&:@:num <- new number:type, 2 # same size
|
||||
40:num <- deaddress 30:&:@:num
|
||||
50:bool <- equal 20:num, 40:num
|
||||
]
|
||||
# both calls to new returned identical addresses
|
||||
+mem: storing 1 in location 5
|
||||
+mem: storing 1 in location 50
|
||||
|
|
|
@ -6,21 +6,21 @@ put(Type_abbreviations, "text", new_type_tree("&:@:character"));
|
|||
|
||||
:(scenario new_string)
|
||||
def main [
|
||||
1:text <- new [abc def]
|
||||
2:char <- index *1:text, 5
|
||||
10:text <- new [abc def]
|
||||
20:char <- index *10:text, 5
|
||||
]
|
||||
# number code for 'e'
|
||||
+mem: storing 101 in location 2
|
||||
+mem: storing 101 in location 20
|
||||
|
||||
:(scenario new_string_handles_unicode)
|
||||
def main [
|
||||
1:text <- new [a«c]
|
||||
2:num <- length *1:text
|
||||
3:char <- index *1:text, 1
|
||||
10:text <- new [a«c]
|
||||
20:num <- length *10:text
|
||||
21:char <- index *10:text, 1
|
||||
]
|
||||
+mem: storing 3 in location 2
|
||||
+mem: storing 3 in location 20
|
||||
# unicode for '«'
|
||||
+mem: storing 171 in location 3
|
||||
+mem: storing 171 in location 21
|
||||
|
||||
:(before "End NEW Check Special-cases")
|
||||
if (is_literal_text(inst.ingredients.at(0))) break;
|
||||
|
@ -29,6 +29,7 @@ if (inst.name == "new" && !inst.ingredients.empty() && is_literal_text(inst.ingr
|
|||
:(after "case NEW" following "Primitive Recipe Implementations")
|
||||
if (is_literal_text(current_instruction().ingredients.at(0))) {
|
||||
products.resize(1);
|
||||
products.at(0).push_back(/*alloc id*/0);
|
||||
products.at(0).push_back(new_mu_text(current_instruction().ingredients.at(0).name));
|
||||
trace("mem") << "new string alloc: " << products.at(0).at(0) << end();
|
||||
break;
|
||||
|
@ -40,8 +41,9 @@ int new_mu_text(const string& contents) {
|
|||
int string_length = unicode_length(contents);
|
||||
//? Total_alloc += string_length+1;
|
||||
//? ++Num_alloc;
|
||||
int result = allocate(string_length+/*array length*/1);
|
||||
int result = allocate(/*array length*/1 + string_length);
|
||||
int curr_address = result;
|
||||
++curr_address; // skip alloc id
|
||||
trace("mem") << "storing string length " << string_length << " in location " << curr_address << end();
|
||||
put(Memory, curr_address, string_length);
|
||||
++curr_address; // skip length
|
||||
|
@ -62,16 +64,16 @@ int new_mu_text(const string& contents) {
|
|||
|
||||
//: a new kind of typo
|
||||
|
||||
:(scenario string_literal_without_instruction)
|
||||
:(scenario literal_text_without_instruction)
|
||||
% Hide_errors = true;
|
||||
def main [
|
||||
[abc]
|
||||
]
|
||||
+error: main: instruction '[abc]' has no recipe in '[abc]'
|
||||
|
||||
//: stash recognizes strings
|
||||
//: stash recognizes texts
|
||||
|
||||
:(scenario stash_string)
|
||||
:(scenario stash_text)
|
||||
def main [
|
||||
1:text <- new [abc]
|
||||
stash [foo:], 1:text
|
||||
|
@ -80,30 +82,29 @@ def main [
|
|||
|
||||
:(before "End inspect Special-cases(r, data)")
|
||||
if (is_mu_text(r)) {
|
||||
assert(scalar(data));
|
||||
return read_mu_text(data.at(0));
|
||||
return read_mu_text(data.at(/*skip alloc id*/1));
|
||||
}
|
||||
|
||||
:(before "End $print Special-cases")
|
||||
else if (is_mu_text(current_instruction().ingredients.at(i))) {
|
||||
cout << read_mu_text(ingredients.at(i).at(0));
|
||||
cout << read_mu_text(ingredients.at(i).at(/*skip alloc id*/1));
|
||||
}
|
||||
|
||||
:(scenario unicode_string)
|
||||
:(scenario unicode_text)
|
||||
def main [
|
||||
1:text <- new [♠]
|
||||
stash [foo:], 1:text
|
||||
]
|
||||
+app: foo: ♠
|
||||
|
||||
:(scenario stash_space_after_string)
|
||||
:(scenario stash_space_after_text)
|
||||
def main [
|
||||
1:text <- new [abc]
|
||||
stash 1:text, [foo]
|
||||
]
|
||||
+app: abc foo
|
||||
|
||||
:(scenario stash_string_as_array)
|
||||
:(scenario stash_text_as_array)
|
||||
def main [
|
||||
1:text <- new [abc]
|
||||
stash *1:text
|
||||
|
@ -114,15 +115,15 @@ def main [
|
|||
:(before "End Preprocess is_mu_text(reagent x)")
|
||||
if (!canonize_type(x)) return false;
|
||||
|
||||
//: Allocate more to routine when initializing a literal string
|
||||
:(scenario new_string_overflow)
|
||||
% Initial_memory_per_routine = 2;
|
||||
//: Allocate more to routine when initializing a literal text
|
||||
:(scenario new_text_overflow)
|
||||
% Initial_memory_per_routine = 3;
|
||||
def main [
|
||||
1:&:num/raw <- new number:type
|
||||
2:text/raw <- new [a] # not enough room in initial page, if you take the array length into account
|
||||
10:&:num/raw <- new number:type
|
||||
20:text/raw <- new [a] # not enough room in initial page, if you take the array length into account
|
||||
]
|
||||
+new: routine allocated memory from 1000 to 1002
|
||||
+new: routine allocated memory from 1002 to 1004
|
||||
+new: routine allocated memory from 1000 to 1003
|
||||
+new: routine allocated memory from 1003 to 1006
|
||||
|
||||
//: helpers
|
||||
:(code)
|
||||
|
@ -140,9 +141,9 @@ int unicode_length(const string& s) {
|
|||
|
||||
string read_mu_text(int address) {
|
||||
if (address == 0) return "";
|
||||
int length = get_or_insert(Memory, address);
|
||||
int length = get_or_insert(Memory, address+/*alloc id*/1);
|
||||
if (length == 0) return "";
|
||||
return read_mu_characters(address+1, length);
|
||||
return read_mu_characters(address+/*alloc id*/1+/*length*/1, length);
|
||||
}
|
||||
|
||||
string read_mu_characters(int start, int length) {
|
||||
|
@ -156,13 +157,21 @@ string read_mu_characters(int start, int length) {
|
|||
|
||||
//: assert: perform sanity checks at runtime
|
||||
|
||||
:(scenario assert)
|
||||
:(scenario assert_literal)
|
||||
% Hide_errors = true; // '%' lines insert arbitrary C code into tests before calling 'run' with the lines below. Must be immediately after :(scenario) line.
|
||||
def main [
|
||||
assert 0, [this is an assert in Mu]
|
||||
]
|
||||
+error: this is an assert in Mu
|
||||
|
||||
:(scenario assert)
|
||||
% Hide_errors = true; // '%' lines insert arbitrary C code into tests before calling 'run' with the lines below. Must be immediately after :(scenario) line.
|
||||
def main [
|
||||
1:text <- new [this is an assert in Mu]
|
||||
assert 0, 1:text
|
||||
]
|
||||
+error: this is an assert in Mu
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
ASSERT,
|
||||
:(before "End Primitive Recipe Numbers")
|
||||
|
@ -173,8 +182,8 @@ case ASSERT: {
|
|||
raise << maybe(get(Recipe, r).name) << "'assert' takes exactly two ingredients rather than '" << to_original_string(inst) << "'\n" << end();
|
||||
break;
|
||||
}
|
||||
if (!is_mu_scalar(inst.ingredients.at(0))) {
|
||||
raise << maybe(get(Recipe, r).name) << "'assert' requires a boolean for its first ingredient, but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
|
||||
if (!is_mu_address(inst.ingredients.at(0)) && !is_mu_scalar(inst.ingredients.at(0))) {
|
||||
raise << maybe(get(Recipe, r).name) << "'assert' requires a scalar or address for its first ingredient, but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
|
||||
break;
|
||||
}
|
||||
if (!is_literal_text(inst.ingredients.at(1)) && !is_mu_text(inst.ingredients.at(1))) {
|
||||
|
@ -185,11 +194,11 @@ case ASSERT: {
|
|||
}
|
||||
:(before "End Primitive Recipe Implementations")
|
||||
case ASSERT: {
|
||||
if (!ingredients.at(0).at(0)) {
|
||||
if (!scalar_ingredient(ingredients, 0)) {
|
||||
if (is_literal_text(current_instruction().ingredients.at(1)))
|
||||
raise << current_instruction().ingredients.at(1).name << '\n' << end();
|
||||
else
|
||||
raise << read_mu_text(ingredients.at(1).at(0)) << '\n' << end();
|
||||
raise << read_mu_text(ingredients.at(1).at(/*skip alloc id*/1)) << '\n' << end();
|
||||
if (!Hide_errors) exit(1);
|
||||
}
|
||||
break;
|
||||
|
|
48
042name.cc
48
042name.cc
|
@ -6,8 +6,8 @@
|
|||
def main [
|
||||
x:num <- copy 0
|
||||
]
|
||||
+name: assign x 1
|
||||
+mem: storing 0 in location 1
|
||||
+name: assign x 2
|
||||
+mem: storing 0 in location 2
|
||||
|
||||
:(scenarios transform)
|
||||
:(scenario transform_names_fails_on_use_before_define)
|
||||
|
@ -42,7 +42,7 @@ void transform_names(const recipe_ordinal r) {
|
|||
map<string, int>& names = Name[r];
|
||||
// store the indices 'used' so far in the map
|
||||
int& curr_idx = names[""];
|
||||
++curr_idx; // avoid using index 0, benign skip in some other cases
|
||||
curr_idx = 2; // reserve indices 0 and 1 for the chaining slot in a later layer
|
||||
for (int i = 0; i < SIZE(caller.steps); ++i) {
|
||||
instruction& inst = caller.steps.at(i);
|
||||
// End transform_names(inst) Special-cases
|
||||
|
@ -135,13 +135,21 @@ bool is_compound_type_starting_with(const type_tree* type, const string& expecte
|
|||
return type->left->value == get(Type_ordinal, expected_name);
|
||||
}
|
||||
|
||||
int find_element_name(const type_ordinal t, const string& name, const string& recipe_name) {
|
||||
int find_element_offset(const type_ordinal t, const string& name, const string& recipe_name) {
|
||||
const type_info& container = get(Type, t);
|
||||
for (int i = 0; i < SIZE(container.elements); ++i)
|
||||
if (container.elements.at(i).name == name) return i;
|
||||
raise << maybe(recipe_name) << "unknown element '" << name << "' in container '" << get(Type, t).name << "'\n" << end();
|
||||
return -1;
|
||||
}
|
||||
int find_element_location(int base_address, const string& name, const type_tree* type, const string& recipe_name) {
|
||||
int offset = find_element_offset(get_base_type(type)->value, name, recipe_name);
|
||||
if (offset == -1) return offset;
|
||||
int result = base_address;
|
||||
for (int i = 0; i < offset; ++i)
|
||||
result += size_of(element_type(type, i));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_numeric_location(const reagent& x) {
|
||||
if (is_literal(x)) return false;
|
||||
|
@ -165,31 +173,35 @@ bool is_special_name(const string& s) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool is_raw(const reagent& r) {
|
||||
return has_property(r, "raw");
|
||||
}
|
||||
|
||||
:(scenario transform_names_supports_containers)
|
||||
def main [
|
||||
x:point <- merge 34, 35
|
||||
y:num <- copy 3
|
||||
]
|
||||
+name: assign x 1
|
||||
+name: assign x 2
|
||||
# skip location 2 because x occupies two locations
|
||||
+name: assign y 3
|
||||
+name: assign y 4
|
||||
|
||||
:(scenario transform_names_supports_static_arrays)
|
||||
def main [
|
||||
x:@:num:3 <- create-array
|
||||
y:num <- copy 3
|
||||
]
|
||||
+name: assign x 1
|
||||
+name: assign x 2
|
||||
# skip locations 2, 3, 4 because x occupies four locations
|
||||
+name: assign y 5
|
||||
+name: assign y 6
|
||||
|
||||
:(scenario transform_names_passes_dummy)
|
||||
# _ is just a dummy result that never gets consumed
|
||||
def main [
|
||||
_, x:num <- copy 0, 1
|
||||
]
|
||||
+name: assign x 1
|
||||
-name: assign _ 1
|
||||
+name: assign x 2
|
||||
-name: assign _ 2
|
||||
|
||||
//: an escape hatch to suppress name conversion that we'll use later
|
||||
:(scenarios run)
|
||||
|
@ -198,7 +210,7 @@ def main [
|
|||
def main [
|
||||
x:num/raw <- copy 0
|
||||
]
|
||||
-name: assign x 1
|
||||
-name: assign x 2
|
||||
+error: can't write to location 0 in 'x:num/raw <- copy 0'
|
||||
|
||||
:(scenarios transform)
|
||||
|
@ -266,7 +278,7 @@ if (inst.name == "get" || inst.name == "get-location" || inst.name == "put") {
|
|||
// since first non-address in base type must be a container, we don't have to canonize
|
||||
type_ordinal base_type = skip_addresses(inst.ingredients.at(0).type);
|
||||
if (contains_key(Type, base_type)) { // otherwise we'll raise an error elsewhere
|
||||
inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name, get(Recipe, r).name));
|
||||
inst.ingredients.at(1).set_value(find_element_offset(base_type, inst.ingredients.at(1).name, get(Recipe, r).name));
|
||||
trace(9993, "name") << "element " << inst.ingredients.at(1).name << " of type " << get(Type, base_type).name << " is at offset " << no_scientific(inst.ingredients.at(1).value) << end();
|
||||
}
|
||||
}
|
||||
|
@ -279,15 +291,13 @@ def main [
|
|||
]
|
||||
+error: main: missing type for 'a' in 'get a, x:offset'
|
||||
|
||||
//: this test is actually illegal so can't call run
|
||||
:(scenarios transform)
|
||||
:(scenario transform_names_handles_containers)
|
||||
def main [
|
||||
a:point <- copy 0/unsafe
|
||||
b:num <- copy 0/unsafe
|
||||
a:point <- merge 0, 0
|
||||
b:num <- copy 0
|
||||
]
|
||||
+name: assign a 1
|
||||
+name: assign b 3
|
||||
+name: assign a 2
|
||||
+name: assign b 4
|
||||
|
||||
//:: Support variant names for exclusive containers in 'maybe-convert'.
|
||||
|
||||
|
@ -316,7 +326,7 @@ if (inst.name == "maybe-convert") {
|
|||
// since first non-address in base type must be an exclusive container, we don't have to canonize
|
||||
type_ordinal base_type = skip_addresses(inst.ingredients.at(0).type);
|
||||
if (contains_key(Type, base_type)) { // otherwise we'll raise an error elsewhere
|
||||
inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name, get(Recipe, r).name));
|
||||
inst.ingredients.at(1).set_value(find_element_offset(base_type, inst.ingredients.at(1).name, get(Recipe, r).name));
|
||||
trace(9993, "name") << "variant " << inst.ingredients.at(1).name << " of type " << get(Type, base_type).name << " has tag " << no_scientific(inst.ingredients.at(1).value) << end();
|
||||
}
|
||||
}
|
||||
|
|
124
043space.cc
124
043space.cc
|
@ -7,44 +7,65 @@
|
|||
//:
|
||||
//: Warning: messing with 'default-space' can corrupt memory. Don't share
|
||||
//: default-space between recipes. Later we'll see how to chain spaces safely.
|
||||
//:
|
||||
//: Tests in this layer can write to a location as part of one type, and read
|
||||
//: it as part of another. This is unsafe and insecure, and we'll stop doing
|
||||
//: this once we switch to variable names.
|
||||
|
||||
//: Under the hood, a space is an array of locations in memory.
|
||||
:(before "End Mu Types Initialization")
|
||||
put(Type_abbreviations, "space", new_type_tree("address:array:location"));
|
||||
|
||||
:(scenario set_default_space)
|
||||
# if default-space is 10, and if an array of 5 locals lies from location 12 to 16 (inclusive),
|
||||
# then local 0 is really location 12, local 1 is really location 13, and so on.
|
||||
def main [
|
||||
# pretend address:array:location; in practice we'll use 'new'
|
||||
10:num <- copy 5 # length
|
||||
default-space:space <- copy 10/unsafe
|
||||
1:num <- copy 23
|
||||
# prepare default-space address
|
||||
10:num/alloc-id, 11:num <- copy 0, 1000
|
||||
# prepare default-space payload
|
||||
1000:num <- copy 0 # alloc id of payload
|
||||
1001:num <- copy 5 # length
|
||||
# actual start of this recipe
|
||||
default-space:space <- copy 10:&:@:location
|
||||
# if default-space is 1000, then:
|
||||
# 1000: alloc id
|
||||
# 1001: array size
|
||||
# 1002: location 0 (space for the chaining slot; described later; often unused)
|
||||
# 1003: location 1 (space for the chaining slot; described later; often unused)
|
||||
# 1004: local 2 (assuming it is a scalar)
|
||||
2:num <- copy 93
|
||||
]
|
||||
+mem: storing 23 in location 12
|
||||
+mem: storing 93 in location 1004
|
||||
|
||||
:(scenario lookup_sidesteps_default_space)
|
||||
def main [
|
||||
# pretend pointer from outside
|
||||
2000:num <- copy 34
|
||||
# pretend address:array:location; in practice we'll use 'new'
|
||||
1000:num <- copy 5 # length
|
||||
# prepare default-space address
|
||||
10:num/alloc-id, 11:num <- copy 0, 1000
|
||||
# prepare default-space payload
|
||||
1000:num <- copy 0 # alloc id of payload
|
||||
1001:num <- copy 5 # length
|
||||
# prepare payload outside the local scope
|
||||
2000:num/alloc-id, 2001:num <- copy 0, 34
|
||||
# actual start of this recipe
|
||||
default-space:space <- copy 1000/unsafe
|
||||
1:&:num <- copy 2000/unsafe # even local variables always contain raw addresses
|
||||
8:num/raw <- copy *1:&:num
|
||||
default-space:space <- copy 10:&:@:location
|
||||
# a local address
|
||||
2:num, 3:num <- copy 0, 2000
|
||||
20:num/raw <- copy *2:&:num
|
||||
]
|
||||
+mem: storing 34 in location 8
|
||||
+mem: storing 2000 in location 1005
|
||||
+mem: storing 34 in location 20
|
||||
|
||||
//: precondition: disable name conversion for 'default-space'
|
||||
|
||||
:(scenarios transform)
|
||||
:(scenario convert_names_passes_default_space)
|
||||
% Hide_errors = true;
|
||||
def main [
|
||||
default-space:num, x:num <- copy 0, 1
|
||||
default-space:num <- copy 0
|
||||
x:num <- copy 1
|
||||
]
|
||||
+name: assign x 1
|
||||
+name: assign x 2
|
||||
-name: assign default-space 1
|
||||
-name: assign default-space 2
|
||||
:(scenarios run)
|
||||
|
||||
:(before "End is_disqualified Special-cases")
|
||||
if (x.name == "default-space")
|
||||
|
@ -74,7 +95,7 @@ void absolutize(reagent& x) {
|
|||
|
||||
//: hook replaced in a later layer
|
||||
int space_base(const reagent& x) {
|
||||
return current_call().default_space ? current_call().default_space : 0;
|
||||
return current_call().default_space ? (current_call().default_space + /*skip alloc id*/1) : 0;
|
||||
}
|
||||
|
||||
int address(int offset, int base) {
|
||||
|
@ -95,9 +116,11 @@ int address(int offset, int base) {
|
|||
|
||||
:(after "Begin Preprocess write_memory(x, data)")
|
||||
if (x.name == "default-space") {
|
||||
if (!scalar(data) || !is_mu_space(x))
|
||||
if (!is_mu_space(x))
|
||||
raise << maybe(current_recipe_name()) << "'default-space' should be of type address:array:location, but is " << to_string(x.type) << '\n' << end();
|
||||
current_call().default_space = data.at(0);
|
||||
if (SIZE(data) != 2)
|
||||
raise << maybe(current_recipe_name()) << "'default-space' getting data from non-address\n" << end();
|
||||
current_call().default_space = data.at(/*skip alloc id*/1);
|
||||
return;
|
||||
}
|
||||
:(code)
|
||||
|
@ -112,14 +135,21 @@ bool is_mu_space(reagent/*copy*/ x) {
|
|||
|
||||
:(scenario get_default_space)
|
||||
def main [
|
||||
default-space:space <- copy 10/unsafe
|
||||
1:space/raw <- copy default-space:space
|
||||
# prepare default-space address
|
||||
10:num/alloc-id, 11:num <- copy 0, 1000
|
||||
# prepare default-space payload
|
||||
1000:num <- copy 0 # alloc id of payload
|
||||
1001:num <- copy 5 # length
|
||||
# actual start of this recipe
|
||||
default-space:space <- copy 10:space
|
||||
2:space/raw <- copy default-space:space
|
||||
]
|
||||
+mem: storing 10 in location 1
|
||||
+mem: storing 1000 in location 3
|
||||
|
||||
:(after "Begin Preprocess read_memory(x)")
|
||||
if (x.name == "default-space") {
|
||||
vector<double> result;
|
||||
result.push_back(/*alloc id*/0);
|
||||
result.push_back(current_call().default_space);
|
||||
return result;
|
||||
}
|
||||
|
@ -128,17 +158,20 @@ if (x.name == "default-space") {
|
|||
|
||||
:(scenario lookup_sidesteps_default_space_in_get)
|
||||
def main [
|
||||
# pretend pointer to container from outside
|
||||
2000:num <- copy 34
|
||||
2001:num <- copy 35
|
||||
# pretend address:array:location; in practice we'll use 'new'
|
||||
1000:num <- copy 5 # length
|
||||
# prepare default-space address
|
||||
10:num/alloc-id, 11:num <- copy 0, 1000
|
||||
# prepare default-space payload
|
||||
1000:num <- copy 0 # alloc id of payload
|
||||
1001:num <- copy 5 # length
|
||||
# prepare payload outside the local scope
|
||||
2000:num/alloc-id, 2001:num/x, 2002:num/y <- copy 0, 34, 35
|
||||
# actual start of this recipe
|
||||
default-space:space <- copy 1000/unsafe
|
||||
1:&:point <- copy 2000/unsafe
|
||||
9:num/raw <- get *1:&:point, 1:offset
|
||||
default-space:space <- copy 10:space
|
||||
# a local address
|
||||
2:num, 3:num <- copy 0, 2000
|
||||
3000:num/raw <- get *2:&:point, 1:offset
|
||||
]
|
||||
+mem: storing 35 in location 9
|
||||
+mem: storing 35 in location 3000
|
||||
|
||||
:(before "Read element" following "case GET:")
|
||||
element.properties.push_back(pair<string, string_tree*>("raw", NULL));
|
||||
|
@ -147,18 +180,21 @@ element.properties.push_back(pair<string, string_tree*>("raw", NULL));
|
|||
|
||||
:(scenario lookup_sidesteps_default_space_in_index)
|
||||
def main [
|
||||
# pretend pointer to array from outside
|
||||
2000:num <- copy 2 # length
|
||||
2001:num <- copy 34
|
||||
2002:num <- copy 35
|
||||
# pretend address:array:location; in practice we'll use 'new'
|
||||
1000:num <- copy 5 # length
|
||||
# prepare default-space address
|
||||
10:num/alloc-id, 11:num <- copy 0, 1000
|
||||
# prepare default-space payload
|
||||
1000:num <- copy 0 # alloc id of payload
|
||||
1001:num <- copy 5 # length
|
||||
# prepare an array address
|
||||
20:num/alloc-id, 21:num <- copy 0, 2000
|
||||
# prepare an array payload
|
||||
2000:num/alloc-id, 2001:num/length, 2002:num/index:0, 2003:num/index:1 <- copy 0, 2, 34, 35
|
||||
# actual start of this recipe
|
||||
default-space:space <- copy 1000/unsafe
|
||||
1:&:@:num <- copy 2000/unsafe
|
||||
9:num/raw <- index *1:&:@:num, 1
|
||||
default-space:space <- copy 10:&:@:location
|
||||
1:&:@:num <- copy 20:&:@:num/raw
|
||||
3000:num/raw <- index *1:&:@:num, 1
|
||||
]
|
||||
+mem: storing 35 in location 9
|
||||
+mem: storing 35 in location 3000
|
||||
|
||||
:(before "Read element" following "case INDEX:")
|
||||
element.properties.push_back(pair<string, string_tree*>("raw", NULL));
|
||||
|
@ -172,8 +208,8 @@ def main [
|
|||
x:num <- copy 0
|
||||
y:num <- copy 3
|
||||
]
|
||||
# allocate space for x and y, as well as the chaining slot at 0
|
||||
+mem: array length is 3
|
||||
# allocate space for x and y, as well as the chaining slot at indices 0 and 1
|
||||
+mem: array length is 4
|
||||
|
||||
:(before "End is_disqualified Special-cases")
|
||||
if (x.name == "number-of-locals")
|
||||
|
|
|
@ -5,27 +5,35 @@
|
|||
//: (Surrounding spaces are like lexical scopes in other languages.)
|
||||
|
||||
:(scenario surrounding_space)
|
||||
# location 1 in space 1 refers to the space surrounding the default space, here 20.
|
||||
# location 2 in space 1 (remember that locations 0 and 1 are reserved in all
|
||||
# spaces) refers to the space surrounding the default space, here 20.
|
||||
def main [
|
||||
# pretend address:array:location; in practice we'll use 'new'
|
||||
10:num <- copy 5 # length
|
||||
# pretend address:array:location; in practice we'll use 'new"
|
||||
20:num <- copy 5 # length
|
||||
# prepare default-space address
|
||||
10:num/alloc-id, 11:num <- copy 0, 1000
|
||||
# prepare default-space payload
|
||||
1000:num <- copy 0 # alloc id of payload
|
||||
1001:num <- copy 5 # length
|
||||
# prepare address of chained space
|
||||
20:num/alloc-id, 21:num <- copy 0, 2000
|
||||
# prepare payload of chained space
|
||||
2000:num <- copy 0 # alloc id of payload
|
||||
2001:num <- copy 5 # length
|
||||
# actual start of this recipe
|
||||
default-space:space <- copy 10/unsafe
|
||||
default-space:space <- copy 10:space
|
||||
#: later layers will explain the /names: property
|
||||
0:space/names:dummy <- copy 20/unsafe
|
||||
1:num <- copy 32
|
||||
1:num/space:1 <- copy 33
|
||||
0:space/names:dummy <- copy 20:space/raw
|
||||
2:num <- copy 94
|
||||
2:num/space:1 <- copy 95
|
||||
]
|
||||
def dummy [ # just for the /names: property above
|
||||
]
|
||||
# chain space: 10 + (length) 1
|
||||
+mem: storing 20 in location 11
|
||||
# store to default space: 10 + (skip length) 1 + (index) 1
|
||||
+mem: storing 32 in location 12
|
||||
# store to chained space: (contents of location 12) 20 + (length) 1 + (index) 1
|
||||
+mem: storing 33 in location 22
|
||||
# chain space: 1000 + (alloc id) 1 + (length) 1
|
||||
+mem: storing 0 in location 1002
|
||||
+mem: storing 2000 in location 1003
|
||||
# store to default space: 1000 + (alloc id) 1 + (length) 1 + (index) 2
|
||||
+mem: storing 94 in location 1004
|
||||
# store to chained space: (contents of location 1003) 2000 + (alloc id) 1 + (length) 1 + (index) 2
|
||||
+mem: storing 95 in location 2004
|
||||
|
||||
//: If you think of a space as a collection of variables with a common
|
||||
//: lifetime, surrounding allows managing shorter lifetimes inside a longer
|
||||
|
@ -33,14 +41,16 @@ def dummy [ # just for the /names: property above
|
|||
|
||||
:(replace{} "int space_base(const reagent& x)")
|
||||
int space_base(const reagent& x) {
|
||||
int base = current_call().default_space ? current_call().default_space : 0;
|
||||
int base = current_call().default_space ? (current_call().default_space+/*skip alloc id*/1) : 0;
|
||||
return space_base(x, space_index(x), base);
|
||||
}
|
||||
|
||||
int space_base(const reagent& x, int space_index, int base) {
|
||||
if (space_index == 0)
|
||||
return base;
|
||||
return space_base(x, space_index-1, get_or_insert(Memory, base+/*skip length*/1));
|
||||
double chained_space_address = base+/*skip length*/1+/*skip alloc id of chaining slot*/1;
|
||||
double chained_space_base = get_or_insert(Memory, chained_space_address) + /*skip alloc id of chained space*/1;
|
||||
return space_base(x, space_index-1, chained_space_base);
|
||||
}
|
||||
|
||||
int space_index(const reagent& x) {
|
||||
|
|
|
@ -9,14 +9,14 @@
|
|||
:(scenario closure)
|
||||
def main [
|
||||
default-space:space <- new location:type, 30
|
||||
1:space/names:new-counter <- new-counter
|
||||
2:num/raw <- increment-counter 1:space/names:new-counter
|
||||
3:num/raw <- increment-counter 1:space/names:new-counter
|
||||
2:space/names:new-counter <- new-counter
|
||||
10:num/raw <- increment-counter 2:space/names:new-counter
|
||||
11:num/raw <- increment-counter 2:space/names:new-counter
|
||||
]
|
||||
def new-counter [
|
||||
default-space:space <- new location:type, 30
|
||||
x:num <- copy 23
|
||||
y:num <- copy 3 # variable that will be incremented
|
||||
y:num <- copy 13 # variable that will be incremented
|
||||
return default-space:space
|
||||
]
|
||||
def increment-counter [
|
||||
|
@ -27,7 +27,7 @@ def increment-counter [
|
|||
return y:num/space:1
|
||||
]
|
||||
+name: lexically surrounding space for recipe increment-counter comes from new-counter
|
||||
+mem: storing 5 in location 3
|
||||
+mem: storing 15 in location 11
|
||||
|
||||
//: To make this work, compute the recipe that provides names for the
|
||||
//: surrounding space of each recipe.
|
||||
|
@ -165,6 +165,6 @@ def use-scope [
|
|||
]
|
||||
def main [
|
||||
1:space/raw <- new-scope
|
||||
2:num/raw <- use-scope 1:space/raw
|
||||
3:num/raw <- use-scope 1:space/raw
|
||||
]
|
||||
+mem: storing 34 in location 2
|
||||
+mem: storing 34 in location 3
|
||||
|
|
|
@ -87,27 +87,27 @@ void check_type(set<reagent>& known, const reagent& x, const recipe& caller) {
|
|||
|
||||
:(scenario transform_fills_in_missing_types)
|
||||
def main [
|
||||
x:num <- copy 1
|
||||
x:num <- copy 11
|
||||
y:num <- add x, 1
|
||||
]
|
||||
# x is in location 1, y in location 2
|
||||
+mem: storing 2 in location 2
|
||||
# x is in location 2, y in location 3
|
||||
+mem: storing 12 in location 3
|
||||
|
||||
:(scenario transform_fills_in_missing_types_in_product)
|
||||
def main [
|
||||
x:num <- copy 1
|
||||
x <- copy 2
|
||||
x:num <- copy 11
|
||||
x <- copy 12
|
||||
]
|
||||
# x is in location 1
|
||||
+mem: storing 2 in location 1
|
||||
# x is in location 2
|
||||
+mem: storing 12 in location 2
|
||||
|
||||
:(scenario transform_fills_in_missing_types_in_product_and_ingredient)
|
||||
def main [
|
||||
x:num <- copy 1
|
||||
x:num <- copy 11
|
||||
x <- add x, 1
|
||||
]
|
||||
# x is in location 1
|
||||
+mem: storing 2 in location 1
|
||||
# x is in location 2
|
||||
+mem: storing 12 in location 2
|
||||
|
||||
:(scenario transform_fills_in_missing_label_type)
|
||||
def main [
|
||||
|
|
|
@ -67,7 +67,7 @@ container foo:_a:_b [
|
|||
def main [
|
||||
1:text <- new [abc]
|
||||
# compound types for type ingredients
|
||||
{2: (foo number (address array character))} <- merge 34/x, 1:text/y
|
||||
{3: (foo number (address array character))} <- merge 34/x, 1:text/y
|
||||
]
|
||||
$error: 0
|
||||
|
||||
|
@ -82,7 +82,7 @@ container bar:_a:_b [
|
|||
]
|
||||
def main [
|
||||
1:text <- new [abc]
|
||||
2:bar:num:@:char <- merge 34/x, 1:text/y
|
||||
3:bar:num:@:char <- merge 34/x, 1:text/y
|
||||
]
|
||||
$error: 0
|
||||
|
||||
|
@ -247,9 +247,9 @@ container foo:_t [
|
|||
]
|
||||
def main [
|
||||
1:foo:point <- merge 14, 15, 16
|
||||
2:num <- get 1:foo:point, y:offset
|
||||
4:num <- get 1:foo:point, y:offset
|
||||
]
|
||||
+mem: storing 16 in location 2
|
||||
+mem: storing 16 in location 4
|
||||
|
||||
:(scenario get_on_shape_shifting_container_2)
|
||||
container foo:_t [
|
||||
|
@ -258,10 +258,10 @@ container foo:_t [
|
|||
]
|
||||
def main [
|
||||
1:foo:point <- merge 14, 15, 16
|
||||
2:point <- get 1:foo:point, x:offset
|
||||
4:point <- get 1:foo:point, x:offset
|
||||
]
|
||||
+mem: storing 14 in location 2
|
||||
+mem: storing 15 in location 3
|
||||
+mem: storing 14 in location 4
|
||||
+mem: storing 15 in location 5
|
||||
|
||||
:(scenario get_on_shape_shifting_container_3)
|
||||
container foo:_t [
|
||||
|
@ -269,10 +269,12 @@ container foo:_t [
|
|||
y:num
|
||||
]
|
||||
def main [
|
||||
1:foo:&:point <- merge 34/unsafe, 48
|
||||
3:&:point <- get 1:foo:&:point, x:offset
|
||||
1:num/alloc-id, 2:num <- copy 0, 34
|
||||
3:foo:&:point <- merge 1:&:point, 48
|
||||
6:&:point <- get 1:foo:&:point, x:offset
|
||||
]
|
||||
+mem: storing 34 in location 3
|
||||
+mem: storing 0 in location 6
|
||||
+mem: storing 34 in location 7
|
||||
|
||||
:(scenario get_on_shape_shifting_container_inside_container)
|
||||
container foo:_t [
|
||||
|
@ -285,9 +287,9 @@ container bar [
|
|||
]
|
||||
def main [
|
||||
1:bar <- merge 14, 15, 16, 17
|
||||
2:num <- get 1:bar, 1:offset
|
||||
5:num <- get 1:bar, 1:offset
|
||||
]
|
||||
+mem: storing 17 in location 2
|
||||
+mem: storing 17 in location 5
|
||||
|
||||
:(scenario get_on_complex_shape_shifting_container)
|
||||
container foo:_a:_b [
|
||||
|
@ -296,11 +298,11 @@ container foo:_a:_b [
|
|||
]
|
||||
def main [
|
||||
1:text <- new [abc]
|
||||
{2: (foo number (address array character))} <- merge 34/x, 1:text/y
|
||||
3:text <- get {2: (foo number (address array character))}, y:offset
|
||||
4:bool <- equal 1:text, 3:text
|
||||
{3: (foo number (address array character))} <- merge 34/x, 1:text/y
|
||||
6:text <- get {3: (foo number (address array character))}, y:offset
|
||||
8:bool <- equal 1:text, 6:text
|
||||
]
|
||||
+mem: storing 1 in location 4
|
||||
+mem: storing 1 in location 8
|
||||
|
||||
:(before "End element_type Special-cases")
|
||||
replace_type_ingredients(element, type, info, " while computing element type of container");
|
||||
|
@ -346,8 +348,8 @@ exclusive-container foo:_a [
|
|||
]
|
||||
def main [
|
||||
1:text <- new [abc]
|
||||
2:foo:point <- merge 0/variant, 34/xx, 35/xy
|
||||
10:point, 20:bool <- maybe-convert 2:foo:point, 0/variant
|
||||
3:foo:point <- merge 0/variant, 34/xx, 35/xy
|
||||
10:point, 20:bool <- maybe-convert 3:foo:point, 0/variant
|
||||
]
|
||||
+mem: storing 1 in location 20
|
||||
+mem: storing 35 in location 11
|
||||
|
@ -565,8 +567,8 @@ container foo:_t [
|
|||
y:num
|
||||
]
|
||||
def main [
|
||||
10:foo:point <- merge 14, 15, 16
|
||||
1:num <- get 10:foo, 1:offset
|
||||
1:foo:point <- merge 14, 15, 16
|
||||
10:num <- get 1:foo, 1:offset
|
||||
]
|
||||
# todo: improve error message
|
||||
+error: illegal type "foo" seems to be missing a type ingredient or three while computing element type of container
|
||||
|
@ -586,7 +588,7 @@ def main [
|
|||
% Hide_errors = true;
|
||||
def foo [
|
||||
local-scope
|
||||
x:adress:array:number <- copy 0 # typo
|
||||
x:adress:array:number <- copy null # typo
|
||||
]
|
||||
# shouldn't crash
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
:(scenario shape_shifting_recipe)
|
||||
def main [
|
||||
10:point <- merge 14, 15
|
||||
11:point <- foo 10:point
|
||||
12:point <- foo 10:point
|
||||
]
|
||||
# non-matching variant
|
||||
def foo a:num -> result:num [
|
||||
|
@ -17,8 +17,8 @@ def foo a:_t -> result:_t [
|
|||
load-ingredients
|
||||
result <- copy a
|
||||
]
|
||||
+mem: storing 14 in location 11
|
||||
+mem: storing 15 in location 12
|
||||
+mem: storing 14 in location 12
|
||||
+mem: storing 15 in location 13
|
||||
|
||||
//: Before anything else, disable transforms for shape-shifting recipes and
|
||||
//: make sure we never try to actually run a shape-shifting recipe. We should
|
||||
|
@ -538,7 +538,7 @@ void ensure_all_concrete_types(/*const*/ reagent& x, const recipe& exemplar) {
|
|||
:(scenario shape_shifting_recipe_2)
|
||||
def main [
|
||||
10:point <- merge 14, 15
|
||||
11:point <- foo 10:point
|
||||
12:point <- foo 10:point
|
||||
]
|
||||
# non-matching shape-shifting variant
|
||||
def foo a:_t, b:_t -> result:num [
|
||||
|
@ -552,13 +552,13 @@ def foo a:_t -> result:_t [
|
|||
load-ingredients
|
||||
result <- copy a
|
||||
]
|
||||
+mem: storing 14 in location 11
|
||||
+mem: storing 15 in location 12
|
||||
+mem: storing 14 in location 12
|
||||
+mem: storing 15 in location 13
|
||||
|
||||
:(scenario shape_shifting_recipe_nonroot)
|
||||
def main [
|
||||
10:foo:point <- merge 14, 15, 16
|
||||
20:point/raw <- bar 10:foo:point
|
||||
20:point <- bar 10:foo:point
|
||||
]
|
||||
# shape-shifting recipe with type ingredient following some other type
|
||||
def bar a:foo:_t -> result:_t [
|
||||
|
@ -592,7 +592,7 @@ def foo x:c:_bar:_baz [
|
|||
:(scenario shape_shifting_recipe_type_deduction_ignores_offsets)
|
||||
def main [
|
||||
10:foo:point <- merge 14, 15, 16
|
||||
20:point/raw <- bar 10:foo:point
|
||||
20:point <- bar 10:foo:point
|
||||
]
|
||||
def bar a:foo:_t -> result:_t [
|
||||
local-scope
|
||||
|
@ -699,14 +699,14 @@ def main [
|
|||
1:&:point <- new point:type
|
||||
*1:&:point <- put *1:&:point, y:offset, 34
|
||||
3:&:point <- bar 1:&:point # specialize _t to address:point
|
||||
4:point <- copy *3:&:point
|
||||
5:point <- copy *3:&:point
|
||||
]
|
||||
def bar a:_t -> result:_t [
|
||||
local-scope
|
||||
load-ingredients
|
||||
result <- copy a
|
||||
]
|
||||
+mem: storing 34 in location 5
|
||||
+mem: storing 34 in location 6
|
||||
|
||||
//: specializing a type ingredient with a compound type -- while *inside* another compound type
|
||||
:(scenario shape_shifting_recipe_supports_compound_types_2)
|
||||
|
@ -935,7 +935,7 @@ def foo x:_elem -> y:num [
|
|||
:(scenario specialize_most_similar_variant)
|
||||
def main [
|
||||
1:&:num <- new number:type
|
||||
2:num <- foo 1:&:num
|
||||
10:num <- foo 1:&:num
|
||||
]
|
||||
def foo x:_elem -> y:num [
|
||||
local-scope
|
||||
|
@ -947,14 +947,14 @@ def foo x:&:_elem -> y:num [
|
|||
load-ingredients
|
||||
return 35
|
||||
]
|
||||
+mem: storing 35 in location 2
|
||||
+mem: storing 35 in location 10
|
||||
|
||||
:(scenario specialize_most_similar_variant_2)
|
||||
# version with headers padded with lots of unrelated concrete types
|
||||
def main [
|
||||
1:num <- copy 23
|
||||
2:&:@:num <- copy null
|
||||
3:num <- foo 2:&:@:num, 1:num
|
||||
4:num <- foo 2:&:@:num, 1:num
|
||||
]
|
||||
# variant with concrete type
|
||||
def foo dummy:&:@:num, x:num -> y:num, dummy:&:@:num [
|
||||
|
@ -969,7 +969,7 @@ def foo dummy:&:@:num, x:_elem -> y:num, dummy:&:@:num [
|
|||
return 35
|
||||
]
|
||||
# prefer the concrete variant
|
||||
+mem: storing 34 in location 3
|
||||
+mem: storing 34 in location 4
|
||||
|
||||
:(scenario specialize_most_similar_variant_3)
|
||||
def main [
|
||||
|
@ -977,13 +977,13 @@ def main [
|
|||
foo 1:text
|
||||
]
|
||||
def foo x:text [
|
||||
2:num <- copy 34
|
||||
10:num <- copy 34
|
||||
]
|
||||
def foo x:&:_elem [
|
||||
2:num <- copy 35
|
||||
10:num <- copy 35
|
||||
]
|
||||
# make sure the more precise version was used
|
||||
+mem: storing 34 in location 2
|
||||
+mem: storing 34 in location 10
|
||||
|
||||
:(scenario specialize_literal_as_number)
|
||||
def main [
|
||||
|
|
|
@ -18,6 +18,7 @@ case TO_TEXT: {
|
|||
:(before "End Primitive Recipe Implementations")
|
||||
case TO_TEXT: {
|
||||
products.resize(1);
|
||||
products.at(0).push_back(/*alloc id*/0);
|
||||
products.at(0).push_back(new_mu_text(inspect(current_instruction().ingredients.at(0), ingredients.at(0))));
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ Transform.push_back(rewrite_literal_string_to_text); // idempotent
|
|||
set<string> recipes_taking_literal_strings;
|
||||
:(code)
|
||||
void initialize_transform_rewrite_literal_string_to_text() {
|
||||
recipes_taking_literal_strings.insert("assert");
|
||||
recipes_taking_literal_strings.insert("$print");
|
||||
recipes_taking_literal_strings.insert("$dump-trace");
|
||||
recipes_taking_literal_strings.insert("$system");
|
||||
|
|
|
@ -399,12 +399,17 @@ def remove-between start:&:duplex-list:_elem, end:&:duplex-list:_elem/contained-
|
|||
# start->next = end
|
||||
*next <- put *next, prev:offset, null
|
||||
*start <- put *start, next:offset, end
|
||||
return-unless end
|
||||
{
|
||||
break-if end
|
||||
stash [spliced:] next
|
||||
return
|
||||
}
|
||||
# end->prev->next = 0
|
||||
# end->prev = start
|
||||
prev:&:duplex-list:_elem <- get *end, prev:offset
|
||||
assert prev, [malformed duplex list - 2]
|
||||
*prev <- put *prev, next:offset, null
|
||||
stash [spliced:] next
|
||||
*end <- put *end, prev:offset, start
|
||||
]
|
||||
|
||||
|
@ -437,6 +442,9 @@ scenario remove-range [
|
|||
12 <- 15
|
||||
20 <- 0
|
||||
]
|
||||
trace-should-contain [
|
||||
app: spliced: 16 <-> 17 <-> 18
|
||||
]
|
||||
]
|
||||
|
||||
scenario remove-range-to-final [
|
||||
|
@ -472,6 +480,49 @@ scenario remove-range-to-final [
|
|||
12 <- 18
|
||||
20 <- 0 # no more elements
|
||||
]
|
||||
trace-should-contain [
|
||||
app: spliced: 15 <-> 16 <-> 17
|
||||
]
|
||||
]
|
||||
|
||||
scenario remove-range-to-penultimate [
|
||||
local-scope
|
||||
# construct a duplex list with six elements [13, 14, 15, 16, 17, 18]
|
||||
list:&:duplex-list:num <- push 18, null
|
||||
list <- push 17, list
|
||||
list <- push 16, list
|
||||
list <- push 15, list
|
||||
list <- push 14, list
|
||||
list <- push 13, list
|
||||
run [
|
||||
# delete 15 and 16
|
||||
# start pointer: to the second element
|
||||
list2:&:duplex-list:num <- next list
|
||||
# end pointer: to the last (sixth) element
|
||||
end:&:duplex-list:num <- next list2
|
||||
end <- next end
|
||||
end <- next end
|
||||
remove-between list2, end
|
||||
# now check the list
|
||||
10:num/raw <- get *list, value:offset
|
||||
list <- next list
|
||||
11:num/raw <- get *list, value:offset
|
||||
list <- next list
|
||||
12:num/raw <- get *list, value:offset
|
||||
list <- next list
|
||||
13:num/raw <- get *list, value:offset
|
||||
20:&:duplex-list:num/raw <- next list
|
||||
]
|
||||
memory-should-contain [
|
||||
10 <- 13
|
||||
11 <- 14
|
||||
12 <- 17
|
||||
13 <- 18
|
||||
20 <- 0 # no more elements
|
||||
]
|
||||
trace-should-contain [
|
||||
app: spliced: 15 <-> 16
|
||||
]
|
||||
]
|
||||
|
||||
scenario remove-range-empty [
|
||||
|
|
12
069hash.cc
12
069hash.cc
|
@ -62,7 +62,7 @@ size_t hash_mu_address(size_t h, reagent& r) {
|
|||
}
|
||||
|
||||
size_t hash_mu_text(size_t h, const reagent& r) {
|
||||
string input = read_mu_text(get_or_insert(Memory, r.value));
|
||||
string input = read_mu_text(get_or_insert(Memory, r.value+/*skip alloc id*/1));
|
||||
for (int i = 0; i < SIZE(input); ++i) {
|
||||
h = hash_iter(h, static_cast<size_t>(input.at(i)));
|
||||
//? cerr << i << ": " << h << '\n';
|
||||
|
@ -319,11 +319,11 @@ def main [
|
|||
:(scenario hash_matches_old_version)
|
||||
def main [
|
||||
1:text <- new [abc]
|
||||
2:num <- hash 1:text
|
||||
3:num <- hash_old 1:text
|
||||
4:bool <- equal 2:num, 3:num
|
||||
3:num <- hash 1:text
|
||||
4:num <- hash_old 1:text
|
||||
5:bool <- equal 3:num, 4:num
|
||||
]
|
||||
+mem: storing 1 in location 4
|
||||
+mem: storing 1 in location 5
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
HASH_OLD,
|
||||
|
@ -343,7 +343,7 @@ case HASH_OLD: {
|
|||
}
|
||||
:(before "End Primitive Recipe Implementations")
|
||||
case HASH_OLD: {
|
||||
string input = read_mu_text(ingredients.at(0).at(0));
|
||||
string input = read_mu_text(ingredients.at(0).at(/*skip alloc id*/1));
|
||||
size_t h = 0 ;
|
||||
|
||||
for (int i = 0; i < SIZE(input); ++i) {
|
||||
|
|
18
074wait.cc
18
074wait.cc
|
@ -262,22 +262,20 @@ def main [
|
|||
:(scenario get_location_indirect)
|
||||
# 'get-location' can read from container address
|
||||
def main [
|
||||
1:num <- copy 10
|
||||
10:num <- copy 34
|
||||
11:num <- copy 35
|
||||
4:location <- get-location 1:&:point/lookup, 0:offset
|
||||
1:num/alloc-id, 2:num <- copy 0, 10
|
||||
10:num/alloc-id, 11:num/x, 12:num/y <- copy 0, 34, 35
|
||||
20:location <- get-location 1:&:point/lookup, 0:offset
|
||||
]
|
||||
+mem: storing 10 in location 4
|
||||
+mem: storing 11 in location 20
|
||||
|
||||
:(scenario get_location_indirect_2)
|
||||
def main [
|
||||
1:num <- copy 10
|
||||
10:num <- copy 34
|
||||
11:num <- copy 35
|
||||
4:&:num <- copy 20/unsafe
|
||||
1:num/alloc-id, 2:num <- copy 0, 10
|
||||
10:num/alloc-id, 11:num/x, 12:num/y <- copy 0, 34, 35
|
||||
4:num/alloc-id, 5:num <- copy 0, 20
|
||||
4:&:location/lookup <- get-location 1:&:point/lookup, 0:offset
|
||||
]
|
||||
+mem: storing 10 in location 20
|
||||
+mem: storing 11 in location 21
|
||||
|
||||
//: allow waiting on a routine to complete
|
||||
|
||||
|
|
|
@ -145,8 +145,14 @@ assert(Next_predefined_global_for_scenarios < Reserved_for_tests);
|
|||
|
||||
:(before "End Globals")
|
||||
// Scenario Globals.
|
||||
extern const int SCREEN = Next_predefined_global_for_scenarios++;
|
||||
extern const int SCREEN = next_predefined_global_for_scenarios(/*size_of(address:screen)*/2);
|
||||
// End Scenario Globals.
|
||||
:(code)
|
||||
int next_predefined_global_for_scenarios(int size) {
|
||||
int result = Next_predefined_global_for_scenarios;
|
||||
Next_predefined_global_for_scenarios += size;
|
||||
return result;
|
||||
}
|
||||
|
||||
//: give 'screen' a fixed location in scenarios
|
||||
:(before "End Special Scenario Variable Names(r)")
|
||||
|
@ -250,19 +256,27 @@ struct raw_string_stream {
|
|||
|
||||
:(code)
|
||||
void check_screen(const string& expected_contents, const int color) {
|
||||
int screen_location = get_or_insert(Memory, SCREEN);
|
||||
int data_offset = find_element_name(get(Type_ordinal, "screen"), "data", "");
|
||||
assert(data_offset >= 0);
|
||||
int screen_data_location = screen_location+data_offset; // type: address:array:character
|
||||
int screen_data_start = get_or_insert(Memory, screen_data_location); // type: array:character
|
||||
int width_offset = find_element_name(get(Type_ordinal, "screen"), "num-columns", "");
|
||||
int screen_width = get_or_insert(Memory, screen_location+width_offset);
|
||||
int height_offset = find_element_name(get(Type_ordinal, "screen"), "num-rows", "");
|
||||
int screen_height = get_or_insert(Memory, screen_location+height_offset);
|
||||
int screen_location = get_or_insert(Memory, SCREEN+/*skip address alloc id*/1) + /*skip payload alloc id*/1;
|
||||
reagent screen("x:screen"); // just to ensure screen.type is reclaimed
|
||||
int screen_data_location = find_element_location(screen_location, "data", screen.type, "check_screen"); // type: address:array:character
|
||||
assert(screen_data_location >= 0);
|
||||
//? cerr << "screen data is at location " << screen_data_location << '\n';
|
||||
int screen_data_start = get_or_insert(Memory, screen_data_location+/*skip address alloc id*/1) + /*skip payload alloc id*/1; // type: array:character
|
||||
//? cerr << "screen data start is at " << screen_data_start << '\n';
|
||||
int screen_width_location = find_element_location(screen_location, "num-columns", screen.type, "check_screen");
|
||||
//? cerr << "screen width is at location " << screen_width_location << '\n';
|
||||
int screen_width = get_or_insert(Memory, screen_width_location);
|
||||
//? cerr << "screen width: " << screen_width << '\n';
|
||||
int screen_height_location = find_element_location(screen_location, "num-rows", screen.type, "check_screen");
|
||||
//? cerr << "screen height is at location " << screen_height_location << '\n';
|
||||
int screen_height = get_or_insert(Memory, screen_height_location);
|
||||
//? cerr << "screen height: " << screen_height << '\n';
|
||||
int top_index_location= find_element_location(screen_location, "top-idx", screen.type, "check_screen");
|
||||
//? cerr << "top of screen is at location " << top_index_location << '\n';
|
||||
int top_index = get_or_insert(Memory, top_index_location);
|
||||
//? cerr << "top of screen is index " << top_index << '\n';
|
||||
raw_string_stream cursor(expected_contents);
|
||||
// todo: too-long expected_contents should fail
|
||||
int top_index_offset = find_element_name(get(Type_ordinal, "screen"), "top-idx", "");
|
||||
int top_index = get_or_insert(Memory, screen_location+top_index_offset);
|
||||
for (int i=0, row=top_index/screen_width; i < screen_height; ++i, row=(row+1)%screen_height) {
|
||||
cursor.skip_whitespace_and_comments();
|
||||
if (cursor.at_end()) break;
|
||||
|
@ -385,18 +399,25 @@ case _DUMP_SCREEN: {
|
|||
|
||||
:(code)
|
||||
void dump_screen() {
|
||||
int screen_location = get_or_insert(Memory, SCREEN);
|
||||
int width_offset = find_element_name(get(Type_ordinal, "screen"), "num-columns", "");
|
||||
int screen_width = get_or_insert(Memory, screen_location+width_offset);
|
||||
int height_offset = find_element_name(get(Type_ordinal, "screen"), "num-rows", "");
|
||||
int screen_height = get_or_insert(Memory, screen_location+height_offset);
|
||||
int data_offset = find_element_name(get(Type_ordinal, "screen"), "data", "");
|
||||
assert(data_offset >= 0);
|
||||
int screen_data_location = screen_location+data_offset; // type: address:array:character
|
||||
int screen_data_start = get_or_insert(Memory, screen_data_location); // type: array:character
|
||||
assert(get_or_insert(Memory, screen_data_start) == screen_width*screen_height);
|
||||
int top_index_offset = find_element_name(get(Type_ordinal, "screen"), "top-idx", "");
|
||||
int top_index = get_or_insert(Memory, screen_location+top_index_offset);
|
||||
int screen_location = get_or_insert(Memory, SCREEN+/*skip address alloc id*/1) + /*skip payload alloc id*/1;
|
||||
reagent screen("x:screen"); // just to ensure screen.type is reclaimed
|
||||
int screen_data_location = find_element_location(screen_location, "data", screen.type, "check_screen"); // type: address:array:character
|
||||
assert(screen_data_location >= 0);
|
||||
//? cerr << "screen data is at location " << screen_data_location << '\n';
|
||||
int screen_data_start = get_or_insert(Memory, screen_data_location+/*skip address alloc id*/1) + /*skip payload alloc id*/1; // type: array:character
|
||||
//? cerr << "screen data start is at " << screen_data_start << '\n';
|
||||
int screen_width_location = find_element_location(screen_location, "num-columns", screen.type, "check_screen");
|
||||
//? cerr << "screen width is at location " << screen_width_location << '\n';
|
||||
int screen_width = get_or_insert(Memory, screen_width_location);
|
||||
//? cerr << "screen width: " << screen_width << '\n';
|
||||
int screen_height_location = find_element_location(screen_location, "num-rows", screen.type, "check_screen");
|
||||
//? cerr << "screen height is at location " << screen_height_location << '\n';
|
||||
int screen_height = get_or_insert(Memory, screen_height_location);
|
||||
//? cerr << "screen height: " << screen_height << '\n';
|
||||
int top_index_location= find_element_location(screen_location, "top-idx", screen.type, "check_screen");
|
||||
//? cerr << "top of screen is at location " << top_index_location << '\n';
|
||||
int top_index = get_or_insert(Memory, top_index_location);
|
||||
//? cerr << "top of screen is index " << top_index << '\n';
|
||||
for (int i=0, row=top_index/screen_width; i < screen_height; ++i, row=(row+1)%screen_height) {
|
||||
cerr << '.';
|
||||
int curr = screen_data_start+/*length*/1+row*screen_width* /*size of screen-cell*/2;
|
||||
|
|
|
@ -34,7 +34,7 @@ scenario keyboard-in-scenario [
|
|||
]
|
||||
|
||||
:(before "End Scenario Globals")
|
||||
extern const int CONSOLE = Next_predefined_global_for_scenarios++;
|
||||
extern const int CONSOLE = next_predefined_global_for_scenarios(/*size_of(address:console)*/2);
|
||||
//: give 'console' a fixed location in scenarios
|
||||
:(before "End Special Scenario Variable Names(r)")
|
||||
Name[r]["console"] = CONSOLE;
|
||||
|
@ -61,8 +61,8 @@ case ASSUME_CONSOLE: {
|
|||
int size = /*length*/1 + num_events*size_of_event();
|
||||
int event_data_address = allocate(size);
|
||||
// store length
|
||||
put(Memory, event_data_address, num_events);
|
||||
int curr_address = event_data_address + /*skip length*/1;
|
||||
put(Memory, event_data_address+/*skip alloc id*/1, num_events);
|
||||
int curr_address = event_data_address + /*skip alloc id*/1 + /*skip length*/1;
|
||||
for (int i = 0; i < SIZE(r.steps); ++i) {
|
||||
const instruction& inst = r.steps.at(i);
|
||||
if (inst.name == "left-click") {
|
||||
|
@ -113,13 +113,13 @@ case ASSUME_CONSOLE: {
|
|||
}
|
||||
}
|
||||
}
|
||||
assert(curr_address == event_data_address+size);
|
||||
assert(curr_address == event_data_address+/*skip alloc id*/1+size);
|
||||
// wrap the array of events in a console object
|
||||
int console_address = allocate(size_of_console());
|
||||
trace("mem") << "storing console in " << console_address << end();
|
||||
put(Memory, CONSOLE, console_address);
|
||||
put(Memory, CONSOLE+/*skip alloc id*/1, console_address);
|
||||
trace("mem") << "storing console data in " << console_address+/*offset of 'data' in container 'events'*/1 << end();
|
||||
put(Memory, console_address+/*offset of 'data' in container 'events'*/1, event_data_address);
|
||||
put(Memory, console_address+/*skip alloc id*/1+/*offset of 'data' in container 'events'*/1+/*skip alloc id of 'data'*/1, event_data_address);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ case _OPEN_FILE_FOR_READING: {
|
|||
}
|
||||
:(before "End Primitive Recipe Implementations")
|
||||
case _OPEN_FILE_FOR_READING: {
|
||||
string filename = read_mu_text(ingredients.at(0).at(0));
|
||||
string filename = read_mu_text(ingredients.at(0).at(/*skip alloc id*/1));
|
||||
assert(sizeof(long long int) >= sizeof(FILE*));
|
||||
FILE* f = fopen(filename.c_str(), "r");
|
||||
long long int result = reinterpret_cast<long long int>(f);
|
||||
|
@ -70,7 +70,7 @@ case _OPEN_FILE_FOR_WRITING: {
|
|||
}
|
||||
:(before "End Primitive Recipe Implementations")
|
||||
case _OPEN_FILE_FOR_WRITING: {
|
||||
string filename = read_mu_text(ingredients.at(0).at(0));
|
||||
string filename = read_mu_text(ingredients.at(0).at(/*skip alloc id*/1));
|
||||
assert(sizeof(long long int) >= sizeof(FILE*));
|
||||
long long int result = reinterpret_cast<long long int>(fopen(filename.c_str(), "w"));
|
||||
products.resize(1);
|
||||
|
|
|
@ -71,7 +71,7 @@ scenario escaping-file-contents [
|
|||
]
|
||||
|
||||
:(before "End Globals")
|
||||
extern const int RESOURCES = Next_predefined_global_for_scenarios++;
|
||||
extern const int RESOURCES = next_predefined_global_for_scenarios(/*size_of(address:resources)*/2);
|
||||
//: give 'resources' a fixed location in scenarios
|
||||
:(before "End Special Scenario Variable Names(r)")
|
||||
Name[r]["resources"] = RESOURCES;
|
||||
|
@ -203,26 +203,28 @@ string munge_resources_contents(const string& data, const string& filename, cons
|
|||
}
|
||||
|
||||
void construct_resources_object(const map<string, string>& contents) {
|
||||
int resources_data_address = allocate(SIZE(contents)*2 + /*array length*/1);
|
||||
int curr = resources_data_address + /*skip length*/1;
|
||||
int resources_data_address = allocate(SIZE(contents) * /*size of resource*/4 + /*array length*/1);
|
||||
int curr = resources_data_address + /*skip alloc id*/1 + /*skip array length*/1;
|
||||
for (map<string, string>::const_iterator p = contents.begin(); p != contents.end(); ++p) {
|
||||
++curr; // skip alloc id of resource.name
|
||||
put(Memory, curr, new_mu_text(p->first));
|
||||
trace("mem") << "storing file name " << get(Memory, curr) << " in location " << curr << end();
|
||||
++curr;
|
||||
++curr; // skip alloc id of resource.contents
|
||||
put(Memory, curr, new_mu_text(p->second));
|
||||
trace("mem") << "storing file contents " << get(Memory, curr) << " in location " << curr << end();
|
||||
++curr;
|
||||
}
|
||||
curr = resources_data_address;
|
||||
put(Memory, curr, SIZE(contents)); // size of array
|
||||
curr = resources_data_address + /*skip alloc id of resources.data*/1;
|
||||
put(Memory, curr, SIZE(contents)); // array length
|
||||
trace("mem") << "storing resources size " << get(Memory, curr) << " in location " << curr << end();
|
||||
// wrap the resources data in a 'resources' object
|
||||
int resources_address = allocate(size_of_resources());
|
||||
curr = resources_address+/*offset of 'data' element*/1;
|
||||
curr = resources_address+/*alloc id*/1+/*offset of 'data' element*/1+/*skip alloc id of 'data' element*/1;
|
||||
put(Memory, curr, resources_data_address);
|
||||
trace("mem") << "storing resources data address " << resources_data_address << " in location " << curr << end();
|
||||
// save in product
|
||||
put(Memory, RESOURCES, resources_address);
|
||||
put(Memory, RESOURCES+/*skip alloc id*/1, resources_address);
|
||||
trace("mem") << "storing resources address " << resources_address << " in location " << RESOURCES << end();
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ case _OPEN_CLIENT_SOCKET: {
|
|||
}
|
||||
:(before "End Primitive Recipe Implementations")
|
||||
case _OPEN_CLIENT_SOCKET: {
|
||||
string host = read_mu_text(ingredients.at(0).at(0));
|
||||
string host = read_mu_text(ingredients.at(0).at(/*skip alloc id*/1));
|
||||
int port = ingredients.at(1).at(0);
|
||||
socket_t* client = client_socket(host, port);
|
||||
products.resize(1);
|
||||
|
|
|
@ -3,20 +3,22 @@
|
|||
|
||||
:(scenario run_interactive_code)
|
||||
def main [
|
||||
1:num <- copy 0
|
||||
2:text <- new [1:num/raw <- copy 34]
|
||||
run-sandboxed 2:text
|
||||
3:num <- copy 1:num
|
||||
1:num <- copy 0 # reserve space for the sandbox
|
||||
10:text <- new [1:num/raw <- copy 34]
|
||||
#? $print 10:num [|] 11:num [: ] 1000:num [|] *10:text [ (] 10:text [)] 10/newline
|
||||
run-sandboxed 10:text
|
||||
20:num <- copy 1:num
|
||||
]
|
||||
+mem: storing 34 in location 3
|
||||
+mem: storing 34 in location 20
|
||||
|
||||
:(scenario run_interactive_empty)
|
||||
def main [
|
||||
1:text <- copy 0/unsafe
|
||||
2:text <- run-sandboxed 1:text
|
||||
10:text <- copy null
|
||||
20:text <- run-sandboxed 10:text
|
||||
]
|
||||
# result is null
|
||||
+mem: storing 0 in location 2
|
||||
+mem: storing 0 in location 20
|
||||
+mem: storing 0 in location 21
|
||||
|
||||
//: As the name suggests, 'run-sandboxed' will prevent certain operations that
|
||||
//: regular Mu code can perform.
|
||||
|
@ -52,12 +54,16 @@ case RUN_SANDBOXED: {
|
|||
}
|
||||
:(before "End Primitive Recipe Implementations")
|
||||
case RUN_SANDBOXED: {
|
||||
bool new_code_pushed_to_stack = run_interactive(ingredients.at(0).at(0));
|
||||
bool new_code_pushed_to_stack = run_interactive(ingredients.at(0).at(/*skip alloc id*/1));
|
||||
if (!new_code_pushed_to_stack) {
|
||||
products.resize(5);
|
||||
products.at(0).push_back(/*alloc id*/0);
|
||||
products.at(0).push_back(0);
|
||||
products.at(1).push_back(/*alloc id*/0);
|
||||
products.at(1).push_back(trace_error_contents());
|
||||
products.at(2).push_back(/*alloc id*/0);
|
||||
products.at(2).push_back(0);
|
||||
products.at(3).push_back(/*alloc id*/0);
|
||||
products.at(3).push_back(trace_app_contents());
|
||||
products.at(4).push_back(1); // completed
|
||||
run_code_end();
|
||||
|
@ -90,6 +96,7 @@ string Save_trace_file;
|
|||
// all errors.
|
||||
// returns true if successfully called (no errors found during load and transform)
|
||||
bool run_interactive(int address) {
|
||||
//? cerr << "run_interactive: " << address << '\n';
|
||||
assert(contains_key(Recipe_ordinal, "interactive") && get(Recipe_ordinal, "interactive") != 0);
|
||||
// try to sandbox the run as best you can
|
||||
// todo: test this
|
||||
|
@ -98,6 +105,7 @@ bool run_interactive(int address) {
|
|||
Memory.erase(i);
|
||||
}
|
||||
string command = trim(strip_comments(read_mu_text(address)));
|
||||
//? cerr << "command: " << command << '\n';
|
||||
Name[get(Recipe_ordinal, "interactive")].clear();
|
||||
run_code_begin(/*should_stash_snapshots*/true);
|
||||
if (command.empty()) return false;
|
||||
|
@ -213,15 +221,20 @@ load(string(
|
|||
"]\n" +
|
||||
"recipe sandbox [\n" +
|
||||
"local-scope\n" +
|
||||
//? "$print [aaa] 10/newline\n" +
|
||||
"screen:&:screen <- new-fake-screen 30, 5\n" +
|
||||
"routine-id:num <- start-running interactive, screen\n" +
|
||||
"limit-time routine-id, 100000/instructions\n" +
|
||||
"wait-for-routine routine-id\n" +
|
||||
//? "$print [bbb] 10/newline\n" +
|
||||
"instructions-run:num <- number-of-instructions routine-id\n" +
|
||||
"stash instructions-run [instructions run]\n" +
|
||||
"sandbox-state:num <- routine-state routine-id\n" +
|
||||
"completed?:bool <- equal sandbox-state, 1/completed\n" +
|
||||
//? "$print [completed: ] completed? 10/newline\n" +
|
||||
"output:text <- $most-recent-products\n" +
|
||||
//? "$print [zzz] 10/newline\n" +
|
||||
//? "$print output\n" +
|
||||
"errors:text <- save-errors\n" +
|
||||
"stashes:text <- save-app-trace\n" +
|
||||
"$cleanup-run-sandboxed\n" +
|
||||
|
@ -281,6 +294,7 @@ case _MOST_RECENT_PRODUCTS: {
|
|||
:(before "End Primitive Recipe Implementations")
|
||||
case _MOST_RECENT_PRODUCTS: {
|
||||
products.resize(1);
|
||||
products.at(0).push_back(/*alloc id*/0);
|
||||
products.at(0).push_back(new_mu_text(Most_recent_products));
|
||||
break;
|
||||
}
|
||||
|
@ -296,6 +310,7 @@ case SAVE_ERRORS: {
|
|||
:(before "End Primitive Recipe Implementations")
|
||||
case SAVE_ERRORS: {
|
||||
products.resize(1);
|
||||
products.at(0).push_back(/*alloc id*/0);
|
||||
products.at(0).push_back(trace_error_contents());
|
||||
break;
|
||||
}
|
||||
|
@ -311,6 +326,7 @@ case SAVE_APP_TRACE: {
|
|||
:(before "End Primitive Recipe Implementations")
|
||||
case SAVE_APP_TRACE: {
|
||||
products.resize(1);
|
||||
products.at(0).push_back(/*alloc id*/0);
|
||||
products.at(0).push_back(trace_app_contents());
|
||||
break;
|
||||
}
|
||||
|
@ -332,64 +348,64 @@ case _CLEANUP_RUN_SANDBOXED: {
|
|||
:(scenario "run_interactive_converts_result_to_text")
|
||||
def main [
|
||||
# try to interactively add 2 and 2
|
||||
1:text <- new [add 2, 2]
|
||||
2:text <- run-sandboxed 1:text
|
||||
10:@:char <- copy *2:text
|
||||
10:text <- new [add 2, 2]
|
||||
20:text <- run-sandboxed 10:text
|
||||
30:@:char <- copy *20:text
|
||||
]
|
||||
# first letter in the output should be '4' in unicode
|
||||
+mem: storing 52 in location 11
|
||||
+mem: storing 52 in location 31
|
||||
|
||||
:(scenario "run_interactive_ignores_products_in_nested_functions")
|
||||
def main [
|
||||
1:text <- new [foo]
|
||||
2:text <- run-sandboxed 1:text
|
||||
10:@:char <- copy *2:text
|
||||
10:text <- new [foo]
|
||||
20:text <- run-sandboxed 10:text
|
||||
30:@:char <- copy *20:text
|
||||
]
|
||||
def foo [
|
||||
20:num <- copy 1234
|
||||
40:num <- copy 1234
|
||||
{
|
||||
break
|
||||
reply 5678
|
||||
}
|
||||
]
|
||||
# no product should have been tracked
|
||||
+mem: storing 0 in location 10
|
||||
+mem: storing 0 in location 30
|
||||
|
||||
:(scenario "run_interactive_ignores_products_in_previous_instructions")
|
||||
def main [
|
||||
1:text <- new [
|
||||
10:text <- new [
|
||||
add 1, 1 # generates a product
|
||||
foo] # no products
|
||||
2:text <- run-sandboxed 1:text
|
||||
10:@:char <- copy *2:text
|
||||
20:text <- run-sandboxed 10:text
|
||||
30:@:char <- copy *20:text
|
||||
]
|
||||
def foo [
|
||||
20:num <- copy 1234
|
||||
40:num <- copy 1234
|
||||
{
|
||||
break
|
||||
reply 5678
|
||||
}
|
||||
]
|
||||
# no product should have been tracked
|
||||
+mem: storing 0 in location 10
|
||||
+mem: storing 0 in location 30
|
||||
|
||||
:(scenario "run_interactive_remembers_products_before_final_label")
|
||||
def main [
|
||||
1:text <- new [
|
||||
10:text <- new [
|
||||
add 1, 1 # generates a product
|
||||
+foo] # no products
|
||||
2:text <- run-sandboxed 1:text
|
||||
10:@:char <- copy *2:text
|
||||
20:text <- run-sandboxed 10:text
|
||||
30:@:char <- copy *20:text
|
||||
]
|
||||
def foo [
|
||||
20:num <- copy 1234
|
||||
40:num <- copy 1234
|
||||
{
|
||||
break
|
||||
reply 5678
|
||||
}
|
||||
]
|
||||
# product tracked
|
||||
+mem: storing 50 in location 11
|
||||
+mem: storing 50 in location 31
|
||||
|
||||
:(scenario "run_interactive_returns_text")
|
||||
def main [
|
||||
|
@ -399,38 +415,41 @@ def main [
|
|||
y:text <- new [b]
|
||||
z:text <- append x:text, y:text
|
||||
]
|
||||
2:text <- run-sandboxed 1:text
|
||||
10:@:char <- copy *2:text
|
||||
10:text <- run-sandboxed 1:text
|
||||
#? $print 10:text 10/newline
|
||||
20:@:char <- copy *10:text
|
||||
]
|
||||
# output contains "ab"
|
||||
+mem: storing 97 in location 11
|
||||
+mem: storing 98 in location 12
|
||||
+mem: storing 97 in location 21
|
||||
+mem: storing 98 in location 22
|
||||
|
||||
:(scenario "run_interactive_returns_errors")
|
||||
def main [
|
||||
# run a command that generates an error
|
||||
1:text <- new [x:num <- copy 34
|
||||
10:text <- new [x:num <- copy 34
|
||||
get x:num, foo:offset]
|
||||
2:text, 3:text <- run-sandboxed 1:text
|
||||
10:@:char <- copy *3:text
|
||||
20:text, 30:text <- run-sandboxed 10:text
|
||||
40:@:char <- copy *30:text
|
||||
]
|
||||
# error should be "unknown element foo in container number"
|
||||
+mem: storing 117 in location 11
|
||||
+mem: storing 110 in location 12
|
||||
+mem: storing 107 in location 13
|
||||
+mem: storing 110 in location 14
|
||||
+mem: storing 117 in location 41
|
||||
+mem: storing 110 in location 42
|
||||
+mem: storing 107 in location 43
|
||||
+mem: storing 110 in location 44
|
||||
# ...
|
||||
|
||||
:(scenario run_interactive_with_comment)
|
||||
def main [
|
||||
# 2 instructions, with a comment after the first
|
||||
1:&:@:num <- new [a:num <- copy 0 # abc
|
||||
10:text <- new [a:num <- copy 0 # abc
|
||||
b:num <- copy 0
|
||||
]
|
||||
2:text, 3:text <- run-sandboxed 1:text
|
||||
20:text, 30:text <- run-sandboxed 10:text
|
||||
]
|
||||
# no errors
|
||||
+mem: storing 0 in location 3
|
||||
# skip alloc id
|
||||
+mem: storing 0 in location 30
|
||||
+mem: storing 0 in location 31
|
||||
|
||||
:(after "Running One Instruction")
|
||||
if (Track_most_recent_products && SIZE(Current_routine->calls) == Call_depth_to_track_most_recent_products_at
|
||||
|
@ -441,6 +460,7 @@ if (Track_most_recent_products && SIZE(Current_routine->calls) == Call_depth_to_
|
|||
:(before "End Running One Instruction")
|
||||
if (Track_most_recent_products && SIZE(Current_routine->calls) == Call_depth_to_track_most_recent_products_at) {
|
||||
Most_recent_products = track_most_recent_products(current_instruction(), products);
|
||||
//? cerr << "most recent products: " << Most_recent_products << '\n';
|
||||
}
|
||||
:(code)
|
||||
string track_most_recent_products(const instruction& instruction, const vector<vector<double> >& products) {
|
||||
|
@ -458,8 +478,8 @@ string track_most_recent_products(const instruction& instruction, const vector<v
|
|||
// => abc
|
||||
if (i < SIZE(instruction.products)) {
|
||||
if (is_mu_text(instruction.products.at(i))) {
|
||||
if (!scalar(products.at(i))) continue; // error handled elsewhere
|
||||
out << read_mu_text(products.at(i).at(0)) << '\n';
|
||||
if (SIZE(products.at(i)) != 2) continue; // weak silent check for address
|
||||
out << read_mu_text(products.at(i).at(/*skip alloc id*/1)) << '\n';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -551,7 +571,7 @@ case RELOAD: {
|
|||
:(before "End Primitive Recipe Implementations")
|
||||
case RELOAD: {
|
||||
restore_non_recipe_snapshots();
|
||||
string code = read_mu_text(ingredients.at(0).at(0));
|
||||
string code = read_mu_text(ingredients.at(0).at(/*skip alloc id*/1));
|
||||
run_code_begin(/*should_stash_snapshots*/false);
|
||||
routine* save_current_routine = Current_routine;
|
||||
Current_routine = NULL;
|
||||
|
@ -562,17 +582,30 @@ case RELOAD: {
|
|||
Sandbox_mode = false;
|
||||
Current_routine = save_current_routine;
|
||||
products.resize(1);
|
||||
products.at(0).push_back(/*alloc id*/0);
|
||||
products.at(0).push_back(trace_error_contents());
|
||||
run_code_end(); // wait until we're done with the trace contents
|
||||
break;
|
||||
}
|
||||
|
||||
:(scenario reload_loads_function_definitions)
|
||||
def main [
|
||||
local-scope
|
||||
x:text <- new [recipe foo [
|
||||
1:num/raw <- copy 34
|
||||
]]
|
||||
reload x
|
||||
run-sandboxed [foo]
|
||||
2:num/raw <- copy 1:num/raw
|
||||
]
|
||||
+mem: storing 34 in location 2
|
||||
|
||||
:(scenario reload_continues_past_error)
|
||||
def main [
|
||||
local-scope
|
||||
x:text <- new [recipe foo [
|
||||
get 1234:num, foo:offset
|
||||
]]
|
||||
get 1234:num, foo:offset
|
||||
]]
|
||||
reload x
|
||||
1:num/raw <- copy 34
|
||||
]
|
||||
|
@ -593,9 +626,11 @@ def main [
|
|||
]
|
||||
]
|
||||
# save warning addresses in locations of type 'number' to avoid spurious changes to them due to 'abandon'
|
||||
1:num/raw <- reload x
|
||||
2:num/raw <- reload x
|
||||
10:text/raw <- reload x
|
||||
20:text/raw <- reload x
|
||||
]
|
||||
# no errors on either load
|
||||
+mem: storing 0 in location 1
|
||||
+mem: storing 0 in location 2
|
||||
+mem: storing 0 in location 10
|
||||
+mem: storing 0 in location 11
|
||||
+mem: storing 0 in location 20
|
||||
+mem: storing 0 in location 21
|
||||
|
|
|
@ -336,9 +336,9 @@ channels.
|
|||
|
||||
Routines are expected to communicate purely by message passing, though nothing
|
||||
stops them from sharing memory since all routines share a common address
|
||||
space. However, idiomatic Mu will make it hard to accidentally read or clobber
|
||||
random memory locations. Bounds checking is baked deeply into the semantics,
|
||||
and pointers can never be invalidated.
|
||||
space. However, idiomatic Mu will make it hard to accidentally read or
|
||||
clobber random memory locations. Bounds checking is baked deeply into
|
||||
the semantics, and using pointers after freeing them immediately fails.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -81,18 +81,20 @@ scenario editor-initializes-without-data [
|
|||
assume-screen 5/width, 3/height
|
||||
run [
|
||||
e:&:editor <- new-editor null/data, 2/left, 5/right
|
||||
2:editor/raw <- copy *e
|
||||
1:editor/raw <- copy *e
|
||||
]
|
||||
memory-should-contain [
|
||||
# 2 (data) <- just the § sentinel
|
||||
# 3 (top of screen) <- the § sentinel
|
||||
4 <- 0 # bottom-of-screen; null since text fits on screen
|
||||
# 5 (before cursor) <- the § sentinel
|
||||
6 <- 2 # left
|
||||
7 <- 4 # right (inclusive)
|
||||
8 <- 0 # bottom (not set until render)
|
||||
9 <- 1 # cursor row
|
||||
10 <- 2 # cursor column
|
||||
# 1,2 (data) <- just the § sentinel
|
||||
# 3,4 (top of screen) <- the § sentinel
|
||||
# 5 (bottom of screen) <- null since text fits on screen
|
||||
5 <- 0
|
||||
6 <- 0
|
||||
# 7,8 (before cursor) <- the § sentinel
|
||||
9 <- 2 # left
|
||||
10 <- 4 # right (inclusive)
|
||||
11 <- 0 # bottom (not set until render)
|
||||
12 <- 1 # cursor row
|
||||
13 <- 2 # cursor column
|
||||
]
|
||||
screen-should-contain [
|
||||
. .
|
||||
|
|
|
@ -57,5 +57,5 @@ def f [
|
|||
return-continuation-until-mark 999/exception-tag, [error will robinson!], 0/unused
|
||||
}
|
||||
# normal return: 3 results including 0 continuation placeholder at start
|
||||
return 0/continuation-placeholder, 0/no-error, 34/regular-result
|
||||
return 0/continuation-placeholder, null/no-error, 34/regular-result
|
||||
]
|
||||
|
|
22
index.html
22
index.html
|
@ -158,18 +158,16 @@ for gradually constructing long strings in a piecemeal fashion.
|
|||
space at run-time as pointers or <em>addresses</em>. All Mu instructions can
|
||||
dereference or <a href='html/035lookup.cc.html'><em>lookup</em></a> addresses
|
||||
of values in addition to operating on regular values. These addresses are
|
||||
manually managed like C. However, all allocations are transparently
|
||||
reference-counted or <a href='html/036refcount.cc.html'><em>refcounted</em></a>,
|
||||
with every copy of a pointer updating refcounts appropriately. When the
|
||||
refcount of an allocation drops to zero it is transparently <a href='html/037abandon.cc.html'>reclaimed</a>
|
||||
and made available to future allocations. By construction it is impossible to
|
||||
reclaim memory prematurely, while some other part of a program is still
|
||||
pointing to it. This eliminates a whole class of undefined behavior and
|
||||
security vulnerabilities that plague C. Compared to Rust, Mu pays some
|
||||
additional runtime cost in exchange for C-like flexibility (you can copy
|
||||
addresses around all you like, and write from any copy of an address) and
|
||||
simpler implementation (no static analysis). Mu by convention abbreviates type
|
||||
<tt>address</tt> to <tt>&</tt>.
|
||||
manually managed like C, and can be reclaimed using the <a href='html/037abandon.cc.html'><tt>abandon</tt></a>
|
||||
instruction. To ensure that stale addresses aren't used after being
|
||||
abandoned/reused, each allocation gets a unique <em>alloc id</em> that is also
|
||||
stored in the address returned. The lookup operation ensures that the alloc id
|
||||
of an address matches that of its payload. This eliminates a whole class of
|
||||
undefined behavior and security vulnerabilities that plague C. Compared to
|
||||
Rust, Mu pays some additional runtime cost in exchange for C-like flexibility
|
||||
(you can copy addresses around all you like, and write from any copy of an
|
||||
address) and simpler implementation (no static analysis). Mu by convention
|
||||
abbreviates type <tt>address</tt> to <tt>&</tt>.
|
||||
|
||||
<p/>Support for higher-order recipes that can pass <a href='html/072recipe.cc.html'>recipes</a>
|
||||
around like any other value.
|
||||
|
|
|
@ -81,18 +81,20 @@ scenario editor-initializes-without-data [
|
|||
assume-screen 5/width, 3/height
|
||||
run [
|
||||
e:&:editor <- new-editor null/data, 2/left, 5/right
|
||||
2:editor/raw <- copy *e
|
||||
1:editor/raw <- copy *e
|
||||
]
|
||||
memory-should-contain [
|
||||
# 2 (data) <- just the § sentinel
|
||||
# 3 (top of screen) <- the § sentinel
|
||||
4 <- 0 # bottom-of-screen; null since text fits on screen
|
||||
# 5 (before cursor) <- the § sentinel
|
||||
6 <- 2 # left
|
||||
7 <- 4 # right (inclusive)
|
||||
8 <- 0 # bottom (not set until render)
|
||||
9 <- 1 # cursor row
|
||||
10 <- 2 # cursor column
|
||||
# 1,2 (data) <- just the § sentinel
|
||||
# 3,4 (top of screen) <- the § sentinel
|
||||
# 5 (bottom of screen) <- null since text fits on screen
|
||||
5 <- 0
|
||||
6 <- 0
|
||||
# 7,8 (before cursor) <- the § sentinel
|
||||
9 <- 2 # left
|
||||
10 <- 4 # right (inclusive)
|
||||
11 <- 0 # bottom (not set until render)
|
||||
12 <- 1 # cursor row
|
||||
13 <- 2 # cursor column
|
||||
]
|
||||
screen-should-contain [
|
||||
. .
|
||||
|
|
Loading…
Reference in New Issue