Commit 75efc31329
Changed files (9)
example/arrays/arrays.zig
@@ -0,0 +1,16 @@
+export executable "arrays";
+
+#link("c")
+extern {
+ fn puts(s: *const u8) -> i32;
+ fn exit(code: i32) -> unreachable;
+}
+
+export fn _start() -> unreachable {
+ let mut array : [i32; 10];
+
+ array[4] = array[1] + 5;
+
+
+ exit(0);
+}
src/analyze.cpp
@@ -6,11 +6,45 @@
*/
#include "analyze.hpp"
-#include "semantic_info.hpp"
#include "error.hpp"
#include "zig_llvm.hpp"
#include "os.hpp"
+static AstNode *first_executing_node(AstNode *node) {
+ switch (node->type) {
+ case NodeTypeFnCallExpr:
+ return first_executing_node(node->data.fn_call_expr.fn_ref_expr);
+ case NodeTypeRoot:
+ case NodeTypeRootExportDecl:
+ case NodeTypeFnProto:
+ case NodeTypeFnDef:
+ case NodeTypeFnDecl:
+ case NodeTypeParamDecl:
+ case NodeTypeType:
+ case NodeTypeBlock:
+ case NodeTypeExternBlock:
+ case NodeTypeDirective:
+ case NodeTypeReturnExpr:
+ case NodeTypeVariableDeclaration:
+ case NodeTypeBinOpExpr:
+ case NodeTypeCastExpr:
+ case NodeTypeNumberLiteral:
+ case NodeTypeStringLiteral:
+ case NodeTypeUnreachable:
+ case NodeTypeSymbol:
+ case NodeTypePrefixOpExpr:
+ case NodeTypeArrayAccessExpr:
+ case NodeTypeUse:
+ case NodeTypeVoid:
+ case NodeTypeBoolLiteral:
+ case NodeTypeIfExpr:
+ case NodeTypeLabel:
+ case NodeTypeGoto:
+ return node;
+ }
+ zig_panic("unreachable");
+}
+
void add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
ErrorMsg *err = allocate<ErrorMsg>(1);
err->line_start = node->line;
@@ -48,9 +82,10 @@ static void set_root_export_version(CodeGen *g, Buf *version_buf, AstNode *node)
}
}
-TypeTableEntry *new_type_table_entry() {
+TypeTableEntry *new_type_table_entry(TypeTableEntryId id) {
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
entry->arrays_by_size.init(2);
+ entry->id = id;
return entry;
}
@@ -61,7 +96,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
if (*parent_pointer) {
return *parent_pointer;
} else {
- TypeTableEntry *entry = new_type_table_entry();
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer);
entry->type_ref = LLVMPointerType(child_type->type_ref, 0);
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "*%s %s", is_const ? "const" : "mut", buf_ptr(&child_type->name));
@@ -80,7 +115,7 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in
if (existing_entry) {
return existing_entry->value;
} else {
- TypeTableEntry *entry = new_type_table_entry();
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
entry->type_ref = LLVMArrayType(child_type->type_ref, array_size);
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "[%s; %d]", buf_ptr(&child_type->name), array_size);
@@ -357,6 +392,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
case NodeTypeBlock:
case NodeTypeBinOpExpr:
case NodeTypeFnCallExpr:
+ case NodeTypeArrayAccessExpr:
case NodeTypeNumberLiteral:
case NodeTypeStringLiteral:
case NodeTypeUnreachable:
@@ -466,7 +502,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
// ignore void statements once we enter unreachable land.
continue;
}
- add_node_error(g, child, buf_sprintf("unreachable code"));
+ add_node_error(g, first_executing_node(child), buf_sprintf("unreachable code"));
break;
}
return_type = analyze_expression(g, import, child_context, nullptr, child);
@@ -641,14 +677,21 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
case NodeTypeFnCallExpr:
{
- Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr);
+ AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
+ if (fn_ref_expr->type != NodeTypeSymbol) {
+ add_node_error(g, node,
+ buf_sprintf("function pointers not allowed"));
+ break;
+ }
+
+ Buf *name = &fn_ref_expr->data.symbol;
auto entry = import->fn_table.maybe_get(name);
if (!entry)
entry = g->fn_table.maybe_get(name);
if (!entry) {
- add_node_error(g, node,
+ add_node_error(g, fn_ref_expr,
buf_sprintf("undefined function: '%s'", buf_ptr(name)));
// still analyze the parameters, even though we don't know what to expect
for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
@@ -691,6 +734,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
break;
}
+ case NodeTypeArrayAccessExpr:
+ {
+ // here we are always reading the array
+ TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr,
+ node->data.array_access_expr.array_ref_expr);
+ if (lhs_type->id == TypeTableEntryIdArray) {
+ zig_panic("TODO");
+ } else {
+ add_node_error(g, node, buf_sprintf("array access of non-array"));
+ }
+
+ break;
+ }
case NodeTypeNumberLiteral:
// TODO: generic literal int type
return_type = g->builtin_types.entry_i32;
@@ -897,6 +953,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
case NodeTypeBlock:
case NodeTypeBinOpExpr:
case NodeTypeFnCallExpr:
+ case NodeTypeArrayAccessExpr:
case NodeTypeNumberLiteral:
case NodeTypeStringLiteral:
case NodeTypeUnreachable:
src/analyze.hpp
@@ -8,17 +8,216 @@
#ifndef ZIG_ANALYZE_HPP
#define ZIG_ANALYZE_HPP
-struct CodeGen;
-struct AstNode;
-struct Buf;
+#include "codegen.hpp"
+#include "hash_map.hpp"
+#include "zig_llvm.hpp"
+#include "errmsg.hpp"
-struct TypeTableEntry;
-struct LocalVariableTableEntry;
+struct FnTableEntry;
struct BlockContext;
+struct TypeTableEntry;
+
+struct TypeTableEntryPointer {
+ TypeTableEntry *pointer_child;
+ bool pointer_is_const;
+};
+
+struct TypeTableEntryInt {
+ bool is_signed;
+};
+
+enum TypeTableEntryId {
+ TypeTableEntryIdInvalid,
+ TypeTableEntryIdVoid,
+ TypeTableEntryIdBool,
+ TypeTableEntryIdUnreachable,
+ TypeTableEntryIdInt,
+ TypeTableEntryIdFloat,
+ TypeTableEntryIdPointer,
+ TypeTableEntryIdArray,
+};
+
+struct TypeTableEntry {
+ TypeTableEntryId id;
+
+ LLVMTypeRef type_ref;
+ LLVMZigDIType *di_type;
+ uint64_t size_in_bits;
+ uint64_t align_in_bits;
+
+ Buf name;
+
+ union {
+ TypeTableEntryPointer pointer;
+ TypeTableEntryInt integral;
+ } data;
+
+ // use these fields to make sure we don't duplicate type table entries for the same type
+ TypeTableEntry *pointer_const_parent;
+ TypeTableEntry *pointer_mut_parent;
+ HashMap<int, TypeTableEntry *, int_hash, int_eq> arrays_by_size;
+
+};
+
+struct ImportTableEntry {
+ AstNode *root;
+ Buf *path; // relative to root_source_dir
+ LLVMZigDIFile *di_file;
+ Buf *source_code;
+ ZigList<int> *line_offsets;
+
+ // reminder: hash tables must be initialized before use
+ HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
+};
+
+struct LabelTableEntry {
+ AstNode *label_node;
+ LLVMBasicBlockRef basic_block;
+ bool used;
+ bool entered_from_fallthrough;
+};
+
+struct FnTableEntry {
+ LLVMValueRef fn_value;
+ AstNode *proto_node;
+ AstNode *fn_def_node;
+ bool is_extern;
+ bool internal_linkage;
+ unsigned calling_convention;
+ ImportTableEntry *import_entry;
+
+ // reminder: hash tables must be initialized before use
+ HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
+};
+
+struct CodeGen {
+ LLVMModuleRef module;
+ ZigList<ErrorMsg*> errors;
+ LLVMBuilderRef builder;
+ LLVMZigDIBuilder *dbuilder;
+ LLVMZigDICompileUnit *compile_unit;
+
+ // reminder: hash tables must be initialized before use
+ HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
+ HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table;
+ HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
+ HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table;
+ HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
+
+ struct {
+ TypeTableEntry *entry_bool;
+ TypeTableEntry *entry_u8;
+ TypeTableEntry *entry_i32;
+ TypeTableEntry *entry_f32;
+ TypeTableEntry *entry_string_literal;
+ TypeTableEntry *entry_void;
+ TypeTableEntry *entry_unreachable;
+ TypeTableEntry *entry_invalid;
+ } builtin_types;
+
+ LLVMTargetDataRef target_data_ref;
+ unsigned pointer_size_bytes;
+ bool is_static;
+ bool strip_debug_symbols;
+ CodeGenBuildType build_type;
+ LLVMTargetMachineRef target_machine;
+ bool is_native_target;
+ Buf *root_source_dir;
+ Buf *root_out_name;
+
+ // The function definitions this module includes. There must be a corresponding
+ // fn_protos entry.
+ ZigList<FnTableEntry *> fn_defs;
+ // The function prototypes this module includes. In the case of external declarations,
+ // there will not be a corresponding fn_defs entry.
+ ZigList<FnTableEntry *> fn_protos;
+
+ OutType out_type;
+ FnTableEntry *cur_fn;
+ LLVMBasicBlockRef cur_basic_block;
+ BlockContext *cur_block_context;
+ bool c_stdint_used;
+ AstNode *root_export_decl;
+ int version_major;
+ int version_minor;
+ int version_patch;
+ bool verbose;
+ ErrColor err_color;
+ ImportTableEntry *root_import;
+};
+
+struct LocalVariableTableEntry {
+ Buf name;
+ TypeTableEntry *type;
+ LLVMValueRef value_ref;
+ bool is_const;
+ bool is_ptr; // if true, value_ref is a pointer
+ AstNode *decl_node;
+ LLVMZigDILocalVariable *di_loc_var;
+ int arg_index;
+};
+
+struct BlockContext {
+ AstNode *node; // either NodeTypeFnDef or NodeTypeBlock
+ BlockContext *root; // always points to the BlockContext with the NodeTypeFnDef
+ BlockContext *parent; // nullptr when this is the root
+ HashMap<Buf *, LocalVariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
+ LLVMZigDIScope *di_scope;
+};
+
+struct TypeNode {
+ TypeTableEntry *entry;
+};
+
+struct FnProtoNode {
+ FnTableEntry *fn_table_entry;
+};
+
+struct FnDefNode {
+ TypeTableEntry *implicit_return_type;
+ BlockContext *block_context;
+ bool skip;
+ // Required to be a pre-order traversal of the AST. (parents must come before children)
+ ZigList<BlockContext *> all_block_contexts;
+};
+
+struct ExprNode {
+ TypeTableEntry *type_entry;
+ // the context in which this expression is evaluated.
+ // for blocks, this points to the containing scope, not the block's own scope for its children.
+ BlockContext *block_context;
+};
+
+struct AssignNode {
+ LocalVariableTableEntry *var_entry;
+};
+
+struct BlockNode {
+ BlockContext *block_context;
+};
+
+struct CodeGenNode {
+ union {
+ TypeNode type_node; // for NodeTypeType
+ FnDefNode fn_def_node; // for NodeTypeFnDef
+ FnProtoNode fn_proto_node; // for NodeTypeFnProto
+ LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel
+ AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign
+ BlockNode block_node; // for NodeTypeBlock
+ } data;
+ ExprNode expr_node; // for all the expression nodes
+};
+
+static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) {
+ // Assume that the expression evaluates to a simple name and return the buf
+ // TODO after type checking works we should be able to remove this hack
+ assert(node->type == NodeTypeSymbol);
+ return &node->data.symbol;
+}
void semantic_analyze(CodeGen *g);
void add_node_error(CodeGen *g, AstNode *node, Buf *msg);
-TypeTableEntry *new_type_table_entry();
+TypeTableEntry *new_type_table_entry(TypeTableEntryId id);
TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const);
LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name);
src/codegen.cpp
@@ -11,7 +11,6 @@
#include "os.hpp"
#include "config.h"
#include "error.hpp"
-#include "semantic_info.hpp"
#include "analyze.hpp"
#include "errmsg.hpp"
@@ -168,6 +167,12 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
}
}
+static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) {
+ assert(node->type == NodeTypeArrayAccessExpr);
+
+ zig_panic("TODO gen arary access");
+}
+
static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypePrefixOpExpr);
assert(node->data.prefix_op_expr.primary_expr);
@@ -229,49 +234,55 @@ static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) {
return LLVMBuildShl(g->builder, val1, val2, "");
case BinOpTypeBitShiftRight:
add_debug_source_node(g, node);
- if (op1_type->is_signed_int) {
+ if (op1_type->id == TypeTableEntryIdInt) {
return LLVMBuildAShr(g->builder, val1, val2, "");
} else {
return LLVMBuildLShr(g->builder, val1, val2, "");
}
case BinOpTypeAdd:
add_debug_source_node(g, node);
- if (op1_type->is_float) {
+ if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFAdd(g->builder, val1, val2, "");
} else {
return LLVMBuildNSWAdd(g->builder, val1, val2, "");
}
case BinOpTypeSub:
add_debug_source_node(g, node);
- if (op1_type->is_float) {
+ if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFSub(g->builder, val1, val2, "");
} else {
return LLVMBuildNSWSub(g->builder, val1, val2, "");
}
case BinOpTypeMult:
add_debug_source_node(g, node);
- if (op1_type->is_float) {
+ if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFMul(g->builder, val1, val2, "");
} else {
return LLVMBuildNSWMul(g->builder, val1, val2, "");
}
case BinOpTypeDiv:
add_debug_source_node(g, node);
- if (op1_type->is_float) {
+ if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFDiv(g->builder, val1, val2, "");
- } else if (op1_type->is_signed_int) {
- return LLVMBuildSDiv(g->builder, val1, val2, "");
} else {
- return LLVMBuildUDiv(g->builder, val1, val2, "");
+ assert(op1_type->id == TypeTableEntryIdInt);
+ if (op1_type->data.integral.is_signed) {
+ return LLVMBuildSDiv(g->builder, val1, val2, "");
+ } else {
+ return LLVMBuildUDiv(g->builder, val1, val2, "");
+ }
}
case BinOpTypeMod:
add_debug_source_node(g, node);
- if (op1_type->is_float) {
+ if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFRem(g->builder, val1, val2, "");
- } else if (op1_type->is_signed_int) {
- return LLVMBuildSRem(g->builder, val1, val2, "");
} else {
- return LLVMBuildURem(g->builder, val1, val2, "");
+ assert(op1_type->id == TypeTableEntryIdInt);
+ if (op1_type->data.integral.is_signed) {
+ return LLVMBuildSRem(g->builder, val1, val2, "");
+ } else {
+ return LLVMBuildURem(g->builder, val1, val2, "");
+ }
}
case BinOpTypeBoolOr:
case BinOpTypeBoolAnd:
@@ -337,11 +348,13 @@ static LLVMValueRef gen_cmp_expr(CodeGen *g, AstNode *node) {
assert(op1_type == op2_type);
add_debug_source_node(g, node);
- if (op1_type->is_float) {
+ if (op1_type->id == TypeTableEntryIdFloat) {
LLVMRealPredicate pred = cmp_op_to_real_predicate(node->data.bin_op_expr.bin_op);
return LLVMBuildFCmp(g->builder, pred, val1, val2, "");
} else {
- LLVMIntPredicate pred = cmp_op_to_int_predicate(node->data.bin_op_expr.bin_op, op1_type->is_signed_int);
+ assert(op1_type->id == TypeTableEntryIdInt);
+ LLVMIntPredicate pred = cmp_op_to_int_predicate(node->data.bin_op_expr.bin_op,
+ op1_type->data.integral.is_signed);
return LLVMBuildICmp(g->builder, pred, val1, val2, "");
}
}
@@ -596,6 +609,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
return gen_prefix_op_expr(g, node);
case NodeTypeFnCallExpr:
return gen_fn_call_expr(g, node);
+ case NodeTypeArrayAccessExpr:
+ return gen_array_access_expr(g, node);
case NodeTypeUnreachable:
add_debug_source_node(g, node);
return LLVMBuildUnreachable(g->builder);
@@ -865,12 +880,12 @@ static void do_code_gen(CodeGen *g) {
static void define_primitive_types(CodeGen *g) {
{
// if this type is anywhere in the AST, we should never hit codegen.
- TypeTableEntry *entry = new_type_table_entry();
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInvalid);
buf_init_from_str(&entry->name, "(invalid)");
g->builtin_types.entry_invalid = entry;
}
{
- TypeTableEntry *entry = new_type_table_entry();
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool);
entry->type_ref = LLVMInt1Type();
buf_init_from_str(&entry->name, "bool");
entry->size_in_bits = 1;
@@ -882,7 +897,7 @@ static void define_primitive_types(CodeGen *g) {
g->builtin_types.entry_bool = entry;
}
{
- TypeTableEntry *entry = new_type_table_entry();
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMInt8Type();
buf_init_from_str(&entry->name, "u8");
entry->size_in_bits = 8;
@@ -895,12 +910,12 @@ static void define_primitive_types(CodeGen *g) {
}
g->builtin_types.entry_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
{
- TypeTableEntry *entry = new_type_table_entry();
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMInt32Type();
buf_init_from_str(&entry->name, "i32");
entry->size_in_bits = 32;
entry->align_in_bits = 32;
- entry->is_signed_int = true;
+ entry->data.integral.is_signed = true;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
@@ -908,12 +923,11 @@ static void define_primitive_types(CodeGen *g) {
g->builtin_types.entry_i32 = entry;
}
{
- TypeTableEntry *entry = new_type_table_entry();
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
entry->type_ref = LLVMFloatType();
buf_init_from_str(&entry->name, "f32");
entry->size_in_bits = 32;
entry->align_in_bits = 32;
- entry->is_float = true;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_float());
@@ -921,7 +935,7 @@ static void define_primitive_types(CodeGen *g) {
g->builtin_types.entry_f32 = entry;
}
{
- TypeTableEntry *entry = new_type_table_entry();
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid);
entry->type_ref = LLVMVoidType();
buf_init_from_str(&entry->name, "void");
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
@@ -931,7 +945,7 @@ static void define_primitive_types(CodeGen *g) {
g->builtin_types.entry_void = entry;
}
{
- TypeTableEntry *entry = new_type_table_entry();
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUnreachable);
entry->type_ref = LLVMVoidType();
buf_init_from_str(&entry->name, "unreachable");
entry->di_type = g->builtin_types.entry_void->di_type;
src/parser.cpp
@@ -7,7 +7,7 @@
#include "parser.hpp"
#include "errmsg.hpp"
-#include "semantic_info.hpp"
+#include "analyze.hpp"
#include <stdarg.h>
#include <stdio.h>
@@ -70,6 +70,8 @@ const char *node_type_str(NodeType node_type) {
return "BinOpExpr";
case NodeTypeFnCallExpr:
return "FnCallExpr";
+ case NodeTypeArrayAccessExpr:
+ return "ArrayAccessExpr";
case NodeTypeExternBlock:
return "ExternBlock";
case NodeTypeDirective:
@@ -231,6 +233,11 @@ void ast_print(AstNode *node, int indent) {
ast_print(child, indent + 2);
}
break;
+ case NodeTypeArrayAccessExpr:
+ fprintf(stderr, "%s\n", node_type_str(node->type));
+ ast_print(node->data.array_access_expr.array_ref_expr, indent + 2);
+ ast_print(node->data.array_access_expr.subscript, indent + 2);
+ break;
case NodeTypeDirective:
fprintf(stderr, "%s\n", node_type_str(node->type));
break;
@@ -566,10 +573,6 @@ static void ast_parse_param_decl_list(ParseContext *pc, int token_index, int *ne
static void ast_parse_fn_call_param_list(ParseContext *pc, int token_index, int *new_token_index,
ZigList<AstNode*> *params)
{
- Token *l_paren = &pc->tokens->at(token_index);
- token_index += 1;
- ast_expect_token(pc, l_paren, TokenIdLParen);
-
Token *token = &pc->tokens->at(token_index);
if (token->id == TokenIdRParen) {
token_index += 1;
@@ -680,22 +683,39 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
}
/*
-FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma)) token(RParen) | PrimaryExpression
+SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression)
+FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
+ArrayAccessExpression : token(LBracket) Expression token(RBracket)
*/
-static AstNode *ast_parse_fn_call_expr(ParseContext *pc, int *token_index, bool mandatory) {
+static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, bool mandatory) {
AstNode *primary_expr = ast_parse_primary_expr(pc, token_index, mandatory);
- if (!primary_expr)
+ if (!primary_expr) {
return nullptr;
+ }
- Token *l_paren = &pc->tokens->at(*token_index);
- if (l_paren->id != TokenIdLParen)
- return primary_expr;
+ Token *token = &pc->tokens->at(*token_index);
+ if (token->id == TokenIdLParen) {
+ *token_index += 1;
- AstNode *node = ast_create_node_with_node(pc, NodeTypeFnCallExpr, primary_expr);
- node->data.fn_call_expr.fn_ref_expr = primary_expr;
- ast_parse_fn_call_param_list(pc, *token_index, token_index, &node->data.fn_call_expr.params);
+ AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, token);
+ node->data.fn_call_expr.fn_ref_expr = primary_expr;
+ ast_parse_fn_call_param_list(pc, *token_index, token_index, &node->data.fn_call_expr.params);
+ return node;
+ } else if (token->id == TokenIdLBracket) {
+ *token_index += 1;
- return node;
+ AstNode *node = ast_create_node(pc, NodeTypeArrayAccessExpr, token);
+ node->data.array_access_expr.array_ref_expr = primary_expr;
+ node->data.array_access_expr.subscript = ast_parse_expression(pc, token_index, true);
+
+ Token *r_bracket = &pc->tokens->at(*token_index);
+ *token_index += 1;
+ ast_expect_token(pc, r_bracket, TokenIdRBracket);
+
+ return node;
+ } else {
+ return primary_expr;
+ }
}
static PrefixOp tok_to_prefix_op(Token *token) {
@@ -725,17 +745,17 @@ static PrefixOp ast_parse_prefix_op(ParseContext *pc, int *token_index, bool man
}
/*
-PrefixOpExpression : PrefixOp FnCallExpression | FnCallExpression
+PrefixOpExpression : PrefixOp SuffixOpExpression | SuffixOpExpression
*/
static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index);
PrefixOp prefix_op = ast_parse_prefix_op(pc, token_index, false);
if (prefix_op == PrefixOpInvalid)
- return ast_parse_fn_call_expr(pc, token_index, mandatory);
+ return ast_parse_suffix_op_expr(pc, token_index, mandatory);
- AstNode *primary_expr = ast_parse_fn_call_expr(pc, token_index, true);
+ AstNode *prefix_op_expr = ast_parse_suffix_op_expr(pc, token_index, true);
AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token);
- node->data.prefix_op_expr.primary_expr = primary_expr;
+ node->data.prefix_op_expr.primary_expr = prefix_op_expr;
node->data.prefix_op_expr.prefix_op = prefix_op;
return node;
src/parser.hpp
@@ -38,6 +38,7 @@ enum NodeType {
NodeTypeSymbol,
NodeTypePrefixOpExpr,
NodeTypeFnCallExpr,
+ NodeTypeArrayAccessExpr,
NodeTypeUse,
NodeTypeVoid,
NodeTypeBoolLiteral,
@@ -143,6 +144,11 @@ struct AstNodeFnCallExpr {
ZigList<AstNode *> params;
};
+struct AstNodeArrayAccessExpr {
+ AstNode *array_ref_expr;
+ AstNode *subscript;
+};
+
struct AstNodeExternBlock {
ZigList<AstNode *> *directives;
ZigList<AstNode *> fn_decls;
@@ -219,6 +225,7 @@ struct AstNode {
AstNodeCastExpr cast_expr;
AstNodePrefixOpExpr prefix_op_expr;
AstNodeFnCallExpr fn_call_expr;
+ AstNodeArrayAccessExpr array_access_expr;
AstNodeUse use;
AstNodeIfExpr if_expr;
AstNodeLabel label;
src/semantic_info.hpp
@@ -1,194 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_SEMANTIC_INFO_HPP
-#define ZIG_SEMANTIC_INFO_HPP
-
-#include "codegen.hpp"
-#include "hash_map.hpp"
-#include "zig_llvm.hpp"
-#include "errmsg.hpp"
-
-struct FnTableEntry;
-struct BlockContext;
-
-struct TypeTableEntry {
- LLVMTypeRef type_ref;
- LLVMZigDIType *di_type;
- uint64_t size_in_bits;
- uint64_t align_in_bits;
- bool is_signed_int;
- bool is_float;
-
- TypeTableEntry *pointer_child;
- bool pointer_is_const;
- int user_defined_id;
- Buf name;
-
- // use these fields to make sure we don't duplicate type table entries for the same type
- TypeTableEntry *pointer_const_parent;
- TypeTableEntry *pointer_mut_parent;
- HashMap<int, TypeTableEntry *, int_hash, int_eq> arrays_by_size;
-};
-
-struct ImportTableEntry {
- AstNode *root;
- Buf *path; // relative to root_source_dir
- LLVMZigDIFile *di_file;
- Buf *source_code;
- ZigList<int> *line_offsets;
-
- // reminder: hash tables must be initialized before use
- HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
-};
-
-struct LabelTableEntry {
- AstNode *label_node;
- LLVMBasicBlockRef basic_block;
- bool used;
- bool entered_from_fallthrough;
-};
-
-struct FnTableEntry {
- LLVMValueRef fn_value;
- AstNode *proto_node;
- AstNode *fn_def_node;
- bool is_extern;
- bool internal_linkage;
- unsigned calling_convention;
- ImportTableEntry *import_entry;
-
- // reminder: hash tables must be initialized before use
- HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
-};
-
-struct CodeGen {
- LLVMModuleRef module;
- ZigList<ErrorMsg*> errors;
- LLVMBuilderRef builder;
- LLVMZigDIBuilder *dbuilder;
- LLVMZigDICompileUnit *compile_unit;
-
- // reminder: hash tables must be initialized before use
- HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
- HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table;
- HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
- HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table;
- HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
-
- struct {
- TypeTableEntry *entry_bool;
- TypeTableEntry *entry_u8;
- TypeTableEntry *entry_i32;
- TypeTableEntry *entry_f32;
- TypeTableEntry *entry_string_literal;
- TypeTableEntry *entry_void;
- TypeTableEntry *entry_unreachable;
- TypeTableEntry *entry_invalid;
- } builtin_types;
-
- LLVMTargetDataRef target_data_ref;
- unsigned pointer_size_bytes;
- bool is_static;
- bool strip_debug_symbols;
- CodeGenBuildType build_type;
- LLVMTargetMachineRef target_machine;
- bool is_native_target;
- Buf *root_source_dir;
- Buf *root_out_name;
-
- // The function definitions this module includes. There must be a corresponding
- // fn_protos entry.
- ZigList<FnTableEntry *> fn_defs;
- // The function prototypes this module includes. In the case of external declarations,
- // there will not be a corresponding fn_defs entry.
- ZigList<FnTableEntry *> fn_protos;
-
- OutType out_type;
- FnTableEntry *cur_fn;
- LLVMBasicBlockRef cur_basic_block;
- BlockContext *cur_block_context;
- bool c_stdint_used;
- AstNode *root_export_decl;
- int version_major;
- int version_minor;
- int version_patch;
- bool verbose;
- ErrColor err_color;
- ImportTableEntry *root_import;
-};
-
-struct LocalVariableTableEntry {
- Buf name;
- TypeTableEntry *type;
- LLVMValueRef value_ref;
- bool is_const;
- bool is_ptr; // if true, value_ref is a pointer
- AstNode *decl_node;
- LLVMZigDILocalVariable *di_loc_var;
- int arg_index;
-};
-
-struct BlockContext {
- AstNode *node; // either NodeTypeFnDef or NodeTypeBlock
- BlockContext *root; // always points to the BlockContext with the NodeTypeFnDef
- BlockContext *parent; // nullptr when this is the root
- HashMap<Buf *, LocalVariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
- LLVMZigDIScope *di_scope;
-};
-
-struct TypeNode {
- TypeTableEntry *entry;
-};
-
-struct FnProtoNode {
- FnTableEntry *fn_table_entry;
-};
-
-struct FnDefNode {
- TypeTableEntry *implicit_return_type;
- BlockContext *block_context;
- bool skip;
- // Required to be a pre-order traversal of the AST. (parents must come before children)
- ZigList<BlockContext *> all_block_contexts;
-};
-
-struct ExprNode {
- TypeTableEntry *type_entry;
- // the context in which this expression is evaluated.
- // for blocks, this points to the containing scope, not the block's own scope for its children.
- BlockContext *block_context;
-};
-
-struct AssignNode {
- LocalVariableTableEntry *var_entry;
-};
-
-struct BlockNode {
- BlockContext *block_context;
-};
-
-struct CodeGenNode {
- union {
- TypeNode type_node; // for NodeTypeType
- FnDefNode fn_def_node; // for NodeTypeFnDef
- FnProtoNode fn_proto_node; // for NodeTypeFnProto
- LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel
- AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign
- BlockNode block_node; // for NodeTypeBlock
- } data;
- ExprNode expr_node; // for all the expression nodes
-};
-
-static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) {
- // Assume that the expression evaluates to a simple name and return the buf
- // TODO after type checking works we should be able to remove this hack
- assert(node->type == NodeTypeSymbol);
- return &node->data.symbol;
-}
-
-#endif
test/run_tests.cpp
@@ -392,7 +392,7 @@ fn a() {
b(1);
}
fn b(a: i32, b: i32, c: i32) { }
- )SOURCE", 1, ".tmp_source.zig:3:5: error: wrong number of arguments. Expected 3, got 1.");
+ )SOURCE", 1, ".tmp_source.zig:3:6: error: wrong number of arguments. Expected 3, got 1.");
add_compile_fail_case("invalid type", R"SOURCE(
fn a() -> bogus {}
README.md
@@ -64,6 +64,8 @@ compromises backward compatibility.
* main function with command line arguments
* void pointer constant
* sizeof
+ * address of operator
+ * global variables
* static initializers
* assert
* function pointers
@@ -202,9 +204,13 @@ MultiplyOperator : token(Star) | token(Slash) | token(Percent)
CastExpression : PrefixOpExpression token(as) Type | PrefixOpExpression
-PrefixOpExpression : PrefixOp FnCallExpression | FnCallExpression
+PrefixOpExpression : PrefixOp SuffixOpExpression | SuffixOpExpression
-FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma)) token(RParen) | PrimaryExpression
+SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression)
+
+FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
+
+ArrayAccessExpression : token(LBracket) Expression token(RBracket)
PrefixOp : token(Not) | token(Dash) | token(Tilde)
@@ -220,7 +226,7 @@ KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False)
## Operator Precedence
```
-x()
+x() x[]
!x -x ~x
as
* / %