mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-12-18 00:21:31 +00:00
648 lines
14 KiB
Text
648 lines
14 KiB
Text
%{
|
|
/*
|
|
qc-lex.l
|
|
|
|
lexer for quakec
|
|
|
|
Copyright (C) 2001 Bill Currie <bill@taniwha.org>
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
Date: 2001/06/12
|
|
|
|
This program 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 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program 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, write to:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
static __attribute__ ((unused)) const char rcsid[] =
|
|
"$Id$";
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
|
|
#include <QF/dstring.h>
|
|
#include <QF/hash.h>
|
|
#include <QF/sys.h>
|
|
|
|
#include "qfcc.h"
|
|
#include "expr.h"
|
|
#include "class.h"
|
|
#include "immediate.h"
|
|
#include "options.h"
|
|
#include "struct.h"
|
|
#include "type.h"
|
|
#include "qc-parse.h"
|
|
|
|
#ifndef YY_PROTO
|
|
# define YY_PROTO(x) x
|
|
#else
|
|
# define YY_FLEX_REALLOC_HACK
|
|
#endif
|
|
int yyget_lineno (void);
|
|
FILE *yyget_in (void);
|
|
FILE *yyget_out (void);
|
|
int yyget_leng (void);
|
|
char *yyget_text (void);
|
|
void yyset_lineno (int line_number);
|
|
void yyset_in (FILE * in_str);
|
|
void yyset_out (FILE * out_str);
|
|
int yyget_debug (void);
|
|
void yyset_debug (int bdebug);
|
|
int yylex_destroy (void);
|
|
|
|
#define YY_NO_UNPUT
|
|
#define YY_DECL int yylex YY_PROTO(( void ))
|
|
YY_DECL;
|
|
|
|
|
|
int type_or_name (char *token);
|
|
|
|
int do_grab (char *token);
|
|
|
|
void add_frame_macro (char *token);
|
|
|
|
const char *make_string (char *token);
|
|
|
|
extern YYSTYPE yylval;
|
|
extern int element_flag;
|
|
|
|
%}
|
|
|
|
DIGIT [0-9]
|
|
XDIGIT [0-9a-fA-F]
|
|
ID [a-zA-Z_][a-zA-Z_0-9]*
|
|
FLOAT {DIGIT}+"."{DIGIT}*
|
|
NUM ({DIGIT}+("."{DIGIT}*)?)
|
|
s [ \t]
|
|
m ([\-+]?)
|
|
|
|
%x grab_frame grab_other
|
|
|
|
%%
|
|
|
|
"/*" {
|
|
int c;
|
|
do {
|
|
while ((c = input ()) != '*' && c != EOF
|
|
&& c != '\n')
|
|
;
|
|
while (c == '*')
|
|
c = input ();
|
|
if (c == EOF)
|
|
error (0, "EOF in comment");
|
|
if (c == '\n')
|
|
pr.source_line++;
|
|
} while (c != '/' && c != EOF);
|
|
}
|
|
|
|
"//".* /* nothing to do */
|
|
|
|
{DIGIT}+ {
|
|
yylval.integer_val = atoi (yytext);
|
|
return INT_VAL;
|
|
}
|
|
|
|
0[xX]{XDIGIT}+ {
|
|
const char *c = yytext + 2;
|
|
yylval.integer_val = 0;
|
|
while (*c) {
|
|
yylval.integer_val *= 16;
|
|
yylval.integer_val += *c - '0';
|
|
if (*c > '9')
|
|
yylval.integer_val -= 'A' - '9' - 1;
|
|
if (*c > 'F')
|
|
yylval.integer_val -= 'a' - 'A';
|
|
c++;
|
|
}
|
|
return INT_VAL;
|
|
}
|
|
|
|
{FLOAT}* {
|
|
yylval.float_val = atof (yytext);
|
|
return FLOAT_VAL;
|
|
}
|
|
|
|
{ID} return type_or_name(yytext);
|
|
@{ID} {
|
|
int tok = type_or_name(yytext);
|
|
if (tok == '@')
|
|
REJECT;
|
|
return tok;
|
|
}
|
|
@ return '@';
|
|
|
|
\"(\\.|[^"\\])*\" {
|
|
yylval.string_val = make_string (yytext);
|
|
return STRING_VAL;
|
|
}
|
|
|
|
'{s}*{m}{NUM}{s}+{m}{NUM}{s}+{m}{NUM}{s}*' {
|
|
sscanf (yytext, "' %f %f %f '",
|
|
&yylval.vector_val[0], &yylval.vector_val[1],
|
|
&yylval.vector_val[2]);
|
|
return VECTOR_VAL;
|
|
}
|
|
|
|
'{s}*{m}{NUM}{s}+{m}{NUM}{s}+{m}{NUM}{s}+{m}{NUM}{s}*' {
|
|
sscanf (yytext, "' %f %f %f %f '",
|
|
&yylval.vector_val[0], &yylval.vector_val[1],
|
|
&yylval.vector_val[2], &yylval.vector_val[3]);
|
|
return QUATERNION_VAL;
|
|
}
|
|
|
|
'(\\[^xX0-7\r\n]|[^'\r\n]|\\[xX][0-9A-Fa-f]+|\\[0-7]+)*' {
|
|
const char *str = make_string (yytext);
|
|
|
|
if (str[1])
|
|
warning (0, "multibyte char constant");
|
|
yylval.integer_val = *str;
|
|
return INT_VAL;
|
|
}
|
|
|
|
^#{s}+{DIGIT}+{s}+\"(\.|[^"\n])*\".*$ {
|
|
char *p;
|
|
char *s;
|
|
const char *str;
|
|
int line;
|
|
|
|
p = yytext + 1;
|
|
line = strtol (p, &s, 10);
|
|
p = s;
|
|
while (isspace ((unsigned char)*p))
|
|
p++;
|
|
if (!*p)
|
|
error (0, "Unexpected end of file");
|
|
str = make_string (p); // grab the filename
|
|
while (*p && *p != '\n') // ignore flags
|
|
p++;
|
|
pr.source_line = line - 1;
|
|
pr.source_file = ReuseString (strip_path (str));
|
|
}
|
|
|
|
[+\-*/&|^%]= {
|
|
yylval.op = yytext[0];
|
|
return ASX;
|
|
}
|
|
|
|
"<<=" {
|
|
yylval.op = SHL;
|
|
return ASX;
|
|
}
|
|
|
|
">>=" {
|
|
yylval.op = SHR;
|
|
return ASX;
|
|
}
|
|
|
|
[!(){}.*/&|^~+\-=\[\];,#%?:] {
|
|
if (yytext[0] == '{' && element_flag) {
|
|
element_flag = 0;
|
|
return ELE_START;
|
|
}
|
|
return yytext[0];
|
|
}
|
|
|
|
"..." return ELLIPSIS;
|
|
|
|
"<<" return SHL;
|
|
">>" return SHR;
|
|
|
|
"&&" return AND;
|
|
"||" return OR;
|
|
"==" return EQ;
|
|
"!=" return NE;
|
|
"<=" return LE;
|
|
">=" return GE;
|
|
"<" return LT;
|
|
">" return GT;
|
|
|
|
"++" {
|
|
yylval.op = '+';
|
|
return INCOP;
|
|
}
|
|
|
|
"--" {
|
|
yylval.op = '-';
|
|
return INCOP;
|
|
}
|
|
|
|
"$"{s}*{ID} {
|
|
int ret = do_grab(yytext);
|
|
if (ret > 0)
|
|
return ret;
|
|
else
|
|
BEGIN (-ret);
|
|
}
|
|
|
|
<grab_frame>{ID} add_frame_macro (yytext);
|
|
|
|
<grab_other>[^\r\n]* /* skip */
|
|
|
|
<*>\r*\n {
|
|
pr.source_line++;
|
|
BEGIN (INITIAL);
|
|
}
|
|
|
|
<*>{s}* /* skip */
|
|
|
|
<*>. error (0, "all your typo are belong to us");
|
|
|
|
%%
|
|
|
|
int
|
|
yywrap (void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
int value;
|
|
type_t *type;
|
|
unsigned int traditional;
|
|
unsigned int version;
|
|
} keyword_t;
|
|
|
|
static keyword_t keywords[] = {
|
|
{"void", TYPE, &type_void, 1, PROG_ID_VERSION},
|
|
{"float", TYPE, &type_float, 1, PROG_ID_VERSION},
|
|
{"string", TYPE, &type_string, 1, PROG_ID_VERSION},
|
|
{"vector", TYPE, &type_vector, 1, PROG_ID_VERSION},
|
|
{"entity", TYPE, &type_entity, 1, PROG_ID_VERSION},
|
|
{"quaternion", TYPE, &type_quaternion, 0, PROG_VERSION},
|
|
{"integer", TYPE, &type_integer, 0, PROG_VERSION},
|
|
{"unsigned", TYPE, &type_uinteger, 0, PROG_VERSION},
|
|
{"function", TYPE, &type_function, 0, PROG_VERSION},
|
|
{"id", TYPE, &type_id, 0, PROG_VERSION},
|
|
{"Class", TYPE, &type_Class, 0, PROG_VERSION},
|
|
{"Protocol", TYPE, &type_Protocol, 0, PROG_VERSION},
|
|
{"Method", TYPE, &type_Method, 0, PROG_VERSION},
|
|
{"Super", TYPE, &type_Super, 0, PROG_VERSION},
|
|
{"SEL", TYPE, &type_SEL, 0, PROG_VERSION},
|
|
{"IMP", TYPE, &type_IMP, 0, PROG_VERSION},
|
|
{"local", LOCAL, 0, 1, PROG_ID_VERSION},
|
|
{"return", RETURN, 0, 1, PROG_ID_VERSION},
|
|
{"while", WHILE, 0, 1, PROG_ID_VERSION},
|
|
{"do", DO, 0, 1, PROG_ID_VERSION},
|
|
{"if", IF, 0, 1, PROG_ID_VERSION},
|
|
{"else", ELSE, 0, 1, PROG_ID_VERSION},
|
|
{"for", FOR, 0, 0, PROG_ID_VERSION},
|
|
{"break", BREAK, 0, 0, PROG_ID_VERSION},
|
|
{"continue", CONTINUE, 0, 0, PROG_ID_VERSION},
|
|
{"switch", SWITCH, 0, 0, PROG_ID_VERSION},
|
|
{"case", CASE, 0, 0, PROG_ID_VERSION},
|
|
{"default", DEFAULT, 0, 0, PROG_ID_VERSION},
|
|
{"NIL", NIL, 0, 0, PROG_ID_VERSION},
|
|
{"struct", STRUCT, 0, 0, PROG_VERSION},
|
|
{"union", UNION, 0, 0, PROG_VERSION},
|
|
{"enum", ENUM, 0, 0, PROG_ID_VERSION},
|
|
{"typedef", TYPEDEF, 0, 0, PROG_ID_VERSION},
|
|
{"super", SUPER, 0, 0, PROG_VERSION},
|
|
|
|
{"@class", CLASS, 0, 0, PROG_VERSION},
|
|
{"@defs", DEFS, 0, 0, PROG_VERSION},
|
|
{"@encode", ENCODE, 0, 0, PROG_VERSION},
|
|
{"@end", END, 0, 0, PROG_VERSION},
|
|
{"@implementation", IMPLEMENTATION, 0, 0, PROG_VERSION},
|
|
{"@interface", INTERFACE, 0, 0, PROG_VERSION},
|
|
{"@private", PRIVATE, 0, 0, PROG_VERSION},
|
|
{"@protected", PROTECTED, 0, 0, PROG_VERSION},
|
|
{"@protocol", PROTOCOL, 0, 0, PROG_VERSION},
|
|
{"@public", PUBLIC, 0, 0, PROG_VERSION},
|
|
{"@selector", SELECTOR, 0, 0, PROG_VERSION},
|
|
{"@self", SELF, 0, 0, PROG_VERSION},
|
|
{"@this", THIS, 0, 0, PROG_VERSION},
|
|
{"@args", ARGS, 0, 0, PROG_VERSION},
|
|
{"@argc", ARGC, 0, 0, PROG_VERSION},
|
|
{"@argv", ARGV, 0, 0, PROG_VERSION},
|
|
{"@va_list", TYPE, &type_va_list, 0, PROG_VERSION},
|
|
{"@extern", EXTERN, 0, 1, PROG_ID_VERSION},
|
|
{"@static", STATIC, 0, 1, PROG_ID_VERSION},
|
|
{"@system", SYSTEM, 0, 1, PROG_ID_VERSION},
|
|
{"@sizeof", SIZEOF, 0, 0, PROG_VERSION},
|
|
};
|
|
|
|
static const char *
|
|
keyword_get_key (void *kw, void *unused)
|
|
{
|
|
return ((keyword_t*)kw)->name;
|
|
}
|
|
|
|
int
|
|
type_or_name (char *token)
|
|
{
|
|
static hashtab_t *keyword_tab;
|
|
keyword_t *keyword;
|
|
type_t *type;
|
|
class_t *class;
|
|
|
|
if (!keyword_tab) {
|
|
unsigned int i;
|
|
keyword_tab = Hash_NewTable (1021, keyword_get_key, 0, 0);
|
|
for (i = 0; i < sizeof (keywords) / sizeof (keywords[0]); i++)
|
|
if (keywords[i].traditional >= options.traditional
|
|
&& keywords[i].version <= options.code.progsversion)
|
|
Hash_Add (keyword_tab, &keywords[i]);
|
|
}
|
|
keyword = Hash_Find (keyword_tab, token);
|
|
if (keyword) {
|
|
if (token[0] == '@' && !class_Class.super_class)
|
|
class_init ();
|
|
yylval.type = keyword->type;
|
|
return keyword->value;
|
|
}
|
|
if (token[0] == '@') {
|
|
return '@';
|
|
}
|
|
if ((type = get_typedef (token))) {
|
|
yylval.type = type;
|
|
return TYPE;
|
|
}
|
|
if ((class = get_class (token, 0))) {
|
|
yylval.string_val = save_string (token);
|
|
return CLASS_NAME;
|
|
}
|
|
yylval.string_val = save_string (token);
|
|
return NAME;
|
|
}
|
|
|
|
static hashtab_t *frame_tab;
|
|
static hashtab_t *grab_tab;
|
|
|
|
typedef struct frame_s {
|
|
struct frame_s *next;
|
|
const char *name;
|
|
int num;
|
|
} frame_t;
|
|
|
|
static frame_t *free_frames;
|
|
|
|
static frame_t grab_list[] = {
|
|
{0, "cd", 0},
|
|
{0, "origin", 0},
|
|
{0, "base", 0},
|
|
{0, "flags", 0},
|
|
{0, "scale", 0},
|
|
{0, "skin", 0},
|
|
};
|
|
|
|
static const char *
|
|
frame_get_key (void *f, void *unused)
|
|
{
|
|
return ((frame_t*)f)->name;
|
|
}
|
|
|
|
static void
|
|
frame_free (void *_f, void *unused)
|
|
{
|
|
frame_t *f = (frame_t *)_f;
|
|
f->next = free_frames;
|
|
free_frames = f;
|
|
}
|
|
|
|
int
|
|
do_grab (char *token)
|
|
{
|
|
static int initialized;
|
|
frame_t *frame;
|
|
|
|
if (!initialized) {
|
|
unsigned int i;
|
|
|
|
initialized = 1;
|
|
frame_tab = Hash_NewTable (1021, frame_get_key, frame_free, 0);
|
|
grab_tab = Hash_NewTable (1021, frame_get_key, 0, 0);
|
|
for (i = 0; i < sizeof (grab_list) / sizeof (grab_list[0]); i++)
|
|
Hash_Add (grab_tab, &grab_list[i]);
|
|
}
|
|
while (isspace ((unsigned char)*++token))
|
|
// advance over $ and leading space
|
|
;
|
|
if (!strcmp (token, "frame"))
|
|
return -grab_frame;
|
|
if (Hash_Find (grab_tab, token))
|
|
return -grab_other;
|
|
frame = Hash_Find (frame_tab, token);
|
|
if (frame) {
|
|
yylval.integer_val = frame->num;
|
|
return INT_VAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int frame_number;
|
|
|
|
void
|
|
add_frame_macro (char *token)
|
|
{
|
|
frame_t *frame;
|
|
ALLOC (1024, frame_t, frames, frame);
|
|
|
|
frame->name = save_string (token);
|
|
frame->num = frame_number++;
|
|
Hash_Add (frame_tab, frame);
|
|
}
|
|
|
|
void
|
|
clear_frame_macros (void)
|
|
{
|
|
frame_number = 0;
|
|
if (frame_tab)
|
|
Hash_FlushTable (frame_tab);
|
|
}
|
|
|
|
const char *
|
|
make_string (char *token)
|
|
{
|
|
char s[2];
|
|
int c;
|
|
int i;
|
|
int mask;
|
|
int boldnext;
|
|
int quote;
|
|
static dstring_t *str;
|
|
|
|
if (!str)
|
|
str = dstring_newstr ();
|
|
dstring_clearstr (str);
|
|
|
|
s[1] = 0;
|
|
|
|
mask = 0x00;
|
|
boldnext = 0;
|
|
|
|
quote = *token++;
|
|
do {
|
|
c = *token++;
|
|
if (!c)
|
|
error (0, "EOF inside quote");
|
|
if (c == '\n')
|
|
error (0, "newline inside quote");
|
|
if (c == '\\') { // escape char
|
|
c = *token++;
|
|
if (!c)
|
|
error (0, "EOF inside quote");
|
|
switch (c) {
|
|
case '\\':
|
|
c = '\\';
|
|
break;
|
|
case 'n':
|
|
c = '\n';
|
|
break;
|
|
case '"':
|
|
c = '\"';
|
|
break;
|
|
case '\'':
|
|
c = '\'';
|
|
break;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
for (i = c = 0; i < 3
|
|
&& *token >= '0'
|
|
&& *token <= '7'; i++, token++) {
|
|
c *= 8;
|
|
c += *token - '0';
|
|
}
|
|
if (!*token)
|
|
error (0, "EOF inside quote");
|
|
break;
|
|
case 'x':
|
|
c = 0;
|
|
while (*token && isxdigit ((unsigned char)*token)) {
|
|
c *= 16;
|
|
if (*token <= '9')
|
|
c += *token - '0';
|
|
else if (*token <= 'F')
|
|
c += *token - 'A' + 10;
|
|
else
|
|
c += *token - 'a' + 10;
|
|
token++;
|
|
}
|
|
if (!*token)
|
|
error (0, "EOF inside quote");
|
|
break;
|
|
case 'a':
|
|
c = '\a';
|
|
break;
|
|
case 'b':
|
|
//XXX mask ^= 0x80;
|
|
c = '\b';
|
|
break;
|
|
case 'e':
|
|
c = '\033';
|
|
break;
|
|
case 'f':
|
|
c = '\f';
|
|
break;
|
|
case 'r':
|
|
c = '\r';
|
|
break;
|
|
case 't':
|
|
c = '\t';
|
|
break;
|
|
case 'v':
|
|
c = '\v';
|
|
break;
|
|
case '^':
|
|
if (*token == '\"')
|
|
error (0, "Unexpected end of string after \\^");
|
|
boldnext = 1;
|
|
continue;
|
|
case '[':
|
|
c = 0x90;
|
|
break;
|
|
case ']':
|
|
c = 0x91;
|
|
break;
|
|
case '.':
|
|
c = 28;
|
|
break;
|
|
case '<':
|
|
//XXX c = 29;
|
|
mask = 0x80;
|
|
continue;
|
|
case '-':
|
|
c = 30;
|
|
break;
|
|
case '>':
|
|
//XXX c = 31;
|
|
mask = 0x00;
|
|
continue;
|
|
case '(':
|
|
c = 128;
|
|
break;
|
|
case '=':
|
|
c = 129;
|
|
break;
|
|
case ')':
|
|
c = 130;
|
|
break;
|
|
case '{':
|
|
c = 0;
|
|
while (*token && *token != '}'
|
|
&& isdigit ((unsigned char)*token)) {
|
|
c *= 10;
|
|
c += *token - '0';
|
|
}
|
|
if (!*token)
|
|
error (0, "EOF inside quote");
|
|
if (*token != '}')
|
|
error (0, "non-digit inside \\{}");
|
|
else
|
|
token++;
|
|
if (c > 255)
|
|
warning (0, "\\{%d} > 255", c);
|
|
break;
|
|
default:
|
|
error (0, "Unknown escape char");
|
|
break;
|
|
}
|
|
} else if (c == quote) {
|
|
break;;
|
|
}
|
|
if (boldnext)
|
|
c = c ^ 0x80;
|
|
boldnext = 0;
|
|
c = c ^ mask;
|
|
s[0] = c;
|
|
dstring_appendstr (str, s);
|
|
} while (1);
|
|
|
|
return save_string (str->str);
|
|
}
|
|
|
|
#ifdef YY_FLEX_REALLOC_HACK
|
|
static void *(*const yy_flex_realloc_hack)(void *,yy_size_t) = yy_flex_realloc;
|
|
#else
|
|
static __attribute__ ((unused)) void (*yyunput_hack)(int, char*) = yyunput;
|
|
#endif
|