static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels);
static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname);
static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname);
-static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
+static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef, bool *is_vararg);
static void parseerror(parser_t *parser, const char *fmt, ...)
{
type_name[exprs[0]->vtype]);
return false;
}
- out = (ast_expression*)ast_unary_new(ctx, (VINSTR_NEG_F-TYPE_FLOAT) + exprs[0]->vtype, exprs[0]);
+ if (exprs[0]->vtype == TYPE_FLOAT)
+ out = (ast_expression*)ast_unary_new(ctx, VINSTR_NEG_F, exprs[0]);
+ else
+ out = (ast_expression*)ast_unary_new(ctx, VINSTR_NEG_V, exprs[0]);
break;
case opid2('!','P'):
if (!(out = fold_op(parser->fold, op, exprs))) {
switch (exprs[0]->vtype) {
case TYPE_FLOAT:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_ADD_F, exprs[0], exprs[1]);
break;
case TYPE_VECTOR:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_V, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_ADD_V, exprs[0], exprs[1]);
break;
default:
compile_error(ctx, "invalid types used in expression: cannot add type %s and %s",
if (!(out = fold_op(parser->fold, op, exprs))) {
switch (exprs[0]->vtype) {
case TYPE_FLOAT:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_SUB_F, exprs[0], exprs[1]);
break;
case TYPE_VECTOR:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_SUB_V, exprs[0], exprs[1]);
break;
default:
compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s",
switch (exprs[0]->vtype) {
case TYPE_FLOAT:
if (exprs[1]->vtype == TYPE_VECTOR)
- out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_FV, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_MUL_FV, exprs[0], exprs[1]);
else
- out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_MUL_F, exprs[0], exprs[1]);
break;
case TYPE_VECTOR:
if (exprs[1]->vtype == TYPE_FLOAT)
- out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_MUL_VF, exprs[0], exprs[1]);
else
- out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
break;
default:
compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s",
}
if (!(out = fold_op(parser->fold, op, exprs))) {
if (exprs[0]->vtype == TYPE_FLOAT)
- out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_DIV_F, exprs[0], exprs[1]);
else {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
* since scalar ^ vector is not allowed.
*/
if (exprs[0]->vtype == TYPE_FLOAT) {
- out = (ast_expression*)ast_binary_new(ctx,
+ out = fold_binary(ctx,
(op->id == opid1('^') ? VINSTR_BITXOR : op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
exprs[0], exprs[1]);
} else {
* Bitop all the values of the vector components against the
* vectors components in question.
*/
- out = (ast_expression*)ast_binary_new(ctx,
+ out = fold_binary(ctx,
(op->id == opid1('^') ? VINSTR_BITXOR_V : op->id == opid1('|') ? VINSTR_BITOR_V : VINSTR_BITAND_V),
exprs[0], exprs[1]);
} else {
- out = (ast_expression*)ast_binary_new(ctx,
+ out = fold_binary(ctx,
(op->id == opid1('^') ? VINSTR_BITXOR_VF : op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF),
exprs[0], exprs[1]);
}
case opid2('<','<'):
case opid2('>','>'):
+ if (NotSameType(TYPE_FLOAT)) {
+ compile_error(ctx, "invalid types used in expression: cannot perform shift between types %s and %s",
+ type_name[exprs[0]->vtype],
+ type_name[exprs[1]->vtype]);
+ return false;
+ }
+
+ if (!(out = fold_op(parser->fold, op, exprs))) {
+ ast_expression *shift = intrin_func(parser->intrin, (op->id == opid2('<','<')) ? "__builtin_lshift" : "__builtin_rshift");
+ ast_call *call = ast_call_new(parser_ctx(parser), shift);
+ vec_push(call->params, exprs[0]);
+ vec_push(call->params, exprs[1]);
+ out = (ast_expression*)call;
+ }
+ break;
+
case opid3('<','<','='):
case opid3('>','>','='):
- if(!(out = fold_op(parser->fold, op, exprs))) {
- compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts");
+ if (NotSameType(TYPE_FLOAT)) {
+ compile_error(ctx, "invalid types used in expression: cannot perform shift operation between types %s and %s",
+ type_name[exprs[0]->vtype],
+ type_name[exprs[1]->vtype]);
return false;
}
+
+ if(!(out = fold_op(parser->fold, op, exprs))) {
+ ast_expression *shift = intrin_func(parser->intrin, (op->id == opid3('<','<','=')) ? "__builtin_lshift" : "__builtin_rshift");
+ ast_call *call = ast_call_new(parser_ctx(parser), shift);
+ vec_push(call->params, exprs[0]);
+ vec_push(call->params, exprs[1]);
+ out = (ast_expression*)ast_store_new(
+ parser_ctx(parser),
+ INSTR_STORE_F,
+ exprs[0],
+ (ast_expression*)call
+ );
+ }
+
break;
case opid2('|','|'):
}
}
}
- out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
+ out = fold_binary(ctx, generated_op, exprs[0], exprs[1]);
}
break;
}
if (!(out = fold_op(parser->fold, op, exprs))) {
- out = (ast_expression*)ast_binary_new(
+ out = fold_binary(
parser_ctx(parser),
VINSTR_CROSS,
exprs[0],
}
if (!(out = fold_op(parser->fold, op, exprs))) {
+ /* This whole block is NOT fold_binary safe */
ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
eq->refs = AST_REF_NONE;
return false;
}
if (!(out = fold_op(parser->fold, op, exprs)))
- out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
+ out = fold_binary(ctx, generated_op, exprs[0], exprs[1]);
break;
case opid2('!', '='):
if (exprs[0]->vtype != exprs[1]->vtype) {
return false;
}
if (!(out = fold_op(parser->fold, op, exprs)))
- out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
+ out = fold_binary(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
break;
case opid2('=', '='):
if (exprs[0]->vtype != exprs[1]->vtype) {
return false;
}
if (!(out = fold_op(parser->fold, op, exprs)))
- out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
+ out = fold_binary(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
break;
case opid1('='):
}
if (!out)
return false;
- out = (ast_expression*)ast_binary_new(ctx, subop,
- out,
- (ast_expression*)parser->fold->imm_float[1]);
+ out = fold_binary(ctx, subop,
+ out,
+ (ast_expression*)parser->fold->imm_float[1]);
break;
case opid2('+','='):
out = (ast_expression*)ast_binstore_new(ctx, assignop, INSTR_MUL_VF,
exprs[0], exprs[1]);
} else {
- out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F,
- (ast_expression*)parser->fold->imm_float[1],
- exprs[1]);
+ out = fold_binary(ctx, INSTR_DIV_F,
+ (ast_expression*)parser->fold->imm_float[1],
+ exprs[1]);
if (!out) {
compile_error(ctx, "internal error: failed to generate division");
return false;
else
assignop = type_store_instr[exprs[0]->vtype];
if (exprs[0]->vtype == TYPE_FLOAT)
- out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_BITAND, exprs[0], exprs[1]);
else
- out = (ast_expression*)ast_binary_new(ctx, VINSTR_BITAND_V, exprs[0], exprs[1]);
+ out = fold_binary(ctx, VINSTR_BITAND_V, exprs[0], exprs[1]);
if (!out)
return false;
(void)check_write_to(ctx, exprs[0]);
}
if (!(out = fold_op(parser->fold, op, exprs))) {
if (exprs[0]->vtype == TYPE_FLOAT) {
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
+ out = fold_binary(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
} else {
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, (ast_expression*)parser->fold->imm_vector[1], exprs[0]);
+ out = fold_binary(ctx, INSTR_SUB_V, (ast_expression*)parser->fold->imm_vector[1], exprs[0]);
}
}
break;
return NULL;
}
- typevar = parse_typename(parser, NULL, NULL);
+ typevar = parse_typename(parser, NULL, NULL, NULL);
if (!typevar) {
ast_unref(idx);
return NULL;
var = intrin_func(parser->intrin, parser_tokval(parser));
}
+ /*
+ * Try it again, intrin_func deals with the alias method as well
+ * the first one masks for __builtin though, we emit warning here.
+ */
+ if (!var) {
+ if ((var = intrin_func(parser->intrin, parser_tokval(parser)))) {
+ (void)!compile_warning(
+ parser_ctx(parser),
+ WARN_BUILTINS,
+ "using implicitly defined builtin `__builtin_%s' for `%s'",
+ parser_tokval(parser),
+ parser_tokval(parser)
+ );
+ }
+ }
+
+
if (!var) {
char *correct = NULL;
size_t i;
return false;
}
}
+ else if (!strcmp(parser_tokval(parser), "final")) {
+ flags |= AST_FLAG_FINAL_DECL;
+ if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+ parseerror(parser, "`final` 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;
had_var = true;
else if (!strcmp(parser_tokval(parser), "noref"))
had_noref = true;
+ else if (!strcmp(parser_tokval(parser), "final"))
+ flags |= AST_FLAG_FINAL_DECL;
else if (!had_const && !had_var && !had_noref && !had_attrib && !had_static && !flags) {
return false;
}
return parser_create_array_getter_impl(parser, array);
}
-static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
{
lex_ctx_t ctx;
/* parse variables until we hit a closing paren */
while (parser->tok != ')') {
+ bool is_varargs = false;
+
if (!first) {
/* there must be commas between them */
if (parser->tok != ',') {
}
first = false;
- if (parser->tok == TOKEN_DOTS) {
+ param = parse_typename(parser, NULL, NULL, &is_varargs);
+ if (!param && !is_varargs)
+ goto on_error;
+ if (is_varargs) {
/* '...' indicates a varargs function */
variadic = true;
- if (!parser_next(parser) || (parser->tok != ')' && parser->tok != TOKEN_IDENT)) {
+ if (parser->tok != ')' && parser->tok != TOKEN_IDENT) {
parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
goto on_error;
}
goto on_error;
}
}
- }
- else
- {
- /* for anything else just parse a typename */
- param = parse_typename(parser, NULL, NULL);
- if (!param)
- goto on_error;
+ } else {
vec_push(params, param);
if (param->expression.vtype >= TYPE_VARIANT) {
char tname[1024]; /* typename is reserved in C++ */
* void() foo(), bar
* then the type-information 'void()' can be stored in 'storebase'
*/
-static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef)
+static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef, bool *is_vararg)
{
ast_value *var, *tmp;
lex_ctx_t ctx;
bool wasarray = false;
size_t morefields = 0;
+ bool vararg = (parser->tok == TOKEN_DOTS);
+
ctx = parser_ctx(parser);
/* types may start with a dot */
morefields += 3;
else
break;
+ vararg = false;
if (!parser_next(parser)) {
parseerror(parser, "expected typename for field definition");
return NULL;
if (parser->tok == TOKEN_IDENT)
cached_typedef = parser_find_typedef(parser, parser_tokval(parser), 0);
if (!cached_typedef && parser->tok != TOKEN_TYPENAME) {
+ if (vararg && is_vararg) {
+ *is_vararg = true;
+ return NULL;
+ }
parseerror(parser, "expected typename");
return NULL;
}
ast_value *typevar, *oldtype;
ast_expression *old;
- typevar = parse_typename(parser, NULL, NULL);
+ typevar = parse_typename(parser, NULL, NULL, NULL);
if (!typevar)
return false;
parseerror(parser, "`static` qualifier is not supported in global scope");
/* get the first complete variable */
- var = parse_typename(parser, &basetype, cached_typedef);
+ var = parse_typename(parser, &basetype, cached_typedef, NULL);
if (!var) {
if (basetype)
ast_delete(basetype);
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;
}
*/
char *defname = NULL;
size_t prefix_len, ln;
+ size_t sn, sn_size;
ln = strlen(parser->function->name);
vec_append(defname, ln, parser->function->name);
/* now rename the global */
ln = strlen(var->name);
vec_append(defname, ln, var->name);
+ /* if a variable of that name already existed, add the
+ * counter value.
+ * The counter is incremented either way.
+ */
+ sn_size = vec_size(parser->function->static_names);
+ for (sn = 0; sn != sn_size; ++sn) {
+ if (strcmp(parser->function->static_names[sn], var->name) == 0)
+ break;
+ }
+ if (sn != sn_size) {
+ char *num = NULL;
+ int len = util_asprintf(&num, "#%u", parser->function->static_count);
+ vec_append(defname, len, num);
+ mem_d(num);
+ }
+ else
+ vec_push(parser->function->static_names, util_strdup(var->name));
+ parser->function->static_count++;
ast_value_set_name(var, defname);
/* push it to the to-be-generated globals */
if (!cexp)
break;
- if (!localblock) {
+ if (!localblock || is_static) {
cval = (ast_value*)cexp;
if (cval != parser->nil &&
(!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield))
)
{
- parseerror(parser, "cannot initialize a global constant variable with a non-constant expression");
+ parseerror(parser, "initializer is non constant");
}
else
{
- if (!OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
+ if (!is_static &&
+ !OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
qualifier != CV_VAR)
{
var->cvq = CV_CONST;