* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
static char sccsid
[] = "@(#)scanner.c 5.1 (Berkeley) %G%";
static char rcsid
[] = "$Header: scanner.c,v 1.5 84/12/26 10:42:05 linton Exp $";
#define MAXLINESIZE 10240
public String initfile
= ".dbxinit";
typedef enum { WHITE
, ALPHA
, NUM
, OTHER
} Charclass
;
private Charclass
class[256 + 1];
private Charclass
*lexclass
= class + 1;
#define isdigit(c) (lexclass[c] == NUM)
#define isalnum(c) (lexclass[c] == ALPHA or lexclass[c] == NUM)
#define ishexdigit(c) ( \
isdigit(c) or (c >= 'a' and c <= 'f') or (c >= 'A' and c <= 'F') \
public char scanner_linebuf
[MAXLINESIZE
];
private char *curchar
, *prevchar
;
} inclinfo
[MAXINCLDEPTH
];
private unsigned int curinclindex
;
private Token
getident();
private Token
getstring();
private Boolean
eofinput();
private enterlexclass(class, s
)
for (p
= s
; *p
!= '\0'; p
++) {
for (i
= 0; i
< 257; i
++) {
enterlexclass(WHITE
, " \t");
enterlexclass(ALPHA
, "abcdefghijklmnopqrstuvwxyz");
enterlexclass(ALPHA
, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_$");
enterlexclass(NUM
, "0123456789");
curchar
= scanner_linebuf
;
scanner_linebuf
[0] = '\0';
* The input is line buffered. Tokens cannot cross line boundaries.
* There are two "modes" of operation: one as in a compiler,
* and one for reading shell-like syntax. In the first mode
* there is the additional choice of doing alias processing.
private Boolean shellmode
;
printf("(%s) ", cmdname
);
line
= fgets(scanner_linebuf
, MAXLINESIZE
, in
);
} while (line
== nil
and not eofinput());
while (lexclass
[*p
] == WHITE
) {
while (lexclass
[*p
] == WHITE
) {
if (lexclass
[c
] == ALPHA
) {
} else if (lexclass
[c
] == NUM
) {
} else if (isdigit(*curchar
)) {
} else if (*curchar
== '>') {
n
= MAXLINESIZE
- (p
- &scanner_linebuf
[0]);
if (fgets(p
, n
, in
) == nil
) {
if (shellmode
and index("!&*<>()[]", c
) == nil
) {
fprintf(stderr
, "yylex returns ");
* Put the given string before the current character
* in the current line, thus inserting it into the input stream.
avail
= curchar
- &scanner_linebuf
[0];
curchar
= &scanner_linebuf
[avail
- need
];
if (p
+ shift
>= &scanner_linebuf
[MAXLINESIZE
]) {
error("alias expansion too large");
curchar
= &scanner_linebuf
[0];
* Get the actuals for a macro call.
private String
movetochar (str
, c
)
error("missing ')' in macro call");
error("not enough parameters in macro call");
error("too many parameters in macro call");
private String
*getactuals (n
)
if (lexclass
[*p
] != WHITE
) {
error("missing actuals for macro");
for (i
= 0; i
< n
- 1; i
++) {
* Do command macro expansion, assuming curchar points to the beginning
* of the actuals, and we are not in shell mode.
char buf
[4096], namebuf
[100];
register char *p
, *q
, *r
;
actual
= getactuals(list_size(pl
));
error("alias expansion too large");
if (lexclass
[*q
] == ALPHA
) {
if (streq(ident(n
), namebuf
)) {
if (streq(s
, "syntax error")) {
start
= p
- &scanner_linebuf
[0];
if (p
> &scanner_linebuf
[0]) {
while (lexclass
[*p
] == WHITE
and p
> &scanner_linebuf
[0]) {
fprintf(stderr
, "%s", scanner_linebuf
);
fprintf(stderr
, "%*c", start
, ' ');
if (p
== &scanner_linebuf
[0]) {
fprintf(stderr
, "^ unrecognized command");
fprintf(stderr
, "^ syntax error");
curchar
= scanner_linebuf
;
scanner_linebuf
[0] = '\0';
* If chkalias is true, check first to see if it's an alias.
* Otherwise, check to see if it's a keyword.
private Token
getident (chkalias
)
} while (index(" \t\n!&<>*[]()'\"", *p
) == nil
);
yylval
.y_name
= identname(buf
, false);
if (findalias(yylval
.y_name
, &pl
, &str
)) {
while (lexclass
[*curchar
] == WHITE
) {
t
= findkeyword(yylval
.y_name
, NAME
);
t
= findkeyword(yylval
.y_name
, NAME
);
} else if (*(p
+1) == 't') {
} else if (varIsSet("$hexin")) {
} else if (varIsSet("$hexin")) {
} else if (varIsSet("$octin")) {
} while (ishexdigit(*p
));
if (*p
== 'e' or *p
== 'E') {
if (*p
== '+' or *p
== '-' or isdigit(*p
)) {
yylval
.y_real
= atof(buf
);
yylval
.y_int
= atol(buf
);
yylval
.y_int
= octal(buf
);
* Convert a string of octal digits to an integer.
for (p
= s
; *p
!= '\0'; p
++) {
* Convert a string of hexadecimal digits to an integer.
for (p
= s
; *p
!= '\0'; p
++) {
if (*p
>= 'a' and *p
<= 'f') {
} else if (*p
>= 'A' and *p
<= 'F') {
private Token
getstring (quote
)
while (not endofstring
) {
if (*p
== '\\' and *(p
+1) == '\n') {
if (fgets(scanner_linebuf
, MAXLINESIZE
, in
) == nil
) {
error("non-terminated string");
p
= &scanner_linebuf
[0] - 1;
} else if (*p
== '\n' or *p
== '\0') {
error("non-terminated string");
} else if (*p
== quote
) {
if (quote
== '\'' and buf
[1] == '\0') {
yylval
.y_string
= strdup(buf
);
* Process a character constant.
* Watch out for backslashes.
* Input file management routines.
public setinput(filename
)
f
= fopen(filename
, "r");
error("can't open %s", filename
);
if (curinclindex
>= MAXINCLDEPTH
) {
error("unreasonable input nesting on \"%s\"", filename
);
inclinfo
[curinclindex
].savefile
= in
;
inclinfo
[curinclindex
].savefn
= errfilename
;
inclinfo
[curinclindex
].savelineno
= errlineno
;
private Boolean
eofinput()
in
= inclinfo
[curinclindex
].savefile
;
errfilename
= inclinfo
[curinclindex
].savefn
;
errlineno
= inclinfo
[curinclindex
].savelineno
;
* Pop the current input. Return whether successful.
public Boolean
popinput()
b
= (Boolean
) (not eofinput());
* Return whether we are currently reading from standard input.
return (Boolean
) (in
== stdin
);
* Send the current line to the shell.
while (*p
!= '\0' and (*p
== '\n' or lexclass
[*p
] == WHITE
)) {
if (*p
== '\0' and isterm(in
)) {
* Read the rest of the current line in "shell mode".
* Print out a token for debugging.
fprintf(f
, "char '\\n'");
fprintf(f
, "char '%c'", t
);
fprintf(f
, "\"%s\"", keywdstring(t
));