// -*- 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 <click/config.h>
#include "lexert.hh"
#include "lexertinfo.hh"
#include "routert.hh"
#include <click/confparse.hh>
#include <ctype.h>
#include <stdlib.h>
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<Lexeme> 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<String> args;
String word;
cp_argvec(requirement.string(), args);
for (int i = 0; i < args.size(); i++) {
Vector<String> 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 <click/vector.cc>
#include <click/hashmap.cc>
syntax highlighted by Code2HTML, v. 0.9.1