// -*- c-basic-offset: 4 -*- /* * lexert.{cc,hh} -- configuration file parser for tools * Eddie Kohler * * Copyright (c) 1999-2000 Massachusetts Institute of Technology * Copyright (c) 2000 Mazu Networks, Inc. * Copyright (c) 2001-2003 International Computer Science Institute * Copyright (c) 2004 Regents of the University of California * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the conditions * listed in the Click LICENSE file. These conditions include: you must * preserve this copyright notice, and you cannot mention the copyright * holders in advertising related to the Software without their permission. * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This * notice is a summary of the Click LICENSE file; the license in that file is * legally binding. */ #include #include "lexert.hh" #include "lexertinfo.hh" #include "routert.hh" #include #include #include static LexerTInfo *stub_lexinfo = 0; LexerT::LexerT(ErrorHandler *errh, bool ignore_line_directives) : _data(0), _end(0), _pos(0), _lineno(1), _ignore_line_directives(ignore_line_directives), _tpos(0), _tfull(0), _router(0), _base_type_map(0), _errh(errh) { if (!_errh) _errh = ErrorHandler::default_handler(); if (!stub_lexinfo) stub_lexinfo = new LexerTInfo; _lexinfo = stub_lexinfo; clear(); } LexerT::~LexerT() { clear(); } void LexerT::reset(const String &data, const String &filename) { clear(); _big_string = data; _data = _pos = _big_string.begin(); _end = _big_string.end(); if (!filename) _filename = "line "; else if (filename.back() != ':' && !isspace(filename.back())) _filename = filename + ":"; else _filename = filename; _original_filename = _filename; _lineno = 1; } void LexerT::clear() { if (_router) _router->unuse(); _router = new RouterT; _router->use(); // hold a reference to the router _big_string = ""; // _data was freed by _big_string _data = 0; _end = 0; _pos = 0; _filename = ""; _lineno = 0; _tpos = 0; _tfull = 0; _base_type_map.clear(); _anonymous_offset = 0; } void LexerT::set_lexinfo(LexerTInfo *li) { _lexinfo = (li ? li : stub_lexinfo); } // LEXING: LOWEST LEVEL String LexerT::remaining_text() const { return _big_string.substring(_pos, _end); } void LexerT::set_remaining_text(const String &s) { _big_string = s; _data = _pos = s.begin(); _end = s.end(); } const char * LexerT::skip_line(const char *s) { _lineno++; for (; s < _end; s++) if (*s == '\n') return s + 1; else if (*s == '\r') { if (s + 1 < _end && s[1] == '\n') return s + 2; else return s + 1; } _lineno--; return s; } const char * LexerT::skip_slash_star(const char *s) { for (; s < _end; s++) if (*s == '\n') _lineno++; else if (*s == '\r') { if (s + 1 < _end && s[1] == '\n') s++; _lineno++; } else if (*s == '*' && s + 1 < _end && s[1] == '/') return s + 2; return _end; } const char * LexerT::skip_backslash_angle(const char *s) { for (; s < _end; s++) if (*s == '\n') _lineno++; else if (*s == '\r') { if (s + 1 < _end && s[1] == '\n') s++; _lineno++; } else if (*s == '/' && s + 1 < _end) { if (s[1] == '/') s = skip_line(s + 2) - 1; else if (s[1] == '*') s = skip_slash_star(s + 2) - 1; } else if (*s == '>') return s + 1; return _end; } const char * LexerT::skip_quote(const char *s, char endc) { for (; s < _end; s++) if (*s == '\n') _lineno++; else if (*s == '\r') { if (s + 1 < _end && s[1] == '\n') s++; _lineno++; } else if (*s == '\\' && endc == '\"' && s + 1 < _end) { if (s[1] == '<') s = skip_backslash_angle(s + 2) - 1; else if (s[1] == '\"') s++; } else if (*s == endc) return s + 1; return _end; } const char * LexerT::process_line_directive(const char *s) { const char *first_pos = s; for (s++; s < _end && (*s == ' ' || *s == '\t'); s++) /* nada */; if (s + 4 < _end && *s == 'l' && s[1] == 'i' && s[2] == 'n' && s[3] == 'e' && (s[4] == ' ' || s[4] == '\t')) { for (s += 5; s < _end && (*s == ' ' || *s == '\t'); s++) /* nada */; } if (s >= _end || !isdigit(*s)) { // complain about bad directive lerror(first_pos, s, "unknown preprocessor directive"); return skip_line(s); } else if (_ignore_line_directives) return skip_line(s); // parse line number for (_lineno = 0; s < _end && isdigit(*s); s++) _lineno = _lineno * 10 + *s - '0'; _lineno--; // account for extra line for (; s < _end && (*s == ' ' || *s == '\t'); s++) /* nada */; if (s < _end && *s == '\"') { // parse filename const char *first_in_filename = s; for (s++; s < _end && *s != '\"' && *s != '\n' && *s != '\r'; s++) if (*s == '\\' && s + 1 < _end && s[1] != '\n' && s[1] != '\r') s++; _filename = cp_unquote(_big_string.substring(first_in_filename, s) + "\":"); // an empty filename means return to the input file's name if (_filename == ":") _filename = _original_filename; } // reach end of line for (; s < _end && *s != '\n' && *s != '\r'; s++) /* nada */; if (s + 1 < _end && *s == '\r' && s[1] == '\n') s++; return s; } Lexeme LexerT::next_lexeme() { const char *s = _pos; while (true) { while (s < _end && isspace(*s)) { if (*s == '\n') _lineno++; else if (*s == '\r') { if (s + 1 < _end && s[1] == '\n') s++; _lineno++; } s++; } const char *opos = s; if (s >= _end) { _pos = _end; return Lexeme(); } else if (*s == '/' && s + 1 < _end) { if (s[1] == '/') s = skip_line(s + 2); else if (s[1] == '*') s = skip_slash_star(s + 2); else break; _lexinfo->notify_comment(opos, s); } else if (*s == '#' && (s == _data || s[-1] == '\n' || s[-1] == '\r')) { s = process_line_directive(s); _lexinfo->notify_line_directive(opos, s); } else break; } const char *word_pos = s; // find length of current word if (isalnum(*s) || *s == '_' || *s == '@') { more_word_characters: s++; while (s < _end && (isalnum(*s) || *s == '_' || *s == '@')) s++; if (s + 1 < _end && *s == '/' && (isalnum(s[1]) || s[1] == '_' || s[1] == '@')) goto more_word_characters; _pos = s; String word = _big_string.substring(word_pos, s); if (word.length() == 16 && word == "connectiontunnel") { _lexinfo->notify_keyword(word, word_pos, s); return Lexeme(lexTunnel, word, word_pos); } else if (word.length() == 12 && word == "elementclass") { _lexinfo->notify_keyword(word, word_pos, s); return Lexeme(lexElementclass, word, word_pos); } else if (word.length() == 7 && word == "require") { _lexinfo->notify_keyword(word, word_pos, s); return Lexeme(lexRequire, word, word_pos); } else return Lexeme(lexIdent, word, word_pos); } // check for variable if (*s == '$') { s++; while (s < _end && (isalnum(*s) || *s == '_')) s++; if (s > word_pos + 1) { _pos = s; return Lexeme(lexVariable, _big_string.substring(word_pos + 1, s), word_pos); } else s--; } if (s + 1 < _end) { if (*s == '-' && s[1] == '>') { _pos = s + 2; return Lexeme(lexArrow, _big_string.substring(s, s + 2), word_pos); } else if (*s == ':' && s[1] == ':') { _pos = s + 2; return Lexeme(lex2Colon, _big_string.substring(s, s + 2), word_pos); } else if (*s == '|' && s[1] == '|') { _pos = s + 2; return Lexeme(lex2Bar, _big_string.substring(s, s + 2), word_pos); } } if (s + 2 < _end && *s == '.' && s[1] == '.' && s[2] == '.') { _pos = s + 3; return Lexeme(lex3Dot, _big_string.substring(s, s + 3), word_pos); } _pos = s + 1; return Lexeme(*s, _big_string.substring(s, s + 1), word_pos); } Lexeme LexerT::lex_config() { const char *config_pos = _pos; const char *s = _pos; unsigned paren_depth = 1; for (; s < _end; s++) if (*s == '(') paren_depth++; else if (*s == ')') { paren_depth--; if (!paren_depth) break; } else if (*s == '\n') _lineno++; else if (*s == '\r') { if (s + 1 < _end && s[1] == '\n') s++; _lineno++; } else if (*s == '/' && s + 1 < _end) { if (s[1] == '/') s = skip_line(s + 2) - 1; else if (s[1] == '*') s = skip_slash_star(s + 2) - 1; } else if (*s == '\'' || *s == '\"') s = skip_quote(s + 1, *s) - 1; else if (*s == '\\' && s + 1 < _end && s[1] == '<') s = skip_backslash_angle(s + 2) - 1; _pos = s; _lexinfo->notify_config_string(config_pos, s); return Lexeme(lexConfig, _big_string.substring(config_pos, s), config_pos); } String LexerT::lexeme_string(int kind) { char buf[12]; if (kind == lexIdent) return "identifier"; else if (kind == lexVariable) return "variable"; else if (kind == lexArrow) return "'->'"; else if (kind == lex2Colon) return "'::'"; else if (kind == lex2Bar) return "'||'"; else if (kind == lex3Dot) return "'...'"; else if (kind == lexTunnel) return "'connectiontunnel'"; else if (kind == lexElementclass) return "'elementclass'"; else if (kind == lexRequire) return "'require'"; else if (kind >= 32 && kind < 127) { sprintf(buf, "'%c'", kind); return buf; } else { sprintf(buf, "'\\%03d'", kind); return buf; } } // LEXING: MIDDLE LEVEL (WITH PUSHBACK) const Lexeme & LexerT::lex() { int p = _tpos; if (_tpos == _tfull) { _tcircle[p] = next_lexeme(); _tfull = (_tfull + 1) % TCIRCLE_SIZE; } _tpos = (_tpos + 1) % TCIRCLE_SIZE; return _tcircle[p]; } void LexerT::unlex(const Lexeme &t) { _tpos = (_tpos + TCIRCLE_SIZE - 1) % TCIRCLE_SIZE; _tcircle[_tpos] = t; assert(_tfull != _tpos); } bool LexerT::expect(int kind, bool report_error) { // Never adds anything to '_tcircle'. This requires a nonobvious // implementation. if (_tpos != _tfull) { if (_tcircle[_tpos].is(kind)) { _tpos = (_tpos + 1) % TCIRCLE_SIZE; return true; } if (report_error) lerror(_tcircle[_tpos], "expected %s", lexeme_string(kind).c_str()); } else { const char *old_pos = _pos; if (next_lexeme().is(kind)) return true; if (report_error) lerror(old_pos, _pos, "expected %s", lexeme_string(kind).c_str()); _pos = old_pos; } return false; } const char * LexerT::next_pos() const { if (_tpos != _tfull) return _tcircle[_tpos].pos1(); else return _pos; } // ERRORS String LexerT::landmark() const { return _filename + String(_lineno); } void LexerT::vlerror(const char *pos1, const char *pos2, const String &lm, const char *format, va_list val) { String text = _errh->make_text(ErrorHandler::ERR_ERROR, format, val); _lexinfo->notify_error(text, pos1, pos2); text = _errh->decorate_text(ErrorHandler::ERR_ERROR, lm, text); _errh->handle_text(ErrorHandler::ERR_ERROR, text); } int LexerT::lerror(const char *pos1, const char *pos2, const char *format, ...) { va_list val; va_start(val, format); vlerror(pos1, pos2, landmark(), format, val); va_end(val); return -1; } int LexerT::lerror(const Lexeme &t, const char *format, ...) { va_list val; va_start(val, format); vlerror(t.pos1(), t.pos2(), landmark(), format, val); va_end(val); return -1; } // ELEMENT TYPES ElementClassT * LexerT::element_type(const Lexeme &t) const { assert(t.is(lexIdent)); String name = t.string(); ElementClassT *type = _router->declared_type(name); if (!type) type = _base_type_map[name]; if (type) _lexinfo->notify_class_reference(type, t.pos1(), t.pos2()); return type; } ElementClassT * LexerT::force_element_type(const Lexeme &t) { assert(t.is(lexIdent)); String name = t.string(); ElementClassT *type = _router->declared_type(name); if (!type) type = _base_type_map[name]; if (!type) { if (_router->eindex(name) >= 0) lerror(t, "'%s' was previously used as an element name", name.c_str()); type = ElementClassT::base_type(name); _base_type_map.insert(name, type); } _lexinfo->notify_class_reference(type, t.pos1(), t.pos2()); return type; } // ELEMENTS String LexerT::anon_element_name(const String &class_name) const { int anonymizer = _router->nelements() - _anonymous_offset + 1; return ";" + class_name + "@" + String(anonymizer); } int LexerT::make_element(String name, const Lexeme &location, const char *decl_pos2, ElementClassT *type, const String &conf, const String &lm) { // check 'name' for validity for (int i = 0; i < name.length(); i++) { bool ok = false; for (; i < name.length() && name[i] != '/'; i++) if (!isdigit(name[i])) ok = true; if (!ok) { lerror(location, "element name '%s' has all-digit component", name.c_str()); break; } } ElementT *e = _router->get_element(name, type, conf, lm ? lm : landmark()); _lexinfo->notify_element_declaration(e, location.pos1(), location.pos2(), (decl_pos2 < 0 ? location.pos2() : decl_pos2)); return e->eindex(); } int LexerT::make_anon_element(const Lexeme &what, const char *decl_pos2, ElementClassT *type, const String &conf, const String &lm) { return make_element(anon_element_name(type->name()), what, decl_pos2, type, conf, lm); } void LexerT::connect(int element1, int port1, int port2, int element2) { if (port1 < 0) port1 = 0; if (port2 < 0) port2 = 0; _router->add_connection (PortT(_router->element(element1), port1), PortT(_router->element(element2), port2), landmark()); } // PARSING bool LexerT::yport(int &port, const char *&pos1, const char *&pos2) { const Lexeme &tlbrack = lex(); if (!tlbrack.is('[')) { unlex(tlbrack); return false; } pos1 = tlbrack.pos1(); const Lexeme &tword = lex(); if (tword.is(lexIdent)) { String p = tword.string(); const char *ps = p.c_str(); if (isdigit(ps[0]) || ps[0] == '-') port = strtol(ps, (char **)&ps, 0); if (*ps != 0) { lerror(tword, "syntax error: port number should be integer"); port = 0; } expect(']'); pos2 = next_pos(); return true; } else if (tword.is(']')) { lerror(tword, "syntax error: expected port number"); port = 0; pos2 = tword.pos2(); return true; } else { lerror(tword, "syntax error: expected port number"); unlex(tword); return false; } } bool LexerT::yelement(int &element, bool comma_ok) { Lexeme tname = lex(); String name; ElementClassT *etype; const char *decl_pos2 = 0; if (tname.is(lexIdent)) { etype = element_type(tname); name = tname.string(); decl_pos2 = tname.pos2(); } else if (tname.is('{')) { etype = ycompound(String(), tname.pos1(), tname.pos1()); name = etype->name(); decl_pos2 = next_pos(); } else { unlex(tname); return false; } Lexeme configuration; String lm; const Lexeme &tparen = lex(); if (tparen.is('(')) { lm = landmark(); // report landmark from before config string if (!etype) etype = force_element_type(tname); configuration = lex_config(); expect(')'); decl_pos2 = next_pos(); } else unlex(tparen); if (etype) element = make_anon_element(tname, decl_pos2, etype, configuration.string(), lm); else { const Lexeme &t2colon = lex(); unlex(t2colon); if (t2colon.is(lex2Colon) || (t2colon.is(',') && comma_ok)) { ydeclaration(tname); element = _router->eindex(name); } else { element = _router->eindex(name); if (element < 0) { // assume it's an element type etype = force_element_type(tname); element = make_anon_element(tname, tname.pos2(), etype, configuration.string(), lm); } else _lexinfo->notify_element_reference(_router->element(element), tname.pos1(), tname.pos2()); } } return true; } void LexerT::ydeclaration(const Lexeme &first_element) { Vector decls; Lexeme t; if (first_element) { decls.push_back(first_element); goto midpoint; } while (true) { t = lex(); if (!t.is(lexIdent)) lerror(t, "syntax error: expected element name"); else decls.push_back(t); midpoint: const Lexeme &tsep = lex(); if (tsep.is(',')) /* do nothing */; else if (tsep.is(lex2Colon)) break; else { lerror(tsep, "syntax error: expected '::' or ','"); unlex(tsep); return; } } String lm = landmark(); ElementClassT *etype; Lexeme etypet = lex(); if (etypet.is(lexIdent)) etype = force_element_type(etypet); else if (etypet.is('{')) etype = ycompound(String(), etypet.pos1(), etypet.pos1()); else { lerror(etypet, "missing element type in declaration"); return; } Lexeme configuration; t = lex(); if (t.is('(')) { configuration = lex_config(); expect(')'); } else unlex(t); const char *decl_pos2 = (decls.size() == 1 ? next_pos() : 0); for (int i = 0; i < decls.size(); i++) { String name = decls[i].string(); if (ElementT *old_e = _router->element(name)) ElementT::redeclaration_error(_errh, "element", name, landmark(), old_e->landmark()); else if (_router->declared_type(name) || _base_type_map[name]) lerror(decls[i], "class '%s' used as element name", name.c_str()); else make_element(name, decls[i], decl_pos2, etype, configuration.string(), lm); } } bool LexerT::yconnection() { int element1 = -1, port1 = -1; const char *port1_pos1 = 0, *port1_pos2 = 0; Lexeme t; while (true) { int element2, port2 = -1; const char *port2_pos1, *port2_pos2; // get element yport(port2, port2_pos1, port2_pos2); if (!yelement(element2, element1 < 0)) { if (port1 >= 0) lerror(port1_pos1, port1_pos2, "output port useless at end of chain"); return element1 >= 0; } if (element1 >= 0) connect(element1, port1, port2, element2); else if (port2 >= 0) lerror(port2_pos1, port2_pos2, "input port useless at start of chain"); port1 = -1; relex: t = lex(); switch (t.kind()) { case ',': case lex2Colon: if (router()->element(element2)->anonymous()) // type used as name lerror(t, "class '%s' used as element name", router()->etype_name(element2).c_str()); else lerror(t, "syntax error before '%s'", t.string().c_str()); goto relex; case lexArrow: break; case '[': unlex(t); yport(port1, port1_pos1, port1_pos2); goto relex; case lexIdent: case '{': case '}': case lex2Bar: case lexTunnel: case lexElementclass: case lexRequire: unlex(t); // FALLTHRU case ';': case lexEOF: if (port1 >= 0) lerror(port1_pos1, port1_pos2, "output port useless at end of chain", port1); return true; default: lerror(t, "syntax error near '%#s'", t.string().c_str()); if (t.kind() >= lexIdent) // save meaningful tokens unlex(t); return true; } // have 'x ->' element1 = element2; } } void LexerT::yelementclass(const char *pos1) { Lexeme tname = lex(); String eclass_name; if (!tname.is(lexIdent)) { unlex(tname); lerror(tname, "expected element type name"); } else { String n = tname.string(); if (_router->eindex(n) >= 0) lerror(tname, "'%s' already used as an element name", n.c_str()); else eclass_name = n; } Lexeme tnext = lex(); if (tnext.is('{')) (void) ycompound(eclass_name, pos1, tname.pos1()); else if (tnext.is(lexIdent)) { ElementClassT *ec = force_element_type(tnext); if (eclass_name) { ElementClassT *new_ec = new SynonymElementClassT(eclass_name, ec, _router); _router->add_declared_type(new_ec, false); _lexinfo->notify_class_declaration(new_ec, false, pos1, tname.pos1(), tnext.pos2()); } } else lerror(tnext, "syntax error near '%#s'", tnext.string().c_str()); } void LexerT::ytunnel() { Lexeme tname1 = lex(); if (!tname1.is(lexIdent)) { unlex(tname1); lerror(tname1, "expected tunnel input name"); } expect(lexArrow); Lexeme tname2 = lex(); if (!tname2.is(lexIdent)) { unlex(tname2); lerror(tname2, "expected tunnel output name"); } if (tname1.is(lexIdent) && tname2.is(lexIdent)) _router->add_tunnel(tname1.string(), tname2.string(), landmark(), _errh); } void LexerT::ycompound_arguments(RouterT *comptype) { Lexeme t1, t2; while (1) { String vartype, varname; // read "IDENTIFIER $VARIABLE" or "$VARIABLE" t1 = lex(); if (t1.is(lexIdent)) { t2 = lex(); if (t2.is(lexVariable)) { vartype = t1.string(); varname = t2.string(); } else { if (comptype->nformals() > 0) lerror(t2, "expected variable"); unlex(t2); unlex(t1); break; } } else if (t1.is(lexVariable)) varname = t1.string(); else if (t1.is('|')) break; else { if (comptype->nformals() > 0) lerror(t1, "expected variable"); unlex(t1); break; } comptype->add_formal(varname, vartype); const Lexeme &tsep = lex(); if (tsep.is('|')) break; else if (!tsep.is(',')) { lerror(tsep, "expected ',' or '|'"); unlex(tsep); break; } } // check argument types bool positional = true, error = false; for (int i = 0; i < comptype->nformals(); i++) if (const String &ftype = comptype->formal_types()[i]) { positional = false; if (ftype == "__REST__") { if (i < comptype->nformals() - 1) error = true; } else for (int j = i + 1; j < comptype->nformals(); j++) if (comptype->formal_types()[j] == ftype) { lerror(t1, "repeated keyword parameter '%s' in compound element", ftype.c_str()); break; } } else if (!positional) error = true; if (error) lerror(t1, "compound element parameters out of order\n(The correct order is '[positional], [keywords], [__REST__]'.)"); } ElementClassT * LexerT::ycompound(String name, const char *decl_pos1, const char *name_pos1) { bool anonymous = (name.length() == 0); // '{' was already read RouterT *old_router = _router; int old_offset = _anonymous_offset; RouterT *first = 0, *last = 0; ElementClassT *extension = 0; const char *pos2 = name_pos1; while (1) { Lexeme dots = lex(); if (dots.is(lex3Dot)) { // '...' marks an extension type if (anonymous) { lerror(dots, "cannot extend anonymous compound element class"); extension = ElementClassT::base_type("Error"); } else { extension = force_element_type(Lexeme(lexIdent, name, name_pos1)); _lexinfo->notify_class_extension(extension, dots.pos1(), dots.pos2()); } dots = lex(); if (!first || !dots.is('}')) lerror(dots.pos1(), dots.pos2(), "'...' should occur last, after one or more compounds"); if (dots.is('}') && first) break; } unlex(dots); // create a compound RouterT *compound_class = new RouterT(name, landmark(), old_router); _router = compound_class->cast_router(); _anonymous_offset = 2; ycompound_arguments(compound_class); while (ystatement(true)) /* nada */; compound_class->finish_type(_errh); if (last) last->set_overload_type(compound_class); else first = compound_class; last = compound_class; // check for '||' or '}' const Lexeme &t = lex(); if (!t.is(lex2Bar)) { pos2 = t.pos2(); break; } } _anonymous_offset = old_offset; _router = old_router; if (extension) last->set_overload_type(extension); old_router->add_declared_type(first, anonymous); _lexinfo->notify_class_declaration(first, anonymous, decl_pos1, name_pos1, pos2); return first; } void LexerT::yrequire() { if (expect('(')) { Lexeme requirement = lex_config(); expect(')'); // pre-read ';' to make it easier to write parsing extensions expect(';', false); Vector args; String word; cp_argvec(requirement.string(), args); for (int i = 0; i < args.size(); i++) { Vector words; cp_spacevec(args[i], words); if (words.size() == 0) /* do nothing */; else if (!cp_word(words[0], &word)) lerror(requirement, "bad requirement: not a word"); else if (words.size() > 1) lerror(requirement, "bad requirement: too many words"); else _router->add_requirement(word); } } } bool LexerT::ystatement(bool nested) { const Lexeme &t = lex(); switch (t.kind()) { case lexIdent: case '[': case '{': unlex(t); yconnection(); return true; case lexElementclass: yelementclass(t.pos1()); return true; case lexTunnel: ytunnel(); return true; case lexRequire: yrequire(); return true; case ';': return true; case '}': case lex2Bar: if (!nested) goto syntax_error; unlex(t); return false; case lexEOF: if (nested) lerror(t, "expected '}'"); return false; default: syntax_error: lerror(t, "syntax error near '%#s'", t.string().c_str()); return true; } } // COMPLETION RouterT * LexerT::finish() { RouterT *r = _router; _router = 0; // resolve anonymous element names r->deanonymize_elements(); // returned router has one reference count return r; } #include #include