mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-01-18 14:21:36 +00:00
Implemented length operator. This closes #130
This commit is contained in:
parent
31cd263e33
commit
b08195e2da
6 changed files with 156 additions and 71 deletions
56
fold.c
56
fold.c
|
@ -511,6 +511,7 @@ static GMQCC_INLINE void sfloat_init(sfloat_state_t *state) {
|
|||
#define isfloat(X) (((ast_expression*)(X))->vtype == TYPE_FLOAT)
|
||||
#define isvector(X) (((ast_expression*)(X))->vtype == TYPE_VECTOR)
|
||||
#define isstring(X) (((ast_expression*)(X))->vtype == TYPE_STRING)
|
||||
#define isarray(X) (((ast_expression*)(X))->vtype == TYPE_ARRAY)
|
||||
#define isfloats(X,Y) (isfloat (X) && isfloat (Y))
|
||||
|
||||
/*
|
||||
|
@ -1337,6 +1338,14 @@ static GMQCC_INLINE ast_expression *fold_op_cross(fold_t *fold, ast_value *a, as
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE ast_expression *fold_op_length(fold_t *fold, ast_value *a) {
|
||||
if (fold_can_1(a) && isstring(a))
|
||||
return fold_constgen_float(fold, strlen(fold_immvalue_string(a)), false);
|
||||
if (isarray(a))
|
||||
return fold_constgen_float(fold, vec_size(a->initlist), false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_expression *fold_op(fold_t *fold, const oper_info *info, ast_expression **opexprs) {
|
||||
ast_value *a = (ast_value*)opexprs[0];
|
||||
ast_value *b = (ast_value*)opexprs[1];
|
||||
|
@ -1374,29 +1383,30 @@ ast_expression *fold_op(fold_t *fold, const oper_info *info, ast_expression **op
|
|||
return e
|
||||
|
||||
switch(info->id) {
|
||||
fold_op_case(2, ('-', 'P'), neg, (fold, a));
|
||||
fold_op_case(2, ('!', 'P'), not, (fold, a));
|
||||
fold_op_case(1, ('+'), add, (fold, a, b));
|
||||
fold_op_case(1, ('-'), sub, (fold, a, b));
|
||||
fold_op_case(1, ('*'), mul, (fold, a, b));
|
||||
fold_op_case(1, ('/'), div, (fold, a, b));
|
||||
fold_op_case(1, ('%'), mod, (fold, a, b));
|
||||
fold_op_case(1, ('|'), bor, (fold, a, b));
|
||||
fold_op_case(1, ('&'), band, (fold, a, b));
|
||||
fold_op_case(1, ('^'), xor, (fold, a, b));
|
||||
fold_op_case(1, ('<'), ltgt, (fold, a, b, true));
|
||||
fold_op_case(1, ('>'), ltgt, (fold, a, b, false));
|
||||
fold_op_case(2, ('<', '<'), lshift, (fold, a, b));
|
||||
fold_op_case(2, ('>', '>'), rshift, (fold, a, b));
|
||||
fold_op_case(2, ('|', '|'), andor, (fold, a, b, true));
|
||||
fold_op_case(2, ('&', '&'), andor, (fold, a, b, false));
|
||||
fold_op_case(2, ('?', ':'), tern, (fold, a, b, c));
|
||||
fold_op_case(2, ('*', '*'), exp, (fold, a, b));
|
||||
fold_op_case(3, ('<','=','>'), lteqgt, (fold, a, b));
|
||||
fold_op_case(2, ('!', '='), cmp, (fold, a, b, true));
|
||||
fold_op_case(2, ('=', '='), cmp, (fold, a, b, false));
|
||||
fold_op_case(2, ('~', 'P'), bnot, (fold, a));
|
||||
fold_op_case(2, ('>', '<'), cross, (fold, a, b));
|
||||
fold_op_case(2, ('-', 'P'), neg, (fold, a));
|
||||
fold_op_case(2, ('!', 'P'), not, (fold, a));
|
||||
fold_op_case(1, ('+'), add, (fold, a, b));
|
||||
fold_op_case(1, ('-'), sub, (fold, a, b));
|
||||
fold_op_case(1, ('*'), mul, (fold, a, b));
|
||||
fold_op_case(1, ('/'), div, (fold, a, b));
|
||||
fold_op_case(1, ('%'), mod, (fold, a, b));
|
||||
fold_op_case(1, ('|'), bor, (fold, a, b));
|
||||
fold_op_case(1, ('&'), band, (fold, a, b));
|
||||
fold_op_case(1, ('^'), xor, (fold, a, b));
|
||||
fold_op_case(1, ('<'), ltgt, (fold, a, b, true));
|
||||
fold_op_case(1, ('>'), ltgt, (fold, a, b, false));
|
||||
fold_op_case(2, ('<', '<'), lshift, (fold, a, b));
|
||||
fold_op_case(2, ('>', '>'), rshift, (fold, a, b));
|
||||
fold_op_case(2, ('|', '|'), andor, (fold, a, b, true));
|
||||
fold_op_case(2, ('&', '&'), andor, (fold, a, b, false));
|
||||
fold_op_case(2, ('?', ':'), tern, (fold, a, b, c));
|
||||
fold_op_case(2, ('*', '*'), exp, (fold, a, b));
|
||||
fold_op_case(3, ('<','=','>'), lteqgt, (fold, a, b));
|
||||
fold_op_case(2, ('!', '='), cmp, (fold, a, b, true));
|
||||
fold_op_case(2, ('=', '='), cmp, (fold, a, b, false));
|
||||
fold_op_case(2, ('~', 'P'), bnot, (fold, a));
|
||||
fold_op_case(2, ('>', '<'), cross, (fold, a, b));
|
||||
fold_op_case(3, ('l', 'e', 'n'), length, (fold, a));
|
||||
}
|
||||
#undef fold_op_case
|
||||
compile_error(fold_ctx(fold), "internal error: attempted to constant-fold for unsupported operator");
|
||||
|
|
22
lexer.c
22
lexer.c
|
@ -1301,6 +1301,28 @@ int lex_do(lex_file *lex)
|
|||
return (lex->tok.ttype = TOKEN_OPERATOR);
|
||||
}
|
||||
|
||||
/* length operator */
|
||||
if (ch == 'l') {
|
||||
if ((nextch = lex_getch(lex)) == 'e') {
|
||||
if ((nextch = lex_getch(lex)) == 'n') {
|
||||
if ((nextch = lex_getch(lex)) == 'g') {
|
||||
if ((nextch = lex_getch(lex)) == 't') {
|
||||
if ((nextch = lex_getch(lex)) == 'h') {
|
||||
lex_tokench(lex, 'l');
|
||||
lex_tokench(lex, 'e');
|
||||
lex_tokench(lex, 'n');
|
||||
lex_tokench(lex, 'g');
|
||||
lex_tokench(lex, 't');
|
||||
lex_tokench(lex, 'h');
|
||||
lex_endtoken(lex);
|
||||
return (lex->tok.ttype = TOKEN_OPERATOR);
|
||||
} else lex_ungetch(lex, nextch);
|
||||
} else lex_ungetch(lex, nextch);
|
||||
} else lex_ungetch(lex, nextch);
|
||||
} else lex_ungetch(lex, nextch);
|
||||
} else lex_ungetch(lex, nextch);
|
||||
}
|
||||
|
||||
if (isident_start(ch))
|
||||
{
|
||||
const char *v;
|
||||
|
|
97
lexer.h
97
lexer.h
|
@ -176,71 +176,72 @@ typedef struct {
|
|||
#define opid3(a,b,c) (((uint8_t)a<<16)|((uint8_t)b<<8)|(uint8_t)c)
|
||||
|
||||
static const oper_info c_operators[] = {
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX, false}, /* paren expression - non function call */
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX, false}, /* paren expression - non function call */
|
||||
{ "length", 1, opid3('l','e','n'), ASSOC_RIGHT, 98, OP_PREFIX, true},
|
||||
|
||||
{ "++", 1, opid3('S','+','+'), ASSOC_LEFT, 17, OP_SUFFIX, false},
|
||||
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 17, OP_SUFFIX, false},
|
||||
{ ".", 2, opid1('.'), ASSOC_LEFT, 17, 0, false},
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 17, 0, false}, /* function call */
|
||||
{ "[", 2, opid1('['), ASSOC_LEFT, 17, 0, false}, /* array subscript */
|
||||
{ "++", 1, opid3('S','+','+'), ASSOC_LEFT, 17, OP_SUFFIX, false},
|
||||
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 17, OP_SUFFIX, false},
|
||||
{ ".", 2, opid1('.'), ASSOC_LEFT, 17, 0, false},
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 17, 0, false}, /* function call */
|
||||
{ "[", 2, opid1('['), ASSOC_LEFT, 17, 0, false}, /* array subscript */
|
||||
|
||||
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
|
||||
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
|
||||
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
|
||||
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
|
||||
|
||||
{ "**", 2, opid2('*','*'), ASSOC_RIGHT, 14, 0, true},
|
||||
{ "!", 1, opid2('!','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
{ "~", 1, opid2('~','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
|
||||
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
/* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX, false}, */
|
||||
{ "**", 2, opid2('*','*'), ASSOC_RIGHT, 14, 0, true},
|
||||
{ "!", 1, opid2('!','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
{ "~", 1, opid2('~','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
|
||||
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
/* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX, false}, */
|
||||
|
||||
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "%", 2, opid1('%'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "><", 2, opid2('>','<'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "%", 2, opid1('%'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "><", 2, opid2('>','<'), ASSOC_LEFT, 13, 0, true},
|
||||
|
||||
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0, true},
|
||||
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
|
||||
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0, true},
|
||||
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
|
||||
|
||||
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0, true},
|
||||
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0, true},
|
||||
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0, true},
|
||||
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0, true},
|
||||
|
||||
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0, false},
|
||||
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0, false},
|
||||
{ "<=>", 2, opid3('<','=','>'), ASSOC_LEFT, 10, 0, true},
|
||||
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0, false},
|
||||
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0, false},
|
||||
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0, false},
|
||||
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0, false},
|
||||
{ "<=>", 2, opid3('<','=','>'), ASSOC_LEFT, 10, 0, true},
|
||||
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0, false},
|
||||
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0, false},
|
||||
|
||||
{ "==", 2, opid2('=','='), ASSOC_LEFT, 9, 0, true},
|
||||
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0, true},
|
||||
{ "==", 2, opid2('=','='), ASSOC_LEFT, 9, 0, true},
|
||||
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0, true},
|
||||
|
||||
{ "&", 2, opid1('&'), ASSOC_LEFT, 8, 0, true},
|
||||
{ "&", 2, opid1('&'), ASSOC_LEFT, 8, 0, true},
|
||||
|
||||
{ "^", 2, opid1('^'), ASSOC_LEFT, 7, 0, true},
|
||||
{ "^", 2, opid1('^'), ASSOC_LEFT, 7, 0, true},
|
||||
|
||||
{ "|", 2, opid1('|'), ASSOC_LEFT, 6, 0, true},
|
||||
{ "|", 2, opid1('|'), ASSOC_LEFT, 6, 0, true},
|
||||
|
||||
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
|
||||
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
|
||||
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0, true},
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0, true},
|
||||
|
||||
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0, true},
|
||||
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0, true},
|
||||
|
||||
{ "=", 2, opid1('='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ ">>=", 2, opid3('>','>','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "<<=", 2, opid3('<','<','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "^=", 2, opid2('^','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "=", 2, opid1('='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ ">>=", 2, opid3('>','>','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "<<=", 2, opid3('<','<','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "^=", 2, opid2('^','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 2, 0, false},
|
||||
|
||||
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0, false},
|
||||
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0, false},
|
||||
|
||||
{ ",", 2, opid1(','), ASSOC_LEFT, 0, 0, false}
|
||||
{ ",", 2, opid1(','), ASSOC_LEFT, 0, 0, false}
|
||||
};
|
||||
|
||||
static const oper_info fte_operators[] = {
|
||||
|
|
16
parser.c
16
parser.c
|
@ -1145,6 +1145,22 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
out = (ast_expression*)asbinstore;
|
||||
break;
|
||||
|
||||
case opid3('l', 'e', 'n'):
|
||||
if (exprs[0]->vtype != TYPE_STRING && exprs[0]->vtype != TYPE_ARRAY) {
|
||||
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
|
||||
compile_error(ast_ctx(exprs[0]), "invalid type for length operator: %s", ty1);
|
||||
return false;
|
||||
}
|
||||
/* strings must be const, arrays are statically sized */
|
||||
if (exprs[0]->vtype == TYPE_STRING &&
|
||||
!(((ast_value*)exprs[0])->hasvalue && ((ast_value*)exprs[0])->cvq == CV_CONST))
|
||||
{
|
||||
compile_error(ast_ctx(exprs[0]), "operand of length operator not a valid constant expression");
|
||||
return false;
|
||||
}
|
||||
out = fold_op(parser->fold, op, exprs);
|
||||
break;
|
||||
|
||||
case opid2('~', 'P'):
|
||||
if (exprs[0]->vtype != TYPE_FLOAT && exprs[0]->vtype != TYPE_VECTOR) {
|
||||
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
|
||||
|
|
23
tests/length.qc
Normal file
23
tests/length.qc
Normal file
|
@ -0,0 +1,23 @@
|
|||
const string a = "hello world"; // 11
|
||||
float b[] = { 1, 2, 3 }; // 3
|
||||
float c[5] = { 5, 4, 3, 2, 1 }; // 5
|
||||
const float d[] = { 1 }; // 1
|
||||
|
||||
void main() {
|
||||
print(ftos(length a), "\n"); // 11
|
||||
print(ftos(length b), "\n"); // 3
|
||||
print(ftos(length c), "\n"); // 5
|
||||
print(ftos(length d), "\n"); // 1
|
||||
|
||||
static float al = length(a);
|
||||
static float bl = length(b);
|
||||
static float cl = length(c);
|
||||
static float dl = length(d);
|
||||
|
||||
print(ftos(al), "\n"); // 11
|
||||
print(ftos(bl), "\n"); // 3
|
||||
print(ftos(cl), "\n"); // 5
|
||||
print(ftos(dl), "\n"); // 1
|
||||
|
||||
print(ftos(length "hello world"), "\n"); // 11
|
||||
}
|
13
tests/length.tmpl
Normal file
13
tests/length.tmpl
Normal file
|
@ -0,0 +1,13 @@
|
|||
I: length.qc
|
||||
D: length operator
|
||||
T: -execute
|
||||
C: -std=gmqcc
|
||||
M: 11
|
||||
M: 3
|
||||
M: 5
|
||||
M: 1
|
||||
M: 11
|
||||
M: 3
|
||||
M: 5
|
||||
M: 1
|
||||
M: 11
|
Loading…
Reference in a new issue