//: Exclusive containers contain exactly one of a fixed number of 'variants' //: of different types. //: //: They also implicitly contain a tag describing precisely which variant is //: currently stored in them. :(before "End Mu Types Initialization") //: We'll use this container as a running example, with two number elements. { type_ordinal tmp = put(Type_ordinal, "number-or-point", Next_type_ordinal++); get_or_insert(Type, tmp); // initialize get(Type, tmp).kind = EXCLUSIVE_CONTAINER; get(Type, tmp).name = "number-or-point"; get(Type, tmp).elements.push_back(reagent("i:number")); get(Type, tmp).elements.push_back(reagent("p:point")); } //: Tests in this layer often explicitly set up memory before reading it as an //: array. Don't do this in general. I'm tagging exceptions with /raw to keep //: checks in future layers from flagging them. :(scenario copy_exclusive_container) # Copying exclusive containers copies all their contents and an extra location for the tag. def main [ 1:number <- copy 1 # 'point' variant 2:number <- copy 34 3:number <- copy 35 4:number-or-point <- copy 1:number-or-point/unsafe ] +mem: storing 1 in location 4 +mem: storing 34 in location 5 +mem: storing 35 in location 6 :(before "End size_of(type) Cases") if (t.kind == EXCLUSIVE_CONTAINER) { // Compute size_of Exclusive Container return get(Container_metadata, type).size; } :(before "End compute_container_sizes Cases") if (info.kind == EXCLUSIVE_CONTAINER) { container_metadata metadata; // size of an exclusive container is the size of its largest variant // (So like containers, it can't contain arrays.) int size = 0; for (int i = 0; i < SIZE(info.elements); ++i) { reagent/*copy*/ element = info.elements.at(i); // Compute Exclusive Container Size(element) compute_container_sizes(element.type, pending_metadata); int variant_size = size_of(element); if (variant_size > size) size = variant_size; } // ...+1 for its tag. metadata.size = size+1; Container_metadata.push_back(pair(new type_tree(*type), metadata)); } //:: To access variants of an exclusive container, use 'maybe-convert'. //: It always returns an address (so that you can modify it) or null (to //: signal that the conversion failed (because the container contains a //: different variant). //: 'maybe-convert' requires a literal in ingredient 1. We'll use a synonym //: called 'variant'. :(before "End Mu Types Initialization") put(Type_ordinal, "variant", 0); :(scenario maybe_convert) def main [ 12:number <- copy 1 13:number <- copy 35 14:number <- copy 36 20:point, 22:boolean <- maybe-convert 12:number-or-point/unsafe, 1:variant ] # boolean +mem: storing 1 in location 22 # point +mem: storing 35 in location 20 +mem: storing 36 in location 21 :(scenario maybe_convert_fail) def main [ 12:number <- copy 1 13:number <- copy 35 14:number <- copy 36 20:number, 21:boolean <- maybe-convert 12:number-or-point/unsafe, 0:variant ] # boolean +mem: storing 0 in location 21 # number: no write :(before "End Primitive Recipe Declarations") MAYBE_CONVERT, :(before "End Primitive Recipe Numbers") put(Recipe_ordinal, "maybe-convert", MAYBE_CONVERT); :(before "End Primitive Recipe Checks") case MAYBE_CONVERT: { const recipe& caller = get(Recipe, r); if (SIZE(inst.ingredients) != 2) { raise << maybe(caller.name) << "'maybe-convert' expects exactly 2 ingredients in '" << inst.original_string << "'\n" << end(); break; } reagent/*copy*/ base = inst.ingredients.at(0); // Update MAYBE_CONVERT base in Check if (!base.type || !base.type->value || get(Type, base.type->value).kind != EXCLUSIVE_CONTAINER) { raise << maybe(caller.name) << "first ingredient of 'maybe-convert' should be an exclusive-container, but got '" << base.original_string << "'\n" << end(); break; } if (!is_literal(inst.ingredients.at(1))) { raise << maybe(caller.name) << "second ingredient of 'maybe-convert' should have type 'variant', but got '" << inst.ingredients.at(1).original_string << "'\n" << end(); break; } if (inst.products.empty()) break; if (SIZE(inst.products) != 2) { raise << maybe(caller.name) << "'maybe-convert' expects exactly 2 products in '" << inst.original_string << "'\n" << end(); break; } reagent/*copy*/ product = inst.products.at(0); // Update MAYBE_CONVERT product in Check reagent& offset = inst.ingredients.at(1); populate_value(offset); if (offset.value >= SIZE(get(Type, base.type->value).elements)) { raise << maybe(caller.name) << "invalid tag " << offset.value << " in '" << inst.original_string << '\n' << end(); break; } const reagent& variant = variant_type(base, offset.value); if (!types_coercible(product, variant)) { raise << maybe(caller.name) << "'maybe-convert " << base.original_string << ", " << inst.ingredients.at(1).original_string << "' should write to " << to_string(variant.type) << " but '
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module ranger.ext.openstruct</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">

<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="ranger.html"><font color="#ffffff">ranger</font></a>.<a href="ranger.ext.html"><font color="#ffffff">ext</font></a>.openstruct</strong></big></big></font></td
><td alig