quakeforge/tools/qfcc/source/qc-lex.l
2022-04-29 18:12:47 +09:00

726 lines
18 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
#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 "tools/qfcc/include/class.h"
#include "tools/qfcc/include/debug.h"
#include "tools/qfcc/include/diagnostic.h"
#include "tools/qfcc/include/expr.h"
#include "tools/qfcc/include/grab.h"
#include "tools/qfcc/include/options.h"
#include "tools/qfcc/include/pragma.h"
#include "tools/qfcc/include/qfcc.h"
#include "tools/qfcc/include/shared.h"
#include "tools/qfcc/include/strpool.h"
#include "tools/qfcc/include/struct.h"
#include "tools/qfcc/include/symtab.h"
#include "tools/qfcc/include/type.h"
#include "tools/qfcc/include/value.h"
#include "tools/qfcc/source/qc-parse.h"
#ifndef YY_PROTO
# define YY_PROTO(x) x
#else
# define YY_FLEX_REALLOC_HACK
#endif
#define YY_NO_UNPUT
#define YY_DECL int yylex YY_PROTO(( void ))
YY_DECL;
int yyget_lineno (void) __attribute__((pure));
int yyget_leng (void) __attribute__((pure));
int yywrap (void) __attribute__((const));
char *yyget_text (void) __attribute__((pure));
int yyget_debug (void) __attribute__((pure));
FILE *yyget_in (void) __attribute__((pure));
FILE *yyget_out (void) __attribute__((pure));
static int keyword_or_id (const char *token);
static expr_t *parse_float_vector (const char *token, int width);
static expr_t *parse_int_vector (const char *token, int width);
extern QC_YYSTYPE qc_yylval;
%}
s [ \t]
m [\-+]
D [0-9]
B [01]
X [0-9a-fA-F]
ID [a-zA-Z_][a-zA-Z_0-9]*
FLOAT ({D}+|{D}*\.{D}+|{D}+\.{D}*)([eE]{m}?{D}+)?
FLOATf {FLOAT}[fF]
FLOATd {FLOAT}[dD]
FCOMP {m}?{FLOAT}
FD [fFdD]
INT ({D}+|0[xX]{X}+|0[bB]{B})
ICOMP {m}?{INT}
UL ([uU]?([lL][lL]?)?)
ULFD ({UL}|{FD})
RANGE \.\.
ELLIPSIS \.\.\.
FRAMEID {ID}(\.{ID})*
PRAGMAID {ID}(-{ID})*
STRING \"(\\.|[^"\\])*\"
%x GRAB_FRAME GRAB_OTHER GRAB_WRITE COMMENT PRAGMA
%%
grab_frame = GRAB_FRAME;
grab_other = GRAB_OTHER;
grab_write = GRAB_WRITE;
"/*" { BEGIN (COMMENT); }
<COMMENT>"/*" { warning (0, "nested /* in comment"); }
<COMMENT>"*/" { BEGIN (INITIAL); }
<COMMENT>\r*\n { pr.source_line++; }
<COMMENT>. /* nothing to do */
<COMMENT><<EOF>> { error (0, "EOF in comment"); return 0; }
"//".* /* nothing to do */
^#{s}+{D}+{s}+\"(\.|[^"\n])*\".*$ { line_info (yytext + 1); }
^#line{s}+{D}+{s}+\"(\.|[^"\n])*\".*$ { line_info (yytext + 5); }
^{s}*#{s}*pragma{s}+ { BEGIN (PRAGMA); }
{INT}+{UL}? {
const char *c = yytext + yyleng - 1;
pr_long_t i;
if (yytext[0] == '0' && tolower (yytext[1] == 'b'))
i = strtol (yytext + 2, 0, 2);
else
i = strtol (yytext, 0, 0);
if (tolower (*c) == 'u') {
if (tolower (c[1]) == 'l') {
qc_yylval.expr = new_ulong_expr (i);
} else {
qc_yylval.expr = new_uint_expr (i);
}
} else {
if (tolower (c[1]) == 'l') {
qc_yylval.expr = new_long_expr (i);
} else {
qc_yylval.expr = new_int_expr (i);
qc_yylval.expr->implicit = 1;
}
}
return VALUE;
}
{FLOAT} {
// advanced code defaults to double, but traditional
// and extended code defaults to float
if (options.traditional < 1) {
double d = strtod (yytext, 0);
qc_yylval.expr = new_double_expr (d);
qc_yylval.expr->implicit = 1;
} else {
float f = strtof (yytext, 0);
qc_yylval.expr = new_float_expr (f);
}
return VALUE;
}
{FLOATf} {
float f = strtof (yytext, 0);
qc_yylval.expr = new_float_expr (f);
return VALUE;
}
{FLOATd} {
// advanced code defaults to double, but traditional
// and extended code defaults to float
if (options.traditional < 1) {
double d = strtod (yytext, 0);
qc_yylval.expr = new_double_expr (d);
} else {
float f = strtof (yytext, 0);
qc_yylval.expr = new_float_expr (f);
warning (0, "truncating double constant to float");
}
return VALUE;
}
{ID} {
int tok = keyword_or_id(yytext);
return tok;
}
@{ID} {
int tok = keyword_or_id(yytext);
if (tok == '@')
REJECT;
return tok;
}
{STRING} {
const char *s = make_string (yytext, 0);
qc_yylval.expr = new_string_expr (s);
return STRING;
}
@ return '@';
'{s}*{ICOMP}{s}+{ICOMP}{s}*'{ULFD}? {
qc_yylval.expr = parse_int_vector (yytext, 2);
return VALUE;
}
'{s}*{ICOMP}{s}+{ICOMP}{s}+{ICOMP}{s}*'{ULFD}? {
qc_yylval.expr = parse_int_vector (yytext, 3);
return VALUE;
}
'{s}*{ICOMP}{s}+{ICOMP}{s}+{ICOMP}{s}+{ICOMP}{s}*'{ULFD}? {
qc_yylval.expr = parse_int_vector (yytext, 4);
return VALUE;
}
'{s}*{FCOMP}{s}+{FCOMP}{s}*'{FD}? {
qc_yylval.expr = parse_float_vector (yytext, 2);
return VALUE;
}
'{s}*{FCOMP}{s}+{FCOMP}{s}+{FCOMP}{s}*'{FD}? {
qc_yylval.expr = parse_float_vector (yytext, 3);
return VALUE;
}
'{s}*{FCOMP}{s}+{FCOMP}{s}+{FCOMP}{s}+{FCOMP}{s}*'{FD}? {
qc_yylval.expr = parse_float_vector (yytext, 4);
return VALUE;
}
'(\\[^xX0-7\r\n]|[^'\r\n]|\\[xX][0-9A-Fa-f]+|\\[0-7]+)*' {
const char *str = make_string (yytext, 0);
if (str[1])
warning (0, "multibyte char constant");
qc_yylval.expr = new_int_expr (*str);
return VALUE;
}
[+\-*/&|^%]= {
qc_yylval.op = yytext[0];
return ASX;
}
"%%=" {
qc_yylval.op = MOD;
return ASX;
}
"<<=" {
qc_yylval.op = SHL;
return ASX;
}
">>=" {
qc_yylval.op = SHR;
return ASX;
}
[!(){}.*/&|^~+\-=\[\];,#%?:] {
qc_yylval.pointer = 0; // ensure pointer vals are null
return yytext[0];
}
"%%" {
qc_yylval.pointer = 0; // ensure pointer vals are null
return MOD;
}
{ELLIPSIS} return ELLIPSIS;
"<<" return SHL;
">>" return SHR;
"&&" return AND;
"||" return OR;
"==" return EQ;
"!=" return NE;
"<=" return LE;
">=" return GE;
"<" return LT;
">" return GT;
"++" {
qc_yylval.op = '+';
return INCOP;
}
"--" {
qc_yylval.op = '-';
return INCOP;
}
"$"{s}*{FRAMEID} {
int ret = do_grab (yytext);
if (ret >= 0) {
qc_yylval.expr = new_int_expr (ret);
return VALUE;
} else {
BEGIN (-ret);
}
}
<GRAB_FRAME>{FRAMEID} add_frame_macro (yytext);
<GRAB_OTHER>[^\r\n]* /* skip */
<GRAB_WRITE>{STRING} {
const char *s = make_string (yytext, 0);
write_frame_macros (s);
BEGIN (GRAB_OTHER); // ignore rest of line
}
<PRAGMA>{ID} { pragma_add_arg (yytext); }
<PRAGMA>@{ID} { pragma_add_arg (yytext); }
<*>\r*\n {
if (YY_START == PRAGMA) {
pragma_process ();
}
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;
} keyword_t;
// These keywords are part of the Ruamoko language and require the QuakeForge
// Ruamoko VM.
static keyword_t rua_keywords[] = {
#define VEC_TYPE(type_name, base_type) { #type_name, TYPE, &type_##type_name },
#include "tools/qfcc/include/vec_types.h"
};
// These keywords are all part of the Ruamoko (Objective-QC) language.
// The first time any one of them is encountered, the class system will be
// initialized.
// If not compiling for the QuakeForge VM, or if Ruamoko has been disabled,
// then they will be unavailable as keywords.
static keyword_t obj_keywords[] = {
{"id", OBJECT, &type_id },
{"Class", TYPE, &type_Class },
{"Method", TYPE, &type_method },
{"Super", TYPE, &type_super },
{"SEL", TYPE, &type_SEL },
{"IMP", TYPE, &type_IMP },
{"@class", CLASS },
{"@defs", DEFS },
{"@encode", ENCODE },
{"@end", END },
{"@implementation", IMPLEMENTATION },
{"@interface", INTERFACE },
{"@private", PRIVATE },
{"@protected", PROTECTED },
{"@protocol", PROTOCOL },
{"@public", PUBLIC },
{"@reference", REFERENCE },
{"@selector", SELECTOR },
{"@self", SELF },
{"@this", THIS },
// This is a hack to trigger the initialization of the class
// sytem if it is seen before any other Objective-QC symbol. Otherwise,
// it is just an identifier, though it does reference a built-in type
// created by the class system.
{"obj_module", 0 },
};
// These keywords are extensions to QC and thus available only in advanced
// or extended code. However, if they are preceeded by an @ (eg, @for), then
// they are always available. This is to prevent them from causing trouble
// for traditional code that might use these words as identifiers, but still
// make the language features available to traditional code.
static keyword_t at_keywords[] = {
{"for", FOR },
{"goto", GOTO },
{"break", BREAK },
{"continue", CONTINUE},
{"switch", SWITCH },
{"case", CASE },
{"default", DEFAULT },
{"nil", NIL },
{"struct", STRUCT },
{"union", STRUCT },
{"enum", ENUM },
{"typedef", TYPEDEF },
{"extern", EXTERN },
{"static", STATIC },
{"sizeof", SIZEOF },
{"not", NOT },
};
// These keywords require the QuakeForge VM to be of any use. ie, they cannot
// be supported (sanely) by v6 progs.
static keyword_t qf_keywords[] = {
{"quaternion", TYPE, &type_quaternion},
{"double", TYPE, &type_double},
{"int", TYPE, &type_int },
{"unsigned", UNSIGNED, 0 },
{"signed", SIGNED, 0 },
{"long", LONG, 0 },
{"short", SHORT, 0 },
{"@function", TYPE, &type_func },
{"@args", ARGS, 0 },
{"@va_list", TYPE, &type_va_list },
{"@param", TYPE, &type_param },
{"@return", AT_RETURN, 0 },
{"@cross", CROSS, 0 },
{"@dot", DOT, 0 },
};
// These keywors are always available. Other than the @ keywords, they
// form traditional QuakeC.
static keyword_t keywords[] = {
{"void", TYPE, &type_void },
{"float", TYPE, &type_float },
{"string", TYPE, &type_string},
{"vector", TYPE, &type_vector},
{"entity", TYPE, &type_entity},
{"local", LOCAL, 0 },
{"return", RETURN, 0 },
{"while", WHILE, 0 },
{"do", DO, 0 },
{"if", IF, 0 },
{"else", ELSE, 0 },
{"@system", SYSTEM, 0 },
{"@overload", OVERLOAD, 0 },
{"@attribute", ATTRIBUTE, 0 },
};
static const char *
keyword_get_key (const void *kw, void *unused)
{
return ((keyword_t*)kw)->name;
}
static int
process_keyword (keyword_t *keyword, const char *token)
{
if (keyword->value == STRUCT) {
qc_yylval.op = token[0];
} else if (keyword->value == OBJECT) {
symbol_t *sym;
sym = symtab_lookup (current_symtab, token);
qc_yylval.symbol = sym;
// the global id symbol is always just a name so attempts to redefine
// it globally can be caught and treated as an error, but it needs to
// be redefinable when in an enclosing scope.
if (sym->sy_type == sy_name) {
// this is the global id (object)
return OBJECT;
} else if (sym->sy_type == sy_type) {
// id has been redeclared via a typedef
return TYPE_NAME;
}
// id has been redelcared as a variable (hopefully)
return NAME;
} else {
qc_yylval.type = keyword->type;
}
return keyword->value;
}
static int
keyword_or_id (const char *token)
{
static hashtab_t *keyword_tab;
static hashtab_t *qf_keyword_tab;
static hashtab_t *at_keyword_tab;
static hashtab_t *obj_keyword_tab;
static hashtab_t *rua_keyword_tab;
keyword_t *keyword = 0;
symbol_t *sym;
if (!keyword_tab) {
size_t i;
keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0, 0);
qf_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0, 0);
at_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0, 0);
obj_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0, 0);
rua_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0, 0);
#define NUMKEYS(_k) (sizeof (_k) / sizeof (_k[0]))
for (i = 0; i < NUMKEYS(keywords); i++)
Hash_Add (keyword_tab, &keywords[i]);
for (i = 0; i < NUMKEYS(qf_keywords); i++)
Hash_Add (qf_keyword_tab, &qf_keywords[i]);
for (i = 0; i < NUMKEYS(at_keywords); i++)
Hash_Add (at_keyword_tab, &at_keywords[i]);
for (i = 0; i < NUMKEYS(obj_keywords); i++)
Hash_Add (obj_keyword_tab, &obj_keywords[i]);
for (i = 0; i < NUMKEYS(rua_keywords); i++)
Hash_Add (rua_keyword_tab, &rua_keywords[i]);
}
if (options.traditional < 1) {
if (options.code.progsversion == PROG_VERSION) {
keyword = Hash_Find (rua_keyword_tab, token);
}
if (!keyword) {
keyword = Hash_Find (obj_keyword_tab, token);
if (keyword) {
if (!obj_initialized)
class_init ();
}
}
if (!keyword)
keyword = Hash_Find (qf_keyword_tab, token);
}
if (!keyword && options.traditional < 2)
keyword = Hash_Find (at_keyword_tab, token);
if (!keyword && token[0] == '@') {
keyword = Hash_Find (at_keyword_tab, token + 1);
if (keyword)
token += 1;
}
if (!keyword)
keyword = Hash_Find (keyword_tab, token);
if (keyword && keyword->value)
return process_keyword (keyword, token);
if (token[0] == '@') {
return '@';
}
sym = symtab_lookup (current_symtab, token);
if (!sym)
sym = new_symbol (token);
qc_yylval.symbol = sym;
if (sym->sy_type == sy_type)
return TYPE_NAME;
if (sym->sy_type == sy_class)
return CLASS_NAME;
return NAME;
}
static expr_t *
parse_int_vector (const char *token, int width)
{
char t1 = 0, t2 = 0;
type_t *type = 0;
union {
pr_long_t l[4];
pr_type_t t[PR_SIZEOF (dvec4)];
} long_data = {};
pr_type_t *data = __builtin_choose_expr (
sizeof (pr_long_t) == sizeof (long), long_data.t, (void) 0);
switch (width) {
case 4:
sscanf (token, "' %li %li %li %li '%c%c",
&long_data.l[0], &long_data.l[1],
&long_data.l[2], &long_data.l[3], &t1, &t2);
break;
case 3:
sscanf (token, "' %li %li %li '%c%c",
&long_data.l[0], &long_data.l[1],
&long_data.l[2], &t1, &t2);
break;
case 2:
sscanf (token, "' %li %li '%c%c",
&long_data.l[0], &long_data.l[1], &t1, &t2);
break;
}
t1 = tolower (t1);
t2 = tolower (t2);
switch (t1) {
case 'u':
if (t2 == 'l') {
type = &type_ulong;
} else {
type = &type_uint;
volatile union {
pr_uint_t u[4];
pr_type_t t[PR_SIZEOF (ivec4)];
} uint_data = {
.u = {
long_data.l[0],
long_data.l[1],
long_data.l[2],
long_data.l[3],
}
};
data = (pr_type_t *) uint_data.t;
}
break;
case 'l':
type = &type_long;
break;
case 'f':
type = &type_float;
volatile union {
pr_float_t f[4];
pr_type_t t[PR_SIZEOF (vec4)];
} float_data = {
.f = {
long_data.l[0],
long_data.l[1],
long_data.l[2],
long_data.l[3],
}
};
data = (pr_type_t *) float_data.t;
break;
case 'd':
type = &type_double;
volatile union {
pr_double_t d[4];
pr_type_t t[PR_SIZEOF (dvec4)];
} double_data = {
.d = {
long_data.l[0],
long_data.l[1],
long_data.l[2],
long_data.l[3],
}
};
data = (pr_type_t *) double_data.t;
break;
case 0:
type = &type_int;
volatile union {
pr_int_t i[4];
pr_type_t t[PR_SIZEOF (ivec4)];
} int_data = {
.i = {
long_data.l[0],
long_data.l[1],
long_data.l[2],
long_data.l[3],
}
};
data = (pr_type_t *) int_data.t;
break;
}
type = vector_type (type, width);
expr_t *expr = new_value_expr (new_type_value (type, data));
expr->implicit = !t1;
return expr;
}
static expr_t *
parse_float_vector (const char *token, int width)
{
char t = 0;
type_t *type = 0;
union {
pr_double_t d[4];
pr_type_t t[PR_SIZEOF (dvec4)];
} double_data = {};
pr_type_t *data = __builtin_choose_expr (
sizeof (pr_double_t) == sizeof (double), double_data.t, (void) 0);
switch (width) {
case 4:
sscanf (token, "' %lf %lf %lf %lf '%c",
&double_data.d[0], &double_data.d[1],
&double_data.d[2], &double_data.d[3], &t);
break;
case 3:
sscanf (token, "' %lf %lf %lf '%c",
&double_data.d[0], &double_data.d[1],
&double_data.d[1], &t);
type = (t == 'f' || t == 'F') ? &type_vec3 : &type_dvec3;
break;
case 2:
sscanf (token, "' %lf %lf '%c",
&double_data.d[0], &double_data.d[1], &t);
type = (t == 'f' || t == 'F') ? &type_vec2 : &type_dvec2;
break;
}
if (t == 'f' || t == 'F') {
volatile union {
pr_float_t f[4];
pr_type_t t[PR_SIZEOF (vec4)];
} float_data = {
.f = {
double_data.d[0],
double_data.d[1],
double_data.d[2],
double_data.d[3],
}
};
data = (pr_type_t *) float_data.t;
type = &type_float;
} else {
type = &type_double;
}
type = vector_type (type, width);
expr_t *expr = new_value_expr (new_type_value (type, data));
expr->implicit = !t;
return expr;
}
#ifdef YY_FLEX_REALLOC_HACK
static __attribute__ ((used)) void *(*const yy_flex_realloc_hack)(void *,yy_size_t) = yy_flex_realloc;
#else
#ifdef yyunput
static __attribute__ ((used)) void (*yyunput_hack)(int, char*) = yyunput;
#endif
static __attribute__ ((used)) int (*input_hack)(void) = input;
#endif