mirror of
https://github.com/ziglang/zig.git
synced 2025-12-24 07:03:11 +00:00
support if conditionals
This commit is contained in:
parent
1ed926c321
commit
08a2311efd
22
README.md
22
README.md
@ -44,7 +44,11 @@ make
|
|||||||
|
|
||||||
* variable declarations and assignment expressions
|
* variable declarations and assignment expressions
|
||||||
* Type checking
|
* Type checking
|
||||||
|
* loops
|
||||||
|
* labels and goto
|
||||||
* inline assembly and syscalls
|
* inline assembly and syscalls
|
||||||
|
* conditional compilation and ability to check target platform and architecture
|
||||||
|
* main function with command line arguments
|
||||||
* running code at compile time
|
* running code at compile time
|
||||||
* print! macro that takes var args
|
* print! macro that takes var args
|
||||||
* panic! macro that prints a stack trace to stderr in debug mode and calls
|
* panic! macro that prints a stack trace to stderr in debug mode and calls
|
||||||
@ -104,14 +108,26 @@ Type : token(Symbol) | PointerType | token(Unreachable)
|
|||||||
|
|
||||||
PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type
|
PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type
|
||||||
|
|
||||||
Block : token(LBrace) list(option(Expression), token(Semicolon)) token(RBrace)
|
Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
|
||||||
|
|
||||||
Expression : BoolOrExpression | ReturnExpression
|
Statement : NonBlockExpression token(Semicolon) | BlockExpression
|
||||||
|
|
||||||
|
Expression : BlockExpression | NonBlockExpression
|
||||||
|
|
||||||
|
NonBlockExpression : BoolOrExpression | ReturnExpression
|
||||||
|
|
||||||
|
BlockExpression : IfExpression | Block
|
||||||
|
|
||||||
BoolOrExpression : BoolAndExpression token(BoolOr) BoolAndExpression | BoolAndExpression
|
BoolOrExpression : BoolAndExpression token(BoolOr) BoolAndExpression | BoolAndExpression
|
||||||
|
|
||||||
ReturnExpression : token(Return) option(Expression)
|
ReturnExpression : token(Return) option(Expression)
|
||||||
|
|
||||||
|
IfExpression : token(If) Expression Block option(Else | ElseIf)
|
||||||
|
|
||||||
|
ElseIf : token(Else) IfExpression
|
||||||
|
|
||||||
|
Else : token(Else) Block
|
||||||
|
|
||||||
BoolAndExpression : ComparisonExpression token(BoolAnd) ComparisonExpression | ComparisonExpression
|
BoolAndExpression : ComparisonExpression token(BoolAnd) ComparisonExpression | ComparisonExpression
|
||||||
|
|
||||||
ComparisonExpression : BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression
|
ComparisonExpression : BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression
|
||||||
@ -144,7 +160,7 @@ FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma)
|
|||||||
|
|
||||||
PrefixOp : token(Not) | token(Dash) | token(Tilde)
|
PrefixOp : token(Not) | token(Dash) | token(Tilde)
|
||||||
|
|
||||||
PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | Block | token(Symbol)
|
PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol)
|
||||||
|
|
||||||
GroupedExpression : token(LParen) Expression token(RParen)
|
GroupedExpression : token(LParen) Expression token(RParen)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -7,8 +7,8 @@ if exists("b:current_syntax")
|
|||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
|
|
||||||
syn keyword zigKeyword fn return mut const extern unreachable export pub as use
|
syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void
|
||||||
syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 void
|
syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128
|
||||||
|
|
||||||
syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
|
syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
|
||||||
syn region zigCommentLineDoc start="//\%(//\@!\|!\)" end="$" contains=zigTodo,@Spell
|
syn region zigCommentLineDoc start="//\%(//\@!\|!\)" end="$" contains=zigTodo,@Spell
|
||||||
|
|||||||
112
src/analyze.cpp
112
src/analyze.cpp
@ -274,6 +274,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
|||||||
case NodeTypeSymbol:
|
case NodeTypeSymbol:
|
||||||
case NodeTypeCastExpr:
|
case NodeTypeCastExpr:
|
||||||
case NodeTypePrefixOpExpr:
|
case NodeTypePrefixOpExpr:
|
||||||
|
case NodeTypeIfExpr:
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,7 +303,9 @@ static void check_type_compatibility(CodeGen *g, AstNode *node, TypeTableEntry *
|
|||||||
add_node_error(g, node, buf_sprintf("type mismatch. expected %s. got %s", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
|
add_node_error(g, node, buf_sprintf("type mismatch. expected %s. got %s", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) {
|
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||||
|
TypeTableEntry *expected_type, AstNode *node)
|
||||||
|
{
|
||||||
TypeTableEntry *return_type = nullptr;
|
TypeTableEntry *return_type = nullptr;
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case NodeTypeBlock:
|
case NodeTypeBlock:
|
||||||
@ -348,10 +351,64 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
|||||||
|
|
||||||
case NodeTypeBinOpExpr:
|
case NodeTypeBinOpExpr:
|
||||||
{
|
{
|
||||||
// TODO: think about expected types
|
switch (node->data.bin_op_expr.bin_op) {
|
||||||
analyze_expression(g, import, context, expected_type, node->data.bin_op_expr.op1);
|
case BinOpTypeBoolOr:
|
||||||
analyze_expression(g, import, context, expected_type, node->data.bin_op_expr.op2);
|
case BinOpTypeBoolAnd:
|
||||||
return_type = expected_type;
|
analyze_expression(g, import, context, g->builtin_types.entry_bool,
|
||||||
|
node->data.bin_op_expr.op1);
|
||||||
|
analyze_expression(g, import, context, g->builtin_types.entry_bool,
|
||||||
|
node->data.bin_op_expr.op2);
|
||||||
|
return_type = g->builtin_types.entry_bool;
|
||||||
|
break;
|
||||||
|
case BinOpTypeCmpEq:
|
||||||
|
case BinOpTypeCmpNotEq:
|
||||||
|
case BinOpTypeCmpLessThan:
|
||||||
|
case BinOpTypeCmpGreaterThan:
|
||||||
|
case BinOpTypeCmpLessOrEq:
|
||||||
|
case BinOpTypeCmpGreaterOrEq:
|
||||||
|
// TODO think how should type checking for these work?
|
||||||
|
analyze_expression(g, import, context, g->builtin_types.entry_i32,
|
||||||
|
node->data.bin_op_expr.op1);
|
||||||
|
analyze_expression(g, import, context, g->builtin_types.entry_i32,
|
||||||
|
node->data.bin_op_expr.op2);
|
||||||
|
return_type = g->builtin_types.entry_bool;
|
||||||
|
break;
|
||||||
|
case BinOpTypeBinOr:
|
||||||
|
zig_panic("TODO bin or type");
|
||||||
|
break;
|
||||||
|
case BinOpTypeBinXor:
|
||||||
|
zig_panic("TODO bin xor type");
|
||||||
|
break;
|
||||||
|
case BinOpTypeBinAnd:
|
||||||
|
zig_panic("TODO bin and type");
|
||||||
|
break;
|
||||||
|
case BinOpTypeBitShiftLeft:
|
||||||
|
zig_panic("TODO bit shift left type");
|
||||||
|
break;
|
||||||
|
case BinOpTypeBitShiftRight:
|
||||||
|
zig_panic("TODO bit shift right type");
|
||||||
|
break;
|
||||||
|
case BinOpTypeAdd:
|
||||||
|
case BinOpTypeSub:
|
||||||
|
// TODO think how should type checking for these work?
|
||||||
|
analyze_expression(g, import, context, g->builtin_types.entry_i32,
|
||||||
|
node->data.bin_op_expr.op1);
|
||||||
|
analyze_expression(g, import, context, g->builtin_types.entry_i32,
|
||||||
|
node->data.bin_op_expr.op2);
|
||||||
|
return_type = g->builtin_types.entry_i32;
|
||||||
|
break;
|
||||||
|
case BinOpTypeMult:
|
||||||
|
zig_panic("TODO mult type");
|
||||||
|
break;
|
||||||
|
case BinOpTypeDiv:
|
||||||
|
zig_panic("TODO div type");
|
||||||
|
break;
|
||||||
|
case BinOpTypeMod:
|
||||||
|
zig_panic("TODO modulus type");
|
||||||
|
break;
|
||||||
|
case BinOpTypeInvalid:
|
||||||
|
zig_unreachable();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,11 +483,46 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
|||||||
|
|
||||||
case NodeTypeSymbol:
|
case NodeTypeSymbol:
|
||||||
// look up symbol in symbol table
|
// look up symbol in symbol table
|
||||||
zig_panic("TODO");
|
zig_panic("TODO analyze_expression symbol");
|
||||||
|
|
||||||
case NodeTypeCastExpr:
|
case NodeTypeCastExpr:
|
||||||
|
zig_panic("TODO analyze_expression cast expr");
|
||||||
|
break;
|
||||||
|
|
||||||
case NodeTypePrefixOpExpr:
|
case NodeTypePrefixOpExpr:
|
||||||
zig_panic("TODO");
|
switch (node->data.prefix_op_expr.prefix_op) {
|
||||||
|
case PrefixOpBoolNot:
|
||||||
|
analyze_expression(g, import, context, g->builtin_types.entry_bool,
|
||||||
|
node->data.prefix_op_expr.primary_expr);
|
||||||
|
return_type = g->builtin_types.entry_bool;
|
||||||
|
break;
|
||||||
|
case PrefixOpBinNot:
|
||||||
|
zig_panic("TODO type check bin not");
|
||||||
|
break;
|
||||||
|
case PrefixOpNegation:
|
||||||
|
zig_panic("TODO type check negation");
|
||||||
|
break;
|
||||||
|
case PrefixOpInvalid:
|
||||||
|
zig_unreachable();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NodeTypeIfExpr:
|
||||||
|
{
|
||||||
|
analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_expr.condition);
|
||||||
|
|
||||||
|
TypeTableEntry *else_type;
|
||||||
|
if (node->data.if_expr.else_node) {
|
||||||
|
else_type = analyze_expression(g, import, context, expected_type, node->data.if_expr.else_node);
|
||||||
|
} else {
|
||||||
|
else_type = g->builtin_types.entry_void;
|
||||||
|
}
|
||||||
|
TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type,
|
||||||
|
node->data.if_expr.then_block);
|
||||||
|
|
||||||
|
check_type_compatibility(g, node, expected_type, else_type);
|
||||||
|
return_type = then_type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case NodeTypeDirective:
|
case NodeTypeDirective:
|
||||||
case NodeTypeFnDecl:
|
case NodeTypeFnDecl:
|
||||||
case NodeTypeFnProto:
|
case NodeTypeFnProto:
|
||||||
@ -445,6 +537,11 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
|||||||
}
|
}
|
||||||
assert(return_type);
|
assert(return_type);
|
||||||
check_type_compatibility(g, node, expected_type, return_type);
|
check_type_compatibility(g, node, expected_type, return_type);
|
||||||
|
|
||||||
|
assert(!node->codegen_node);
|
||||||
|
node->codegen_node = allocate<CodeGenNode>(1);
|
||||||
|
node->codegen_node->data.expr_node.type_entry = return_type;
|
||||||
|
|
||||||
return return_type;
|
return return_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,6 +606,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
|
|||||||
case NodeTypeSymbol:
|
case NodeTypeSymbol:
|
||||||
case NodeTypeCastExpr:
|
case NodeTypeCastExpr:
|
||||||
case NodeTypePrefixOpExpr:
|
case NodeTypePrefixOpExpr:
|
||||||
|
case NodeTypeIfExpr:
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
143
src/codegen.cpp
143
src/codegen.cpp
@ -120,6 +120,10 @@ static LLVMValueRef get_variable_value(CodeGen *g, Buf *name) {
|
|||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TypeTableEntry *get_expr_type(AstNode *node) {
|
||||||
|
return node->codegen_node->data.expr_node.type_entry;
|
||||||
|
}
|
||||||
|
|
||||||
static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
|
static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
|
||||||
assert(node->type == NodeTypeFnCallExpr);
|
assert(node->type == NodeTypeFnCallExpr);
|
||||||
|
|
||||||
@ -283,6 +287,7 @@ static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
|
|||||||
|
|
||||||
LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1);
|
LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1);
|
||||||
|
|
||||||
|
LLVMBasicBlockRef orig_block = LLVMGetInsertBlock(g->builder);
|
||||||
// block for when val1 == true
|
// block for when val1 == true
|
||||||
LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndTrue");
|
LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndTrue");
|
||||||
// block for when val1 == false (don't even evaluate the second part)
|
// block for when val1 == false (don't even evaluate the second part)
|
||||||
@ -297,13 +302,14 @@ static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
|
|||||||
LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
|
LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
|
||||||
add_debug_source_node(g, node);
|
add_debug_source_node(g, node);
|
||||||
LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
|
LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
|
||||||
|
LLVMBuildBr(g->builder, false_block);
|
||||||
|
|
||||||
LLVMPositionBuilderAtEnd(g->builder, false_block);
|
LLVMPositionBuilderAtEnd(g->builder, false_block);
|
||||||
add_debug_source_node(g, node);
|
add_debug_source_node(g, node);
|
||||||
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
|
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
|
||||||
LLVMValueRef one_i1 = LLVMConstAllOnes(LLVMInt1Type());
|
LLVMValueRef one_i1 = LLVMConstAllOnes(LLVMInt1Type());
|
||||||
LLVMValueRef incoming_values[2] = {one_i1, val2_i1};
|
LLVMValueRef incoming_values[2] = {one_i1, val2_i1};
|
||||||
LLVMBasicBlockRef incoming_blocks[2] = {LLVMGetInsertBlock(g->builder), true_block};
|
LLVMBasicBlockRef incoming_blocks[2] = {orig_block, true_block};
|
||||||
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
|
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
|
||||||
|
|
||||||
return phi;
|
return phi;
|
||||||
@ -314,6 +320,8 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
|
|||||||
|
|
||||||
LLVMValueRef val1 = gen_expr(g, expr_node->data.bin_op_expr.op1);
|
LLVMValueRef val1 = gen_expr(g, expr_node->data.bin_op_expr.op1);
|
||||||
|
|
||||||
|
LLVMBasicBlockRef orig_block = LLVMGetInsertBlock(g->builder);
|
||||||
|
|
||||||
// block for when val1 == false
|
// block for when val1 == false
|
||||||
LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrFalse");
|
LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrFalse");
|
||||||
// block for when val1 == true (don't even evaluate the second part)
|
// block for when val1 == true (don't even evaluate the second part)
|
||||||
@ -328,13 +336,14 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
|
|||||||
LLVMValueRef val2 = gen_expr(g, expr_node->data.bin_op_expr.op2);
|
LLVMValueRef val2 = gen_expr(g, expr_node->data.bin_op_expr.op2);
|
||||||
add_debug_source_node(g, expr_node);
|
add_debug_source_node(g, expr_node);
|
||||||
LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
|
LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
|
||||||
|
LLVMBuildBr(g->builder, true_block);
|
||||||
|
|
||||||
LLVMPositionBuilderAtEnd(g->builder, true_block);
|
LLVMPositionBuilderAtEnd(g->builder, true_block);
|
||||||
add_debug_source_node(g, expr_node);
|
add_debug_source_node(g, expr_node);
|
||||||
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
|
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
|
||||||
LLVMValueRef one_i1 = LLVMConstAllOnes(LLVMInt1Type());
|
LLVMValueRef one_i1 = LLVMConstAllOnes(LLVMInt1Type());
|
||||||
LLVMValueRef incoming_values[2] = {one_i1, val2_i1};
|
LLVMValueRef incoming_values[2] = {one_i1, val2_i1};
|
||||||
LLVMBasicBlockRef incoming_blocks[2] = {LLVMGetInsertBlock(g->builder), false_block};
|
LLVMBasicBlockRef incoming_blocks[2] = {orig_block, false_block};
|
||||||
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
|
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
|
||||||
|
|
||||||
return phi;
|
return phi;
|
||||||
@ -383,9 +392,91 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
|
|||||||
return LLVMBuildRetVoid(g->builder);
|
return LLVMBuildRetVoid(g->builder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
Expression : BoolOrExpression | ReturnExpression
|
static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {
|
||||||
*/
|
assert(node->type == NodeTypeIfExpr);
|
||||||
|
assert(node->data.if_expr.condition);
|
||||||
|
assert(node->data.if_expr.then_block);
|
||||||
|
|
||||||
|
LLVMValueRef cond_value = gen_expr(g, node->data.if_expr.condition);
|
||||||
|
|
||||||
|
TypeTableEntry *then_type = get_expr_type(node->data.if_expr.then_block);
|
||||||
|
bool use_expr_value = (then_type != g->builtin_types.entry_unreachable &&
|
||||||
|
then_type != g->builtin_types.entry_void);
|
||||||
|
|
||||||
|
if (node->data.if_expr.else_node) {
|
||||||
|
LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
|
||||||
|
LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
|
||||||
|
LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
|
||||||
|
|
||||||
|
LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(g->builder, then_block);
|
||||||
|
LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block);
|
||||||
|
LLVMBuildBr(g->builder, endif_block);
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(g->builder, else_block);
|
||||||
|
LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node);
|
||||||
|
LLVMBuildBr(g->builder, endif_block);
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(g->builder, endif_block);
|
||||||
|
if (use_expr_value) {
|
||||||
|
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
|
||||||
|
LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
|
||||||
|
LLVMBasicBlockRef incoming_blocks[2] = {then_block, else_block};
|
||||||
|
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
|
||||||
|
|
||||||
|
return phi;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!use_expr_value);
|
||||||
|
|
||||||
|
LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
|
||||||
|
LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
|
||||||
|
|
||||||
|
LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block);
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(g->builder, then_block);
|
||||||
|
gen_expr(g, node->data.if_expr.then_block);
|
||||||
|
LLVMBuildBr(g->builder, endif_block);
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(g->builder, endif_block);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
|
||||||
|
assert(block_node->type == NodeTypeBlock);
|
||||||
|
|
||||||
|
ImportTableEntry *import = g->cur_fn->import_entry;
|
||||||
|
|
||||||
|
LLVMZigDILexicalBlock *di_block = LLVMZigCreateLexicalBlock(g->dbuilder, g->block_scopes.last(),
|
||||||
|
import->di_file, block_node->line + 1, block_node->column + 1);
|
||||||
|
g->block_scopes.append(LLVMZigLexicalBlockToScope(di_block));
|
||||||
|
|
||||||
|
add_debug_source_node(g, block_node);
|
||||||
|
|
||||||
|
LLVMValueRef return_value;
|
||||||
|
for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
|
||||||
|
AstNode *statement_node = block_node->data.block.statements.at(i);
|
||||||
|
return_value = gen_expr(g, statement_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (implicit_return_type) {
|
||||||
|
if (implicit_return_type == g->builtin_types.entry_void) {
|
||||||
|
LLVMBuildRetVoid(g->builder);
|
||||||
|
} else if (implicit_return_type != g->builtin_types.entry_unreachable) {
|
||||||
|
LLVMBuildRet(g->builder, return_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g->block_scopes.pop();
|
||||||
|
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case NodeTypeBinOpExpr:
|
case NodeTypeBinOpExpr:
|
||||||
@ -403,6 +494,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
|||||||
return LLVMBuildUnreachable(g->builder);
|
return LLVMBuildUnreachable(g->builder);
|
||||||
case NodeTypeVoid:
|
case NodeTypeVoid:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
case NodeTypeIfExpr:
|
||||||
|
return gen_if_expr(g, node);
|
||||||
case NodeTypeNumberLiteral:
|
case NodeTypeNumberLiteral:
|
||||||
{
|
{
|
||||||
Buf *number_str = &node->data.number;
|
Buf *number_str = &node->data.number;
|
||||||
@ -427,6 +520,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
|||||||
Buf *name = &node->data.symbol;
|
Buf *name = &node->data.symbol;
|
||||||
return get_variable_value(g, name);
|
return get_variable_value(g, name);
|
||||||
}
|
}
|
||||||
|
case NodeTypeBlock:
|
||||||
|
return gen_block(g, node, nullptr);
|
||||||
case NodeTypeRoot:
|
case NodeTypeRoot:
|
||||||
case NodeTypeRootExportDecl:
|
case NodeTypeRootExportDecl:
|
||||||
case NodeTypeFnProto:
|
case NodeTypeFnProto:
|
||||||
@ -434,7 +529,6 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
|||||||
case NodeTypeFnDecl:
|
case NodeTypeFnDecl:
|
||||||
case NodeTypeParamDecl:
|
case NodeTypeParamDecl:
|
||||||
case NodeTypeType:
|
case NodeTypeType:
|
||||||
case NodeTypeBlock:
|
|
||||||
case NodeTypeExternBlock:
|
case NodeTypeExternBlock:
|
||||||
case NodeTypeDirective:
|
case NodeTypeDirective:
|
||||||
case NodeTypeUse:
|
case NodeTypeUse:
|
||||||
@ -443,30 +537,6 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
|||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_block(CodeGen *g, ImportTableEntry *import, AstNode *block_node, TypeTableEntry *implicit_return_type) {
|
|
||||||
assert(block_node->type == NodeTypeBlock);
|
|
||||||
|
|
||||||
LLVMZigDILexicalBlock *di_block = LLVMZigCreateLexicalBlock(g->dbuilder, g->block_scopes.last(),
|
|
||||||
import->di_file, block_node->line + 1, block_node->column + 1);
|
|
||||||
g->block_scopes.append(LLVMZigLexicalBlockToScope(di_block));
|
|
||||||
|
|
||||||
add_debug_source_node(g, block_node);
|
|
||||||
|
|
||||||
LLVMValueRef return_value;
|
|
||||||
for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
|
|
||||||
AstNode *statement_node = block_node->data.block.statements.at(i);
|
|
||||||
return_value = gen_expr(g, statement_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (implicit_return_type == g->builtin_types.entry_void) {
|
|
||||||
LLVMBuildRetVoid(g->builder);
|
|
||||||
} else if (implicit_return_type != g->builtin_types.entry_unreachable) {
|
|
||||||
LLVMBuildRet(g->builder, return_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
g->block_scopes.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnProto *fn_proto,
|
static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnProto *fn_proto,
|
||||||
LLVMZigDIFile *di_file)
|
LLVMZigDIFile *di_file)
|
||||||
{
|
{
|
||||||
@ -558,7 +628,7 @@ static void do_code_gen(CodeGen *g) {
|
|||||||
LLVMGetParams(fn, codegen_fn_def->params);
|
LLVMGetParams(fn, codegen_fn_def->params);
|
||||||
|
|
||||||
TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;
|
TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;
|
||||||
gen_block(g, import, fn_def_node->data.fn_def.body, implicit_return_type);
|
gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type);
|
||||||
|
|
||||||
g->block_scopes.pop();
|
g->block_scopes.pop();
|
||||||
}
|
}
|
||||||
@ -585,6 +655,15 @@ static void define_primitive_types(CodeGen *g) {
|
|||||||
buf_init_from_str(&entry->name, "(invalid)");
|
buf_init_from_str(&entry->name, "(invalid)");
|
||||||
g->builtin_types.entry_invalid = entry;
|
g->builtin_types.entry_invalid = entry;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
|
||||||
|
entry->type_ref = LLVMInt1Type();
|
||||||
|
buf_init_from_str(&entry->name, "bool");
|
||||||
|
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 1, 8,
|
||||||
|
LLVMZigEncoding_DW_ATE_unsigned());
|
||||||
|
g->type_table.put(&entry->name, entry);
|
||||||
|
g->builtin_types.entry_bool = entry;
|
||||||
|
}
|
||||||
{
|
{
|
||||||
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
|
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
|
||||||
entry->type_ref = LLVMInt8Type();
|
entry->type_ref = LLVMInt8Type();
|
||||||
@ -803,7 +882,7 @@ static Buf *to_c_type(CodeGen *g, AstNode *type_node) {
|
|||||||
g->c_stdint_used = true;
|
g->c_stdint_used = true;
|
||||||
return buf_create_from_str("int32_t");
|
return buf_create_from_str("int32_t");
|
||||||
} else {
|
} else {
|
||||||
zig_panic("TODO");
|
zig_panic("TODO to_c_type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
135
src/parser.cpp
135
src/parser.cpp
@ -91,6 +91,8 @@ const char *node_type_str(NodeType node_type) {
|
|||||||
return "Use";
|
return "Use";
|
||||||
case NodeTypeVoid:
|
case NodeTypeVoid:
|
||||||
return "Void";
|
return "Void";
|
||||||
|
case NodeTypeIfExpr:
|
||||||
|
return "IfExpr";
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
@ -236,7 +238,15 @@ void ast_print(AstNode *node, int indent) {
|
|||||||
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.use.path));
|
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.use.path));
|
||||||
break;
|
break;
|
||||||
case NodeTypeVoid:
|
case NodeTypeVoid:
|
||||||
fprintf(stderr, "Void\n");
|
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||||
|
break;
|
||||||
|
case NodeTypeIfExpr:
|
||||||
|
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||||
|
if (node->data.if_expr.condition)
|
||||||
|
ast_print(node->data.if_expr.condition, indent + 2);
|
||||||
|
ast_print(node->data.if_expr.then_block, indent + 2);
|
||||||
|
if (node->data.if_expr.else_node)
|
||||||
|
ast_print(node->data.if_expr.else_node, indent + 2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,6 +363,7 @@ static void ast_invalid_token_error(ParseContext *pc, Token *token) {
|
|||||||
|
|
||||||
static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory);
|
static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory);
|
||||||
static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory);
|
static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory);
|
||||||
|
static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory);
|
||||||
|
|
||||||
|
|
||||||
static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
|
static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
|
||||||
@ -558,7 +569,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | Block | token(Symbol)
|
PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol)
|
||||||
*/
|
*/
|
||||||
static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||||
Token *token = &pc->tokens->at(*token_index);
|
Token *token = &pc->tokens->at(*token_index);
|
||||||
@ -588,11 +599,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode *block_node = ast_parse_block(pc, token_index, false);
|
|
||||||
if (block_node) {
|
|
||||||
return block_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
|
AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
|
||||||
if (grouped_expr_node) {
|
if (grouped_expr_node) {
|
||||||
return grouped_expr_node;
|
return grouped_expr_node;
|
||||||
@ -975,6 +981,50 @@ static AstNode *ast_parse_bool_and_expr(ParseContext *pc, int *token_index, bool
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ElseIf : token(Else) IfExpression
|
||||||
|
Else : token(Else) Block
|
||||||
|
*/
|
||||||
|
static AstNode *ast_parse_else_or_else_if(ParseContext *pc, int *token_index, bool mandatory) {
|
||||||
|
Token *else_token = &pc->tokens->at(*token_index);
|
||||||
|
|
||||||
|
if (else_token->id != TokenIdKeywordElse) {
|
||||||
|
if (mandatory) {
|
||||||
|
ast_invalid_token_error(pc, else_token);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*token_index += 1;
|
||||||
|
|
||||||
|
AstNode *if_expr = ast_parse_if_expr(pc, token_index, false);
|
||||||
|
if (if_expr)
|
||||||
|
return if_expr;
|
||||||
|
|
||||||
|
return ast_parse_block(pc, token_index, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IfExpression : token(If) Expression Block option(Else | ElseIf)
|
||||||
|
*/
|
||||||
|
static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||||
|
Token *if_tok = &pc->tokens->at(*token_index);
|
||||||
|
if (if_tok->id != TokenIdKeywordIf) {
|
||||||
|
if (mandatory) {
|
||||||
|
ast_invalid_token_error(pc, if_tok);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*token_index += 1;
|
||||||
|
|
||||||
|
AstNode *node = ast_create_node(pc, NodeTypeIfExpr, if_tok);
|
||||||
|
node->data.if_expr.condition = ast_parse_expression(pc, token_index, true);
|
||||||
|
node->data.if_expr.then_block = ast_parse_block(pc, token_index, true);
|
||||||
|
node->data.if_expr.else_node = ast_parse_else_or_else_if(pc, token_index, false);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ReturnExpression : token(Return) option(Expression)
|
ReturnExpression : token(Return) option(Expression)
|
||||||
*/
|
*/
|
||||||
@ -1016,27 +1066,68 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, int *token_index, bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Expression : BoolOrExpression | ReturnExpression
|
BlockExpression : IfExpression | Block
|
||||||
*/
|
*/
|
||||||
static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory) {
|
static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||||
Token *token = &pc->tokens->at(*token_index);
|
Token *token = &pc->tokens->at(*token_index);
|
||||||
|
|
||||||
AstNode *return_expr = ast_parse_return_expr(pc, token_index, false);
|
AstNode *if_expr = ast_parse_if_expr(pc, token_index, false);
|
||||||
if (return_expr)
|
if (if_expr)
|
||||||
return return_expr;
|
return if_expr;
|
||||||
|
|
||||||
|
AstNode *block = ast_parse_block(pc, token_index, false);
|
||||||
|
if (block)
|
||||||
|
return block;
|
||||||
|
|
||||||
|
if (mandatory)
|
||||||
|
ast_invalid_token_error(pc, token);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
NonBlockExpression : BoolOrExpression | ReturnExpression
|
||||||
|
*/
|
||||||
|
static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||||
|
Token *token = &pc->tokens->at(*token_index);
|
||||||
|
|
||||||
AstNode *bool_or_expr = ast_parse_bool_or_expr(pc, token_index, false);
|
AstNode *bool_or_expr = ast_parse_bool_or_expr(pc, token_index, false);
|
||||||
if (bool_or_expr)
|
if (bool_or_expr)
|
||||||
return bool_or_expr;
|
return bool_or_expr;
|
||||||
|
|
||||||
if (!mandatory)
|
AstNode *return_expr = ast_parse_return_expr(pc, token_index, false);
|
||||||
return nullptr;
|
if (return_expr)
|
||||||
|
return return_expr;
|
||||||
|
|
||||||
|
if (mandatory)
|
||||||
ast_invalid_token_error(pc, token);
|
ast_invalid_token_error(pc, token);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Block : token(LBrace) list(option(Expression), token(Semicolon)) token(RBrace)
|
Expression : BlockExpression | NonBlockExpression
|
||||||
|
*/
|
||||||
|
static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory) {
|
||||||
|
Token *token = &pc->tokens->at(*token_index);
|
||||||
|
|
||||||
|
AstNode *block_expr = ast_parse_block_expr(pc, token_index, false);
|
||||||
|
if (block_expr)
|
||||||
|
return block_expr;
|
||||||
|
|
||||||
|
AstNode *non_block_expr = ast_parse_non_block_expr(pc, token_index, false);
|
||||||
|
if (non_block_expr)
|
||||||
|
return non_block_expr;
|
||||||
|
|
||||||
|
if (mandatory)
|
||||||
|
ast_invalid_token_error(pc, token);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Statement : NonBlockExpression token(Semicolon) | BlockExpression
|
||||||
|
Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
|
||||||
*/
|
*/
|
||||||
static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) {
|
static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) {
|
||||||
Token *last_token = &pc->tokens->at(*token_index);
|
Token *last_token = &pc->tokens->at(*token_index);
|
||||||
@ -1058,16 +1149,22 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato
|
|||||||
// {2;} -> {2;void}
|
// {2;} -> {2;void}
|
||||||
// {;2} -> {void;2}
|
// {;2} -> {void;2}
|
||||||
for (;;) {
|
for (;;) {
|
||||||
AstNode *expression_node = ast_parse_expression(pc, token_index, false);
|
AstNode *statement_node = ast_parse_block_expr(pc, token_index, false);
|
||||||
if (!expression_node) {
|
bool semicolon_expected = !statement_node;
|
||||||
expression_node = ast_create_node(pc, NodeTypeVoid, last_token);
|
if (!statement_node) {
|
||||||
|
statement_node = ast_parse_non_block_expr(pc, token_index, false);
|
||||||
|
if (!statement_node) {
|
||||||
|
statement_node = ast_create_node(pc, NodeTypeVoid, last_token);
|
||||||
}
|
}
|
||||||
node->data.block.statements.append(expression_node);
|
}
|
||||||
|
node->data.block.statements.append(statement_node);
|
||||||
|
|
||||||
last_token = &pc->tokens->at(*token_index);
|
last_token = &pc->tokens->at(*token_index);
|
||||||
if (last_token->id == TokenIdRBrace) {
|
if (last_token->id == TokenIdRBrace) {
|
||||||
*token_index += 1;
|
*token_index += 1;
|
||||||
return node;
|
return node;
|
||||||
|
} else if (!semicolon_expected) {
|
||||||
|
continue;
|
||||||
} else if (last_token->id == TokenIdSemicolon) {
|
} else if (last_token->id == TokenIdSemicolon) {
|
||||||
*token_index += 1;
|
*token_index += 1;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -39,6 +39,7 @@ enum NodeType {
|
|||||||
NodeTypeFnCallExpr,
|
NodeTypeFnCallExpr,
|
||||||
NodeTypeUse,
|
NodeTypeUse,
|
||||||
NodeTypeVoid,
|
NodeTypeVoid,
|
||||||
|
NodeTypeIfExpr,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AstNodeRoot {
|
struct AstNodeRoot {
|
||||||
@ -167,6 +168,12 @@ struct AstNodeUse {
|
|||||||
ZigList<AstNode *> *directives;
|
ZigList<AstNode *> *directives;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AstNodeIfExpr {
|
||||||
|
AstNode *condition;
|
||||||
|
AstNode *then_block;
|
||||||
|
AstNode *else_node; // null, block node, or other if expr node
|
||||||
|
};
|
||||||
|
|
||||||
struct AstNode {
|
struct AstNode {
|
||||||
enum NodeType type;
|
enum NodeType type;
|
||||||
int line;
|
int line;
|
||||||
@ -190,6 +197,7 @@ struct AstNode {
|
|||||||
AstNodePrefixOpExpr prefix_op_expr;
|
AstNodePrefixOpExpr prefix_op_expr;
|
||||||
AstNodeFnCallExpr fn_call_expr;
|
AstNodeFnCallExpr fn_call_expr;
|
||||||
AstNodeUse use;
|
AstNodeUse use;
|
||||||
|
AstNodeIfExpr if_expr;
|
||||||
Buf number;
|
Buf number;
|
||||||
Buf string;
|
Buf string;
|
||||||
Buf symbol;
|
Buf symbol;
|
||||||
|
|||||||
@ -63,6 +63,7 @@ struct CodeGen {
|
|||||||
HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
|
HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
TypeTableEntry *entry_bool;
|
||||||
TypeTableEntry *entry_u8;
|
TypeTableEntry *entry_u8;
|
||||||
TypeTableEntry *entry_i32;
|
TypeTableEntry *entry_i32;
|
||||||
TypeTableEntry *entry_string_literal;
|
TypeTableEntry *entry_string_literal;
|
||||||
@ -111,10 +112,15 @@ struct FnDefNode {
|
|||||||
LLVMValueRef *params;
|
LLVMValueRef *params;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ExprNode {
|
||||||
|
TypeTableEntry *type_entry;
|
||||||
|
};
|
||||||
|
|
||||||
struct CodeGenNode {
|
struct CodeGenNode {
|
||||||
union {
|
union {
|
||||||
TypeNode type_node; // for NodeTypeType
|
TypeNode type_node; // for NodeTypeType
|
||||||
FnDefNode fn_def_node; // for NodeTypeFnDef
|
FnDefNode fn_def_node; // for NodeTypeFnDef
|
||||||
|
ExprNode expr_node; // for all the expression nodes
|
||||||
} data;
|
} data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -183,6 +183,10 @@ static void end_token(Tokenize *t) {
|
|||||||
t->cur_tok->id = TokenIdKeywordUse;
|
t->cur_tok->id = TokenIdKeywordUse;
|
||||||
} else if (mem_eql_str(token_mem, token_len, "void")) {
|
} else if (mem_eql_str(token_mem, token_len, "void")) {
|
||||||
t->cur_tok->id = TokenIdKeywordVoid;
|
t->cur_tok->id = TokenIdKeywordVoid;
|
||||||
|
} else if (mem_eql_str(token_mem, token_len, "if")) {
|
||||||
|
t->cur_tok->id = TokenIdKeywordIf;
|
||||||
|
} else if (mem_eql_str(token_mem, token_len, "else")) {
|
||||||
|
t->cur_tok->id = TokenIdKeywordElse;
|
||||||
}
|
}
|
||||||
|
|
||||||
t->cur_tok = nullptr;
|
t->cur_tok = nullptr;
|
||||||
@ -577,6 +581,8 @@ static const char * token_name(Token *token) {
|
|||||||
case TokenIdKeywordAs: return "As";
|
case TokenIdKeywordAs: return "As";
|
||||||
case TokenIdKeywordUse: return "Use";
|
case TokenIdKeywordUse: return "Use";
|
||||||
case TokenIdKeywordVoid: return "Void";
|
case TokenIdKeywordVoid: return "Void";
|
||||||
|
case TokenIdKeywordIf: return "If";
|
||||||
|
case TokenIdKeywordElse: return "Else";
|
||||||
case TokenIdLParen: return "LParen";
|
case TokenIdLParen: return "LParen";
|
||||||
case TokenIdRParen: return "RParen";
|
case TokenIdRParen: return "RParen";
|
||||||
case TokenIdComma: return "Comma";
|
case TokenIdComma: return "Comma";
|
||||||
|
|||||||
@ -24,6 +24,8 @@ enum TokenId {
|
|||||||
TokenIdKeywordAs,
|
TokenIdKeywordAs,
|
||||||
TokenIdKeywordUse,
|
TokenIdKeywordUse,
|
||||||
TokenIdKeywordVoid,
|
TokenIdKeywordVoid,
|
||||||
|
TokenIdKeywordIf,
|
||||||
|
TokenIdKeywordElse,
|
||||||
TokenIdLParen,
|
TokenIdLParen,
|
||||||
TokenIdRParen,
|
TokenIdRParen,
|
||||||
TokenIdComma,
|
TokenIdComma,
|
||||||
|
|||||||
@ -189,6 +189,30 @@ static void add_compiling_test_cases(void) {
|
|||||||
)SOURCE");
|
)SOURCE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add_simple_case("if statements", R"SOURCE(
|
||||||
|
#link("c")
|
||||||
|
extern {
|
||||||
|
fn puts(s: *const u8) -> i32;
|
||||||
|
fn exit(code: i32) -> unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn _start() -> unreachable {
|
||||||
|
if 1 != 0 {
|
||||||
|
puts("1 is true");
|
||||||
|
} else {
|
||||||
|
puts("1 is false");
|
||||||
|
}
|
||||||
|
if 0 != 0 {
|
||||||
|
puts("0 is true");
|
||||||
|
} else if 1 - 1 != 0 {
|
||||||
|
puts("1 - 1 is true");
|
||||||
|
}
|
||||||
|
if !(0 != 0) {
|
||||||
|
puts("!0 is true");
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
)SOURCE", "1 is true\n!0 is true\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_compile_failure_test_cases(void) {
|
static void add_compile_failure_test_cases(void) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user