Commit 0ae9023832

Andrew Kelley <superjoe30@gmail.com>
2016-08-12 07:25:13
add CBuf to standard library
and fix ability to take address of variables from other namespaces
1 parent 0a482bb
src/analyze.cpp
@@ -2688,6 +2688,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
             return node->data.field_access_expr.type_struct_field->type_entry;
         } else if (wrapped_in_fn_call) {
             BlockContext *container_block_context = get_container_block_context(bare_struct_type);
+            assert(container_block_context);
             auto entry = container_block_context->decl_table.maybe_get(field_name);
             AstNode *fn_decl_node = entry ? entry->value : nullptr;
             if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) {
src/codegen.cpp
@@ -1243,6 +1243,11 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou
 
     AstNode *struct_expr_node = node->data.field_access_expr.struct_expr;
 
+    *out_type_entry = node->data.field_access_expr.type_struct_field->type_entry;
+    if (!type_has_bits(*out_type_entry)) {
+        return nullptr;
+    }
+
     LLVMValueRef struct_ptr;
     if (struct_expr_node->type == NodeTypeSymbol) {
         VariableTableEntry *var = get_resolved_expr(struct_expr_node)->variable;
@@ -1272,8 +1277,6 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou
     int gen_field_index = node->data.field_access_expr.type_struct_field->gen_index;
     assert(gen_field_index >= 0);
 
-    *out_type_entry = node->data.field_access_expr.type_struct_field->type_entry;
-
     set_debug_source_node(g, node);
     return LLVMBuildStructGEP(g->builder, struct_ptr, gen_field_index, "");
 }
@@ -1490,7 +1493,14 @@ static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node,
             zig_unreachable();
         }
     } else if (node->type == NodeTypeFieldAccessExpr) {
-        target_ref = gen_field_ptr(g, node, out_type_entry);
+        AstNode *struct_expr_node = node->data.field_access_expr.struct_expr;
+        TypeTableEntry *struct_type = get_expr_type(struct_expr_node);
+        if (struct_type->id == TypeTableEntryIdNamespace) {
+            target_ref = gen_field_access_expr(g, node, true);
+            *out_type_entry = get_expr_type(node);
+        } else {
+            target_ref = gen_field_ptr(g, node, out_type_entry);
+        }
     } else if (node->type == NodeTypePrefixOpExpr) {
         assert(node->data.prefix_op_expr.prefix_op == PrefixOpDereference);
         AstNode *target_expr = node->data.prefix_op_expr.primary_expr;
std/cstr.zig
@@ -1,3 +1,11 @@
+const List = @import("list.zig").List;
+const mem = @import("mem.zig");
+const Allocator = mem.Allocator;
+const debug = @import("debug.zig");
+const assert = debug.assert;
+
+const strlen = len;
+
 // TODO fix https://github.com/andrewrk/zig/issues/140
 // and then make this able to run at compile time
 #static_eval_enable(false)
@@ -17,10 +25,121 @@ pub fn cmp(a: &const u8, b: &const u8) -> i32 {
 }
 
 pub fn to_slice_const(str: &const u8) -> []const u8 {
-    return str[0...len(str)];
+    return str[0...strlen(str)];
 }
 
 pub fn to_slice(str: &u8) -> []u8 {
-    return str[0...len(str)];
+    return str[0...strlen(str)];
+}
+
+
+/// A buffer that allocates memory and maintains a null byte at the end.
+pub struct CBuf {
+    list: List(u8),
+
+    /// Must deinitialize with deinit.
+    pub fn init(self: &CBuf, allocator: &Allocator) {
+        self.list.init(allocator);
+        // This resize is guaranteed to not have an error because we use a list
+        // with preallocated memory of at least 1 byte.
+        %%self.resize(0);
+    }
+
+    /// Must deinitialize with deinit.
+    pub fn init_from_mem(self: &CBuf, allocator: &Allocator, m: []const u8) -> %void {
+        self.init(allocator);
+        %return self.resize(m.len);
+        mem.copy(u8, self.list.items, m);
+    }
+
+    /// Must deinitialize with deinit.
+    pub fn init_from_cstr(self: &CBuf, allocator: &Allocator, s: &const u8) -> %void {
+        self.init_from_mem(allocator, s[0...strlen(s)])
+    }
+
+    /// Must deinitialize with deinit.
+    pub fn init_from_cbuf(self: &CBuf, cbuf: &const CBuf) -> %void {
+        self.init_from_mem(cbuf.list.allocator, cbuf.list.items[0...cbuf.len()])
+    }
+
+    /// Must deinitialize with deinit.
+    pub fn init_from_slice(self: &CBuf, other: &const CBuf, start: usize, end: usize) -> %void {
+        self.init_from_mem(other.list.allocator, other.list.items[start...end])
+    }
+
+    pub fn deinit(self: &CBuf) {
+        self.list.deinit();
+    }
+
+    pub fn resize(self: &CBuf, new_len: usize) -> %void {
+        %return self.list.resize(new_len + 1);
+        self.list.items[self.len()] = 0;
+    }
+
+    pub fn len(self: &const CBuf) -> usize {
+        return self.list.len - 1;
+    }
+
+    pub fn append_mem(self: &CBuf, m: []const u8) -> %void {
+        const old_len = self.len();
+        %return self.resize(old_len + m.len);
+        mem.copy(u8, self.list.items[old_len...], m);
+    }
+
+    pub fn append_cstr(self: &CBuf, s: &const u8) -> %void {
+        self.append_mem(s[0...strlen(s)])
+    }
+
+    pub fn append_char(self: &CBuf, c: u8) -> %void {
+        %return self.resize(self.len() + 1);
+        self.list.items[self.len() - 1] = c;
+    }
+
+    pub fn eql_mem(self: &const CBuf, m: []const u8) -> bool {
+        if (self.len() != m.len) return false;
+        return mem.cmp(u8, self.list.items[0...m.len], m) == mem.Cmp.Equal;
+    }
+
+    pub fn eql_cstr(self: &const CBuf, s: &const u8) -> bool {
+        self.eql_mem(s[0...strlen(s)])
+    }
+
+    pub fn eql_cbuf(self: &const CBuf, other: &const CBuf) -> bool {
+        self.eql_mem(other.list.items[0...other.len()])
+    }
+
+    pub fn starts_with_mem(self: &const CBuf, m: []const u8) -> bool {
+        if (self.len() < m.len) return false;
+        return mem.cmp(u8, self.list.items[0...m.len], m) == mem.Cmp.Equal;
+    }
+
+    pub fn starts_with_cbuf(self: &const CBuf, other: &const CBuf) -> bool {
+        self.starts_with_mem(other.list.items[0...other.len()])
+    }
+
+    pub fn starts_with_cstr(self: &const CBuf, s: &const u8) -> bool {
+        self.starts_with_mem(s[0...strlen(s)])
+    }
 }
 
+#attribute("test")
+fn test_simple_cbuf() {
+    var buf: CBuf = undefined;
+    buf.init(&debug.global_allocator);
+    assert(buf.len() == 0);
+    %%buf.append_cstr(c"hello");
+    %%buf.append_char(' ');
+    %%buf.append_mem("world");
+    assert(buf.eql_cstr(c"hello world"));
+    assert(buf.eql_mem("hello world"));
+
+    var buf2: CBuf = undefined;
+    %%buf2.init_from_cbuf(&buf);
+    assert(buf.eql_cbuf(&buf2));
+
+    assert(buf.starts_with_mem("hell"));
+    assert(buf.starts_with_cstr(c"hell"));
+
+    %%buf2.resize(4);
+    assert(buf.starts_with_cbuf(&buf2));
+}
std/debug.zig
@@ -1,10 +1,11 @@
+const Allocator = @import("mem.zig").Allocator;
 const io = @import("io.zig");
 
 pub fn assert(b: bool) {
     if (!b) unreachable{}
 }
 
-pub fn print_stack_trace() {
+pub fn printStackTrace() {
     var maybe_fp: ?&const u8 = @frame_address();
     while (true) {
         const fp = maybe_fp ?? break;
@@ -14,3 +15,27 @@ pub fn print_stack_trace() {
         maybe_fp = *(&const ?&const u8)(fp);
     }
 }
+
+pub var global_allocator = Allocator {
+    .alloc_fn = globalAlloc,
+    .realloc_fn = globalRealloc,
+    .free_fn = globalFree,
+    .context = null,
+};
+
+var some_mem: [10 * 1024]u8 = undefined;
+var some_mem_index: usize = 0;
+
+fn globalAlloc(self: &Allocator, n: usize) -> %[]u8 {
+    const result = some_mem[some_mem_index ... some_mem_index + n];
+    some_mem_index += n;
+    return result;
+}
+
+fn globalRealloc(self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 {
+    const result = %return globalAlloc(self, new_size);
+    @memcpy(result.ptr, old_mem.ptr, old_mem.len);
+    return result;
+}
+
+fn globalFree(self: &Allocator, old_mem: []u8) { }
std/hash_map.zig
@@ -1,4 +1,5 @@
-const assert = @import("debug.zig").assert;
+const debug = @import("debug.zig");
+const assert = debug.assert;
 const math = @import("math.zig");
 const mem = @import("mem.zig");
 const Allocator = mem.Allocator;
@@ -9,7 +10,7 @@ const debug_u32 = if (want_modification_safety) u32 else void;
 pub fn HashMap(inline K: type, inline V: type, inline hash: fn(key: K)->u32,
     inline eql: fn(a: K, b: K)->bool) -> type
 {
-    SmallHashMap(K, V, hash, eql, 8)
+    SmallHashMap(K, V, hash, eql, @sizeof(usize))
 }
 
 pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)->bool, STATIC_SIZE: usize) {
@@ -63,9 +64,8 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b
         hm.allocator = allocator;
         hm.size = 0;
         hm.max_distance_from_start_index = 0;
-        for (hm.entries) |*entry| {
-            entry.used = false;
-        }
+        hm.prealloc_entries = zeroes; // sets used to false for all entries
+        hm.modification_count = zeroes;
     }
 
     pub fn deinit(hm: &Self) {
@@ -162,7 +162,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b
 
     fn increment_modification_count(hm: &Self) {
         if (want_modification_safety) {
-            hm.modification_count += 1;
+            hm.modification_count +%= 1;
         }
     }
 
@@ -231,35 +231,10 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b
     }
 }
 
-var global_allocator = Allocator {
-    .alloc_fn = global_alloc,
-    .realloc_fn = global_realloc,
-    .free_fn = global_free,
-    .context = null,
-};
-
-var some_mem: [200]u8 = undefined;
-var some_mem_index: usize = 0;
-
-fn global_alloc(self: &Allocator, n: usize) -> %[]u8 {
-    const result = some_mem[some_mem_index ... some_mem_index + n];
-    some_mem_index += n;
-    return result;
-}
-
-fn global_realloc(self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 {
-    const result = %return global_alloc(self, new_size);
-    @memcpy(result.ptr, old_mem.ptr, old_mem.len);
-    return result;
-}
-
-fn global_free(self: &Allocator, old_mem: []u8) {
-}
-
 #attribute("test")
 fn basic_hash_map_test() {
     var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined;
-    map.init(&global_allocator);
+    map.init(&debug.global_allocator);
     defer map.deinit();
 
     %%map.put(1, 11);
std/list.zig
@@ -1,22 +1,25 @@
-const assert = @import("debug.zig").assert;
+const debug = @import("debug.zig");
+const assert = debug.assert;
 const mem = @import("mem.zig");
 const Allocator = mem.Allocator;
 
 pub fn List(inline T: type) -> type {
-    SmallList(T, 8)
+    SmallList(T, @sizeof(usize))
 }
 
+// TODO: make sure that setting STATIC_SIZE to 0 codegens to the same code
+// as if this were programmed without STATIC_SIZE at all.
 pub struct SmallList(T: type, STATIC_SIZE: usize) {
     const Self = SmallList(T, STATIC_SIZE);
 
     items: []T,
-    length: usize,
+    len: usize,
     prealloc_items: [STATIC_SIZE]T,
     allocator: &Allocator,
 
     pub fn init(l: &Self, allocator: &Allocator) {
         l.items = l.prealloc_items[0...];
-        l.length = 0;
+        l.len = 0;
         l.allocator = allocator;
     }
 
@@ -27,10 +30,15 @@ pub struct SmallList(T: type, STATIC_SIZE: usize) {
     }
 
     pub fn append(l: &Self, item: T) -> %void {
-        const new_length = l.length + 1;
+        const new_length = l.len + 1;
         %return l.ensure_capacity(new_length);
-        l.items[l.length] = item;
-        l.length = new_length;
+        l.items[l.len] = item;
+        l.len = new_length;
+    }
+
+    pub fn resize(l: &Self, new_len: usize) -> %void {
+        %return l.ensure_capacity(new_len);
+        l.len = new_len;
     }
 
     pub fn ensure_capacity(l: &Self, new_capacity: usize) -> %void {
@@ -50,35 +58,10 @@ pub struct SmallList(T: type, STATIC_SIZE: usize) {
     }
 }
 
-var global_allocator = Allocator {
-    .alloc_fn = global_alloc,
-    .realloc_fn = global_realloc,
-    .free_fn = global_free,
-    .context = null,
-};
-
-var some_mem: [200]u8 = undefined;
-var some_mem_index: usize = 0;
-
-fn global_alloc(self: &Allocator, n: usize) -> %[]u8 {
-    const result = some_mem[some_mem_index ... some_mem_index + n];
-    some_mem_index += n;
-    return result;
-}
-
-fn global_realloc(self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 {
-    const result = %return global_alloc(self, new_size);
-    @memcpy(result.ptr, old_mem.ptr, old_mem.len);
-    return result;
-}
-
-fn global_free(self: &Allocator, old_mem: []u8) {
-}
-
 #attribute("test")
 fn basic_list_test() {
     var list: List(i32) = undefined;
-    list.init(&global_allocator);
+    list.init(&debug.global_allocator);
     defer list.deinit();
 
     {var i: usize = 0; while (i < 10; i += 1) {
std/math.zig
@@ -1,3 +1,9 @@
+pub enum Cmp {
+    Equal,
+    Greater,
+    Less,
+}
+
 pub fn f64_from_bits(bits: u64) -> f64 {
     *(&f64)(&bits)
 }
std/mem.zig
@@ -3,6 +3,8 @@ const math = @import("math.zig");
 const os = @import("os.zig");
 const io = @import("io.zig");
 
+pub const Cmp = math.Cmp;
+
 pub error NoMem;
 
 pub type Context = u8;
@@ -40,7 +42,20 @@ pub struct Allocator {
 
 /// Copy all of source into dest at position 0.
 /// dest.len must be >= source.len.
-pub fn copy(inline T: type, dest: []T, source: []T) {
+pub fn copy(inline T: type, dest: []T, source: []const T) {
     assert(dest.len >= source.len);
     @memcpy(dest.ptr, source.ptr, @sizeof(T) * source.len);
 }
+
+/// Return < 0, == 0, or > 0 if memory a is less than, equal to, or greater than,
+/// memory b, respectively.
+pub fn cmp(inline T: type, a: []const T, b: []const T) -> Cmp {
+    const n = math.min(usize, a.len, b.len);
+    var i: usize = 0;
+    while (i < n; i += 1) {
+        if (a[i] == b[i]) continue;
+        return if (a[i] > b[i]) Cmp.Greater else if (a[i] < b[i]) Cmp.Less else Cmp.Equal;
+    }
+
+    return if (a.len > b.len) Cmp.Greater else if (a.len < b.len) Cmp.Less else Cmp.Equal;
+}