mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Fixes bug handling chained && ||. Work towards more macro functionality. Correctly emit stdcall. Corrected character parsing in escaped string entries.
This commit is contained in:
@@ -43,8 +43,6 @@ There are some small work being done on the parser here, but most of the structu
|
||||
- `$switch` and `$for` not handled.
|
||||
- Enums not correctly handled.
|
||||
- Type resolution not complete for all types.
|
||||
- `type` not handled.
|
||||
- Identifier analysis incomplete.
|
||||
- Macro call not handled completely.
|
||||
|
||||
#### What's missing overall
|
||||
|
||||
@@ -29,7 +29,7 @@ Things missing:
|
||||
- Strings
|
||||
- Array
|
||||
- Slice
|
||||
- Values: size, alignment, name, qualifiedName
|
||||
- Values: alignment, name, qualifiedName
|
||||
- Functions: offsetof
|
||||
- Distinct types
|
||||
- Simd types?
|
||||
@@ -46,7 +46,6 @@ Things missing:
|
||||
* Struct / union
|
||||
- Cast to union?
|
||||
- Structural typed anonymous structs and casts to them.
|
||||
- Auto deref on access.
|
||||
|
||||
* Expressions
|
||||
- Disallow x >= 0 and x < 0 on unsigned types unless in a macro.
|
||||
|
||||
@@ -8,6 +8,10 @@ module acorn::arr;
|
||||
* See Copyright Notice in avm.h
|
||||
*/
|
||||
|
||||
error SearchError
|
||||
{
|
||||
NOT_FOUND
|
||||
}
|
||||
/* Return a new Array, allocating len slots for Values. */
|
||||
func Value new(Value th, Value *dest, Value type, AuintIdx len)
|
||||
{
|
||||
|
||||
@@ -50,7 +50,7 @@ int findBlockVar(Value th, Value locvars, Value varnm)
|
||||
}
|
||||
|
||||
/* Look for local variable. Returns idx if found, -1 otherwise. */
|
||||
func int CompInfo.findLocalVar(CompInfo *comp, Value varnm)
|
||||
func int CompInfo.findLocalVar(CompInfo *comp, Value varnm) throws SearchError
|
||||
{
|
||||
assert(varnm.isSym());
|
||||
|
||||
@@ -68,7 +68,7 @@ func int CompInfo.findLocalVar(CompInfo *comp, Value varnm)
|
||||
}
|
||||
locvars = arrGet(th, locvars, 0); // link to prior local variables
|
||||
} while (locvars != aNull);
|
||||
return -1;
|
||||
throw SearchError.NOT_FOUND;
|
||||
}
|
||||
|
||||
/* Look for closure variable. Returns idx if found, -1 otherwise. */
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
module game_of_life;
|
||||
|
||||
extern func void printf(char *fmt, ...);
|
||||
extern func int atoi(char *val);
|
||||
|
||||
struct GameBoard
|
||||
{
|
||||
int h;
|
||||
@@ -8,19 +11,27 @@ struct GameBoard
|
||||
byte* temp;
|
||||
}
|
||||
|
||||
extern void *__stdoutp;
|
||||
extern func void fflush(void *std);
|
||||
extern func int rand();
|
||||
extern func void* malloc(usize size);
|
||||
extern func void usleep(int time);
|
||||
|
||||
func void GameBoard.show(GameBoard *board)
|
||||
{
|
||||
printf("\033[H");
|
||||
|
||||
printf("\e[H");
|
||||
byte* current = board.world;
|
||||
for (int y = 0; y < board.h; y++)
|
||||
{
|
||||
for (int x = 0; x < board.w; x++)
|
||||
{
|
||||
printf(*current ? "\033[07m \033[m" : " ");
|
||||
printf(*current ? "\e[07m \e[m" : " ");
|
||||
current++;
|
||||
}
|
||||
printf("\033[E");
|
||||
printf("\e[E");
|
||||
}
|
||||
stdout.fflush();
|
||||
fflush(__stdoutp);
|
||||
}
|
||||
|
||||
func void GameBoard.evolve(GameBoard *board)
|
||||
@@ -34,24 +45,26 @@ func void GameBoard.evolve(GameBoard *board)
|
||||
{
|
||||
for (int x1 = x - 1; x1 <= x + 1; x1++)
|
||||
{
|
||||
int actualX = (x1 + w) % w;
|
||||
int actualY = (y1 + h) % h;
|
||||
if (board.world[x + y * w]) n++;
|
||||
int actualX = (x1 + board.w) % board.w;
|
||||
int actualY = (y1 + board.h) % board.h;
|
||||
if (board.world[actualX + actualY * board.w]) n++;
|
||||
}
|
||||
}
|
||||
if (board.world(x + y * w)) n--;
|
||||
board.temp[x + y * w] = (n == 3 || (n == 2 && board.world(x + y * w)));
|
||||
if (board.world[x + y * board.w]) n--;
|
||||
board.temp[x + y * board.w] = cast(n == 3 || (n == 2 && board.world[x + y * board.w]), byte);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < w * h; i++)
|
||||
for (int i = 0; i < board.w * board.h; i++)
|
||||
{
|
||||
board.world[i] = board.temp[i];
|
||||
}
|
||||
}
|
||||
|
||||
int main(int c, string[] v)
|
||||
|
||||
func int main(int c, char** v)
|
||||
{
|
||||
int w = 0, h = 0;
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
if (c > 1) w = atoi(v[1]);
|
||||
if (c > 2) h = atoi(v[2]);
|
||||
if (w <= 0) w = 30;
|
||||
@@ -60,18 +73,18 @@ int main(int c, string[] v)
|
||||
GameBoard board;
|
||||
board.w = w;
|
||||
board.h = h;
|
||||
board.board = malloc(h * w);
|
||||
board.temp = malloc(h * w);
|
||||
board.world = malloc(cast(h * w, ulong));
|
||||
board.temp = malloc(cast(h * w, ulong));
|
||||
|
||||
for (int i = h * w - 1; i >= 0; i--)
|
||||
{
|
||||
board.world[i] = rand() < RAND_MAX / 10 ? 1 : 0;
|
||||
board.world[i] = rand() % 10 == 0 ? 1 : 0;
|
||||
}
|
||||
while (1)
|
||||
for (int j = 0; j < 1000; j++)
|
||||
{
|
||||
|
||||
board.show();
|
||||
board.evolve();
|
||||
usleep(200000);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -882,6 +882,7 @@ func void testErrors()
|
||||
printf("Throws: %d\n", throwsDone.times);
|
||||
printf("End of errors\n");
|
||||
}
|
||||
|
||||
macro void arrayCallMacro($x)
|
||||
{
|
||||
printf("Array call: %d, %d\n", $x[0], $x[1]);
|
||||
@@ -1089,8 +1090,155 @@ func void testTypeValues()
|
||||
printf("Testunion.a sizeof: %d = 2\n", TestUnionSize.a.sizeof);
|
||||
}
|
||||
|
||||
func int testTypeofHelp()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
func void testTypeof()
|
||||
{
|
||||
typeid y = typeof(1 + testTypeofHelp());
|
||||
}
|
||||
|
||||
macro void printMessage($x)
|
||||
{
|
||||
for (int i = 0; i < $x; i++)
|
||||
{
|
||||
printf("print message! %d\n", i);
|
||||
}
|
||||
$x = $x + 1;
|
||||
}
|
||||
|
||||
func void testMacros()
|
||||
{
|
||||
int i = 3;
|
||||
@printMessage(i);
|
||||
printf("i: %d\n", i);
|
||||
}
|
||||
|
||||
const char[] LUT_ENC = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/',
|
||||
};
|
||||
|
||||
const byte ERR = 0xFF;
|
||||
|
||||
const byte[256] LUT_DEC = {
|
||||
// '+', ',', '-', '.', '/', '0', '1', '2'
|
||||
62, ERR, ERR, ERR, 63, 52, 53, 54,
|
||||
// '3', '4', '5', '6', '7', '8', '9', ':'
|
||||
55, 56, 57, 58, 59, 60, 61, ERR,
|
||||
// ';', '<', '=', '>', '?', '@', 'A', 'B'
|
||||
ERR, ERR, ERR, ERR, ERR, ERR, 0, 1,
|
||||
// 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'
|
||||
2, 3, 4, 5, 6, 7, 8, 9,
|
||||
// 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R'
|
||||
10, 11, 12, 13, 14, 15, 16, 17,
|
||||
// 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
|
||||
18, 19, 20, 21, 22, 23, 24, 25,
|
||||
// '[', '\', ']', '^', '_', '`', 'a', 'b'
|
||||
ERR, ERR, ERR, ERR, ERR, ERR, 26, 27,
|
||||
// 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'
|
||||
28, 29, 30, 31, 32, 33, 34, 35,
|
||||
// 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r'
|
||||
36, 37, 38, 39, 40, 41, 42, 43,
|
||||
// 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
|
||||
44, 45, 46, 47, 48, 49, 50, 51,
|
||||
};
|
||||
|
||||
|
||||
const char PAD = '=';
|
||||
const char FIRST = '+';
|
||||
const char LAST = 'z';
|
||||
|
||||
public error Base64Error
|
||||
{
|
||||
INVALID_CHARACTER
|
||||
}
|
||||
|
||||
public func void encode(byte[] in, string *out)
|
||||
{
|
||||
int j = 0;
|
||||
|
||||
for (int i = 0; i < in.len; i++);
|
||||
{
|
||||
switch (i % 3)
|
||||
{
|
||||
case 0:
|
||||
out[j++] = LUT_ENC[(in[i] >> 2) & 0x3F];
|
||||
case 1:
|
||||
out[j++] = LUT_ENC[(in[i-1] & 0x3) << 4 + ((in[i] >> 4) & 0xF)];
|
||||
case 2:
|
||||
out[j++] = LUT_ENC[(in[i-1] & 0xF) << 2 + ((in[i] >> 6) & 0x3)];
|
||||
out[j++] = LUT_ENC[in[i] & 0x3F];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// move back
|
||||
int last = in.len - 1;
|
||||
|
||||
// check the last and add padding
|
||||
switch (last % 3)
|
||||
{
|
||||
case 0:
|
||||
out[j++] = LUT_ENC[(in[last] & 0x3) << 4];
|
||||
out[j++] = PAD;
|
||||
out[j++] = PAD;
|
||||
case 1:
|
||||
out[j++] = LUT_ENC[(in[last] & 0xF) << 2];
|
||||
out[j++] = PAD;
|
||||
}
|
||||
}
|
||||
|
||||
public func int decode(char[] in, byte[] out) throws Base64Error
|
||||
{
|
||||
int j = 0;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
char value = in[i];
|
||||
|
||||
if (value == PAD) return j;
|
||||
|
||||
if (value < FIRST || in[i] > LAST) throw INVALID_CHARACTER;
|
||||
byte c = LUT_DEC[in[i] - FIRST);
|
||||
if (c == ERR) throw INVALID_CHARACTER;
|
||||
|
||||
switch (i % 4)
|
||||
{
|
||||
case 0:
|
||||
out[j] = c << 2;
|
||||
case 1:
|
||||
out[j++] += (c >> 4) & 0x3;
|
||||
// if not last char with padding
|
||||
if (i < (len - 3) || in[len - 2] != PAD)
|
||||
{
|
||||
out[j] = (c & 0xF) << 4;
|
||||
}
|
||||
case 2:
|
||||
out[j++] += (c >> 2) & 0xF;
|
||||
if (i < (len -2) || in[len -1] != PAD)
|
||||
{
|
||||
out[j] = (c & 0x3) << 6;
|
||||
}
|
||||
case 3:
|
||||
out[j++] += c;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
func int main(int x)
|
||||
{
|
||||
testMacros();
|
||||
testTypeof();
|
||||
testSimple();
|
||||
testErrorBug();
|
||||
testErrors();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module bar;
|
||||
|
||||
import baz::foo;
|
||||
import baz.foo;
|
||||
import testbaz;
|
||||
|
||||
public func void test()
|
||||
|
||||
@@ -492,9 +492,21 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent)
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_IDENTIFIER:
|
||||
DUMPF("(ident %s", expr->identifier_expr.identifier);
|
||||
if (expr->identifier_expr.is_macro)
|
||||
{
|
||||
DUMPF("(ident @%s", expr->identifier_expr.identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
DUMPF("(ident %s", expr->identifier_expr.identifier);
|
||||
}
|
||||
DUMPEXPC(expr);
|
||||
DUMPEND();
|
||||
case EXPR_MACRO_BLOCK:
|
||||
DUMP("(macro_block");
|
||||
DUMPASTS(expr->macro_block.stmts);
|
||||
DUMPDECLS(expr->macro_block.params);
|
||||
DUMPEND();
|
||||
case EXPR_EXPR_BLOCK:
|
||||
if (!expr->expr_block.stmts)
|
||||
{
|
||||
@@ -655,10 +667,6 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent)
|
||||
DUMPEXPR(expr->expr_scope.expr);
|
||||
// TODO defers.
|
||||
DUMPEND();
|
||||
case EXPR_MACRO_EXPR:
|
||||
DUMP("(macro-expr");
|
||||
DUMPEXPR(expr->macro_expr);
|
||||
DUMPEND();
|
||||
case EXPR_RANGE:
|
||||
DUMP("(range");
|
||||
DUMPEXPR(expr->range_expr.left);
|
||||
|
||||
@@ -257,6 +257,7 @@ typedef struct _VarDecl
|
||||
{
|
||||
unsigned id : 16;
|
||||
VarDeclKind kind : 3;
|
||||
bool constant : 1;
|
||||
TypeInfo *type_info;
|
||||
Expr *init_expr;
|
||||
void *backend_debug_ref;
|
||||
@@ -569,6 +570,7 @@ typedef struct
|
||||
Path *path;
|
||||
const char *identifier;
|
||||
bool is_ref;
|
||||
bool is_macro;
|
||||
Decl *decl;
|
||||
} ExprIdentifier;
|
||||
|
||||
@@ -595,6 +597,13 @@ typedef struct
|
||||
Ast **stmts;
|
||||
} ExprFuncBlock;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Ast **stmts;
|
||||
Expr **args;
|
||||
Decl **params;
|
||||
} ExprMacroBlock;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Expr *left;
|
||||
@@ -643,7 +652,6 @@ struct _Expr
|
||||
ExprStructValue struct_value_expr;
|
||||
ExprTypeAccess type_access;
|
||||
ExprTry try_expr;
|
||||
Expr* macro_expr;
|
||||
ExprBinary binary_expr;
|
||||
ExprTernary ternary_expr;
|
||||
ExprUnary unary_expr;
|
||||
@@ -658,6 +666,7 @@ struct _Expr
|
||||
Expr** expression_list;
|
||||
ExprScope expr_scope;
|
||||
ExprFuncBlock expr_block;
|
||||
ExprMacroBlock macro_block;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -987,9 +996,9 @@ typedef struct _Context
|
||||
{
|
||||
bool in_macro_call : 1;
|
||||
bool in_macro : 1;
|
||||
Decl **macro_locals_start;
|
||||
int macro_counter;
|
||||
int macro_nesting;
|
||||
Expr *first_macro_call;
|
||||
};
|
||||
Decl *locals[MAX_LOCALS];
|
||||
DynamicScope scopes[MAX_SCOPE_DEPTH];
|
||||
|
||||
@@ -232,8 +232,8 @@ typedef enum
|
||||
EXPR_CAST,
|
||||
EXPR_TYPEOF,
|
||||
EXPR_SCOPED_EXPR,
|
||||
EXPR_MACRO_EXPR,
|
||||
EXPR_EXPR_BLOCK,
|
||||
EXPR_MACRO_BLOCK,
|
||||
EXPR_RANGE,
|
||||
EXPR_DESIGNATED_INITIALIZER,
|
||||
EXPR_COMPOUND_LITERAL,
|
||||
@@ -267,6 +267,7 @@ typedef enum
|
||||
SCOPE_NEXT = 1 << 2,
|
||||
SCOPE_DEFER = 1 << 3,
|
||||
SCOPE_EXPR_BLOCK = 1 << 4,
|
||||
SCOPE_MACRO = 1 << 5
|
||||
} ScopeFlags;
|
||||
|
||||
typedef enum
|
||||
|
||||
@@ -173,7 +173,8 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
|
||||
case EXPR_DESIGNATED_INITIALIZER:
|
||||
// Should only appear when generating designated initializers.
|
||||
UNREACHABLE
|
||||
|
||||
case EXPR_MACRO_BLOCK:
|
||||
TODO
|
||||
case EXPR_IDENTIFIER:
|
||||
return expr->identifier_expr.decl->ref;
|
||||
case EXPR_UNARY:
|
||||
@@ -201,7 +202,6 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
case EXPR_EXPRESSION_LIST:
|
||||
case EXPR_CAST:
|
||||
case EXPR_MACRO_EXPR:
|
||||
case EXPR_TYPEOF:
|
||||
UNREACHABLE
|
||||
}
|
||||
@@ -522,16 +522,23 @@ static LLVMValueRef gencontext_emit_logical_and_or(GenContext *context, Expr *ex
|
||||
|
||||
gencontext_emit_block(context, rhs_block);
|
||||
LLVMValueRef rhs = gencontext_emit_expr(context, expr->binary_expr.right);
|
||||
LLVMBasicBlockRef end_block = context->current_block;
|
||||
gencontext_emit_br(context, phi_block);
|
||||
|
||||
// Generate phi
|
||||
// Generate phi
|
||||
gencontext_emit_block(context, phi_block);
|
||||
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(type_bool), "val");
|
||||
|
||||
// Simplify for LLVM by entering the constants we already know of.
|
||||
LLVMValueRef result_on_skip = llvm_int(type_bool, op == BINARYOP_AND ? 0 : 1);
|
||||
|
||||
// One possibility here is that a return happens inside of the expression.
|
||||
if (!end_block)
|
||||
{
|
||||
return result_on_skip;
|
||||
}
|
||||
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(type_bool), "val");
|
||||
LLVMValueRef logic_values[2] = { result_on_skip, rhs };
|
||||
LLVMBasicBlockRef blocks[2] = { start_block, rhs_block };
|
||||
LLVMBasicBlockRef blocks[2] = { start_block, end_block };
|
||||
LLVMAddIncoming(phi, logic_values, blocks, 2);
|
||||
|
||||
return phi;
|
||||
@@ -823,6 +830,10 @@ LLVMValueRef gencontext_emit_post_unary_expr(GenContext *context, Expr *expr)
|
||||
|
||||
LLVMValueRef gencontext_emit_typeid(GenContext *context, Expr *expr)
|
||||
{
|
||||
if (type_is_builtin(expr->typeid_expr->type->type_kind))
|
||||
{
|
||||
return gencontext_emit_const_int(context, type_usize, expr->typeid_expr->type->type_kind);
|
||||
}
|
||||
assert(expr->typeid_expr->type->backend_typeid);
|
||||
return expr->typeid_expr->type->backend_typeid;
|
||||
}
|
||||
@@ -1356,6 +1367,47 @@ static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr
|
||||
return return_out ? gencontext_emit_load(context, expr->type, return_out) : NULL;
|
||||
}
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_macro_block(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMValueRef old_ret_out = context->return_out;
|
||||
LLVMBasicBlockRef saved_expr_block = context->expr_block_exit;
|
||||
|
||||
LLVMBasicBlockRef expr_block = gencontext_create_free_block(context, "expr_block.exit");
|
||||
context->expr_block_exit = expr_block;
|
||||
|
||||
LLVMValueRef return_out = NULL;
|
||||
if (expr->type != type_void)
|
||||
{
|
||||
return_out = gencontext_emit_alloca(context, llvm_type(expr->type), "blockret");
|
||||
}
|
||||
context->return_out = return_out;
|
||||
|
||||
Ast **stmts = expr->macro_block.stmts;
|
||||
VECEACH(expr->macro_block.params, i)
|
||||
{
|
||||
// In case we have a constant, we never do an emit. The value is already folded.
|
||||
if (!expr->macro_block.args[i]) continue;
|
||||
Decl *decl = expr->macro_block.params[i];
|
||||
decl->ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name);
|
||||
LLVMValueRef value = gencontext_emit_expr(context, expr->macro_block.args[i]);
|
||||
LLVMBuildStore(context->builder, value, decl->ref);
|
||||
}
|
||||
|
||||
VECEACH(stmts, i)
|
||||
{
|
||||
gencontext_emit_stmt(context, stmts[i]);
|
||||
}
|
||||
gencontext_emit_br(context, expr_block);
|
||||
|
||||
// Emit the exit block.
|
||||
gencontext_emit_block(context, expr_block);
|
||||
|
||||
context->return_out = old_ret_out;
|
||||
context->expr_block_exit = saved_expr_block;
|
||||
|
||||
return return_out ? gencontext_emit_load(context, expr->type, return_out) : NULL;
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id, LLVMTypeRef *types,
|
||||
LLVMValueRef *values, unsigned arg_count)
|
||||
{
|
||||
@@ -1392,6 +1444,8 @@ NESTED_RETRY:
|
||||
case EXPR_DESIGNATED_INITIALIZER:
|
||||
// Should only appear when generating designated initializers.
|
||||
UNREACHABLE
|
||||
case EXPR_MACRO_BLOCK:
|
||||
return gencontext_emit_macro_block(context, expr);
|
||||
case EXPR_COMPOUND_LITERAL:
|
||||
expr = expr->expr_compound_literal.initializer;
|
||||
goto NESTED_RETRY;
|
||||
@@ -1416,7 +1470,6 @@ NESTED_RETRY:
|
||||
case EXPR_TYPEID:
|
||||
return gencontext_emit_typeid(context, expr);
|
||||
case EXPR_TYPE_ACCESS:
|
||||
case EXPR_MACRO_EXPR:
|
||||
case EXPR_TYPEOF:
|
||||
// These are folded in the semantic analysis step.
|
||||
UNREACHABLE
|
||||
|
||||
@@ -211,8 +211,7 @@ void gencontext_emit_function_decl(GenContext *context, Decl *decl)
|
||||
}
|
||||
gencontext_add_attribute(context, function, nounwind_attribute, -1);
|
||||
|
||||
// TODO only for windows.
|
||||
if (decl->func.attr_stdcall)
|
||||
if (decl->func.attr_stdcall && (build_target.os == OS_TYPE_WIN32))
|
||||
{
|
||||
LLVMSetFunctionCallConv(function, LLVMX86StdcallCallConv);
|
||||
LLVMSetDLLStorageClass(function, LLVMDLLImportStorageClass);
|
||||
|
||||
@@ -97,13 +97,19 @@ bool parse_param_list(Context *context, Expr ***result, bool allow_type)
|
||||
}
|
||||
}
|
||||
|
||||
static Expr *parse_macro_expr(Context *context, Expr *left)
|
||||
static Expr *parse_macro_ident(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Expr *macro_expr = EXPR_NEW_TOKEN(EXPR_MACRO_EXPR, context->tok);
|
||||
Expr *macro_ident = EXPR_NEW_TOKEN(EXPR_IDENTIFIER, context->tok);
|
||||
macro_ident->identifier_expr.is_macro = true;
|
||||
advance_and_verify(context, TOKEN_AT);
|
||||
macro_expr->macro_expr = TRY_EXPR_OR(parse_precedence(context, PREC_UNARY + 1), poisoned_expr);
|
||||
return macro_expr;
|
||||
bool had_error = false;
|
||||
macro_ident->identifier_expr.path = parse_path_prefix(context, &had_error);
|
||||
if (had_error) return poisoned_expr;
|
||||
macro_ident->identifier_expr.identifier = context->tok.string;
|
||||
CONSUME_OR(TOKEN_IDENT, poisoned_expr);
|
||||
RANGE_EXTEND_PREV(macro_ident);
|
||||
return macro_ident;
|
||||
}
|
||||
|
||||
|
||||
@@ -381,7 +387,9 @@ static Expr *parse_identifier(Context *context, Expr *left)
|
||||
static Expr *parse_maybe_scope(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Path *path = parse_path_prefix(context);
|
||||
bool had_error;
|
||||
Path *path = parse_path_prefix(context, &had_error);
|
||||
if (had_error) return poisoned_expr;
|
||||
switch (context->tok.type)
|
||||
{
|
||||
case TOKEN_IDENT:
|
||||
@@ -707,7 +715,13 @@ static Expr *parse_string_literal(Context *context, Expr *left)
|
||||
if (context->tok.string[i] == '\\')
|
||||
{
|
||||
i++;
|
||||
i += append_esc_string_token(str, context->tok.string + i, &len) - 1;
|
||||
int scanned = append_esc_string_token(str, context->tok.string + i, &len) - 1;
|
||||
if (scanned < -1)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Invalid escape in string.");
|
||||
return poisoned_expr;
|
||||
}
|
||||
i += scanned;
|
||||
continue;
|
||||
}
|
||||
str[len++] = context->tok.string[i];
|
||||
@@ -861,7 +875,7 @@ ParseRule rules[TOKEN_EOF + 1] = {
|
||||
[TOKEN_IDENT] = { parse_maybe_scope, NULL, PREC_NONE },
|
||||
[TOKEN_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE },
|
||||
[TOKEN_CT_IDENT] = { parse_identifier, NULL, PREC_NONE },
|
||||
[TOKEN_AT] = { parse_macro_expr, NULL, PREC_UNARY },
|
||||
[TOKEN_AT] = { parse_macro_ident, NULL, PREC_NONE },
|
||||
[TOKEN_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE },
|
||||
[TOKEN_STRING] = { parse_string_literal, NULL, PREC_NONE },
|
||||
[TOKEN_REAL] = { parse_double, NULL, PREC_NONE },
|
||||
|
||||
@@ -632,8 +632,6 @@ Ast *parse_stmt(Context *context)
|
||||
{
|
||||
return parse_expr_stmt(context);
|
||||
}
|
||||
case TOKEN_TYPEOF:
|
||||
TODO
|
||||
case TOKEN_LOCAL: // Local means declaration!
|
||||
case TOKEN_CONST: // Const means declaration!
|
||||
return parse_declaration_stmt(context);
|
||||
@@ -743,6 +741,7 @@ Ast *parse_stmt(Context *context)
|
||||
case TOKEN_NIL:
|
||||
case TOKEN_TRUE:
|
||||
case TOKEN_LPARBRA:
|
||||
case TOKEN_TYPEOF:
|
||||
return parse_expr_stmt(context);
|
||||
case TOKEN_INVALID_TOKEN:
|
||||
advance(context);
|
||||
|
||||
@@ -245,8 +245,9 @@ static inline Path *parse_module_path(Context *context)
|
||||
return path_create_from_string(scratch_ptr, offset, span);
|
||||
}
|
||||
|
||||
Path *parse_path_prefix(Context *context)
|
||||
Path *parse_path_prefix(Context *context, bool *had_error)
|
||||
{
|
||||
*had_error = false;
|
||||
if (context->tok.type != TOKEN_IDENT || context->next_tok.type != TOKEN_SCOPE) return NULL;
|
||||
|
||||
char *scratch_ptr = context->path_scratch;
|
||||
@@ -277,6 +278,7 @@ Path *parse_path_prefix(Context *context)
|
||||
if (type != TOKEN_IDENT)
|
||||
{
|
||||
sema_error_range(path->span, "A module name was expected here.");
|
||||
*had_error = true;
|
||||
return NULL;
|
||||
|
||||
}
|
||||
@@ -312,7 +314,9 @@ Path *parse_path_prefix(Context *context)
|
||||
static inline TypeInfo *parse_base_type(Context *context)
|
||||
{
|
||||
SourceRange range = context->tok.span;
|
||||
Path *path = parse_path_prefix(context);
|
||||
bool had_error;
|
||||
Path *path = parse_path_prefix(context, &had_error);
|
||||
if (had_error) return poisoned_type_info;
|
||||
if (path)
|
||||
{
|
||||
TypeInfo *type_info = type_info_new(TYPE_INFO_IDENTIFIER, range);
|
||||
@@ -587,8 +591,11 @@ static inline Decl *parse_const_declaration(Context *context, Visibility visibil
|
||||
}
|
||||
else
|
||||
{
|
||||
if (token_is_type(context->tok.type) || context->tok.type == TOKEN_CT_TYPE_IDENT || context->tok.type == TOKEN_TYPE_IDENT)
|
||||
{
|
||||
decl->var.type_info = TRY_TYPE_OR(parse_type(context), poisoned_decl);
|
||||
}
|
||||
if (!consume_const_name(context, "constant")) return poisoned_decl;
|
||||
decl->var.type_info = TRY_TYPE_OR(parse_type(context), poisoned_decl);
|
||||
}
|
||||
|
||||
CONSUME_OR(TOKEN_EQ, poisoned_decl);
|
||||
@@ -755,7 +762,9 @@ static inline bool parse_attributes(Context *context, Decl *parent_decl)
|
||||
|
||||
while (try_consume(context, TOKEN_AT))
|
||||
{
|
||||
Path *path = parse_path_prefix(context);
|
||||
bool had_error;
|
||||
Path *path = parse_path_prefix(context, &had_error);
|
||||
if (had_error) return false;
|
||||
|
||||
Attr *attr = malloc_arena(sizeof(Attr));
|
||||
|
||||
@@ -859,6 +868,11 @@ static inline bool parse_param_decl(Context *context, Visibility parent_visibili
|
||||
{
|
||||
if (context->tok.type != TOKEN_COMMA && context->tok.type != TOKEN_RPAREN)
|
||||
{
|
||||
if (context->tok.type == TOKEN_CT_IDENT)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Compile time identifiers are only allowed as macro parameters.");
|
||||
return false;
|
||||
}
|
||||
sema_error_at(context->prev_tok_end, "Unexpected end of the parameter list, did you forget an ')'?");
|
||||
return false;
|
||||
}
|
||||
@@ -1098,7 +1112,9 @@ static inline Decl *parse_generics_declaration(Context *context, Visibility visi
|
||||
{
|
||||
rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl);
|
||||
}
|
||||
Path *path = parse_path_prefix(context);
|
||||
bool had_error;
|
||||
Path *path = parse_path_prefix(context, &had_error);
|
||||
if (had_error) return poisoned_decl;
|
||||
Decl *decl = decl_new(DECL_GENERIC, context->tok, visibility);
|
||||
decl->generic_decl.path = path;
|
||||
if (!consume_ident(context, "generic function name")) return poisoned_decl;
|
||||
@@ -1495,7 +1511,9 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit
|
||||
func->func.function_signature.rtype = return_type;
|
||||
|
||||
SourceRange start = context->tok.span;
|
||||
Path *path = parse_path_prefix(context);
|
||||
bool had_error;
|
||||
Path *path = parse_path_prefix(context, &had_error);
|
||||
if (had_error) return poisoned_decl;
|
||||
if (path || context->tok.type == TOKEN_TYPE_IDENT)
|
||||
{
|
||||
// Special case, actually an extension
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#define TRY_EXPECT_OR(_tok, _message, _type) do { if (context->tok.type != _tok) { SEMA_TOKEN_ERROR(context->tok, _message); return _type; } } while(0)
|
||||
#define TRY_CONSUME_OR(_tok, _message, _type) do { if (!consume(context, _tok, _message)) return _type; } while(0)
|
||||
#define TRY_CONSUME(_tok, _message) TRY_CONSUME_OR(_tok, _message, poisoned_ast)
|
||||
#define TRY_CONSUME_EOS_OR(_res) TRY_CONSUME_OR(TOKEN_EOS, "Expected ';'", _res)
|
||||
#define TRY_CONSUME_EOS_OR(_res) do { if (context->tok.type != TOKEN_EOS) { sema_error_at(context->prev_tok_end, "Expected ';'"); return _res; } advance(context); } while(0)
|
||||
#define TRY_CONSUME_EOS() TRY_CONSUME_EOS_OR(poisoned_ast)
|
||||
#define RETURN_AFTER_EOS(_ast) extend_ast_with_prev_token(context, ast); TRY_CONSUME_EOS_OR(poisoned_ast); return _ast
|
||||
#define TRY_CONSUME_LBRACE() TRY_CONSUME(TOKEN_LBRACE, "Expected '{'")
|
||||
@@ -30,7 +30,7 @@ SEMA_TOKEN_ERROR(context->tok, "Expected ',' or ')'"); return _res; } } while(0)
|
||||
|
||||
|
||||
Ast *parse_stmt(Context *context);
|
||||
Path *parse_path_prefix(Context *context);
|
||||
Path *parse_path_prefix(Context *context, bool *had_error);
|
||||
Expr *parse_type_expression_with_path(Context *context, Path *path);
|
||||
Expr *parse_expr(Context *context);
|
||||
TypeInfo *parse_type(Context *context);
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#define EXIT_T_MISMATCH() return sema_type_mismatch(left, canonical, cast_type)
|
||||
#define IS_EXPLICT()
|
||||
#define RETURN_NON_CONST_CAST(kind) do { if (left->expr_kind != EXPR_CONST) { insert_cast(left, kind, canonical); return true; } } while (0)
|
||||
#define REQUIRE_EXPLICIT_CAST(_cast_type)\
|
||||
do { if (_cast_type == CAST_TYPE_EXPLICIT) break;\
|
||||
if (_cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;\
|
||||
EXIT_T_MISMATCH(); } while (0)
|
||||
|
||||
static inline void insert_cast(Expr *expr, CastKind kind, Type *canonical)
|
||||
{
|
||||
@@ -71,13 +75,9 @@ bool erro(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
|
||||
bool ptxi(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type != CAST_TYPE_EXPLICIT)
|
||||
{
|
||||
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
|
||||
EXIT_T_MISMATCH();
|
||||
}
|
||||
RETURN_NON_CONST_CAST(CAST_PTRXI);
|
||||
REQUIRE_EXPLICIT_CAST(cast_type);
|
||||
|
||||
RETURN_NON_CONST_CAST(CAST_PTRXI);
|
||||
assert(left->const_expr.kind == TYPE_POINTER);
|
||||
expr_const_set_int(&left->const_expr, 0, type->type_kind);
|
||||
left->type = type;
|
||||
@@ -110,16 +110,6 @@ static inline bool may_implicitly_cast_ptr_to_ptr(Type *current_type, Type *targ
|
||||
}
|
||||
|
||||
|
||||
bool ptfu(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
bool fupt(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
bool stst(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
TODO
|
||||
@@ -206,6 +196,7 @@ void const_int_to_fp_cast(Expr *left, Type *canonical, Type *type)
|
||||
*/
|
||||
bool boxi(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
|
||||
if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH();
|
||||
RETURN_NON_CONST_CAST(CAST_BOOLINT);
|
||||
assert(left->const_expr.kind == TYPE_BOOL);
|
||||
@@ -281,8 +272,8 @@ bool fpfp(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
*/
|
||||
bool fpxi(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
|
||||
if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH();
|
||||
REQUIRE_EXPLICIT_CAST(cast_type);
|
||||
|
||||
RETURN_NON_CONST_CAST(CAST_FPUI);
|
||||
|
||||
assert(canonical->type_kind >= TYPE_I8 && canonical->type_kind <= TYPE_U64);
|
||||
@@ -343,8 +334,7 @@ bool ixxen(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
bool ixxer(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
// Assigning zero = no value is always ok.
|
||||
if (cast_type == CAST_TYPE_IMPLICIT) EXIT_T_MISMATCH();
|
||||
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
|
||||
REQUIRE_EXPLICIT_CAST(cast_type);
|
||||
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
@@ -459,7 +449,7 @@ bool uisi(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastTyp
|
||||
*/
|
||||
bool siui(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH();
|
||||
REQUIRE_EXPLICIT_CAST(cast_type);
|
||||
|
||||
RETURN_NON_CONST_CAST(CAST_SIUI);
|
||||
|
||||
@@ -891,7 +881,6 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type)
|
||||
if (type_is_integer(canonical)) return ptxi(expr, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_BOOL) return ptbo(expr, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_POINTER) return ptpt(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_FUNC) return ptfu(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_VARARRAY) return ptva(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_SUBARRAY) return ptsa(expr, from_type, canonical, to_type, cast_type);
|
||||
break;
|
||||
|
||||
@@ -447,7 +447,8 @@ static AttributeType sema_analyse_attribute(Context *context, Attr *attr, Attrib
|
||||
[ATTRIBUTE_NORETURN] = ATTR_FUNC,
|
||||
[ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_VAR | ATTR_STRUCT | ATTR_UNION,
|
||||
[ATTRIBUTE_INLINE] = ATTR_FUNC,
|
||||
[ATTRIBUTE_OPAQUE] = ATTR_STRUCT | ATTR_UNION
|
||||
[ATTRIBUTE_OPAQUE] = ATTR_STRUCT | ATTR_UNION,
|
||||
[ATTRIBUTE_STDCALL] = ATTR_FUNC
|
||||
};
|
||||
|
||||
if ((attribute_domain[type] & domain) != domain)
|
||||
@@ -457,6 +458,8 @@ static AttributeType sema_analyse_attribute(Context *context, Attr *attr, Attrib
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case ATTRIBUTE_STDCALL:
|
||||
return type;
|
||||
case ATTRIBUTE_ALIGN:
|
||||
if (!attr->expr)
|
||||
{
|
||||
|
||||
@@ -11,18 +11,20 @@
|
||||
* - Disallow jumping in and out of an expression block.
|
||||
*/
|
||||
|
||||
static Expr **expr_copy_expr_list_from_macro(Context *context, Expr *macro, Expr **expr_list);
|
||||
static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_expr);
|
||||
static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source);
|
||||
static Ast **ast_copy_list_from_macro(Context *context, Expr *macro, Ast **to_copy);
|
||||
static Expr **expr_copy_expr_list_from_macro(Context *context, Expr **expr_list);
|
||||
static Expr *expr_copy_from_macro(Context *context, Expr *source_expr);
|
||||
static Ast *ast_copy_from_macro(Context *context, Ast *source);
|
||||
static Ast **ast_copy_list_from_macro(Context *context, Ast **to_copy);
|
||||
static bool sema_expr_analyse_type_access(Context *context, Type *to, Expr *expr);
|
||||
static Decl *decl_copy_local_from_macro(Context *context, Decl *to_copy);
|
||||
static TypeInfo *type_info_copy_from_macro(Context *context, TypeInfo *source);
|
||||
|
||||
#define MACRO_COPY_EXPR(x) x = expr_copy_from_macro(context, macro, x)
|
||||
#define MACRO_COPY_TYPE(x) x = type_info_copy_from_macro(context, macro, x)
|
||||
#define MACRO_COPY_TYPE_LIST(x) x = type_info_copy_list_from_macro(context, macro, x)
|
||||
#define MACRO_COPY_EXPR_LIST(x) x = expr_copy_expr_list_from_macro(context, macro, x)
|
||||
#define MACRO_COPY_AST_LIST(x) x = ast_copy_list_from_macro(context, macro, x)
|
||||
#define MACRO_COPY_AST(x) x = ast_copy_from_macro(context, macro, x)
|
||||
#define MACRO_COPY_EXPR(x) x = expr_copy_from_macro(context, x)
|
||||
#define MACRO_COPY_TYPE(x) x = type_info_copy_from_macro(context, x)
|
||||
#define MACRO_COPY_TYPE_LIST(x) x = type_info_copy_list_from_macro(context, x)
|
||||
#define MACRO_COPY_EXPR_LIST(x) x = expr_copy_expr_list_from_macro(context, x)
|
||||
#define MACRO_COPY_AST_LIST(x) x = ast_copy_list_from_macro(context, x)
|
||||
#define MACRO_COPY_AST(x) x = ast_copy_from_macro(context, x)
|
||||
|
||||
bool sema_analyse_expr_may_be_function(Context *context, Expr *expr);
|
||||
|
||||
@@ -165,6 +167,15 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline Decl *decl_copy_local_from_macro(Context *context, Decl *to_copy)
|
||||
{
|
||||
assert(to_copy->decl_kind == DECL_VAR);
|
||||
Decl *copy = malloc_arena(sizeof(Decl));
|
||||
memcpy(copy, to_copy, sizeof(Decl));
|
||||
MACRO_COPY_TYPE(copy->var.type_info);
|
||||
MACRO_COPY_EXPR(copy->var.init_expr);
|
||||
return copy;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_enum_constant(Expr *expr, const char *name, Decl *decl)
|
||||
{
|
||||
@@ -255,7 +266,7 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr
|
||||
}
|
||||
if (decl->decl_kind == DECL_MACRO)
|
||||
{
|
||||
if (!context->in_macro_call)
|
||||
if (!expr->identifier_expr.is_macro)
|
||||
{
|
||||
SEMA_ERROR(expr, "Macro expansions must be prefixed with '@', try using '@%s(...)' instead.", decl->name);
|
||||
return false;
|
||||
@@ -264,6 +275,17 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr
|
||||
expr->type = type_void;
|
||||
return true;
|
||||
}
|
||||
if (expr->identifier_expr.is_macro)
|
||||
{
|
||||
SEMA_ERROR(expr, "Only macro expansions can be prefixed with '@', please try to remove it.", decl->name);
|
||||
}
|
||||
if (decl->decl_kind == DECL_VAR && decl->var.constant)
|
||||
{
|
||||
assert(decl->var.init_expr && decl->var.init_expr->resolve_status == RESOLVE_DONE);
|
||||
// Todo, maybe make a copy?
|
||||
expr_replace(expr, decl->var.init_expr);
|
||||
return true;
|
||||
}
|
||||
assert(decl->type);
|
||||
expr->identifier_expr.decl = decl;
|
||||
expr->type = decl->type;
|
||||
@@ -472,6 +494,109 @@ static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr
|
||||
return sema_expr_analyse_func_invocation(context, &decl->func.function_signature, expr, decl, to, struct_var);
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_expr, Decl *decl)
|
||||
{
|
||||
if (context->macro_nesting >= MAX_MACRO_NESTING)
|
||||
{
|
||||
SEMA_ERROR(call_expr, "Too deep nesting (more than %d levels) when evaluating this macro.", MAX_MACRO_NESTING);
|
||||
return false;
|
||||
}
|
||||
|
||||
Expr **args = call_expr->call_expr.arguments;
|
||||
Decl **func_params = decl->macro_decl.parameters;
|
||||
|
||||
unsigned num_args = vec_size(args);
|
||||
for (unsigned i = 0; i < num_args; i++)
|
||||
{
|
||||
Expr *arg = args[i];
|
||||
Decl *param = func_params[i];
|
||||
if (!sema_analyse_expr(context, param->type, arg))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (param->var.type_info)
|
||||
{
|
||||
if (!sema_resolve_type_info(context, param->var.type_info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
param->type = param->var.type_info->type;
|
||||
}
|
||||
else
|
||||
{
|
||||
param->type = arg->type;
|
||||
}
|
||||
}
|
||||
|
||||
context->macro_nesting++;
|
||||
context->macro_counter++;
|
||||
Decl **old_macro_locals_start = context->macro_locals_start;
|
||||
context->macro_locals_start = context->last_local;
|
||||
|
||||
bool ok = true;
|
||||
|
||||
Ast *body = ast_copy_from_macro(context, decl->macro_decl.body);
|
||||
|
||||
call_expr->type = type_void;
|
||||
|
||||
Ast **saved_returns = context_push_returns(context);
|
||||
context->expected_block_type = to;
|
||||
context_push_scope_with_flags(context, SCOPE_MACRO);
|
||||
|
||||
for (unsigned i = 0; i < num_args; i++)
|
||||
{
|
||||
Decl *param = func_params[i];
|
||||
if (args[i]->expr_kind == EXPR_CONST)
|
||||
{
|
||||
param = decl_copy_local_from_macro(context, param);
|
||||
param->var.constant = true;
|
||||
param->var.init_expr = args[i];
|
||||
args[i] = NULL;
|
||||
}
|
||||
sema_add_local(context, param);
|
||||
}
|
||||
|
||||
VECEACH(body->compound_stmt.stmts, i)
|
||||
{
|
||||
if (!sema_analyse_statement(context, body->compound_stmt.stmts[i]))
|
||||
{
|
||||
ok = false;
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vec_size(context->returns))
|
||||
{
|
||||
if (to)
|
||||
{
|
||||
SEMA_ERROR(decl, "Missing return in macro that evaluates to %s.", type_to_error_string(to));
|
||||
ok = false;
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
Expr *first_return_expr = context->returns[0]->return_stmt.expr;
|
||||
Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void;
|
||||
// Let's unify the return statements.
|
||||
left_canonical = unify_returns(context, left_canonical);
|
||||
if (!left_canonical)
|
||||
{
|
||||
ok = false;
|
||||
goto EXIT;
|
||||
}
|
||||
call_expr->type = left_canonical;
|
||||
call_expr->expr_kind = EXPR_MACRO_BLOCK;
|
||||
call_expr->macro_block.stmts = body->compound_stmt.stmts;
|
||||
call_expr->macro_block.params = func_params;
|
||||
call_expr->macro_block.args = args;
|
||||
EXIT:
|
||||
context_pop_scope(context);
|
||||
context_pop_returns(context, saved_returns);
|
||||
context->macro_nesting--;
|
||||
context->macro_locals_start = old_macro_locals_start;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
Expr *func_expr = expr->call_expr.function;
|
||||
@@ -509,8 +634,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr
|
||||
case DECL_FUNC:
|
||||
return sema_expr_analyse_func_call(context, to, expr, decl, struct_var);
|
||||
case DECL_MACRO:
|
||||
SEMA_ERROR(expr, "Macro calls must be preceeded by '@'.");
|
||||
return false;
|
||||
return sema_expr_analyse_macro_call(context, to, expr, decl);
|
||||
case DECL_GENERIC:
|
||||
return sema_expr_analyse_generic_call(context, to, expr);
|
||||
case DECL_POISONED:
|
||||
@@ -2611,7 +2735,7 @@ static Expr *expr_shallow_copy(Expr *source)
|
||||
|
||||
|
||||
|
||||
static TypeInfo *type_info_copy_from_macro(Context *context, Expr *macro, TypeInfo *source)
|
||||
static TypeInfo *type_info_copy_from_macro(Context *context, TypeInfo *source)
|
||||
{
|
||||
if (!source) return NULL;
|
||||
TypeInfo *copy = malloc_arena(sizeof(TypeInfo));
|
||||
@@ -2621,50 +2745,50 @@ static TypeInfo *type_info_copy_from_macro(Context *context, Expr *macro, TypeIn
|
||||
case TYPE_INFO_POISON:
|
||||
return copy;
|
||||
case TYPE_INFO_IDENTIFIER:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
TODO
|
||||
break;
|
||||
return copy;
|
||||
case TYPE_INFO_EXPRESSION:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
copy->unresolved_type_expr = expr_copy_from_macro(context, macro, source->unresolved_type_expr);
|
||||
copy->unresolved_type_expr = expr_copy_from_macro(context, source->unresolved_type_expr);
|
||||
return copy;
|
||||
case TYPE_INFO_ARRAY:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
copy->array.len = expr_copy_from_macro(context, macro, source->array.len);
|
||||
copy->array.base = type_info_copy_from_macro(context, macro, source->array.base);
|
||||
copy->array.len = expr_copy_from_macro(context, source->array.len);
|
||||
copy->array.base = type_info_copy_from_macro(context, source->array.base);
|
||||
return copy;
|
||||
case TYPE_INFO_INC_ARRAY:
|
||||
case TYPE_INFO_VARARRAY:
|
||||
case TYPE_INFO_SUBARRAY:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
copy->array.base = type_info_copy_from_macro(context, macro, source->array.base);
|
||||
copy->array.base = type_info_copy_from_macro(context, source->array.base);
|
||||
return copy;
|
||||
case TYPE_INFO_POINTER:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
copy->pointer = type_info_copy_from_macro(context, macro, source->pointer);
|
||||
copy->pointer = type_info_copy_from_macro(context, source->pointer);
|
||||
return copy;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
|
||||
static Ast** ast_copy_list_from_macro(Context *context, Expr *macro, Ast **to_copy)
|
||||
static Ast** ast_copy_list_from_macro(Context *context, Ast **to_copy)
|
||||
{
|
||||
Ast **result = NULL;
|
||||
VECEACH(to_copy, i)
|
||||
{
|
||||
vec_add(result, ast_copy_from_macro(context, macro, to_copy[i]));
|
||||
vec_add(result, ast_copy_from_macro(context, to_copy[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_expr)
|
||||
static Expr *expr_copy_from_macro(Context *context, Expr *source_expr)
|
||||
{
|
||||
if (!source_expr) return NULL;
|
||||
Expr *expr = expr_shallow_copy(source_expr);
|
||||
switch (source_expr->expr_kind)
|
||||
{
|
||||
case EXPR_MACRO_BLOCK:
|
||||
UNREACHABLE
|
||||
case EXPR_TYPEOF:
|
||||
MACRO_COPY_EXPR(expr->typeof_expr);
|
||||
return expr;
|
||||
@@ -2709,8 +2833,8 @@ static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_ex
|
||||
MACRO_COPY_TYPE(expr->typeid_expr);
|
||||
return expr;
|
||||
case EXPR_IDENTIFIER:
|
||||
TODO
|
||||
break;
|
||||
// TODO
|
||||
return expr;
|
||||
case EXPR_TYPE_ACCESS:
|
||||
MACRO_COPY_TYPE(expr->type_access.type);
|
||||
return expr;
|
||||
@@ -2741,35 +2865,32 @@ static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_ex
|
||||
case EXPR_SCOPED_EXPR:
|
||||
MACRO_COPY_EXPR(expr->expr_scope.expr);
|
||||
return expr;
|
||||
case EXPR_MACRO_EXPR:
|
||||
MACRO_COPY_EXPR(expr->macro_expr);
|
||||
return expr;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static Expr **expr_copy_expr_list_from_macro(Context *context, Expr *macro, Expr **expr_list)
|
||||
static Expr **expr_copy_expr_list_from_macro(Context *context, Expr **expr_list)
|
||||
{
|
||||
Expr **result = NULL;
|
||||
VECEACH(expr_list, i)
|
||||
{
|
||||
vec_add(result, expr_copy_from_macro(context, macro, expr_list[i]));
|
||||
vec_add(result, expr_copy_from_macro(context, expr_list[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static TypeInfo** type_info_copy_list_from_macro(Context *context, Expr *macro, TypeInfo **to_copy)
|
||||
static TypeInfo** type_info_copy_list_from_macro(Context *context, TypeInfo **to_copy)
|
||||
{
|
||||
TypeInfo **result = NULL;
|
||||
VECEACH(to_copy, i)
|
||||
{
|
||||
vec_add(result, type_info_copy_from_macro(context, macro, to_copy[i]));
|
||||
vec_add(result, type_info_copy_from_macro(context, to_copy[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source)
|
||||
static Ast *ast_copy_from_macro(Context *context, Ast *source)
|
||||
{
|
||||
Ast *ast = ast_shallow_copy(source);
|
||||
switch (source->ast_kind)
|
||||
@@ -2822,7 +2943,7 @@ static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source)
|
||||
MACRO_COPY_TYPE_LIST(ast->ct_case_stmt.types);
|
||||
return ast;
|
||||
case AST_DECLARE_STMT:
|
||||
TODO
|
||||
ast->declare_stmt = decl_copy_local_from_macro(context, ast->declare_stmt);
|
||||
return ast;
|
||||
case AST_DEFAULT_STMT:
|
||||
MACRO_COPY_AST(ast->case_stmt.body);
|
||||
@@ -2905,89 +3026,6 @@ static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source)
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *macro, Expr *inner)
|
||||
{
|
||||
if (context->macro_nesting > MAX_MACRO_NESTING)
|
||||
{
|
||||
SEMA_ERROR(context->first_macro_call, "Too deep nesting (more than %d levels) when evaluating this macro.", MAX_MACRO_NESTING);
|
||||
return false;
|
||||
}
|
||||
|
||||
Expr *func_expr = inner->call_expr.function;
|
||||
bool was_in_macro_call = context->in_macro_call;
|
||||
context->in_macro_call = true;
|
||||
bool ok = sema_analyse_expr(context, NULL, inner->call_expr.function);
|
||||
context->in_macro_call = was_in_macro_call;
|
||||
if (!ok) return ok;
|
||||
|
||||
Decl *decl;
|
||||
switch (inner->call_expr.function->expr_kind)
|
||||
{
|
||||
case EXPR_IDENTIFIER:
|
||||
decl = func_expr->identifier_expr.decl;
|
||||
break;
|
||||
default:
|
||||
SEMA_ERROR(inner, "Expected a macro identifier here");
|
||||
return false;
|
||||
}
|
||||
if (decl->decl_kind != DECL_MACRO)
|
||||
{
|
||||
SEMA_ERROR(macro, "A macro was expected here.");
|
||||
return false;
|
||||
}
|
||||
Expr **args =func_expr->call_expr.arguments;
|
||||
Decl **func_params = decl->macro_decl.parameters;
|
||||
|
||||
unsigned num_args = vec_size(args);
|
||||
for (unsigned i = 0; i < num_args; i++)
|
||||
{
|
||||
Expr *arg = args[i];
|
||||
Decl *param = func_params[i];
|
||||
if (!sema_analyse_expr(context, param->type, arg)) return false;
|
||||
}
|
||||
|
||||
Ast *body = ast_copy_from_macro(context, inner, decl->macro_decl.body);
|
||||
|
||||
ok = true;
|
||||
macro->type = type_void;
|
||||
|
||||
Ast **saved_returns = context_push_returns(context);
|
||||
context->expected_block_type = to;
|
||||
context_push_scope_with_flags(context, SCOPE_EXPR_BLOCK);
|
||||
VECEACH(body->compound_stmt.stmts, i)
|
||||
{
|
||||
if (!sema_analyse_statement(context, body->compound_stmt.stmts[i]))
|
||||
{
|
||||
ok = false;
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vec_size(context->returns))
|
||||
{
|
||||
if (to)
|
||||
{
|
||||
SEMA_ERROR(decl, "Missing return in macro that evaluates to %s.", type_to_error_string(to));
|
||||
ok = false;
|
||||
}
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
Expr *first_return_expr = context->returns[0]->return_stmt.expr;
|
||||
Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void;
|
||||
// Let's unify the return statements.
|
||||
left_canonical = unify_returns(context, left_canonical);
|
||||
if (!left_canonical)
|
||||
{
|
||||
ok = false;
|
||||
goto EXIT;
|
||||
}
|
||||
macro->type = left_canonical;
|
||||
EXIT:
|
||||
context_pop_scope(context);
|
||||
context_pop_returns(context, saved_returns);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_macro_call2(Context *context, Type *to, Expr *expr, Decl *macro)
|
||||
{
|
||||
@@ -3007,9 +3045,11 @@ static inline bool sema_expr_analyse_macro_call2(Context *context, Type *to, Exp
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_macro_expr(Context *context, Type *to, Expr *expr)
|
||||
static inline bool sema_expr_analyse_macro_ident(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
Expr *inner = expr->macro_expr;
|
||||
return false;
|
||||
/*
|
||||
Expr *inner = expr->macro_ident;
|
||||
switch (inner->expr_kind)
|
||||
{
|
||||
case EXPR_CALL:
|
||||
@@ -3020,7 +3060,7 @@ static inline bool sema_expr_analyse_macro_expr(Context *context, Type *to, Expr
|
||||
default:
|
||||
SEMA_ERROR(expr, "Expected a macro name after '@'");
|
||||
return false;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_type(Context *context, Type *to, Expr *expr)
|
||||
@@ -3112,6 +3152,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
|
||||
case EXPR_DESIGNATED_INITIALIZER:
|
||||
// Created during semantic analysis
|
||||
UNREACHABLE
|
||||
case EXPR_MACRO_BLOCK:
|
||||
case EXPR_SCOPED_EXPR:
|
||||
UNREACHABLE
|
||||
case EXPR_TYPEOF:
|
||||
@@ -3120,8 +3161,6 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
|
||||
return sema_expr_analyse_compound_literal(context, to, expr);
|
||||
case EXPR_EXPR_BLOCK:
|
||||
return sema_expr_analyse_expr_block(context, to, expr);
|
||||
case EXPR_MACRO_EXPR:
|
||||
return sema_expr_analyse_macro_expr(context, to, expr);
|
||||
case EXPR_TRY:
|
||||
return sema_expr_analyse_try(context, to, expr);
|
||||
case EXPR_RANGE:
|
||||
|
||||
@@ -75,6 +75,7 @@ Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl
|
||||
if (context->current_scope)
|
||||
{
|
||||
Decl **first = &context->locals[0];
|
||||
if (context->macro_nesting) first = context->macro_locals_start;
|
||||
Decl **current = context->last_local - 1;
|
||||
while (current >= first)
|
||||
{
|
||||
@@ -141,4 +142,15 @@ bool sema_add_local(Context *context, Decl *decl)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sema_add_macro_local(Context *context, Decl *decl)
|
||||
{
|
||||
if (context->last_local == &context->locals[MAX_LOCALS - 1])
|
||||
{
|
||||
SEMA_ERROR(decl, "Reached the maximum number of locals.");
|
||||
return false;
|
||||
}
|
||||
context->last_local[0] = decl;
|
||||
context->last_local++;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@ void context_push_scope_with_flags(Context *context, ScopeFlags flags)
|
||||
{
|
||||
context->current_scope->flags = previous_flags | flags;
|
||||
}
|
||||
if (previous_flags & SCOPE_MACRO)
|
||||
{
|
||||
context->current_scope->flags = previous_flags | SCOPE_MACRO;
|
||||
}
|
||||
context->current_scope->flags_created = flags;
|
||||
}
|
||||
|
||||
|
||||
@@ -194,6 +194,7 @@ static void type_append_signature_name_user_defined(Decl *decl, char *dst, size_
|
||||
}
|
||||
void type_append_signature_name(Type *type, char *dst, size_t *offset)
|
||||
{
|
||||
type = type->canonical;
|
||||
assert(*offset < MAX_FUNCTION_SIGNATURE_SIZE);
|
||||
const char *name;
|
||||
switch (type->type_kind)
|
||||
|
||||
@@ -112,8 +112,8 @@ static inline bool is_digit(char c)
|
||||
static inline int char_to_nibble(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c <= 'F') return c - 'A';
|
||||
if (c <= 'f') return c - 'f';
|
||||
if (c <= 'F') return c - 'A' + 10;
|
||||
if (c <= 'f') return c - 'a' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user