about summary refs log tree commit diff stats
path: root/apps
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-09-24 21:17:25 -0700
committerKartik Agaram <vc@akkartik.com>2020-09-24 21:17:25 -0700
commit0ecd596b7040d6e3ccb5237004a46b0199e40cd0 (patch)
tree7c1311b874800dcb574710a0ae88ce7d117a7915 /apps
parenta04f816cff4a2a558a522b859f8339637db6b726 (diff)
downloadmu-0ecd596b7040d6e3ccb5237004a46b0199e40cd0.tar.gz
6853 - tile: initialize a test function definition
Diffstat (limited to 'apps')
-rw-r--r--apps/tile/data.mu56
-rw-r--r--apps/tile/rpn.mu3
-rw-r--r--apps/tile/word.mu1
3 files changed, 59 insertions, 1 deletions
diff --git a/apps/tile/data.mu b/apps/tile/data.mu
index bdd42ce5..a4ee5b4c 100644
--- a/apps/tile/data.mu
+++ b/apps/tile/data.mu
@@ -11,7 +11,8 @@ type sandbox {
 }
 
 type function {
-  args: (handle word)
+  name: (handle array byte)
+  args: (handle word)  # in reverse order
   body: (handle line)
   # some sort of indication of spatial location
   next: (handle function)
@@ -58,6 +59,8 @@ type table {
 # if 'out' is non-null, save the first word of the program there
 fn initialize-program _program: (addr program), out: (addr handle word) {
   var program/esi: (addr program) <- copy _program
+  var defs/eax: (addr handle function) <- get program, defs
+  create-primitive-defs defs
   var sandbox-ah/eax: (addr handle sandbox) <- get program, sandboxes
   allocate sandbox-ah
   var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah
@@ -97,3 +100,54 @@ fn initialize-word _self: (addr word) {
   initialize-gap-buffer data
   # TODO: sometimes initialize box-data rather than scalar-data
 }
+
+fn create-primitive-defs _self: (addr handle function) {
+  # x 2* = x 2 *
+  var self/esi: (addr handle function) <- copy _self
+  allocate self
+  var _f/eax: (addr function) <- lookup *self
+  var f/esi: (addr function) <- copy _f
+  var name-ah/eax: (addr handle array byte) <- get f, name
+  populate-text-with name-ah, "2*"
+  var args-ah/eax: (addr handle word) <- get f, args
+  allocate args-ah
+  var args/eax: (addr word) <- lookup *args-ah
+  initialize-word-with args, "x"
+  var body-ah/eax: (addr handle line) <- get f, body
+  allocate body-ah
+  var body/eax: (addr line) <- lookup *body-ah
+  initialize-line body, 0
+  var curr-word-ah/ecx: (addr handle word) <- get body, data
+  allocate curr-word-ah
+  var curr-word/eax: (addr word) <- lookup *curr-word-ah
+  initialize-word-with curr-word, "x"
+  curr-word-ah <- get curr-word, next
+  allocate curr-word-ah
+  curr-word <- lookup *curr-word-ah
+  initialize-word-with curr-word, "2"
+  curr-word-ah <- get curr-word, next
+  allocate curr-word-ah
+  curr-word <- lookup *curr-word-ah
+  initialize-word-with curr-word, "*"
+  # TODO: populate prev pointers
+}
+
+fn populate-text-with _out: (addr handle array byte), _in: (addr array byte) {
+  var in/esi: (addr array byte) <- copy _in
+  var n/ecx: int <- length in
+  var out/edx: (addr handle array byte) <- copy _out
+  populate out, n
+  var _out-addr/eax: (addr array byte) <- lookup *out
+  var out-addr/edx: (addr array byte) <- copy _out-addr
+  var i/eax: int <- copy 0
+  {
+    compare i, n
+    break-if->=
+    var src/esi: (addr byte) <- index in, i
+    var val/ecx: byte <- copy-byte *src
+    var dest/edi: (addr byte) <- index out-addr, i
+    copy-byte-to *dest, val
+    i <- increment
+    loop
+  }
+}
diff --git a/apps/tile/rpn.mu b/apps/tile/rpn.mu
index 4f9a926c..83f6e909 100644
--- a/apps/tile/rpn.mu
+++ b/apps/tile/rpn.mu
@@ -46,6 +46,9 @@ fn evaluate defs: (addr function), bindings: (addr table), scratch: (addr line),
         push-int-stack out, a
         break $evaluate:process-word
       }
+      # HERE: if curr-text is a known function name, call it appropriately
+      {
+      }
       # otherwise it's an int
       {
         var n/eax: int <- parse-decimal-int-from-stream curr-text
diff --git a/apps/tile/word.mu b/apps/tile/word.mu
index e0d20f56..d8b7778a 100644
--- a/apps/tile/word.mu
+++ b/apps/tile/word.mu
@@ -3,6 +3,7 @@
 fn initialize-word-with _self: (addr word), s: (addr array byte) {
   var self/esi: (addr word) <- copy _self
   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
+  allocate data-ah
   var data/eax: (addr gap-buffer) <- lookup *data-ah
   initialize-gap-buffer-with data, s
 }
>339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
//: Structured programming
//:
//: Our jump recipes are quite inconvenient to use, so mu provides a
//: lightweight tool called 'transform_braces' to work in a slightly more
//: convenient format with nested braces:
//:
//:   {
//:     some instructions
//:     {
//:       more instructions
//:     }
//:   }
//:
//: Braces are just labels, they require no special parsing. The pseudo
//: recipes 'loop' and 'break' jump to just after the enclosing '{' and '}'
//: respectively.
//:
//: Conditional and unconditional 'loop' and 'break' should give us 80% of the
//: benefits of the control-flow primitives we're used to in other languages,
//: like 'if', 'while', 'for', etc.

:(scenarios transform)
:(scenario brace_conversion)
recipe main [
  {
    break
    1:number <- copy 0
  }
]
+transform: --- transform braces for recipe main
+transform: jump 1:offset
+transform: copy ...

:(before "End Instruction Modifying Transforms")
Transform.push_back(transform_braces);  // idempotent

:(code)
void transform_braces(const recipe_ordinal r) {
  const int OPEN = 0, CLOSE = 1;
  // use signed integer for step index because we'll be doing arithmetic on it
  list<pair<int/*OPEN/CLOSE*/, /*step*/long long int> > braces;
  trace(9991, "transform") << "--- transform braces for recipe " << get(Recipe, r).name << end();
//?   cerr << "--- transform braces for recipe " << get(Recipe, r).name << '\n';
  for (long long int index = 0; index < SIZE(get(Recipe, r).steps); ++index) {
    const instruction& inst = get(Recipe, r).steps.at(index);
    if (inst.label == "{") {
      trace(9993, "transform") << maybe(get(Recipe, r).name) << "push (open, " << index << ")" << end();
      braces.push_back(pair<int,long long int>(OPEN, index));
    }
    if (inst.label == "}") {
      trace(9993, "transform") << "push (close, " << index << ")" << end();
      braces.push_back(pair<int,long long int>(CLOSE, index));
    }
  }
  stack</*step*/long long int> open_braces;
  for (long long int index = 0; index < SIZE(get(Recipe, r).steps); ++index) {
    instruction& inst = get(Recipe, r).steps.at(index);
    if (inst.label == "{") {
      open_braces.push(index);
      continue;
    }
    if (inst.label == "}") {
      if (open_braces.empty()) {
        raise << "missing '{' in " << get(Recipe, r).name << '\n';
        return;
      }
      open_braces.pop();
      continue;
    }
    if (inst.is_label) continue;
    if (inst.old_name != "loop"
         && inst.old_name != "loop-if"
         && inst.old_name != "loop-unless"
         && inst.old_name != "break"
         && inst.old_name != "break-if"
         && inst.old_name != "break-unless") {
      trace(9992, "transform") << inst.old_name << " ..." << end();
      continue;
    }
    // check for errors
    if (inst.old_name.find("-if") != string::npos || inst.old_name.find("-unless") != string::npos) {
      if (inst.ingredients.empty()) {
        raise_error << inst.old_name << " expects 1 or 2 ingredients, but got none\n" << end();
        continue;
      }
    }
    // update instruction operation
    if (inst.old_name.find("-if") != string::npos) {
      inst.name = "jump-if";
      inst.operation = JUMP_IF;
    }
    else if (inst.old_name.find("-unless") != string::npos) {
      inst.name = "jump-unless";
      inst.operation = JUMP_UNLESS;
    }
    else {
      inst.name = "jump";
      inst.operation = JUMP;
    }
    // check for explicitly provided targets
    if (inst.old_name.find("-if") != string::npos || inst.old_name.find("-unless") != string::npos) {
      // conditional branches check arg 1
      if (SIZE(inst.ingredients) > 1 && is_literal(inst.ingredients.at(1))) {
        trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(1).name << ":offset" << end();
        continue;
      }
    }
    else {
      // unconditional branches check arg 0
      if (!inst.ingredients.empty() && is_literal(inst.ingredients.at(0))) {
        trace(9992, "transform") << "jump " << inst.ingredients.at(0).name << ":offset" << end();
        continue;
      }
    }
    // if implicit, compute target
    reagent target;
    target.type = new type_tree(get(Type_ordinal, "offset"));
    target.properties.at(0).second = new string_tree("offset");
    target.set_value(0);
    if (open_braces.empty())
      raise_error << inst.old_name << " needs a '{' before\n" << end();
    else if (inst.old_name.find("loop") != string::npos)
      target.set_value(open_braces.top()-index);
    else  // break instruction
      target.set_value(matching_brace(open_braces.top(), braces, r) - index - 1);
    inst.ingredients.push_back(target);
    // log computed target
    if (inst.name == "jump")
      trace(9992, "transform") << "jump " << no_scientific(target.value) << ":offset" << end();
    else
      trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(0).name << ", " << no_scientific(target.value) << ":offset" << end();
  }
}

// returns a signed integer not just so that we can return -1 but also to
// enable future signed arithmetic
long long int matching_brace(long long int index, const list<pair<int, long long int> >& braces, recipe_ordinal r) {
  int stacksize = 0;
  for (list<pair<int, long long int> >::const_iterator p = braces.begin(); p != braces.end(); ++p) {
    if (p->second < index) continue;
    stacksize += (p->first ? 1 : -1);
    if (stacksize == 0) return p->second;
  }
  raise_error << maybe(get(Recipe, r).name) << "unbalanced '{'\n" << end();
  return SIZE(get(Recipe, r).steps);  // exit current routine
}

:(scenario loop)
recipe main [
  1:number <- copy 0
  2:number <- copy 0
  {
    3:number <- copy 0
    loop
  }
]
+transform: --- transform braces for recipe main
+transform: copy ...
+transform: copy ...
+transform: copy ...
+transform: jump -2:offset

:(scenario break_empty_block)
recipe main [
  1:number <- copy 0
  {
    break
  }
]
+transform: --- transform braces for recipe main
+transform: copy ...
+transform: jump 0:offset

:(scenario break_cascading)
recipe main [
  1:number <- copy 0
  {
    break
  }
  {
    break
  }
]
+transform: --- transform braces for recipe main
+transform: copy ...
+transform: jump 0:offset
+transform: jump 0:offset

:(scenario break_cascading_2)
recipe main [
  1:number <- copy 0
  2:number <- copy 0
  {
    break
    3:number <- copy 0
  }
  {
    break
  }
]
+transform: --- transform braces for recipe main
+transform: copy ...
+transform: copy ...
+transform: jump 1:offset
+transform: copy ...
+transform: jump 0:offset

:(scenario break_if)
recipe main [
  1:number <- copy 0
  2:number <- copy 0
  {
    break-if 2:number
    3:number <- copy 0
  }
  {
    break
  }
]
+transform: --- transform braces for recipe main
+transform: copy ...
+transform: copy ...
+transform: jump-if 2, 1:offset
+transform: copy ...
+transform: jump 0:offset

:(scenario break_nested)
recipe main [
  1:number <- copy 0
  {
    2:number <- copy 0
    break
    {
      3:number <- copy 0
    }
    4:number <- copy 0
  }
]
+transform: jump 4:offset

:(scenario break_nested_degenerate)
recipe main [
  1:number <- copy 0
  {
    2:number <- copy 0
    break
    {
    }
    4:number <- copy 0
  }
]
+transform: jump 3:offset

:(scenario break_nested_degenerate_2)
recipe main [
  1:number <- copy 0
  {
    2:number <- copy 0
    break
    {
    }
  }
]
+transform: jump 2:offset

:(scenario break_label)
% Hide_errors = true;
recipe main [
  1:number <- copy 0
  {
    break +foo:offset
  }
]
+transform: jump +foo:offset

:(scenario break_unless)
recipe main [
  1:number <- copy 0
  2:number <- copy 0
  {
    break-unless 2:number
    3:number <- copy 0
  }
]
+transform: --- transform braces for recipe main
+transform: copy ...
+transform: copy ...
+transform: jump-unless 2, 1:offset
+transform: copy ...

:(scenario loop_unless)
recipe main [
  1:number <- copy 0
  2:number <- copy 0
  {
    loop-unless 2:number
    3:number <- copy 0
  }
]
+transform: --- transform braces for recipe main
+transform: copy ...
+transform: copy ...
+transform: jump-unless 2, -1:offset
+transform: copy ...

:(scenario loop_nested)
recipe main [
  1:number <- copy 0
  {
    2:number <- copy 0
    {
      3:number <- copy 0
    }
    loop-if 4:boolean
    5:number <- copy 0
  }
]
+transform: --- transform braces for recipe main
+transform: jump-if 4, -5:offset

:(scenario loop_label)
recipe main [
  1:number <- copy 0
  +foo
  2:number <- copy 0
]
+transform: --- transform braces for recipe main
+transform: copy ...
+transform: copy ...

//: test how things actually run
:(scenarios run)
:(scenario brace_conversion_and_run)
recipe test-factorial [
  1:number <- copy 5
  2:number <- copy 1
  {
    3:boolean <- equal 1:number, 1
    break-if 3:boolean
#    $print 1:number
    2:number <- multiply 2:number, 1:number
    1:number <- subtract 1:number, 1
    loop
  }
  4:number <- copy 2:number  # trigger a read
]
+mem: location 2 is 120

:(scenario break_outside_braces_fails)
% Hide_errors = true;
recipe main [
  break
]
+error: break needs a '{' before

:(scenario break_conditional_without_ingredient_fails)
% Hide_errors = true;
recipe main [
  {
    break-if
  }
]
+error: break-if expects 1 or 2 ingredients, but got none

//: Make sure these pseudo recipes get consistent numbers in all tests, even
//: though they aren't implemented. Allows greater flexibility in ordering
//: transforms.

:(before "End Primitive Recipe Declarations")
BREAK,
BREAK_IF,
BREAK_UNLESS,
LOOP,
LOOP_IF,
LOOP_UNLESS,
:(before "End Primitive Recipe Numbers")
put(Recipe_ordinal, "break", BREAK);
put(Recipe_ordinal, "break-if", BREAK_IF);
put(Recipe_ordinal, "break-unless", BREAK_UNLESS);
put(Recipe_ordinal, "loop", LOOP);
put(Recipe_ordinal, "loop-if", LOOP_IF);
put(Recipe_ordinal, "loop-unless", LOOP_UNLESS);
:(before "End Primitive Recipe Checks")
case BREAK: break;
case BREAK_IF: break;
case BREAK_UNLESS: break;
case LOOP: break;
case LOOP_IF: break;
case LOOP_UNLESS: break;