Skip to content

Commit

Permalink
Implemented length operator. This closes #130
Browse files Browse the repository at this point in the history
  • Loading branch information
graphitemaster committed Sep 27, 2014
1 parent 31cd263 commit b08195e
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 71 deletions.
56 changes: 33 additions & 23 deletions fold.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))

/*
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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");
Expand Down
22 changes: 22 additions & 0 deletions lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
97 changes: 49 additions & 48 deletions lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down
16 changes: 16 additions & 0 deletions parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
23 changes: 23 additions & 0 deletions tests/length.qc
Original file line number Diff line number Diff line change
@@ -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 changes: 13 additions & 0 deletions tests/length.tmpl
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit b08195e

Please sign in to comment.