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;
/* we store the '=' operator info */
const oper_info *assign_op;
- /* TYPE_FIELD -> parser_find_fields is used instead of find_var
- * TODO: TYPE_VECTOR -> x, y and z are accepted in the gmqcc standard
- * anything else: type error
- */
- qcint memberof;
-
/* Keep track of our ternary vs parenthesis nesting state.
* If we reach a 'comma' operator in a ternary without a paren,
* we shall trigger -Wternary-precedence.
*/
enum parser_pot *pot;
+ /* magic values */
+ ast_value *const_vec[3];
+
/* pragma flags */
bool noref;
} parser_t;
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);
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('?',':')) &&
+ !(i == 1 && op->id == opid1('.')))
+ {
+ 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(',')) {
return false;
case opid1('.'):
- if (exprs[0]->expression.vtype == TYPE_ENTITY) {
+ if (exprs[0]->expression.vtype == TYPE_VECTOR &&
+ exprs[1]->expression.vtype == TYPE_NOEXPR)
+ {
+ if (exprs[1] == (ast_expression*)parser->const_vec[0])
+ out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL);
+ else if (exprs[1] == (ast_expression*)parser->const_vec[1])
+ out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL);
+ else if (exprs[1] == (ast_expression*)parser->const_vec[2])
+ out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL);
+ else {
+ parseerror(parser, "access to invalid vector component");
+ return false;
+ }
+ }
+ else if (exprs[0]->expression.vtype == TYPE_ENTITY) {
if (exprs[1]->expression.vtype != TYPE_FIELD) {
parseerror(parser, "type error: right hand of member-operand should be an entity-field");
return false;
out = (ast_expression*)ast_entfield_new(ctx, exprs[0], exprs[1]);
}
else if (exprs[0]->expression.vtype == TYPE_VECTOR) {
- parseerror(parser, "internal error: vector access is not supposed to be handled at this point");
+ parseerror(parser, "vectors cannot be accessed this way");
return false;
}
else {
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('|','|'):
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
*/
while (true)
{
- if (gotmemberof)
- gotmemberof = false;
- else
- parser->memberof = 0;
-
if (OPTS_FLAG(TRANSLATABLE_STRINGS) &&
parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "_"))
{
}
else if (parser->tok == TOKEN_IDENT)
{
+ const char *ctoken = parser_tokval(parser);
+ ast_expression *prev = vec_size(sy.out) ? vec_last(sy.out).out : NULL;
ast_expression *var;
if (wantop) {
parseerror(parser, "expected operator or end of statement");
goto onerr;
}
wantop = true;
- /* variable */
- if (opts.standard == COMPILER_GMQCC)
+ /* a_vector.{x,y,z} */
+ if (!vec_size(sy.ops) ||
+ !vec_last(sy.ops).etype ||
+ operators[vec_last(sy.ops).etype-1].id != opid1('.') ||
+ (prev >= intrinsic_debug_typestring &&
+ prev <= intrinsic_debug_typestring))
{
- if (parser->memberof == TYPE_ENTITY) {
- /* still get vars first since there could be a fieldpointer */
- var = parser_find_var(parser, parser_tokval(parser));
- if (!var)
- var = parser_find_field(parser, parser_tokval(parser));
- }
- else if (parser->memberof == TYPE_VECTOR)
- {
- parseerror(parser, "TODO: implement effective vector member access");
- goto onerr;
- }
- else if (parser->memberof) {
- parseerror(parser, "namespace for member not found");
- goto onerr;
- }
- else
- var = parser_find_var(parser, parser_tokval(parser));
+ /* When adding more intrinsics, fix the above condition */
+ prev = NULL;
+ }
+ if (prev && prev->expression.vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1])
+ {
+ var = (ast_expression*)parser->const_vec[ctoken[0]-'x'];
} else {
var = parser_find_var(parser, parser_tokval(parser));
if (!var)
}
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;
}
olast = NULL;
}
- if (op->id == opid1('.') && opts.standard == COMPILER_GMQCC) {
- /* for gmqcc standard: open up the namespace of the previous type */
- ast_expression *prevex = vec_last(sy.out).out;
- if (!prevex) {
- parseerror(parser, "unexpected member operator");
- goto onerr;
- }
- if (prevex->expression.vtype == TYPE_ENTITY)
- parser->memberof = TYPE_ENTITY;
- else if (prevex->expression.vtype == TYPE_VECTOR)
- parser->memberof = TYPE_VECTOR;
- else {
- parseerror(parser, "type error: type has no members");
- goto onerr;
- }
- gotmemberof = true;
- }
-
if (op->id == opid1('(')) {
if (wantop) {
size_t sycount = vec_size(sy.out);
goto onerr;
}
if (parser->tok == ';' ||
- (!parens && parser->tok == ']'))
+ (!parens && (parser->tok == ']' || parser->tok == ')' || parser->tok == '}')))
{
break;
}
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)
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;
}
+ 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");
}
/* parse computed goto sides */
-static ast_expression *parse_goto_computed(parser_t *parser, ast_expression *side) {
+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)
+ if (!*side)
return NULL;
- if (ast_istype(side, ast_ternary)) {
- on_true = parse_goto_computed(parser, ((ast_ternary*)side)->on_true);
- on_false = parse_goto_computed(parser, ((ast_ternary*)side)->on_false);
+ 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 (((ast_ternary*)side)->on_false) ast_unref(((ast_ternary*)side)->on_false);
- if (((ast_ternary*)side)->on_true) ast_unref(((ast_ternary*)side)->on_true);
+ if (on_true) ast_unref(on_true);
+ if (on_false) ast_unref(on_false);
return NULL;
}
- return (ast_expression*)ast_ifthen_new(parser_ctx(parser), ((ast_ternary*)side)->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));
+ 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;
/* failed to parse expression for goto */
if (!(expression = parse_expression(parser, false, true)) ||
- !(*out = parse_goto_computed(parser, expression))) {
+ !(*out = parse_goto_computed(parser, &expression))) {
parseerror(parser, "invalid goto expression");
ast_unref(expression);
return false;
}
}
+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;
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 {
}
}
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);
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);
parser->nil->cvq = CV_CONST;
if (OPTS_FLAG(UNTYPED_NIL))
util_htset(parser->htglobals, "nil", (void*)parser->nil);
+
+ parser->const_vec[0] = ast_value_new(empty_ctx, "<vector.x>", TYPE_NOEXPR);
+ parser->const_vec[1] = ast_value_new(empty_ctx, "<vector.y>", TYPE_NOEXPR);
+ parser->const_vec[2] = ast_value_new(empty_ctx, "<vector.z>", TYPE_NOEXPR);
return true;
}
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);
ast_value_delete(parser->nil);
+ ast_value_delete(parser->const_vec[0]);
+ ast_value_delete(parser->const_vec[1]);
+ ast_value_delete(parser->const_vec[2]);
+
mem_d(parser);
}