//
// aegis - project change supervisor
// Copyright (C) 1991-1995, 1997, 1999, 2002-2008, 2012 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
#include
#include
#include
#include // must be last
struct file_ty
{
~file_ty() { if (fp) fclose(fp); fp = 0; line_number = 0; next = 0; }
file_ty() : fp(0), line_number(1), next(0) { }
FILE *fp;
int line_number;
nstring file_name;
file_ty *next;
};
static file_ty *file;
static int error_count;
extern parse_STYPE parse_lval;
static nstring_list ifiles;
static nstring_list include_path;
static symtab keyword;
static nstring most_recent_comment;
/**
* The lex_initialize function adds all the keywords to the symbol table.
* The keywords are intentionally case sensitive.
*/
static void
lex_initialize(void)
{
struct keyword_ty
{
const char *k_name;
int k_token;
};
static const keyword_ty table[] =
{
{ "boolean", BOOLEAN, },
{ "false", BOOLEAN_CONSTANT, },
{ "hide_if_default", HIDE_IF_DEFAULT, },
{ "include", INCLUDE, },
{ "integer", INTEGER, },
{ "real", REAL, },
{ "redefinition_ok", REDEFINITION_OK, },
{ "show_if_default", SHOW_IF_DEFAULT, },
{ "string", STRING, },
{ "time", TIME, },
{ "true", BOOLEAN_CONSTANT, },
{ "type", TYPE, },
};
if (!keyword.empty())
return;
for (const keyword_ty *kp = table; kp < ENDOF(table); ++kp)
{
keyword.assign(kp->k_name, kp->k_token);
}
}
void
lex_open(const nstring &s)
{
file_ty *f = new file_ty;
if (!file)
lex_initialize();
if (s[0] != '/')
{
for (size_t j = 0; j < include_path.size(); ++j)
{
char buffer[2000];
snprintf
(
buffer,
sizeof(buffer),
"%s/%s",
include_path[j].c_str(),
s.c_str()
);
f->fp = fopen(buffer, "r");
if (f->fp)
{
f->file_name = nstring(buffer);
break;
}
if (errno != ENOENT)
nfatal("open %s", buffer);
}
}
if (!f->fp)
{
f->fp = fopen(s.c_str(), "r");
if (!f->fp)
nfatal("open %s", s.quote_c().c_str());
f->file_name = s;
}
f->next = file;
ifiles.push_back_unique(s);
file = f;
}
void
lex_close(void)
{
if (error_count)
exit(1);
delete file;
}
static int
lex_getc(void)
{
int c = 0;
for (;;)
{
c = fgetc(file->fp);
if (c != EOF)
break;
if (ferror(file->fp))
nfatal("read %s", file->file_name.quote_c().c_str());
if (!file->next)
break;
file_ty *old = file;
file = old->next;
delete old;
}
if (c == '\n')
file->line_number++;
return c;
}
static void
lex_getc_undo(int c)
{
switch (c)
{
case EOF:
break;
case '\n':
file->line_number--;
// fall through...
default:
ungetc(c, file->fp);
break;
}
}
int
parse_lex(void)
{
static nstring_accumulator buffer;
for (;;)
{
buffer.clear();
int line_number_start = file->line_number;
int c = lex_getc();
switch (c)
{
case ' ':
case '\t':
case '\f':
case '\n':
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
parse_lval.lv_integer = 0;
for (;;)
{
parse_lval.lv_integer = 10 * parse_lval.lv_integer + c - '0';
c = lex_getc();
if (c < '0' || c > '9')
{
lex_getc_undo(c);
break;
}
}
trace(("INTEGER_CONSTANT\n"));
return INTEGER_CONSTANT;
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':
{
for (;;)
{
buffer.push_back((char)c);
c = lex_getc();
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;
}
lex_getc_undo(c);
break;
}
nstring s = buffer.mkstr();
int *data = keyword.query(s);
if (data)
{
trace(("keyword:%s\n", s.c_str()));
return *data;
}
parse_lval.lv_string = new nstring(s);
trace(("NAME\n"));
return NAME;
}
case '/':
c = lex_getc();
if (c != '*')
{
lex_getc_undo(c);
trace(("character:'/'\n"));
return '/';
}
for (;;)
{
for (;;)
{
c = lex_getc();
if (c == EOF)
{
bad_comment:
parse_error("end-of-file inside comment");
exit(1);
}
if (!buffer.empty() || !isspace((unsigned char)c))
buffer.push_back((char)c);
if (c == '*')
break;
}
for (;;)
{
c = lex_getc();
if (c == EOF)
goto bad_comment;
buffer.push_back((char)c);
if (c != '*')
break;
}
if (c == '/')
{
assert(buffer.size() >= 2);
buffer.pop_back();
buffer.pop_back();
while
(
!buffer.empty()
&&
isspace((unsigned char)buffer.back())
)
buffer.pop_back();
most_recent_comment = buffer.mkstr();
break;
}
}
break;
case '<':
for (;;)
{
c = lex_getc();
if (c == EOF)
goto str_eof;
if (c == '\n')
goto str_eoln;
if (c == '>')
break;
buffer.push_back((char)c);
}
parse_lval.lv_string = new nstring(buffer.mkstr());
trace(("STRING_CONSTANT\n"));
return STRING_CONSTANT;
case '"':
for (;;)
{
c = lex_getc();
if (c == EOF)
{
str_eof:
file->line_number = line_number_start;
parse_error("end-of-file within string");
break;
}
if (c == '\n')
{
str_eoln:
file->line_number = line_number_start;
parse_error("end-of-line within string");
break;
}
if (c == '"')
break;
if (c == '\\')
{
c = lex_getc();
switch (c)
{
default:
parse_error("unknown '\\%c' escape", c);
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((char)c);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
{
int v = 0;
for (int n = 0; n < 3; ++n)
{
v = v * 8 + c - '0';
c = lex_getc();
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((char)v);
}
break;
}
}
else
buffer.push_back((char)c);
}
parse_lval.lv_string = new nstring(buffer.mkstr());
trace(("STRING_CONSTANT\n"));
return STRING_CONSTANT;
default:
trace(("character:%s\n", unctrl(c)));
return c;
}
}
}
void
parse_error(const char *s, ...)
{
va_list ap;
char buffer[1000];
va_start(ap, s);
vsnprintf(buffer, sizeof(buffer), s, ap);
va_end(ap);
error_raw("%s: %d: %s", file->file_name.c_str(), file->line_number, buffer);
if (++error_count >= 20)
error_raw("%s: too many errors, bye!", file->file_name.c_str());
}
void
lex_message(const char *s, ...)
{
va_list ap;
va_start(ap, s);
char buffer[1000];
vsnprintf(buffer, sizeof(buffer), s, ap);
va_end(ap);
error_raw("%s: %d: %s", file->file_name.c_str(), file->line_number, buffer);
}
bool
lex_in_include_file(void)
{
trace(("lex_in_include_file() => %d\n", (file && file->next)));
return (file && file->next);
}
void
lex_include_path(const nstring &dir)
{
include_path.push_back_unique(dir);
}
nstring
lex_comment_get()
{
trace(("%s\n", __PRETTY_FUNCTION__));
nstring s = most_recent_comment;
most_recent_comment.clear();
return s;
}
nstring
lex_position_get()
{
trace(("%s\n", __PRETTY_FUNCTION__));
if (!file)
return "";
return
nstring::format("%s: %d", file->file_name.c_str(), file->line_number);
}
#ifdef DEBUG
static char lex_debug_buf[3000];
static char *lex_debug_buf_p = lex_debug_buf;
void
lex_debug_vprintf(const char *s, va_list ap)
{
char buffer[1000];
vsnprintf(buffer, sizeof(buffer), s, ap);
char *start = lex_debug_buf_p;
lex_debug_buf_p =
strendcpy
(
lex_debug_buf_p,
buffer,
lex_debug_buf + sizeof(lex_debug_buf)
);
const char *ep = strchr(start, '\n');
if (ep)
{
error_raw
(
"%s: %d: %.*s",
file->file_name.c_str(),
file->line_number,
(ep - lex_debug_buf),
lex_debug_buf
);
size_t nbytes = lex_debug_buf_p - (ep + 1);
memmove
(
lex_debug_buf,
ep + 1,
nbytes + 1
);
lex_debug_buf_p = lex_debug_buf + nbytes;
}
}
void
lex_debug_printf(const char *s, ...)
{
va_list ap;
va_start(ap, s);
lex_debug_vprintf(s, ap);
va_end(ap);
}
void
lex_debug_fprintf(void *, const char *s, ...)
{
va_list ap;
va_start(ap, s);
lex_debug_vprintf(s, ap);
va_end(ap);
}
#endif // DEBUG
// vim: set ts=8 sw=4 et :