about summary refs log blame commit diff stats
path: root/util.c
blob: abd82defb07cddee6a50117452a15ee94ce19a8f (plain) (tree)
; product, const recipe& caller, const instruction& inst) { int ingredient_index = 0; merge_check_state state; state.data.push(merge_check_point(product, 0)); while (true) { assert(!state.data.empty()); trace(102, "transform") << ingredient_index << " vs " << SIZE(ingredients) << end(); if (ingredient_index >= SIZE(ingredients)) { raise << maybe(caller.name) << "too few ingredients in '" << to_original_string(inst) << "'\n" << end(); return; } reagent& container = state.data.top().container; if (!container.type) return; // error handled elsewhere const type_tree* top_root_type = container.type->atom ? container.type : container.type->left; assert(top_root_type->atom); type_info& container_info = get(Type, top_root_type->value); switch (container_info.kind) { case CONTAINER: { // degenerate case: merge with the same type always succeeds if (state.data.top().container_element_index == 0 && types_coercible(container, inst.ingredients.at(ingredient_index))) return; const reagent& expected_ingredient = element_type(container.type, state.data.top().container_element_index); trace(102, "transform") << "checking container " << to_string(container) << " || " << to_string(expected_ingredient) << " vs ingredient " << ingredient_index << end(); // if the current element is the ingredient we expect, move on to the next element/ingredient if (types_coercible(expected_ingredient, ingredients.at(ingredient_index))) { ++ingredient_index; ++state.data.top().container_element_index; while (state.data.top().container_element_index >= SIZE(get(Type, get_base_type(state.data.top().container.type)->value).elements)) { state.data.pop(); if (state.data.empty()) { if (ingredient_index < SIZE(ingredients)) raise << maybe(caller.name) << "too many ingredients in '" << to_original_string(inst) << "'\n" << end(); return; } ++state.data.top().container_element_index; } } // if not, maybe it's a field of the current element else { // no change to ingredient_index state.data.push(merge_check_point(expected_ingredient, 0)); } break; } // End check_merge_call Special-cases default: { if (!types_coercible(container, ingredients.at(ingredient_index))) { raise << maybe(caller.name) << "incorrect type of ingredient " << ingredient_index << " in '" << to_original_string(inst) << "'\n" << end(); raise << " (expected '" << debug_string(container) << "')\n" << end(); raise << " (got '" << debug_string(ingredients.at(ingredient_index)) << "')\n" << end(); return; } ++ingredient_index; // ++state.data.top().container_element_index; // unnecessary, but wouldn't do any harm do { state.data.pop(); if (state.data.empty()) { if (ingredient_index < SIZE(ingredients)) raise << maybe(caller.name) << "too many ingredients in '" << to_original_string(inst) << "'\n" << end(); return; } ++state.data.top().container_element_index; } while (state.data.top().container_element_index >= SIZE(get(Type, get_base_type(state.data.top().container.type)->value).elements)); } } } // never gets here assert(false); } //: replaced in a later layer //: todo: find some clean way to take this call completely out of this layer const type_tree* get_base_type(const type_tree* t) { return t; } void test_merge_check_product() { Hide_errors = true; run( "def main [\n" " 1:num <- merge 3\n" "]\n" ); CHECK_TRACE_CONTENTS( "error: main: 'merge' should yield a container in '1:num <- merge 3'\n" ); } :(before "End Includes") #include <stack> using std::stack;