+ ast_value_delete(typevar);
+ return false;
+ }
+
+ if (parser->tok == '[') {
+ /* got a frame definition: [ framenum, nextthink ]
+ * this translates to:
+ * self.frame = framenum;
+ * self.nextthink = time + 0.1;
+ * self.think = nextthink;
+ */
+ nextthink = NULL;
+
+ fld_think = parser_find_field(parser, "think");
+ fld_nextthink = parser_find_field(parser, "nextthink");
+ fld_frame = parser_find_field(parser, "frame");
+ if (!fld_think || !fld_nextthink || !fld_frame) {
+ parseerror(parser, "cannot use [frame,think] notation without the required fields");
+ parseerror(parser, "please declare the following entityfields: `frame`, `think`, `nextthink`");
+ ast_value_delete(typevar);
+ return false;
+ }
+ gbl_time = parser_find_global(parser, "time");
+ gbl_self = parser_find_global(parser, "self");
+ if (!gbl_time || !gbl_self) {
+ parseerror(parser, "cannot use [frame,think] notation without the required globals");
+ parseerror(parser, "please declare the following globals: `time`, `self`");
+ ast_value_delete(typevar);
+ return false;
+ }
+
+ if (!parser_next(parser)) {
+ ast_value_delete(typevar);
+ return false;
+ }
+
+ framenum = parser_expression_leave(parser, true);
+ if (!framenum) {
+ parseerror(parser, "expected a framenumber constant in[frame,think] notation");
+ ast_value_delete(typevar);
+ return false;
+ }
+ if (!ast_istype(framenum, ast_value) || !( (ast_value*)framenum )->isconst) {
+ ast_unref(framenum);
+ parseerror(parser, "framenumber in [frame,think] notation must be a constant");
+ }
+
+ if (parser->tok != ',') {
+ ast_unref(framenum);
+ parseerror(parser, "expected comma after frame number in [frame,think] notation");
+ parseerror(parser, "Got a %i\n", parser->tok);
+ ast_value_delete(typevar);
+ return false;
+ }
+
+ if (!parser_next(parser)) {
+ ast_unref(framenum);
+ ast_value_delete(typevar);
+ return false;
+ }
+
+ if (parser->tok == TOKEN_IDENT && !parser_find_var(parser, parser_tokval(parser)))
+ {
+ /* qc allows the use of not-yet-declared functions here
+ * - this automatically creates a prototype */
+ varentry_t varent;
+ ast_value *thinkfunc;
+ ast_expression *functype = fld_think->expression.next;
+
+ thinkfunc = ast_value_new(parser_ctx(parser), parser_tokval(parser), functype->expression.vtype);
+ if (!thinkfunc || !ast_type_adopt(thinkfunc, functype)) {
+ ast_unref(framenum);
+ parseerror(parser, "failed to create implicit prototype for `%s`", parser_tokval(parser));
+ ast_value_delete(typevar);
+ return false;
+ }
+
+ if (!parser_next(parser)) {
+ ast_unref(framenum);
+ ast_value_delete(typevar);
+ return false;
+ }
+
+ varent.var = (ast_expression*)thinkfunc;
+ varent.name = util_strdup(thinkfunc->name);
+ (void)!parser_t_globals_add(parser, varent);
+ nextthink = (ast_expression*)thinkfunc;
+
+ } else {
+ nextthink = parser_expression_leave(parser, true);
+ if (!nextthink) {
+ ast_unref(framenum);
+ parseerror(parser, "expected a think-function in [frame,think] notation");
+ ast_value_delete(typevar);
+ return false;
+ }
+ }
+
+ if (!ast_istype(nextthink, ast_value)) {
+ ast_unref(nextthink);
+ ast_unref(framenum);
+ parseerror(parser, "think-function in [frame,think] notation must be a constant");
+ }
+
+ if (parser->tok != ']') {
+ parseerror(parser, "expected closing `]` for [frame,think] notation");
+ ast_unref(nextthink);
+ ast_unref(framenum);
+ ast_value_delete(typevar);
+ return false;
+ }
+
+ if (!parser_next(parser)) {
+ ast_unref(nextthink);
+ ast_unref(framenum);
+ ast_value_delete(typevar);
+ return false;
+ }
+
+ if (parser->tok != '{') {
+ parseerror(parser, "a function body has to be declared after a [frame,think] declaration");
+ ast_unref(nextthink);
+ ast_unref(framenum);
+ ast_value_delete(typevar);
+ return false;
+ }
+
+ has_frame_think = true;
+ }
+
+ block = ast_block_new(parser_ctx(parser));
+ if (!block) {
+ parseerror(parser, "failed to allocate block");
+ ast_value_delete(typevar);
+ return false;
+ }
+
+ if (has_frame_think) {
+ lex_ctx ctx;
+ ast_expression *self_frame;
+ ast_expression *self_nextthink;
+ ast_expression *self_think;
+ ast_expression *time_plus_1;
+ ast_store *store_frame;
+ ast_store *store_nextthink;
+ ast_store *store_think;
+
+ ctx = parser_ctx(parser);
+ self_frame = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_frame);
+ self_nextthink = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_nextthink);
+ self_think = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_think);
+
+ time_plus_1 = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F,
+ gbl_time, (ast_expression*)parser_const_float(parser, 0.1));
+
+ store_frame = ast_store_new(ctx, INSTR_STOREP_F, self_frame, framenum);
+ store_nextthink = ast_store_new(ctx, INSTR_STOREP_F, self_nextthink, time_plus_1);
+ store_think = ast_store_new(ctx, INSTR_STOREP_FNC, self_think, nextthink);
+
+ if (!ast_block_exprs_add(block, (ast_expression*)store_frame) ||
+ !ast_block_exprs_add(block, (ast_expression*)store_nextthink) ||
+ !ast_block_exprs_add(block, (ast_expression*)store_think) )
+ {
+ parseerror(parser, "failed to generate code for [frame,think]");
+ ast_block_delete(block);
+ ast_value_delete(typevar);
+ return false;
+ }
+ }
+
+ for (parami = 0; parami < var->expression.params_count; ++parami) {
+ ast_value *param = var->expression.params[parami];
+ varentry_t vx, vy, vz;
+
+ if (param->expression.vtype != TYPE_VECTOR &&
+ (param->expression.vtype != TYPE_FIELD ||
+ param->expression.next->expression.vtype != TYPE_VECTOR))
+ {
+ continue;
+ }
+
+ if (!create_vector_members(parser, param, &vx, &vy, &vz)) {
+ ast_block_delete(block);
+ ast_value_delete(typevar);
+ return false;
+ }
+
+ (void)!parser_t_locals_add(parser, vx);
+ (void)!parser_t_locals_add(parser, vy);
+ (void)!parser_t_locals_add(parser, vz);
+ if (!ast_block_collect(block, vx.var) ||
+ !ast_block_collect(block, vy.var) ||
+ !ast_block_collect(block, vz.var) )
+ {
+ (void)!parser_pop_local(parser);
+ (void)!parser_pop_local(parser);
+ (void)!parser_pop_local(parser);
+ ast_block_delete(block);
+ ast_value_delete(typevar);
+ return false;
+ }
+ }
+
+ func = ast_function_new(ast_ctx(var), var->name, var);
+ if (!func) {
+ parseerror(parser, "failed to allocate function for `%s`", var->name);
+ ast_block_delete(block);
+ parser->function = old;
+ ast_value_delete(typevar);
+ return false;
+ }
+ if (!parser_t_functions_add(parser, func)) {
+ parseerror(parser, "failed to allocate slot for function `%s`", var->name);
+ ast_function_delete(func);
+ var->constval.vfunc = NULL;
+ ast_value_delete(typevar);
+ ast_block_delete(block);
+ parser->function = old;