about summary refs log tree commit diff stats
path: root/linux/translate_subx_debug
blob: e18eca3156ef0fe8e7817b711b5c2fcfb34f5e19 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/bin/sh
# Translate given SubX files with debug information on Linux.
#
# Mu provides 3 canonical ways to translate unsafe SubX programs:
#   0. The C++ translator 'bootstrap translate' can generate traces for
#   debugging on Linux or BSD or Mac, but doesn't support any syntax sugar.
#   1. The self-hosted translator can be run natively on Linux using
#   'translate_subx'. It is fast and supports all syntax sugar, but you get no
#   trace for debugging.
#   2. The self-hosted translator can be run emulated on Linux or BSD or Mac
#   using 'translate_subx_emulated'. It supports all syntax sugar. However, it
#   can be slow if you generate traces.
#
# This script fills in the gap above by stitching together aspects from
# multiple approaches. It translates syntax sugar natively, but emulates lower
# levels using the C++ translator. The result is complete and relatively fast
# even when generating traces.
#
# The cost: it needs Linux. There is no script to generate traces while
# running emulated on BSD or Mac. That's often impractically slow.

set -e

cat $*          |./braces          > a.braces
cat a.braces    |./calls           > a.calls
cat a.calls     |./sigils          > a.sigils

bootstrap/bootstrap --debug translate a.sigils -o a.elf

chmod +x a.elf
hlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .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 */
//: Continuations are a powerful primitive for constructing advanced kinds of
//: control *policies* like back-tracking. They're usually provided using a
//: primitive called 'call-cc': http://en.wikipedia.org/wiki/Call-with-current-continuation)
//: But in mu 'call-cc' is constructed out of a combination of two primitives:
//:   'current-continuation', which returns a continuation, and
//:   'continue-from', which takes a continuation to switch to.

//: todo: implement continuations in mu's memory
:(before "End Globals")
map<long long int, call_stack> Continuation;
long long int Next_continuation_id = 0;
:(before "End Setup")
Continuation.clear();
Next_continuation_id = 0;

:(before "End Mu Types Initialization")
type_number continuation = Type_number["continuation"] = Next_type_number++;
Type[continuation].name = "continuation";

:(before "End Primitive Recipe Declarations")
CURRENT_CONTINUATION,
:(before "End Primitive Recipe Numbers")
Recipe_number["current-continuation"] = CURRENT_CONTINUATION;
:(before "End Primitive Recipe Implementations")
case CURRENT_CONTINUATION: {
  // copy the current call stack
  Continuation[Next_continuation_id] = Current_routine->calls;  // deep copy because calls have no pointers
  // make sure calling the copy doesn't spawn the same continuation again
  ++Continuation[Next_continuation_id].front().running_step_index;
  products.resize(1);
  products.at(0).push_back(Next_continuation_id);
  ++Next_continuation_id;
  trace("current-continuation") << "new continuation " << Next_continuation_id;
  break;
}

:(before "End Primitive Recipe Declarations")
CONTINUE_FROM,
:(before "End Primitive Recipe Numbers")
Recipe_number["continue-from"] = CONTINUE_FROM;
:(before "End Primitive Recipe Implementations")
case CONTINUE_FROM: {
  assert(scalar(ingredients.at(0)));
  long long int c = ingredients.at(0).at(0);
  Current_routine->calls = Continuation[c];  // deep copy because calls have no pointers
  // refresh instruction_counter to next instruction after current-continuation
  instruction_counter = current_step_index()+1;
  continue;  // not done with caller; don't increment current_step_index() further
}

:(scenario continuation)
# simulate a loop using continuations
recipe main [
  1:number <- copy 0:literal
  2:continuation <- current-continuation
  {
#?     $print 1:number
    3:boolean <- greater-or-equal 1:number, 3:literal
    break-if 3:boolean
    1:number <- add 1:number, 1:literal
    continue-from 2:continuation  # loop
  }
]
+mem: storing 1 in location 1
+mem: storing 2 in location 1
+mem: storing 3 in location 1
-mem: storing 4 in location 1
# ensure every iteration doesn't copy the stack over and over
$current-continuation: 1

:(scenario continuation_inside_caller)
recipe main [
  1:number <- copy 0:literal
  2:continuation <- loop-body
  {
    3:boolean <- greater-or-equal 1:number, 3:literal
    break-if 3:boolean
    continue-from 2:continuation  # loop
  }
]

recipe loop-body [
  4:continuation <- current-continuation
  1:number <- add 1:number, 1:literal
]
+mem: storing 1 in location 1
+mem: storing 2 in location 1
+mem: storing 3 in location 1
-mem: storing 4 in location 1

//:: A variant of continuations is the 'delimited' continuation that can be called like any other recipe.
//:
//: In mu, this is constructed out of two primitives:
//:
//:  * 'create-delimited-continuation' lays down a mark on the call
//:    stack and calls the provided function (it is sometimes called 'prompt')
//:  * 'reply-current-continuation' copies the top of the stack until the
//:    mark, and returns it as the response of create-delimited-continuation
//:    (which might be a distant ancestor on the call stack; intervening calls
//:    don't return)
//:
//: The resulting slice of the stack can now be called just like a regular
//: function.

:(scenario delimited_continuation)
recipe main [
  1:continuation <- create-delimited-continuation f:recipe 12:literal  # 12 is an argument to f
  2:number <- copy 5:literal
  {
    2:number <- call 1:continuation, 2:number  # 2 is an argument to g, the 'top' of the continuation
    3:boolean <- greater-or-equal 2:number, 8:literal
    break-if 3:boolean
    loop
  }
]

recipe f [
  11:number <- next-ingredient
  12:number <- g 11:number
  reply 12:number
]

recipe g [
  21:number <- next-ingredient
  rewind-ingredients
  reply-delimited-continuation
  # calls of the continuation start from here
  22:number <- next-ingredient
  23:number <- add 22:number, 1:literal
  reply 23:number
]
#? ?
# first call of 'g' executes the part before reply-delimited-continuation
+mem: storing 12 in location 21
+run: 2:number <- copy 5:literal
+mem: storing 5 in location 2
# calls of the continuation execute the part after reply-delimited-continuation
+run: 2:number <- call 1:continuation, 2:number
+mem: storing 5 in location 22
+mem: storing 6 in location 2
+run: 2:number <- call 1:continuation, 2:number
+mem: storing 6 in location 22
+mem: storing 7 in location 2
+run: 2:number <- call 1:continuation, 2:number
+mem: storing 7 in location 22
+mem: storing 8 in location 2
# first call of 'g' does not execute the part after reply-delimited-continuation
-mem: storing 12 in location 22
# calls of the continuation don't execute the part before reply-delimited-continuation
-mem: storing 5 in location 21
-mem: storing 6 in location 21
-mem: storing 7 in location 21
# termination
-mem: storing 9 in location 2

//: 'create-delimited-continuation' is like 'call' except it adds a 'reset' mark to
//: the call stack

:(before "End call Fields")
bool is_reset;
:(before "End call Constructor")
is_reset = false;

//: like call, but mark the current call as a 'reset' call before pushing the next one on it

:(before "End Primitive Recipe Declarations")
CREATE_DELIMITED_CONTINUATION,
:(before "End Primitive Recipe Numbers")
Recipe_number["create-delimited-continuation"] = CREATE_DELIMITED_CONTINUATION;
:(before "End Primitive Recipe Implementations")
case CREATE_DELIMITED_CONTINUATION: {
  Current_routine->calls.front().is_reset = true;
  Current_routine->calls.push_front(call(Recipe_number[current_instruction().ingredients.at(0).name]));
  ingredients.erase(ingredients.begin());  // drop the callee
  goto complete_call;
}

//: save the slice of current call stack until the 'create-delimited-continuation'
//: call, and return it as the result.
//: todo: implement delimited continuations in mu's memory
:(before "End Globals")
map<long long int, call_stack> Delimited_continuation;
long long int Next_delimited_continuation_id = 0;
:(before "End Setup")
Delimited_continuation.clear();
Next_delimited_continuation_id = 0;

:(before "End Primitive Recipe Declarations")
REPLY_DELIMITED_CONTINUATION,
:(before "End Primitive Recipe Numbers")
Recipe_number["reply-delimited-continuation"] = REPLY_DELIMITED_CONTINUATION;
:(before "End Primitive Recipe Implementations")
case REPLY_DELIMITED_CONTINUATION: {
  // first clear any existing ingredients, to isolate the creation of the
  // continuation from its calls
  Current_routine->calls.front().ingredient_atoms.clear();
  Current_routine->calls.front().next_ingredient_to_process = 0;
  // copy the current call stack until the most recent 'reset' call
  call_stack::iterator find_reset(call_stack& c);  // manual prototype containing '::'
  call_stack::iterator reset = find_reset(Current_routine->calls);
  assert(reset != Current_routine->calls.end());
  Delimited_continuation[Next_delimited_continuation_id] = call_stack(Current_routine->calls.begin(), reset);
  while (Current_routine->calls.begin() != reset) {
    --Callstack_depth;
    Current_routine->calls.pop_front();
  }
  // return it as the result of the 'reset' call
  products.resize(1);
  products.at(0).push_back(Next_delimited_continuation_id);
  ++Next_delimited_continuation_id;
  // refresh instruction_counter to caller's step_index
  instruction_counter = current_step_index();
  break;
}

:(code)
call_stack::iterator find_reset(call_stack& c) {
  for (call_stack::iterator p = c.begin(); p != c.end(); ++p)
    if (p->is_reset) return p;
  return c.end();
}

//: overload 'call' for continuations
:(after "case CALL:")
  if (current_instruction().ingredients.at(0).properties.at(0).second.at(0) == "continuation") {
    // copy multiple calls on to current call stack
    assert(scalar(ingredients.at(0)));
    assert(Delimited_continuation.find(ingredients.at(0).at(0)) != Delimited_continuation.end());
    const call_stack& new_calls = Delimited_continuation[ingredients.at(0).at(0)];
    for (call_stack::const_reverse_iterator p = new_calls.rbegin(); p != new_calls.rend(); ++p)
      Current_routine->calls.push_front(*p);
    ++current_step_index();  // skip past the reply-delimited-continuation
    ingredients.erase(ingredients.begin());  // drop the callee
    goto complete_call;
  }