diff --git a/README.md b/README.md index 7cfc9e7e4..38a871d2f 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ Statement : NonBlockExpression token(Semicolon) | BlockExpression Expression : BlockExpression | NonBlockExpression -NonBlockExpression : BoolOrExpression | ReturnExpression +NonBlockExpression : ReturnExpression | VariableDeclaration | BoolOrExpression BlockExpression : IfExpression | Block @@ -122,6 +122,8 @@ BoolOrExpression : BoolAndExpression token(BoolOr) BoolAndExpression | BoolAndEx ReturnExpression : token(Return) option(Expression) +VariableDeclaration : token(Let) token(Symbole) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression)) + IfExpression : token(If) Expression Block option(Else | ElseIf) ElseIf : token(Else) IfExpression diff --git a/example/expressions/expressions.zig b/example/expressions/expressions.zig new file mode 100644 index 000000000..289bee9c4 --- /dev/null +++ b/example/expressions/expressions.zig @@ -0,0 +1,14 @@ +#link("c") +extern { + fn puts(s: *const u8) -> i32; + fn exit(code: i32) -> unreachable; +} + +export fn _start() -> unreachable { + let a : i32 = 1; + let b = 2; + let c : i32; + // let d; // compile error + puts("Hello, world!"); + exit(a + b); +} diff --git a/src/analyze.cpp b/src/analyze.cpp index 9fe5a87bb..ef3b9ff76 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -277,6 +277,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, case NodeTypeType: case NodeTypeFnDecl: case NodeTypeReturnExpr: + case NodeTypeVariableDeclaration: case NodeTypeRoot: case NodeTypeBlock: case NodeTypeBinOpExpr: @@ -373,6 +374,14 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, break; } + case NodeTypeVariableDeclaration: + { + zig_panic("TODO: analyze variable declaration"); + + return_type = g->builtin_types.entry_void; + break; + } + case NodeTypeBinOpExpr: { switch (node->data.bin_op_expr.bin_op) { @@ -630,6 +639,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, case NodeTypeType: case NodeTypeFnDecl: case NodeTypeReturnExpr: + case NodeTypeVariableDeclaration: case NodeTypeRoot: case NodeTypeBlock: case NodeTypeBinOpExpr: diff --git a/src/codegen.cpp b/src/codegen.cpp index 4da2075f9..13d72f612 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -477,6 +477,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { return gen_bin_op_expr(g, node); case NodeTypeReturnExpr: return gen_return_expr(g, node); + case NodeTypeVariableDeclaration: + zig_panic("TODO: variable declaration code gen"); case NodeTypeCastExpr: return gen_cast_expr(g, node); case NodeTypePrefixOpExpr: diff --git a/src/parser.cpp b/src/parser.cpp index b1b9e122a..bd4c95960 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -75,6 +75,8 @@ const char *node_type_str(NodeType node_type) { return "Directive"; case NodeTypeReturnExpr: return "ReturnExpr"; + case NodeTypeVariableDeclaration: + return "VariableDeclaration"; case NodeTypeCastExpr: return "CastExpr"; case NodeTypeNumberLiteral: @@ -178,6 +180,16 @@ void ast_print(AstNode *node, int indent) { if (node->data.return_expr.expr) ast_print(node->data.return_expr.expr, indent + 2); break; + case NodeTypeVariableDeclaration: + { + Buf *name_buf = &node->data.variable_declaration.symbol; + fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(name_buf)); + if (node->data.variable_declaration.type) + ast_print(node->data.variable_declaration.type, indent + 2); + if (node->data.variable_declaration.expr) + ast_print(node->data.variable_declaration.expr, indent + 2); + break; + } case NodeTypeExternBlock: { fprintf(stderr, "%s\n", node_type_str(node->type)); @@ -1042,6 +1054,45 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m } } +/* +VariableDeclaration : token(Let) token(Symbole) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression)) +*/ +static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) { + Token *let_tok = &pc->tokens->at(*token_index); + if (let_tok->id == TokenIdKeywordLet) { + *token_index += 1; + AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, let_tok); + + Token *name_token = &pc->tokens->at(*token_index); + *token_index += 1; + ast_expect_token(pc, name_token, TokenIdSymbol); + ast_buf_from_token(pc, name_token, &node->data.variable_declaration.symbol); + + Token *eq_or_colon = &pc->tokens->at(*token_index); + *token_index += 1; + if (eq_or_colon->id == TokenIdEq) { + node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true); + return node; + } else if (eq_or_colon->id == TokenIdColon) { + node->data.variable_declaration.type = ast_parse_type(pc, *token_index, token_index); + + Token *eq_token = &pc->tokens->at(*token_index); + if (eq_token->id == TokenIdEq) { + *token_index += 1; + + node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true); + } + return node; + } else { + ast_invalid_token_error(pc, eq_or_colon); + } + } else if (mandatory) { + ast_invalid_token_error(pc, let_tok); + } else { + return nullptr; + } +} + /* BoolOrExpression : BoolAndExpression token(BoolOr) BoolAndExpression | BoolAndExpression */ @@ -1086,19 +1137,23 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool ma } /* -NonBlockExpression : BoolOrExpression | ReturnExpression +NonBlockExpression : ReturnExpression | VariableDeclaration | BoolOrExpression */ 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); - if (bool_or_expr) - return bool_or_expr; - AstNode *return_expr = ast_parse_return_expr(pc, token_index, false); if (return_expr) return return_expr; + AstNode *variable_declaration_expr = ast_parse_variable_declaration_expr(pc, token_index, false); + if (variable_declaration_expr) + return variable_declaration_expr; + + AstNode *bool_or_expr = ast_parse_bool_or_expr(pc, token_index, false); + if (bool_or_expr) + return bool_or_expr; + if (mandatory) ast_invalid_token_error(pc, token); diff --git a/src/parser.hpp b/src/parser.hpp index 3bfafd75b..8c7e86a20 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -29,6 +29,7 @@ enum NodeType { NodeTypeExternBlock, NodeTypeDirective, NodeTypeReturnExpr, + NodeTypeVariableDeclaration, NodeTypeBinOpExpr, NodeTypeCastExpr, NodeTypeNumberLiteral, @@ -95,6 +96,13 @@ struct AstNodeReturnExpr { AstNode *expr; }; +struct AstNodeVariableDeclaration { + Buf symbol; + // one or both of type and expr will be non null + AstNode *type; + AstNode *expr; +}; + enum BinOpType { BinOpTypeInvalid, // TODO: include assignment? @@ -190,6 +198,7 @@ struct AstNode { AstNodeParamDecl param_decl; AstNodeBlock block; AstNodeReturnExpr return_expr; + AstNodeVariableDeclaration variable_declaration; AstNodeBinOpExpr bin_op_expr; AstNodeExternBlock extern_block; AstNodeDirective directive; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 68e072fd5..82119ba28 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -165,6 +165,8 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordFn; } else if (mem_eql_str(token_mem, token_len, "return")) { t->cur_tok->id = TokenIdKeywordReturn; + } else if (mem_eql_str(token_mem, token_len, "let")) { + t->cur_tok->id = TokenIdKeywordLet; } else if (mem_eql_str(token_mem, token_len, "mut")) { t->cur_tok->id = TokenIdKeywordMut; } else if (mem_eql_str(token_mem, token_len, "const")) { @@ -574,6 +576,7 @@ static const char * token_name(Token *token) { case TokenIdKeywordConst: return "Const"; case TokenIdKeywordMut: return "Mut"; case TokenIdKeywordReturn: return "Return"; + case TokenIdKeywordLet: return "Let"; case TokenIdKeywordExtern: return "Extern"; case TokenIdKeywordUnreachable: return "Unreachable"; case TokenIdKeywordPub: return "Pub"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 598176b36..139b4727d 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -15,6 +15,7 @@ enum TokenId { TokenIdSymbol, TokenIdKeywordFn, TokenIdKeywordReturn, + TokenIdKeywordLet, TokenIdKeywordMut, TokenIdKeywordConst, TokenIdKeywordExtern,