/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014
* Wolfgang Bumiller
* Dale Weiler
*
/* returns true when it was a variable qualifier, false otherwise!
* on error, cvq is set to CV_WRONG
*/
+typedef struct {
+ const char *name;
+ size_t flag;
+} attribute_t;
+
static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags, char **message)
{
bool had_const = false;
bool had_static = false;
uint32_t flags = 0;
- *cvq = CV_NONE;
+ static attribute_t attributes[] = {
+ { "noreturn", AST_FLAG_NORETURN },
+ { "inline", AST_FLAG_INLINE },
+ { "eraseable", AST_FLAG_ERASEABLE },
+ { "accumulate", AST_FLAG_ACCUMULATE },
+ { "last", AST_FLAG_FINAL_DECL }
+ };
+
+ *cvq = CV_NONE;
+
for (;;) {
+ size_t i;
if (parser->tok == TOKEN_ATTRIBUTE_OPEN) {
had_attrib = true;
/* parse an attribute */
*cvq = CV_WRONG;
return false;
}
- if (!strcmp(parser_tokval(parser), "noreturn")) {
- flags |= AST_FLAG_NORETURN;
- if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
- parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`");
- *cvq = CV_WRONG;
- return false;
+
+ for (i = 0; i < GMQCC_ARRAY_COUNT(attributes); i++) {
+ if (!strcmp(parser_tokval(parser), attributes[i].name)) {
+ flags |= attributes[i].flag;
+ if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+ parseerror(parser, "`%s` attribute has no parameters, expected `]]`",
+ attributes[i].name);
+ *cvq = CV_WRONG;
+ return false;
+ }
+ break;
}
}
- else if (!strcmp(parser_tokval(parser), "noref")) {
+
+ if (i != GMQCC_ARRAY_COUNT(attributes))
+ goto leave;
+
+
+ if (!strcmp(parser_tokval(parser), "noref")) {
had_noref = true;
if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
parseerror(parser, "`noref` attribute has no parameters, expected `]]`");
return false;
}
}
- else if (!strcmp(parser_tokval(parser), "inline")) {
- flags |= AST_FLAG_INLINE;
- if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
- parseerror(parser, "`inline` attribute has no parameters, expected `]]`");
- *cvq = CV_WRONG;
- return false;
- }
- }
- else if (!strcmp(parser_tokval(parser), "eraseable")) {
- flags |= AST_FLAG_ERASEABLE;
- if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
- parseerror(parser, "`eraseable` attribute has no parameters, expected `]]`");
- *cvq = CV_WRONG;
- return false;
- }
- }
- else if (!strcmp(parser_tokval(parser), "accumulate")) {
- flags |= AST_FLAG_ACCUMULATE;
- if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
- parseerror(parser, "`accumulate` attribute has no parameters, expected `]]`");
- *cvq = CV_WRONG;
- return false;
- }
- }
else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) {
flags |= AST_FLAG_ALIAS;
*message = NULL;
return false;
}
}
+ else if (!strcmp(parser_tokval(parser), "coverage") && !(flags & AST_FLAG_COVERAGE)) {
+ flags |= AST_FLAG_COVERAGE;
+ if (!parser_next(parser)) {
+ error_in_coverage:
+ parseerror(parser, "parse error in coverage attribute");
+ *cvq = CV_WRONG;
+ return false;
+ }
+ if (parser->tok == '(') {
+ if (!parser_next(parser)) {
+ bad_coverage_arg:
+ parseerror(parser, "invalid parameter for coverage() attribute\n"
+ "valid are: block");
+ *cvq = CV_WRONG;
+ return false;
+ }
+ if (parser->tok != ')') {
+ do {
+ if (parser->tok != TOKEN_IDENT)
+ goto bad_coverage_arg;
+ if (!strcmp(parser_tokval(parser), "block"))
+ flags |= AST_FLAG_BLOCK_COVERAGE;
+ else if (!strcmp(parser_tokval(parser), "none"))
+ flags &= ~(AST_FLAG_COVERAGE_MASK);
+ else
+ goto bad_coverage_arg;
+ if (!parser_next(parser))
+ goto error_in_coverage;
+ if (parser->tok == ',') {
+ if (!parser_next(parser))
+ goto error_in_coverage;
+ }
+ } while (parser->tok != ')');
+ }
+ if (parser->tok != ')' || !parser_next(parser))
+ goto error_in_coverage;
+ } else {
+ /* without parameter [[coverage]] equals [[coverage(block)]] */
+ flags |= AST_FLAG_BLOCK_COVERAGE;
+ }
+ }
else
{
/* Skip tokens until we hit a ]] */
}
else
break;
+
+ leave:
if (!parser_next(parser))
goto onerr;
}
}
if (has_frame_think) {
- lex_ctx_t 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*)fold_constgen_float(parser->fold, 0.1f));
-
- if (!self_frame || !self_nextthink || !self_think || !time_plus_1) {
- if (self_frame) ast_delete(self_frame);
- if (self_nextthink) ast_delete(self_nextthink);
- if (self_think) ast_delete(self_think);
- if (time_plus_1) ast_delete(time_plus_1);
- retval = false;
- }
-
- if (retval)
- {
- 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 (!store_frame) {
- ast_delete(self_frame);
- retval = false;
- }
- if (!store_nextthink) {
- ast_delete(self_nextthink);
- retval = false;
- }
- if (!store_think) {
- ast_delete(self_think);
- retval = false;
+ if (!OPTS_FLAG(EMULATE_STATE)) {
+ ast_state *state_op = ast_state_new(parser_ctx(parser), framenum, nextthink);
+ if (!ast_block_add_expr(block, (ast_expression*)state_op)) {
+ parseerror(parser, "failed to generate state op for [frame,think]");
+ ast_unref(nextthink);
+ ast_unref(framenum);
+ ast_delete(block);
+ return false;
}
- if (!retval) {
- if (store_frame) ast_delete(store_frame);
- if (store_nextthink) ast_delete(store_nextthink);
- if (store_think) ast_delete(store_think);
+ } else {
+ /* emulate OP_STATE in code: */
+ lex_ctx_t 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;
+
+ float frame_delta = 1.0f / (float)OPTS_OPTION_U32(OPTION_STATE_FPS);
+
+ 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*)fold_constgen_float(parser->fold, frame_delta));
+
+ if (!self_frame || !self_nextthink || !self_think || !time_plus_1) {
+ if (self_frame) ast_delete(self_frame);
+ if (self_nextthink) ast_delete(self_nextthink);
+ if (self_think) ast_delete(self_think);
+ if (time_plus_1) ast_delete(time_plus_1);
retval = false;
}
- if (!ast_block_add_expr(block, (ast_expression*)store_frame) ||
- !ast_block_add_expr(block, (ast_expression*)store_nextthink) ||
- !ast_block_add_expr(block, (ast_expression*)store_think))
+
+ if (retval)
{
- retval = false;
+ 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 (!store_frame) {
+ ast_delete(self_frame);
+ retval = false;
+ }
+ if (!store_nextthink) {
+ ast_delete(self_nextthink);
+ retval = false;
+ }
+ if (!store_think) {
+ ast_delete(self_think);
+ retval = false;
+ }
+ if (!retval) {
+ if (store_frame) ast_delete(store_frame);
+ if (store_nextthink) ast_delete(store_nextthink);
+ if (store_think) ast_delete(store_think);
+ retval = false;
+ }
+ if (!ast_block_add_expr(block, (ast_expression*)store_frame) ||
+ !ast_block_add_expr(block, (ast_expression*)store_nextthink) ||
+ !ast_block_add_expr(block, (ast_expression*)store_think))
+ {
+ retval = false;
+ }
}
- }
- if (!retval) {
- parseerror(parser, "failed to generate code for [frame,think]");
- ast_unref(nextthink);
- ast_unref(framenum);
- ast_delete(block);
- return false;
+ if (!retval) {
+ parseerror(parser, "failed to generate code for [frame,think]");
+ ast_unref(nextthink);
+ ast_unref(framenum);
+ ast_delete(block);
+ return false;
+ }
}
}
parseerror(parser, "failed to create accessor function value");
return false;
}
+ fval->expression.flags &= ~(AST_FLAG_COVERAGE_MASK);
func = ast_function_new(ast_ctx(array), funcname, fval);
if (!func) {
}
var->cvq = qualifier;
+ if (qflags & AST_FLAG_COVERAGE) /* specified in QC, drop our default */
+ var->expression.flags &= ~(AST_FLAG_COVERAGE_MASK);
var->expression.flags |= qflags;
/*
retval = false;
goto cleanup;
}
+ if (old->flags & AST_FLAG_FINAL_DECL) {
+ parseerror(parser, "cannot redeclare variable `%s`, declared final here: %s:%i",
+ var->name, ast_ctx(old).file, ast_ctx(old).line);
+ retval = false;
+ goto cleanup;
+ }
proto = (ast_value*)old;
if (!ast_istype(old, ast_value)) {
parseerror(parser, "internal error: not an ast_value");
goto cleanup;
}
proto->expression.flags |= var->expression.flags;
+ /* copy the context for finals,
+ * so the error can show where it was actually made 'final'
+ */
+ if (proto->expression.flags & AST_FLAG_FINAL_DECL)
+ ast_ctx(old) = ast_ctx(var);
ast_delete(var);
var = proto;
}
mem_d(parser);
}
+static bool parser_set_coverage_func(parser_t *parser, ir_builder *ir) {
+ size_t i;
+ ast_expression *expr;
+ ast_value *cov;
+ ast_function *func;
+
+ if (!OPTS_OPTION_BOOL(OPTION_COVERAGE))
+ return true;
+
+ func = NULL;
+ for (i = 0; i != vec_size(parser->functions); ++i) {
+ if (!strcmp(parser->functions[i]->name, "coverage")) {
+ func = parser->functions[i];
+ break;
+ }
+ }
+ if (!func) {
+ if (OPTS_OPTION_BOOL(OPTION_COVERAGE)) {
+ con_out("coverage support requested but no coverage() builtin declared\n");
+ ir_builder_delete(ir);
+ return false;
+ }
+ return true;
+ }
+
+ cov = func->vtype;
+ expr = (ast_expression*)cov;
+
+ if (expr->vtype != TYPE_FUNCTION || vec_size(expr->params) != 0) {
+ char ty[1024];
+ ast_type_to_string(expr, ty, sizeof(ty));
+ con_out("invalid type for coverage(): %s\n", ty);
+ ir_builder_delete(ir);
+ return false;
+ }
+
+ ir->coverage_func = func->ir_func->value;
+ return true;
+}
+
bool parser_finish(parser_t *parser, const char *output)
{
- size_t i;
- ir_builder *ir;
- bool retval = true;
+ size_t i;
+ ir_builder *ir;
+ bool retval = true;
if (compile_errors) {
con_out("*** there were compile errors\n");
if (!fold_generate(parser->fold, ir))
return false;
+ /* before generating any functions we need to set the coverage_func */
+ if (!parser_set_coverage_func(parser, ir))
+ return false;
+
for (i = 0; i < vec_size(parser->globals); ++i) {
ast_value *asvalue;
if (!ast_istype(parser->globals[i], ast_value))