:(before "End Globals")
map<recipe_ordinal, recipe> Recipe;
map<string, recipe_ordinal> Recipe_ordinal;
recipe_ordinal Next_recipe_ordinal = 1;
:(after "Types")
typedef int recipe_ordinal;
:(before "End Types")
struct recipe {
string name;
vector<instruction> steps;
recipe();
};
:(before "struct recipe")
struct instruction {
bool is_label;
string label;
string name;
string old_name;
string original_string;
recipe_ordinal operation;
vector<reagent> ingredients;
vector<reagent> products;
instruction();
void clear();
bool is_empty();
};
:(before "struct instruction")
struct reagent {
string original_string;
string name;
type_tree* type;
vector<pair<string, string_tree*> > properties;
double value;
bool initialized;
reagent(const string& s);
reagent() :type(NULL), value(0), initialized(false) {}
~reagent();
void clear();
reagent(const reagent& old);
reagent& operator=(const reagent& old);
void set_value(double v) { value = v; initialized = true; }
};
:(before "struct reagent")
struct type_tree {
bool atom;
string name;
type_ordinal value;
type_tree* left;
type_tree* right;
~type_tree();
type_tree(const type_tree& old);
explicit type_tree(string name);
type_tree(string name, type_ordinal v) :atom(true), name(name), value(v), left(NULL), right(NULL) {}
type_tree(type_tree* l, type_tree* r) :atom(false), value(0), left(l), right(r) {}
type_tree& operator=(const type_tree& old);
bool operator==(const type_tree& other) const;
bool operator!=(const type_tree& other) const { return !operator==(other); }
bool operator<(const type_tree& other) const;
bool operator>(const type_tree& other) const { return other.operator<(*this); }
};
struct string_tree {
bool atom;
string value;
string_tree* left;
string_tree* right;
~string_tree();
string_tree(const string_tree& old);
explicit string_tree(string v) :atom(true), value(v), left(NULL), right(NULL) {}
string_tree(string_tree* l, string_tree* r) :atom(false), left(l), right(r) {}
};
:(code)
type_tree::type_tree(string name) :atom(true), name(name), value(get(Type_ordinal, name)), left(NULL), right(NULL) {}
:(before "End Globals")
map<int, double> Memory;
:(before "End Setup")
Memory.clear();
:(after "Types")
typedef int type_ordinal;
:(before "End Globals")
map<string, type_ordinal> Type_ordinal;
map<type_ordinal, type_info> Type;
type_ordinal Next_type_ordinal = 1;
:(code)
void setup_types() {
Type.clear(); Type_ordinal.clear();
put(Type_ordinal, "literal", 0);
Next_type_ordinal = 1;
type_ordinal number = put(Type_ordinal, "number", Next_type_ordinal++);
get_or_insert(Type, number).name = "number";
put(Type_ordinal, "location", number);
type_ordinal address = put(Type_ordinal, "address", Next_type_ordinal++);
get_or_insert(Type, address).name = "address";
type_ordinal boolean = put(Type_ordinal, "boolean", Next_type_ordinal++);
get_or_insert(Type, boolean).name = "boolean";
type_ordinal character = put(Type_ordinal, "character", Next_type_ordinal++);
get_or_insert(Type, character).name = "character";
type_ordinal array = put(Type_ordinal, "array", Next_type_ordinal++);
get_or_insert(Type, array).name = "array";
}
void teardown_types() {
for (map<type_ordinal, type_info>::iterator p = Type.begin(); p != Type.end(); ++p) {
for (int i = 0; i < SIZE(p->second.elements); ++i)
p->second.elements.clear();
}
Type_ordinal.clear();
}
:(before "End One-time Setup")
setup_types();
atexit(teardown_types);
:(before "End Types")
enum kind_of_type {
PRIMITIVE,
CONTAINER,
EXCLUSIVE_CONTAINER
};
struct type_info {
string name;
kind_of_type kind;
vector<reagent> elements;
type_info() :kind(PRIMITIVE) {
}
};
enum primitive_recipes {
IDLE = 0,
COPY,
MAX_PRIMITIVE_RECIPES,
};
:(code)
void setup_recipes() {
Recipe.clear(); Recipe_ordinal.clear();
put(Recipe_ordinal, "idle", IDLE);
put(Recipe_ordinal, "copy", COPY);
}
:(before "End One-time Setup")
setup_recipes();
assert(MAX_PRIMITIVE_RECIPES < 200);
Next_recipe_ordinal = 200;
put(Recipe_ordinal, "main", Next_recipe_ordinal++);
:(before "End Commandline Parsing")
assert(Next_recipe_ordinal < 1000);
:(before "End Setup")
Next_recipe_ordinal = 1000;
:(before "End Globals")
map<string, recipe_ordinal> Recipe_ordinal_snapshot;
map<recipe_ordinal, recipe> Recipe_snapshot;
map<string, type_ordinal> Type_ordinal_snapshot;
map<type_ordinal, type_info> Type_snapshot;
:(before "End One-time Setup")
save_snapshots();
:(before "End Setup")
restore_snapshots();
:(code)
void save_snapshots() {
Recipe_ordinal_snapshot = Recipe_ordinal;
Recipe_snapshot = Recipe;
Type_ordinal_snapshot = Type_ordinal;
Type_snapshot = Type;
}
void restore_snapshots() {
Recipe = Recipe_snapshot;
Recipe_ordinal = Recipe_ordinal_snapshot;
restore_non_recipe_snapshots();
}
void restore_non_recipe_snapshots() {
Type_ordinal = Type_ordinal_snapshot;
Type = Type_snapshot;
}
:(code)
recipe::recipe() {
}
instruction::instruction() :is_label(false), operation(IDLE) {
}
void instruction::clear() { is_label=false; label.clear(); name.clear(); old_name.clear(); operation=IDLE; ingredients.clear(); products.clear(); original_string.clear(); }
bool instruction::is_empty() { return !is_label && name.empty(); }
reagent::reagent(const string& s) :original_string(s), type(NULL), value(0), initialized(false) {
istringstream in(s);
in >> std::noskipws;
istringstream first_row(slurp_until(in, '/'));
first_row >> std::noskipws;
name = slurp_until(first_row, ':');
string_tree* type_names = parse_property_list(first_row);
type = new_type_tree(type_names);
delete type_names;
if (is_integer(name) && type == NULL)
type = new type_tree("literal");
if (name == "_" && type == NULL)
type = new type_tree("literal");
slurp_properties(in, properties);
}
void slurp_properties(istream& in, vector<pair<string, string_tree*> >& out) {
while (has_data(in)) {
istringstream row(slurp_until(in, '/'));
row >> std::noskipws;
string key = slurp_until(row, ':');
string_tree* value = parse_property_list(row);
out.push_back(pair<string, string_tree*>(key, value));
}
}
string_tree* parse_property_list(istream& in) {
skip_whitespace_but_not_newline(in);
if (!has_data(in)) return NULL;
string_tree* left = new string_tree(slurp_until(in, ':'));
if (!has_data(in)) return left;
string_tree* right = parse_property_list(in);
return new string_tree(left, right);
}
type_tree* new_type_tree(const string_tree* properties) {
if (!properties) return NULL;
if (properties->atom) {
const string& type_name = properties->value;
int value = 0;
if (contains_key(Type_ordinal, type_name))
value = get(Type_ordinal, type_name);
else if (is_integer(type_name))
value = 0;
else if (properties->value == "->")
value = 0;
else
value = -1;
return new type_tree(type_name, value);
}
return new type_tree(new_type_tree(properties->left),
new_type_tree(properties->right));
}
reagent::reagent(const reagent& other) {
original_string = other.original_string;
name = other.name;
value = other.value;
initialized = other.initialized;
for (int i = 0; i < SIZE(other.properties); ++i) {
properties.push_back(pair<string, string_tree*>(other.properties.at(i).first,
other.properties.at(i).second ? new string_tree(*other.properties.at(i).second) : NULL));
}
type = other.type ? new type_tree(*other.type) : NULL;
}
type_tree::type_tree(const type_tree& old) {
atom = old.atom;
name = old.name;
value = old.value;
left = old.left ? new type_tree(*old.left) : NULL;
right = old.right ? new type_tree(*old.right) : NULL;
}
type_tree& type_tree::operator=(const type_tree& old) {
atom = old.atom;
name = old.name;
value = old.value;
left = old.left ? new type_tree(*old.left) : NULL;
right = old.right ? new type_tree(*old.right) : NULL;
return *this;
}
bool type_tree::operator==(const type_tree& other) const {
if (atom != other.atom) return false;
if (atom)
return name == other.name && value == other.value;
return (left == other.left || *left == *other.left)
&& (right == other.right || *right == *other.right);
}
bool type_tree::operator<(const type_tree& other) const {
if (atom != other.atom) return atom > other.atom;
if (atom) return name < other.name;
if (left && !other.left) return false;
if (!left && other.left) return true;
if (right && !other.right) return false;
if (!right && other.right) return true;
if (left == other.left || *left == *other.left) return *right < *other.right;
if (right == other.right || *right == *other.right) return *left < *other.left;
if ((left == other.right || *left == *other.right)
&& (right == other.left || *right == *other.left))
return *left < *other.left;
if (*left < *other.left && *left < *other.right) return true;
if (*right < *other.left && *right < *other.right) return true;
return false;
}
:(before "End Unit Tests")
void test_compare_atom_types() {
reagent a("a:address"), b("b:boolean");
CHECK(*a.type < *b.type);
CHECK(!(*b.type < *a.type));
}
void test_compare_equal_atom_types() {
reagent a("a:address"), b("b:address");
CHECK(!(*a.type < *b.type));
CHECK(!(*b.type < *a.type));
}
void test_compare_atom_with_non_atom() {
reagent a("a:address:number"), b("b:boolean");
CHECK(!(*a.type < *b.type));
CHECK(*b.type < *a.type);
}
void test_compare_lists_with_identical_structure() {
reagent a("a:address:address"), b("b:address:boolean");
CHECK(*a.type < *b.type);
CHECK(!(*b.type < *a.type));
}
void test_compare_identical_lists() {
reagent a("a:address:boolean"), b("b:address:boolean");
CHECK(!(*a.type < *b.type));
CHECK(!(*b.type < *a.type));
}
void test_compare_list_with_extra_element() {
reagent a("a:address:address"), b("b:address:address:number");
CHECK(*a.type < *b.type);
CHECK(!(*b.type < *a.type));
}
void test_compare_list_with_smaller_left_but_larger_right() {
reagent a("a:address:number"), b("b:character:array");
assert(a.type->left->atom && a.type->right->atom);
assert(b.type->left->atom && b.type->right->atom);
CHECK(*a.type < *b.type);
CHECK(!(*b.type < *a.type));
}
void test_compare_list_with_smaller_left_but_larger_right_identical_types() {
reagent a("a:address:boolean"), b("b:boolean:address");
assert(a.type->left->atom && a.type->right->atom);
assert(b.type->left->atom && b.type->right->atom);
CHECK(*a.type < *b.type);
CHECK(!(*b.type < *a.type));
}
:(code)
string_tree::string_tree(const string_tree& old) {
atom = old.atom;
value = old.value;
left = old.left ? new string_tree(*old.left) : NULL;
right = old.right ? new string_tree(*old.right) : NULL;
}
reagent& reagent::operator=(const reagent& other) {
original_string = other.original_string;
for (int i = 0; i < SIZE(properties); ++i)
if (properties.at(i).second) delete properties.at(i).second;
properties.clear();
for (int i = 0; i < SIZE(other.properties); ++i)
properties.push_back(pair<string, string_tree*>(other.properties.at(i).first, other.properties.at(i).second ? new string_tree(*other.properties.at(i).second) : NULL));
name = other.name;
value = other.value;
initialized = other.initialized;
if (type) delete type;
type = other.type ? new type_tree(*other.type) : NULL;
return *this;
}
reagent::~reagent() {
clear();
}
void reagent::clear() {
for (int i = 0; i < SIZE(properties); ++i) {
if (properties.at(i).second) {
delete properties.at(i).second;
properties.at(i).second = NULL;
}
}
delete type;
type = NULL;
}
type_tree::~type_tree() {
delete left;
delete right;
}
string_tree::~string_tree() {
delete left;
delete right;
}
void append(type_tree*& base, type_tree* extra) {
if (!base) {
base = extra;
return;
}
type_tree* curr = base;
while (curr->right) curr = curr->right;
curr->right = extra;
}
void append(string_tree*& base, string_tree* extra) {
if (!base) {
base = extra;
return;
}
string_tree* curr = base;
while (curr->right) curr = curr->right;
curr->right = extra;
}
string slurp_until(istream& in, char delim) {
ostringstream out;
char c;
while (in >> c) {
if (c == delim) {
break;
}
out << c;
}
return out.str();
}
bool has_property(const reagent& x, const string& name) {
for (int i = 0; i < SIZE(x.properties); ++i) {
if (x.properties.at(i).first == name) return true;
}
return false;
}
string_tree* property(const reagent& r, const string& name) {
for (int p = 0; p != SIZE(r.properties); ++p) {
if (r.properties.at(p).first == name)
return r.properties.at(p).second;
}
return NULL;
}
:(before "End Globals")
extern const string Ignore(",");
:(code)
void skip_whitespace_but_not_newline(istream& in) {
while (true) {
if (!has_data(in)) break;
else if (in.peek() == '\n') break;
else if (isspace(in.peek())) in.get();
else if (Ignore.find(in.peek()) != string::npos) in.get();
else break;
}
}
void dump_memory() {
for (map<int, double>::iterator p = Memory.begin(); p != Memory.end(); ++p) {
cout << p->first << ": " << no_scientific(p->second) << '\n';
}
}
string to_string(const recipe& r) {
ostringstream out;
out << "recipe " << r.name << " [\n";
for (int i = 0; i < SIZE(r.steps); ++i)
out << " " << to_string(r.steps.at(i)) << '\n';
out << "]\n";
return out.str();
}
string debug_string(const recipe& x) {
ostringstream out;
out << "- recipe " << x.name << '\n';
for (int index = 0; index < SIZE(x.steps); ++index) {
const instruction& inst = x.steps.at(index);
out << "inst: " << to_string(inst) << '\n';
out << " ingredients\n";
for (int i = 0; i < SIZE(inst.ingredients); ++i)
out << " " << debug_string(inst.ingredients.at(i)) << '\n';
out << " products\n";
for (int i = 0; i < SIZE(inst.products); ++i)
out << " " << debug_string(inst.products.at(i)) << '\n';
}
return out.str();
}
string to_original_string(const instruction& inst) {
if (inst.is_label) return inst.label;
ostringstream out;
for (int i = 0; i < SIZE(inst.products); ++i) {
if (i > 0) out << ", ";
out << inst.products.at(i).original_string;
}
if (!inst.products.empty()) out << " <- ";
out << inst.name << ' ';
for (int i = 0; i < SIZE(inst.ingredients); ++i) {
if (i > 0) out << ", ";
out << inst.ingredients.at(i).original_string;
}
return out.str();
}
string to_string(const instruction& inst) {
if (inst.is_label) return inst.label;
ostringstream out;
for (int i = 0; i < SIZE(inst.products); ++i) {
if (i > 0) out << ", ";
out << to_string(inst.products.at(i));
}
if (!inst.products.empty()) out << " <- ";
out << inst.name << ' ';
for (int i = 0; i < SIZE(inst.ingredients); ++i) {
if (i > 0) out << ", ";
out << to_string(inst.ingredients.at(i));
}
return out.str();
}
string to_string(const reagent& r) {
if (is_dummy(r)) return "_";
ostringstream out;
out << "{";
out << r.name << ": " << names_to_string(r.type);
if (!r.properties.empty()) {
for (int i = 0; i < SIZE(r.properties); ++i)
out << ", \"" << r.properties.at(i).first << "\": " << to_string(r.properties.at(i).second);
}
out << "}";
return out.str();
}
bool is_dummy(const reagent& x) {
return x.name == "_";
}
string debug_string(const reagent& x) {
ostringstream out;
out << x.name << ": " << x.value << ' ' << to_string(x.type) << " -- " << to_string(x);
return out.str();
}
string to_string(const string_tree* property) {
if (!property) return "()";
ostringstream out;
dump(property, out);
return out.str();
}
void dump(const string_tree* x, ostream& out) {
if (!x) return;
if (x->atom) {
out << '"' << x->value << '"';
return;
}
out << '(';
const string_tree* curr = x;
while (curr && !curr->atom) {
dump(curr->left, out);
if (curr->right) out << ' ';
curr = curr->right;
}
dump(curr, out);
out << ')';
}
string to_string(const type_tree* type) {
if (!type) return "NULLNULLNULL";
ostringstream out;
dump(type, out);
return out.str();
}
void dump(const type_tree* x, ostream& out) {
if (!x) return;
if (x->atom) {
dump(x->value, out);
return;
}
out << '(';
const type_tree* curr = x;
while (curr && !curr->atom) {
dump(curr->left, out);
if (curr->right) out << ' ';
curr = curr->right;
}
dump(curr, out);
out << ')';
}
void dump(type_ordinal type, ostream& out) {
if (contains_key(Type, type))
out << get(Type, type).name;
else
out << "?" << type;
}
string names_to_string(const type_tree* type) {
if (!type) return "()";
ostringstream out;
dump_names(type, out);
return out.str();
}
void dump_names(const type_tree* x, ostream& out) {
if (!x) return;
if (x->atom) {
out << '"' << x->name << '"';
return;
}
out << '(';
const type_tree* curr = x;
while (curr && !curr->atom) {
dump_names(curr->left, out);
if (curr->right) out << ' ';
curr = curr->right;
}
dump_names(curr, out);
out << ')';
}
string names_to_string_without_quotes(const type_tree* type) {
if (!type) return "NULLNULLNULL";
ostringstream out;
dump_names_without_quotes(type, out);
return out.str();
}
void dump_names_without_quotes(const type_tree* x, ostream& out) {
if (!x) return;
if (x->atom) {
out << x->name;
return;
}
out << '(';
const type_tree* curr = x;
while (curr && !curr->atom) {
dump_names_without_quotes(curr->left, out);
if (curr->right) out << ' ';
curr = curr->right;
}
dump_names_without_quotes(curr, out);
out << ')';
}
:(before "End Types")
struct no_scientific {
double x;
explicit no_scientific(double y) :x(y) {}
};
:(code)
ostream& operator<<(ostream& os, no_scientific x) {
if (!isfinite(x.x)) {
os << x.x;
return os;
}
ostringstream tmp;
tmp << std::fixed << x.x;
os << trim_floating_point(tmp.str());
return os;
}
string trim_floating_point(const string& in) {
if (in.empty()) return "";
if (in.find('.') == string::npos) return in;
int length = SIZE(in);
while (length > 1) {
if (in.at(length-1) != '0') break;
--length;
}
if (in.at(length-1) == '.') --length;
if (length == 0) return "0";
return in.substr(0, length);
}
void test_trim_floating_point() {
CHECK_EQ(trim_floating_point(""), "");
CHECK_EQ(trim_floating_point(".0"), "0");
CHECK_EQ(trim_floating_point("1.5000"), "1.5");
CHECK_EQ(trim_floating_point("1.000001"), "1.000001");
CHECK_EQ(trim_floating_point("23.000000"), "23");
CHECK_EQ(trim_floating_point("23.0"), "23");
CHECK_EQ(trim_floating_point("23."), "23");
CHECK_EQ(trim_floating_point("23"), "23");
CHECK_EQ(trim_floating_point("230"), "230");
CHECK_EQ(trim_floating_point("3.000000"), "3");
CHECK_EQ(trim_floating_point("3.0"), "3");
CHECK_EQ(trim_floating_point("3."), "3");
CHECK_EQ(trim_floating_point("3"), "3");
}
:(before "End Includes")
#include <utility>
using std::pair;
#include <math.h>