esclex.c revision 837416c3fd6b55b504f517ad92a135ead81f4cea
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* esclex.c -- lexer for esc
*
* this module provides lexical analysis and error handling routine
* expected by the yacc-generated parser (i.e. yylex() and yyerror()).
* it also does lots of tracking of things like filenames, line numbers,
* and what tokens are seen on a line up to the point where a syntax error
* was found. this module also arranges for the input source files to
* be run through cpp.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include "out.h"
#include "alloc.h"
#include "stats.h"
#include "stable.h"
#include "lut.h"
#include "literals.h"
#include "tree.h"
#include "esclex.h"
#include "eftread.h"
#include "check.h"
#include "y.tab.h"
/* ridiculously long token buffer -- disallow any token longer than this */
#define MAXTOK 8192
/* some misc stats we keep on the lexer & parser */
struct filestats {
} *Fstats;
static int Errcount;
/* input file state */
static char **Files;
static const char *Fileopened;
static int Line;
static const char *File;
#ifdef ESC
static const char *Cppargs;
static const char *Cppstdargs = "-undef -Y.";
#endif /* ESC */
/* for debugging */
static int Lexecho; /* echo tokens as we read them */
/* forward declarations of our internal routines */
static void doident();
/*
* table of reserved words. this table is only used by lex_init()
* to intialize the Rwords lookup table.
*/
static const struct {
const char *word;
const int val;
} Rwords[] = {
{ "asru", ASRU },
{ "count", COUNT },
{ "div", DIV },
{ "engine", ENGINE },
{ "event", EVENT },
{ "fru", FRU },
{ "if", IF },
{ "mask", MASK },
{ "prop", PROP },
{ "config", CONFIG },
/*
* PATHFUNC indicates functions that operate only on paths
* and quotes
*/
{ "is_connected", PATHFUNC },
{ "is_under", PATHFUNC },
};
/*
* Rwordslut is a lookup table of reserved words. lhs is the word
* (in the string table) and the rhs is the token value returned
* by the yylex() for that word.
*/
static const struct {
const char *suffix;
const unsigned long long nsec;
} Timesuffix[] = {
{ "nanosecond", 1ULL },
{ "nanoseconds", 1ULL },
{ "nsec", 1ULL },
{ "nsecs", 1ULL },
{ "ns", 1ULL },
{ "microsecond", 1000ULL },
{ "microseconds", 1000ULL },
{ "usec", 1000ULL },
{ "usecs", 1000ULL },
{ "us", 1000ULL },
{ "millisecond", 1000000ULL },
{ "milliseconds", 1000000ULL },
{ "msec", 1000000ULL },
{ "msecs", 1000000ULL },
{ "ms", 1000000ULL },
{ "second", 1000000000ULL },
{ "seconds", 1000000000ULL },
{ "s", 1000000000ULL },
{ "minute", 1000000000ULL * 60 },
{ "minutes", 1000000000ULL * 60 },
{ "min", 1000000000ULL * 60 },
{ "mins", 1000000000ULL * 60 },
{ "m", 1000000000ULL * 60 },
{ "hour", 1000000000ULL * 60 * 60 },
{ "hours", 1000000000ULL * 60 * 60 },
{ "hr", 1000000000ULL * 60 * 60 },
{ "hrs", 1000000000ULL * 60 * 60 },
{ "h", 1000000000ULL * 60 * 60 },
{ "day", 1000000000ULL * 60 * 60 * 24 },
{ "days", 1000000000ULL * 60 * 60 * 24 },
{ "d", 1000000000ULL * 60 * 60 * 24 },
{ "week", 1000000000ULL * 60 * 60 * 24 * 7 },
{ "weeks", 1000000000ULL * 60 * 60 * 24 * 7 },
{ "wk", 1000000000ULL * 60 * 60 * 24 * 7 },
{ "wks", 1000000000ULL * 60 * 60 * 24 * 7 },
{ "month", 1000000000ULL * 60 * 60 * 24 * 30 },
{ "months", 1000000000ULL * 60 * 60 * 24 * 30 },
{ "year", 1000000000ULL * 60 * 60 * 24 * 365 },
{ "years", 1000000000ULL * 60 * 60 * 24 * 365 },
{ "yr", 1000000000ULL * 60 * 60 * 24 * 365 },
{ "yrs", 1000000000ULL * 60 * 60 * 24 * 365 },
};
/*
* some wrappers around the general lut functions to provide type checking...
*/
static struct lut *
{
}
static int
{
}
static struct lut *
const unsigned long long *ullp)
{
}
const unsigned long long *
{
}
/*
* lex_init -- initialize the lexer with appropriate filenames & debug flags
*/
/*ARGSUSED*/
void
{
int i;
#ifdef ESC
const char *ptr;
#endif /* ESC */
#ifdef ESC
/* allow user to tell us where cpp is if it is some weird place */
/* and in case it takes some special stdargs */
Cppstdargs = ptr;
/* verify we can find cpp */
}
#endif /* ESC */
/* verify we can find all the input files */
while (*av) {
av++;
}
/* put reserved words into the string table & a lookup table */
/* initialize table of timeval suffixes */
for (i = 0; i < sizeof (Timesuffix) / sizeof (*Timesuffix); i++) {
}
/* record start time */
}
void
closefile(void)
{
#ifdef ESC
"bailing out.", Fileopened);
#else
#endif /* ESC */
}
}
/*
* yylex -- the lexer, called yylex() because that's what yacc wants
*/
int
yylex()
{
int c;
int nextc;
const char *cptr;
int startline;
int val;
for (;;) {
char ibuf[80];
#ifdef ESC
#else
#endif /* ESC */
Line = 1;
bol = 1;
/* add name to stats for visibility */
static int fnum;
char nbuf[100];
if (ibuf[0] != '\0') {
fnum);
} else {
}
fnum++;
}
}
case '#':
/* enforce that we're at beginning of line */
if (!bol)
(c == ' ' || c == '\t'))
;
if (!isdigit(c)) {
/*
* three cases here:
* #pragma
* #ident
* #something-we-don't-understand
* anything we don't expect we just ignore.
*/
*ptr++ = c;
*ptr++ = c;
*ptr++ = '\0';
/* skip white space */
(c == ' ' || c == '\t'))
;
if (c == EOF || c == '\n')
"bad #pragma");
/* pull in next token */
*ptr++ = c;
!isspace(c))
*ptr++ = c;
*ptr++ = '\0';
doident();
} else {
/* handle file & line info from cpp */
Line = 0;
do {
if (!isdigit(c))
break;
Line--; /* newline will increment it */
if (c != '"')
"bad # statement (file name)");
*ptr++ = c;
*ptr++ = '\0';
if (c != '"')
"bad # statement (quotes)");
}
/* skip the rest of the cpp line */
;
if (c == EOF)
else
break;
case EOF:
closefile();
continue;
case '\n':
Line++;
bol = 1;
break;
case '\r':
case ' ':
case '\t':
bol = 0;
break;
case '/':
bol = 0;
/* comment handling */
else if (nextc == '*') {
if (c == '\n')
Line++;
else if (c == '*' &&
(c == '/')))
break;
}
if (c == EOF) {
"end of comment not seen "
"(started on line %d)",
}
} else {
/* wasn't a comment, return the '/' token */
}
break;
case '"': {
int prevc;
bol = 0;
prevc = '\0';
/* quoted string handling */
for (;;) {
if (c == EOF)
"end of string not seen "
"(started on line %d)",
else if (c == '\n')
Line++;
break;
*ptr++ = c;
prevc = c;
}
*ptr++ = '\0';
}
case '&':
bol = 0;
/* && */
else {
}
/*NOTREACHED*/
break;
case '|':
bol = 0;
/* || */
else {
}
/*NOTREACHED*/
break;
case '!':
bol = 0;
/* ! or != */
else {
}
/*NOTREACHED*/
break;
case '=':
bol = 0;
/* == */
else {
}
/*NOTREACHED*/
break;
case '-':
bol = 0;
/* -> */
else {
}
/*NOTREACHED*/
break;
case '<':
bol = 0;
/* <= */
else if (nextc == '<')
/* << */
else {
}
/*NOTREACHED*/
break;
case '>':
bol = 0;
/* >= */
else if (nextc == '>')
/* >> */
else {
}
/*NOTREACHED*/
break;
default:
bol = 0;
if (isdigit(c)) {
int base;
/* collect rest of number */
if (c == '0') {
*ptr++ = c;
*ptr++ = '\0';
} else if (c == 'x' || c == 'X') {
*ptr++ = c;
base = 16;
} else {
base = 8;
}
} else {
*ptr++ = c;
base = 10;
}
"number too long");
switch (base) {
case 16:
if (c >= 'a' && c <= 'f' ||
c >= 'A' && c <= 'F') {
*ptr++ = c;
continue;
}
/*FALLTHRU*/
case 10:
if (c >= '8' && c <= '9') {
*ptr++ = c;
continue;
}
/*FALLTHRU*/
case 8:
if (c >= '0' && c <= '7') {
*ptr++ = c;
continue;
}
/* not valid for this base */
*ptr++ = '\0';
}
}
*ptr++ = '\0';
} else if (isalpha(c)) {
/* collect identifier */
*ptr++ = c;
for (;;) {
if ((isalnum(c) || c == '_') &&
*ptr++ = c;
else {
break;
}
}
"identifier too long");
*ptr++ = '\0';
}
} else
}
/*NOTREACHED*/
}
}
/*
* the record()/dumpline() routines are used to track & report
* the list of tokens seen on a given line. this is used in two ways.
* first, syntax errors found by the parser are reported by us (via
* yyerror()) and we tack on the tokens processed so far on the current
* line to help indicate exactly where the error is. second, if "lexecho"
* debugging is turned on, these routines provide it.
*/
#define MAXRECORD 1000
static int Recordedline;
static struct {
int tok;
const char *s;
static int Recordnext;
static int
{
if (Line != Recordedline) {
/* starting new line, dump out the previous line */
if (Lexecho && Recordedline) {
}
Recordedline = Line;
Recordnext = 0;
}
if (Recordnext >= MAXRECORD)
Recorded[Recordnext++].s = s;
return (tok);
}
/*ARGSUSED*/
static void
{
int i;
for (i = 0; i < Recordnext; i++)
case T_QUOTE:
Recorded[i].s);
break;
default:
Recorded[i].s);
break;
}
else
case EOF:
break;
case ARROW:
Recorded[i].s);
break;
case EQ:
break;
case NE:
break;
case OR:
break;
case AND:
break;
case LE:
break;
case GE:
break;
case LSHIFT:
break;
case RSHIFT:
break;
default:
else
break;
}
}
/*
* yyerror -- report a pareser error, called yyerror because yacc wants it
*/
void
yyerror(const char *s)
{
Errcount++;
}
/*
* doident -- handle "#pragma ident" directives
*/
static void
doident()
{
int c;
/* skip white space and quotes */
(c == ' ' || c == '\t' || c == '"'))
;
if (c == EOF || c == '\n')
/* pull in next token */
*ptr++ = c;
*ptr++ = c;
*ptr++ = '\0';
if (c != '\n') {
/* skip to end of line (including close quote, if any) */
;
}
}
/*
* dodictionary -- handle "#pragma dictionary" directives
*/
static void
{
int c;
/* skip white space and quotes */
(c == ' ' || c == '\t' || c == '"'))
;
if (c == EOF || c == '\n')
/* pull in next token */
*ptr++ = c;
*ptr++ = c;
*ptr++ = '\0';
if (c != '\n') {
/* skip to end of line (including close quote, if any) */
;
}
}
/*
* doallow_cycles -- handle "#pragma allow_cycles" directives
*/
static void
{
int c;
unsigned long long newlevel;
/*
* by default the compiler does not allow cycles or loops
* in propagations. when cycles are encountered, the
* compiler prints out an error message.
*
* "#pragma allow_cycles" and
* "#pragma allow_cycles 0"
* allow cycles, but any such cycle will produce a warning
* message.
*
* "#pragma allow_cycles N"
* with N > 0 will allow cycles and not produce any
* warning messages.
*/
/* skip white space and quotes */
(c == ' ' || c == '\t' || c == '"'))
;
if (c == EOF || c == '\n')
else {
/* pull in next token */
*ptr++ = c;
*ptr++ = c;
*ptr++ = '\0';
if (c != '\n') {
/* skip to end of line */
;
}
}
(void) check_cycle_level(newlevel);
"pragma set: allow_cycles (%s)",
}
/*
* dopragma -- handle #pragma directives
*/
static void
{
doident();
dodictionary();
if (Pragma_new_errors_only++ == 0)
"pragma set: new_errors_only");
if (Pragma_trust_ereports++ == 0)
"pragma set: trust_ereports");
else
"unknown pragma ignored: \"%s\"", tok);
}
/*
* lex_fini -- finalize the lexer
*/
int
lex_fini(void)
{
closefile();
if (Lexecho) {
}
return (Errcount);
}
void
lex_free(void)
{
/*
* Free up memory consumed by the lexer
*/
}
}