//: Containers contain a fixed number of elements of different types. :(before "End Mu Types Initialization") //: We'll use this container as a running example in scenarios below. type_ordinal point = put(Type_ordinal, "point", Next_type_ordinal++); get_or_insert(Type, point); // initialize get(Type, point).kind = CONTAINER; get(Type, point).name = "point"; get(Type, point).elements.push_back(reagent("x:number")); get(Type, point).elements.push_back(reagent("y:number")); //: Containers can be copied around with a single instruction just like //: numbers, no matter how large they are. //: Tests in this layer often explicitly set up memory before reading it as a //: container. Don't do this in general. I'm tagging such cases with /unsafe; //: they'll be exceptions to later checks. :(scenario copy_multiple_locations) def main [ 1:num <- copy 34 2:num <- copy 35 3:point <- copy 1:point/unsafe ] +mem: storing 34 in location 3 +mem: storing 35 in location 4 //: trying to copy to a differently-typed destination will fail :(scenario copy_checks_size) % Hide_errors = true; def main [ 2:point <- copy 1:num ] +error: main: can't copy '1:num' to '2:point'; types don't match :(before "End Mu Types Initialization") // A more complex example container, containing another container as one of // its elements. type_ordinal point_number = put(Type_ordinal, "point-number", Next_type_ordinal++); get_or_insert(Type, point_number); // initialize get(Type, point_number).kind = CONTAINER; get(Type, point_number).name = "point-number"; get(Type, point_number).elements.push_back(reagent("xy:point")); get(Type, point_number).elements.push_back(reagent("z:number")); :(scenario copy_handles_nested_container_elements) def main [ 12:num <- copy 34 13:num <- copy 35 14:num <- copy 36 15:point-number <- copy 12:point-number/unsafe ] +mem: storing 36 in location 17 //: products of recipes can include containers :(scenario return_container) def main [ 3:point <- f 2 ] def f [ 12:num <- next-ingredient 13:num <- copy 35 return 12:point/raw ] +run: result 0 is [2, 35] +mem: storing 2 in location 3 +mem: storing 35 in location 4 //: Containers can be checked for equality with a single instruction just like //: numbers, no matter how large they are. :(scenario compare_multiple_locations) def main [ 1:num <- copy 34 # first 2:num <- copy 35 3:num <- copy 36 4:num <- copy 34 # second 5:num <- copy 35 6:num <- copy 36 7:bool <- equal 1:point-number/raw, 4:point-number/unsafe ] +mem: storing 1 in location 7 :(scenario compare_multiple_locations_2) def main [ 1:num <- copy 34 # first 2:num <- copy 35 3:num <- copy 36 4:num <- copy 34 # second 5:num <- copy 35 6:num <- copy 37 # different 7:bool <- equal 1:point-number/raw, 4:point-number/unsafe ] +mem: storing 0 in location 7 :(before "End size_of(type) Special-cases") if (type->value == -1) return 1; // error value, but we'll raise it elsewhere if (type->value == 0) return 1; if (!contains_key(Type, type->value)) { raise << "no such type " << type->value << '\n' << end(); return 0; } type_info t = get(Type, type->value); if (t.kind == CONTAINER) { // size of a container is the sum of the sizes of its elements int result = 0; for (int i = 0; i < SIZE(t.elements); ++i) { // todo: strengthen assertion to disallow mutual type recursion if (t.elements.at(i).type->value == type->value) { raise << "container " << t.name << " can't include itself as a member\n" << end(); return 0; } result += size_of(element_type(type, i)); } return result; } :(scenario stash_container) def main [ 1:num <- copy 34 # first 2:num <- copy 35 3:num <- copy 36 stash [foo:], 1:point-number/raw ] +app: foo: 34 35 36 //:: To access elements of a container, use 'get' //: 'get' takes a 'base' container and an 'offset' into it and returns the //: appropriate element of the container value. :(scenario get) def main [ 12:num <- copy 34 13:num <- copy 35 15:num <- get 12:point/raw, 1:offset # unsafe ] +mem: storing 35 in location 15 :(before "End Primitive Recipe Declarations") GET, :(before "End Primitive Recipe Numbers") put(Recipe_ordinal, "get", GET); :(before "End Primitive Recipe Checks") case GET: { if (SIZE(inst.ingredients) != 2) { raise << maybe(get(Recipe, r).name) << "'get' expects exactly 2 ingredients in '" << to_original_string(inst) << "'\n" << end(); break; } reagent/*copy*/ base = inst.ingredients.at(0); // new copy for every invocation // Update GET base in Check if (!base.type) { raise << maybe(get(Recipe, r).name) << "first ingredient of 'get' should be a container, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); break; } const type_tree* base_type = base.type; // Update GET base_type in Check if (!base_type->atom || base_type->value == 0 || !contains_key(Type, base_type->value) || get(Type, base_type->value).kind != CONTAINER) { raise << maybe(get(Recipe, r).name) << "first ingredient of 'get' should be a container, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); break; } const reagent& offset = inst.ingredients.at(1); if (!i
#!/bin/sh
# Hacky little helper called from edit/ and sandbox/ apps to save a snapshot
# of lesson/ using git.
set -e
test -d lesson/.git || exit 0 # give up if it's not a git repo
cd lesson
# explicitly say '--all' for git 1.9
git add --all .
# bug in git: git diff -q messes up --exit-code
git diff HEAD --exit-code >/dev/null || git commit -a -m . >/dev/null