Add len() operator

This commit is contained in:
2025-08-08 23:07:16 +02:00
parent 208fcc008e
commit b2d3131cd4
6 changed files with 144 additions and 24 deletions
+61 -2
View File
@@ -351,6 +351,7 @@ typedef enum {
NODE_INCLUDE, NODE_INCLUDE,
NODE_SELECT, NODE_SELECT,
NODE_NESTED, NODE_NESTED,
NODE_OPER_LEN,
NODE_OPER_POS, NODE_OPER_POS,
NODE_OPER_NEG, NODE_OPER_NEG,
NODE_OPER_ASS, NODE_OPER_ASS,
@@ -459,6 +460,7 @@ typedef enum {
TOKEN_KWORD_TRUE, TOKEN_KWORD_TRUE,
TOKEN_KWORD_FALSE, TOKEN_KWORD_FALSE,
TOKEN_KWORD_INCLUDE, TOKEN_KWORD_INCLUDE,
TOKEN_KWORD_LEN,
TOKEN_VALUE_FLOAT, TOKEN_VALUE_FLOAT,
TOKEN_VALUE_INT, TOKEN_VALUE_INT,
TOKEN_VALUE_STR, TOKEN_VALUE_STR,
@@ -550,6 +552,7 @@ String tok2str(Token token, char *buf, int max)
case TOKEN_KWORD_TRUE: return S("true"); case TOKEN_KWORD_TRUE: return S("true");
case TOKEN_KWORD_FALSE: return S("false"); case TOKEN_KWORD_FALSE: return S("false");
case TOKEN_KWORD_INCLUDE: return S("include"); case TOKEN_KWORD_INCLUDE: return S("include");
case TOKEN_KWORD_LEN: return S("len");
case TOKEN_VALUE_FLOAT: case TOKEN_VALUE_FLOAT:
{ {
@@ -688,6 +691,7 @@ Token next_token(Parser *p)
if (streq(kword, S("true"))) return (Token) { .type=TOKEN_KWORD_TRUE }; if (streq(kword, S("true"))) return (Token) { .type=TOKEN_KWORD_TRUE };
if (streq(kword, S("false"))) return (Token) { .type=TOKEN_KWORD_FALSE }; if (streq(kword, S("false"))) return (Token) { .type=TOKEN_KWORD_FALSE };
if (streq(kword, S("include"))) return (Token) { .type=TOKEN_KWORD_INCLUDE }; if (streq(kword, S("include"))) return (Token) { .type=TOKEN_KWORD_INCLUDE };
if (streq(kword, S("len"))) return (Token) { .type=TOKEN_KWORD_LEN };
return (Token) { .type=TOKEN_IDENT, .sval=kword }; return (Token) { .type=TOKEN_IDENT, .sval=kword };
} }
@@ -1249,6 +1253,23 @@ Node *parse_atom(Parser *p)
} }
break; break;
case TOKEN_KWORD_LEN:
{
Node *child = parse_atom(p);
if (child == NULL)
return NULL;
Node *parent = alloc_node(p);
if (parent == NULL)
return NULL;
parent->type = NODE_OPER_LEN;
parent->left = child;
ret = parent;
}
break;
case TOKEN_IDENT: case TOKEN_IDENT:
{ {
Node *node = alloc_node(p); Node *node = alloc_node(p);
@@ -2046,6 +2067,12 @@ void print_node(Node *node)
} }
break; break;
case NODE_OPER_LEN:
printf("len(");
print_node(node->left);
printf(")");
break;
case NODE_OPER_POS: case NODE_OPER_POS:
printf("("); printf("(");
printf("+"); printf("+");
@@ -2420,6 +2447,7 @@ enum {
OPCODE_FOR = 0x2E, OPCODE_FOR = 0x2E,
OPCODE_PUSHT = 0x2F, OPCODE_PUSHT = 0x2F,
OPCODE_PUSHFL = 0x30, OPCODE_PUSHFL = 0x30,
OPCODE_LEN = 0x31,
}; };
typedef struct { typedef struct {
@@ -2757,6 +2785,7 @@ bool is_expr(Node *node)
case NODE_SELECT: case NODE_SELECT:
case NODE_NESTED: case NODE_NESTED:
case NODE_FUNC_CALL: case NODE_FUNC_CALL:
case NODE_OPER_LEN:
case NODE_OPER_POS: case NODE_OPER_POS:
case NODE_OPER_NEG: case NODE_OPER_NEG:
case NODE_OPER_ASS: case NODE_OPER_ASS:
@@ -3002,6 +3031,20 @@ void assemble_expr(Assembler *a, Node *node, int num_results)
} }
break; break;
case NODE_OPER_LEN:
assemble_expr(a, node->left, 1);
append_u8(&a->out, OPCODE_LEN);
if (num_results == 0)
append_u8(&a->out, OPCODE_POP);
else if (num_results != -1 && num_results != 1) {
append_u8(&a->out, OPCODE_GROUP);
append_u8(&a->out, OPCODE_GTRUNC);
append_u32(&a->out, num_results-1);
append_u8(&a->out, OPCODE_GCOALESCE);
}
break;
case NODE_OPER_POS: case NODE_OPER_POS:
assemble_expr(a, node->left, num_results); assemble_expr(a, node->left, num_results);
break; break;
@@ -5526,8 +5569,6 @@ int step(WL_State *state)
uint8_t var_2 = read_u8(state); uint8_t var_2 = read_u8(state);
uint32_t end = read_u32(state); uint32_t end = read_u32(state);
printf("for end %u\n", end); // TODO
int base; int base;
{ {
int group = state->frames[state->num_frames-1].group; int group = state->frames[state->num_frames-1].group;
@@ -5576,6 +5617,24 @@ int step(WL_State *state)
} }
break; break;
case OPCODE_LEN:
{
Value set = state->eval_stack[state->eval_depth-1];
Type type = type_of(set);
if (type != TYPE_ARRAY && type != TYPE_MAP) {
assert(0); // TODO
}
Value len = make_int(state->a, value_length(set));
if (len == VALUE_ERROR) {
assert(0); // TODO
}
state->eval_stack[state->eval_depth++] = len;
}
break;
default: default:
eval_report(state, "Invalid opcode (offset %d)", state->off-1); eval_report(state, "Invalid opcode (offset %d)", state->off-1);
return -1; return -1;
+15
View File
@@ -317,6 +317,7 @@ bool is_expr(Node *node)
case NODE_SELECT: case NODE_SELECT:
case NODE_NESTED: case NODE_NESTED:
case NODE_FUNC_CALL: case NODE_FUNC_CALL:
case NODE_OPER_LEN:
case NODE_OPER_POS: case NODE_OPER_POS:
case NODE_OPER_NEG: case NODE_OPER_NEG:
case NODE_OPER_ASS: case NODE_OPER_ASS:
@@ -562,6 +563,20 @@ void assemble_expr(Assembler *a, Node *node, int num_results)
} }
break; break;
case NODE_OPER_LEN:
assemble_expr(a, node->left, 1);
append_u8(&a->out, OPCODE_LEN);
if (num_results == 0)
append_u8(&a->out, OPCODE_POP);
else if (num_results != -1 && num_results != 1) {
append_u8(&a->out, OPCODE_GROUP);
append_u8(&a->out, OPCODE_GTRUNC);
append_u32(&a->out, num_results-1);
append_u8(&a->out, OPCODE_GCOALESCE);
}
break;
case NODE_OPER_POS: case NODE_OPER_POS:
assemble_expr(a, node->left, num_results); assemble_expr(a, node->left, num_results);
break; break;
+1
View File
@@ -49,6 +49,7 @@ enum {
OPCODE_FOR = 0x2E, OPCODE_FOR = 0x2E,
OPCODE_PUSHT = 0x2F, OPCODE_PUSHT = 0x2F,
OPCODE_PUSHFL = 0x30, OPCODE_PUSHFL = 0x30,
OPCODE_LEN = 0x31,
}; };
typedef struct { typedef struct {
+18
View File
@@ -934,6 +934,24 @@ int step(WL_State *state)
} }
break; break;
case OPCODE_LEN:
{
Value set = state->eval_stack[state->eval_depth-1];
Type type = type_of(set);
if (type != TYPE_ARRAY && type != TYPE_MAP) {
assert(0); // TODO
}
Value len = make_int(state->a, value_length(set));
if (len == VALUE_ERROR) {
assert(0); // TODO
}
state->eval_stack[state->eval_depth++] = len;
}
break;
default: default:
eval_report(state, "Invalid opcode (offset %d)", state->off-1); eval_report(state, "Invalid opcode (offset %d)", state->off-1);
return -1; return -1;
+26
View File
@@ -25,6 +25,7 @@ typedef enum {
TOKEN_KWORD_TRUE, TOKEN_KWORD_TRUE,
TOKEN_KWORD_FALSE, TOKEN_KWORD_FALSE,
TOKEN_KWORD_INCLUDE, TOKEN_KWORD_INCLUDE,
TOKEN_KWORD_LEN,
TOKEN_VALUE_FLOAT, TOKEN_VALUE_FLOAT,
TOKEN_VALUE_INT, TOKEN_VALUE_INT,
TOKEN_VALUE_STR, TOKEN_VALUE_STR,
@@ -116,6 +117,7 @@ String tok2str(Token token, char *buf, int max)
case TOKEN_KWORD_TRUE: return S("true"); case TOKEN_KWORD_TRUE: return S("true");
case TOKEN_KWORD_FALSE: return S("false"); case TOKEN_KWORD_FALSE: return S("false");
case TOKEN_KWORD_INCLUDE: return S("include"); case TOKEN_KWORD_INCLUDE: return S("include");
case TOKEN_KWORD_LEN: return S("len");
case TOKEN_VALUE_FLOAT: case TOKEN_VALUE_FLOAT:
{ {
@@ -254,6 +256,7 @@ Token next_token(Parser *p)
if (streq(kword, S("true"))) return (Token) { .type=TOKEN_KWORD_TRUE }; if (streq(kword, S("true"))) return (Token) { .type=TOKEN_KWORD_TRUE };
if (streq(kword, S("false"))) return (Token) { .type=TOKEN_KWORD_FALSE }; if (streq(kword, S("false"))) return (Token) { .type=TOKEN_KWORD_FALSE };
if (streq(kword, S("include"))) return (Token) { .type=TOKEN_KWORD_INCLUDE }; if (streq(kword, S("include"))) return (Token) { .type=TOKEN_KWORD_INCLUDE };
if (streq(kword, S("len"))) return (Token) { .type=TOKEN_KWORD_LEN };
return (Token) { .type=TOKEN_IDENT, .sval=kword }; return (Token) { .type=TOKEN_IDENT, .sval=kword };
} }
@@ -815,6 +818,23 @@ Node *parse_atom(Parser *p)
} }
break; break;
case TOKEN_KWORD_LEN:
{
Node *child = parse_atom(p);
if (child == NULL)
return NULL;
Node *parent = alloc_node(p);
if (parent == NULL)
return NULL;
parent->type = NODE_OPER_LEN;
parent->left = child;
ret = parent;
}
break;
case TOKEN_IDENT: case TOKEN_IDENT:
{ {
Node *node = alloc_node(p); Node *node = alloc_node(p);
@@ -1612,6 +1632,12 @@ void print_node(Node *node)
} }
break; break;
case NODE_OPER_LEN:
printf("len(");
print_node(node->left);
printf(")");
break;
case NODE_OPER_POS: case NODE_OPER_POS:
printf("("); printf("(");
printf("+"); printf("+");
+1
View File
@@ -20,6 +20,7 @@ typedef enum {
NODE_INCLUDE, NODE_INCLUDE,
NODE_SELECT, NODE_SELECT,
NODE_NESTED, NODE_NESTED,
NODE_OPER_LEN,
NODE_OPER_POS, NODE_OPER_POS,
NODE_OPER_NEG, NODE_OPER_NEG,
NODE_OPER_ASS, NODE_OPER_ASS,