// // aegis - project change supervisor // Copyright (C) 1991-1996, 1998, 1999, 2001-2008, 2010, 2012, 2014 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or (at // your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include #include #include #include #include #include #include #include #include #include #include #include // must be after #include #include #include #include #include #include #include #include /** * The source global variable is used to remember the managed input * stream if the currently being parsed meta-data file. */ static input::pointer source; static int error_count; extern gram_STYPE gram_lval; static nstring_accumulator buffer; input::pointer lex_iopen_file(const nstring &filename) { // // Open the underlying binary file. // input::pointer fp = input_file::open(filename); // // Decompress the input stream. If it *isn't* compressed, this // incurs NO overhead, because the gunzip code gets itself out // of the way, and returns the original fp. // fp = input_gunzip::create_if_candidate(fp); fp = input_bunzip2::create_if_candidate(fp); // // Get rid of CRLF sequences in the input. // This happens, for instance, when the file is created on // windows nt, but used on Unix. // fp = input_crlf::create(fp); return fp; } input::pointer lex_iopen_env(const nstring &name) { return input_env::create(name); } void lex_open_input(const input::pointer &ifp) { assert(!source); source = ifp; } void lex_close(void) { assert(source); if (error_count) { sub_context_ty *scp; scp = sub_context_new(); sub_var_set_string(scp, "File_Name", source->name()); sub_var_set_long(scp, "Number", error_count); sub_var_optional(scp, "Number"); fatal_intl(scp, i18n("$filename: has errors")); // NOTREACHED } source.reset(); } static inline void lex_getc_undo(int c) { if (c >= 0) source->ungetc(c); } int gram_lex(void) { sub_context_ty *scp; int c; int ndigits; for (;;) { c = source->getch(); switch (c) { case ' ': case '\t': case '\f': case '\n': break; case '0': buffer.clear(); buffer.push_back('0'); c = source->getch(); if (c == 'x' || c == 'X') { buffer.push_back(c); ndigits = 0; for (;;) { c = source->getch(); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': ++ndigits; buffer.push_back(c); continue; default: break; } break; } if (!ndigits) { gram_error(i18n("malformed hex constant")); gram_lval.lv_integer = 0; goto integer_return; } lex_getc_undo(c); buffer.push_back(' '); gram_lval.lv_integer = strtoul(buffer.get_data(), (char **)0, 16); goto integer_return; } if (c == '.') { buffer.push_back(c); goto fraction; } if (c == 'e' || c == 'E') goto exponent; for (;;) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': buffer.push_back(c); c = source->getch(); continue; default: break; } break; } lex_getc_undo(c); buffer.push_back(' '); gram_lval.lv_integer = strtoul(buffer.get_data(), (char **)0, 8); goto integer_return; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': buffer.clear(); for (;;) { buffer.push_back(c); c = source->getch(); if (c < 0) break; if (!isdigit((unsigned char)c)) break; } if (c == '.') { buffer.push_back(c); goto fraction; } if (c == 'e' || c == 'E') goto exponent; lex_getc_undo(c); buffer.push_back(' '); gram_lval.lv_integer = strtoul(buffer.get_data(), (char **)0, 10); assert(gram_lval.lv_integer >= 0); integer_return: trace(("%s: INTEGER %ld\n", source->name().c_str(), gram_lval.lv_integer)); return INTEGER; case '.': c = source->getch(); if (c < 0 || !isdigit((unsigned char)c)) { lex_getc_undo(c); return '.'; } buffer.clear(); buffer.push_back('0'); buffer.push_back('.'); buffer.push_back(c); fraction: for (;;) { c = source->getch(); if (c < 0 || !isdigit((unsigned char)c)) break; buffer.push_back(c); } if (c == 'e' || c == 'E') { exponent: buffer.push_back(c); c = source->getch(); if (c == '+' || c == '-') { buffer.push_back(c); c = source->getch(); } ndigits = 0; for (;;) { c = source->getch(); if (c < 0 || !isdigit((unsigned char)c)) break; ++ndigits; buffer.push_back(c); } if (!ndigits) { gram_error(i18n("malformed exponent")); gram_lval.lv_real = 0; trace(("%s: REAL 0\n", source->name().c_str())); return REAL; } } lex_getc_undo(c); buffer.push_back('\0'); gram_lval.lv_real = atof(buffer.get_data()); trace(("%s: REAL %g\n", source->name().c_str(), gram_lval.lv_real)); return REAL; case '"': buffer.clear(); for (;;) { c = source->getch(); if (c == EOF) { str_eof: gram_error("end-of-file within string"); break; } if (c == '\n') { gram_error("end-of-line within string"); break; } if (c == '"') break; if (c == '\\') { c = source->getch(); switch (c) { default: scp = sub_context_new(); sub_var_set_format(scp, "Name", "\\%c", c); lex_error(scp, i18n("unknown '$name' escape")); sub_context_delete(scp); break; case '\n': break; case EOF: goto str_eof; case 'b': buffer.push_back('\b'); break; case 'n': buffer.push_back('\n'); break; case 'r': buffer.push_back('\r'); break; case 't': buffer.push_back('\t'); break; case 'f': buffer.push_back('\f'); break; case '"': case '\\': buffer.push_back(c); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { int n; int v; v = 0; for (n = 0; n < 3; ++n) { v = v * 8 + c - '0'; c = source->getch(); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': continue; default: lex_getc_undo(c); break; } break; } buffer.push_back(v); } break; } } else buffer.push_back(c); } gram_lval.lv_string = new nstring(buffer.mkstr()); trace(("%s: STRING %s\n", source->name().c_str(), gram_lval.lv_string->quote_c().c_str())); return STRING; case '@': buffer.clear(); for (;;) { c = source->getch(); switch (c) { case EOF: goto str_eof; case '@': c = source->getch(); if (c == EOF) break; if (c != '@') { source->ungetc(c); break; } // fall through... default: buffer.push_back(c); continue; } break; } gram_lval.lv_string = new nstring(buffer.mkstr()); trace(("%s: STRING %s\n", source->name().c_str(), gram_lval.lv_string->quote_c().c_str())); return STRING; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '_': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': buffer.clear(); for (;;) { buffer.push_back(c); c = source->getch(); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '_': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': continue; default: lex_getc_undo(c); break; } break; } if (buffer.size() == 4 && !memcmp(buffer.get_data(), "ZERO", 4)) { gram_lval.lv_integer = MAGIC_ZERO; goto integer_return; } gram_lval.lv_string = new nstring(buffer.mkstr()); trace(("%s: NAME %s\n", source->name().c_str(), gram_lval.lv_string->quote_c().c_str())); return NAME; case '#': // // Shell style single line comment // single_line_comment: for (;;) { c = source->getch(); if (c == EOF || c == '\n') break; } break; case '/': // // C and C++ style comments // c = source->getch(); if (c == '/') { // // C++ style single line comment. // goto single_line_comment; } if (c != '*') { // // Not a C style block comment, // just return the slash. // lex_getc_undo(c); trace(("%s: '/'\n", source->name().c_str())); return '/'; } for (;;) { for (;;) { c = source->getch(); if (c == EOF) { bad_comment: gram_error("end-of-file within comment"); quit(1); } if (c == '*') break; } for (;;) { c = source->getch(); if (c == EOF) goto bad_comment; if (c != '*') break; } if (c == '/') break; } break; case EOF: trace(("%s: end of file\n", source->name().c_str())); return 0; default: trace(("%s: '%c'\n", source->name().c_str(), c)); return c; } } } void gram_error(const char *s) { sub_context_ty *scp; scp = sub_context_new(); lex_error(scp, s); sub_context_delete(scp); } void lex_error(sub_context_ty *scp, const char *s) { string_ty *msg; msg = subst_intl(scp, s); // re-use substitution context sub_var_set_string(scp, "MeSsaGe", msg); sub_var_set_string(scp, "File_Name", source->name()); error_intl(scp, i18n("$filename: $message")); str_free(msg); if (++error_count >= 20) { // re-use substitution context sub_var_set_string(scp, "File_Name", source->name()); fatal_intl(scp, i18n("$filename: too many errors")); } #if 0 // // This stuff is here to insulate against error messages that various // versions if GNU Bison may or may not issue. If there are similar // issues with byacc, put them here, too. // i18n("syntax error: cannot back up") i18n("syntax error; also virtual memory exhausted"); #endif } nstring lex_position(void) { if (source) return source->name(); return "end-of-input"; } // vim: set ts=8 sw=4 et :