757 lines
13 KiB
Plaintext
757 lines
13 KiB
Plaintext
/*
|
|
// Filename: lexer.l
|
|
// Created by: drose (16Jan99)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
*/
|
|
|
|
%{
|
|
#include "pandabase.h"
|
|
#include "lexerDefs.h"
|
|
#include "parserDefs.h"
|
|
#include "config_egg.h"
|
|
#include "parser.h"
|
|
#include "indent.h"
|
|
#include "pnotify.h"
|
|
#include "lightMutex.h"
|
|
#include "thread.h"
|
|
#include "pstrtod.h"
|
|
|
|
#include <math.h>
|
|
|
|
extern "C" int eggyywrap(void); // declared below.
|
|
|
|
static int yyinput(void); // declared by flex.
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Static variables
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
// This mutex protects all of these global variables.
|
|
LightMutex egg_lock;
|
|
|
|
// 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 egg file we're parsing. We keep it so we
|
|
// can print it out for error messages.
|
|
static string egg_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
|
|
egg_init_lexer(istream &in, const string &filename) {
|
|
inp = ∈
|
|
egg_filename = filename;
|
|
line_number = 0;
|
|
col_number = 0;
|
|
error_count = 0;
|
|
warning_count = 0;
|
|
initial_token = START_EGG;
|
|
}
|
|
|
|
void
|
|
egg_start_group_body() {
|
|
/* Set the initial state to begin within a group_body context,
|
|
instead of at the beginning of the egg file. */
|
|
initial_token = START_GROUP_BODY;
|
|
}
|
|
|
|
void
|
|
egg_start_texture_body() {
|
|
initial_token = START_TEXTURE_BODY;
|
|
}
|
|
|
|
void
|
|
egg_start_primitive_body() {
|
|
initial_token = START_PRIMITIVE_BODY;
|
|
}
|
|
|
|
int
|
|
egg_error_count() {
|
|
return error_count;
|
|
}
|
|
|
|
int
|
|
egg_warning_count() {
|
|
return warning_count;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Internal support functions.
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
int
|
|
eggyywrap(void) {
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
eggyyerror(const string &msg) {
|
|
if (egg_cat.is_error()) {
|
|
ostream &out = egg_cat.error(false);
|
|
|
|
out << "\nError";
|
|
if (!egg_filename.empty()) {
|
|
out << " in " << egg_filename;
|
|
}
|
|
out
|
|
<< " at line " << line_number << ", column " << col_number << ":\n"
|
|
<< setiosflags(Notify::get_literal_flag())
|
|
<< current_line << "\n";
|
|
indent(out, col_number-1)
|
|
<< "^\n" << msg << "\n\n"
|
|
<< resetiosflags(Notify::get_literal_flag()) << flush;
|
|
}
|
|
error_count++;
|
|
}
|
|
|
|
void
|
|
eggyyerror(ostringstream &strm) {
|
|
string s = strm.str();
|
|
eggyyerror(s);
|
|
}
|
|
|
|
void
|
|
eggyywarning(const string &msg) {
|
|
if (egg_cat.is_warning()) {
|
|
ostream &out = egg_cat.warning(false);
|
|
|
|
out << "\nWarning";
|
|
if (!egg_filename.empty()) {
|
|
out << " in " << egg_filename;
|
|
}
|
|
out
|
|
<< " at line " << line_number << ", column " << col_number << ":\n"
|
|
<< setiosflags(Notify::get_literal_flag())
|
|
<< current_line << "\n";
|
|
indent(out, col_number-1)
|
|
<< "^\n" << msg << "\n\n"
|
|
<< resetiosflags(Notify::get_literal_flag()) << flush;
|
|
}
|
|
warning_count++;
|
|
}
|
|
|
|
void
|
|
eggyywarning(ostringstream &strm) {
|
|
string s = strm.str();
|
|
eggyywarning(s);
|
|
}
|
|
|
|
// 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 (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.
|
|
int length = min(max_error_width, result);
|
|
strncpy(current_line, buffer, length);
|
|
current_line[length] = '\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;
|
|
}
|
|
Thread::consider_yield();
|
|
}
|
|
#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() {
|
|
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 != '"' && c != EOF) {
|
|
result += c;
|
|
c = read_char(line, col);
|
|
}
|
|
|
|
if (c == EOF) {
|
|
eggyyerror("This quotation mark is unterminated.");
|
|
}
|
|
|
|
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 == '*') {
|
|
ostringstream errmsg;
|
|
errmsg << "This comment contains a nested /* symbol at line "
|
|
<< line << ", column " << col-1 << "--possibly unclosed?"
|
|
<< ends;
|
|
eggyywarning(errmsg);
|
|
}
|
|
last_c = c;
|
|
c = read_char(line, col);
|
|
}
|
|
|
|
if (c == EOF) {
|
|
eggyyerror("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;
|
|
}
|
|
|
|
%}
|
|
|
|
HEX 0x[0-9a-fA-F]*
|
|
BINARY 0b[01]*
|
|
NUMERIC ([+-]?(([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, yytext+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();
|
|
}
|
|
|
|
[{}] {
|
|
// Send curly braces as themselves.
|
|
accept();
|
|
return eggyytext[0];
|
|
}
|
|
|
|
|
|
|
|
"<ANIMPRELOAD>" {
|
|
accept();
|
|
return ANIMPRELOAD;
|
|
}
|
|
"<BEZIERCURVE>" {
|
|
accept();
|
|
return BEZIERCURVE;
|
|
}
|
|
"<BFACE>" {
|
|
accept();
|
|
return BFACE;
|
|
}
|
|
"<BILLBOARD>" {
|
|
accept();
|
|
return BILLBOARD;
|
|
}
|
|
"<BILLBOARDCENTER>" {
|
|
accept();
|
|
return BILLBOARDCENTER;
|
|
}
|
|
"<BINORMAL>" {
|
|
accept();
|
|
return BINORMAL;
|
|
}
|
|
"<BUNDLE>" {
|
|
accept();
|
|
return BUNDLE;
|
|
}
|
|
"<CHAR*>" {
|
|
accept();
|
|
return SCALAR;
|
|
}
|
|
"<CLOSED>" {
|
|
accept();
|
|
return CLOSED;
|
|
}
|
|
"<COLLIDE>" {
|
|
accept();
|
|
return COLLIDE;
|
|
}
|
|
"<COMMENT>" {
|
|
accept();
|
|
return COMMENT;
|
|
}
|
|
"<COMPONENT>" {
|
|
accept();
|
|
return COMPONENT;
|
|
}
|
|
"<COORDINATESYSTEM>" {
|
|
accept();
|
|
return COORDSYSTEM;
|
|
}
|
|
"<CV>" {
|
|
accept();
|
|
return CV;
|
|
}
|
|
"<DART>" {
|
|
accept();
|
|
return DART;
|
|
}
|
|
"<DNORMAL>" {
|
|
accept();
|
|
return DNORMAL;
|
|
}
|
|
"<DRGBA>" {
|
|
accept();
|
|
return DRGBA;
|
|
}
|
|
"<DUV>" {
|
|
accept();
|
|
return DUV;
|
|
}
|
|
"<DXYZ>" {
|
|
accept();
|
|
return DXYZ;
|
|
}
|
|
"<DCS>" {
|
|
accept();
|
|
return DCS;
|
|
}
|
|
"<DISTANCE>" {
|
|
accept();
|
|
return DISTANCE;
|
|
}
|
|
"<DTREF>" {
|
|
accept();
|
|
return DTREF;
|
|
}
|
|
"<DYNAMICVERTEXPOOL>" {
|
|
accept();
|
|
return DYNAMICVERTEXPOOL;
|
|
}
|
|
"<FILE>" {
|
|
accept();
|
|
return EXTERNAL_FILE;
|
|
}
|
|
"<GROUP>" {
|
|
accept();
|
|
return GROUP;
|
|
}
|
|
"<DEFAULTPOSE>" {
|
|
accept();
|
|
return DEFAULTPOSE;
|
|
}
|
|
"<JOINT>" {
|
|
accept();
|
|
return JOINT;
|
|
}
|
|
"<KNOTS>" {
|
|
accept();
|
|
return KNOTS;
|
|
}
|
|
"<INCLUDE>" {
|
|
accept();
|
|
return INCLUDE;
|
|
}
|
|
"<INSTANCE>" {
|
|
accept();
|
|
return INSTANCE;
|
|
}
|
|
"<LINE>" {
|
|
accept();
|
|
return LINE;
|
|
}
|
|
"<LOOP>" {
|
|
accept();
|
|
return LOOP;
|
|
}
|
|
"<MATERIAL>" {
|
|
accept();
|
|
return MATERIAL;
|
|
}
|
|
"<MATRIX3>" {
|
|
accept();
|
|
return MATRIX3;
|
|
}
|
|
"<MATRIX4>" {
|
|
accept();
|
|
return MATRIX4;
|
|
}
|
|
"<MODEL>" {
|
|
accept();
|
|
return MODEL;
|
|
}
|
|
"<MREF>" {
|
|
accept();
|
|
return MREF;
|
|
}
|
|
"<NORMAL>" {
|
|
accept();
|
|
return NORMAL;
|
|
}
|
|
"<NURBSCURVE>" {
|
|
accept();
|
|
return NURBSCURVE;
|
|
}
|
|
"<NURBSSURFACE>" {
|
|
accept();
|
|
return NURBSSURFACE;
|
|
}
|
|
"<OBJECTTYPE>" {
|
|
accept();
|
|
return OBJECTTYPE;
|
|
}
|
|
"<ORDER>" {
|
|
accept();
|
|
return ORDER;
|
|
}
|
|
"<OUTTANGENT>" {
|
|
accept();
|
|
return OUTTANGENT;
|
|
}
|
|
"<POINTLIGHT>" {
|
|
accept();
|
|
return POINTLIGHT;
|
|
}
|
|
"<POLYGON>" {
|
|
accept();
|
|
return POLYGON;
|
|
}
|
|
"<REF>" {
|
|
accept();
|
|
return REF;
|
|
}
|
|
"<RGBA>" {
|
|
accept();
|
|
return RGBA;
|
|
}
|
|
"<ROTATE>" {
|
|
accept();
|
|
return ROTATE;
|
|
}
|
|
"<ROTX>" {
|
|
accept();
|
|
return ROTX;
|
|
}
|
|
"<ROTY>" {
|
|
accept();
|
|
return ROTY;
|
|
}
|
|
"<ROTZ>" {
|
|
accept();
|
|
return ROTZ;
|
|
}
|
|
"<S$ANIM>" {
|
|
accept();
|
|
return SANIM;
|
|
}
|
|
"<SCALAR>" {
|
|
accept();
|
|
return SCALAR;
|
|
}
|
|
"<SCALE>" {
|
|
accept();
|
|
return SCALE;
|
|
}
|
|
"<SEQUENCE>" {
|
|
accept();
|
|
return SEQUENCE;
|
|
}
|
|
"<SHADING>" {
|
|
accept();
|
|
return SHADING;
|
|
}
|
|
"<SWITCH>" {
|
|
accept();
|
|
return SWITCH;
|
|
}
|
|
"<SWITCHCONDITION>" {
|
|
accept();
|
|
return SWITCHCONDITION;
|
|
}
|
|
"<TABLE>" {
|
|
accept();
|
|
return TABLE;
|
|
}
|
|
"<V>" {
|
|
accept();
|
|
return TABLE_V;
|
|
}
|
|
"<TAG>" {
|
|
accept();
|
|
return TAG;
|
|
}
|
|
"<TANGENT>" {
|
|
accept();
|
|
return TANGENT;
|
|
}
|
|
"<TEXLIST>" {
|
|
accept();
|
|
return TEXLIST;
|
|
}
|
|
"<TEXTURE>" {
|
|
accept();
|
|
return TEXTURE;
|
|
}
|
|
"<TLENGTHS>" {
|
|
accept();
|
|
return TLENGTHS;
|
|
}
|
|
"<TRANSFORM>" {
|
|
accept();
|
|
return TRANSFORM;
|
|
}
|
|
"<TRANSLATE>" {
|
|
accept();
|
|
return TRANSLATE;
|
|
}
|
|
"<TREF>" {
|
|
accept();
|
|
return TREF;
|
|
}
|
|
"<TRIANGLEFAN>" {
|
|
accept();
|
|
return TRIANGLEFAN;
|
|
}
|
|
"<TRIANGLESTRIP>" {
|
|
accept();
|
|
return TRIANGLESTRIP;
|
|
}
|
|
"<TRIM>" {
|
|
accept();
|
|
return TRIM;
|
|
}
|
|
"<TXT>" {
|
|
accept();
|
|
return TXT;
|
|
}
|
|
"<U-KNOTS>" {
|
|
accept();
|
|
return UKNOTS;
|
|
}
|
|
"<U_KNOTS>" {
|
|
accept();
|
|
return UKNOTS;
|
|
}
|
|
"<UV>" {
|
|
accept();
|
|
return UV;
|
|
}
|
|
"<V-KNOTS>" {
|
|
accept();
|
|
return VKNOTS;
|
|
}
|
|
"<V_KNOTS>" {
|
|
accept();
|
|
return VKNOTS;
|
|
}
|
|
"<VERTEX>" {
|
|
accept();
|
|
return VERTEX;
|
|
}
|
|
"<VERTEXANIM>" {
|
|
accept();
|
|
return VERTEXANIM;
|
|
}
|
|
"<VERTEXPOOL>" {
|
|
accept();
|
|
return VERTEXPOOL;
|
|
}
|
|
"<VERTEXREF>" {
|
|
accept();
|
|
return VERTEXREF;
|
|
}
|
|
"<XFM$ANIM>" {
|
|
accept();
|
|
return XFMANIM;
|
|
}
|
|
"<XFM$ANIM_S$>" {
|
|
accept();
|
|
return XFMSANIM;
|
|
}
|
|
|
|
|
|
|
|
{NUMERIC} {
|
|
// An integer or floating-point number.
|
|
accept();
|
|
eggyylval._number = patof(eggyytext);
|
|
eggyylval._string = yytext;
|
|
return EGG_NUMBER;
|
|
}
|
|
|
|
{HEX} {
|
|
// A hexadecimal integer number.
|
|
accept();
|
|
eggyylval._ulong = strtoul(yytext+2, NULL, 16);
|
|
eggyylval._string = yytext;
|
|
return EGG_ULONG;
|
|
}
|
|
|
|
{BINARY} {
|
|
// A binary integer number.
|
|
accept();
|
|
eggyylval._ulong = strtoul(yytext+2, NULL, 2);
|
|
eggyylval._string = yytext;
|
|
return EGG_ULONG;
|
|
}
|
|
|
|
"nan"{HEX} {
|
|
// not-a-number. These sometimes show up in egg files accidentally.
|
|
accept();
|
|
memset(&eggyylval._number, 0, sizeof(eggyylval._number));
|
|
*(unsigned long *)&eggyylval._number = strtoul(yytext+3, NULL, 0);
|
|
eggyylval._string = yytext;
|
|
return EGG_NUMBER;
|
|
}
|
|
|
|
"inf" {
|
|
// infinity. As above.
|
|
accept();
|
|
eggyylval._number = HUGE_VAL;
|
|
eggyylval._string = yytext;
|
|
return EGG_NUMBER;
|
|
}
|
|
|
|
"-inf" {
|
|
// minus infinity. As above.
|
|
accept();
|
|
eggyylval._number = -HUGE_VAL;
|
|
eggyylval._string = yytext;
|
|
return EGG_NUMBER;
|
|
}
|
|
|
|
"1.#inf" {
|
|
// infinity, on Win32. As above.
|
|
accept();
|
|
eggyylval._number = HUGE_VAL;
|
|
eggyylval._string = yytext;
|
|
return EGG_NUMBER;
|
|
}
|
|
|
|
"-1.#inf" {
|
|
// minus infinity, on Win32. As above.
|
|
accept();
|
|
eggyylval._number = -HUGE_VAL;
|
|
eggyylval._string = yytext;
|
|
return EGG_NUMBER;
|
|
}
|
|
|
|
|
|
["] {
|
|
// Quoted string.
|
|
accept();
|
|
eggyylval._string = scan_quoted_string();
|
|
return EGG_STRING;
|
|
}
|
|
|
|
[^ \t\n\r{}"]+ {
|
|
// Unquoted string.
|
|
accept();
|
|
eggyylval._string = yytext;
|
|
return EGG_STRING;
|
|
}
|