// Reorder a file based on directives starting with ':(' (tangle directives). // Insert #line directives to preserve line numbers in the original. // Clear lines starting with '//:' (tangle comments). #include #include #include #include using std::vector; #include using std::list; #include using std::pair; #include using std::string; #include using std::istream; using std::ostream; using std::cin; using std::cout; using std::cerr; #include using std::istringstream; using std::ostringstream; #include using std::ifstream; #include using std::isspace; // unicode-aware //// Core data structures struct Line { string filename; size_t line_number; string contents; Line() :line_number(0) {} Line(const string& text) :line_number(0) { contents = text; } Line(const string& text, const string& f, const size_t& l) { contents = text; filename = f; line_number = l; } Line(const string& text, const Line& origin) { contents = text; filename = origin.filename; line_number = origin.line_number; } }; // Emit a list of line contents, inserting directives just at discontinuities. // Needs to be a macro because 'out' can have the side effect of creating a // new trace in Trace_stream. #define EMIT(lines, out) if (!lines.empty()) { \ string last_file = lines.begin()->filename; \ size_t last_line = lines.begin()->line_number-1; \ out << line_directive(lines.begin()->line_number, lines.begin()->filename) << '\n'; \ for (list::const_iterator p = lines.begin(); p != lines.end(); ++p) { \ if (last_file != p->filename || last_line != p->line_number-1) \ out << line_directive(p->line_number, p->filename) << '\n'; \ out << p->contents << '\n'; \ last_file = p->filename; \ last_line = p->line_number; \ } \ } //// Traces and white-box tests bool Passed = true; long Num_failures = 0; #define CHECK(X) \ if (!(X)) { \ ++Num_failures; \ cerr << "\nF " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): " << #X << '\n'; \ Passed = false; \ return; \ } #define CHECK_EQ(X, Y) \ if ((X) != (Y)) { \ ++Num_failures; \ cerr << "\nF " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): " << #X << " == " << #Y << '\n'; \ cerr << " got " << (X) << '\n'; /* BEWARE: multiple eval */ \ Passed = false; \ return; \ } bool Hide_warnings = false; struct trace_stream { vector > past_lines; // [(layer label, line)] // accumulator for current line ostringstream* curr_stream; string curr_layer; trace_stream() :curr_stream(NULL) {} ~trace_stream() { if (curr_stream) delete curr_stream; } ostringstream& stream(string layer) { newline(); curr_stream = new ostringstream; curr_layer = layer; return *curr_stream; } // be sure to call this before messing with curr_stream or curr_layer void newline() { if (!curr_stream) return; string curr_contents = curr_stream->str(); curr_contents.erase(curr_contents.find_last_not_of("\r\n")+1); past_lines.push_back(pair(curr_layer, curr_contents)); delete curr_stream; curr_stream = NULL; } string readable_contents(string layer) { // missing layer = everything newline(); ostringstream output; for (vector >::iterator p = past_lines.begin(); p != past_lines.end(); ++p) if (layer.empty() || layer == p->first) output << p->first << ": " << with_newline(p->second); return output.str(); } string with_newline(string s) { if (s[s.size()-1] != '\n') return s+'\n'; return s; } }; trace_stream* Trace_stream = NULL; // Top-level helper. IMPORTANT: can't nest. #define trace(layer) !Trace_stream ? cerr /*print nothing*/ : Trace_stream->stream(layer) // Warnings should go straight to cerr by default since calls to trace() have // some unfriendly constraints (they delay printing, they can't nest) #define raise ((!Trace_stream || !Hide_warnings) ? cerr /*do print*/ : Trace_stream->stream("warn")) << __FILE__ << ":" << __LINE__ << " " // raise << die exits after printing -- unless Hide_warnings is set. struct die {}; ostream& operator<<(ostream& os, __attribute__((unused)) die) { if (Hide_warnings) return os; os << "dying\n"; exit(1); } #define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream; #define DUMP(layer) cerr << Trace_stream->readable_contents(layer) // Trace_stream is a resource, lease_tracer uses RAII to manage it. struct lease_tracer { lease_tracer() { Trace_stream = new trace_stream; } ~lease_tracer() { delete Trace_stream, Trace_stream = NULL; } }; #define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer; vector split(string s, string delim) { vector result; string::size_type begin=0, end=s.find(delim); while (true) { if (end == string::npos) { result.push_back(string(s, begin, string::npos)); break; } result.push_back(string(s, begin, end-begin)); begin = end+delim.size(); end = s.find(delim, begin); } return result; } bool check_trace_contents(string FUNCTION, string FILE, int LINE, string layer, string expected) { // empty layer == everything vector expected_lines = split(expected, "\n"); size_t curr_expected_line = 0; while (curr_expected_line < expected_lines.size() && expected_lines[curr_expected_line].empty()) ++curr_expected_line; if (curr_expected_line == expected_lines.size()) return true; Trace_stream->newline(); ostringstream output; for (vector >::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { if (!layer.empty() && layer != p->first) continue; if (p->second != expected_lines[curr_expected_line]) continue; ++curr_expected_line; while (curr_expected_line < expected_lines.size() && expected_lines[curr_expected_line].empty()) ++curr_expected_line; if (curr_expected_line == expected_lines.size()) return true; } ++Num_failures; cerr << "\nF " << FUNCTION << "(" << FILE << ":" << LINE << "): missing [" << expected_lines[curr_expected_line] << "] in trace:\n"; DUMP(layer); Passed = false; return false; } #define CHECK_TRACE_CONTENTS(...) check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__) int trace_count(string layer, string line) { Trace_stream->newline(); long result = 0; for (vector >::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { if (layer == p->first) if (line == "" || p->second == line) ++result; } return result; } #define CHECK_TRACE_WARNS() CHECK(trace_count("warn", "") > 0) #define CHECK_TRACE_DOESNT_WARN() \ if (trace_count("warn") > 0) { \ ++Num_failures; \ cerr << "\nF " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected warnings\n"; \ DUMP("warn"); \ Passed = false; \ return; \ } bool trace_doesnt_contain(string layer, string line) { return trace_count(layer, line) == 0; } #define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__)) // Tests for trace infrastructure void test_trace_check_compares() { CHECK_TRACE_CONTENTS("test layer", ""); trace("test layer") << "foo"; CHECK_TRACE_CONTENTS("test layer", "foo"); } void test_trace_check_filters_layers() { trace("test layer 1") << "foo"; trace("test layer 2") << "bar"; CHECK_TRACE_CONTENTS("test layer 1", "foo"); } void test_trace_check_ignores_other_lines() { trace("test layer 1") << "foo"; trace("test layer 1") << "bar"; CHECK_TRACE_CONTENTS("test layer 1", "foo"); } void test_trace_check_always_finds_empty_lines() { CHECK_TRACE_CONTENTS("test layer 1", ""); } void test_trace_check_treats_empty_layers_as_wildcards() { trace("test layer 1") << "foo"; CHECK_TRACE_CONTENTS("", "foo"); } void test_trace_check_multiple_lines_at_once() { trace("test layer 1")
# support for non-int values is untested

type value-stack {
  data: (handle array value)
  top: int
}

fn initialize-value-stack _self: (addr value-stack), n: int {
  var self/esi: (addr value-stack) <- copy _self
  var d/edi: (addr handle array value) <- get self, data
  populate d, n
  var top/eax: (addr int) <- get self, top
  copy-to *top, 0
}

fn clear-value-stack _self: (addr value-stack) {
  var self/esi: (addr value-stack) <- copy _self
  var top/eax: (addr int) <- get self, top
  copy-to *top, 0
}

fn push-number-to-value-stack _self: (addr value-stack), _val: float {
  var self/esi: (addr value-stack) <- copy _self
  var top-addr/ecx: (addr int) <- get self, top
  var data-ah/edx: (addr handle array value) <- get self, data
  var data/eax: (addr array value) <- lookup *data-ah
  var top/edx: int <- copy *top-addr
  var dest-offset/edx: (offset value) <- compute-offset data, top
  var dest-addr/edx: (addr value) <- index data, dest-offset
  var dest-addr2/eax: (addr float) <- get dest-addr, number-data
  var val/xmm0: float <- copy _val
#?   print-float-decimal-approximate 0, val, 3
  copy-to *dest-addr2, val
  increment *top-addr
  var type-addr/eax: (addr int) <- get dest-addr, type
  copy-to *type-addr, 0  # number
}

fn push-string-to-value-stack _self: (addr value-stack), val: (handle array byte) {
  var self/esi: (addr value-stack) <- copy _self
  var top-addr/ecx: (addr int) <- get self, top
  var data-ah/edx: (addr handle array value) <- get self, data
  var data/eax: (addr array