mu/017parse_tree.cc
Kartik K. Agaram 93d4cc937e 3663 - fix a refcounting bug: '(type)' != 'type'
This was a large commit, and most of it is a follow-up to commit 3309,
undoing what is probably the final ill-considered optimization I added
to s-expressions in Mu: I was always representing (a b c) as (a b . c),
etc. That is now gone.

Why did I need to take it out? The key problem was the error silently
ignored in layer 30. That was causing size_of("(type)") to silently
return garbage rather than loudly complain (assuming 'type' was a simple
type).

But to take it out I had to modify types_strictly_match (layer 21) to
actually strictly match and not just do a prefix match.

In the process of removing the prefix match, I had to make extracting
recipe types from recipe headers more robust. So far it only matched the
first element of each ingredient's type; these matched:

  (recipe address:number -> address:number)
  (recipe address -> address)

I didn't notice because the dotted notation optimization was actually
representing this as:

  (recipe address:number -> address number)

---

One final little thing in this commit: I added an alias for 'assert'
called 'assert_for_now', to indicate that I'm not sure something's
really an invariant, that it might be triggered by (invalid) user
programs, and so require more thought on error handling down the road.

But this may well be an ill-posed distinction. It may be overwhelmingly
uneconomic to continually distinguish between model invariants and error
states for input. I'm starting to grow sympathetic to Google Analytics's
recent approach of just banning assertions altogether. We'll see..
2016-11-10 21:39:02 -08:00

107 lines
3.0 KiB
C++

// So far instructions can only contain linear lists of properties. Now we add
// support for more complex trees of properties in dilated reagents. This will
// come in handy later for expressing complex types, like "a dictionary from
// (address to array of charaters) to (list of numbers)".
//
// Type trees aren't as general as s-expressions even if they look like them:
// the first element of a type tree is always an atom, and it can never be
// dotted (right->right->right->...->right is always NULL).
//
// For now you can't use the simpler 'colon-based' representation inside type
// trees. Once you start typing parens, keep on typing parens.
:(scenarios load)
:(scenario dilated_reagent_with_nested_brackets)
def main [
{1: number, foo: (bar (baz quux))} <- copy 34
]
+parse: product: {1: "number", "foo": ("bar" ("baz" "quux"))}
:(before "End Parsing Dilated Reagent Property(value)")
value = parse_string_tree(value);
:(before "End Parsing Dilated Reagent Type Property(type_names)")
type_names = parse_string_tree(type_names);
:(code)
string_tree* parse_string_tree(string_tree* s) {
assert(s->atom);
if (!starts_with(s->value, "(")) return s;
string_tree* result = parse_string_tree(s->value);
delete s;
return result;
}
string_tree* parse_string_tree(const string& s) {
istringstream in(s);
in >> std::noskipws;
return parse_string_tree(in);
}
string_tree* parse_string_tree(istream& in) {
skip_whitespace_but_not_newline(in);
if (!has_data(in)) return NULL;
if (in.peek() == ')') {
in.get();
return NULL;
}
if (in.peek() != '(') {
string s = next_word(in);
if (s.empty()) {
assert(!has_data(in));
raise << "incomplete string tree at end of file (0)\n" << end();
return NULL;
}
string_tree* result = new string_tree(s);
return result;
}
in.get(); // skip '('
string_tree* result = NULL;
string_tree** curr = &result;
while (true) {
skip_whitespace_but_not_newline(in);
assert(has_data(in));
if (in.peek() == ')') break;
*curr = new string_tree(NULL, NULL);
if (in.peek() == '(') {
(*curr)->left = parse_string_tree(in);
}
else {
string s = next_word(in);
if (s.empty()) {
assert(!has_data(in));
raise << "incomplete string tree at end of file (1)\n" << end();
return NULL;
}
(*curr)->left = new string_tree(s);
}
curr = &(*curr)->right;
}
in.get(); // skip ')'
assert(*curr == NULL);
return result;
}
:(scenario dilated_reagent_with_type_tree)
% Hide_errors = true; // 'map' isn't defined yet
def main [
{1: (foo (address array character) (bar number))} <- copy 34
]
# just to avoid errors
container foo [
]
container bar [
]
+parse: product: {1: ("foo" ("address" "array" "character") ("bar" "number"))}
:(scenario dilated_empty_tree)
def main [
{1: number, foo: ()} <- copy 34
]
+parse: product: {1: "number", "foo": ()}
:(scenario dilated_singleton_tree)
def main [
{1: number, foo: (bar)} <- copy 34
]
+parse: product: {1: "number", "foo": ("bar")}