Add $foreach

This commit is contained in:
Christoffer Lerno
2022-01-24 18:53:44 +01:00
parent 4f212f7634
commit 1e7ad2e241
9 changed files with 169 additions and 36 deletions

View File

@@ -1164,8 +1164,9 @@ typedef struct
TokenId index;
TokenId value;
Expr *expr;
Ast *body;
} AstCtForStmt;
AstId body;
} AstCtForeachStmt;
typedef struct
{
@@ -1290,7 +1291,7 @@ typedef struct Ast_
AstCtIfStmt ct_if_stmt; // 24
AstCtIfStmt ct_elif_stmt; // 24
Ast *ct_else_stmt; // 8
AstCtForStmt ct_for_stmt; // 64
AstCtForeachStmt ct_foreach_stmt; // 64
AstScopedStmt scoped_stmt; // 16
AstScopingStmt scoping_stmt;
AstAssertStmt ct_assert_stmt;

View File

@@ -286,13 +286,16 @@ Ast *ast_copy_deep(Ast *source)
MACRO_COPY_AST(ast->ct_elif_stmt.then);
MACRO_COPY_AST(ast->ct_elif_stmt.elif);
return ast;
case AST_CT_ELSE_STMT:
MACRO_COPY_AST(ast->ct_else_stmt);
return ast;
case AST_CT_FOR_STMT:
MACRO_COPY_AST(ast->ct_for_stmt.body);
MACRO_COPY_EXPR(ast->ct_for_stmt.expr);
case AST_CT_FOREACH_STMT:
ast->ct_foreach_stmt.body = astid_copy_deep(ast->ct_foreach_stmt.body);
MACRO_COPY_EXPR(ast->ct_foreach_stmt.expr);
return ast;
case AST_CT_FOR_STMT:
TODO
case AST_CT_SWITCH_STMT:
MACRO_COPY_EXPR(ast->ct_switch_stmt.cond);
MACRO_COPY_AST_LIST(ast->ct_switch_stmt.body);

View File

@@ -57,6 +57,7 @@ typedef enum
AST_CT_ELIF_STMT,
AST_CT_ELSE_STMT,
AST_CT_FOR_STMT,
AST_CT_FOREACH_STMT,
AST_CT_SWITCH_STMT,
AST_DECLARE_STMT,
AST_DEFAULT_STMT,
@@ -474,10 +475,13 @@ typedef enum
TOKEN_CT_DEFAULT, // $default
TOKEN_CT_DEFINED, // $defined
TOKEN_CT_FOR, // $for
TOKEN_CT_FOREACH, // $foreach
TOKEN_CT_ELIF, // $elif
TOKEN_CT_ELSE, // $else
TOKEN_CT_ENDIF, // $endif
TOKEN_CT_ENDSWITCH, // $endswitch
TOKEN_CT_ENDFOR, // $endfor
TOKEN_CT_ENDFOREACH, // $endforeach
TOKEN_CT_EXTNAMEOF, // $extnameof
TOKEN_CT_IF, // $if
TOKEN_CT_NAMEOF, // $nameof

View File

@@ -1289,7 +1289,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
break;
case AST_ASSERT_STMT:
llvm_emit_assert_stmt(c, ast);
break;;
break;
case AST_CT_ASSERT:
case AST_CT_IF_STMT:
case AST_CT_ELIF_STMT:
@@ -1298,6 +1298,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
case AST_CT_SWITCH_STMT:
case AST_CASE_STMT:
case AST_DEFAULT_STMT:
case AST_CT_FOREACH_STMT:
UNREACHABLE
case AST_SWITCH_STMT:
gencontext_emit_switch(c, ast);

View File

@@ -666,31 +666,38 @@ static inline Ast *parse_return(ParseContext *context)
/**
* ct_for_stmt
* : CT_FOR '(' CT_IDENT IN expression ')' statement
* | CT_FOR '(' CT_IDENT, CT_IDENT IN expression ')' statement
* ct_foreach_stmt
* | CT_FOREACH '(' CT_IDENT (',' CT_IDENT)? ':' expr ')' statement
* ;
*
* @return
*/
static inline Ast* parse_ct_for_stmt(ParseContext *context)
static inline Ast* parse_ct_foreach_stmt(ParseContext *context)
{
Ast *ast = AST_NEW_TOKEN(AST_CT_FOR_STMT, context->tok);
advance_and_verify(context, TOKEN_CT_FOR);
Ast *ast = AST_NEW_TOKEN(AST_CT_FOREACH_STMT, context->tok);
advance_and_verify(context, TOKEN_CT_FOREACH);
CONSUME_OR(TOKEN_LPAREN, poisoned_ast);
if (context->next_tok.type == TOKEN_COMMA)
{
ast->ct_for_stmt.index = context->tok.id;
ast->ct_foreach_stmt.index = context->tok.id;
TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time index variable", poisoned_ast);
advance_and_verify(context, TOKEN_COMMA);
}
ast->ct_for_stmt.value = context->tok.id;
ast->ct_foreach_stmt.value = context->tok.id;
TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time variable", poisoned_ast);
TRY_CONSUME_OR(TOKEN_COLON, "Expected ':'.", poisoned_ast);
ASSIGN_EXPR_ELSE(ast->ct_for_stmt.expr, parse_expr(context), poisoned_ast);
ASSIGN_EXPR_ELSE(ast->ct_foreach_stmt.expr, parse_expr(context), poisoned_ast);
CONSUME_OR(TOKEN_RPAREN, poisoned_ast);
ASSIGN_AST_ELSE(ast->ct_for_stmt.body, parse_stmt(context), poisoned_ast);
CONSUME_OR(TOKEN_COLON, poisoned_ast);
Ast *body = new_ast(AST_COMPOUND_STMT, ast->span);
ast->ct_foreach_stmt.body = astid(body);
AstId *current = &body->compound_stmt.first_stmt;
while (!try_consume(context, TOKEN_CT_ENDFOREACH))
{
ASSIGN_AST_ELSE(Ast *stmt, parse_stmt(context), poisoned_ast);
*current = astid(stmt);
current = &stmt->next;
}
return ast;
}
@@ -856,8 +863,10 @@ Ast *parse_stmt(ParseContext *context)
return parse_ct_if_stmt(context);
case TOKEN_CT_SWITCH:
return parse_ct_switch_stmt(context);
case TOKEN_CT_FOREACH:
return parse_ct_foreach_stmt(context);
case TOKEN_CT_FOR:
return parse_ct_for_stmt(context);
TODO
case TOKEN_CT_UNREACHABLE:
return parse_unreachable_stmt(context);
case TOKEN_STAR:
@@ -972,6 +981,8 @@ Ast *parse_stmt(ParseContext *context)
case TOKEN_BITSTRUCT:
case TOKEN_LVEC:
case TOKEN_RVEC:
case TOKEN_CT_ENDFOR:
case TOKEN_CT_ENDFOREACH:
SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type));
advance(context);
return poisoned_ast;

View File

@@ -3643,6 +3643,7 @@ static inline void sema_update_const_initializer_with_designator(
}
/**
* Create a const initializer.
*/
@@ -4029,14 +4030,11 @@ static inline bool sema_expr_analyse_initializer(SemaContext *context, Type *ext
static inline bool sema_expr_analyse_initializer_list(SemaContext *context, Type *to, Expr *expr)
{
if (!to)
{
return sema_expr_analyse_initializer(context, type_complist, type_complist, expr);
}
assert(to);
Type *assigned = type_flatten(to);
switch (assigned->type_kind)
{
case TYPE_UNTYPED_LIST:
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_ARRAY:
@@ -6967,7 +6965,7 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr)
return sema_expr_analyse_access(context, expr);
case EXPR_INITIALIZER_LIST:
case EXPR_DESIGNATED_INITIALIZER_LIST:
return sema_expr_analyse_initializer_list(context, NULL, expr);
return sema_expr_analyse_initializer_list(context, type_complist, expr);
case EXPR_CAST:
return sema_expr_analyse_cast(context, expr);
case EXPR_EXPRESSION_LIST:

View File

@@ -773,6 +773,7 @@ static inline bool sema_analyse_var_stmt(SemaContext *context, Ast *statement)
// 2. Convert it to a NOP early
statement->ast_kind = AST_NOP_STMT;
Expr *init;
assert(decl->decl_kind == DECL_VAR);
switch (decl->var.kind)
{
@@ -783,8 +784,7 @@ static inline bool sema_analyse_var_stmt(SemaContext *context, Ast *statement)
SEMA_ERROR(decl->var.type_info, "Compile time type variables may not have a type.");
return false;
}
Expr *init = decl->var.init_expr;
if (init)
if ((init = decl->var.init_expr))
{
if (!sema_analyse_expr_lvalue(context, init)) return false;
if (init->expr_kind != EXPR_TYPEINFO)
@@ -804,12 +804,12 @@ static inline bool sema_analyse_var_stmt(SemaContext *context, Ast *statement)
SEMA_ERROR(decl->var.type_info, "Compile time variables may only be built-in types.");
return false;
}
if (decl->var.init_expr)
if ((init = decl->var.init_expr))
{
if (!sema_analyse_expr_rhs(context, decl->type, decl->var.init_expr, false)) return false;
if (!expr_is_constant_eval(decl->var.init_expr, CONSTANT_EVAL_ANY))
if (!sema_analyse_expr_rhs(context, decl->type, init, false)) return false;
if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY))
{
SEMA_ERROR(decl->var.init_expr, "Expected a constant expression assigned to %s.", decl->name);
SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name);
return false;
}
}
@@ -821,16 +821,15 @@ static inline bool sema_analyse_var_stmt(SemaContext *context, Ast *statement)
}
else
{
Expr *decl_init = decl->var.init_expr;
if (decl_init)
if ((init = decl->var.init_expr))
{
if (!sema_analyse_expr(context, decl_init)) return false;
if (!expr_is_constant_eval(decl_init, CONSTANT_EVAL_ANY))
if (!sema_analyse_expr(context, init)) return false;
if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY))
{
SEMA_ERROR(decl->var.init_expr, "Expected a constant expression assigned to %s.", decl->name);
SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name);
return false;
}
decl->type = decl->var.init_expr->type;
decl->type = init->type;
}
else
{
@@ -2183,6 +2182,62 @@ static bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statement)
return sema_analyse_ct_switch_body(context, statement);
}
static bool sema_analyse_ct_foreach_stmt(SemaContext *context, Ast *statement)
{
Expr *collection = statement->ct_foreach_stmt.expr;
if (!sema_analyse_ct_expr(context, collection)) return false;
if (collection->expr_kind != EXPR_INITIALIZER_LIST)
{
SEMA_ERROR(collection, "Expected a list to iterate over");
return false;
}
if (!expr_is_constant_eval(collection, CONSTANT_EVAL_ANY))
{
SEMA_ERROR(collection, "A compile time $foreach must be over a constant value.");
return false;
}
Expr **expression = collection->initializer_list;
Decl *index = NULL;
TokenId index_token = statement->ct_foreach_stmt.index;
AstId start = 0;
SCOPE_START;
if (index_token.index)
{
index = decl_new_var(index_token, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL);
index->type = type_int;
if (!sema_add_local(context, index)) return SCOPE_POP_ERROR();
}
Decl *value = decl_new_var(statement->ct_foreach_stmt.value, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL);
if (!sema_add_local(context, value)) return SCOPE_POP_ERROR();
// Get the body
Ast *body = astptr(statement->ct_foreach_stmt.body);
AstId *current = &start;
VECEACH(expression, i)
{
Ast *compound_stmt = ast_copy_deep(body);
value->var.init_expr = expression[i];
if (index)
{
Expr *expr = expr_new(EXPR_CONST, index->span);
expr_const_set_int(&expr->const_expr, i, TYPE_I32);
expr->const_expr.narrowable = true;
expr->type = type_int;
index->var.init_expr = expr;
index->type = type_int;
}
if (!sema_analyse_compound_stmt(context, compound_stmt)) return SCOPE_POP_ERROR();
*current = astid(compound_stmt);
current = &compound_stmt->next;
}
SCOPE_END;
statement->ast_kind = AST_COMPOUND_STMT;
statement->compound_stmt.first_stmt = start;
statement->compound_stmt.defer_list = (DeferList) { 0, 0 };
return true;
}
static bool sema_analyse_switch_stmt(SemaContext *context, Ast *statement)
{
statement->switch_stmt.scope_defer = context->active_scope.in_defer;
@@ -2465,6 +2520,8 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state
case AST_CT_ELIF_STMT:
case AST_CT_ELSE_STMT:
UNREACHABLE
case AST_CT_FOREACH_STMT:
return sema_analyse_ct_foreach_stmt(context, statement);
case AST_CT_FOR_STMT:
TODO
}

View File

@@ -351,6 +351,8 @@ const char *token_type_to_string(TokenType type)
return "$defined";
case TOKEN_CT_FOR:
return "$for";
case TOKEN_CT_FOREACH:
return "$foreach";
case TOKEN_CT_ELSE:
return "$else";
case TOKEN_CT_ELIF:
@@ -359,6 +361,10 @@ const char *token_type_to_string(TokenType type)
return "$endif";
case TOKEN_CT_ENDSWITCH:
return "$endswitch";
case TOKEN_CT_ENDFOR:
return "$endfor";
case TOKEN_CT_ENDFOREACH:
return "$endforeach";
case TOKEN_CT_EXTNAMEOF:
return "$extnameof";
case TOKEN_CT_IF:

View File

@@ -0,0 +1,52 @@
// #target: x64-darwin
module test;
extern fn void printf(char*, ...);
fn void main()
{
var $foo = { 1, 10, 34 };
$foreach ($i : $foo):
printf("Foo %d\n", $i);
$endforeach;
$foreach ($i, $j : $foo):
printf("Bar %d %d\n", $i, $j);
$endforeach;
$foreach ($x : { 123, "abc", 1177, "hello" }):
$typeof($x) z = $x;
$switch ($typeof($x)):
$case int:
printf("Bar %d\n", $x);
$default:
printf("Bar %s\n", $x);
$endswitch;
$endforeach;
}
/* #expect: test.ll
define void @test.main() #0 {
entry:
%z = alloca i32, align 4
%z1 = alloca [3 x i8]*, align 8
%z2 = alloca i32, align 4
%z3 = alloca [5 x i8]*, align 8
call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 1)
call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.1, i32 0, i32 0), i32 10)
call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.2, i32 0, i32 0), i32 34)
call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.3, i32 0, i32 0), i32 0, i32 1)
call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.4, i32 0, i32 0), i32 1, i32 10)
call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.5, i32 0, i32 0), i32 2, i32 34)
store i32 123, i32* %z, align 4
call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.6, i32 0, i32 0), i32 123)
store [3 x i8]* bitcast ([4 x i8]* @.str.7 to [3 x i8]*), [3 x i8]** %z1, align 8
call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.8, i32 0, i32 0), [3 x i8]* bitcast ([4 x i8]* @.str.9 to [3 x i8]*))
store i32 1177, i32* %z2, align 4
call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.10, i32 0, i32 0), i32 1177)
store [5 x i8]* bitcast ([6 x i8]* @.str.11 to [5 x i8]*), [5 x i8]** %z3, align 8
call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.12, i32 0, i32 0), [5 x i8]* bitcast ([6 x i8]* @.str.13 to [5 x i8]*))
ret void
}