* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include <stdio.h>
-#include <stdarg.h>
+#include <string.h>
#include <math.h>
#include "gmqcc.h"
lex_file *lex;
int tok;
+ bool ast_cleaned;
+
ast_expression **globals;
ast_expression **fields;
ast_function **functions;
/* code generator */
code_t *code;
-
- /* vector of global return vars.
- * for example, you can return string, float, vector, or other
- * things, hese will be created as globals here instead of
- * locals in a function (saves space).
- */
- ast_value **returns;
} parser_t;
static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1;
char name[32];
util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
ast_value_set_name(out, name);
+ out->expression.flags |= AST_FLAG_INCLUDE_DEF;
}
return out;
}
char name[32];
util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
out = ast_value_new(parser_ctx(parser), name, TYPE_STRING);
+ out->expression.flags |= AST_FLAG_INCLUDE_DEF;
} else
out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_STRING);
out->cvq = CV_CONST;
return (ast_expression*)util_htget(parser->htglobals, name);
}
-static ast_value* parser_find_returnvalue(parser_t *parser, int vtype)
-{
- ast_value *out;
- size_t i;
- char *name = NULL;
-
- /* find existing global for the job */
- for (i = 0; i < vec_size(parser->returns); i++)
- if (parser->returns[i]->expression.vtype == vtype)
- return parser->returns[i];
-
- util_asprintf(&name, "#ret_%s", type_name[vtype]);
-
- out = ast_value_new(parser_ctx(parser), name, vtype);
- out->hasvalue = false;
- out->isimm = false;
-
- vec_push(parser->returns, out);
-
- mem_d(name);
- return out;
-}
-
static ast_expression* parser_find_param(parser_t *parser, const char *name)
{
size_t i;
* We should also consider adding correction tables for
* other things as well.
*/
- if (OPTS_OPTION_BOOL(OPTION_CORRECTION)) {
+ if (OPTS_OPTION_BOOL(OPTION_CORRECTION) && strlen(parser_tokval(parser)) <= 16) {
correction_t corr;
correct_init(&corr);
ast_expression *exp = NULL;
ast_expression *var = NULL;
ast_return *ret = NULL;
- ast_expression *find = NULL;
+ ast_value *retval = parser->function->return_value;
ast_value *expected = parser->function->vtype;
lex_ctx ctx = parser_ctx(parser);
parseerror(parser, "return assignments not activated, try using -freturn-assigments");
return false;
}
-
+
+ if (type_store_instr[expected->expression.next->vtype] == VINSTR_END) {
+ char ty1[1024];
+ ast_type_to_string(expected->expression.next, ty1, sizeof(ty1));
+ parseerror(parser, "invalid return type: `%s'", ty1);
+ return false;
+ }
+
if (!parser_next(parser)) {
parseerror(parser, "expected return assignment expression");
return false;
}
-
+
if (!(exp = parse_expression_leave(parser, false, false, false)))
return false;
-
- if (exp->vtype != TYPE_NIL &&
- exp->vtype != ((ast_expression*)expected)->next->vtype)
- {
- parseerror(parser, "return assignment with invalid expression");
+
+ /* prepare the return value */
+ if (!retval) {
+ retval = ast_value_new(ctx, "#LOCAL_RETURN", TYPE_VOID);
+ ast_type_adopt(retval, expected->expression.next);
+ parser->function->return_value = retval;
}
-
+
+ if (!ast_compare_type(exp, (ast_expression*)retval)) {
+ char ty1[1024], ty2[1024];
+ ast_type_to_string(exp, ty1, sizeof(ty1));
+ ast_type_to_string(&retval->expression, ty2, sizeof(ty2));
+ parseerror(parser, "invalid type for return value: `%s', expected `%s'", ty1, ty2);
+ }
+
/* store to 'return' local variable */
var = (ast_expression*)ast_store_new(
ctx,
- type_store_instr[exp->vtype],
- (ast_expression*)parser_find_returnvalue(parser, exp->vtype),
- (ast_expression*)exp
- );
-
+ type_store_instr[expected->expression.next->vtype],
+ (ast_expression*)retval, exp);
+
if (!var) {
ast_unref(exp);
return false;
}
-
+
+ if (parser->tok != ';')
+ parseerror(parser, "missing semicolon after return assignment");
+ else if (!parser_next(parser))
+ parseerror(parser, "parse error after return assignment");
+
*out = var;
return true;
}
} else {
if (!parser_next(parser))
parseerror(parser, "parse error");
-
- /* build expression to return */
- if ((find = (ast_expression*)parser_find_returnvalue(parser, expected->expression.next->vtype)) && OPTS_FLAG(RETURN_ASSIGNMENTS))
- ret = ast_return_new(ctx, find);
-
- else if (expected->expression.next->vtype != TYPE_VOID)
+
+ if (!retval && expected->expression.next->vtype != TYPE_VOID)
{
(void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
- ret = ast_return_new(ctx, NULL);
}
+ ret = ast_return_new(ctx, (ast_expression*)retval);
}
*out = (ast_expression*)ret;
return true;
}
return parse_typedef(parser);
}
- parseerror(parser, "Unexpected keyword");
+ parseerror(parser, "Unexpected keyword: `%s'", parser_tokval(parser));
return false;
}
else if (parser->tok == '{')
}
vec_push(func->blocks, block);
-
+
parser->function = old;
if (!parser_leaveblock(parser))
/* for the sake of less code we parse-in in this function */
if (!parser_next(parser)) {
+ ast_delete(var);
parseerror(parser, "expected parameter list");
return NULL;
}
return NULL;
}
- cexp = parse_expression_leave(parser, true, false, false);
+ if (parser->tok != ']') {
+ cexp = parse_expression_leave(parser, true, false, false);
- if (!cexp || !ast_istype(cexp, ast_value)) {
- if (cexp)
- ast_unref(cexp);
- ast_delete(var);
- parseerror(parser, "expected array-size as constant positive integer");
- return NULL;
+ if (!cexp || !ast_istype(cexp, ast_value)) {
+ if (cexp)
+ ast_unref(cexp);
+ ast_delete(var);
+ parseerror(parser, "expected array-size as constant positive integer");
+ return NULL;
+ }
+ cval = (ast_value*)cexp;
+ }
+ else {
+ cexp = NULL;
+ cval = NULL;
}
- cval = (ast_value*)cexp;
tmp = ast_value_new(ctx, "<type[]>", TYPE_ARRAY);
tmp->expression.next = (ast_expression*)var;
var = tmp;
- if (cval->expression.vtype == TYPE_INTEGER)
- tmp->expression.count = cval->constval.vint;
- else if (cval->expression.vtype == TYPE_FLOAT)
- tmp->expression.count = cval->constval.vfloat;
- else {
+ if (cval) {
+ if (cval->expression.vtype == TYPE_INTEGER)
+ tmp->expression.count = cval->constval.vint;
+ else if (cval->expression.vtype == TYPE_FLOAT)
+ tmp->expression.count = cval->constval.vfloat;
+ else {
+ ast_unref(cexp);
+ ast_delete(var);
+ parseerror(parser, "array-size must be a positive integer constant");
+ return NULL;
+ }
+
ast_unref(cexp);
- ast_delete(var);
- parseerror(parser, "array-size must be a positive integer constant");
- return NULL;
+ } else {
+ var->expression.count = -1;
+ var->expression.flags |= AST_FLAG_ARRAY_INIT;
}
- ast_unref(cexp);
if (parser->tok != ']') {
ast_delete(var);
/* parse on */
if (!parser_next(parser)) {
ast_delete(var);
+ mem_d(name);
parseerror(parser, "error after variable or field declaration");
return NULL;
}
if (parser->tok == '[') {
wasarray = true;
var = parse_arraysize(parser, var);
- if (!var)
+ if (!var) {
+ if (name) mem_d(name);
return NULL;
+ }
}
/* This is the point where we can turn it into a field */
while (parser->tok == '(') {
var = parse_parameter_list(parser, var);
if (!var) {
- if (name)
- mem_d((void*)name);
+ if (name) mem_d(name);
return NULL;
}
}
if (name) {
if (!ast_value_set_name(var, name)) {
ast_delete(var);
+ mem_d(name);
parseerror(parser, "internal error: failed to set name");
return NULL;
}
/* free the name, ast_value_set_name duplicates */
- mem_d((void*)name);
+ mem_d(name);
}
return var;
return true;
}
+static bool create_array_accessors(parser_t *parser, ast_value *var)
+{
+ char name[1024];
+ util_snprintf(name, sizeof(name), "%s##SET", var->name);
+ if (!parser_create_array_setter(parser, var, name))
+ return false;
+ util_snprintf(name, sizeof(name), "%s##GET", var->name);
+ if (!parser_create_array_getter(parser, var, var->expression.next, name))
+ return false;
+ return true;
+}
+
+static bool parse_array(parser_t *parser, ast_value *array)
+{
+ size_t i;
+ if (array->initlist) {
+ parseerror(parser, "array already initialized elsewhere");
+ return false;
+ }
+ if (!parser_next(parser)) {
+ parseerror(parser, "parse error in array initializer");
+ return false;
+ }
+ i = 0;
+ while (parser->tok != '}') {
+ ast_value *v = (ast_value*)parse_expression_leave(parser, true, false, false);
+ if (!v)
+ return false;
+ if (!ast_istype(v, ast_value) || !v->hasvalue || v->cvq != CV_CONST) {
+ ast_unref(v);
+ parseerror(parser, "initializing element must be a compile time constant");
+ return false;
+ }
+ vec_push(array->initlist, v->constval);
+ if (v->expression.vtype == TYPE_STRING) {
+ array->initlist[i].vstring = util_strdupe(array->initlist[i].vstring);
+ ++i;
+ }
+ ast_unref(v);
+ if (parser->tok == '}')
+ break;
+ if (parser->tok != ',' || !parser_next(parser)) {
+ parseerror(parser, "expected comma or '}' in element list");
+ return false;
+ }
+ }
+ if (!parser_next(parser) || parser->tok != ';') {
+ parseerror(parser, "expected semicolon after initializer, got %s");
+ return false;
+ }
+ /*
+ if (!parser_next(parser)) {
+ parseerror(parser, "parse error after initializer");
+ return false;
+ }
+ */
+
+ if (array->expression.flags & AST_FLAG_ARRAY_INIT) {
+ if (array->expression.count != (size_t)-1) {
+ parseerror(parser, "array `%s' has already been initialized with %u elements",
+ array->name, (unsigned)array->expression.count);
+ }
+ array->expression.count = vec_size(array->initlist);
+ if (!create_array_accessors(parser, array))
+ return false;
+ }
+ return true;
+}
+
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)
{
ast_value *var;
* deal with arrays
*/
if (var->expression.vtype == TYPE_ARRAY) {
- char name[1024];
- util_snprintf(name, sizeof(name), "%s##SET", var->name);
- if (!parser_create_array_setter(parser, var, name))
- goto cleanup;
- util_snprintf(name, sizeof(name), "%s##GET", var->name);
- if (!parser_create_array_getter(parser, var, var->expression.next, name))
- goto cleanup;
+ if (var->expression.count != (size_t)-1) {
+ if (!create_array_accessors(parser, var))
+ goto cleanup;
+ }
}
else if (!localblock && !nofields &&
var->expression.vtype == TYPE_FIELD &&
parseerror(parser, "TODO: initializers for local arrays");
break;
}
- /*
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels);
-*/
- parseerror(parser, "TODO: initializing global arrays is not supported yet!");
- break;
+
+ var->hasvalue = true;
+ if (!parse_array(parser, var))
+ break;
}
else if (var->expression.vtype == TYPE_FUNCTION && (parser->tok == '{' || parser->tok == '['))
{
parser->reserved_version = NULL;
}
- parser->returns = NULL;
return parser;
}
return parser_compile(parser);
}
-void parser_cleanup(parser_t *parser)
+static void parser_remove_ast(parser_t *parser)
{
size_t i;
+ if (parser->ast_cleaned)
+ return;
+ parser->ast_cleaned = true;
for (i = 0; i < vec_size(parser->accessors); ++i) {
ast_delete(parser->accessors[i]->constval.vfunc);
parser->accessors[i]->constval.vfunc = NULL;
for (i = 0; i < vec_size(parser->globals); ++i) {
ast_delete(parser->globals[i]);
}
- for (i = 0; i < vec_size(parser->returns); ++i) {
- ast_delete(parser->returns[i]);
- }
vec_free(parser->accessors);
vec_free(parser->functions);
vec_free(parser->imm_vector);
ast_value_delete(parser->const_vec[2]);
util_htdel(parser->aliases);
-
intrin_intrinsics_destroy(parser);
+}
+void parser_cleanup(parser_t *parser)
+{
+ parser_remove_ast(parser);
code_cleanup(parser->code);
mem_d(parser);
return false;
}
}
- for (i = 0; i < vec_size(parser->returns); ++i) {
- if (!ast_global_codegen(parser->returns[i], ir, false)) {
- con_out("internal error: failed to generate return assignment %s\n", parser->returns[i]->name);
- ir_builder_delete(ir);
- return false;
- }
- }
if (parser->reserved_version &&
!ast_global_codegen(parser->reserved_version, ir, false))
{
return false;
}
}
+
+ generate_checksum(parser);
if (OPTS_OPTION_BOOL(OPTION_DUMP))
ir_builder_dump(ir, con_out);
for (i = 0; i < vec_size(parser->functions); ++i) {
return false;
}
}
+ parser_remove_ast(parser);
if (compile_Werrors) {
con_out("*** there were warnings treated as errors\n");
if (OPTS_OPTION_BOOL(OPTION_DUMPFIN))
ir_builder_dump(ir, con_out);
- generate_checksum(parser);
-
if (!ir_builder_generate(parser->code, ir, output)) {
con_out("*** failed to generate output file\n");
ir_builder_delete(ir);
return false;
}
}
-
ir_builder_delete(ir);
return retval;
}