/*
- * Copyright (C) 2012
+ * Copyright (C) 2012, 2013
* Wolfgang Bumiller
* Dale Weiler
*
#include "gmqcc.h"
#include "lexer.h"
+#include "ast.h"
/* beginning of locals */
#define PARSER_HT_LOCALS 2
ht htglobals;
ht *typedefs;
+ /* same as above but for the spelling corrector */
+ correct_trie_t **correct_variables;
+ size_t ***correct_variables_score; /* vector of vector of size_t* */
+
/* not to be used directly, we use the hash table */
ast_expression **_locals;
size_t *_blocklocals;
static void parser_enterblock(parser_t *parser);
static bool parser_leaveblock(parser_t *parser);
static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e);
+static void parser_addglobal(parser_t *parser, const char *name, ast_expression *e);
static bool parse_typedef(parser_t *parser);
static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring);
static ast_block* parse_block(parser_t *parser);
static bool parse_block_into(parser_t *parser, ast_block *block);
static bool parse_statement_or_block(parser_t *parser, ast_expression **out);
static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases);
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma);
-static ast_expression* parse_expression(parser_t *parser, bool stopatcomma);
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels);
+static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels);
static void parseerror(parser_t *parser, const char *fmt, ...)
{
return ( ast_expression*)util_htget(parser->htfields, name);
}
+static ast_expression* parser_find_label(parser_t *parser, const char *name)
+{
+ size_t i;
+ for(i = 0; i < vec_size(parser->labels); i++)
+ if (!strcmp(parser->labels[i]->name, name))
+ return (ast_expression*)parser->labels[i];
+ return NULL;
+}
+
static ast_expression* parser_find_global(parser_t *parser, const char *name)
{
return (ast_expression*)util_htget(parser->htglobals, name);
exprs[i] = sy->out[vec_size(sy->out)+i].out;
blocks[i] = sy->out[vec_size(sy->out)+i].block;
asvalue[i] = (ast_value*)exprs[i];
+
+ if (exprs[i]->expression.vtype == TYPE_NOEXPR &&
+ !(i != 0 && op->id == opid2('?',':')))
+ {
+ if (ast_istype(exprs[i], ast_label))
+ compile_error(ast_ctx(exprs[i]), "expected expression, got an unknown identifier");
+ else
+ compile_error(ast_ctx(exprs[i]), "not an expression");
+ (void)!compile_warning(ast_ctx(exprs[i]), WARN_DEBUG, "expression %u\n", (unsigned int)i);
+ }
}
if (blocks[0] && !vec_size(blocks[0]->exprs) && op->id != opid1(',')) {
exprs[0], exprs[1]);
break;
case opid1('^'):
- parseerror(parser, "TODO: bitxor");
+ compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor via ^");
return false;
case opid2('<','<'):
case opid2('>','>'):
+ if (CanConstFold(exprs[0], exprs[1]) && ! NotSameType(TYPE_FLOAT)) {
+ if (op->id == opid2('<','<'))
+ out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) << (int)(ConstF(1))));
+ else
+ out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) >> (int)(ConstF(1))));
+ break;
+ }
case opid3('<','<','='):
case opid3('>','>','='):
- parseerror(parser, "TODO: shifts");
+ compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts");
return false;
case opid2('|','|'):
( (generated_op == INSTR_OR)
? (immediate_is_true(ctx, asvalue[0]) || immediate_is_true(ctx, asvalue[1]))
: (immediate_is_true(ctx, asvalue[0]) && immediate_is_true(ctx, asvalue[1])) )
- ? 0 : 1);
+ ? 1 : 0);
}
else
{
return false;
}
vec_pop(parser->pot);
- if (exprs[1]->expression.vtype != exprs[2]->expression.vtype) {
+ if (!ast_compare_type(exprs[1], exprs[2])) {
ast_type_to_string(exprs[1], ty1, sizeof(ty1));
ast_type_to_string(exprs[2], ty2, sizeof(ty2));
parseerror(parser, "operands of ternary expression must have the same type, got %s and %s", ty1, ty2);
return false;
}
if (CanConstFold1(exprs[0]))
- out = (ConstF(0) ? exprs[1] : exprs[2]);
+ out = (immediate_is_true(ctx, asvalue[0]) ? exprs[1] : exprs[2]);
else
out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
break;
}
else
assignop = type_storep_instr[exprs[0]->expression.vtype];
- if (assignop == AINSTR_END ||
- !ast_compare_type(field->expression.next, exprs[1]))
+ if (assignop == AINSTR_END || !ast_compare_type(field->expression.next, exprs[1]))
{
ast_type_to_string(field->expression.next, ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
}
- else if (exprs[1]->expression.vtype != TYPE_NIL &&
- !ast_compare_type(exprs[0], exprs[1]))
+ else if (!ast_compare_type(exprs[0], exprs[1]))
{
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
return false;
}
-
if (!fun->expression.next) {
parseerror(parser, "could not determine function return type");
return false;
"call to `%s` (deprecated: %s)\n"
"-> `%s` declared here: %s:%i",
fval->name, fval->desc, fval->name, ast_ctx(fun).file,
- ast_ctx(fun).line);
+ ast_ctx(fun).line);
}
if (vec_size(fun->expression.params) != paramcount &&
vec_size(fun->expression.params) < paramcount))
{
const char *fewmany = (vec_size(fun->expression.params) > paramcount) ? "few" : "many";
- if (opts.standard == COMPILER_GMQCC)
- {
- if (fval)
- parseerror(parser, "too %s parameters for call to %s: expected %i, got %i\n"
- " -> `%s` has been declared here: %s:%i",
- fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
- fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
- else
- parseerror(parser, "too %s parameters for function call: expected %i, got %i\n"
- " -> it has been declared here: %s:%i",
- fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
- ast_ctx(fun).file, (int)ast_ctx(fun).line);
- return false;
- }
+ if (fval)
+ return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT,
+ "too %s parameters for call to %s: expected %i, got %i\n"
+ " -> `%s` has been declared here: %s:%i",
+ fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
+ fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
else
- {
- if (fval)
- return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS,
- "too %s parameters for call to %s: expected %i, got %i\n"
- " -> `%s` has been declared here: %s:%i",
- fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
- fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
- else
- return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS,
- "too %s parameters for function call: expected %i, got %i\n"
- " -> it has been declared here: %s:%i",
- fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
- ast_ctx(fun).file, (int)ast_ctx(fun).line);
- }
+ return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT,
+ "too %s parameters for function call: expected %i, got %i\n"
+ " -> it has been declared here: %s:%i",
+ fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
+ ast_ctx(fun).file, (int)ast_ctx(fun).line);
}
}
}
}
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma)
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels)
{
ast_expression *expr = NULL;
shunt sy;
bool wantop = false;
bool gotmemberof = false;
+ /* only warn once about an assignment in a truth value because the current code
+ * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part
+ */
+ bool warn_truthvalue = true;
/* count the parens because an if starts with one, so the
* end of a condition is an unmatched closing paren
if (!var)
var = parser_find_field(parser, parser_tokval(parser));
}
+ if (!var && with_labels) {
+ var = (ast_expression*)parser_find_label(parser, parser_tokval(parser));
+ if (!with_labels) {
+ ast_label *lbl = ast_label_new(parser_ctx(parser), parser_tokval(parser), true);
+ var = (ast_expression*)lbl;
+ vec_push(parser->labels, lbl);
+ }
+ }
if (!var) {
/* intrinsics */
if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
var = (ast_expression*)intrinsic_debug_typestring;
-
}
else
{
+ size_t i;
+ char *correct = NULL;
+
+ /*
+ * sometimes people use preprocessing predefs without enabling them
+ * i've done this thousands of times already myself. Lets check for
+ * it in the predef table. And diagnose it better :)
+ */
+ if (!OPTS_FLAG(FTEPP_PREDEFS)) {
+ for (i = 0; i < sizeof(ftepp_predefs)/sizeof(*ftepp_predefs); i++) {
+ if (!strcmp(ftepp_predefs[i].name, parser_tokval(parser))) {
+ parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
+ goto onerr;
+ }
+ }
+ }
+
+ /*
+ * TODO: determine the best score for the identifier: be it
+ * a variable, a field.
+ *
+ * We should also consider adding correction tables for
+ * other things as well.
+ */
+ if (OPTS_FLAG(ENHANCED_DIAGNOSTICS)) {
+ correction_t corr;
+ correct_init(&corr);
+
+ for (i = 0; i < vec_size(parser->correct_variables); i++) {
+ correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser));
+ if (strcmp(correct, parser_tokval(parser))) {
+ break;
+ } else if (correct) {
+ mem_d(correct);
+ correct = NULL;
+ }
+ }
+ correct_free(&corr);
+
+ if (correct) {
+ parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct);
+ mem_d(correct);
+ goto onerr;
+ }
+ }
parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
goto onerr;
}
if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
olast = &operators[vec_last(sy.ops).etype-1];
+#define IsAssignOp(x) (\
+ (x) == opid1('=') || \
+ (x) == opid2('+','=') || \
+ (x) == opid2('-','=') || \
+ (x) == opid2('*','=') || \
+ (x) == opid2('/','=') || \
+ (x) == opid2('%','=') || \
+ (x) == opid2('&','=') || \
+ (x) == opid2('|','=') || \
+ (x) == opid3('&','~','=') \
+ )
+ if (warn_truthvalue) {
+ if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) ||
+ (olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) ||
+ (truthvalue && !vec_size(parser->pot) && IsAssignOp(op->id))
+ )
+ {
+ (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value");
+ warn_truthvalue = false;
+ }
+ }
+
while (olast && (
(op->prec < olast->prec) ||
(op->assoc == ASSOC_LEFT && op->prec <= olast->prec) ) )
goto onerr;
}
if (parser->tok == ';' ||
- (!parens && parser->tok == ']'))
+ (!parens && (parser->tok == ']' || parser->tok == ')' || parser->tok == '}')))
{
break;
}
return NULL;
}
-static ast_expression* parse_expression(parser_t *parser, bool stopatcomma)
+static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels)
{
- ast_expression *e = parse_expression_leave(parser, stopatcomma);
+ ast_expression *e = parse_expression_leave(parser, stopatcomma, false, with_labels);
if (!e)
return NULL;
+ if (parser->tok != ';') {
+ parseerror(parser, "semicolon expected after expression");
+ ast_unref(e);
+ return NULL;
+ }
if (!parser_next(parser)) {
- ast_delete(e);
+ ast_unref(e);
return NULL;
}
return e;
vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
vec_push(parser->_blocktypedefs, vec_size(parser->_typedefs));
vec_push(parser->_block_ctx, parser_ctx(parser));
+
+ /* corrector */
+ vec_push(parser->correct_variables, correct_trie_new());
+ vec_push(parser->correct_variables_score, NULL);
}
static bool parser_leaveblock(parser_t *parser)
}
util_htdel(vec_last(parser->variables));
+ correct_del(vec_last(parser->correct_variables), vec_last(parser->correct_variables_score));
+
vec_pop(parser->variables);
+ vec_pop(parser->correct_variables);
+ vec_pop(parser->correct_variables_score);
if (!vec_size(parser->_blocklocals)) {
parseerror(parser, "internal error: parser_leaveblock with no block (2)");
return false;
vec_pop(parser->typedefs);
vec_pop(parser->_block_ctx);
+
return rv;
}
{
vec_push(parser->_locals, e);
util_htset(vec_last(parser->variables), name, (void*)e);
+
+ /* corrector */
+ correct_add (
+ vec_last(parser->correct_variables),
+ &vec_last(parser->correct_variables_score),
+ name
+ );
+}
+
+static void parser_addglobal(parser_t *parser, const char *name, ast_expression *e)
+{
+ vec_push(parser->globals, e);
+ util_htset(parser->htglobals, name, e);
+
+ /* corrector */
+ correct_add (
+ parser->correct_variables[0],
+ &parser->correct_variables_score[0],
+ name
+ );
}
static ast_expression* process_condition(parser_t *parser, ast_expression *cond, bool *_ifnot)
return false;
}
/* parse the condition */
- cond = parse_expression_leave(parser, false);
+ cond = parse_expression_leave(parser, false, true, false);
if (!cond)
return false;
/* closing paren */
ast_delete(cond);
return false;
}
+ if (!ontrue)
+ ontrue = (ast_expression*)ast_block_new(parser_ctx(parser));
/* check for an else */
if (!strcmp(parser_tokval(parser), "else")) {
/* parse into the 'else' branch */
return false;
}
/* parse the condition */
- cond = parse_expression_leave(parser, false);
+ cond = parse_expression_leave(parser, false, true, false);
if (!cond)
return false;
/* closing paren */
return false;
}
/* parse the condition */
- cond = parse_expression_leave(parser, false);
+ cond = parse_expression_leave(parser, false, true, false);
if (!cond)
return false;
/* closing paren */
typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
if (typevar || parser->tok == TOKEN_TYPENAME) {
+#if 0
if (opts.standard != COMPILER_GMQCC) {
if (parsewarning(parser, WARN_EXTENSIONS,
"current standard does not allow variable declarations in for-loop initializers"))
goto onerr;
}
+#endif
if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, 0, NULL))
goto onerr;
}
else if (parser->tok != ';')
{
- initexpr = parse_expression_leave(parser, false);
+ initexpr = parse_expression_leave(parser, false, false, false);
if (!initexpr)
goto onerr;
}
/* parse the condition */
if (parser->tok != ';') {
- cond = parse_expression_leave(parser, false);
+ cond = parse_expression_leave(parser, false, true, false);
if (!cond)
goto onerr;
}
/* parse the incrementor */
if (parser->tok != ')') {
- increment = parse_expression_leave(parser, false);
+ increment = parse_expression_leave(parser, false, false, false);
if (!increment)
goto onerr;
if (!ast_side_effects(increment)) {
}
if (parser->tok != ';') {
- exp = parse_expression(parser, false);
+ exp = parse_expression(parser, false, false);
if (!exp)
return false;
- if (exp->expression.vtype != expected->expression.next->expression.vtype) {
+ if (exp->expression.vtype != TYPE_NIL &&
+ exp->expression.vtype != expected->expression.next->expression.vtype)
+ {
parseerror(parser, "return with invalid expression");
}
if (!parser_next(parser))
parseerror(parser, "parse error");
if (expected->expression.next->expression.vtype != TYPE_VOID) {
- if (opts.standard != COMPILER_GMQCC)
- (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
- else
- parseerror(parser, "return without value");
+ (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
}
ret = ast_return_new(ctx, NULL);
}
return false;
}
+ if (!vec_size(loops)) {
+ if (is_continue)
+ parseerror(parser, "`continue` can only be used inside loops");
+ else
+ parseerror(parser, "`break` can only be used inside loops or switches");
+ }
+
if (parser->tok == TOKEN_IDENT) {
if (!OPTS_FLAG(LOOP_LABELS))
parseerror(parser, "labeled loops not activated, try using -floop-labels");
return false;
}
/* parse the operand */
- operand = parse_expression_leave(parser, false);
+ operand = parse_expression_leave(parser, false, false, false);
if (!operand)
return false;
parseerror(parser, "expected expression for case");
return false;
}
- swcase.value = parse_expression_leave(parser, false);
+ swcase.value = parse_expression_leave(parser, false, false, false);
if (!swcase.value) {
ast_delete(switchnode);
parseerror(parser, "expected expression for case");
return true;
}
+/* parse computed goto sides */
+static ast_expression *parse_goto_computed(parser_t *parser, ast_expression **side) {
+ ast_expression *on_true;
+ ast_expression *on_false;
+ ast_expression *cond;
+
+ if (!*side)
+ return NULL;
+
+ if (ast_istype(*side, ast_ternary)) {
+ ast_ternary *tern = (ast_ternary*)*side;
+ on_true = parse_goto_computed(parser, &tern->on_true);
+ on_false = parse_goto_computed(parser, &tern->on_false);
+
+ if (!on_true || !on_false) {
+ parseerror(parser, "expected label or expression in ternary");
+ if (on_true) ast_unref(on_true);
+ if (on_false) ast_unref(on_false);
+ return NULL;
+ }
+
+ cond = tern->cond;
+ tern->cond = NULL;
+ ast_delete(tern);
+ *side = NULL;
+ return (ast_expression*)ast_ifthen_new(parser_ctx(parser), cond, on_true, on_false);
+ } else if (ast_istype(*side, ast_label)) {
+ ast_goto *gt = ast_goto_new(parser_ctx(parser), ((ast_label*)*side)->name);
+ ast_goto_set_label(gt, ((ast_label*)*side));
+ *side = NULL;
+ return (ast_expression*)gt;
+ }
+ return NULL;
+}
+
static bool parse_goto(parser_t *parser, ast_expression **out)
{
- size_t i;
- ast_goto *gt;
+ ast_goto *gt = NULL;
+ ast_expression *lbl;
- if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
- parseerror(parser, "expected label name after `goto`");
+ if (!parser_next(parser))
return false;
+
+ if (parser->tok != TOKEN_IDENT) {
+ ast_expression *expression;
+
+ /* could be an expression i.e computed goto :-) */
+ if (parser->tok != '(') {
+ parseerror(parser, "expected label name after `goto`");
+ return false;
+ }
+
+ /* failed to parse expression for goto */
+ if (!(expression = parse_expression(parser, false, true)) ||
+ !(*out = parse_goto_computed(parser, &expression))) {
+ parseerror(parser, "invalid goto expression");
+ ast_unref(expression);
+ return false;
+ }
+
+ return true;
}
+ /* not computed goto */
gt = ast_goto_new(parser_ctx(parser), parser_tokval(parser));
-
- for (i = 0; i < vec_size(parser->labels); ++i) {
- if (!strcmp(parser->labels[i]->name, parser_tokval(parser))) {
- ast_goto_set_label(gt, parser->labels[i]);
- break;
+ lbl = parser_find_label(parser, gt->name);
+ if (lbl) {
+ if (!ast_istype(lbl, ast_label)) {
+ parseerror(parser, "internal error: label is not an ast_label");
+ ast_delete(gt);
+ return false;
}
+ ast_goto_set_label(gt, (ast_label*)lbl);
}
- if (i == vec_size(parser->labels))
+ else
vec_push(parser->gotos, gt);
if (!parser_next(parser) || parser->tok != ';') {
parseerror(parser, "label must be an identifier");
return false;
}
- label = ast_label_new(parser_ctx(parser), parser_tokval(parser));
- if (!label)
- return false;
- vec_push(parser->labels, label);
+ label = (ast_label*)parser_find_label(parser, parser_tokval(parser));
+ if (label) {
+ if (!label->undefined) {
+ parseerror(parser, "label `%s` already defined", label->name);
+ return false;
+ }
+ label->undefined = false;
+ }
+ else {
+ label = ast_label_new(parser_ctx(parser), parser_tokval(parser), false);
+ vec_push(parser->labels, label);
+ }
*out = (ast_expression*)label;
if (!parser_next(parser)) {
parseerror(parser, "parse error after label");
}
else
{
- ast_expression *exp = parse_expression(parser, false);
+ ast_expression *exp = parse_expression(parser, false, false);
if (!exp)
return false;
*out = exp;
}
}
+static bool parse_enum(parser_t *parser)
+{
+ qcfloat num = 0;
+ ast_value **values = NULL;
+ ast_value *var = NULL;
+ ast_value *asvalue;
+
+ ast_expression *old;
+
+ if (!parser_next(parser) || parser->tok != '{') {
+ parseerror(parser, "expected `{` after `enum` keyword");
+ return false;
+ }
+
+ while (true) {
+ if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
+ if (parser->tok == '}') {
+ /* allow an empty enum */
+ break;
+ }
+ parseerror(parser, "expected identifier or `}`");
+ goto onerror;
+ }
+
+ old = parser_find_field(parser, parser_tokval(parser));
+ if (!old)
+ old = parser_find_global(parser, parser_tokval(parser));
+ if (old) {
+ parseerror(parser, "value `%s` has already been declared here: %s:%i",
+ parser_tokval(parser), ast_ctx(old).file, ast_ctx(old).line);
+ goto onerror;
+ }
+
+ var = ast_value_new(parser_ctx(parser), parser_tokval(parser), TYPE_FLOAT);
+ vec_push(values, var);
+ var->cvq = CV_CONST;
+ var->hasvalue = true;
+ var->constval.vfloat = num++;
+
+ parser_addglobal(parser, var->name, (ast_expression*)var);
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected `=`, `}` or comma after identifier");
+ goto onerror;
+ }
+
+ if (parser->tok == ',')
+ continue;
+ if (parser->tok == '}')
+ break;
+ if (parser->tok != '=') {
+ parseerror(parser, "expected `=`, `}` or comma after identifier");
+ goto onerror;
+ }
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected expression after `=`");
+ goto onerror;
+ }
+
+ /* We got a value! */
+ old = parse_expression_leave(parser, true, false, false);
+ asvalue = (ast_value*)old;
+ if (!ast_istype(old, ast_value) || asvalue->cvq != CV_CONST || !asvalue->hasvalue) {
+ parseerror(parser, "enumeration value for must be a constant");
+ goto onerror;
+ }
+ num = (var->constval.vfloat = asvalue->constval.vfloat) + 1;
+
+ if (parser->tok == '}')
+ break;
+ if (parser->tok != ',') {
+ parseerror(parser, "expected `}` or comma after expression");
+ goto onerror;
+ }
+ }
+
+ if (parser->tok != '}') {
+ parseerror(parser, "internal error: breaking without `}`");
+ goto onerror;
+ }
+
+ if (!parser_next(parser) || parser->tok != ';') {
+ parseerror(parser, "expected semicolon after enumeration");
+ goto onerror;
+ }
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "parse error after enumeration");
+ goto onerror;
+ }
+
+ vec_free(values);
+ return true;
+
+onerror:
+ vec_free(values);
+ return false;
+}
+
static bool parse_block_into(parser_t *parser, ast_block *block)
{
bool retval = true;
if (!parser_next(parser))
return false;
- framenum = parse_expression_leave(parser, true);
+ framenum = parse_expression_leave(parser, true, false, false);
if (!framenum) {
parseerror(parser, "expected a framenumber constant in[frame,think] notation");
return false;
return false;
}
- vec_push(parser->globals, (ast_expression*)thinkfunc);
- util_htset(parser->htglobals, thinkfunc->name, thinkfunc);
+ parser_addglobal(parser, thinkfunc->name, (ast_expression*)thinkfunc);
+
nextthink = (ast_expression*)thinkfunc;
} else {
- nextthink = parse_expression_leave(parser, true);
+ nextthink = parse_expression_leave(parser, true, false, false);
if (!nextthink) {
ast_unref(framenum);
parseerror(parser, "expected a think-function in [frame,think] notation");
return NULL;
}
- cexp = parse_expression_leave(parser, true);
+ cexp = parse_expression_leave(parser, true, false, false);
if (!cexp || !ast_istype(cexp, ast_value)) {
if (cexp)
}
av = (var ->expression.flags & AST_FLAG_NORETURN);
ao = (proto->expression.flags & AST_FLAG_NORETURN);
- if (av != ao) {
+ if (!av != !ao) {
return !parsewarning(parser, WARN_DIFFERENT_ATTRIBUTES,
"`%s` declared with different attributes%s\n"
" -> previous declaration here: %s:%i",
{
/* other globals */
if (old) {
- if (opts.standard == COMPILER_GMQCC) {
- parseerror(parser, "global `%s` already declared here: %s:%i",
- var->name, ast_ctx(old).file, ast_ctx(old).line);
+ if (parsewarning(parser, WARN_DOUBLE_DECLARATION,
+ "global `%s` already declared here: %s:%i",
+ var->name, ast_ctx(old).file, ast_ctx(old).line))
+ {
retval = false;
goto cleanup;
- } else {
- if (parsewarning(parser, WARN_DOUBLE_DECLARATION,
- "global `%s` already declared 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");
- retval = false;
- proto = NULL;
- goto cleanup;
- }
- if (!parser_check_qualifiers(parser, var, proto)) {
- retval = false;
- proto = NULL;
- goto cleanup;
- }
- proto->expression.flags |= var->expression.flags;
- ast_delete(var);
- var = proto;
}
+ proto = (ast_value*)old;
+ if (!ast_istype(old, ast_value)) {
+ parseerror(parser, "internal error: not an ast_value");
+ retval = false;
+ proto = NULL;
+ goto cleanup;
+ }
+ if (!parser_check_qualifiers(parser, var, proto)) {
+ retval = false;
+ proto = NULL;
+ goto cleanup;
+ }
+ proto->expression.flags |= var->expression.flags;
+ ast_delete(var);
+ var = proto;
}
if (opts.standard == COMPILER_QCC &&
(old = parser_find_field(parser, var->name)))
}
}
else {
- vec_push(parser->globals, (ast_expression*)var);
- util_htset(parser->htglobals, var->name, var);
+ parser_addglobal(parser, var->name, (ast_expression*)var);
if (isvector) {
for (i = 0; i < 3; ++i) {
- vec_push(parser->globals, (ast_expression*)me[i]);
- util_htset(parser->htglobals, me[i]->name, me[i]);
+ parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]);
}
}
}
/* Add it to the local scope */
util_htset(vec_last(parser->variables), var->name, (void*)var);
+
+ /* corrector */
+ correct_add (
+ vec_last(parser->correct_variables),
+ &vec_last(parser->correct_variables_score),
+ var->name
+ );
+
/* now rename the global */
ln = strlen(var->name);
vec_append(defname, ln, var->name);
for (i = 0; i < 3; ++i) {
util_htset(vec_last(parser->variables), me[i]->name, (void*)(me[i]));
+ /* corrector */
+ correct_add(
+ vec_last(parser->correct_variables),
+ &vec_last(parser->correct_variables_score),
+ me[i]->name
+ );
+
vec_shrinkto(defname, prefix_len);
ln = strlen(me[i]->name);
vec_append(defname, ln, me[i]->name);
ast_expression *cexp;
ast_value *cval;
- cexp = parse_expression_leave(parser, true);
+ cexp = parse_expression_leave(parser, true, false, false);
if (!cexp)
break;
}
else
{
- if (opts.standard != COMPILER_GMQCC &&
- !OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
+ if (!OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
qualifier != CV_VAR)
{
var->cvq = CV_CONST;
return false;
return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags, vstring);
}
+ else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "enum"))
+ {
+ return parse_enum(parser);
+ }
else if (parser->tok == TOKEN_KEYWORD)
{
if (!strcmp(parser_tokval(parser), "typedef")) {
vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
vec_push(parser->_blocktypedefs, 0);
+ /* corrector */
+ vec_push(parser->correct_variables, correct_trie_new());
+ vec_push(parser->correct_variables_score, NULL);
+
empty_ctx.file = "<internal>";
empty_ctx.line = 0;
parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL);
vec_free(parser->_blocklocals);
vec_free(parser->_locals);
+ /* corrector */
+ for (i = 0; i < vec_size(parser->correct_variables); ++i) {
+ correct_del(parser->correct_variables[i], parser->correct_variables_score[i]);
+ }
+ vec_free(parser->correct_variables);
+ vec_free(parser->correct_variables_score);
+
+
for (i = 0; i < vec_size(parser->_typedefs); ++i)
ast_delete(parser->_typedefs[i]);
vec_free(parser->_typedefs);