https://github.com/akkartik/mu/blob/master/017parse_tree.cc

      1 // So far instructions can only contain linear lists of properties. Now we add
      2 // support for more complex trees of properties in dilated reagents. This will
      3 // come in handy later for expressing complex types, like "a dictionary from
      4 // (address to array of charaters) to (list of numbers)".
      5 //
      6 // Type trees aren't as general as s-expressions even if they look like them:
      7 // the first element of a type tree is always an atom, and it can never be
      8 // dotted (right->right->right->...->right is always NULL).
      9 //
     10 // For now you can't use the simpler 'colon-based' representation inside type
     11 // trees. Once you start typing parens, keep on typing parens.
     12
     13 :(scenarios load)
     14 :(scenario dilated_reagent_with_nested_brackets)
     15 def main [
     16   {1: number, foo: (bar (baz quux))} <- copy 34
     17 ]
     18 +parse:   product: {1: "number", "foo": ("bar" ("baz" "quux"))}
     19
     20 :(before "End Parsing Dilated Reagent Property(value)")
     21 value = parse_string_tree(value);
     22 :(before "End Parsing Dilated Reagent Type Property(type_names)")
     23 type_names = parse_string_tree(type_names);
     24
     25 :(code)
     26 string_tree* parse_string_tree(string_tree* s) {
     27   assert(s->atom);
     28   if (!starts_with(s->value, "(")) return s;
     29   string_tree* result = parse_string_tree(s->value);
     30   delete s;
     31   return result;
     32 }
     33
     34 string_tree* parse_string_tree(const string& s) {
     35   istringstream in(s);
     36   in >> std::noskipws;
     37   return parse_string_tree(in);
     38 }
     39
     40 string_tree* parse_string_tree(istream& in) {
     41   skip_whitespace_but_not_newline(in);
     42   if (!has_data(in)) return NULL;
     43   if (in.peek() == ')') {
     44     in.get();
     45     return NULL;
     46   }
     47   if (in.peek() != '(') {
     48     string s = next_word(in);
     49     if (s.empty()) {
     50       assert(!has_data(in));
     51       raise << "incomplete string tree at end of file (0)\n" << end();
     52       return NULL;
     53     }
     54     string_tree* result = new string_tree(s);
     55     return result;
     56   }
     57   in.get();  // skip '('
     58   string_tree* result = NULL;
     59   string_tree** curr = &result;
     60   while (true) {
     61     skip_whitespace_but_not_newline(in);
     62     assert(has_data(in));
     63     if (in.peek() == ')') break;
     64     *curr = new string_tree(NULL, NULL);
     65     if (in.peek() == '(') {
     66       (*curr)->left = parse_string_tree(in);
     67     }
     68     else {
     69       string s = next_word(in);
     70       if (s.empty()) {
     71         assert(!has_data(in));
     72         raise << "incomplete string tree at end of file (1)\n" << end();
     73         return NULL;
     74       }
     75       (*curr)->left = new string_tree(s);
     76     }
     77     curr = &(*curr)->right;
     78   }
     79   in.get();  // skip ')'
     80   assert(*curr == NULL);
     81   return result;
     82 }
     83
     84 :(scenario dilated_reagent_with_type_tree)
     85 % Hide_errors = true;  // 'map' isn't defined yet
     86 def main [
     87   {1: (foo (address array character) (bar number))} <- copy 34
     88 ]
     89 # just to avoid errors
     90 container foo [
     91 ]
     92 container bar [
     93 ]
     94 +parse:   product: {1: ("foo" ("address" "array" "character") ("bar" "number"))}
     95
     96 :(scenario dilated_empty_tree)
     97 def main [
     98   {1: number, foo: ()} <- copy 34
     99 ]
    100 +parse:   product: {1: "number", "foo": ()}
    101
    102 :(scenario dilated_singleton_tree)
    103 def main [
    104   {1: number, foo: (bar)} <- copy 34
    105 ]
    106 +parse:   product: {1: "number", "foo": ("bar")}