parse if maybe expression

This commit is contained in:
Andrew Kelley 2015-12-26 15:05:27 -07:00
parent 5943f99510
commit 1f8e3871ee
9 changed files with 176 additions and 72 deletions

View File

@ -44,10 +44,11 @@ compromises backward compatibility.
### Current Status ### Current Status
* Core language features are lacking such as structs, enums, loops. * Have a look in the examples/ folder to see some code examples.
* Some language features available such as loops, inline assembly, expressions,
literals, functions.
* Only Linux x86_64 is supported. * Only Linux x86_64 is supported.
* Only building for the native target is supported. * Only building for the native target is supported.
* Have a look in the examples/ folder to see some code examples.
* Optimized machine code that Zig produces is indistinguishable from * Optimized machine code that Zig produces is indistinguishable from
optimized machine code produced from equivalent C program. optimized machine code produced from equivalent C program.
* Zig can generate dynamic libraries, executables, object files, and C * Zig can generate dynamic libraries, executables, object files, and C

View File

@ -102,7 +102,11 @@ BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | BoolAndExp
ReturnExpression : token(Return) option(Expression) ReturnExpression : token(Return) option(Expression)
IfExpression : token(If) Expression Block option(Else | ElseIf) IfExpression : IfVarExpression | IfBoolExpression
IfBoolExpression : token(If) option((token) Expression Block option(Else | ElseIf)
IfVarExpression : token(If) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(MaybeAssign) Expression Block Option(Else | ElseIf)
ElseIf : token(Else) IfExpression ElseIf : token(Else) IfExpression

View File

@ -12,17 +12,16 @@ fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
const answer = rand_int(&rand_state, 0, 100) + 1; const answer = rand_int(&rand_state, 0, 100) + 1;
while true { while true {
Buffer line = readline("\nGuess a number between 1 and 100: "); line = readline("\nGuess a number between 1 and 100: ");
(const err, const guess) = parse_number(line); if const guess ?= parse_number(line) {
if err == Error.None { if (guess > answer) {
if guess == answer { print_str("Guess lower.\n");
print_str("You win!\n"); } else if (guess < answer) {
return 0;
} else if guess < answer {
print_str("Guess higher.\n"); print_str("Guess higher.\n");
} else { } else {
print_str("Guess lower.\n"); print_str("You win!\n");
return 0;
} }
} else { } else {
print_str("Invalid number format.\n"); print_str("Invalid number format.\n");

View File

@ -45,7 +45,8 @@ static AstNode *first_executing_node(AstNode *node) {
case NodeTypeUse: case NodeTypeUse:
case NodeTypeVoid: case NodeTypeVoid:
case NodeTypeBoolLiteral: case NodeTypeBoolLiteral:
case NodeTypeIfExpr: case NodeTypeIfBoolExpr:
case NodeTypeIfVarExpr:
case NodeTypeLabel: case NodeTypeLabel:
case NodeTypeGoto: case NodeTypeGoto:
case NodeTypeBreak: case NodeTypeBreak:
@ -528,7 +529,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
case NodeTypeSymbol: case NodeTypeSymbol:
case NodeTypeCastExpr: case NodeTypeCastExpr:
case NodeTypePrefixOpExpr: case NodeTypePrefixOpExpr:
case NodeTypeIfExpr: case NodeTypeIfBoolExpr:
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr: case NodeTypeWhileExpr:
case NodeTypeLabel: case NodeTypeLabel:
case NodeTypeGoto: case NodeTypeGoto:
@ -598,7 +600,8 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) {
case NodeTypeSymbol: case NodeTypeSymbol:
case NodeTypeCastExpr: case NodeTypeCastExpr:
case NodeTypePrefixOpExpr: case NodeTypePrefixOpExpr:
case NodeTypeIfExpr: case NodeTypeIfBoolExpr:
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr: case NodeTypeWhileExpr:
case NodeTypeLabel: case NodeTypeLabel:
case NodeTypeGoto: case NodeTypeGoto:
@ -1394,6 +1397,39 @@ static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *impor
return g->builtin_types.entry_unreachable; return g->builtin_types.entry_unreachable;
} }
static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_bool_expr.condition);
TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type,
node->data.if_bool_expr.then_block);
TypeTableEntry *else_type;
if (node->data.if_bool_expr.else_node) {
else_type = analyze_expression(g, import, context, expected_type, node->data.if_bool_expr.else_node);
} else {
else_type = g->builtin_types.entry_void;
else_type = resolve_type_compatibility(g, context, node, expected_type, else_type);
}
if (expected_type) {
return (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
} else {
return resolve_peer_type_compatibility(g, context, node,
node->data.if_bool_expr.then_block, node->data.if_bool_expr.else_node,
then_type, else_type);
}
}
static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
assert(node->type == NodeTypeIfVarExpr);
zig_panic("TODO analyze_if_var_expr");
}
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node) TypeTableEntry *expected_type, AstNode *node)
{ {
@ -1651,31 +1687,12 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
} }
} }
break; break;
case NodeTypeIfExpr: case NodeTypeIfBoolExpr:
{ return_type = analyze_if_bool_expr(g, import, context, expected_type, node);
analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_expr.condition); break;
case NodeTypeIfVarExpr:
TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, return_type = analyze_if_var_expr(g, import, context, expected_type, node);
node->data.if_expr.then_block);
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;
else_type = resolve_type_compatibility(g, context, node, expected_type, else_type);
}
if (expected_type) {
return_type = (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
} else {
return_type = resolve_peer_type_compatibility(g, context, node,
node->data.if_expr.then_block, node->data.if_expr.else_node,
then_type, else_type);
}
break; break;
}
case NodeTypeWhileExpr: case NodeTypeWhileExpr:
return_type = analyze_while_expr(g, import, context, expected_type, node); return_type = analyze_while_expr(g, import, context, expected_type, node);
break; break;
@ -1828,7 +1845,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
case NodeTypeSymbol: case NodeTypeSymbol:
case NodeTypeCastExpr: case NodeTypeCastExpr:
case NodeTypePrefixOpExpr: case NodeTypePrefixOpExpr:
case NodeTypeIfExpr: case NodeTypeIfBoolExpr:
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr: case NodeTypeWhileExpr:
case NodeTypeLabel: case NodeTypeLabel:
case NodeTypeGoto: case NodeTypeGoto:

View File

@ -769,18 +769,18 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
} }
} }
static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeIfExpr); assert(node->type == NodeTypeIfBoolExpr);
assert(node->data.if_expr.condition); assert(node->data.if_bool_expr.condition);
assert(node->data.if_expr.then_block); assert(node->data.if_bool_expr.then_block);
LLVMValueRef cond_value = gen_expr(g, node->data.if_expr.condition); LLVMValueRef cond_value = gen_expr(g, node->data.if_bool_expr.condition);
TypeTableEntry *then_type = get_expr_type(node->data.if_expr.then_block); TypeTableEntry *then_type = get_expr_type(node->data.if_bool_expr.then_block);
bool use_expr_value = (then_type->id != TypeTableEntryIdUnreachable && bool use_expr_value = (then_type->id != TypeTableEntryIdUnreachable &&
then_type->id != TypeTableEntryIdVoid); then_type->id != TypeTableEntryIdVoid);
if (node->data.if_expr.else_node) { if (node->data.if_bool_expr.else_node) {
LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then"); LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else"); LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf"); LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
@ -788,13 +788,13 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {
LLVMBuildCondBr(g->builder, cond_value, then_block, else_block); LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
LLVMPositionBuilderAtEnd(g->builder, then_block); LLVMPositionBuilderAtEnd(g->builder, then_block);
LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block); LLVMValueRef then_expr_result = gen_expr(g, node->data.if_bool_expr.then_block);
if (get_expr_type(node->data.if_expr.then_block)->id != TypeTableEntryIdUnreachable) if (get_expr_type(node->data.if_bool_expr.then_block)->id != TypeTableEntryIdUnreachable)
LLVMBuildBr(g->builder, endif_block); LLVMBuildBr(g->builder, endif_block);
LLVMPositionBuilderAtEnd(g->builder, else_block); LLVMPositionBuilderAtEnd(g->builder, else_block);
LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node); LLVMValueRef else_expr_result = gen_expr(g, node->data.if_bool_expr.else_node);
if (get_expr_type(node->data.if_expr.else_node)->id != TypeTableEntryIdUnreachable) if (get_expr_type(node->data.if_bool_expr.else_node)->id != TypeTableEntryIdUnreachable)
LLVMBuildBr(g->builder, endif_block); LLVMBuildBr(g->builder, endif_block);
LLVMPositionBuilderAtEnd(g->builder, endif_block); LLVMPositionBuilderAtEnd(g->builder, endif_block);
@ -818,14 +818,19 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {
LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block); LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block);
LLVMPositionBuilderAtEnd(g->builder, then_block); LLVMPositionBuilderAtEnd(g->builder, then_block);
gen_expr(g, node->data.if_expr.then_block); gen_expr(g, node->data.if_bool_expr.then_block);
if (get_expr_type(node->data.if_expr.then_block)->id != TypeTableEntryIdUnreachable) if (get_expr_type(node->data.if_bool_expr.then_block)->id != TypeTableEntryIdUnreachable)
LLVMBuildBr(g->builder, endif_block); LLVMBuildBr(g->builder, endif_block);
LLVMPositionBuilderAtEnd(g->builder, endif_block); LLVMPositionBuilderAtEnd(g->builder, endif_block);
return nullptr; return nullptr;
} }
static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeIfVarExpr);
zig_panic("TODO gen_if_var_expr");
}
static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) { static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
assert(block_node->type == NodeTypeBlock); assert(block_node->type == NodeTypeBlock);
@ -1112,8 +1117,10 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
return LLVMConstAllOnes(LLVMInt1Type()); return LLVMConstAllOnes(LLVMInt1Type());
else else
return LLVMConstNull(LLVMInt1Type()); return LLVMConstNull(LLVMInt1Type());
case NodeTypeIfExpr: case NodeTypeIfBoolExpr:
return gen_if_expr(g, node); return gen_if_bool_expr(g, node);
case NodeTypeIfVarExpr:
return gen_if_var_expr(g, node);
case NodeTypeWhileExpr: case NodeTypeWhileExpr:
return gen_while_expr(g, node); return gen_while_expr(g, node);
case NodeTypeAsmExpr: case NodeTypeAsmExpr:

View File

@ -114,8 +114,10 @@ const char *node_type_str(NodeType node_type) {
return "Void"; return "Void";
case NodeTypeBoolLiteral: case NodeTypeBoolLiteral:
return "BoolLiteral"; return "BoolLiteral";
case NodeTypeIfExpr: case NodeTypeIfBoolExpr:
return "IfExpr"; return "IfBoolExpr";
case NodeTypeIfVarExpr:
return "IfVarExpr";
case NodeTypeWhileExpr: case NodeTypeWhileExpr:
return "WhileExpr"; return "WhileExpr";
case NodeTypeLabel: case NodeTypeLabel:
@ -321,14 +323,27 @@ void ast_print(AstNode *node, int indent) {
case NodeTypeBoolLiteral: case NodeTypeBoolLiteral:
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false"); fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false");
break; break;
case NodeTypeIfExpr: case NodeTypeIfBoolExpr:
fprintf(stderr, "%s\n", node_type_str(node->type)); fprintf(stderr, "%s\n", node_type_str(node->type));
if (node->data.if_expr.condition) if (node->data.if_bool_expr.condition)
ast_print(node->data.if_expr.condition, indent + 2); ast_print(node->data.if_bool_expr.condition, indent + 2);
ast_print(node->data.if_expr.then_block, indent + 2); ast_print(node->data.if_bool_expr.then_block, indent + 2);
if (node->data.if_expr.else_node) if (node->data.if_bool_expr.else_node)
ast_print(node->data.if_expr.else_node, indent + 2); ast_print(node->data.if_bool_expr.else_node, indent + 2);
break; break;
case NodeTypeIfVarExpr:
{
Buf *name_buf = &node->data.if_var_expr.var_decl.symbol;
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(name_buf));
if (node->data.if_var_expr.var_decl.type)
ast_print(node->data.if_var_expr.var_decl.type, indent + 2);
if (node->data.if_var_expr.var_decl.expr)
ast_print(node->data.if_var_expr.var_decl.expr, indent + 2);
ast_print(node->data.if_var_expr.then_block, indent + 2);
if (node->data.if_var_expr.else_node)
ast_print(node->data.if_var_expr.else_node, indent + 2);
break;
}
case NodeTypeWhileExpr: case NodeTypeWhileExpr:
fprintf(stderr, "%s\n", node_type_str(node->type)); fprintf(stderr, "%s\n", node_type_str(node->type));
ast_print(node->data.while_expr.condition, indent + 2); ast_print(node->data.while_expr.condition, indent + 2);
@ -1644,7 +1659,9 @@ static AstNode *ast_parse_else_or_else_if(ParseContext *pc, int *token_index, bo
} }
/* /*
IfExpression : token(If) Expression Block option(Else | ElseIf) IfExpression : IfVarExpression | IfBoolExpression
IfBoolExpression : token(If) option((token) Expression Block option(Else | ElseIf)
IfVarExpression : token(If) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(Eq) Expression Block Option(Else | ElseIf)
*/ */
static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory) { static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory) {
Token *if_tok = &pc->tokens->at(*token_index); Token *if_tok = &pc->tokens->at(*token_index);
@ -1657,11 +1674,37 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda
} }
*token_index += 1; *token_index += 1;
AstNode *node = ast_create_node(pc, NodeTypeIfExpr, if_tok); Token *token = &pc->tokens->at(*token_index);
node->data.if_expr.condition = ast_parse_expression(pc, token_index, true); if (token->id == TokenIdKeywordConst || token->id == TokenIdKeywordVar) {
node->data.if_expr.then_block = ast_parse_block(pc, token_index, true); AstNode *node = ast_create_node(pc, NodeTypeIfVarExpr, if_tok);
node->data.if_expr.else_node = ast_parse_else_or_else_if(pc, token_index, false); node->data.if_var_expr.var_decl.is_const = (token->id == TokenIdKeywordConst);
*token_index += 1;
Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol);
ast_buf_from_token(pc, name_token, &node->data.if_var_expr.var_decl.symbol);
Token *eq_or_colon = &pc->tokens->at(*token_index);
*token_index += 1;
if (eq_or_colon->id == TokenIdMaybeAssign) {
node->data.if_var_expr.var_decl.expr = ast_parse_expression(pc, token_index, true);
} else if (eq_or_colon->id == TokenIdColon) {
node->data.if_var_expr.var_decl.type = ast_parse_type(pc, token_index);
ast_eat_token(pc, token_index, TokenIdMaybeAssign);
node->data.if_var_expr.var_decl.expr = ast_parse_expression(pc, token_index, true);
} else {
ast_invalid_token_error(pc, eq_or_colon);
}
node->data.if_var_expr.then_block = ast_parse_block(pc, token_index, true);
node->data.if_var_expr.else_node = ast_parse_else_or_else_if(pc, token_index, false);
return node; return node;
} else {
AstNode *node = ast_create_node(pc, NodeTypeIfBoolExpr, if_tok);
node->data.if_bool_expr.condition = ast_parse_expression(pc, token_index, true);
node->data.if_bool_expr.then_block = ast_parse_block(pc, token_index, true);
node->data.if_bool_expr.else_node = ast_parse_else_or_else_if(pc, token_index, false);
return node;
}
} }
/* /*

View File

@ -44,7 +44,8 @@ enum NodeType {
NodeTypeUse, NodeTypeUse,
NodeTypeVoid, NodeTypeVoid,
NodeTypeBoolLiteral, NodeTypeBoolLiteral,
NodeTypeIfExpr, NodeTypeIfBoolExpr,
NodeTypeIfVarExpr,
NodeTypeWhileExpr, NodeTypeWhileExpr,
NodeTypeLabel, NodeTypeLabel,
NodeTypeGoto, NodeTypeGoto,
@ -217,12 +218,18 @@ struct AstNodeUse {
ZigList<AstNode *> *directives; ZigList<AstNode *> *directives;
}; };
struct AstNodeIfExpr { struct AstNodeIfBoolExpr {
AstNode *condition; AstNode *condition;
AstNode *then_block; AstNode *then_block;
AstNode *else_node; // null, block node, or other if expr node AstNode *else_node; // null, block node, or other if expr node
}; };
struct AstNodeIfVarExpr {
AstNodeVariableDeclaration var_decl;
AstNode *then_block;
AstNode *else_node; // null, block node, or other if expr node
};
struct AstNodeWhileExpr { struct AstNodeWhileExpr {
AstNode *condition; AstNode *condition;
AstNode *body; AstNode *body;
@ -341,7 +348,8 @@ struct AstNode {
AstNodeFnCallExpr fn_call_expr; AstNodeFnCallExpr fn_call_expr;
AstNodeArrayAccessExpr array_access_expr; AstNodeArrayAccessExpr array_access_expr;
AstNodeUse use; AstNodeUse use;
AstNodeIfExpr if_expr; AstNodeIfBoolExpr if_bool_expr;
AstNodeIfVarExpr if_var_expr;
AstNodeWhileExpr while_expr; AstNodeWhileExpr while_expr;
AstNodeLabel label; AstNodeLabel label;
AstNodeGoto go_to; AstNodeGoto go_to;

View File

@ -125,6 +125,7 @@ enum TokenizeState {
TokenizeStateSawGreaterThanGreaterThan, TokenizeStateSawGreaterThanGreaterThan,
TokenizeStateSawDot, TokenizeStateSawDot,
TokenizeStateSawDotDot, TokenizeStateSawDotDot,
TokenizeStateSawQuestionMark,
TokenizeStateError, TokenizeStateError,
}; };
@ -402,10 +403,28 @@ void tokenize(Buf *buf, Tokenization *out) {
begin_token(&t, TokenIdDot); begin_token(&t, TokenIdDot);
t.state = TokenizeStateSawDot; t.state = TokenizeStateSawDot;
break; break;
case '?':
begin_token(&t, TokenIdMaybe);
t.state = TokenizeStateSawQuestionMark;
break;
default: default:
tokenize_error(&t, "invalid character: '%c'", c); tokenize_error(&t, "invalid character: '%c'", c);
} }
break; break;
case TokenizeStateSawQuestionMark:
switch (c) {
case '=':
t.cur_tok->id = TokenIdMaybeAssign;
end_token(&t);
t.state = TokenizeStateStart;
break;
default:
t.pos -= 1;
end_token(&t);
t.state = TokenizeStateStart;
continue;
}
break;
case TokenizeStateSawDot: case TokenizeStateSawDot:
switch (c) { switch (c) {
case '.': case '.':
@ -917,6 +936,7 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateSawGreaterThan: case TokenizeStateSawGreaterThan:
case TokenizeStateSawGreaterThanGreaterThan: case TokenizeStateSawGreaterThanGreaterThan:
case TokenizeStateSawDot: case TokenizeStateSawDot:
case TokenizeStateSawQuestionMark:
end_token(&t); end_token(&t);
break; break;
case TokenizeStateSawDotDot: case TokenizeStateSawDotDot:
@ -1012,6 +1032,8 @@ static const char * token_name(Token *token) {
case TokenIdPercent: return "Percent"; case TokenIdPercent: return "Percent";
case TokenIdDot: return "Dot"; case TokenIdDot: return "Dot";
case TokenIdEllipsis: return "Ellipsis"; case TokenIdEllipsis: return "Ellipsis";
case TokenIdMaybe: return "Maybe";
case TokenIdMaybeAssign: return "MaybeAssign";
} }
return "(invalid token)"; return "(invalid token)";
} }

View File

@ -83,6 +83,8 @@ enum TokenId {
TokenIdPercent, TokenIdPercent,
TokenIdDot, TokenIdDot,
TokenIdEllipsis, TokenIdEllipsis,
TokenIdMaybe,
TokenIdMaybeAssign,
}; };
struct Token { struct Token {