📄 lexert.cc
字号:
// -*- 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), _len(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();}voidLexerT::reset(const String &data, const String &filename){ clear(); _big_string = data; _data = _big_string.data(); _len = _big_string.length(); if (!filename) _filename = "line "; else if (filename.back() != ':' && !isspace(filename.back())) _filename = filename + ":"; else _filename = filename; _original_filename = _filename; _lineno = 1;}voidLexerT::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; _len = 0; _pos = 0; _filename = ""; _lineno = 0; _tpos = 0; _tfull = 0; _base_type_map.clear(); _anonymous_offset = 0;}voidLexerT::set_lexinfo(LexerTInfo *li){ _lexinfo = (li ? li : stub_lexinfo);}// LEXING: LOWEST LEVELStringLexerT::remaining_text() const{ return _big_string.substring(_pos);}voidLexerT::set_remaining_text(const String &s){ _big_string = s; _data = s.data(); _pos = 0; _len = s.length();}unsignedLexerT::skip_line(unsigned pos){ _lineno++; for (; pos < _len; pos++) if (_data[pos] == '\n') return pos + 1; else if (_data[pos] == '\r') { if (pos < _len - 1 && _data[pos+1] == '\n') return pos + 2; else return pos + 1; } _lineno--; return _len;}unsignedLexerT::skip_slash_star(unsigned pos){ for (; pos < _len; pos++) if (_data[pos] == '\n') _lineno++; else if (_data[pos] == '\r') { if (pos < _len - 1 && _data[pos+1] == '\n') pos++; _lineno++; } else if (_data[pos] == '*' && pos < _len - 1 && _data[pos+1] == '/') return pos + 2; return _len;}unsignedLexerT::skip_backslash_angle(unsigned pos){ for (; pos < _len; pos++) if (_data[pos] == '\n') _lineno++; else if (_data[pos] == '\r') { if (pos < _len - 1 && _data[pos+1] == '\n') pos++; _lineno++; } else if (_data[pos] == '/' && pos < _len - 1) { if (_data[pos+1] == '/') pos = skip_line(pos + 2) - 1; else if (_data[pos+1] == '*') pos = skip_slash_star(pos + 2) - 1; } else if (_data[pos] == '>') return pos + 1; return _len;}unsignedLexerT::skip_quote(unsigned pos, char endc){ for (; pos < _len; pos++) if (_data[pos] == '\n') _lineno++; else if (_data[pos] == '\r') { if (pos < _len - 1 && _data[pos+1] == '\n') pos++; _lineno++; } else if (_data[pos] == '\\' && endc == '\"' && pos < _len - 1) { if (_data[pos+1] == '<') pos = skip_backslash_angle(pos + 2) - 1; else if (_data[pos+1] == '\"') pos++; } else if (_data[pos] == endc) return pos + 1; return _len;}unsignedLexerT::process_line_directive(unsigned pos){ const char *data = _data; unsigned len = _len; unsigned first_pos = pos; for (pos++; pos < len && (data[pos] == ' ' || data[pos] == '\t'); pos++) /* nada */; if (pos < len - 4 && data[pos] == 'l' && data[pos+1] == 'i' && data[pos+2] == 'n' && data[pos+3] == 'e' && (data[pos+4] == ' ' || data[pos+4] == '\t')) { for (pos += 5; pos < len && (data[pos] == ' ' || data[pos] == '\t'); pos++) /* nada */; } if (pos >= len || !isdigit(data[pos])) { // complain about bad directive lerror(first_pos, pos, "unknown preprocessor directive"); return skip_line(pos); } else if (_ignore_line_directives) return skip_line(pos); // parse line number for (_lineno = 0; pos < len && isdigit(data[pos]); pos++) _lineno = _lineno * 10 + data[pos] - '0'; _lineno--; // account for extra line for (; pos < len && (data[pos] == ' ' || data[pos] == '\t'); pos++) /* nada */; if (pos < len && data[pos] == '\"') { // parse filename unsigned first_in_filename = pos; for (pos++; pos < len && data[pos] != '\"' && data[pos] != '\n' && data[pos] != '\r'; pos++) if (data[pos] == '\\' && pos < len - 1 && data[pos+1] != '\n' && data[pos+1] != '\r') pos++; _filename = cp_unquote(_big_string.substring(first_in_filename, pos - first_in_filename) + "\":"); // an empty filename means return to the input file's name if (_filename == ":") _filename = _original_filename; } // reach end of line for (; pos < len && data[pos] != '\n' && data[pos] != '\r'; pos++) /* nada */; if (pos < len - 1 && data[pos] == '\r' && data[pos+1] == '\n') pos++; return pos;}LexemeLexerT::next_lexeme(){ unsigned pos = _pos; while (true) { while (pos < _len && isspace(_data[pos])) { if (_data[pos] == '\n') _lineno++; else if (_data[pos] == '\r') { if (pos < _len - 1 && _data[pos+1] == '\n') pos++; _lineno++; } pos++; } unsigned opos = pos; if (pos >= _len) { _pos = _len; return Lexeme(); } else if (_data[pos] == '/' && pos < _len - 1) { if (_data[pos+1] == '/') pos = skip_line(pos + 2); else if (_data[pos+1] == '*') pos = skip_slash_star(pos + 2); else break; _lexinfo->notify_comment(opos, pos); } else if (_data[pos] == '#' && (pos == 0 || _data[pos-1] == '\n' || _data[pos-1] == '\r')) { pos = process_line_directive(pos); _lexinfo->notify_line_directive(opos, pos); } else break; } unsigned word_pos = pos; // find length of current word if (isalnum(_data[pos]) || _data[pos] == '_' || _data[pos] == '@') { more_word_characters: pos++; while (pos < _len && (isalnum(_data[pos]) || _data[pos] == '_' || _data[pos] == '@')) pos++; if (pos < _len - 1 && _data[pos] == '/' && (isalnum(_data[pos+1]) || _data[pos+1] == '_' || _data[pos+1] == '@')) goto more_word_characters; _pos = pos; String word = _big_string.substring(word_pos, pos - word_pos); if (word.length() == 16 && word == "connectiontunnel") { _lexinfo->notify_keyword(word, word_pos, pos); return Lexeme(lexTunnel, word, word_pos); } else if (word.length() == 12 && word == "elementclass") { _lexinfo->notify_keyword(word, word_pos, pos); return Lexeme(lexElementclass, word, word_pos); } else if (word.length() == 7 && word == "require") { _lexinfo->notify_keyword(word, word_pos, pos); return Lexeme(lexRequire, word, word_pos); } else return Lexeme(lexIdent, word, word_pos); } // check for variable if (_data[pos] == '$') { pos++; while (pos < _len && (isalnum(_data[pos]) || _data[pos] == '_')) pos++; if (pos > word_pos + 1) { _pos = pos; return Lexeme(lexVariable, _big_string.substring(word_pos, pos - word_pos), word_pos); } else pos--; } if (pos < _len - 1) { if (_data[pos] == '-' && _data[pos+1] == '>') { _pos = pos + 2; return Lexeme(lexArrow, _big_string.substring(word_pos, 2), word_pos); } else if (_data[pos] == ':' && _data[pos+1] == ':') { _pos = pos + 2; return Lexeme(lex2Colon, _big_string.substring(word_pos, 2), word_pos); } else if (_data[pos] == '|' && _data[pos+1] == '|') { _pos = pos + 2; return Lexeme(lex2Bar, _big_string.substring(word_pos, 2), word_pos); } } if (pos < _len - 2 && _data[pos] == '.' && _data[pos+1] == '.' && _data[pos+2] == '.') { _pos = pos + 3; return Lexeme(lex3Dot, _big_string.substring(word_pos, 3), word_pos); } _pos = pos + 1; return Lexeme(_data[word_pos], _big_string.substring(word_pos, 1), word_pos);}LexemeLexerT::lex_config(){ unsigned config_pos = _pos; unsigned pos = _pos; unsigned paren_depth = 1; for (; pos < _len; pos++) if (_data[pos] == '(') paren_depth++; else if (_data[pos] == ')') { paren_depth--; if (!paren_depth) break; } else if (_data[pos] == '\n') _lineno++; else if (_data[pos] == '\r') { if (pos < _len - 1 && _data[pos+1] == '\n') pos++; _lineno++; } else if (_data[pos] == '/' && pos < _len - 1) { if (_data[pos+1] == '/') pos = skip_line(pos + 2) - 1; else if (_data[pos+1] == '*') pos = skip_slash_star(pos + 2) - 1; } else if (_data[pos] == '\'' || _data[pos] == '\"') pos = skip_quote(pos + 1, _data[pos]) - 1; else if (_data[pos] == '\\' && pos < _len - 1 && _data[pos+1] == '<') pos = skip_backslash_angle(pos + 2) - 1; _pos = pos; _lexinfo->notify_config_string(config_pos, pos); return Lexeme(lexConfig, _big_string.substring(config_pos, pos - config_pos), config_pos);}StringLexerT::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];}voidLexerT::unlex(const Lexeme &t){ _tpos = (_tpos + TCIRCLE_SIZE - 1) % TCIRCLE_SIZE; _tcircle[_tpos] = t; assert(_tfull != _tpos);}boolLexerT::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).cc()); } else { unsigned old_pos = _pos; if (next_lexeme().is(kind)) return true; if (report_error) lerror(old_pos, _pos, "expected %s", lexeme_string(kind).cc()); _pos = old_pos; } return false;}intLexerT::next_pos() const{ if (_tpos != _tfull) return _tcircle[_tpos].pos1(); else return _pos;}// ERRORSStringLexerT::landmark() const{ return _filename + String(_lineno);}voidLexerT::vlerror(int pos1, int 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);}intLexerT::lerror(int pos1, int pos2, const char *format, ...){ va_list val; va_start(val, format); vlerror(pos1, pos2, landmark(), format, val); va_end(val); return -1;}intLexerT::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 TYPESElementClassT *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;}// ELEMENTSStringLexerT::anon_element_name(const String &class_name) const{ int anonymizer = _router->nelements() - _anonymous_offset + 1; return ";" + class_name + "@" + String(anonymizer);}intLexerT::make_element(String name, const Lexeme &location, int 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.cc()); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -