766 lines
14 KiB
Plaintext
766 lines
14 KiB
Plaintext
/*
|
|
// Filename: dcLexer.lxx
|
|
// Created by: drose (05Oct00)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
*/
|
|
|
|
%{
|
|
#include "dcLexerDefs.h"
|
|
#include "dcParserDefs.h"
|
|
#include "dcParser.h"
|
|
#include "dcindent.h"
|
|
|
|
|
|
static int yyinput(void); // declared by flex.
|
|
extern "C" int dcyywrap();
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Static variables
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
// We'll increment line_number and col_number as we parse the file, so
|
|
// that we can report the position of an error.
|
|
static int line_number = 0;
|
|
static int col_number = 0;
|
|
|
|
// current_line holds as much of the current line as will fit. Its
|
|
// only purpose is for printing it out to report an error to the user.
|
|
static const int max_error_width = 1024;
|
|
static char current_line[max_error_width + 1];
|
|
|
|
static int error_count = 0;
|
|
static int warning_count = 0;
|
|
|
|
// This is the pointer to the current input stream.
|
|
static istream *inp = NULL;
|
|
|
|
// This is the name of the dc file we're parsing. We keep it so we
|
|
// can print it out for error messages.
|
|
static string dc_filename;
|
|
|
|
// This is the initial token state returned by the lexer. It allows
|
|
// the yacc grammar to start from initial points.
|
|
static int initial_token;
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Defining the interface to the lexer.
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
dc_init_lexer(istream &in, const string &filename) {
|
|
inp = ∈
|
|
dc_filename = filename;
|
|
line_number = 0;
|
|
col_number = 0;
|
|
error_count = 0;
|
|
warning_count = 0;
|
|
initial_token = START_DC;
|
|
}
|
|
|
|
void
|
|
dc_start_parameter_value() {
|
|
/* Set the initial state to begin parsing a parameter value, instead
|
|
of at the beginning of the dc file. */
|
|
initial_token = START_PARAMETER_VALUE;
|
|
}
|
|
|
|
void
|
|
dc_start_parameter_description() {
|
|
/* Set the initial state to begin parsing a parameter description, instead
|
|
of at the beginning of the dc file. */
|
|
initial_token = START_PARAMETER_DESCRIPTION;
|
|
}
|
|
|
|
int
|
|
dc_error_count() {
|
|
return error_count;
|
|
}
|
|
|
|
int
|
|
dc_warning_count() {
|
|
return warning_count;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Internal support functions.
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
int
|
|
dcyywrap(void) {
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
dcyyerror(const string &msg) {
|
|
cerr << "\nError";
|
|
if (!dc_filename.empty()) {
|
|
cerr << " in " << dc_filename;
|
|
}
|
|
cerr
|
|
<< " at line " << line_number << ", column " << col_number << ":\n"
|
|
<< current_line << "\n";
|
|
indent(cerr, col_number-1)
|
|
<< "^\n" << msg << "\n\n";
|
|
|
|
error_count++;
|
|
}
|
|
|
|
void
|
|
dcyywarning(const string &msg) {
|
|
cerr << "\nWarning";
|
|
if (!dc_filename.empty()) {
|
|
cerr << " in " << dc_filename;
|
|
}
|
|
cerr
|
|
<< " at line " << line_number << ", column " << col_number << ":\n"
|
|
<< current_line << "\n";
|
|
indent(cerr, col_number-1)
|
|
<< "^\n" << msg << "\n\n";
|
|
|
|
warning_count++;
|
|
}
|
|
|
|
// Now define a function to take input from an istream instead of a
|
|
// stdio FILE pointer. This is flex-specific.
|
|
static void
|
|
input_chars(char *buffer, int &result, int max_size) {
|
|
nassertv(inp != NULL);
|
|
if (*inp) {
|
|
inp->read(buffer, max_size);
|
|
result = inp->gcount();
|
|
if (result >= 0 && result < max_size) {
|
|
// Truncate at the end of the read.
|
|
buffer[result] = '\0';
|
|
}
|
|
|
|
if (line_number == 0) {
|
|
// This is a special case. If we are reading the very first bit
|
|
// from the stream, copy it into the current_line array. This
|
|
// is because the \n.* rule below, which fills current_line
|
|
// normally, doesn't catch the first line.
|
|
strncpy(current_line, dcyytext, max_error_width);
|
|
current_line[max_error_width] = '\0';
|
|
line_number++;
|
|
col_number = 0;
|
|
|
|
// Truncate it at the newline.
|
|
char *end = strchr(current_line, '\n');
|
|
if (end != NULL) {
|
|
*end = '\0';
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// End of file or I/O error.
|
|
result = 0;
|
|
}
|
|
}
|
|
#undef YY_INPUT
|
|
#define YY_INPUT(buffer, result, max_size) input_chars(buffer, result, max_size)
|
|
|
|
// read_char reads and returns a single character, incrementing the
|
|
// supplied line and column numbers as appropriate. A convenience
|
|
// function for the scanning functions below.
|
|
static int
|
|
read_char(int &line, int &col) {
|
|
int c = yyinput();
|
|
if (c == '\n') {
|
|
line++;
|
|
col = 0;
|
|
} else {
|
|
col++;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
// scan_quoted_string reads a string delimited by quotation marks and
|
|
// returns it.
|
|
static string
|
|
scan_quoted_string(char quote_mark) {
|
|
string result;
|
|
|
|
// We don't touch the current line number and column number during
|
|
// scanning, so that if we detect an error while scanning the string
|
|
// (e.g. an unterminated string), we'll report the error as
|
|
// occurring at the start of the string, not at the end--somewhat
|
|
// more convenient for the user.
|
|
|
|
// Instead of adjusting the global line_number and col_number
|
|
// variables, we'll operate on our own local variables for the
|
|
// interim.
|
|
int line = line_number;
|
|
int col = col_number;
|
|
|
|
int c;
|
|
c = read_char(line, col);
|
|
while (c != quote_mark && c != EOF) {
|
|
// A newline is not allowed within a string unless it is escaped.
|
|
if (c == '\n') {
|
|
c = EOF;
|
|
break;
|
|
|
|
} else if (c == '\\') {
|
|
// Backslash escapes the following character. We also respect
|
|
// some C conventions.
|
|
c = read_char(line, col);
|
|
switch (c) {
|
|
case 'a':
|
|
result += '\a';
|
|
c = read_char(line, col);
|
|
break;
|
|
|
|
case 'n':
|
|
result += '\n';
|
|
c = read_char(line, col);
|
|
break;
|
|
|
|
case 'r':
|
|
result += '\r';
|
|
c = read_char(line, col);
|
|
break;
|
|
|
|
case 't':
|
|
result += '\t';
|
|
c = read_char(line, col);
|
|
break;
|
|
|
|
case 'x':
|
|
{
|
|
int hex = 0;
|
|
c = read_char(line, col);
|
|
for (int i = 0; i < 2 && isxdigit(c); i++) {
|
|
hex = hex * 16 + (isdigit(c) ? c - '0' : tolower(c) - 'a' + 10);
|
|
c = read_char(line, col);
|
|
}
|
|
|
|
result += hex;
|
|
}
|
|
break;
|
|
|
|
case '0':
|
|
{
|
|
int oct = 0;
|
|
c = read_char(line, col);
|
|
for (int i = 0; i < 3 && (c >= '0' && c < '7'); i++) {
|
|
oct = oct * 8 + (c - '0');
|
|
c = read_char(line, col);
|
|
}
|
|
|
|
result += oct;
|
|
}
|
|
break;
|
|
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
{
|
|
int dec = 0;
|
|
c = read_char(line, col);
|
|
for (int i = 0; i < 3 && isdigit(c); i++) {
|
|
dec = dec * 10 + (c - '0');
|
|
c = read_char(line, col);
|
|
}
|
|
|
|
result += dec;
|
|
}
|
|
break;
|
|
|
|
case EOF:
|
|
break;
|
|
|
|
default:
|
|
result += c;
|
|
c = read_char(line, col);
|
|
}
|
|
|
|
} else {
|
|
result += c;
|
|
c = read_char(line, col);
|
|
}
|
|
}
|
|
|
|
if (c == EOF) {
|
|
dcyyerror("This quotation mark is unterminated.");
|
|
}
|
|
|
|
line_number = line;
|
|
col_number = col;
|
|
|
|
return result;
|
|
}
|
|
|
|
// scan_hex_string reads a string of hexadecimal digits delimited by
|
|
// angle brackets and returns the representative string.
|
|
static string
|
|
scan_hex_string() {
|
|
string result;
|
|
|
|
// We don't touch the current line number and column number during
|
|
// scanning, so that if we detect an error while scanning the string
|
|
// (e.g. an unterminated string), we'll report the error as
|
|
// occurring at the start of the string, not at the end--somewhat
|
|
// more convenient for the user.
|
|
|
|
// Instead of adjusting the global line_number and col_number
|
|
// variables, we'll operate on our own local variables for the
|
|
// interim.
|
|
int line = line_number;
|
|
int col = col_number;
|
|
|
|
bool odd = false;
|
|
int last = 0;
|
|
int c;
|
|
c = read_char(line, col);
|
|
while (c != '>' && c != EOF) {
|
|
int value;
|
|
if (c >= '0' && c <= '9') {
|
|
value = c - '0';
|
|
} else if (c >= 'a' && c <= 'f') {
|
|
value = c - 'a' + 10;
|
|
} else if (c >= 'A' && c <= 'F') {
|
|
value = c - 'A' + 10;
|
|
} else {
|
|
line_number = line;
|
|
col_number = col;
|
|
dcyyerror("Invalid hex digit.");
|
|
return string();
|
|
}
|
|
|
|
odd = !odd;
|
|
if (odd) {
|
|
last = value;
|
|
} else {
|
|
result += (char)((last << 4) | value);
|
|
}
|
|
c = read_char(line, col);
|
|
}
|
|
|
|
if (c == EOF) {
|
|
dcyyerror("This hex string is unterminated.");
|
|
return string();
|
|
} else if (odd) {
|
|
dcyyerror("Odd number of hex digits.");
|
|
return string();
|
|
}
|
|
|
|
line_number = line;
|
|
col_number = col;
|
|
|
|
return result;
|
|
}
|
|
|
|
// eat_c_comment scans past all characters up until the first */
|
|
// encountered.
|
|
static void
|
|
eat_c_comment() {
|
|
// As above, we'll operate on our own local copies of line_number
|
|
// and col_number within this function.
|
|
|
|
int line = line_number;
|
|
int col = col_number;
|
|
|
|
int c, last_c;
|
|
|
|
last_c = '\0';
|
|
c = read_char(line, col);
|
|
while (c != EOF && !(last_c == '*' && c == '/')) {
|
|
if (last_c == '/' && c == '*') {
|
|
dcyywarning("This comment contains a nested /* symbol--possibly unclosed?");
|
|
}
|
|
last_c = c;
|
|
c = read_char(line, col);
|
|
}
|
|
|
|
if (c == EOF) {
|
|
dcyyerror("This comment marker is unclosed.");
|
|
}
|
|
|
|
line_number = line;
|
|
col_number = col;
|
|
}
|
|
|
|
|
|
|
|
// accept() is called below as each piece is pulled off and
|
|
// accepted by the lexer; it increments the current column number.
|
|
inline void accept() {
|
|
col_number += yyleng;
|
|
}
|
|
|
|
%}
|
|
|
|
UNSIGNED_INTEGERNUM ([0-9]+)
|
|
SIGNED_INTEGERNUM ([+-]([0-9]+))
|
|
UNSIGNED_HEXNUM (0x[0-9a-fA-F]*)
|
|
REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
|
|
|
|
%%
|
|
|
|
%{
|
|
if (initial_token != 0) {
|
|
int t = initial_token;
|
|
initial_token = 0;
|
|
return t;
|
|
}
|
|
%}
|
|
|
|
\n.* {
|
|
// New line. Save a copy of the line so we can print it out for the
|
|
// benefit of the user in case we get an error.
|
|
|
|
strncpy(current_line, dcyytext+1, max_error_width);
|
|
current_line[max_error_width] = '\0';
|
|
line_number++;
|
|
col_number=0;
|
|
|
|
// Return the whole line to the lexer, except the newline character,
|
|
// which we eat.
|
|
yyless(1);
|
|
}
|
|
|
|
[ \t\r] {
|
|
// Eat whitespace.
|
|
accept();
|
|
}
|
|
|
|
"//".* {
|
|
// Eat C++-style comments.
|
|
accept();
|
|
}
|
|
|
|
"/*" {
|
|
// Eat C-style comments.
|
|
accept();
|
|
eat_c_comment();
|
|
}
|
|
|
|
|
|
"dclass" {
|
|
accept();
|
|
return KW_DCLASS;
|
|
}
|
|
|
|
"struct" {
|
|
accept();
|
|
return KW_STRUCT;
|
|
}
|
|
|
|
"from" {
|
|
accept();
|
|
return KW_FROM;
|
|
}
|
|
|
|
"import" {
|
|
accept();
|
|
return KW_IMPORT;
|
|
}
|
|
|
|
"typedef" {
|
|
accept();
|
|
return KW_TYPEDEF;
|
|
}
|
|
|
|
"switch" {
|
|
accept();
|
|
return KW_SWITCH;
|
|
}
|
|
|
|
"case" {
|
|
accept();
|
|
return KW_CASE;
|
|
}
|
|
|
|
"int8" {
|
|
accept();
|
|
return KW_INT8;
|
|
}
|
|
|
|
"int16" {
|
|
accept();
|
|
return KW_INT16;
|
|
}
|
|
|
|
"int32" {
|
|
accept();
|
|
return KW_INT32;
|
|
}
|
|
|
|
"int64" {
|
|
accept();
|
|
return KW_INT64;
|
|
}
|
|
|
|
"uint8" {
|
|
accept();
|
|
return KW_UINT8;
|
|
}
|
|
|
|
"uint16" {
|
|
accept();
|
|
return KW_UINT16;
|
|
}
|
|
|
|
"uint32" {
|
|
accept();
|
|
return KW_UINT32;
|
|
}
|
|
|
|
"uint64" {
|
|
accept();
|
|
return KW_UINT64;
|
|
}
|
|
|
|
"float64" {
|
|
accept();
|
|
return KW_FLOAT64;
|
|
}
|
|
|
|
"string" {
|
|
accept();
|
|
return KW_STRING;
|
|
}
|
|
|
|
"blob" {
|
|
accept();
|
|
return KW_BLOB;
|
|
}
|
|
|
|
"blob32" {
|
|
accept();
|
|
return KW_BLOB32;
|
|
}
|
|
|
|
"int8array" {
|
|
accept();
|
|
return KW_INT8ARRAY;
|
|
}
|
|
|
|
"int16array" {
|
|
accept();
|
|
return KW_INT16ARRAY;
|
|
}
|
|
|
|
"int32array" {
|
|
accept();
|
|
return KW_INT32ARRAY;
|
|
}
|
|
|
|
"uint8array" {
|
|
accept();
|
|
return KW_UINT8ARRAY;
|
|
}
|
|
|
|
"uint16array" {
|
|
accept();
|
|
return KW_UINT16ARRAY;
|
|
}
|
|
|
|
"uint32array" {
|
|
accept();
|
|
return KW_UINT32ARRAY;
|
|
}
|
|
|
|
"uint32uint8array" {
|
|
accept();
|
|
return KW_UINT32UINT8ARRAY;
|
|
}
|
|
|
|
"char" {
|
|
accept();
|
|
return KW_CHAR;
|
|
}
|
|
|
|
"required" {
|
|
accept();
|
|
return KW_REQUIRED;
|
|
}
|
|
|
|
"broadcast" {
|
|
accept();
|
|
return KW_BROADCAST;
|
|
}
|
|
|
|
"p2p" {
|
|
accept();
|
|
return KW_P2P;
|
|
}
|
|
|
|
"ram" {
|
|
accept();
|
|
return KW_RAM;
|
|
}
|
|
|
|
"db" {
|
|
accept();
|
|
return KW_DB;
|
|
}
|
|
|
|
"clsend" {
|
|
accept();
|
|
return KW_CLSEND;
|
|
}
|
|
|
|
"clrecv" {
|
|
accept();
|
|
return KW_CLRECV;
|
|
}
|
|
|
|
"ownsend" {
|
|
accept();
|
|
return KW_OWNSEND;
|
|
}
|
|
|
|
"airecv" {
|
|
accept();
|
|
return KW_AIRECV;
|
|
}
|
|
|
|
{UNSIGNED_INTEGERNUM} {
|
|
// An unsigned integer number.
|
|
accept();
|
|
|
|
// atoll isn't fully portable, so we'll decode the integer by hand.
|
|
dcyylval.str = dcyytext;
|
|
dcyylval.u.uint64 = 0;
|
|
const char *p = dcyytext;
|
|
while (*p != '\0') {
|
|
PN_uint64 next_value = dcyylval.u.uint64 * 10;
|
|
if (next_value < dcyylval.u.uint64) {
|
|
dcyyerror("Number out of range.");
|
|
dcyylval.u.uint64 = 1;
|
|
return UNSIGNED_INTEGER;
|
|
}
|
|
|
|
dcyylval.u.uint64 = next_value + (*p - '0');
|
|
++p;
|
|
}
|
|
|
|
return UNSIGNED_INTEGER;
|
|
}
|
|
|
|
{SIGNED_INTEGERNUM} {
|
|
// A signed integer number.
|
|
accept();
|
|
|
|
// atoll isn't fully portable, so we'll decode the integer by hand.
|
|
dcyylval.str = dcyytext;
|
|
|
|
bool neg = false;
|
|
const char *p = dcyytext;
|
|
if (*p == '-') {
|
|
neg = true;
|
|
++p;
|
|
} else if (*p == '+') {
|
|
++p;
|
|
}
|
|
|
|
PN_uint64 value = 0;
|
|
while (*p != '\0') {
|
|
PN_uint64 next_value = value * 10;
|
|
if (next_value < value) {
|
|
dcyyerror("Number out of range.");
|
|
dcyylval.u.int64 = 1;
|
|
return SIGNED_INTEGER;
|
|
}
|
|
|
|
value = next_value + (*p - '0');
|
|
++p;
|
|
}
|
|
|
|
if (neg) {
|
|
dcyylval.u.int64 = -(PN_int64)value;
|
|
if (dcyylval.u.int64 > 0) {
|
|
dcyyerror("Number out of range.");
|
|
dcyylval.u.int64 = 1;
|
|
}
|
|
} else {
|
|
dcyylval.u.int64 = (PN_int64)value;
|
|
if (dcyylval.u.int64 < 0) {
|
|
dcyyerror("Number out of range.");
|
|
dcyylval.u.int64 = 1;
|
|
}
|
|
}
|
|
|
|
return SIGNED_INTEGER;
|
|
}
|
|
|
|
{UNSIGNED_HEXNUM} {
|
|
// A hexadecimal integer number.
|
|
accept();
|
|
|
|
// As above, we'll decode the hex string by hand.
|
|
dcyylval.str = dcyytext;
|
|
dcyylval.u.uint64 = 0;
|
|
const char *p = dcyytext + 2;
|
|
while (*p != '\0') {
|
|
PN_uint64 next_value = dcyylval.u.uint64 * 16;
|
|
if (next_value < dcyylval.u.uint64) {
|
|
dcyyerror("Number out of range.");
|
|
dcyylval.u.uint64 = 1;
|
|
return UNSIGNED_INTEGER;
|
|
}
|
|
|
|
if (isalpha(*p)) {
|
|
dcyylval.u.uint64 = next_value + (tolower(*p) - 'a' + 10);
|
|
} else {
|
|
dcyylval.u.uint64 = next_value + (*p - '0');
|
|
}
|
|
++p;
|
|
}
|
|
|
|
return UNSIGNED_INTEGER;
|
|
}
|
|
|
|
{REALNUM} {
|
|
// A floating-point number.
|
|
accept();
|
|
dcyylval.u.real = atof(dcyytext);
|
|
dcyylval.str = dcyytext;
|
|
return REAL;
|
|
}
|
|
|
|
["] {
|
|
// Quoted string.
|
|
accept();
|
|
dcyylval.str = scan_quoted_string('"');
|
|
return STRING;
|
|
}
|
|
|
|
['] {
|
|
// Single-quoted string.
|
|
accept();
|
|
dcyylval.str = scan_quoted_string('\'');
|
|
return STRING;
|
|
}
|
|
|
|
[<] {
|
|
// Long hex string.
|
|
accept();
|
|
dcyylval.str = scan_hex_string();
|
|
return HEX_STRING;
|
|
}
|
|
|
|
[A-Za-z_][A-Za-z_0-9]* {
|
|
// Identifier.
|
|
accept();
|
|
dcyylval.str = dcyytext;
|
|
return IDENTIFIER;
|
|
}
|
|
|
|
|
|
. {
|
|
// Send any other printable character as itself.
|
|
accept();
|
|
return dcyytext[0];
|
|
}
|
|
|