about summary refs log tree commit diff stats
path: root/subx/054string-equal.subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-02-14 16:24:20 -0800
committerKartik Agaram <vc@akkartik.com>2019-02-14 16:24:20 -0800
commit1639687ba098aa81b0584f7dd609cb9690dc5a04 (patch)
tree1a5ee40c30bf906c6ba5e55b8c5138a467022105 /subx/054string-equal.subx
parent1ab48a69ccfa4ddaa2e1fa803ea6fe568b890abc (diff)
downloadmu-1639687ba098aa81b0584f7dd609cb9690dc5a04.tar.gz
4961
Diffstat (limited to 'subx/054string-equal.subx')
-rw-r--r--subx/054string-equal.subx43
1 files changed, 25 insertions, 18 deletions
diff --git a/subx/054string-equal.subx b/subx/054string-equal.subx
index fdfca84e..f69c0c0f 100644
--- a/subx/054string-equal.subx
+++ b/subx/054string-equal.subx
@@ -13,18 +13,27 @@
     b8/copy-to-EAX  1/imm32/exit
     cd/syscall  0x80/imm8
 
-string-equal?:  # s : string, benchmark : string -> EAX : boolean
+string-equal?:  # s : (address string), benchmark : (address string) -> EAX : boolean
     # pseudocode:
-    #   if s->length != b->length return false
-    #   for i = 0;  i < s->length;  ++i
-    #     if s[i] != b[i] return false
+    #   lens = s->length
+    #   if (lens != benchmark->length) return false
+    #   i = 0
+    #   currs = s->data
+    #   currb = benchmark->data
+    #   while (i < s->length)
+    #     c1 = *currs
+    #     c2 = *currb
+    #     if (c1 != c2) return false
+    #     ++i, ++currs, ++currb
     #   return true
+    #
     # registers:
     #   i: ECX
-    #   s->length: EDX
-    #   b->length: EBX
-    #   b[i]: EBX
-    #   s[i]: EAX
+    #   lens: EDX
+    #   currs: EAX
+    #   currb: EBX
+    #   c1: ESI
+    #   c2: EDI
     #
     # . prolog
     55/push-EBP
@@ -34,17 +43,16 @@ string-equal?:  # s : string, benchmark : string -> EAX : boolean
     52/push-EDX
     53/push-EBX
     56/push-ESI
-    # var s/EAX : (address array byte)
+    # EAX = s
     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
-    # var benchmark/EBX : (address array byte)
+    # EBX = benchmark
     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
-    # if s->length != b->length return false
-    # EDX = s->length
+    # lens/EDX = s->length
     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # copy *EAX to EDX
-    # compare s->length and b->length
+$string-equal?:lengths:
+    # if (lens != benchmark->length) return false
     39/compare                      0/mod/indirect  3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare *EBX with EDX
     75/jump-if-not-equal  $string-equal?:false/disp8
-$string-equal?:lengths:
     # var i/ECX : int = 0
     b9/copy-to-ECX  0/imm32
     # EBX = &b[i]
@@ -52,7 +60,7 @@ $string-equal?:lengths:
     # EAX = &s[i]
     40/inc-EAX
 $string-equal?:loop:
-    # if i >= s->length return true
+    # if (i >= lens) return true
     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
     7d/jump-if-greater-or-equal  $string-equal?:true/disp8
     # if b[i] != s[i] return false
@@ -63,16 +71,15 @@ $string-equal?:loop:
     75/jump-if-not-equal  $string-equal?:false/disp8
     # ++i
     41/inc-ECX
+    # ++c1
     40/inc-EAX
+    # ++c2
     43/inc-EBX
-    # loop
     eb/jump  $string-equal?:loop/disp8
 $string-equal?:true:
-    # return true
     b8/copy-to-EAX  1/imm32
     eb/jump  $string-equal?:end/disp8
 $string-equal?:false:
-    # return false
     b8/copy-to-EAX  0/imm32
 $string-equal?:end:
     # . restore registers
espace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
//: For convenience, allow Mu types to be abbreviated.

:(scenarios transform)
:(scenario type_abbreviations)
type foo = number
def main [
  a:foo <- copy 34
]
+transform: product type after expanding abbreviations: "number"

:(before "End Globals")
map<string, type_tree*> Type_abbreviations, Type_abbreviations_snapshot;

//:: Defining type abbreviations.

:(before "End Command Handlers")
else if (command == "type") {
  load_type_abbreviations(in);
}

:(code)
void load_type_abbreviations(istream& in) {
  string new_type_name = next_word(in);
  assert(has_data(in) || !new_type_name.empty());
  if (!has_data(in) || new_type_name.empty()) {
    raise << "incomplete 'type' statement; must be of the form 'type <new type name> = <type expression>'\n" << end();
    return;
  }
  string arrow = next_word(in);
  assert(has_data(in) || !arrow.empty());
  if (arrow.empty()) {
    raise << "incomplete 'type' statement 'type " << new_type_name << "'\n" << end();
    return;
  }
  if (arrow != "=") {
    raise << "'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type " << new_type_name << ' ' << arrow << "'\n" << end();
    return;
  }
  if (!has_data(in)) {
    raise << "incomplete 'type' statement 'type " << new_type_name << " ='\n" << end();
    return;
  }
  string old = next_word(in);
  if (old.empty()) {
    raise << "incomplete 'type' statement 'type " << new_type_name << " ='\n" << end();
    raise << "'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type " << new_type_name << ' ' << arrow << "'\n" << end();
    return;
  }
  if (contains_key(Type_abbreviations, new_type_name)) {
    raise << "'type' conflict: '" << new_type_name << "' defined as both '" << names_to_string_without_quotes(get(Type_abbreviations, new_type_name)) << "' and '" << old << "'\n" << end();
    return;
  }
  trace(9990, "type") << "alias " << new_type_name << " = " << old << end();
  type_tree* old_type = new_type_tree(old);
  put(Type_abbreviations, new_type_name, old_type);
}

type_tree* new_type_tree(const string& x) {
  string_tree* type_names = starts_with(x, "(") ? parse_string_tree(x) : parse_string_list(x);
  type_tree* result = new_type_tree(type_names);
  delete type_names;
  expand_type_abbreviations(result);
  return result;
}

string_tree* parse_string_list(const string& s) {
  istringstream in(s);
  in >> std::noskipws;
  return parse_property_list(in);
}

:(scenario type_error1)
% Hide_errors = true;
type foo
+error: incomplete 'type' statement 'type foo'

:(scenario type_error2)
% Hide_errors = true;
type foo =
+error: incomplete 'type' statement 'type foo ='

:(scenario type_error3)
% Hide_errors = true;
type foo bar baz
+error: 'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type foo bar'

:(scenario type_conflict_error)
% Hide_errors = true;
type foo = bar
type foo = baz
+error: 'type' conflict: 'foo' defined as both 'bar' and 'baz'

:(scenario type_abbreviation_for_compound)
type foo = address:number
def main [
  1:foo <- copy null
]
+transform: product type after expanding abbreviations: ("address" "number")

//: cleaning up type abbreviations between tests and before exiting

:(before "End save_snapshots")
Type_abbreviations_snapshot = Type_abbreviations;
:(before "End restore_snapshots")
restore_type_abbreviations();
:(before "End One-time Setup")
atexit(clear_type_abbreviations);
:(code)
void restore_type_abbreviations() {
  for (map<string, type_tree*>::iterator p = Type_abbreviations.begin();  p != Type_abbreviations.end();  ++p) {
    if (!contains_key(Type_abbreviations_snapshot, p->first))
      delete p->second;
  }
  Type_abbreviations.clear();
  Type_abbreviations = Type_abbreviations_snapshot;
}
void clear_type_abbreviations() {
  for (map<string, type_tree*>::iterator p = Type_abbreviations.begin();  p != Type_abbreviations.end();  ++p)
    delete p->second;
  Type_abbreviations.clear();
}

//:: A few default abbreviations.

:(before "End Mu Types Initialization")
put(Type_abbreviations, "&", new_type_tree("address"));
put(Type_abbreviations, "@", new_type_tree("array"));
put(Type_abbreviations, "num", new_type_tree("number"));
put(Type_abbreviations, "bool", new_type_tree("boolean"));
put(Type_abbreviations, "char", new_type_tree("character"));

:(scenario use_type_abbreviations_when_declaring_type_abbreviations)
type foo = &:num
def main [
  1:foo <- copy null
]
+transform: product type after expanding abbreviations: ("address" "number")

//:: Expand type aliases before running.
//: We'll do this in a transform so that we don't need to define abbreviations
//: before we use them.

:(scenario abbreviations_for_address_and_array)
def main [
  f 1:&:num  # abbreviation for 'address:number'
  f 2:@:num  # abbreviation for 'array:number'
  f 3:&:@:num  # combining '&' and '@'
  f 4:&:&:@:&:@:num  # ..any number of times
  f {5: (array (& num) 3)}  # support for dilated reagents and more complex parse trees
]
def f [
]
+transform: --- expand type abbreviations in recipe 'main'
+transform: ingredient type after expanding abbreviations: ("address" "number")
+transform: ingredient type after expanding abbreviations: ("array" "number")
+transform: ingredient type after expanding abbreviations: ("address" "array" "number")
+transform: ingredient type after expanding abbreviations: ("address" "address" "array" "address" "array" "number")
+transform: ingredient type after expanding abbreviations: ("array" ("address" "number") "3")

:(before "Transform.push_back(update_instruction_operations)")
Transform.push_back(expand_type_abbreviations);  // idempotent
// Begin Type Modifying Transforms
// End Type Modifying Transforms

:(code)
void expand_type_abbreviations(const recipe_ordinal r) {
  expand_type_abbreviations(get(Recipe, r));
}

void expand_type_abbreviations(const recipe& caller) {
  trace(9991, "transform") << "--- expand type abbreviations in recipe '" << caller.name << "'" << end();
  for (int i = 0;  i < SIZE(caller.steps);  ++i) {
    const instruction& inst = caller.steps.at(i);
    trace(9991, "transform") << "instruction '" << to_original_string(inst) << end();
    for (long int i = 0;  i < SIZE(inst.ingredients);  ++i) {
      expand_type_abbreviations(inst.ingredients.at(i).type);
      trace(9992, "transform") << "ingredient type after expanding abbreviations: " << names_to_string(inst.ingredients.at(i).type) << end();
    }
    for (long int i = 0;  i < SIZE(inst.products);  ++i) {
      expand_type_abbreviations(inst.products.at(i).type);
      trace(9992, "transform") << "product type after expanding abbreviations: " << names_to_string(inst.products.at(i).type) << end();
    }
  }
  // End Expand Type Abbreviations(caller)
}

void expand_type_abbreviations(type_tree* type) {
  if (!type) return;
  if (!type->atom) {
    expand_type_abbreviations(type->left);
    expand_type_abbreviations(type->right);
    return;
  }
  if (contains_key(Type_abbreviations, type->name))
    *type = type_tree(*get(Type_abbreviations, type->name));
}