diff options
-rw-r--r-- | 010vm.cc | 4 | ||||
-rw-r--r-- | 012transform.cc | 12 | ||||
-rw-r--r-- | 020run.cc | 1 | ||||
-rw-r--r-- | 030container.cc | 72 |
4 files changed, 68 insertions, 21 deletions
diff --git a/010vm.cc b/010vm.cc index 1f33c9cf..eec9e2ff 100644 --- a/010vm.cc +++ b/010vm.cc @@ -171,7 +171,9 @@ struct type_info { kind_of_type kind; vector<reagent> elements; // End type_info Fields - type_info() :kind(PRIMITIVE) {} + type_info() :kind(PRIMITIVE) { + // End type_info Constructor + } }; enum primitive_recipes { diff --git a/012transform.cc b/012transform.cc index d3b4384e..b5b6e4fe 100644 --- a/012transform.cc +++ b/012transform.cc @@ -56,6 +56,18 @@ void transform_all() { // End transform_all } +//: Even though a run will involve many calls to transform_all() for tests, +//: our logical model is to load all code, then transform all code, then run. +//: If you load new code that should cause already-transformed recipes to +//: change, that's not supported. To help detect such situations and raise +//: helpful errors we track a count of the number of calls made to +//: transform_all(). +:(before "End Globals") +int Num_calls_to_transform_all = 0; +:(after "void transform_all()") + ++Num_calls_to_transform_all; + +:(code) void parse_int_reagents() { trace(9991, "transform") << "--- parsing any uninitialized reagents as integers" << end(); for (map<recipe_ordinal, recipe>::iterator p = Recipe.begin(); p != Recipe.end(); ++p) { diff --git a/020run.cc b/020run.cc index 7764adc9..a94eb668 100644 --- a/020run.cc +++ b/020run.cc @@ -170,6 +170,7 @@ if (!Run_tests && contains_key(Recipe_ordinal, "main") && contains_key(Recipe, g //? Trace_file = "interactive"; //? START_TRACING_UNTIL_END_OF_SCOPE; trace(9990, "run") << "=== Starting to run" << end(); + assert(Num_calls_to_transform_all == 1); run_main(argc, argv); teardown(); } diff --git a/030container.cc b/030container.cc index 90f9d713..38682e96 100644 --- a/030container.cc +++ b/030container.cc @@ -379,11 +379,38 @@ container bar [ +parse: element: {x: "number"} +parse: element: {y: "number"} +//: if a container is defined again, the new fields add to the original definition +:(scenarios run) +:(scenario container_extend) +container foo [ + x:number +] +# add to previous definition +container foo [ + y:number +] +def main [ + 1:number <- copy 34 + 2:number <- copy 35 + 3:number <- get 1:foo, 0:offset + 4:number <- get 1:foo, 1:offset +] ++mem: storing 34 in location 3 ++mem: storing 35 in location 4 + :(before "End Command Handlers") else if (command == "container") { insert_container(command, CONTAINER, in); } +//: Even though we allow containers to be extended, we don't allow this after +//: a call to transform_all. But we do want to detect this situation and raise +//: an error. This field will help us raise such errors. +:(before "End type_info Fields") +int Num_calls_to_transform_all_at_first_definition; +:(before "End type_info Constructor") +Num_calls_to_transform_all_at_first_definition = -1; + :(code) void insert_container(const string& command, kind_of_type kind, istream& in) { skip_whitespace_but_not_newline(in); @@ -397,6 +424,15 @@ void insert_container(const string& command, kind_of_type kind, istream& in) { trace(9999, "parse") << "type number: " << get(Type_ordinal, name) << end(); skip_bracket(in, "'container' must begin with '['"); type_info& info = get_or_insert(Type, get(Type_ordinal, name)); + if (info.Num_calls_to_transform_all_at_first_definition == -1) { + // initial definition of this container + info.Num_calls_to_transform_all_at_first_definition = Num_calls_to_transform_all; + } + else if (info.Num_calls_to_transform_all_at_first_definition != Num_calls_to_transform_all) { + // extension after transform_all + raise << "there was a call to transform_all() between the definition of container " << name << " and a subsequent extension. This is not supported, since any recipes that used " << name << " values have already been transformed and 'frozen'.\n" << end(); + return; + } info.name = name; info.kind = kind; while (has_data(in)) { @@ -435,26 +471,6 @@ void skip_bracket(istream& in, string message) { raise << message << '\n' << end(); } -:(scenarios run) -:(scenario container_extend) -container foo [ - x:number -] - -# add to previous definition -container foo [ - y:number -] - -def main [ - 1:number <- copy 34 - 2:number <- copy 35 - 3:number <- get 1:foo, 0:offset - 4:number <- get 1:foo, 1:offset -] -+mem: storing 34 in location 3 -+mem: storing 35 in location 4 - //: ensure scenarios are consistent by always starting them at the same type //: number. :(before "End Setup") //: for tests @@ -462,6 +478,22 @@ Next_type_ordinal = 1000; :(before "End Test Run Initialization") assert(Next_type_ordinal < 1000); +:(code) +void test_error_on_transform_all_between_container_definition_and_extension() { + // define a container + run("container foo [\n" + " a:number\n" + "]\n"); + // try to extend the container after transform + transform_all(); + CHECK_TRACE_DOESNT_CONTAIN_ERROR(); + Hide_errors = true; + run("container foo [\n" + " b:number\n" + "]\n"); + CHECK_TRACE_CONTAINS_ERROR(); +} + //:: Allow container definitions anywhere in the codebase, but complain if you //:: can't find a definition at the end. |