Commit 75efc31329

Andrew Kelley <superjoe30@gmail.com>
2015-12-08 22:15:34
add array access syntax
1 parent 2f0e4e9
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
 * / %