/* This file is part of GNU cflow
Copyright (C) 1997, 2005, 2007, 2010, 2014-2016 Sergey Poznyakoff
GNU cflow 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.
GNU cflow 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 . */
%top {
#include
#include
#include
}
%x comment
%x string
%x stringwait
%x longline
%{
struct obstack string_stk;
int line_num;
char *filename;
char *canonical_filename;
YYSTYPE yylval;
unsigned input_file_count; /* Number of input files, processed by source() */
int ident();
void update_loc();
#define lex_error(msg) error_at_line(0, 0, filename, line_num, "%s", msg)
/* Keep the token returned at the previous call to yylex. This is used
as a lexical tie-in to ensure that the next token after STRUCT is
IDENTIFIER. See get_token and ident below. */
static int prev_token;
%}
FILENAME [^\n*?]*
ONUMBER (0[0-7]*)
HNUMBER (0[xX][0-9a-fA-F]*)
DNUMBER ([1-9][0-9]*)
DIGITS [0-9][0-9]*
IDENT [a-zA-Z_][a-zA-Z0-9_]*
WS [ \t\f\r]*
%%
/* comments */
"//".*\n ++line_num;
"/*" BEGIN(comment);
[^*\n]* ;
[^*\n]*\n ++line_num;
"*"+[^*/\n]* ;
"*"+[^*/\n]*\n ++line_num;
"*"+"/" BEGIN(INITIAL);
/* Line directives */
^{WS}#{WS}line{WS}{DIGITS}.*\n |
^{WS}#{WS}{DIGITS}.*\n { update_loc(); }
/* skip any preproc */
^{WS}#.*\\\n { BEGIN(longline); ++line_num; }
{WS}#.*\n ++line_num;
.*\\\n ++line_num;
.*\n { BEGIN(INITIAL); ++line_num; }
/* keywords */
auto /* ignored */;
extern return EXTERN;
static return STATIC;
typedef return TYPEDEF;
struct {yylval.str = "struct"; return STRUCT;}
union {yylval.str = "union"; return STRUCT;}
enum {yylval.str = "enum"; return STRUCT;}
\* { yylval.str = "*";
return MODIFIER;
}
/* Operators
*
*/
"->" {yylval.str = "->"; return MEMBER_OF;}
"." {yylval.str = "."; return MEMBER_OF;}
"*=" {yylval.str = "*="; return OP;}
"/=" {yylval.str = "/="; return OP;}
"/" {yylval.str = "/"; return OP;}
"%=" {yylval.str = "%="; return OP;}
"%" {yylval.str = "%"; return OP;}
"+=" {yylval.str = "+="; return OP;}
"+" {yylval.str = "+"; return OP;}
"-=" {yylval.str = "-="; return OP;}
"-" {yylval.str = "-"; return OP;}
"<<=" {yylval.str = "<<="; return OP;}
">>=" {yylval.str = ">>="; return OP;}
"&=" {yylval.str = "&="; return OP;}
"|=" {yylval.str = "|="; return OP;}
"^=" {yylval.str = "^="; return OP;}
"^" {yylval.str = "^"; return OP;}
"||" {yylval.str = "||"; return OP;}
"|" {yylval.str = "|"; return OP;}
"&&" {yylval.str = "&&"; return OP;}
"&" {yylval.str = "&"; return OP;}
"==" {yylval.str = "=="; return OP;}
"=" {yylval.str = "="; return '=';}
"!=" {yylval.str = "!="; return OP;}
"!" {yylval.str = "!"; return OP;}
">=" {yylval.str = ">="; return OP;}
">" {yylval.str = ">"; return OP;}
"<=" {yylval.str = "<="; return OP;}
"<" {yylval.str = "<"; return OP;}
"<<" {yylval.str = "<<"; return OP;}
">>" {yylval.str = ">>"; return OP;}
"++" {yylval.str = "++"; return OP;}
"--" {yylval.str = "--"; return OP;}
'.' |
'\\.' |
'\\[0-7]{2,3}' |
'\\[xX][0-9a-fA-F]{1,2}' return STRING;
/* Identifiers and constants
*
*/
"..." |
{IDENT} return ident();
{ONUMBER} { obstack_grow(&string_stk, yytext, yyleng+1);
yylval.str = obstack_finish(&string_stk);
return WORD;
}
[+\-]?{DNUMBER}[^eE.] { yyless(yyleng-1);
obstack_grow(&string_stk, yytext, yyleng+1);
yylval.str = obstack_finish(&string_stk);
return WORD;}
{HNUMBER} |
[+\-]?{DIGITS}?\.{DIGITS}([eE][+\-]?{DIGITS})? |
[+\-]?{DIGITS}\.([eE][+\-]?{DIGITS})? {
obstack_grow(&string_stk, yytext, yyleng+1);
yylval.str = obstack_finish(&string_stk);
return WORD;
}
/* strings
* State map:
*
* "blahblahblah"
* "blahblahblah"
* .
*/
\" BEGIN(string);
[^\\"\n]* ;
\n { ++line_num; lex_error(_("unterminated string?")); }
\\. ;
\\\n ++line_num;
\" BEGIN(stringwait);
{WS} ;
\n ++line_num;
\" BEGIN(string);
. {
BEGIN(INITIAL);
yyless(0); /* put the symbol back */
return STRING;
}
\n ++line_num;
{WS} ;
/*\f ;*/
^\{ return LBRACE0;
^\} return RBRACE0;
. return yytext[0];
%%
static char *keywords[] = {
"break",
"case",
"continue",
"default",
"do",
"else",
"for",
"goto",
"if",
"return",
"sizeof",
"switch",
"while"
};
static char *types[] = {
"char",
"double",
"float",
"int",
"void",
};
static char *qualifiers[] = {
"long",
"const",
"register",
"restrict",
"short",
"signed",
"unsigned",
"volatile",
"inline"
};
void
init_tokens()
{
int i;
Symbol *sp;
for (i = 0; i < NUMITEMS(keywords); i++) {
sp = install(keywords[i], INSTALL_OVERWRITE);
sp->type = SymToken;
sp->token_type = WORD;
}
for (i = 0; i < NUMITEMS(types); i++) {
sp = install(types[i], INSTALL_OVERWRITE);
sp->type = SymToken;
sp->token_type = TYPE;
sp->source = NULL;
sp->def_line = -1;
sp->ref_line = NULL;
}
for (i = 0; i < NUMITEMS(qualifiers); i++) {
sp = install(qualifiers[i], INSTALL_OVERWRITE);
sp->type = SymToken;
sp->token_type = QUALIFIER;
sp->source = NULL;
sp->def_line = -1;
sp->ref_line = NULL;
}
sp = install("...", INSTALL_OVERWRITE);
sp->type = SymToken;
sp->token_type = IDENTIFIER;
sp->source = NULL;
sp->def_line = -1;
sp->ref_line = NULL;
}
void
init_lex(int debug_level)
{
yy_flex_debug = debug_level;
obstack_init(&string_stk);
init_tokens();
}
int
ident()
{
/* Do not attempt any symbol table lookup if the previous token was
STRUCT. This helps properly parse constructs like:
typedef struct foo foo;
struct foo {
int dummy;
};
*/
if (prev_token != STRUCT) {
Symbol *sp = lookup(yytext);
if (sp && sp->type == SymToken) {
yylval.str = sp->name;
return sp->token_type;
}
}
obstack_grow(&string_stk, yytext, yyleng);
obstack_1grow(&string_stk, 0);
yylval.str = obstack_finish(&string_stk);
return IDENTIFIER;
}
char *pp_bin;
char *pp_opts;
static struct obstack *opt_stack;
void
set_preprocessor(const char *arg)
{
pp_bin = arg ? xstrdup(arg) : NULL;
}
void
pp_option(const char *arg)
{
if (!opt_stack) {
if (!pp_bin)
pp_bin = CFLOW_PREPROC;
opt_stack = xmalloc(sizeof *opt_stack);
obstack_init(opt_stack);
}
obstack_1grow(opt_stack, ' ');
obstack_grow(opt_stack, arg, strlen (arg));
}
void
pp_finalize()
{
char *s = obstack_finish(opt_stack);
if (!pp_opts)
pp_opts = xstrdup(s);
else {
pp_opts = xrealloc(pp_opts, strlen(pp_opts) + strlen(s) + 1);
strcat(pp_opts, s);
}
obstack_free(opt_stack, s);
free(opt_stack);
opt_stack = NULL;
}
FILE *
pp_open(const char *name)
{
FILE *fp;
char *s;
size_t size;
if (opt_stack)
pp_finalize();
size = strlen(pp_bin) + 1 + strlen(name) + 1;
if (pp_opts)
size += strlen(pp_opts);
s = xmalloc(size);
strcpy(s, pp_bin);
if (pp_opts)
strcat(s, pp_opts);
strcat(s, " ");
strcat(s, name);
if (debug)
fprintf(stderr, _("Command line: %s\n"), s);
fp = popen(s, "r");
if (!fp)
error(0, errno, _("cannot execute `%s'"), s);
free(s);
return fp;
}
void
pp_close(FILE *fp)
{
pclose(fp);
}
int
yywrap()
{
if (!yyin)
return 1;
if (preprocess_option)
pp_close(yyin);
else
fclose(yyin);
yyin = NULL;
#ifdef FLEX_SCANNER
yy_delete_buffer(YY_CURRENT_BUFFER);
#endif
delete_statics();
return 1;
}
static int hit_eof;
int
get_token()
{
int tok;
if (hit_eof)
tok = 0;
else {
tok = yylex();
prev_token = tok;
if (!tok)
hit_eof = 1;
}
return tok;
}
int
source(char *name)
{
FILE *fp;
fp = fopen(name, "r");
if (!fp) {
error(0, errno, _("cannot open `%s'"), name);
return 1;
}
if (preprocess_option) {
fclose(fp);
fp = pp_open(name);
if (!fp)
return 1;
}
obstack_grow(&string_stk, name, strlen(name)+1);
filename = obstack_finish(&string_stk);
canonical_filename = filename;
line_num = 1;
input_file_count++;
hit_eof = 0;
yyrestart(fp);
return 0;
}
static int
getnum(unsigned base, int count)
{
int c, n;
unsigned i;
for (n = 0; count; count--) {
if (isdigit(c = input()))
i = c - '0';
else
i = toupper(c) - 'A' + 10;
if (i > base) {
unput(c);
break;
}
n = n * base + i;
}
return n;
}
int
backslash()
{
int c;
switch (c = input()) {
case 'a': return '\a';
case 'b': return '\b';
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case 't': return '\t';
case 'x': return getnum(16,2);
case '0': return getnum(8,3);
}
return c;
}
void
update_loc()
{
char *p;
for (p = strchr(yytext, '#')+1; *p && isspace(*p); p++)
;
if (p[0] == 'l') /* line */
p += 4;
line_num = strtoul(p, &p, 10);
for ( ; *p && isspace(*p); p++)
;
if (p[0] == '"') {
int n;
for (p++, n = 0; p[n] && p[n] != '"'; n++)
;
obstack_grow(&string_stk, p, n);
obstack_1grow(&string_stk, 0);
filename = obstack_finish(&string_stk);
}
if (debug > 1)
fprintf(stderr, _("New location: %s:%d\n"), filename, line_num);
}