about summary refs log tree commit diff stats
path: root/027call_ingredient.cc
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-04-23 22:11:48 -0700
committerKartik K. Agaram <vc@akkartik.com>2016-04-24 00:35:50 -0700
commitc9a5a7badacd8dba9b5d7cf683301c8ead4b1f12 (patch)
tree3062a12c4fda9757ef633e56f0abc8b8efbe594b /027call_ingredient.cc
parent7bf9212fd47e505711cc0873d30456e3d793034d (diff)
downloadmu-c9a5a7badacd8dba9b5d7cf683301c8ead4b1f12.tar.gz
2862
Layers 0-29 are now a complete rudimentary platform except for pointers
and indirection.
Diffstat (limited to '027call_ingredient.cc')
-rw-r--r--027call_ingredient.cc185
1 files changed, 185 insertions, 0 deletions
diff --git a/027call_ingredient.cc b/027call_ingredient.cc
new file mode 100644
index 00000000..f109076b
--- /dev/null
+++ b/027call_ingredient.cc
@@ -0,0 +1,185 @@
+//: Calls can take ingredients just like primitives. To access a recipe's
+//: ingredients, use 'next-ingredient'.
+
+:(scenario next_ingredient)
+def main [
+  f 2
+]
+def f [
+  12:number <- next-ingredient
+  13:number <- add 1, 12:number
+]
++mem: storing 3 in location 13
+
+:(scenario next_ingredient_missing)
+def main [
+  f
+]
+def f [
+  _, 12:number <- next-ingredient
+]
++mem: storing 0 in location 12
+
+:(before "End call Fields")
+vector<vector<double> > ingredient_atoms;
+vector<reagent> ingredients;
+int next_ingredient_to_process;
+:(before "End call Constructor")
+next_ingredient_to_process = 0;
+
+:(before "End Call Housekeeping")
+for (int i = 0; i < SIZE(ingredients); ++i) {
+  current_call().ingredient_atoms.push_back(ingredients.at(i));
+  reagent ingredient = call_instruction.ingredients.at(i);
+  // End Compute Call Ingredient
+  current_call().ingredients.push_back(ingredient);
+}
+
+:(before "End Primitive Recipe Declarations")
+NEXT_INGREDIENT,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "next-ingredient", NEXT_INGREDIENT);
+:(before "End Primitive Recipe Checks")
+case NEXT_INGREDIENT: {
+  if (!inst.ingredients.empty()) {
+    raise << maybe(get(Recipe, r).name) << "'next-ingredient' didn't expect any ingredients in '" << to_original_string(inst) << "'\n" << end();
+    break;
+  }
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case NEXT_INGREDIENT: {
+  assert(!Current_routine->calls.empty());
+  if (current_call().next_ingredient_to_process < SIZE(current_call().ingredient_atoms)) {
+    reagent product = current_instruction().products.at(0);
+    // End Preprocess NEXT_INGREDIENT product
+    if (current_recipe_name() == "main") {
+      // no ingredient types since the call might be implicit; assume ingredients are always strings
+      // todo: how to test this?
+      if (!is_mu_string(product))
+        raise << "main: wrong type for ingredient " << product.original_string << '\n' << end();
+    }
+    else if (!types_coercible(product,
+                              current_call().ingredients.at(current_call().next_ingredient_to_process))) {
+      raise << maybe(current_recipe_name()) << "wrong type for ingredient " << product.original_string << '\n' << end();
+      // End next-ingredient Type Mismatch Error
+    }
+    products.push_back(
+        current_call().ingredient_atoms.at(current_call().next_ingredient_to_process));
+    assert(SIZE(products) == 1);  products.resize(2);  // push a new vector
+    products.at(1).push_back(1);
+    ++current_call().next_ingredient_to_process;
+  }
+  else {
+    if (SIZE(current_instruction().products) < 2)
+      raise << maybe(current_recipe_name()) << "no ingredient to save in " << current_instruction().products.at(0).original_string << '\n' << end();
+    if (current_instruction().products.empty()) break;
+    products.resize(2);
+    // pad the first product with sufficient zeros to match its type
+    int size = size_of(current_instruction().products.at(0));
+    for (int i = 0; i < size; ++i)
+      products.at(0).push_back(0);
+    products.at(1).push_back(0);
+  }
+  break;
+}
+
+:(scenario next_ingredient_fail_on_missing)
+% Hide_errors = true;
+def main [
+  f
+]
+def f [
+  11:number <- next-ingredient
+]
++error: f: no ingredient to save in 11:number
+
+:(scenario rewind_ingredients)
+def main [
+  f 2
+]
+def f [
+  12:number <- next-ingredient  # consume ingredient
+  _, 1:boolean <- next-ingredient  # will not find any ingredients
+  rewind-ingredients
+  13:number, 2:boolean <- next-ingredient  # will find ingredient again
+]
++mem: storing 2 in location 12
++mem: storing 0 in location 1
++mem: storing 2 in location 13
++mem: storing 1 in location 2
+
+:(before "End Primitive Recipe Declarations")
+REWIND_INGREDIENTS,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "rewind-ingredients", REWIND_INGREDIENTS);
+:(before "End Primitive Recipe Checks")
+case REWIND_INGREDIENTS: {
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case REWIND_INGREDIENTS: {
+  current_call().next_ingredient_to_process = 0;
+  break;
+}
+
+:(scenario ingredient)
+def main [
+  f 1, 2
+]
+def f [
+  12:number <- ingredient 1  # consume second ingredient first
+  13:number, 1:boolean <- next-ingredient  # next-ingredient tries to scan past that
+]
++mem: storing 2 in location 12
++mem: storing 0 in location 1
+
+:(before "End Primitive Recipe Declarations")
+INGREDIENT,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "ingredient", INGREDIENT);
+:(before "End Primitive Recipe Checks")
+case INGREDIENT: {
+  if (SIZE(inst.ingredients) != 1) {
+    raise << maybe(get(Recipe, r).name) << "'ingredient' expects exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
+    break;
+  }
+  if (!is_literal(inst.ingredients.at(0)) && !is_mu_number(inst.ingredients.at(0))) {
+    raise << maybe(get(Recipe, r).name) << "'ingredient' expects a literal ingredient, but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    break;
+  }
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case INGREDIENT: {
+  if (static_cast<int>(ingredients.at(0).at(0)) < SIZE(current_call().ingredient_atoms)) {
+    current_call().next_ingredient_to_process = ingredients.at(0).at(0);
+    products.push_back(
+        current_call().ingredient_atoms.at(current_call().next_ingredient_to_process));
+    assert(SIZE(products) == 1);  products.resize(2);  // push a new vector
+    products.at(1).push_back(1);
+    ++current_call().next_ingredient_to_process;
+  }
+  else {
+    if (SIZE(current_instruction().products) > 1) {
+      products.resize(2);
+      products.at(0).push_back(0);  // todo: will fail noisily if we try to read a compound value
+      products.at(1).push_back(0);
+    }
+  }
+  break;
+}
+
+//: a particularly common array type is the string, or address:array:character
+:(code)
+bool is_mu_string(const reagent& x) {
+  return x.type
+    && x.type->value == get(Type_ordinal, "address")
+    && x.type->right
+    && x.type->right->value == get(Type_ordinal, "shared")
+    && x.type->right->right
+    && x.type->right->right->value == get(Type_ordinal, "array")
+    && x.type->right->right->right
+    && x.type->right->right->right->value == get(Type_ordinal, "character")
+    && x.type->right->right->right->right == NULL;
+}