3561
This commit is contained in:
parent
22f4b76344
commit
9a81d7460f
|
@ -1,6 +1,6 @@
|
|||
//: A simple test harness. To create new tests define functions starting with
|
||||
//: 'test_'. To run all tests so defined, run:
|
||||
//: $ mu test
|
||||
//: $ ./mu test
|
||||
//:
|
||||
//: Every layer should include tests, and can reach into previous layers.
|
||||
//: However, it seems like a good idea never to reach into tests from previous
|
||||
|
|
|
@ -99,7 +99,7 @@ Dump_trace = false;
|
|||
// compilation units. So no extern linkage.
|
||||
const int Max_depth = 9999;
|
||||
const int Error_depth = 0; // definitely always print errors
|
||||
const int App_depth = 2; // temporarily where all mu code will trace to
|
||||
const int App_depth = 2; // temporarily where all Mu code will trace to
|
||||
|
||||
struct trace_stream {
|
||||
vector<trace_line> past_lines;
|
||||
|
@ -393,7 +393,8 @@ using std::ofstream;
|
|||
//: Primitive statements will occupy 101-9989
|
||||
extern const int Initial_callstack_depth = 101;
|
||||
extern const int Max_callstack_depth = 9989;
|
||||
//: Finally, details of primitive mu statements will occupy depth 9990-9999 (more on that later as well)
|
||||
//: Finally, details of primitive Mu statements will occupy depth 9990-9999
|
||||
//: (more on that later as well)
|
||||
//:
|
||||
//: This framework should help us hide some details at each level, mixing
|
||||
//: static ideas like layers with the dynamic notion of call-stack depth.
|
||||
|
|
10
010vm.cc
10
010vm.cc
|
@ -118,9 +118,9 @@ Memory.clear();
|
|||
// value 97 as the letter 'a', while a different location of type 'number'
|
||||
// would not.
|
||||
//
|
||||
// Unlike most computers today, mu stores types in a single big table, shared
|
||||
// by all the mu programs on the computer. This is useful in providing a
|
||||
// seamless experience to help understand arbitrary mu programs.
|
||||
// Unlike most computers today, Mu stores types in a single big table, shared
|
||||
// by all the Mu programs on the computer. This is useful in providing a
|
||||
// seamless experience to help understand arbitrary Mu programs.
|
||||
typedef int type_ordinal;
|
||||
:(before "End Globals")
|
||||
map<string, type_ordinal> Type_ordinal;
|
||||
|
@ -194,7 +194,7 @@ enum primitive_recipes {
|
|||
:(code)
|
||||
//: It's all very well to construct recipes out of other recipes, but we need
|
||||
//: to know how to do *something* out of the box. For the following
|
||||
//: recipes there are only codes, no entries in the book, because mu just knows
|
||||
//: recipes there are only codes, no entries in the book, because Mu just knows
|
||||
//: what to do for them.
|
||||
void setup_recipes() {
|
||||
Recipe.clear(); Recipe_ordinal.clear();
|
||||
|
@ -532,7 +532,7 @@ string_tree* property(const reagent& r, const string& name) {
|
|||
}
|
||||
|
||||
:(before "End Globals")
|
||||
extern const string Ignore(","); // commas are ignored in mu except within [] strings
|
||||
extern const string Ignore(","); // commas are ignored in Mu except within [] strings
|
||||
:(code)
|
||||
void skip_whitespace_but_not_newline(istream& in) {
|
||||
while (true) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//: Phase 1 of running mu code: load it from a textual representation.
|
||||
//: Phase 1 of running Mu code: load it from a textual representation.
|
||||
//:
|
||||
//: The process of running mu code:
|
||||
//: The process of running Mu code:
|
||||
//: load -> transform -> run
|
||||
|
||||
:(scenarios load) // use 'load' instead of 'run' in all scenarios in this layer
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
//: Phase 2: Filter loaded recipes through an extensible list of 'transforms'.
|
||||
//:
|
||||
//: The process of running mu code:
|
||||
//: The process of running Mu code:
|
||||
//: load -> transform -> run
|
||||
//:
|
||||
//: The hope is that this framework of transform tools will provide a
|
||||
//: deconstructed alternative to conventional compilers.
|
||||
//:
|
||||
//: We're going to have many transforms in mu, and getting their order right
|
||||
//: We're going to have many transforms in Mu, and getting their order right
|
||||
//: (not the same as ordering of layers) is a well-known problem. Some tips:
|
||||
//: a) Design each layer to rely on as few previous layers as possible.
|
||||
//:
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//:
|
||||
//: Instead of quotes, we'll use [] to delimit strings. That'll reduce the
|
||||
//: need for escaping since we can support nested brackets. And we can also
|
||||
//: imagine that 'recipe' might one day itself be defined in mu, doing its own
|
||||
//: imagine that 'recipe' might one day itself be defined in Mu, doing its own
|
||||
//: parsing.
|
||||
|
||||
:(scenarios load)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
//: Phase 3: Start running a loaded and transformed recipe.
|
||||
//:
|
||||
//: The process of running mu code:
|
||||
//: The process of running Mu code:
|
||||
//: load -> transform -> run
|
||||
//:
|
||||
//: So far we've seen recipes as lists of instructions, and instructions point
|
||||
//: at other recipes. To kick things off mu needs to know how to run certain
|
||||
//: at other recipes. To kick things off Mu needs to know how to run certain
|
||||
//: 'primitive' recipes. That will then give the ability to run recipes
|
||||
//: containing these primitives.
|
||||
//:
|
||||
|
@ -145,7 +145,7 @@ const vector<instruction>& routine::steps() const {
|
|||
|
||||
//: Step 1: load all .mu files with numeric prefixes (in order)
|
||||
:(before "End Load Recipes")
|
||||
// Load .mu Core
|
||||
// Load Mu Prelude
|
||||
//? Save_trace = true;
|
||||
//? START_TRACING_UNTIL_END_OF_SCOPE;
|
||||
load_file_or_directory("core.mu");
|
||||
|
@ -390,7 +390,7 @@ def main [
|
|||
]
|
||||
-mem: storing 34 in location 0
|
||||
|
||||
//: mu is robust to various combinations of commas and spaces. You just have
|
||||
//: Mu is robust to various combinations of commas and spaces. You just have
|
||||
//: to put spaces around the '<-'.
|
||||
|
||||
:(scenario comma_without_space)
|
||||
|
|
|
@ -112,10 +112,12 @@ def f [
|
|||
]
|
||||
+error: f: return ingredient '14:point' can't be saved in '3:num'
|
||||
|
||||
//: In mu we'd like to assume that any instruction doesn't modify its
|
||||
//: In Mu we'd like to assume that any instruction doesn't modify its
|
||||
//: ingredients unless they're also products. The /same-as-ingredient inside
|
||||
//: the recipe's 'reply' will help catch accidental misuse of such
|
||||
//: 'ingredient-products' (sometimes called in-out parameters in other languages).
|
||||
//: the recipe's 'reply' indicates that an ingredient is intended to be
|
||||
//: modified in place, and will help catch accidental misuse of such
|
||||
//: 'ingredient-products' (sometimes called in-out parameters in other
|
||||
//: languages).
|
||||
|
||||
:(scenario return_same_as_ingredient)
|
||||
% Hide_errors = true;
|
||||
|
|
12
029tools.cc
12
029tools.cc
|
@ -1,10 +1,10 @@
|
|||
//: Allow mu programs to log facts just like we've been doing in C++ so far.
|
||||
//: Allow Mu programs to log facts just like we've been doing in C++ so far.
|
||||
|
||||
:(scenario trace)
|
||||
def main [
|
||||
trace 1, [foo], [this is a trace in mu]
|
||||
trace 1, [foo], [this is a trace in Mu]
|
||||
]
|
||||
+foo: this is a trace in mu
|
||||
+foo: this is a trace in Mu
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
TRACE,
|
||||
|
@ -192,9 +192,9 @@ case _SAVE_TRACE: {
|
|||
:(scenario assert)
|
||||
% Hide_errors = true; // '%' lines insert arbitrary C code into tests before calling 'run' with the lines below. Must be immediately after :(scenario) line.
|
||||
def main [
|
||||
assert 0, [this is an assert in mu]
|
||||
assert 0, [this is an assert in Mu]
|
||||
]
|
||||
+error: this is an assert in mu
|
||||
+error: this is an assert in Mu
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
ASSERT,
|
||||
|
@ -337,7 +337,7 @@ case _LOG: {
|
|||
break;
|
||||
}
|
||||
|
||||
//: set a variable from within mu code
|
||||
//: set a variable from within Mu code
|
||||
//: useful for selectively tracing or printing after some point
|
||||
:(before "End Globals")
|
||||
bool Foo = false;
|
||||
|
|
|
@ -652,7 +652,7 @@ def main [
|
|||
]
|
||||
+error: main: product of 'put' must be first ingredient '1:point', but got '3:point'
|
||||
|
||||
//:: Allow containers to be defined in mu code.
|
||||
//:: Allow containers to be defined in Mu code.
|
||||
|
||||
:(scenarios load)
|
||||
:(scenario container)
|
||||
|
|
|
@ -216,7 +216,7 @@ def main [
|
|||
]
|
||||
$error: 0
|
||||
|
||||
//:: Allow exclusive containers to be defined in mu code.
|
||||
//:: Allow exclusive containers to be defined in Mu code.
|
||||
|
||||
:(scenario exclusive_container)
|
||||
exclusive-container foo [
|
||||
|
|
|
@ -58,7 +58,7 @@ int new_mu_text(const string& contents) {
|
|||
curr += tb_utf8_char_length(raw_contents[curr]);
|
||||
++curr_address;
|
||||
}
|
||||
// mu strings are not null-terminated in memory
|
||||
// Mu strings are not null-terminated in memory.
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//: Structured programming
|
||||
//:
|
||||
//: Our jump recipes are quite inconvenient to use, so mu provides a
|
||||
//: 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:
|
||||
//:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//: A big convenience high-level languages provide is the ability to name memory
|
||||
//: locations. In mu, a transform called 'transform_names' provides this
|
||||
//: locations. In Mu, a transform called 'transform_names' provides this
|
||||
//: convenience.
|
||||
|
||||
:(scenario transform_names)
|
||||
|
|
|
@ -388,11 +388,11 @@ bool Hide_missing_default_space_errors = true;
|
|||
Transform.push_back(check_default_space); // idempotent
|
||||
:(code)
|
||||
void check_default_space(const recipe_ordinal r) {
|
||||
if (Hide_missing_default_space_errors) return; // skip previous core tests; this is only for mu code
|
||||
if (Hide_missing_default_space_errors) return; // skip previous core tests; this is only for Mu code
|
||||
const recipe& caller = get(Recipe, r);
|
||||
// skip scenarios (later layer)
|
||||
// user code should never create recipes with underscores in their names
|
||||
if (caller.name.find("scenario_") == 0) return; // skip mu scenarios which will use raw memory locations
|
||||
if (caller.name.find("scenario_") == 0) return; // skip Mu scenarios which will use raw memory locations
|
||||
if (caller.name.find("run_") == 0) return; // skip calls to 'run', which should be in scenarios and will also use raw memory locations
|
||||
// assume recipes with only numeric addresses know what they're doing (usually tests)
|
||||
if (!contains_non_special_name(r)) return;
|
||||
|
@ -403,7 +403,7 @@ void check_default_space(const recipe_ordinal r) {
|
|||
raise << caller.name << " does not seem to start with default-space or local-scope\n" << end();
|
||||
}
|
||||
}
|
||||
:(after "Load .mu Core")
|
||||
:(after "Load Mu Prelude")
|
||||
Hide_missing_default_space_errors = false;
|
||||
:(after "Test Runs")
|
||||
Hide_missing_default_space_errors = true;
|
||||
|
|
|
@ -112,7 +112,7 @@ scenario foo [
|
|||
]
|
||||
+run: {1: ("address" "array" "character")} <- new {"# not a comment": "literal-string"}
|
||||
|
||||
//:: Run scenarios when we run 'mu test'.
|
||||
//:: Run scenarios when we run './mu test'.
|
||||
//: Treat the text of the scenario as a regular series of instructions.
|
||||
|
||||
:(before "End Globals")
|
||||
|
@ -257,7 +257,8 @@ def scenario-foo [
|
|||
if (recipe_name.find("scenario-") == 0) return true;
|
||||
|
||||
//:: The special instructions we want to support inside scenarios.
|
||||
//: In a compiler for the mu VM these will require more work.
|
||||
//: These are easy to support in an interpreter, but will require more work
|
||||
//: when we eventually build a compiler.
|
||||
|
||||
//: 'run' is a purely lexical convenience to separate the code actually being
|
||||
//: tested from any setup or teardown
|
||||
|
@ -355,7 +356,7 @@ void check_memory(const string& s) {
|
|||
}
|
||||
if (!is_integer(rhs) && !is_noninteger(rhs)) {
|
||||
if (Current_scenario && !Scenario_testing_scenario)
|
||||
// genuine test in a mu file
|
||||
// genuine test in a .mu file
|
||||
raise << "\nF - " << Current_scenario->name << ": location '" << address << "' can't contain non-number " << rhs << "\n" << end();
|
||||
else
|
||||
// just testing scenario support
|
||||
|
@ -369,7 +370,7 @@ void check_memory(const string& s) {
|
|||
trace(9999, "run") << "checking location " << address << end();
|
||||
if (get_or_insert(Memory, address) != value) {
|
||||
if (Current_scenario && !Scenario_testing_scenario) {
|
||||
// genuine test in a mu file
|
||||
// genuine test in a .mu file
|
||||
raise << "\nF - " << Current_scenario->name << ": expected location '" << address << "' to contain " << no_scientific(value) << " but saw " << no_scientific(get_or_insert(Memory, address)) << '\n' << end();
|
||||
}
|
||||
else {
|
||||
|
@ -428,7 +429,7 @@ void check_string(int address, const string& literal) {
|
|||
trace(9999, "run") << "checking location " << address+i << end();
|
||||
if (get_or_insert(Memory, address+i) != literal.at(i)) {
|
||||
if (Current_scenario && !Scenario_testing_scenario) {
|
||||
// genuine test in a mu file
|
||||
// genuine test in a .mu file
|
||||
raise << "\nF - " << Current_scenario->name << ": expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << no_scientific(get_or_insert(Memory, address+i)) << " ('" << static_cast<char>(get_or_insert(Memory, address+i)) << "')\n" << end();
|
||||
}
|
||||
else {
|
||||
|
@ -712,7 +713,7 @@ case CHECK_TRACE_COUNT_FOR_LABEL: {
|
|||
int count = trace_count(label);
|
||||
if (count != expected_count) {
|
||||
if (Current_scenario && !Scenario_testing_scenario) {
|
||||
// genuine test in a mu file
|
||||
// genuine test in a .mu file
|
||||
raise << "\nF - " << Current_scenario->name << ": " << maybe(current_recipe_name()) << "expected " << expected_count << " lines in trace with label '" << label << "' in trace: " << end();
|
||||
DUMP(label);
|
||||
}
|
||||
|
@ -737,7 +738,7 @@ def main [
|
|||
+error: main: expected 2 lines in trace with label 'a' in trace
|
||||
|
||||
//: Minor detail: ignore 'system' calls in scenarios, since anything we do
|
||||
//: with them is by definition impossible to test through mu.
|
||||
//: with them is by definition impossible to test through Mu.
|
||||
:(after "case _SYSTEM:")
|
||||
if (Current_scenario) break;
|
||||
|
||||
|
|
|
@ -78,8 +78,8 @@ if (!candidates.empty()) {
|
|||
}
|
||||
skip_shape_shifting_variants:;
|
||||
|
||||
//: make sure we have no unspecialized shape-shifting recipes being called
|
||||
//: before running mu programs
|
||||
//: before running Mu programs, make sure no unspecialized shape-shifting
|
||||
//: recipes can be called
|
||||
|
||||
:(before "End Instruction Operation Checks")
|
||||
if (contains_key(Recipe, inst.operation) && inst.operation >= MAX_PRIMITIVE_RECIPES
|
||||
|
|
|
@ -524,7 +524,7 @@ set<int> ingredient_indices(const instruction& inst, const set<reagent>& ingredi
|
|||
//: can't pass both pointers back out, because if a caller tries to make both
|
||||
//: identical then you can't tell which value will be written on the way out.
|
||||
//:
|
||||
//: Experimental solution: just tell mu that one points inside the other.
|
||||
//: Experimental solution: just tell Mu that one points inside the other.
|
||||
//: This way we can return just one pointer as high up as necessary to capture
|
||||
//: all modifications performed by a recipe.
|
||||
//:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//: Routines can be put in a 'waiting' state, from which it will be ready to
|
||||
//: run again when a specific memory location changes its value. This is mu's
|
||||
//: run again when a specific memory location changes its value. This is Mu's
|
||||
//: basic technique for orchestrating the order in which different routines
|
||||
//: operate.
|
||||
|
||||
|
|
|
@ -43,9 +43,9 @@ case OPEN_CONSOLE: {
|
|||
int height = tb_height();
|
||||
if (width > 222 || height > 222) tb_shutdown();
|
||||
if (width > 222)
|
||||
raise << "sorry, mu doesn't support windows wider than 222 characters in console mode. Please resize your window.\n" << end();
|
||||
raise << "sorry, Mu doesn't support windows wider than 222 characters in console mode. Please resize your window.\n" << end();
|
||||
if (height > 222)
|
||||
raise << "sorry, mu doesn't support windows taller than 222 characters in console mode. Please resize your window.\n" << end();
|
||||
raise << "sorry, Mu doesn't support windows taller than 222 characters in console mode. Please resize your window.\n" << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -527,7 +527,7 @@ case INTERACTIONS_LEFT: {
|
|||
break;
|
||||
}
|
||||
|
||||
//: a hack to make edit.mu more responsive
|
||||
//: hack to make text-mode apps more responsive under Unix
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
CLEAR_DISPLAY_FROM,
|
||||
|
|
|
@ -282,7 +282,7 @@ void check_screen(const string& expected_contents, const int color) {
|
|||
if (color == -1 || color == get_or_insert(Memory, addr+cell_color_offset)) continue;
|
||||
// contents match but color is off
|
||||
if (Current_scenario && !Scenario_testing_scenario) {
|
||||
// genuine test in a mu file
|
||||
// genuine test in a .mu file
|
||||
raise << "\nF - " << Current_scenario->name << ": expected screen location (" << row << ", " << column << ", address " << addr << ", value " << no_scientific(get_or_insert(Memory, addr)) << ") to be in color " << color << " instead of " << no_scientific(get_or_insert(Memory, addr+cell_color_offset)) << "\n" << end();
|
||||
dump_screen();
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ void check_screen(const string& expected_contents, const int color) {
|
|||
ostringstream color_phrase;
|
||||
if (color != -1) color_phrase << " in color " << color;
|
||||
if (Current_scenario && !Scenario_testing_scenario) {
|
||||
// genuine test in a mu file
|
||||
// genuine test in a .mu file
|
||||
raise << "\nF - " << Current_scenario->name << ": expected screen location (" << row << ", " << column << ") to contain " << curr << expected_pretty << color_phrase.str() << " instead of " << no_scientific(get_or_insert(Memory, addr)) << actual_pretty << '\n' << end();
|
||||
dump_screen();
|
||||
}
|
||||
|
|
|
@ -218,6 +218,7 @@ case _READ_FROM_SOCKET: {
|
|||
}
|
||||
:(before "End Primitive Recipe Implementations")
|
||||
case _READ_FROM_SOCKET: {
|
||||
cerr << "$read-from-socket\n";
|
||||
long long int x = static_cast<long long int>(ingredients.at(0).at(0));
|
||||
socket_t* socket = reinterpret_cast<socket_t*>(x);
|
||||
// 1. we'd like to simply read() from the socket
|
||||
|
@ -227,19 +228,22 @@ case _READ_FROM_SOCKET: {
|
|||
// 3. but poll() will block on EOF, so only use poll() on the very first
|
||||
// $read-from-socket on a socket
|
||||
if (!socket->polled) {
|
||||
socket->polled = true;
|
||||
pollfd p;
|
||||
bzero(&p, sizeof(p));
|
||||
p.fd = socket->fd;
|
||||
p.events = POLLIN | POLLHUP;
|
||||
if (poll(&p, /*num pollfds*/1, /*no timeout*/-1) <= 0) {
|
||||
if (poll(&p, /*num pollfds*/1, /*timeout*/100/*ms*/) <= 0) {
|
||||
raise << maybe(current_recipe_name()) << "error in $read-from-socket\n" << end();
|
||||
products.resize(2);
|
||||
products.at(0).push_back(0);
|
||||
products.at(1).push_back(false);
|
||||
break;
|
||||
}
|
||||
cerr << "poll output: " << p.revents << '\n';
|
||||
cerr << "setting socket->polled\n";
|
||||
socket->polled = true;
|
||||
}
|
||||
cerr << "$read-from-socket " << x << " continuing\n";
|
||||
int bytes = static_cast<int>(ingredients.at(1).at(0));
|
||||
char* contents = new char[bytes];
|
||||
bzero(contents, bytes);
|
||||
|
|
240
092socket.mu
240
092socket.mu
|
@ -1,69 +1,87 @@
|
|||
# Wrappers around socket primitives that take a 'local-network' object and are
|
||||
# thus easier to test.
|
||||
# Wrappers around socket primitives that are easier to test.
|
||||
#
|
||||
# The current semantics of fake port-connections don't match UNIX socket ones,
|
||||
# but we'll improve them as we learn more.
|
||||
|
||||
container local-network [
|
||||
data:&:@:port-connection
|
||||
]
|
||||
|
||||
# Port connections represent connections to ports on localhost.
|
||||
# Before passing a local-network object to network functions
|
||||
# `start-reading-socket` and `start-writing-socket`, add port-connections to
|
||||
# the local-network.
|
||||
# To test client operations, use `assume-resources` with a filename that
|
||||
# begins with a hostname. (Filenames starting with '/' are assumed to be
|
||||
# local.)
|
||||
#
|
||||
# For reading, `receive-from-socket` will check for a
|
||||
# port-connection on the port parameter that's been passed in. If there's
|
||||
# no port-connection for that port, it will return nothing and log an error.
|
||||
# If there is a port-connection for that port, it will transmit the contents
|
||||
# to the provided sink.
|
||||
#
|
||||
# For writing, `start-writing-socket` returns a sink connecting the
|
||||
# caller to the socket on the passed-in port.
|
||||
container port-connection [
|
||||
port:num
|
||||
contents:text
|
||||
]
|
||||
# To test server operations, just run a real client against localhost.
|
||||
|
||||
def new-port-connection port:num, contents:text -> p:&:port-connection [
|
||||
scenario example-server-test [
|
||||
local-scope
|
||||
load-ingredients
|
||||
p:&:port-connection <- new port-connection:type
|
||||
*p <- merge port, contents
|
||||
]
|
||||
|
||||
def new-fake-network -> n:&:local-network [
|
||||
local-scope
|
||||
load-ingredients
|
||||
n:&:local-network <- new local-network:type
|
||||
local-network-ports:&:@:port-connection <- new port-connection:type, 0
|
||||
*n <- put *n, data:offset, local-network-ports
|
||||
]
|
||||
|
||||
scenario write-to-fake-socket [
|
||||
local-scope
|
||||
single-port-network:&:local-network <- new-fake-network
|
||||
sink:&:sink:char, writer:num/routine <- start-writing-socket single-port-network, 8080
|
||||
sink <- write sink, 120/x
|
||||
close sink
|
||||
wait-for-routine writer
|
||||
tested-port-connections:&:@:port-connection <- get *single-port-network, data:offset
|
||||
tested-port-connection:port-connection <- index *tested-port-connections, 0
|
||||
contents:text <- get tested-port-connection, contents:offset
|
||||
10:@:char/raw <- copy *contents
|
||||
memory-should-contain [
|
||||
10:array:character <- [x]
|
||||
# test server without a fake on a random (real) port
|
||||
# that way repeatedly running the test will give ports time to timeout and
|
||||
# close before reusing them
|
||||
make-random-nondeterministic
|
||||
port:num <- random-in-range 0/real-random-numbers, 8000, 8100
|
||||
run [
|
||||
socket:num <- $open-server-socket port
|
||||
$print [server socket: ], socket, 10/newline
|
||||
assert socket, [
|
||||
F - example-server-test: $open-server-socket failed]
|
||||
$print [starting up server routine], 10/newline
|
||||
handler-routine:number <- start-running serve-one-request socket, example-handler
|
||||
]
|
||||
$print [starting to read from port ], port, 10/newline
|
||||
source:&:source:char <- start-reading-from-network 0/real-resources, [localhost], [/], port
|
||||
response:text <- drain source
|
||||
10:@:char/raw <- copy *response
|
||||
memory-should-contain [
|
||||
10:array:character <- [abc]
|
||||
]
|
||||
]
|
||||
# helper just for this scenario
|
||||
def example-handler query:text -> response:text [
|
||||
local-scope
|
||||
load-ingredients
|
||||
reply [abc]
|
||||
]
|
||||
|
||||
#? scenario example-client-test [
|
||||
#? local-scope
|
||||
#? assume-resources [
|
||||
#? [example.com/] -> [abc]
|
||||
#? ]
|
||||
#? run [
|
||||
#? source:&:source:char <- start-reading-from-network resources, [example.com], [/]
|
||||
#? ]
|
||||
#? contents:text <- drain source
|
||||
#? 10:@:char/raw <- copy *contents
|
||||
#? memory-should-contain [
|
||||
#? 10:address:character <- [abc]
|
||||
#? ]
|
||||
#? ]
|
||||
|
||||
type request-handler = (recipe text -> text)
|
||||
|
||||
def serve-one-request socket:num, request-handler:request-handler [
|
||||
local-scope
|
||||
load-ingredients
|
||||
session:num <- $accept socket
|
||||
$print [server session socket: ], session, 10/newline
|
||||
assert session, [
|
||||
F - example-server-test: $accept failed]
|
||||
contents:&:source:char, sink:&:sink:char <- new-channel 30
|
||||
sink <- start-running receive-from-socket session, sink
|
||||
query:text <- drain contents
|
||||
response:text <- call request-handler, query
|
||||
write-to-socket session, response
|
||||
$close-socket session
|
||||
]
|
||||
|
||||
def start-reading-from-network resources:&:resources, host:text, path:text -> contents:&:source:char [
|
||||
local-scope
|
||||
load-ingredients
|
||||
$print [running start-reading-from-network], 10/newline
|
||||
{
|
||||
port:num, port-found?:boolean <- next-ingredient
|
||||
break-if port-found?
|
||||
port <- copy 80/http-port
|
||||
}
|
||||
{
|
||||
break-if resources
|
||||
# real network
|
||||
socket:num <- $open-client-socket host, 80/http-port
|
||||
socket:num <- $open-client-socket host, port
|
||||
$print [client socket: ], socket, 10/newline
|
||||
assert socket, [contents]
|
||||
req:text <- interpolate [GET _ HTTP/1.1], path
|
||||
request-socket socket, req
|
||||
|
@ -102,70 +120,72 @@ def request-socket socket:num, s:text -> socket:num [
|
|||
$write-to-socket socket, 10/lf
|
||||
]
|
||||
|
||||
def start-writing-socket network:&:local-network, port:num -> sink:&:sink:char, routine-id:num [
|
||||
local-scope
|
||||
load-ingredients
|
||||
source:&:source:char, sink:&:sink:char <- new-channel 30
|
||||
{
|
||||
break-if network
|
||||
socket:num <- $open-server-socket port
|
||||
session:num <- $accept socket
|
||||
# TODO Create channel implementation of write-to-socket.
|
||||
return sink, 0/routine-id
|
||||
}
|
||||
# fake network
|
||||
routine-id <- start-running transmit-to-fake-socket network, port, source
|
||||
]
|
||||
#? def start-writing-socket network:&:local-network, port:num -> sink:&:sink:char, routine-id:num [
|
||||
#? local-scope
|
||||
#? load-ingredients
|
||||
#? source:&:source:char, sink:&:sink:char <- new-channel 30
|
||||
#? {
|
||||
#? break-if network
|
||||
#? socket:num <- $open-server-socket port
|
||||
#? session:num <- $accept socket
|
||||
#? # TODO Create channel implementation of write-to-socket.
|
||||
#? return sink, 0/routine-id
|
||||
#? }
|
||||
#? # fake network
|
||||
#? routine-id <- start-running transmit-to-fake-socket network, port, source
|
||||
#? ]
|
||||
|
||||
def transmit-to-fake-socket network:&:local-network, port:num, source:&:source:char -> network:&:local-network, source:&:source:char [
|
||||
local-scope
|
||||
load-ingredients
|
||||
# compute new port connection contents
|
||||
buf:&:buffer <- new-buffer 30
|
||||
{
|
||||
c:char, done?:bool, source <- read source
|
||||
break-unless c
|
||||
buf <- append buf, c
|
||||
break-if done?
|
||||
loop
|
||||
}
|
||||
contents:text <- buffer-to-array buf
|
||||
new-port-connection:&:port-connection <- new-port-connection port, contents
|
||||
# Got the contents of the channel, time to write to fake port.
|
||||
i:num <- copy 0
|
||||
port-connections:&:@:port-connection <- get *network, data:offset
|
||||
len:num <- length *port-connections
|
||||
{
|
||||
done?:bool <- greater-or-equal i, len
|
||||
break-if done?
|
||||
current:port-connection <- index *port-connections, i
|
||||
current-port:num <- get current, port:offset
|
||||
ports-match?:bool <- equal current-port, port
|
||||
i <- add i, 1
|
||||
loop-unless ports-match?
|
||||
# Found an existing connection on this port, overwrite.
|
||||
put-index *port-connections, i, *new-port-connection
|
||||
reply
|
||||
}
|
||||
# Couldn't find an existing connection on this port, initialize a new one.
|
||||
new-len:num <- add len, 1
|
||||
new-port-connections:&:@:port-connection <- new port-connection:type, new-len
|
||||
put *network, data:offset, new-port-connections
|
||||
i:num <- copy 0
|
||||
{
|
||||
done?:bool <- greater-or-equal i, len
|
||||
break-if done?
|
||||
tmp:port-connection <- index *port-connections, i
|
||||
put-index *new-port-connections, i, tmp
|
||||
}
|
||||
put-index *new-port-connections, len, *new-port-connection
|
||||
]
|
||||
#? def transmit-to-fake-socket network:&:local-network, port:num, source:&:source:char -> network:&:local-network, source:&:source:char [
|
||||
#? local-scope
|
||||
#? load-ingredients
|
||||
#? # compute new port connection contents
|
||||
#? buf:&:buffer <- new-buffer 30
|
||||
#? {
|
||||
#? c:char, done?:bool, source <- read source
|
||||
#? break-unless c
|
||||
#? buf <- append buf, c
|
||||
#? break-if done?
|
||||
#? loop
|
||||
#? }
|
||||
#? contents:text <- buffer-to-array buf
|
||||
#? new-port-connection:&:port-connection <- new-port-connection port, contents
|
||||
#? # Got the contents of the channel, time to write to fake port.
|
||||
#? i:num <- copy 0
|
||||
#? port-connections:&:@:port-connection <- get *network, data:offset
|
||||
#? len:num <- length *port-connections
|
||||
#? {
|
||||
#? done?:bool <- greater-or-equal i, len
|
||||
#? break-if done?
|
||||
#? current:port-connection <- index *port-connections, i
|
||||
#? current-port:num <- get current, port:offset
|
||||
#? ports-match?:bool <- equal current-port, port
|
||||
#? i <- add i, 1
|
||||
#? loop-unless ports-match?
|
||||
#? # Found an existing connection on this port, overwrite.
|
||||
#? put-index *port-connections, i, *new-port-connection
|
||||
#? reply
|
||||
#? }
|
||||
#? # Couldn't find an existing connection on this port, initialize a new one.
|
||||
#? new-len:num <- add len, 1
|
||||
#? new-port-connections:&:@:port-connection <- new port-connection:type, new-len
|
||||
#? put *network, data:offset, new-port-connections
|
||||
#? i:num <- copy 0
|
||||
#? {
|
||||
#? done?:bool <- greater-or-equal i, len
|
||||
#? break-if done?
|
||||
#? tmp:port-connection <- index *port-connections, i
|
||||
#? put-index *new-port-connections, i, tmp
|
||||
#? }
|
||||
#? put-index *new-port-connections, len, *new-port-connection
|
||||
#? ]
|
||||
|
||||
def receive-from-socket socket:num, sink:&:sink:char -> sink:&:sink:char [
|
||||
local-scope
|
||||
load-ingredients
|
||||
{
|
||||
$print [read-from-socket ], socket, 10/newline
|
||||
req:text, eof?:bool <- $read-from-socket socket, 4096/bytes
|
||||
loop-unless req
|
||||
bytes-read:num <- length *req
|
||||
i:num <- copy 0
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//: Helper for various programming environments: run arbitrary mu code and
|
||||
//: return some result in string form.
|
||||
//: Helper for various programming environments: run arbitrary Mu code and
|
||||
//: return some result in text form.
|
||||
|
||||
:(scenario run_interactive_code)
|
||||
def main [
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//:
|
||||
//: Location 0 - unused (since it can help uncover bugs)
|
||||
//: Locations 1-899 - reserved for tests
|
||||
//: Locations 900-999 - reserved for predefined globals in mu scenarios, like keyboard, screen, etc.
|
||||
//: Locations 900-999 - reserved for predefined globals in Mu scenarios, like keyboard, screen, etc.
|
||||
:(before "End Setup")
|
||||
assert(Max_variables_in_scenarios == 900);
|
||||
//: Locations 1000 ('Reserved_for_tests') onward - available to the allocator in chunks of size Initial_memory_per_routine.
|
||||
|
@ -26,7 +26,7 @@ assert(Next_recipe_ordinal == 1000);
|
|||
//:: Depths for tracing
|
||||
//:
|
||||
//: 0 - unused
|
||||
//: 1-100 - app-level trace statements in mu
|
||||
//: 1-100 - app-level trace statements in Mu
|
||||
//: 101-9989 - call-stack statements (mostly label run)
|
||||
assert(Initial_callstack_depth == 101);
|
||||
assert(Max_callstack_depth == 9989);
|
||||
|
|
|
@ -7,8 +7,8 @@ def main [
|
|||
|
||||
# The chessboard function takes keyboard and screen objects as 'ingredients'.
|
||||
#
|
||||
# In mu it is good form (though not required) to explicitly show the
|
||||
# hardware you rely on.
|
||||
# In Mu it is good form (though not required) to explicitly state what
|
||||
# hardware a function needs.
|
||||
#
|
||||
# Here the console and screen are both 0, which usually indicates real
|
||||
# hardware rather than a fake for testing as you'll see below.
|
||||
|
@ -17,7 +17,7 @@ def main [
|
|||
close-console # clean up screen, keyboard and mouse
|
||||
]
|
||||
|
||||
## But enough about mu. Here's what it looks like to run the chessboard program.
|
||||
## But enough about Mu. Here's what it looks like to run the chessboard program.
|
||||
|
||||
scenario print-board-and-read-move [
|
||||
local-scope
|
||||
|
|
|
@ -364,7 +364,7 @@ scenario editor-initializes-empty-text [
|
|||
]
|
||||
]
|
||||
|
||||
# just a little color for mu code
|
||||
# just a little color for Mu code
|
||||
|
||||
scenario render-colors-comments [
|
||||
local-scope
|
||||
|
|
|
@ -184,7 +184,7 @@ def run-sandboxes env:&:environment, screen:&:screen -> errors-found?:bool, env:
|
|||
<run-sandboxes-end>
|
||||
]
|
||||
|
||||
# copy code from recipe editor, persist, load into mu
|
||||
# copy code from recipe editor, persist to disk, load
|
||||
# replaced in a later layer (whereupon errors-found? will actually be set)
|
||||
def update-recipes env:&:environment, screen:&:screen -> errors-found?:bool, env:&:environment, screen:&:screen [
|
||||
local-scope
|
||||
|
|
|
@ -4,7 +4,7 @@ container environment [
|
|||
recipe-errors:text
|
||||
]
|
||||
|
||||
# copy code from recipe editor, persist, load into mu, save any errors
|
||||
# copy code from recipe editor, persist to disk, load, save any errors
|
||||
def! update-recipes env:&:environment, screen:&:screen -> errors-found?:bool, env:&:environment, screen:&:screen [
|
||||
local-scope
|
||||
load-ingredients
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Environment for learning programming using mu: http://akkartik.name/post/mu
|
||||
Environment for learning programming using Mu: http://akkartik.name/post/mu
|
||||
|
||||
Run it from the mu directory:
|
||||
Run it from the `mu` directory:
|
||||
|
||||
```shell
|
||||
$ ./mu edit
|
||||
|
|
|
@ -34,7 +34,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
<pre id='vimCodeElement'>
|
||||
<span class="Comment">//: A simple test harness. To create new tests define functions starting with</span>
|
||||
<span class="Comment">//: 'test_'. To run all tests so defined, run:</span>
|
||||
<span class="Comment">//: $ mu test</span>
|
||||
<span class="Comment">//: $ ./mu test</span>
|
||||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: Every layer should include tests, and can reach into previous layers.</span>
|
||||
<span class="Comment">//: However, it seems like a good idea never to reach into tests from previous</span>
|
||||
|
|
|
@ -132,7 +132,7 @@ Dump_trace = <span class="Constant">false</span><span class="Delimiter">;</span>
|
|||
<span class="Comment">// compilation units. So no extern linkage.</span>
|
||||
<span class="Normal">const</span> <span class="Normal">int</span> Max_depth = <span class="Constant">9999</span><span class="Delimiter">;</span>
|
||||
<span class="Normal">const</span> <span class="Normal">int</span> Error_depth = <span class="Constant">0</span><span class="Delimiter">;</span> <span class="Comment">// definitely always print errors</span>
|
||||
<span class="Normal">const</span> <span class="Normal">int</span> App_depth = <span class="Constant">2</span><span class="Delimiter">;</span> <span class="Comment">// temporarily where all mu code will trace to</span>
|
||||
<span class="Normal">const</span> <span class="Normal">int</span> App_depth = <span class="Constant">2</span><span class="Delimiter">;</span> <span class="Comment">// temporarily where all Mu code will trace to</span>
|
||||
|
||||
<span class="Normal">struct</span> trace_stream <span class="Delimiter">{</span>
|
||||
vector<trace_line> past_lines<span class="Delimiter">;</span>
|
||||
|
@ -426,7 +426,8 @@ string trim<span class="Delimiter">(</span><span class="Normal">const</span> str
|
|||
<span class="Comment">//: Primitive statements will occupy 101-9989</span>
|
||||
<span class="Normal">extern</span> <span class="Normal">const</span> <span class="Normal">int</span> Initial_callstack_depth = <span class="Constant">101</span><span class="Delimiter">;</span>
|
||||
<span class="Normal">extern</span> <span class="Normal">const</span> <span class="Normal">int</span> Max_callstack_depth = <span class="Constant">9989</span><span class="Delimiter">;</span>
|
||||
<span class="Comment">//: Finally, details of primitive mu statements will occupy depth 9990-9999 (more on that later as well)</span>
|
||||
<span class="Comment">//: Finally, details of primitive Mu statements will occupy depth 9990-9999</span>
|
||||
<span class="Comment">//: (more on that later as well)</span>
|
||||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: This framework should help us hide some details at each level, mixing</span>
|
||||
<span class="Comment">//: static ideas like layers with the dynamic notion of call-stack depth.</span>
|
||||
|
|
|
@ -154,9 +154,9 @@ Memory<span class="Delimiter">.</span>clear<span class="Delimiter">();</span>
|
|||
<span class="Comment">// value 97 as the letter 'a', while a different location of type 'number'</span>
|
||||
<span class="Comment">// would not.</span>
|
||||
<span class="Comment">//</span>
|
||||
<span class="Comment">// Unlike most computers today, mu stores types in a single big table, shared</span>
|
||||
<span class="Comment">// by all the mu programs on the computer. This is useful in providing a</span>
|
||||
<span class="Comment">// seamless experience to help understand arbitrary mu programs.</span>
|
||||
<span class="Comment">// Unlike most computers today, Mu stores types in a single big table, shared</span>
|
||||
<span class="Comment">// by all the Mu programs on the computer. This is useful in providing a</span>
|
||||
<span class="Comment">// seamless experience to help understand arbitrary Mu programs.</span>
|
||||
<span class="Normal">typedef</span> <span class="Normal">int</span> type_ordinal<span class="Delimiter">;</span>
|
||||
<span class="Delimiter">:(before "End Globals")</span>
|
||||
map<string<span class="Delimiter">,</span> type_ordinal> Type_ordinal<span class="Delimiter">;</span>
|
||||
|
@ -230,7 +230,7 @@ atexit<span class="Delimiter">(</span>teardown_types<span class="Delimiter">);</
|
|||
<span class="Delimiter">:(code)</span>
|
||||
<span class="Comment">//: It's all very well to construct recipes out of other recipes, but we need</span>
|
||||
<span class="Comment">//: to know how to do *something* out of the box. For the following</span>
|
||||
<span class="Comment">//: recipes there are only codes, no entries in the book, because mu just knows</span>
|
||||
<span class="Comment">//: recipes there are only codes, no entries in the book, because Mu just knows</span>
|
||||
<span class="Comment">//: what to do for them.</span>
|
||||
<span class="Normal">void</span> setup_recipes<span class="Delimiter">()</span> <span class="Delimiter">{</span>
|
||||
Recipe<span class="Delimiter">.</span>clear<span class="Delimiter">();</span> Recipe_ordinal<span class="Delimiter">.</span>clear<span class="Delimiter">();</span>
|
||||
|
@ -568,7 +568,7 @@ string_tree* property<span class="Delimiter">(</span><span class="Normal">const<
|
|||
<span class="Delimiter">}</span>
|
||||
|
||||
<span class="Delimiter">:(before "End Globals")</span>
|
||||
<span class="Normal">extern</span> <span class="Normal">const</span> string Ignore<span class="Delimiter">(</span><span class="Constant">","</span><span class="Delimiter">);</span> <span class="Comment">// commas are ignored in mu except within [] strings</span>
|
||||
<span class="Normal">extern</span> <span class="Normal">const</span> string Ignore<span class="Delimiter">(</span><span class="Constant">","</span><span class="Delimiter">);</span> <span class="Comment">// commas are ignored in Mu except within [] strings</span>
|
||||
<span class="Delimiter">:(code)</span>
|
||||
<span class="Normal">void</span> skip_whitespace_but_not_newline<span class="Delimiter">(</span>istream& in<span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
<span class="Normal">while</span> <span class="Delimiter">(</span><span class="Constant">true</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
|
|
|
@ -34,9 +34,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
</head>
|
||||
<body>
|
||||
<pre id='vimCodeElement'>
|
||||
<span class="Comment">//: Phase 1 of running mu code: load it from a textual representation.</span>
|
||||
<span class="Comment">//: Phase 1 of running Mu code: load it from a textual representation.</span>
|
||||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: The process of running mu code:</span>
|
||||
<span class="Comment">//: The process of running Mu code:</span>
|
||||
<span class="Comment">//: load -> transform -> run</span>
|
||||
|
||||
<span class="Delimiter">:(scenarios load)</span> <span class="Comment">// use 'load' instead of 'run' in all scenarios in this layer</span>
|
||||
|
|
|
@ -31,13 +31,13 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
<pre id='vimCodeElement'>
|
||||
<span class="Comment">//: Phase 2: Filter loaded recipes through an extensible list of 'transforms'.</span>
|
||||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: The process of running mu code:</span>
|
||||
<span class="Comment">//: The process of running Mu code:</span>
|
||||
<span class="Comment">//: load -> transform -> run</span>
|
||||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: The hope is that this framework of transform tools will provide a</span>
|
||||
<span class="Comment">//: deconstructed alternative to conventional compilers.</span>
|
||||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: We're going to have many transforms in mu, and getting their order right</span>
|
||||
<span class="Comment">//: We're going to have many transforms in Mu, and getting their order right</span>
|
||||
<span class="Comment">//: (not the same as ordering of layers) is a well-known problem. Some tips:</span>
|
||||
<span class="Comment">//: a) Design each layer to rely on as few previous layers as possible.</span>
|
||||
<span class="Comment">//:</span>
|
||||
|
|
|
@ -38,7 +38,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: Instead of quotes, we'll use [] to delimit strings. That'll reduce the</span>
|
||||
<span class="Comment">//: need for escaping since we can support nested brackets. And we can also</span>
|
||||
<span class="Comment">//: imagine that 'recipe' might one day itself be defined in mu, doing its own</span>
|
||||
<span class="Comment">//: imagine that 'recipe' might one day itself be defined in Mu, doing its own</span>
|
||||
<span class="Comment">//: parsing.</span>
|
||||
|
||||
<span class="Delimiter">:(scenarios load)</span>
|
||||
|
|
|
@ -39,11 +39,11 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
<pre id='vimCodeElement'>
|
||||
<span class="Comment">//: Phase 3: Start running a loaded and transformed recipe.</span>
|
||||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: The process of running mu code:</span>
|
||||
<span class="Comment">//: The process of running Mu code:</span>
|
||||
<span class="Comment">//: load -> transform -> run</span>
|
||||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: So far we've seen recipes as lists of instructions, and instructions point</span>
|
||||
<span class="Comment">//: at other recipes. To kick things off mu needs to know how to run certain</span>
|
||||
<span class="Comment">//: at other recipes. To kick things off Mu needs to know how to run certain</span>
|
||||
<span class="Comment">//: 'primitive' recipes. That will then give the ability to run recipes</span>
|
||||
<span class="Comment">//: containing these primitives.</span>
|
||||
<span class="Comment">//:</span>
|
||||
|
@ -184,7 +184,7 @@ map<string<span class="Delimiter">,</span> <span class="Normal">int</span>>
|
|||
|
||||
<span class="Comment">//: Step 1: load all .mu files with numeric prefixes (in order)</span>
|
||||
<span class="Delimiter">:(before "End Load Recipes")</span>
|
||||
<span class="Comment">// Load .mu Core</span>
|
||||
<span class="Comment">// Load Mu Prelude</span>
|
||||
<span class="CommentedCode">//? Save_trace = true;</span>
|
||||
<span class="CommentedCode">//? START_TRACING_UNTIL_END_OF_SCOPE;</span>
|
||||
load_file_or_directory<span class="Delimiter">(</span><span class="Constant">"core.mu"</span><span class="Delimiter">);</span>
|
||||
|
@ -429,7 +429,7 @@ vector<<span class="Normal">double</span>> read_memory<span class="Delimit
|
|||
]
|
||||
<span class="traceAbsent">-mem: storing 34 in location 0</span>
|
||||
|
||||
<span class="Comment">//: mu is robust to various combinations of commas and spaces. You just have</span>
|
||||
<span class="Comment">//: Mu is robust to various combinations of commas and spaces. You just have</span>
|
||||
<span class="Comment">//: to put spaces around the '<-'.</span>
|
||||
|
||||
<span class="Delimiter">:(scenario comma_without_space)</span>
|
||||
|
|
|
@ -147,10 +147,12 @@ Transform<span class="Delimiter">.</span>push_back<span class="Delimiter">(</spa
|
|||
]
|
||||
<span class="traceContains">+error: f: return ingredient '14:point' can't be saved in '3:num'</span>
|
||||
|
||||
<span class="Comment">//: In mu we'd like to assume that any instruction doesn't modify its</span>
|
||||
<span class="Comment">//: In Mu we'd like to assume that any instruction doesn't modify its</span>
|
||||
<span class="Comment">//: ingredients unless they're also products. The /same-as-ingredient inside</span>
|
||||
<span class="Comment">//: the recipe's 'reply' will help catch accidental misuse of such</span>
|
||||
<span class="Comment">//: 'ingredient-products' (sometimes called in-out parameters in other languages).</span>
|
||||
<span class="Comment">//: the recipe's 'reply' indicates that an ingredient is intended to be</span>
|
||||
<span class="Comment">//: modified in place, and will help catch accidental misuse of such</span>
|
||||
<span class="Comment">//: 'ingredient-products' (sometimes called in-out parameters in other</span>
|
||||
<span class="Comment">//: languages).</span>
|
||||
|
||||
<span class="Delimiter">:(scenario return_same_as_ingredient)</span>
|
||||
<span class="Special">% Hide_errors = true;</span>
|
||||
|
|
|
@ -35,13 +35,13 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
</head>
|
||||
<body>
|
||||
<pre id='vimCodeElement'>
|
||||
<span class="Comment">//: Allow mu programs to log facts just like we've been doing in C++ so far.</span>
|
||||
<span class="Comment">//: Allow Mu programs to log facts just like we've been doing in C++ so far.</span>
|
||||
|
||||
<span class="Delimiter">:(scenario trace)</span>
|
||||
<span class="muRecipe">def</span> main [
|
||||
trace <span class="Constant">1</span><span class="Delimiter">,</span> [foo]<span class="Delimiter">,</span> [<span class="Normal">this</span> is a trace in mu]
|
||||
trace <span class="Constant">1</span><span class="Delimiter">,</span> [foo]<span class="Delimiter">,</span> [<span class="Normal">this</span> is a trace in Mu]
|
||||
]
|
||||
<span class="traceContains">+foo: this is a trace in mu</span>
|
||||
<span class="traceContains">+foo: this is a trace in Mu</span>
|
||||
|
||||
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
|
||||
TRACE<span class="Delimiter">,</span>
|
||||
|
@ -229,9 +229,9 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
<span class="Delimiter">:(scenario assert)</span>
|
||||
<span class="Special">% Hide_errors = true; // '%' lines insert arbitrary C code into tests before calling 'run' with the lines below. Must be immediately after :(scenario) line.</span>
|
||||
<span class="muRecipe">def</span> main [
|
||||
assert <span class="Constant">0</span><span class="Delimiter">,</span> [<span class="Normal">this</span> is an assert in mu]
|
||||
assert <span class="Constant">0</span><span class="Delimiter">,</span> [<span class="Normal">this</span> is an assert in Mu]
|
||||
]
|
||||
<span class="traceContains">+error: this is an assert in mu</span>
|
||||
<span class="traceContains">+error: this is an assert in Mu</span>
|
||||
|
||||
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
|
||||
ASSERT<span class="Delimiter">,</span>
|
||||
|
@ -374,7 +374,7 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
<span class="Identifier">break</span><span class="Delimiter">;</span>
|
||||
<span class="Delimiter">}</span>
|
||||
|
||||
<span class="Comment">//: set a variable from within mu code</span>
|
||||
<span class="Comment">//: set a variable from within Mu code</span>
|
||||
<span class="Comment">//: useful for selectively tracing or printing after some point</span>
|
||||
<span class="Delimiter">:(before "End Globals")</span>
|
||||
<span class="Normal">bool</span> Foo = <span class="Constant">false</span><span class="Delimiter">;</span>
|
||||
|
|
|
@ -690,7 +690,7 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
]
|
||||
<span class="traceContains">+error: main: product of 'put' must be first ingredient '1:point', but got '3:point'</span>
|
||||
|
||||
<span class="SalientComment">//:: Allow containers to be defined in mu code.</span>
|
||||
<span class="SalientComment">//:: Allow containers to be defined in Mu code.</span>
|
||||
|
||||
<span class="Delimiter">:(scenarios load)</span>
|
||||
<span class="Delimiter">:(scenario container)</span>
|
||||
|
|
|
@ -253,7 +253,7 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
]
|
||||
$error: <span class="Constant">0</span>
|
||||
|
||||
<span class="SalientComment">//:: Allow exclusive containers to be defined in mu code.</span>
|
||||
<span class="SalientComment">//:: Allow exclusive containers to be defined in Mu code.</span>
|
||||
|
||||
<span class="Delimiter">:(scenario exclusive_container)</span>
|
||||
<span class="muData">exclusive-container</span> foo [
|
||||
|
|
|
@ -93,7 +93,7 @@ put<span class="Delimiter">(</span>Type_abbreviations<span class="Delimiter">,</
|
|||
curr += tb_utf8_char_length<span class="Delimiter">(</span>raw_contents[curr]<span class="Delimiter">);</span>
|
||||
++curr_address<span class="Delimiter">;</span>
|
||||
<span class="Delimiter">}</span>
|
||||
<span class="Comment">// mu strings are not null-terminated in memory</span>
|
||||
<span class="Comment">// Mu strings are not null-terminated in memory.</span>
|
||||
<span class="Identifier">return</span> result<span class="Delimiter">;</span>
|
||||
<span class="Delimiter">}</span>
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
<pre id='vimCodeElement'>
|
||||
<span class="Comment">//: Structured programming</span>
|
||||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: Our jump recipes are quite inconvenient to use, so mu provides a</span>
|
||||
<span class="Comment">//: Our jump recipes are quite inconvenient to use, so Mu provides a</span>
|
||||
<span class="Comment">//: lightweight tool called 'transform_braces' to work in a slightly more</span>
|
||||
<span class="Comment">//: convenient format with nested braces:</span>
|
||||
<span class="Comment">//:</span>
|
||||
|
|
|
@ -36,7 +36,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
<body>
|
||||
<pre id='vimCodeElement'>
|
||||
<span class="Comment">//: A big convenience high-level languages provide is the ability to name memory</span>
|
||||
<span class="Comment">//: locations. In mu, a transform called 'transform_names' provides this</span>
|
||||
<span class="Comment">//: locations. In Mu, a transform called 'transform_names' provides this</span>
|
||||
<span class="Comment">//: convenience.</span>
|
||||
|
||||
<span class="Delimiter">:(scenario transform_names)</span>
|
||||
|
|
|
@ -426,11 +426,11 @@ Update_refcounts_in_write_memory = <span class="Constant">true</span><span class
|
|||
Transform<span class="Delimiter">.</span>push_back<span class="Delimiter">(</span>check_default_space<span class="Delimiter">);</span> <span class="Comment">// idempotent</span>
|
||||
<span class="Delimiter">:(code)</span>
|
||||
<span class="Normal">void</span> check_default_space<span class="Delimiter">(</span><span class="Normal">const</span> recipe_ordinal r<span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>Hide_missing_default_space_errors<span class="Delimiter">)</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Comment">// skip previous core tests; this is only for mu code</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>Hide_missing_default_space_errors<span class="Delimiter">)</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Comment">// skip previous core tests; this is only for Mu code</span>
|
||||
<span class="Normal">const</span> recipe& caller = get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">);</span>
|
||||
<span class="Comment">// skip scenarios (later layer)</span>
|
||||
<span class="Comment">// user code should never create recipes with underscores in their names</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">.</span>find<span class="Delimiter">(</span><span class="Constant">"scenario_"</span><span class="Delimiter">)</span> == <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Comment">// skip mu scenarios which will use raw memory locations</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">.</span>find<span class="Delimiter">(</span><span class="Constant">"scenario_"</span><span class="Delimiter">)</span> == <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Comment">// skip Mu scenarios which will use raw memory locations</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">.</span>find<span class="Delimiter">(</span><span class="Constant">"run_"</span><span class="Delimiter">)</span> == <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Comment">// skip calls to 'run', which should be in scenarios and will also use raw memory locations</span>
|
||||
<span class="Comment">// assume recipes with only numeric addresses know what they're doing (usually tests)</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>!contains_non_special_name<span class="Delimiter">(</span>r<span class="Delimiter">))</span> <span class="Identifier">return</span><span class="Delimiter">;</span>
|
||||
|
@ -441,7 +441,7 @@ Transform<span class="Delimiter">.</span>push_back<span class="Delimiter">(</spa
|
|||
raise << caller<span class="Delimiter">.</span>name << <span class="Constant">" does not seem to start with default-space or local-scope</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
|
||||
<span class="Delimiter">}</span>
|
||||
<span class="Delimiter">}</span>
|
||||
<span class="Delimiter">:(after "Load .mu Core")</span>
|
||||
<span class="Delimiter">:(after "Load Mu Prelude")</span>
|
||||
Hide_missing_default_space_errors = <span class="Constant">false</span><span class="Delimiter">;</span>
|
||||
<span class="Delimiter">:(after "Test Runs")</span>
|
||||
Hide_missing_default_space_errors = <span class="Constant">true</span><span class="Delimiter">;</span>
|
||||
|
|
|
@ -151,7 +151,7 @@ vector<scenario> Scenarios<span class="Delimiter">;</span>
|
|||
]
|
||||
<span class="traceContains">+run: {1: ("address" "array" "character")} <- new {"# not a comment": "literal-string"}</span>
|
||||
|
||||
<span class="SalientComment">//:: Run scenarios when we run 'mu test'.</span>
|
||||
<span class="SalientComment">//:: Run scenarios when we run './mu test'.</span>
|
||||
<span class="Comment">//: Treat the text of the scenario as a regular series of instructions.</span>
|
||||
|
||||
<span class="Delimiter">:(before "End Globals")</span>
|
||||
|
@ -296,7 +296,8 @@ Name[r][<span class="Constant">"__maybe_make_raw_test__"</span>] = Res
|
|||
<span class="Normal">if</span> <span class="Delimiter">(</span>recipe_name<span class="Delimiter">.</span>find<span class="Delimiter">(</span><span class="Constant">"scenario-"</span><span class="Delimiter">)</span> == <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">true</span><span class="Delimiter">;</span>
|
||||
|
||||
<span class="SalientComment">//:: The special instructions we want to support inside scenarios.</span>
|
||||
<span class="Comment">//: In a compiler for the mu VM these will require more work.</span>
|
||||
<span class="Comment">//: These are easy to support in an interpreter, but will require more work</span>
|
||||
<span class="Comment">//: when we eventually build a compiler.</span>
|
||||
|
||||
<span class="Comment">//: 'run' is a purely lexical convenience to separate the code actually being</span>
|
||||
<span class="Comment">//: tested from any setup or teardown</span>
|
||||
|
@ -394,7 +395,7 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
<span class="Delimiter">}</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_integer<span class="Delimiter">(</span>rhs<span class="Delimiter">)</span> && !is_noninteger<span class="Delimiter">(</span>rhs<span class="Delimiter">))</span> <span class="Delimiter">{</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario && !Scenario_testing_scenario<span class="Delimiter">)</span>
|
||||
<span class="Comment">// genuine test in a mu file</span>
|
||||
<span class="Comment">// genuine test in a .mu file</span>
|
||||
raise << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">F - "</span> << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": location '"</span> << address << <span class="Constant">"' can't contain non-number "</span> << rhs << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
|
||||
<span class="Normal">else</span>
|
||||
<span class="Comment">// just testing scenario support</span>
|
||||
|
@ -408,7 +409,7 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"checking location "</span> << address << end<span class="Delimiter">();</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address<span class="Delimiter">)</span> != value<span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario && !Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
<span class="Comment">// genuine test in a mu file</span>
|
||||
<span class="Comment">// genuine test in a .mu file</span>
|
||||
raise << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">F - "</span> << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": expected location '"</span> << address << <span class="Constant">"' to contain "</span> << no_scientific<span class="Delimiter">(</span>value<span class="Delimiter">)</span> << <span class="Constant">" but saw "</span> << no_scientific<span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address<span class="Delimiter">))</span> << <span class="cSpecial">'\n'</span> << end<span class="Delimiter">();</span>
|
||||
<span class="Delimiter">}</span>
|
||||
<span class="Normal">else</span> <span class="Delimiter">{</span>
|
||||
|
@ -467,7 +468,7 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"checking location "</span> << address+i << end<span class="Delimiter">();</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address+i<span class="Delimiter">)</span> != literal<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">))</span> <span class="Delimiter">{</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario && !Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
<span class="Comment">// genuine test in a mu file</span>
|
||||
<span class="Comment">// genuine test in a .mu file</span>
|
||||
raise << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">F - "</span> << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": expected location "</span> << <span class="Delimiter">(</span>address+i<span class="Delimiter">)</span> << <span class="Constant">" to contain "</span> << literal<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)</span> << <span class="Constant">" but saw "</span> << no_scientific<span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address+i<span class="Delimiter">))</span> << <span class="Constant">" ('"</span> << <span class="Normal">static_cast</span><<span class="Normal">char</span>><span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address+i<span class="Delimiter">))</span> << <span class="Constant">"')</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
|
||||
<span class="Delimiter">}</span>
|
||||
<span class="Normal">else</span> <span class="Delimiter">{</span>
|
||||
|
@ -751,7 +752,7 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
<span class="Normal">int</span> count = trace_count<span class="Delimiter">(</span>label<span class="Delimiter">);</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>count != expected_count<span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario && !Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
<span class="Comment">// genuine test in a mu file</span>
|
||||
<span class="Comment">// genuine test in a .mu file</span>
|
||||
raise << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">F - "</span> << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": "</span> << maybe<span class="Delimiter">(</span>current_recipe_name<span class="Delimiter">())</span> << <span class="Constant">"expected "</span> << expected_count << <span class="Constant">" lines in trace with label '"</span> << label << <span class="Constant">"' in trace: "</span> << end<span class="Delimiter">();</span>
|
||||
DUMP<span class="Delimiter">(</span>label<span class="Delimiter">);</span>
|
||||
<span class="Delimiter">}</span>
|
||||
|
@ -776,7 +777,7 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
<span class="traceContains">+error: main: expected 2 lines in trace with label 'a' in trace</span>
|
||||
|
||||
<span class="Comment">//: Minor detail: ignore 'system' calls in scenarios, since anything we do</span>
|
||||
<span class="Comment">//: with them is by definition impossible to test through mu.</span>
|
||||
<span class="Comment">//: with them is by definition impossible to test through Mu.</span>
|
||||
<span class="Delimiter">:(after "case _SYSTEM:")</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario<span class="Delimiter">)</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
|
||||
|
||||
|
|
|
@ -117,8 +117,8 @@ candidates = strictly_matching_shape_shifting_variants<span class="Delimiter">(<
|
|||
<span class="Delimiter">}</span>
|
||||
<span class="Normal">skip_shape_shifting_variants</span>:<span class="Delimiter">;</span>
|
||||
|
||||
<span class="Comment">//: make sure we have no unspecialized shape-shifting recipes being called</span>
|
||||
<span class="Comment">//: before running mu programs</span>
|
||||
<span class="Comment">//: before running Mu programs, make sure no unspecialized shape-shifting</span>
|
||||
<span class="Comment">//: recipes can be called</span>
|
||||
|
||||
<span class="Delimiter">:(before "End Instruction Operation Checks")</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>contains_key<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> inst<span class="Delimiter">.</span>operation<span class="Delimiter">)</span> && inst<span class="Delimiter">.</span>operation >= MAX_PRIMITIVE_RECIPES
|
||||
|
|
|
@ -561,7 +561,7 @@ set<<span class="Normal">int</span>> ingredient_indices<span class="Delimi
|
|||
<span class="Comment">//: can't pass both pointers back out, because if a caller tries to make both</span>
|
||||
<span class="Comment">//: identical then you can't tell which value will be written on the way out.</span>
|
||||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: Experimental solution: just tell mu that one points inside the other.</span>
|
||||
<span class="Comment">//: Experimental solution: just tell Mu that one points inside the other.</span>
|
||||
<span class="Comment">//: This way we can return just one pointer as high up as necessary to capture</span>
|
||||
<span class="Comment">//: all modifications performed by a recipe.</span>
|
||||
<span class="Comment">//:</span>
|
||||
|
|
|
@ -36,7 +36,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
<body>
|
||||
<pre id='vimCodeElement'>
|
||||
<span class="Comment">//: Routines can be put in a 'waiting' state, from which it will be ready to</span>
|
||||
<span class="Comment">//: run again when a specific memory location changes its value. This is mu's</span>
|
||||
<span class="Comment">//: run again when a specific memory location changes its value. This is Mu's</span>
|
||||
<span class="Comment">//: basic technique for orchestrating the order in which different routines</span>
|
||||
<span class="Comment">//: operate.</span>
|
||||
|
||||
|
|
|
@ -77,9 +77,9 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
<span class="Normal">int</span> height = tb_height<span class="Delimiter">();</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>width > <span class="Constant">222</span> || height > <span class="Constant">222</span><span class="Delimiter">)</span> tb_shutdown<span class="Delimiter">();</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>width > <span class="Constant">222</span><span class="Delimiter">)</span>
|
||||
raise << <span class="Constant">"sorry, mu doesn't support windows wider than 222 characters in console mode. Please resize your window.</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
|
||||
raise << <span class="Constant">"sorry, Mu doesn't support windows wider than 222 characters in console mode. Please resize your window.</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>height > <span class="Constant">222</span><span class="Delimiter">)</span>
|
||||
raise << <span class="Constant">"sorry, mu doesn't support windows taller than 222 characters in console mode. Please resize your window.</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
|
||||
raise << <span class="Constant">"sorry, Mu doesn't support windows taller than 222 characters in console mode. Please resize your window.</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
|
||||
<span class="Identifier">break</span><span class="Delimiter">;</span>
|
||||
<span class="Delimiter">}</span>
|
||||
|
||||
|
@ -561,7 +561,7 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
<span class="Identifier">break</span><span class="Delimiter">;</span>
|
||||
<span class="Delimiter">}</span>
|
||||
|
||||
<span class="Comment">//: a hack to make edit.mu more responsive</span>
|
||||
<span class="Comment">//: hack to make text-mode apps more responsive under Unix</span>
|
||||
|
||||
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
|
||||
CLEAR_DISPLAY_FROM<span class="Delimiter">,</span>
|
||||
|
|
|
@ -319,7 +319,7 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
<span class="Normal">if</span> <span class="Delimiter">(</span>color == -<span class="Constant">1</span> || color == get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> addr+cell_color_offset<span class="Delimiter">))</span> <span class="Identifier">continue</span><span class="Delimiter">;</span>
|
||||
<span class="Comment">// contents match but color is off</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario && !Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
<span class="Comment">// genuine test in a mu file</span>
|
||||
<span class="Comment">// genuine test in a .mu file</span>
|
||||
raise << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">F - "</span> << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": expected screen location ("</span> << row << <span class="Constant">", "</span> << column << <span class="Constant">", address "</span> << addr << <span class="Constant">", value "</span> << no_scientific<span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> addr<span class="Delimiter">))</span> << <span class="Constant">") to be in color "</span> << color << <span class="Constant">" instead of "</span> << no_scientific<span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> addr+cell_color_offset<span class="Delimiter">))</span> << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
|
||||
dump_screen<span class="Delimiter">();</span>
|
||||
<span class="Delimiter">}</span>
|
||||
|
@ -347,7 +347,7 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
ostringstream color_phrase<span class="Delimiter">;</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>color != -<span class="Constant">1</span><span class="Delimiter">)</span> color_phrase << <span class="Constant">" in color "</span> << color<span class="Delimiter">;</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario && !Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
<span class="Comment">// genuine test in a mu file</span>
|
||||
<span class="Comment">// genuine test in a .mu file</span>
|
||||
raise << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">F - "</span> << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": expected screen location ("</span> << row << <span class="Constant">", "</span> << column << <span class="Constant">") to contain "</span> << curr << expected_pretty << color_phrase<span class="Delimiter">.</span>str<span class="Delimiter">()</span> << <span class="Constant">" instead of "</span> << no_scientific<span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> addr<span class="Delimiter">))</span> << actual_pretty << <span class="cSpecial">'\n'</span> << end<span class="Delimiter">();</span>
|
||||
dump_screen<span class="Delimiter">();</span>
|
||||
<span class="Delimiter">}</span>
|
||||
|
|
|
@ -251,6 +251,7 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
<span class="Delimiter">}</span>
|
||||
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
|
||||
<span class="Normal">case</span> _READ_FROM_SOCKET: <span class="Delimiter">{</span>
|
||||
cerr << <span class="Constant">"$read-from-socket</span><span class="cSpecial">\n</span><span class="Constant">"</span><span class="Delimiter">;</span>
|
||||
<span class="Normal">long</span> <span class="Normal">long</span> <span class="Normal">int</span> x = <span class="Normal">static_cast</span><<span class="Normal">long</span> <span class="Normal">long</span> <span class="Normal">int</span>><span class="Delimiter">(</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
|
||||
socket_t* socket = <span class="Normal">reinterpret_cast</span><socket_t*><span class="Delimiter">(</span>x<span class="Delimiter">);</span>
|
||||
<span class="Comment">// 1. we'd like to simply read() from the socket</span>
|
||||
|
@ -260,19 +261,22 @@ put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span
|
|||
<span class="Comment">// 3. but poll() will block on EOF, so only use poll() on the very first</span>
|
||||
<span class="Comment">// $read-from-socket on a socket</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>!socket<span class="Delimiter">-></span>polled<span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
socket<span class="Delimiter">-></span>polled = <span class="Constant">true</span><span class="Delimiter">;</span>
|
||||
pollfd p<span class="Delimiter">;</span>
|
||||
bzero<span class="Delimiter">(</span>&p<span class="Delimiter">,</span> <span class="Normal">sizeof</span><span class="Delimiter">(</span>p<span class="Delimiter">));</span>
|
||||
p<span class="Delimiter">.</span>fd = socket<span class="Delimiter">-></span>fd<span class="Delimiter">;</span>
|
||||
p<span class="Delimiter">.</span>events = POLLIN | POLLHUP<span class="Delimiter">;</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>poll<span class="Delimiter">(</span>&p<span class="Delimiter">,</span> <span class="Comment">/*</span><span class="Comment">num pollfds</span><span class="Comment">*/</span><span class="Constant">1</span><span class="Delimiter">,</span> <span class="Comment">/*</span><span class="Comment">no timeout</span><span class="Comment">*/</span>-<span class="Constant">1</span><span class="Delimiter">)</span> <= <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
<span class="Normal">if</span> <span class="Delimiter">(</span>poll<span class="Delimiter">(</span>&p<span class="Delimiter">,</span> <span class="Comment">/*</span><span class="Comment">num pollfds</span><span class="Comment">*/</span><span class="Constant">1</span><span class="Delimiter">,</span> <span class="Comment">/*</span><span class="Comment">timeout</span><span class="Comment">*/</span><span class="Constant">100</span><span class="Comment">/*</span><span class="Comment">ms</span><span class="Comment">*/</span><span class="Delimiter">)</span> <= <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
||||
raise << maybe<span class="Delimiter">(</span>current_recipe_name<span class="Delimiter">())</span> << <span class="Constant">"error in $read-from-socket</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
|
||||
products<span class="Delimiter">.</span>resize<span class="Delimiter">(</span><span class="Constant">2</span><span class="Delimiter">);</span>
|
||||
products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>push_back<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
|
||||
products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>push_back<span class="Delimiter">(</span><span class="Constant">false</span><span class="Delimiter">);</span>
|
||||
<span class="Identifier">break</span><span class="Delimiter">;</span>
|
||||
<span class="Delimiter">}</span>
|
||||
cerr << <span class="Constant">"poll output: "</span> << p<span class="Delimiter">.</span>revents << <span class="cSpecial">'\n'</span><span class="Delimiter">;</span>
|
||||
cerr << <span class="Constant">"setting socket->polled</span><span class="cSpecial">\n</span><span class="Constant">"</span><span class="Delimiter">;</span>
|
||||
socket<span class="Delimiter">-></span>polled = <span class="Constant">true</span><span class="Delimiter">;</span>
|
||||
<span class="Delimiter">}</span>
|
||||
cerr << <span class="Constant">"$read-from-socket "</span> << x << <span class="Constant">" continuing</span><span class="cSpecial">\n</span><span class="Constant">"</span><span class="Delimiter">;</span>
|
||||
<span class="Normal">int</span> bytes = <span class="Normal">static_cast</span><<span class="Normal">int</span>><span class="Delimiter">(</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
|
||||
<span class="Normal">char</span>* contents = <span class="Normal">new</span> <span class="Normal">char</span>[bytes]<span class="Delimiter">;</span>
|
||||
bzero<span class="Delimiter">(</span>contents<span class="Delimiter">,</span> bytes<span class="Delimiter">);</span>
|
||||
|
|
|
@ -33,72 +33,90 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
</head>
|
||||
<body>
|
||||
<pre id='vimCodeElement'>
|
||||
<span class="Comment"># Wrappers around socket primitives that take a 'local-network' object and are</span>
|
||||
<span class="Comment"># thus easier to test.</span>
|
||||
<span class="Comment"># Wrappers around socket primitives that are easier to test.</span>
|
||||
<span class="Comment">#</span>
|
||||
<span class="Comment"># The current semantics of fake port-connections don't match UNIX socket ones,</span>
|
||||
<span class="Comment"># but we'll improve them as we learn more.</span>
|
||||
|
||||
<span class="muData">container</span> local-network [
|
||||
data:&:@:port-connection
|
||||
]
|
||||
|
||||
<span class="Comment"># Port connections represent connections to ports on localhost.</span>
|
||||
<span class="Comment"># Before passing a local-network object to network functions</span>
|
||||
<span class="Comment"># `start-reading-socket` and `start-writing-socket`, add port-connections to</span>
|
||||
<span class="Comment"># the local-network.</span>
|
||||
<span class="Comment"># To test client operations, use `assume-resources` with a filename that</span>
|
||||
<span class="Comment"># begins with a hostname. (Filenames starting with '/' are assumed to be</span>
|
||||
<span class="Comment"># local.)</span>
|
||||
<span class="Comment">#</span>
|
||||
<span class="Comment"># For reading, `receive-from-socket` will check for a</span>
|
||||
<span class="Comment"># port-connection on the port parameter that's been passed in. If there's</span>
|
||||
<span class="Comment"># no port-connection for that port, it will return nothing and log an error.</span>
|
||||
<span class="Comment"># If there is a port-connection for that port, it will transmit the contents</span>
|
||||
<span class="Comment"># to the provided sink.</span>
|
||||
<span class="Comment">#</span>
|
||||
<span class="Comment"># For writing, `start-writing-socket` returns a sink connecting the</span>
|
||||
<span class="Comment"># caller to the socket on the passed-in port.</span>
|
||||
<span class="muData">container</span> port-connection [
|
||||
port:num
|
||||
contents:text
|
||||
]
|
||||
<span class="Comment"># To test server operations, just run a real client against localhost.</span>
|
||||
|
||||
<span class="muRecipe">def</span> new-port-connection port:num, contents:text<span class="muRecipe"> -> </span>p:&:port-connection [
|
||||
<span class="muScenario">scenario</span> example-server-test [
|
||||
<span class="Constant">local-scope</span>
|
||||
<span class="Constant">load-ingredients</span>
|
||||
p:&:port-connection<span class="Special"> <- </span>new <span class="Constant">port-connection:type</span>
|
||||
*p<span class="Special"> <- </span>merge port, contents
|
||||
]
|
||||
|
||||
<span class="muRecipe">def</span> new-fake-network<span class="muRecipe"> -> </span>n:&:local-network [
|
||||
<span class="Constant">local-scope</span>
|
||||
<span class="Constant">load-ingredients</span>
|
||||
n:&:local-network<span class="Special"> <- </span>new <span class="Constant">local-network:type</span>
|
||||
local-network-ports:&:@:port-connection<span class="Special"> <- </span>new <span class="Constant">port-connection:type</span>, <span class="Constant">0</span>
|
||||
*n<span class="Special"> <- </span>put *n, <span class="Constant">data:offset</span>, local-network-ports
|
||||
]
|
||||
|
||||
<span class="muScenario">scenario</span> write-to-fake-socket [
|
||||
<span class="Constant">local-scope</span>
|
||||
single-port-network:&:local-network<span class="Special"> <- </span>new-fake-network
|
||||
sink:&:sink:char, writer:num/routine<span class="Special"> <- </span>start-writing-socket single-port-network, <span class="Constant">8080</span>
|
||||
sink<span class="Special"> <- </span>write sink, <span class="Constant">120/x</span>
|
||||
close sink
|
||||
wait-for-routine writer
|
||||
tested-port-connections:&:@:port-connection<span class="Special"> <- </span>get *single-port-network, <span class="Constant">data:offset</span>
|
||||
tested-port-connection:port-connection<span class="Special"> <- </span>index *tested-port-connections, <span class="Constant">0</span>
|
||||
contents:text<span class="Special"> <- </span>get tested-port-connection, <span class="Constant">contents:offset</span>
|
||||
<span class="Constant">10</span>:@:char/<span class="Special">raw <- </span>copy *contents
|
||||
memory-should-contain [
|
||||
<span class="Constant">10</span>:array:character<span class="Special"> <- </span><span class="Constant">[x]</span>
|
||||
<span class="Comment"># test server without a fake on a random (real) port</span>
|
||||
<span class="Comment"># that way repeatedly running the test will give ports time to timeout and</span>
|
||||
<span class="Comment"># close before reusing them</span>
|
||||
make-random-nondeterministic
|
||||
port:num<span class="Special"> <- </span>random-in-range <span class="Constant">0/real-random-numbers</span>, <span class="Constant">8000</span>, <span class="Constant">8100</span>
|
||||
run [
|
||||
socket:num<span class="Special"> <- </span>$open-server-socket port
|
||||
$print <span class="Constant">[server socket: ]</span>, socket, <span class="Constant">10/newline</span>
|
||||
assert socket, <span class="Constant">[ </span>
|
||||
<span class="Constant">F - example-server-test: $open-server-socket failed]</span>
|
||||
$print <span class="Constant">[starting up server routine]</span>, <span class="Constant">10/newline</span>
|
||||
handler-routine:number<span class="Special"> <- </span>start-running serve-one-request socket, example-handler
|
||||
]
|
||||
$print <span class="Constant">[starting to read from port ]</span>, port, <span class="Constant">10/newline</span>
|
||||
source:&:source:char<span class="Special"> <- </span>start-reading-from-network <span class="Constant">0/real-resources</span>, <span class="Constant">[localhost]</span>, <span class="Constant">[/]</span>, port
|
||||
response:text<span class="Special"> <- </span>drain source
|
||||
<span class="Constant">10</span>:@:char/<span class="Special">raw <- </span>copy *response
|
||||
memory-should-contain [
|
||||
<span class="Constant">10</span>:array:character<span class="Special"> <- </span><span class="Constant">[abc]</span>
|
||||
]
|
||||
]
|
||||
<span class="Comment"># helper just for this scenario</span>
|
||||
<span class="muRecipe">def</span> example-handler query:text<span class="muRecipe"> -> </span>response:text [
|
||||
<span class="Constant">local-scope</span>
|
||||
<span class="Constant">load-ingredients</span>
|
||||
<span class="muControl">reply</span> <span class="Constant">[abc]</span>
|
||||
]
|
||||
|
||||
<span class="CommentedCode">#? scenario example-client-test [</span>
|
||||
<span class="CommentedCode">#? local-scope</span>
|
||||
<span class="CommentedCode">#? assume-resources [</span>
|
||||
<span class="CommentedCode">#? [example.com/] -> [abc]</span>
|
||||
<span class="CommentedCode">#? ]</span>
|
||||
<span class="CommentedCode">#? run [</span>
|
||||
<span class="CommentedCode">#? source:&:source:char <- start-reading-from-network resources, [example.com], [/]</span>
|
||||
<span class="CommentedCode">#? ]</span>
|
||||
<span class="CommentedCode">#? contents:text <- drain source</span>
|
||||
<span class="CommentedCode">#? 10:@:char/raw <- copy *contents</span>
|
||||
<span class="CommentedCode">#? memory-should-contain [</span>
|
||||
<span class="CommentedCode">#? 10:address:character <- [abc]</span>
|
||||
<span class="CommentedCode">#? ]</span>
|
||||
<span class="CommentedCode">#? ]</span>
|
||||
|
||||
<span class="muData">type</span> request-handler = (recipe text<span class="muRecipe"> -> </span>text)
|
||||
|
||||
<span class="muRecipe">def</span> serve-one-request socket:num, request-handler:request-handler [
|
||||
<span class="Constant">local-scope</span>
|
||||
<span class="Constant">load-ingredients</span>
|
||||
session:num<span class="Special"> <- </span>$accept socket
|
||||
$print <span class="Constant">[server session socket: ]</span>, session, <span class="Constant">10/newline</span>
|
||||
assert session, <span class="Constant">[ </span>
|
||||
<span class="Constant">F - example-server-test: $accept failed]</span>
|
||||
contents:&:source:char, sink:&:sink:char<span class="Special"> <- </span>new-channel <span class="Constant">30</span>
|
||||
sink<span class="Special"> <- </span>start-running receive-from-socket session, sink
|
||||
query:text<span class="Special"> <- </span>drain contents
|
||||
response:text<span class="Special"> <- </span>call request-handler, query
|
||||
write-to-socket session, response
|
||||
$close-socket session
|
||||
]
|
||||
|
||||
<span class="muRecipe">def</span> start-reading-from-network resources:&:resources, host:text, path:text<span class="muRecipe"> -> </span>contents:&:source:char [
|
||||
<span class="Constant">local-scope</span>
|
||||
<span class="Constant">load-ingredients</span>
|
||||
$print <span class="Constant">[running start-reading-from-network]</span>, <span class="Constant">10/newline</span>
|
||||
<span class="Delimiter">{</span>
|
||||
port:num, port-found?:boolean<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
|
||||
<span class="muControl">break-if</span> port-found?
|
||||
port<span class="Special"> <- </span>copy <span class="Constant">80/http-port</span>
|
||||
<span class="Delimiter">}</span>
|
||||
<span class="Delimiter">{</span>
|
||||
<span class="muControl">break-if</span> resources
|
||||
<span class="Comment"># real network</span>
|
||||
socket:num<span class="Special"> <- </span>$open-client-socket host, <span class="Constant">80/http-port</span>
|
||||
socket:num<span class="Special"> <- </span>$open-client-socket host, port
|
||||
$print <span class="Constant">[client socket: ]</span>, socket, <span class="Constant">10/newline</span>
|
||||
assert socket, <span class="Constant">[contents]</span>
|
||||
req:text<span class="Special"> <- </span>interpolate <span class="Constant">[GET _ HTTP/1.1]</span>, path
|
||||
request-socket socket, req
|
||||
|
@ -137,70 +155,72 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
$write-to-socket socket, <span class="Constant">10/lf</span>
|
||||
]
|
||||
|
||||
<span class="muRecipe">def</span> start-writing-socket network:&:local-network, port:num<span class="muRecipe"> -> </span>sink:&:sink:char, routine-id:num [
|
||||
<span class="Constant">local-scope</span>
|
||||
<span class="Constant">load-ingredients</span>
|
||||
source:&:source:char, sink:&:sink:char<span class="Special"> <- </span>new-channel <span class="Constant">30</span>
|
||||
<span class="Delimiter">{</span>
|
||||
<span class="muControl">break-if</span> network
|
||||
socket:num<span class="Special"> <- </span>$open-server-socket port
|
||||
session:num<span class="Special"> <- </span>$accept socket
|
||||
<span class="Comment"># TODO Create channel implementation of write-to-socket.</span>
|
||||
<span class="muControl">return</span> sink, <span class="Constant">0/routine-id</span>
|
||||
<span class="Delimiter">}</span>
|
||||
<span class="Comment"># fake network</span>
|
||||
routine-id<span class="Special"> <- </span>start-running transmit-to-fake-socket network, port, source
|
||||
]
|
||||
<span class="CommentedCode">#? def start-writing-socket network:&:local-network, port:num -> sink:&:sink:char, routine-id:num [</span>
|
||||
<span class="CommentedCode">#? local-scope</span>
|
||||
<span class="CommentedCode">#? load-ingredients</span>
|
||||
<span class="CommentedCode">#? source:&:source:char, sink:&:sink:char <- new-channel 30</span>
|
||||
<span class="CommentedCode">#? {</span>
|
||||
<span class="CommentedCode">#? break-if network</span>
|
||||
<span class="CommentedCode">#? socket:num <- $open-server-socket port</span>
|
||||
<span class="CommentedCode">#? session:num <- $accept socket</span>
|
||||
<span class="CommentedCode">#? # TODO Create channel implementation of write-to-socket.</span>
|
||||
<span class="CommentedCode">#? return sink, 0/routine-id</span>
|
||||
<span class="CommentedCode">#? }</span>
|
||||
<span class="CommentedCode">#? # fake network</span>
|
||||
<span class="CommentedCode">#? routine-id <- start-running transmit-to-fake-socket network, port, source</span>
|
||||
<span class="CommentedCode">#? ]</span>
|
||||
|
||||
<span class="muRecipe">def</span> transmit-to-fake-socket network:&:local-network, port:num, source:&:source:char<span class="muRecipe"> -> </span>network:&:local-network, source:&:source:char [
|
||||
<span class="Constant">local-scope</span>
|
||||
<span class="Constant">load-ingredients</span>
|
||||
<span class="Comment"># compute new port connection contents</span>
|
||||
buf:&:buffer<span class="Special"> <- </span>new-buffer <span class="Constant">30</span>
|
||||
<span class="Delimiter">{</span>
|
||||
c:char, done?:bool, source<span class="Special"> <- </span>read source
|
||||
<span class="muControl">break-unless</span> c
|
||||
buf<span class="Special"> <- </span>append buf, c
|
||||
<span class="muControl">break-if</span> done?
|
||||
<span class="muControl">loop</span>
|
||||
<span class="Delimiter">}</span>
|
||||
contents:text<span class="Special"> <- </span>buffer-to-array buf
|
||||
new-port-connection:&:port-connection<span class="Special"> <- </span>new-port-connection port, contents
|
||||
<span class="Comment"># Got the contents of the channel, time to write to fake port.</span>
|
||||
i:num<span class="Special"> <- </span>copy <span class="Constant">0</span>
|
||||
port-connections:&:@:port-connection<span class="Special"> <- </span>get *network, <span class="Constant">data:offset</span>
|
||||
len:num<span class="Special"> <- </span>length *port-connections
|
||||
<span class="Delimiter">{</span>
|
||||
done?:bool<span class="Special"> <- </span>greater-or-equal i, len
|
||||
<span class="muControl">break-if</span> done?
|
||||
current:port-connection<span class="Special"> <- </span>index *port-connections, i
|
||||
current-port:num<span class="Special"> <- </span>get current, <span class="Constant">port:offset</span>
|
||||
ports-match?:bool<span class="Special"> <- </span>equal current-port, port
|
||||
i<span class="Special"> <- </span>add i, <span class="Constant">1</span>
|
||||
<span class="muControl">loop-unless</span> ports-match?
|
||||
<span class="Comment"># Found an existing connection on this port, overwrite.</span>
|
||||
put-index *port-connections, i, *new-port-connection
|
||||
<span class="muControl">reply</span>
|
||||
<span class="Delimiter">}</span>
|
||||
<span class="Comment"># Couldn't find an existing connection on this port, initialize a new one.</span>
|
||||
new-len:num<span class="Special"> <- </span>add len, <span class="Constant">1</span>
|
||||
new-port-connections:&:@:port-connection<span class="Special"> <- </span>new <span class="Constant">port-connection:type</span>, new-len
|
||||
put *network, <span class="Constant">data:offset</span>, new-port-connections
|
||||
i:num<span class="Special"> <- </span>copy <span class="Constant">0</span>
|
||||
<span class="Delimiter">{</span>
|
||||
done?:bool<span class="Special"> <- </span>greater-or-equal i, len
|
||||
<span class="muControl">break-if</span> done?
|
||||
tmp:port-connection<span class="Special"> <- </span>index *port-connections, i
|
||||
put-index *new-port-connections, i, tmp
|
||||
<span class="Delimiter">}</span>
|
||||
put-index *new-port-connections, len, *new-port-connection
|
||||
]
|
||||
<span class="CommentedCode">#? def transmit-to-fake-socket network:&:local-network, port:num, source:&:source:char -> network:&:local-network, source:&:source:char [</span>
|
||||
<span class="CommentedCode">#? local-scope</span>
|
||||
<span class="CommentedCode">#? load-ingredients</span>
|
||||
<span class="CommentedCode">#? # compute new port connection contents</span>
|
||||
<span class="CommentedCode">#? buf:&:buffer <- new-buffer 30</span>
|
||||
<span class="CommentedCode">#? {</span>
|
||||
<span class="CommentedCode">#? c:char, done?:bool, source <- read source</span>
|
||||
<span class="CommentedCode">#? break-unless c</span>
|
||||
<span class="CommentedCode">#? buf <- append buf, c</span>
|
||||
<span class="CommentedCode">#? break-if done?</span>
|
||||
<span class="CommentedCode">#? loop</span>
|
||||
<span class="CommentedCode">#? }</span>
|
||||
<span class="CommentedCode">#? contents:text <- buffer-to-array buf</span>
|
||||
<span class="CommentedCode">#? new-port-connection:&:port-connection <- new-port-connection port, contents</span>
|
||||
<span class="CommentedCode">#? # Got the contents of the channel, time to write to fake port.</span>
|
||||
<span class="CommentedCode">#? i:num <- copy 0</span>
|
||||
<span class="CommentedCode">#? port-connections:&:@:port-connection <- get *network, data:offset</span>
|
||||
<span class="CommentedCode">#? len:num <- length *port-connections</span>
|
||||
<span class="CommentedCode">#? {</span>
|
||||
<span class="CommentedCode">#? done?:bool <- greater-or-equal i, len</span>
|
||||
<span class="CommentedCode">#? break-if done?</span>
|
||||
<span class="CommentedCode">#? current:port-connection <- index *port-connections, i</span>
|
||||
<span class="CommentedCode">#? current-port:num <- get current, port:offset</span>
|
||||
<span class="CommentedCode">#? ports-match?:bool <- equal current-port, port</span>
|
||||
<span class="CommentedCode">#? i <- add i, 1</span>
|
||||
<span class="CommentedCode">#? loop-unless ports-match?</span>
|
||||
<span class="CommentedCode">#? # Found an existing connection on this port, overwrite.</span>
|
||||
<span class="CommentedCode">#? put-index *port-connections, i, *new-port-connection</span>
|
||||
<span class="CommentedCode">#? reply</span>
|
||||
<span class="CommentedCode">#? }</span>
|
||||
<span class="CommentedCode">#? # Couldn't find an existing connection on this port, initialize a new one.</span>
|
||||
<span class="CommentedCode">#? new-len:num <- add len, 1</span>
|
||||
<span class="CommentedCode">#? new-port-connections:&:@:port-connection <- new port-connection:type, new-len</span>
|
||||
<span class="CommentedCode">#? put *network, data:offset, new-port-connections</span>
|
||||
<span class="CommentedCode">#? i:num <- copy 0</span>
|
||||
<span class="CommentedCode">#? {</span>
|
||||
<span class="CommentedCode">#? done?:bool <- greater-or-equal i, len</span>
|
||||
<span class="CommentedCode">#? break-if done?</span>
|
||||
<span class="CommentedCode">#? tmp:port-connection <- index *port-connections, i</span>
|
||||
<span class="CommentedCode">#? put-index *new-port-connections, i, tmp</span>
|
||||
<span class="CommentedCode">#? }</span>
|
||||
<span class="CommentedCode">#? put-index *new-port-connections, len, *new-port-connection</span>
|
||||
<span class="CommentedCode">#? ]</span>
|
||||
|
||||
<span class="muRecipe">def</span> receive-from-socket socket:num, sink:&:sink:char<span class="muRecipe"> -> </span>sink:&:sink:char [
|
||||
<span class="Constant">local-scope</span>
|
||||
<span class="Constant">load-ingredients</span>
|
||||
<span class="Delimiter">{</span>
|
||||
$print <span class="Constant">[read-from-socket ]</span>, socket, <span class="Constant">10/newline</span>
|
||||
req:text, eof?:bool<span class="Special"> <- </span>$read-from-socket socket, <span class="Constant">4096/bytes</span>
|
||||
<span class="muControl">loop-unless</span> req
|
||||
bytes-read:num<span class="Special"> <- </span>length *req
|
||||
i:num<span class="Special"> <- </span>copy <span class="Constant">0</span>
|
||||
<span class="Delimiter">{</span>
|
||||
|
|
|
@ -34,8 +34,8 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
</head>
|
||||
<body>
|
||||
<pre id='vimCodeElement'>
|
||||
<span class="Comment">//: Helper for various programming environments: run arbitrary mu code and</span>
|
||||
<span class="Comment">//: return some result in string form.</span>
|
||||
<span class="Comment">//: Helper for various programming environments: run arbitrary Mu code and</span>
|
||||
<span class="Comment">//: return some result in text form.</span>
|
||||
|
||||
<span class="Delimiter">:(scenario run_interactive_code)</span>
|
||||
<span class="muRecipe">def</span> main [
|
||||
|
|
|
@ -38,7 +38,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: Location 0 - unused (since it can help uncover bugs)</span>
|
||||
<span class="Comment">//: Locations 1-899 - reserved for tests</span>
|
||||
<span class="Comment">//: Locations 900-999 - reserved for predefined globals in mu scenarios, like keyboard, screen, etc.</span>
|
||||
<span class="Comment">//: Locations 900-999 - reserved for predefined globals in Mu scenarios, like keyboard, screen, etc.</span>
|
||||
<span class="Delimiter">:(before "End Setup")</span>
|
||||
assert<span class="Delimiter">(</span>Max_variables_in_scenarios == <span class="Constant">900</span><span class="Delimiter">);</span>
|
||||
<span class="Comment">//: Locations 1000 ('Reserved_for_tests') onward - available to the allocator in chunks of size Initial_memory_per_routine.</span>
|
||||
|
@ -56,7 +56,7 @@ assert<span class="Delimiter">(</span>Next_recipe_ordinal == <span class="Consta
|
|||
<span class="SalientComment">//:: Depths for tracing</span>
|
||||
<span class="Comment">//:</span>
|
||||
<span class="Comment">//: 0 - unused</span>
|
||||
<span class="Comment">//: 1-100 - app-level trace statements in mu</span>
|
||||
<span class="Comment">//: 1-100 - app-level trace statements in Mu</span>
|
||||
<span class="Comment">//: 101-9989 - call-stack statements (mostly label run)</span>
|
||||
assert<span class="Delimiter">(</span>Initial_callstack_depth == <span class="Constant">101</span><span class="Delimiter">);</span>
|
||||
assert<span class="Delimiter">(</span>Max_callstack_depth == <span class="Constant">9989</span><span class="Delimiter">);</span>
|
||||
|
|
|
@ -43,8 +43,8 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
|
||||
<span class="Comment"># The chessboard function takes keyboard and screen objects as 'ingredients'.</span>
|
||||
<span class="Comment">#</span>
|
||||
<span class="Comment"># In mu it is good form (though not required) to explicitly show the</span>
|
||||
<span class="Comment"># hardware you rely on.</span>
|
||||
<span class="Comment"># In Mu it is good form (though not required) to explicitly state what</span>
|
||||
<span class="Comment"># hardware a function needs.</span>
|
||||
<span class="Comment">#</span>
|
||||
<span class="Comment"># Here the console and screen are both 0, which usually indicates real</span>
|
||||
<span class="Comment"># hardware rather than a fake for testing as you'll see below.</span>
|
||||
|
@ -53,7 +53,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
close-console <span class="Comment"># clean up screen, keyboard and mouse</span>
|
||||
]
|
||||
|
||||
<span class="SalientComment">## But enough about mu. Here's what it looks like to run the chessboard program.</span>
|
||||
<span class="SalientComment">## But enough about Mu. Here's what it looks like to run the chessboard program.</span>
|
||||
|
||||
<span class="muScenario">scenario</span> print-board-and-read-move [
|
||||
<span class="Constant">local-scope</span>
|
||||
|
|
|
@ -399,7 +399,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
]
|
||||
]
|
||||
|
||||
<span class="Comment"># just a little color for mu code</span>
|
||||
<span class="Comment"># just a little color for Mu code</span>
|
||||
|
||||
<span class="muScenario">scenario</span> render-colors-comments [
|
||||
<span class="Constant">local-scope</span>
|
||||
|
|
|
@ -219,7 +219,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
<span class="Constant"> <run-sandboxes-end></span>
|
||||
]
|
||||
|
||||
<span class="Comment"># copy code from recipe editor, persist, load into mu</span>
|
||||
<span class="Comment"># copy code from recipe editor, persist to disk, load</span>
|
||||
<span class="Comment"># replaced in a later layer (whereupon errors-found? will actually be set)</span>
|
||||
<span class="muRecipe">def</span> update-recipes env:&:environment, screen:&:screen<span class="muRecipe"> -> </span>errors-found?:bool, env:&:environment, screen:&:screen [
|
||||
<span class="Constant">local-scope</span>
|
||||
|
|
|
@ -39,7 +39,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
recipe-errors:text
|
||||
]
|
||||
|
||||
<span class="Comment"># copy code from recipe editor, persist, load into mu, save any errors</span>
|
||||
<span class="Comment"># copy code from recipe editor, persist to disk, load, save any errors</span>
|
||||
<span class="muRecipe">def!</span> update-recipes env:&:environment, screen:&:screen<span class="muRecipe"> -> </span>errors-found?:bool, env:&:environment, screen:&:screen [
|
||||
<span class="Constant">local-scope</span>
|
||||
<span class="Constant">load-ingredients</span>
|
||||
|
|
|
@ -35,7 +35,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
<body>
|
||||
<pre id='vimCodeElement'>
|
||||
<span class="SalientComment">## experimental compiler to translate programs written in a generic</span>
|
||||
<span class="SalientComment">## expression-oriented language called 'lambda' into mu</span>
|
||||
<span class="SalientComment">## expression-oriented language called 'lambda' into Mu</span>
|
||||
|
||||
<span class="muScenario">scenario</span> convert-lambda [
|
||||
run [
|
||||
|
@ -600,7 +600,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
|
|||
]
|
||||
]
|
||||
|
||||
<span class="SalientComment">## convert tree of cells to mu text</span>
|
||||
<span class="SalientComment">## convert tree of cells to Mu text</span>
|
||||
|
||||
<span class="muRecipe">def</span> to-mu in:&:cell<span class="muRecipe"> -> </span>out:text [
|
||||
<span class="Constant">local-scope</span>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
## experimental compiler to translate programs written in a generic
|
||||
## expression-oriented language called 'lambda' into mu
|
||||
## expression-oriented language called 'lambda' into Mu
|
||||
|
||||
scenario convert-lambda [
|
||||
run [
|
||||
|
@ -564,7 +564,7 @@ scenario parse-dotted-list-of-more-than-two-atoms [
|
|||
]
|
||||
]
|
||||
|
||||
## convert tree of cells to mu text
|
||||
## convert tree of cells to Mu text
|
||||
|
||||
def to-mu in:&:cell -> out:text [
|
||||
local-scope
|
||||
|
|
2
mu
2
mu
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Compile mu if necessary before running it.
|
||||
# Compile Mu if necessary before running it.
|
||||
|
||||
./build || exit 1
|
||||
|
||||
|
|
8
mu.vim
8
mu.vim
|
@ -20,7 +20,7 @@ set cpo&vim
|
|||
"? let b:syntax = "mu"
|
||||
|
||||
setlocal iskeyword=@,48-57,?,!,_,$,-
|
||||
setlocal formatoptions-=t " mu programs have long lines
|
||||
setlocal formatoptions-=t " Mu programs have long lines
|
||||
setlocal formatoptions+=c " but comments should still wrap
|
||||
|
||||
syntax match muComment /#.*$/ | highlight link muComment Comment
|
||||
|
@ -31,16 +31,16 @@ set comments+=n:#
|
|||
syntax match CommentedCode "#? .*"
|
||||
let b:cmt_head = "#? "
|
||||
|
||||
" mu strings are inside [ ... ] and can span multiple lines
|
||||
" Mu strings are inside [ ... ] and can span multiple lines
|
||||
" don't match '[' at end of line, that's usually code
|
||||
syntax region muString start=+\[[^\]]+ end=+\]+
|
||||
syntax match muString "\[\]"
|
||||
highlight link muString String
|
||||
" mu syntax for representing the screen in scenarios
|
||||
" Mu syntax for representing the screen in scenarios
|
||||
syntax region muScreen start=+ \.+ end=+\.$\|$+
|
||||
highlight link muScreen muString
|
||||
|
||||
" mu literals
|
||||
" Mu literals
|
||||
syntax match muLiteral %[^ ]\+:literal/[^ ,]*\|[^ ]\+:literal\>%
|
||||
syntax match muLiteral %\<[0-9-]\?[0-9]\+\>%
|
||||
syntax match muLiteral %\<[0-9-]\?[0-9]\+/[^ ,]*%
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
# Run this before a fresh project for 'mu edit' or 'mu sandbox' to make sure
|
||||
# you don't lose any work.
|
||||
# Run this before running './mu edit' or './mu sandbox' to make sure you don't
|
||||
# lose any work.
|
||||
#
|
||||
# You'll be editing code in lesson/recipes.mu, and any sandboxes you create
|
||||
# will be in lesson/0, lesson/1, etc., from top to bottom.
|
||||
|
|
|
@ -364,7 +364,7 @@ scenario editor-initializes-empty-text [
|
|||
]
|
||||
]
|
||||
|
||||
# just a little color for mu code
|
||||
# just a little color for Mu code
|
||||
|
||||
scenario render-colors-comments [
|
||||
local-scope
|
||||
|
|
|
@ -4,7 +4,7 @@ container environment [
|
|||
recipe-errors:text
|
||||
]
|
||||
|
||||
# copy code from recipe editor, persist, load into mu, save any errors
|
||||
# copy code from recipe editor, save to disk, load, save any errors
|
||||
# test-recipes is a hook for testing
|
||||
def! update-recipes env:&:environment, screen:&:screen, test-recipes:text -> errors-found?:bool, env:&:environment, screen:&:screen [
|
||||
local-scope
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
Variant of [the mu programming environment](../edit) that runs just the sandbox.
|
||||
Variant of [the Mu programming environment](../edit) that runs just the sandbox.
|
||||
|
||||
Suitable for people who want to run their favorite terminal-based editor with
|
||||
mu. Just run editor and sandbox inside split panes atop tmux. For example,
|
||||
here's mu running alongside vim:
|
||||
Mu. Just run editor and sandbox inside split panes atop tmux. For example,
|
||||
here's Mu running alongside vim:
|
||||
|
||||
<img alt='tmux+vim example' src='../html/tmux-vim-sandbox.png'>
|
||||
|
||||
|
@ -12,9 +12,9 @@ To set this up:
|
|||
|
||||
b) copy the file `mu_run` somewhere in your `$PATH`.
|
||||
|
||||
Now when you start tmux, split it into two vertical panes, run `mu sandbox` on
|
||||
the right pane and your editor on the left. You should be able to hit F4 in
|
||||
Now when you start tmux, split it into two vertical panes, run `./mu sandbox`
|
||||
on the right pane and your editor on the left. You should be able to hit F4 in
|
||||
either side to run the sandbox.
|
||||
|
||||
Known issues: you have to explicitly save inside your editor before hitting
|
||||
F4, unlike with `mu edit`.
|
||||
F4, unlike with `./mu edit`.
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
# hotkey for running mu over tmux (assumes exactly two panes, 'mu sandbox' running on the right/second window)
|
||||
# hotkey for running Mu over tmux (assumes exactly two panes, './mu sandbox' running on the right/second window)
|
||||
bind-key -n F4 run mu_run
|
||||
|
|
|
@ -17,13 +17,13 @@ function! HighlightTangledFile()
|
|||
highlight traceAbsent ctermfg=darkred
|
||||
syntax match tangleScenarioSetup /^\s*% .*/ | highlight link tangleScenarioSetup SpecialChar
|
||||
|
||||
" Our C++ files can have mu code in scenarios, so highlight mu comments like
|
||||
" Our C++ files can have Mu code in scenarios, so highlight Mu comments like
|
||||
" regular comments.
|
||||
syntax match muComment /# .*$/ | highlight link muComment Comment
|
||||
syntax match muSalientComment /##.*$/ | highlight link muSalientComment SalientComment
|
||||
syntax match muCommentedCode /#? .*$/ | highlight link muCommentedCode CommentedCode
|
||||
set comments+=n:#
|
||||
" Some other bare-bones mu highlighting.
|
||||
" Some other bare-bones Mu highlighting.
|
||||
syntax match muLiteral %[^ ]\+:literal/[^ ,]*\|[^ ]\+:literal\>%
|
||||
syntax match muLiteral %[^ ]\+:label/[^ ,]*\|[^ ]\+:label\>%
|
||||
syntax match muLiteral %[^ ]\+:type/[^ ,]*\|[^ ]\+:type\>%
|
||||
|
|
Loading…
Reference in New Issue