Commit 1f8e3871ee
Changed files (9)
doc
example
guess_number
doc/langref.md
@@ -102,7 +102,11 @@ BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | BoolAndExp
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
example/guess_number/main.zig
@@ -12,17 +12,16 @@ fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
const answer = rand_int(&rand_state, 0, 100) + 1;
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 err == Error.None {
- if guess == answer {
- print_str("You win!\n");
- return 0;
- } else if guess < answer {
+ if const guess ?= parse_number(line) {
+ if (guess > answer) {
+ print_str("Guess lower.\n");
+ } else if (guess < answer) {
print_str("Guess higher.\n");
} else {
- print_str("Guess lower.\n");
+ print_str("You win!\n");
+ return 0;
}
} else {
print_str("Invalid number format.\n");
src/analyze.cpp
@@ -45,7 +45,8 @@ static AstNode *first_executing_node(AstNode *node) {
case NodeTypeUse:
case NodeTypeVoid:
case NodeTypeBoolLiteral:
- case NodeTypeIfExpr:
+ case NodeTypeIfBoolExpr:
+ case NodeTypeIfVarExpr:
case NodeTypeLabel:
case NodeTypeGoto:
case NodeTypeBreak:
@@ -528,7 +529,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
case NodeTypeSymbol:
case NodeTypeCastExpr:
case NodeTypePrefixOpExpr:
- case NodeTypeIfExpr:
+ case NodeTypeIfBoolExpr:
+ case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeLabel:
case NodeTypeGoto:
@@ -598,7 +600,8 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) {
case NodeTypeSymbol:
case NodeTypeCastExpr:
case NodeTypePrefixOpExpr:
- case NodeTypeIfExpr:
+ case NodeTypeIfBoolExpr:
+ case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeLabel:
case NodeTypeGoto:
@@ -1394,6 +1397,39 @@ static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *impor
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,
TypeTableEntry *expected_type, AstNode *node)
{
@@ -1651,31 +1687,12 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
}
}
break;
- case NodeTypeIfExpr:
- {
- analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_expr.condition);
-
- TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type,
- 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;
- }
+ case NodeTypeIfBoolExpr:
+ return_type = analyze_if_bool_expr(g, import, context, expected_type, node);
+ break;
+ case NodeTypeIfVarExpr:
+ return_type = analyze_if_var_expr(g, import, context, expected_type, node);
+ break;
case NodeTypeWhileExpr:
return_type = analyze_while_expr(g, import, context, expected_type, node);
break;
@@ -1828,7 +1845,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
case NodeTypeSymbol:
case NodeTypeCastExpr:
case NodeTypePrefixOpExpr:
- case NodeTypeIfExpr:
+ case NodeTypeIfBoolExpr:
+ case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeLabel:
case NodeTypeGoto:
src/codegen.cpp
@@ -769,18 +769,18 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
}
}
-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);
+static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) {
+ assert(node->type == NodeTypeIfBoolExpr);
+ assert(node->data.if_bool_expr.condition);
+ 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 &&
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 else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
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);
LLVMPositionBuilderAtEnd(g->builder, then_block);
- LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block);
- if (get_expr_type(node->data.if_expr.then_block)->id != TypeTableEntryIdUnreachable)
+ LLVMValueRef then_expr_result = gen_expr(g, node->data.if_bool_expr.then_block);
+ if (get_expr_type(node->data.if_bool_expr.then_block)->id != TypeTableEntryIdUnreachable)
LLVMBuildBr(g->builder, endif_block);
LLVMPositionBuilderAtEnd(g->builder, else_block);
- LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node);
- if (get_expr_type(node->data.if_expr.else_node)->id != TypeTableEntryIdUnreachable)
+ LLVMValueRef else_expr_result = gen_expr(g, node->data.if_bool_expr.else_node);
+ if (get_expr_type(node->data.if_bool_expr.else_node)->id != TypeTableEntryIdUnreachable)
LLVMBuildBr(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);
LLVMPositionBuilderAtEnd(g->builder, then_block);
- gen_expr(g, node->data.if_expr.then_block);
- if (get_expr_type(node->data.if_expr.then_block)->id != TypeTableEntryIdUnreachable)
+ gen_expr(g, node->data.if_bool_expr.then_block);
+ if (get_expr_type(node->data.if_bool_expr.then_block)->id != TypeTableEntryIdUnreachable)
LLVMBuildBr(g->builder, endif_block);
LLVMPositionBuilderAtEnd(g->builder, endif_block);
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) {
assert(block_node->type == NodeTypeBlock);
@@ -1112,8 +1117,10 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
return LLVMConstAllOnes(LLVMInt1Type());
else
return LLVMConstNull(LLVMInt1Type());
- case NodeTypeIfExpr:
- return gen_if_expr(g, node);
+ case NodeTypeIfBoolExpr:
+ return gen_if_bool_expr(g, node);
+ case NodeTypeIfVarExpr:
+ return gen_if_var_expr(g, node);
case NodeTypeWhileExpr:
return gen_while_expr(g, node);
case NodeTypeAsmExpr:
src/parser.cpp
@@ -114,8 +114,10 @@ const char *node_type_str(NodeType node_type) {
return "Void";
case NodeTypeBoolLiteral:
return "BoolLiteral";
- case NodeTypeIfExpr:
- return "IfExpr";
+ case NodeTypeIfBoolExpr:
+ return "IfBoolExpr";
+ case NodeTypeIfVarExpr:
+ return "IfVarExpr";
case NodeTypeWhileExpr:
return "WhileExpr";
case NodeTypeLabel:
@@ -321,14 +323,27 @@ void ast_print(AstNode *node, int indent) {
case NodeTypeBoolLiteral:
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false");
break;
- case NodeTypeIfExpr:
+ case NodeTypeIfBoolExpr:
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);
+ if (node->data.if_bool_expr.condition)
+ ast_print(node->data.if_bool_expr.condition, indent + 2);
+ ast_print(node->data.if_bool_expr.then_block, indent + 2);
+ if (node->data.if_bool_expr.else_node)
+ ast_print(node->data.if_bool_expr.else_node, indent + 2);
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:
fprintf(stderr, "%s\n", node_type_str(node->type));
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) {
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;
- 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;
+ Token *token = &pc->tokens->at(*token_index);
+ if (token->id == TokenIdKeywordConst || token->id == TokenIdKeywordVar) {
+ AstNode *node = ast_create_node(pc, NodeTypeIfVarExpr, if_tok);
+ 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;
+ } 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;
+ }
}
/*
src/parser.hpp
@@ -44,7 +44,8 @@ enum NodeType {
NodeTypeUse,
NodeTypeVoid,
NodeTypeBoolLiteral,
- NodeTypeIfExpr,
+ NodeTypeIfBoolExpr,
+ NodeTypeIfVarExpr,
NodeTypeWhileExpr,
NodeTypeLabel,
NodeTypeGoto,
@@ -217,12 +218,18 @@ struct AstNodeUse {
ZigList<AstNode *> *directives;
};
-struct AstNodeIfExpr {
+struct AstNodeIfBoolExpr {
AstNode *condition;
AstNode *then_block;
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 {
AstNode *condition;
AstNode *body;
@@ -341,7 +348,8 @@ struct AstNode {
AstNodeFnCallExpr fn_call_expr;
AstNodeArrayAccessExpr array_access_expr;
AstNodeUse use;
- AstNodeIfExpr if_expr;
+ AstNodeIfBoolExpr if_bool_expr;
+ AstNodeIfVarExpr if_var_expr;
AstNodeWhileExpr while_expr;
AstNodeLabel label;
AstNodeGoto go_to;
src/tokenizer.cpp
@@ -125,6 +125,7 @@ enum TokenizeState {
TokenizeStateSawGreaterThanGreaterThan,
TokenizeStateSawDot,
TokenizeStateSawDotDot,
+ TokenizeStateSawQuestionMark,
TokenizeStateError,
};
@@ -402,10 +403,28 @@ void tokenize(Buf *buf, Tokenization *out) {
begin_token(&t, TokenIdDot);
t.state = TokenizeStateSawDot;
break;
+ case '?':
+ begin_token(&t, TokenIdMaybe);
+ t.state = TokenizeStateSawQuestionMark;
+ break;
default:
tokenize_error(&t, "invalid character: '%c'", c);
}
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:
switch (c) {
case '.':
@@ -917,6 +936,7 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateSawGreaterThan:
case TokenizeStateSawGreaterThanGreaterThan:
case TokenizeStateSawDot:
+ case TokenizeStateSawQuestionMark:
end_token(&t);
break;
case TokenizeStateSawDotDot:
@@ -1012,6 +1032,8 @@ static const char * token_name(Token *token) {
case TokenIdPercent: return "Percent";
case TokenIdDot: return "Dot";
case TokenIdEllipsis: return "Ellipsis";
+ case TokenIdMaybe: return "Maybe";
+ case TokenIdMaybeAssign: return "MaybeAssign";
}
return "(invalid token)";
}
src/tokenizer.hpp
@@ -83,6 +83,8 @@ enum TokenId {
TokenIdPercent,
TokenIdDot,
TokenIdEllipsis,
+ TokenIdMaybe,
+ TokenIdMaybeAssign,
};
struct Token {
README.md
@@ -44,10 +44,11 @@ compromises backward compatibility.
### 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 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 produced from equivalent C program.
* Zig can generate dynamic libraries, executables, object files, and C