| |
| |
| |
| |
| #include "parser.h" |
|
|
| |
|
|
| #define CUR(p) ((p)->tokens[(p)->pos]) |
| #define CURTYPE(p) (CUR(p).type) |
| #define IS(p, t) (CURTYPE(p) == (t)) |
| #define LOC(p) (CUR(p).loc) |
|
|
| static Token eat(Parser *p) { |
| Token t = CUR(p); |
| if (p->pos < p->count - 1) p->pos++; |
| return t; |
| } |
|
|
| static bool check(Parser *p, TokenType t) { |
| return CURTYPE(p) == t; |
| } |
|
|
| static bool match_tok(Parser *p, TokenType t) { |
| if (check(p, t)) { eat(p); return true; } |
| return false; |
| } |
|
|
| static Token expect(Parser *p, TokenType t) { |
| if (check(p, t)) return eat(p); |
| p2s_error(LOC(p), "expected '%s', got '%s' (\"%.*s\")", |
| token_type_str(t), token_type_str(CURTYPE(p)), |
| (int)CUR(p).text_len, CUR(p).text); |
| p->error_count++; |
| return CUR(p); |
| } |
|
|
| static bool is_type_token(Parser *p) { |
| TokenType t = CURTYPE(p); |
| return t == TOK_VOID || t == TOK_CHAR || t == TOK_SHORT || |
| t == TOK_INT || t == TOK_LONG || t == TOK_FLOAT || |
| t == TOK_DOUBLE || t == TOK_UNSIGNED || t == TOK_SIGNED || |
| t == TOK_STRUCT || t == TOK_UNION || t == TOK_ENUM || |
| t == TOK_CONST || t == TOK_VOLATILE || t == TOK_STATIC || |
| t == TOK_EXTERN || t == TOK_INLINE || t == TOK_REGISTER || |
| t == TOK_RESTRICT || t == TOK_TYPEDEF; |
| } |
|
|
| |
| static ASTType *parse_type(Parser *p); |
| static ASTNode *parse_expr(Parser *p); |
| static ASTNode *parse_assign_expr(Parser *p); |
| static ASTNode *parse_stmt(Parser *p); |
| static ASTNode *parse_block(Parser *p); |
| static ASTNode *parse_decl_or_stmt(Parser *p); |
|
|
| static ASTType *parse_base_type(Parser *p) { |
| ASTType *ty = ast_new_type(p->arena, TYPE_INT); |
| bool has_type = false; |
|
|
| |
| while (true) { |
| if (match_tok(p, TOK_CONST)) { ty->is_const = true; continue; } |
| if (match_tok(p, TOK_VOLATILE)) { ty->is_volatile = true; continue; } |
| if (match_tok(p, TOK_STATIC)) { ty->is_static = true; continue; } |
| if (match_tok(p, TOK_EXTERN)) { continue; } |
| if (match_tok(p, TOK_INLINE)) { continue; } |
| if (match_tok(p, TOK_REGISTER)) { continue; } |
| if (match_tok(p, TOK_RESTRICT)) { ty->is_restrict = true; continue; } |
| if (match_tok(p, TOK_TYPEDEF)) { continue; } |
| break; |
| } |
|
|
| |
| if (match_tok(p, TOK_VOID)) { ty->kind = TYPE_VOID; has_type = true; } |
| else if (match_tok(p, TOK_CHAR)) { ty->kind = TYPE_CHAR; has_type = true; } |
| else if (match_tok(p, TOK_SHORT)) { ty->kind = TYPE_SHORT; has_type = true; } |
| else if (match_tok(p, TOK_INT)) { ty->kind = TYPE_INT; has_type = true; } |
| else if (match_tok(p, TOK_LONG)) { ty->kind = TYPE_LONG; has_type = true; } |
| else if (match_tok(p, TOK_FLOAT)) { ty->kind = TYPE_FLOAT; has_type = true; } |
| else if (match_tok(p, TOK_DOUBLE)) { ty->kind = TYPE_DOUBLE; has_type = true; } |
| else if (match_tok(p, TOK_UNSIGNED)){ |
| if (match_tok(p, TOK_CHAR)) ty->kind = TYPE_UNSIGNED_CHAR; |
| else if (match_tok(p, TOK_SHORT)) ty->kind = TYPE_UNSIGNED_SHORT; |
| else if (match_tok(p, TOK_INT)) ty->kind = TYPE_UNSIGNED_INT; |
| else if (match_tok(p, TOK_LONG)) ty->kind = TYPE_UNSIGNED_LONG; |
| else ty->kind = TYPE_UNSIGNED_INT; |
| has_type = true; |
| } |
| else if (match_tok(p, TOK_SIGNED)) { |
| if (match_tok(p, TOK_CHAR)) ty->kind = TYPE_CHAR; |
| else if (match_tok(p, TOK_SHORT)) ty->kind = TYPE_SHORT; |
| else if (match_tok(p, TOK_INT)) ty->kind = TYPE_INT; |
| else if (match_tok(p, TOK_LONG)) ty->kind = TYPE_LONG; |
| else ty->kind = TYPE_INT; |
| has_type = true; |
| } |
| else if (check(p, TOK_STRUCT) || check(p, TOK_UNION)) { |
| bool is_union = check(p, TOK_UNION); |
| eat(p); |
| ty->kind = is_union ? TYPE_UNION : TYPE_STRUCT; |
| if (check(p, TOK_IDENT)) { |
| ty->name = arena_strdup(p->arena, CUR(p).text); |
| eat(p); |
| } |
| has_type = true; |
| |
| if (check(p, TOK_LBRACE)) { |
| eat(p); |
| int depth = 1; |
| while (depth > 0 && !IS(p, TOK_EOF)) { |
| if (check(p, TOK_LBRACE)) depth++; |
| if (check(p, TOK_RBRACE)) depth--; |
| eat(p); |
| } |
| } |
| } |
| else if (check(p, TOK_ENUM)) { |
| eat(p); |
| ty->kind = TYPE_ENUM; |
| if (check(p, TOK_IDENT)) { ty->name = arena_strdup(p->arena, CUR(p).text); eat(p); } |
| if (check(p, TOK_LBRACE)) { |
| eat(p); |
| while (!check(p, TOK_RBRACE) && !IS(p, TOK_EOF)) eat(p); |
| expect(p, TOK_RBRACE); |
| } |
| has_type = true; |
| } |
| else if (check(p, TOK_IDENT)) { |
| |
| ty->kind = TYPE_INT; |
| ty->name = arena_strdup(p->arena, CUR(p).text); |
| eat(p); |
| has_type = true; |
| } |
|
|
| if (!has_type) ty->kind = TYPE_INT; |
|
|
| |
| while (match_tok(p, TOK_LONG)) { ty->kind = TYPE_LONG; } |
|
|
| return ty; |
| } |
|
|
| static ASTType *parse_type(Parser *p) { |
| ASTType *base = parse_base_type(p); |
| |
| while (check(p, TOK_STAR)) { |
| eat(p); |
| ASTType *ptr = ast_ptr_type(p->arena, base); |
| while (match_tok(p, TOK_CONST)) ptr->is_const = true; |
| while (match_tok(p, TOK_RESTRICT)) ptr->is_restrict = true; |
| base = ptr; |
| } |
| return base; |
| } |
|
|
| |
|
|
| static ASTNode *parse_primary(Parser *p) { |
| SourceLoc loc = LOC(p); |
|
|
| if (check(p, TOK_INT_LIT)) { |
| Token t = eat(p); |
| ASTNode *n = ast_new(p->arena, NODE_INT_LIT, loc); |
| n->int_lit.value = t.int_val; |
| return n; |
| } |
| if (check(p, TOK_FLOAT_LIT)) { |
| Token t = eat(p); |
| ASTNode *n = ast_new(p->arena, NODE_FLOAT_LIT, loc); |
| n->float_lit.value = t.float_val; |
| return n; |
| } |
| if (check(p, TOK_STRING_LIT)) { |
| Token t = eat(p); |
| ASTNode *n = ast_new(p->arena, NODE_STRING_LIT, loc); |
| n->string_lit.value = t.text; |
| return n; |
| } |
| if (check(p, TOK_CHAR_LIT)) { |
| Token t = eat(p); |
| ASTNode *n = ast_new(p->arena, NODE_CHAR_LIT, loc); |
| n->char_lit.value = t.int_val; |
| return n; |
| } |
| if (check(p, TOK_IDENT)) { |
| Token t = eat(p); |
| ASTNode *n = ast_new(p->arena, NODE_IDENT, loc); |
| n->ident.name = t.text; |
| return n; |
| } |
| if (match_tok(p, TOK_LPAREN)) { |
| |
| if (is_type_token(p)) { |
| ASTType *ty = parse_type(p); |
| expect(p, TOK_RPAREN); |
| ASTNode *expr = parse_primary(p); |
| ASTNode *n = ast_new(p->arena, NODE_CAST, loc); |
| n->cast.target_type = ty; |
| n->cast.expr = expr; |
| return n; |
| } |
| ASTNode *expr = parse_expr(p); |
| expect(p, TOK_RPAREN); |
| return expr; |
| } |
| if (check(p, TOK_SIZEOF)) { |
| eat(p); |
| ASTNode *n = ast_new(p->arena, NODE_SIZEOF, loc); |
| if (match_tok(p, TOK_LPAREN)) { |
| if (is_type_token(p)) { |
| n->size_of.of_type = parse_type(p); |
| } else { |
| n->size_of.of_expr = parse_expr(p); |
| } |
| expect(p, TOK_RPAREN); |
| } else { |
| n->size_of.of_expr = parse_primary(p); |
| } |
| return n; |
| } |
|
|
| p2s_error(loc, "expected expression, got '%s'", token_type_str(CURTYPE(p))); |
| p->error_count++; |
| eat(p); |
| return ast_new(p->arena, NODE_INT_LIT, loc); |
| } |
|
|
| static ASTNode *parse_postfix(Parser *p) { |
| ASTNode *expr = parse_primary(p); |
| while (true) { |
| SourceLoc loc = LOC(p); |
| if (match_tok(p, TOK_LBRACKET)) { |
| ASTNode *idx = parse_expr(p); |
| expect(p, TOK_RBRACKET); |
| ASTNode *n = ast_new(p->arena, NODE_INDEX, loc); |
| n->index.array = expr; |
| n->index.index = idx; |
| expr = n; |
| } |
| else if (match_tok(p, TOK_LPAREN)) { |
| |
| ASTNode *n = ast_new(p->arena, NODE_CALL, loc); |
| n->call.func = expr; |
| vec_init(&n->call.args); |
| if (!check(p, TOK_RPAREN)) { |
| vec_push(&n->call.args, parse_assign_expr(p)); |
| while (match_tok(p, TOK_COMMA)) |
| vec_push(&n->call.args, parse_assign_expr(p)); |
| } |
| expect(p, TOK_RPAREN); |
| expr = n; |
| } |
| else if (match_tok(p, TOK_DOT)) { |
| Token field = expect(p, TOK_IDENT); |
| ASTNode *n = ast_new(p->arena, NODE_MEMBER, loc); |
| n->member.object = expr; |
| n->member.field = field.text; |
| expr = n; |
| } |
| else if (match_tok(p, TOK_ARROW)) { |
| Token field = expect(p, TOK_IDENT); |
| ASTNode *n = ast_new(p->arena, NODE_ARROW, loc); |
| n->member.object = expr; |
| n->member.field = field.text; |
| expr = n; |
| } |
| else if (match_tok(p, TOK_INC)) { |
| ASTNode *n = ast_new(p->arena, NODE_POSTFIX, loc); |
| n->postfix.op = OP_POST_INC; |
| n->postfix.operand = expr; |
| expr = n; |
| } |
| else if (match_tok(p, TOK_DEC)) { |
| ASTNode *n = ast_new(p->arena, NODE_POSTFIX, loc); |
| n->postfix.op = OP_POST_DEC; |
| n->postfix.operand = expr; |
| expr = n; |
| } |
| else break; |
| } |
| return expr; |
| } |
|
|
| static ASTNode *parse_unary(Parser *p) { |
| SourceLoc loc = LOC(p); |
|
|
| if (match_tok(p, TOK_INC)) { |
| ASTNode *n = ast_new(p->arena, NODE_UNARY, loc); |
| n->unary.op = OP_INC; |
| n->unary.operand = parse_unary(p); |
| return n; |
| } |
| if (match_tok(p, TOK_DEC)) { |
| ASTNode *n = ast_new(p->arena, NODE_UNARY, loc); |
| n->unary.op = OP_DEC; |
| n->unary.operand = parse_unary(p); |
| return n; |
| } |
| if (match_tok(p, TOK_MINUS)) { |
| ASTNode *n = ast_new(p->arena, NODE_UNARY, loc); |
| n->unary.op = OP_NEG; |
| n->unary.operand = parse_unary(p); |
| return n; |
| } |
| if (match_tok(p, TOK_PLUS)) { |
| return parse_unary(p); |
| } |
| if (match_tok(p, TOK_BANG)) { |
| ASTNode *n = ast_new(p->arena, NODE_UNARY, loc); |
| n->unary.op = OP_LNOT; |
| n->unary.operand = parse_unary(p); |
| return n; |
| } |
| if (match_tok(p, TOK_TILDE)) { |
| ASTNode *n = ast_new(p->arena, NODE_UNARY, loc); |
| n->unary.op = OP_NOT; |
| n->unary.operand = parse_unary(p); |
| return n; |
| } |
| if (match_tok(p, TOK_AMP)) { |
| ASTNode *n = ast_new(p->arena, NODE_ADDR_OF, loc); |
| n->unary_expr.operand = parse_unary(p); |
| return n; |
| } |
| if (match_tok(p, TOK_STAR)) { |
| ASTNode *n = ast_new(p->arena, NODE_DEREF, loc); |
| n->unary_expr.operand = parse_unary(p); |
| return n; |
| } |
| return parse_postfix(p); |
| } |
|
|
| |
| static int get_precedence(TokenType t) { |
| switch (t) { |
| case TOK_STAR: case TOK_SLASH: case TOK_PERCENT: return 13; |
| case TOK_PLUS: case TOK_MINUS: return 12; |
| case TOK_LSHIFT: case TOK_RSHIFT: return 11; |
| case TOK_LT: case TOK_GT: case TOK_LE: case TOK_GE: return 10; |
| case TOK_EQ: case TOK_NEQ: return 9; |
| case TOK_AMP: return 8; |
| case TOK_CARET: return 7; |
| case TOK_PIPE: return 6; |
| case TOK_AND: return 5; |
| case TOK_OR: return 4; |
| default: return -1; |
| } |
| } |
|
|
| static OpType token_to_binop(TokenType t) { |
| switch (t) { |
| case TOK_PLUS: return OP_ADD; |
| case TOK_MINUS: return OP_SUB; |
| case TOK_STAR: return OP_MUL; |
| case TOK_SLASH: return OP_DIV; |
| case TOK_PERCENT: return OP_MOD; |
| case TOK_AMP: return OP_AND; |
| case TOK_PIPE: return OP_OR; |
| case TOK_CARET: return OP_XOR; |
| case TOK_LSHIFT: return OP_LSHIFT; |
| case TOK_RSHIFT: return OP_RSHIFT; |
| case TOK_EQ: return OP_EQ; |
| case TOK_NEQ: return OP_NEQ; |
| case TOK_LT: return OP_LT; |
| case TOK_GT: return OP_GT; |
| case TOK_LE: return OP_LE; |
| case TOK_GE: return OP_GE; |
| case TOK_AND: return OP_LAND; |
| case TOK_OR: return OP_LOR; |
| default: return OP_ADD; |
| } |
| } |
|
|
| static ASTNode *parse_binary(Parser *p, int min_prec) { |
| ASTNode *left = parse_unary(p); |
| while (true) { |
| int prec = get_precedence(CURTYPE(p)); |
| if (prec < min_prec) break; |
| SourceLoc loc = LOC(p); |
| OpType op = token_to_binop(CURTYPE(p)); |
| eat(p); |
| ASTNode *right = parse_binary(p, prec + 1); |
| ASTNode *n = ast_new(p->arena, NODE_BINARY, loc); |
| n->binary.op = op; |
| n->binary.left = left; |
| n->binary.right = right; |
| left = n; |
| } |
| return left; |
| } |
|
|
| static ASTNode *parse_ternary(Parser *p) { |
| ASTNode *cond = parse_binary(p, 0); |
| if (match_tok(p, TOK_QUESTION)) { |
| SourceLoc loc = LOC(p); |
| ASTNode *then_expr = parse_expr(p); |
| expect(p, TOK_COLON); |
| ASTNode *else_expr = parse_ternary(p); |
| ASTNode *n = ast_new(p->arena, NODE_TERNARY, loc); |
| n->ternary.cond = cond; |
| n->ternary.then_expr = then_expr; |
| n->ternary.else_expr = else_expr; |
| return n; |
| } |
| return cond; |
| } |
|
|
| static OpType assign_op(TokenType t) { |
| switch (t) { |
| case TOK_ASSIGN: return OP_ASSIGN; |
| case TOK_PLUS_EQ: return OP_ADD_ASSIGN; |
| case TOK_MINUS_EQ: return OP_SUB_ASSIGN; |
| case TOK_STAR_EQ: return OP_MUL_ASSIGN; |
| case TOK_SLASH_EQ: return OP_DIV_ASSIGN; |
| case TOK_PERCENT_EQ: return OP_MOD_ASSIGN; |
| case TOK_AMP_EQ: return OP_AND_ASSIGN; |
| case TOK_PIPE_EQ: return OP_OR_ASSIGN; |
| case TOK_CARET_EQ: return OP_XOR_ASSIGN; |
| case TOK_LSHIFT_EQ: return OP_LSHIFT_ASSIGN; |
| case TOK_RSHIFT_EQ: return OP_RSHIFT_ASSIGN; |
| default: return (OpType)-1; |
| } |
| } |
|
|
| static bool is_assign_token(TokenType t) { |
| return t == TOK_ASSIGN || t == TOK_PLUS_EQ || t == TOK_MINUS_EQ || |
| t == TOK_STAR_EQ || t == TOK_SLASH_EQ || t == TOK_PERCENT_EQ || |
| t == TOK_AMP_EQ || t == TOK_PIPE_EQ || t == TOK_CARET_EQ || |
| t == TOK_LSHIFT_EQ || t == TOK_RSHIFT_EQ; |
| } |
|
|
| static ASTNode *parse_assign_expr(Parser *p) { |
| ASTNode *left = parse_ternary(p); |
| if (is_assign_token(CURTYPE(p))) { |
| SourceLoc loc = LOC(p); |
| OpType op = assign_op(CURTYPE(p)); |
| eat(p); |
| ASTNode *right = parse_assign_expr(p); |
| ASTNode *n = ast_new(p->arena, NODE_ASSIGN, loc); |
| n->assign.op = op; |
| n->assign.target = left; |
| n->assign.value = right; |
| return n; |
| } |
| return left; |
| } |
|
|
| static ASTNode *parse_expr(Parser *p) { |
| ASTNode *left = parse_assign_expr(p); |
| while (match_tok(p, TOK_COMMA)) { |
| SourceLoc loc = LOC(p); |
| ASTNode *right = parse_assign_expr(p); |
| ASTNode *n = ast_new(p->arena, NODE_COMMA_EXPR, loc); |
| n->binary.op = OP_COMMA; |
| n->binary.left = left; |
| n->binary.right = right; |
| left = n; |
| } |
| return left; |
| } |
|
|
| |
|
|
| static ReduceOp parse_reduce_op(Parser *p) { |
| if (check(p, TOK_PLUS)) { eat(p); return REDUCE_ADD; } |
| if (check(p, TOK_STAR)) { eat(p); return REDUCE_MUL; } |
| if (check(p, TOK_AMP)) { eat(p); return REDUCE_AND; } |
| if (check(p, TOK_PIPE)) { eat(p); return REDUCE_OR; } |
| if (check(p, TOK_CARET)) { eat(p); return REDUCE_XOR; } |
| if (check(p, TOK_IDENT)) { |
| if (strcmp(CUR(p).text, "min") == 0) { eat(p); return REDUCE_MIN; } |
| if (strcmp(CUR(p).text, "max") == 0) { eat(p); return REDUCE_MAX; } |
| } |
| p2s_error(LOC(p), "expected reduction operator (+, *, &, |, ^, min, max)"); |
| p->error_count++; |
| return REDUCE_ADD; |
| } |
|
|
| static ASTNode *parse_parallel_for(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| expect(p, TOK_LPAREN); |
|
|
| ASTNode *n = ast_new(p->arena, NODE_PARALLEL_FOR, loc); |
| Token var = expect(p, TOK_IDENT); |
| n->par_for.iter_var = var.text; |
| expect(p, TOK_COMMA); |
| n->par_for.lo = parse_expr(p); |
| expect(p, TOK_COMMA); |
| n->par_for.hi = parse_expr(p); |
| |
| if (match_tok(p, TOK_COMMA)) { |
| n->par_for.step = parse_expr(p); |
| } |
| expect(p, TOK_RPAREN); |
| n->par_for.body = parse_block(p); |
| return n; |
| } |
|
|
| static ASTNode *parse_parallel_reduce(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| expect(p, TOK_LPAREN); |
|
|
| ASTNode *n = ast_new(p->arena, NODE_PARALLEL_REDUCE, loc); |
| Token acc = expect(p, TOK_IDENT); |
| n->par_reduce.accum_var = acc.text; |
| expect(p, TOK_COMMA); |
| n->par_reduce.op = parse_reduce_op(p); |
| expect(p, TOK_COMMA); |
| Token ivar = expect(p, TOK_IDENT); |
| n->par_reduce.iter_var = ivar.text; |
| expect(p, TOK_COMMA); |
| n->par_reduce.lo = parse_expr(p); |
| expect(p, TOK_COMMA); |
| n->par_reduce.hi = parse_expr(p); |
| expect(p, TOK_RPAREN); |
| n->par_reduce.body = parse_block(p); |
| return n; |
| } |
|
|
| static ASTNode *parse_parallel_scan(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| expect(p, TOK_LPAREN); |
|
|
| ASTNode *n = ast_new(p->arena, NODE_PARALLEL_SCAN, loc); |
| Token out = expect(p, TOK_IDENT); |
| n->par_scan.output_var = out.text; |
| expect(p, TOK_COMMA); |
| n->par_scan.op = parse_reduce_op(p); |
| expect(p, TOK_COMMA); |
| Token ivar = expect(p, TOK_IDENT); |
| n->par_scan.iter_var = ivar.text; |
| expect(p, TOK_COMMA); |
| n->par_scan.lo = parse_expr(p); |
| expect(p, TOK_COMMA); |
| n->par_scan.hi = parse_expr(p); |
| expect(p, TOK_RPAREN); |
| n->par_scan.body = parse_block(p); |
| return n; |
| } |
|
|
| static ASTNode *parse_parallel_map(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| expect(p, TOK_LPAREN); |
|
|
| ASTNode *n = ast_new(p->arena, NODE_PARALLEL_MAP, loc); |
| Token dst = expect(p, TOK_IDENT); |
| n->par_map.dst = dst.text; |
| expect(p, TOK_COMMA); |
| Token src = expect(p, TOK_IDENT); |
| n->par_map.src = src.text; |
| expect(p, TOK_COMMA); |
| n->par_map.count = parse_expr(p); |
| if (match_tok(p, TOK_COMMA)) { |
| Token fn = expect(p, TOK_IDENT); |
| n->par_map.func_name = fn.text; |
| } |
| expect(p, TOK_RPAREN); |
| n->par_map.body = parse_block(p); |
| return n; |
| } |
|
|
| static ASTNode *parse_tile_hint(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| expect(p, TOK_LPAREN); |
| ASTNode *n = ast_new(p->arena, NODE_TILE_HINT, loc); |
| Token tgt = expect(p, TOK_IDENT); |
| n->tile_hint.target = tgt.text; |
| expect(p, TOK_COMMA); |
| Token sz = expect(p, TOK_INT_LIT); |
| n->tile_hint.tile_size = (int)sz.int_val; |
| expect(p, TOK_RPAREN); |
| expect(p, TOK_SEMICOLON); |
| return n; |
| } |
|
|
| static ASTNode *parse_simd_hint(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| expect(p, TOK_LPAREN); |
| ASTNode *n = ast_new(p->arena, NODE_SIMD_HINT, loc); |
| Token tgt = expect(p, TOK_IDENT); |
| n->simd_hint.target = tgt.text; |
| expect(p, TOK_RPAREN); |
| expect(p, TOK_SEMICOLON); |
| return n; |
| } |
|
|
| static ASTNode *parse_memory_layout(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| expect(p, TOK_LPAREN); |
| ASTNode *n = ast_new(p->arena, NODE_MEMORY_LAYOUT, loc); |
| Token tgt = expect(p, TOK_IDENT); |
| n->mem_layout.target = tgt.text; |
| expect(p, TOK_COMMA); |
| Token layout = expect(p, TOK_IDENT); |
| n->mem_layout.soa = (strcmp(layout.text, "SOA") == 0 || strcmp(layout.text, "soa") == 0); |
| expect(p, TOK_RPAREN); |
| expect(p, TOK_SEMICOLON); |
| return n; |
| } |
|
|
| static ASTNode *parse_barrier(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| expect(p, TOK_LPAREN); |
| expect(p, TOK_RPAREN); |
| expect(p, TOK_SEMICOLON); |
| return ast_new(p->arena, NODE_BARRIER, loc); |
| } |
|
|
| |
|
|
| static ASTNode *parse_block(Parser *p) { |
| SourceLoc loc = LOC(p); |
| expect(p, TOK_LBRACE); |
| ASTNode *n = ast_new(p->arena, NODE_BLOCK, loc); |
| vec_init(&n->block.stmts); |
| while (!check(p, TOK_RBRACE) && !IS(p, TOK_EOF)) { |
| vec_push(&n->block.stmts, parse_decl_or_stmt(p)); |
| } |
| expect(p, TOK_RBRACE); |
| return n; |
| } |
|
|
| static ASTNode *parse_if(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| expect(p, TOK_LPAREN); |
| ASTNode *cond = parse_expr(p); |
| expect(p, TOK_RPAREN); |
| ASTNode *n = ast_new(p->arena, NODE_IF, loc); |
| n->if_stmt.cond = cond; |
| n->if_stmt.then_body = parse_stmt(p); |
| if (match_tok(p, TOK_ELSE)) |
| n->if_stmt.else_body = parse_stmt(p); |
| return n; |
| } |
|
|
| static ASTNode *parse_while(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| expect(p, TOK_LPAREN); |
| ASTNode *cond = parse_expr(p); |
| expect(p, TOK_RPAREN); |
| ASTNode *n = ast_new(p->arena, NODE_WHILE, loc); |
| n->while_stmt.cond = cond; |
| n->while_stmt.body = parse_stmt(p); |
| return n; |
| } |
|
|
| static ASTNode *parse_do_while(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| ASTNode *n = ast_new(p->arena, NODE_DO_WHILE, loc); |
| n->while_stmt.body = parse_stmt(p); |
| expect(p, TOK_WHILE); |
| expect(p, TOK_LPAREN); |
| n->while_stmt.cond = parse_expr(p); |
| expect(p, TOK_RPAREN); |
| expect(p, TOK_SEMICOLON); |
| return n; |
| } |
|
|
| static ASTNode *parse_for(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| expect(p, TOK_LPAREN); |
|
|
| ASTNode *n = ast_new(p->arena, NODE_FOR, loc); |
|
|
| |
| if (!check(p, TOK_SEMICOLON)) { |
| if (is_type_token(p)) { |
| |
| ASTType *ty = parse_type(p); |
| Token name = expect(p, TOK_IDENT); |
| ASTNode *decl = ast_new(p->arena, NODE_VAR_DECL, loc); |
| decl->var.name = name.text; |
| decl->var.var_type = ty; |
| if (match_tok(p, TOK_ASSIGN)) |
| decl->var.init = parse_assign_expr(p); |
| n->for_stmt.init = decl; |
| } else { |
| n->for_stmt.init = parse_expr(p); |
| } |
| } |
| expect(p, TOK_SEMICOLON); |
|
|
| if (!check(p, TOK_SEMICOLON)) |
| n->for_stmt.cond = parse_expr(p); |
| expect(p, TOK_SEMICOLON); |
|
|
| if (!check(p, TOK_RPAREN)) |
| n->for_stmt.step = parse_expr(p); |
| expect(p, TOK_RPAREN); |
|
|
| n->for_stmt.body = parse_stmt(p); |
| return n; |
| } |
|
|
| static ASTNode *parse_return(Parser *p) { |
| SourceLoc loc = LOC(p); |
| eat(p); |
| ASTNode *n = ast_new(p->arena, NODE_RETURN, loc); |
| if (!check(p, TOK_SEMICOLON)) |
| n->ret.value = parse_expr(p); |
| expect(p, TOK_SEMICOLON); |
| return n; |
| } |
|
|
| static ASTNode *parse_stmt(Parser *p) { |
| switch (CURTYPE(p)) { |
| case TOK_LBRACE: return parse_block(p); |
| case TOK_IF: return parse_if(p); |
| case TOK_WHILE: return parse_while(p); |
| case TOK_DO: return parse_do_while(p); |
| case TOK_FOR: return parse_for(p); |
| case TOK_RETURN: return parse_return(p); |
| case TOK_PARALLEL_FOR: return parse_parallel_for(p); |
| case TOK_PARALLEL_REDUCE: return parse_parallel_reduce(p); |
| case TOK_PARALLEL_SCAN: return parse_parallel_scan(p); |
| case TOK_PARALLEL_MAP: return parse_parallel_map(p); |
| case TOK_BARRIER: return parse_barrier(p); |
| case TOK_TILE_HINT: return parse_tile_hint(p); |
| case TOK_SIMD_HINT: return parse_simd_hint(p); |
| case TOK_MEMORY_LAYOUT: return parse_memory_layout(p); |
| case TOK_BREAK: |
| eat(p); expect(p, TOK_SEMICOLON); |
| return ast_new(p->arena, NODE_BREAK, LOC(p)); |
| case TOK_CONTINUE: |
| eat(p); expect(p, TOK_SEMICOLON); |
| return ast_new(p->arena, NODE_CONTINUE, LOC(p)); |
| default: { |
| SourceLoc loc = LOC(p); |
| ASTNode *expr = parse_expr(p); |
| expect(p, TOK_SEMICOLON); |
| ASTNode *n = ast_new(p->arena, NODE_EXPR_STMT, loc); |
| n->expr_stmt.expr = expr; |
| return n; |
| } |
| } |
| } |
|
|
| |
|
|
| static ASTNode *parse_var_decl(Parser *p, ASTType *type) { |
| SourceLoc loc = LOC(p); |
| Token name = expect(p, TOK_IDENT); |
| ASTNode *n = ast_new(p->arena, NODE_VAR_DECL, loc); |
| n->var.name = name.text; |
| n->var.var_type = type; |
|
|
| |
| while (match_tok(p, TOK_LBRACKET)) { |
| if (check(p, TOK_RBRACKET)) { |
| type = ast_array_type(p->arena, type, -1); |
| } else { |
| Token sz = expect(p, TOK_INT_LIT); |
| type = ast_array_type(p->arena, type, (int)sz.int_val); |
| } |
| expect(p, TOK_RBRACKET); |
| n->var.var_type = type; |
| } |
|
|
| |
| if (match_tok(p, TOK_ASSIGN)) |
| n->var.init = parse_assign_expr(p); |
|
|
| return n; |
| } |
|
|
| static ASTNode *parse_func_decl(Parser *p, ASTType *ret_type, const char *name) { |
| SourceLoc loc = LOC(p); |
| expect(p, TOK_LPAREN); |
|
|
| ASTNode *n = ast_new(p->arena, NODE_FUNC_DECL, loc); |
| n->func.name = name; |
| n->func.return_type = ret_type; |
| vec_init(&n->func.params); |
|
|
| if (!check(p, TOK_RPAREN)) { |
| if (check(p, TOK_VOID) && p->tokens[p->pos + 1].type == TOK_RPAREN) { |
| eat(p); |
| } else { |
| do { |
| if (check(p, TOK_ELLIPSIS)) { eat(p); break; } |
| ASTType *ptype = parse_type(p); |
| ASTNode *param = ast_new(p->arena, NODE_PARAM, LOC(p)); |
| if (check(p, TOK_IDENT)) { |
| param->param.name = CUR(p).text; |
| eat(p); |
| |
| while (match_tok(p, TOK_LBRACKET)) { |
| ptype = ast_ptr_type(p->arena, ptype); |
| if (!check(p, TOK_RBRACKET)) |
| parse_expr(p); |
| expect(p, TOK_RBRACKET); |
| } |
| } |
| param->param.param_type = ptype; |
| vec_push(&n->func.params, param); |
| } while (match_tok(p, TOK_COMMA)); |
| } |
| } |
| expect(p, TOK_RPAREN); |
|
|
| |
| if (check(p, TOK_LBRACE)) { |
| n->func.body = parse_block(p); |
| } else { |
| expect(p, TOK_SEMICOLON); |
| } |
| return n; |
| } |
|
|
| static ASTNode *parse_decl_or_stmt(Parser *p) { |
| |
| if (check(p, TOK_HASH)) { |
| SourceLoc loc = LOC(p); |
| Token t = eat(p); |
| ASTNode *n = ast_new(p->arena, NODE_PREPROC, loc); |
| n->preproc.text = t.text; |
| return n; |
| } |
|
|
| |
| switch (CURTYPE(p)) { |
| case TOK_PARALLEL_FOR: return parse_parallel_for(p); |
| case TOK_PARALLEL_REDUCE: return parse_parallel_reduce(p); |
| case TOK_PARALLEL_SCAN: return parse_parallel_scan(p); |
| case TOK_PARALLEL_MAP: return parse_parallel_map(p); |
| case TOK_BARRIER: return parse_barrier(p); |
| case TOK_TILE_HINT: return parse_tile_hint(p); |
| case TOK_SIMD_HINT: return parse_simd_hint(p); |
| case TOK_MEMORY_LAYOUT: return parse_memory_layout(p); |
| default: break; |
| } |
|
|
| |
| if (is_type_token(p)) { |
| ASTType *type = parse_type(p); |
|
|
| |
| if (check(p, TOK_SEMICOLON)) { |
| eat(p); |
| ASTNode *n = ast_new(p->arena, NODE_VAR_DECL, LOC(p)); |
| n->var.var_type = type; |
| n->var.name = ""; |
| return n; |
| } |
|
|
| if (!check(p, TOK_IDENT)) { |
| |
| return parse_stmt(p); |
| } |
|
|
| Token name = eat(p); |
|
|
| |
| if (check(p, TOK_LPAREN)) { |
| |
| |
| return parse_func_decl(p, type, name.text); |
| } |
|
|
| |
| |
| ASTNode *first = ast_new(p->arena, NODE_VAR_DECL, LOC(p)); |
| first->var.name = name.text; |
| first->var.var_type = type; |
|
|
| |
| while (match_tok(p, TOK_LBRACKET)) { |
| if (check(p, TOK_RBRACKET)) { |
| type = ast_array_type(p->arena, type, -1); |
| } else { |
| ASTNode *sz = parse_expr(p); |
| int arr_sz = -1; |
| if (sz->type == NODE_INT_LIT) arr_sz = (int)sz->int_lit.value; |
| type = ast_array_type(p->arena, type, arr_sz); |
| } |
| expect(p, TOK_RBRACKET); |
| first->var.var_type = type; |
| } |
|
|
| if (match_tok(p, TOK_ASSIGN)) |
| first->var.init = parse_assign_expr(p); |
|
|
| |
| |
| expect(p, TOK_SEMICOLON); |
| return first; |
| } |
|
|
| return parse_stmt(p); |
| } |
|
|
| |
|
|
| Parser *parser_create(Arena *arena, Token *tokens, size_t count, const char *filename) { |
| Parser *p = (Parser *)arena_alloc(arena, sizeof(Parser)); |
| p->tokens = tokens; |
| p->count = count; |
| p->pos = 0; |
| p->arena = arena; |
| p->error_count = 0; |
| p->filename = filename; |
| return p; |
| } |
|
|
| ASTNode *parser_parse(Parser *p) { |
| ASTNode *prog = ast_new(p->arena, NODE_PROGRAM, LOC(p)); |
| vec_init(&prog->program.decls); |
|
|
| while (!IS(p, TOK_EOF)) { |
| ASTNode *decl = parse_decl_or_stmt(p); |
| if (decl) vec_push(&prog->program.decls, decl); |
| if (p->error_count > 20) { |
| p2s_fatal("too many errors, aborting"); |
| break; |
| } |
| } |
| return prog; |
| } |
|
|