Commit ac630d354d

Andrew Kelley <superjoe30@gmail.com>
2015-12-12 10:05:08
std: print_str no longer requires length argument
add explicit casting support from array to string
1 parent a10277b
example/hello_world/hello.zig
@@ -1,12 +1,9 @@
 export executable "hello";
 
-#link("c")
-extern {
-    fn printf(__format: *const u8, ...) -> i32;
-    fn exit(__status: i32) -> unreachable;
-}
+use "std.zig";
 
-export fn _start() -> unreachable {
-    printf("Hello, world!\n");
-    exit(0);
+export fn main(argc: isize, argv: *mut *mut u8, env: *mut *mut u8) -> i32 {
+    // TODO implicit coercion from array to string
+    print_str("Hello, world!\n" as string);
+    return 0;
 }
example/hello_world/hello2.zig
@@ -1,8 +0,0 @@
-export executable "hello";
-
-use "std.zig";
-
-export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 {
-    print_str("Hello, world!\n");
-    return 0;
-}
example/hello_world/hello_libc.zig
@@ -0,0 +1,12 @@
+export executable "hello";
+
+#link("c")
+extern {
+    fn printf(__format: *const u8, ...) -> i32;
+    fn exit(__status: i32) -> unreachable;
+}
+
+export fn _start() -> unreachable {
+    printf("Hello, world!\n");
+    exit(0);
+}
src/analyze.cpp
@@ -599,14 +599,17 @@ LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name) {
     }
 }
 
-static TypeStructField *get_struct_field(TypeTableEntry *struct_type, Buf *name) {
+static void get_struct_field(TypeTableEntry *struct_type, Buf *name, TypeStructField **out_tsf, int *out_i) {
     for (int i = 0; i < struct_type->data.structure.field_count; i += 1) {
         TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
         if (buf_eql_buf(type_struct_field->name, name)) {
-            return type_struct_field;
+            *out_tsf = type_struct_field;
+            *out_i = i;
+            return;
         }
     }
-    return nullptr;
+    *out_tsf = nullptr;
+    *out_i = -1;
 }
 
 static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
@@ -618,10 +621,15 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
     TypeTableEntry *return_type;
 
     if (struct_type->id == TypeTableEntryIdStruct) {
+        FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node;
+
         Buf *field_name = &node->data.field_access_expr.field_name;
-        TypeStructField *type_struct_field = get_struct_field(struct_type, field_name);
-        if (type_struct_field) {
-            return_type = type_struct_field->type_entry;
+
+        get_struct_field(struct_type, field_name,
+                &codegen_field_access->type_struct_field,
+                &codegen_field_access->field_index);
+        if (codegen_field_access->type_struct_field) {
+            return_type = codegen_field_access->type_struct_field->type_entry;
         } else {
             add_node_error(g, node,
                 buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name)));
@@ -1022,14 +1030,24 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                     break;
                 }
 
+                CastNode *cast_node = &node->codegen_node->data.cast_node;
+
                 // special casing this for now, TODO think about casting and do a general solution
                 if (wanted_type == g->builtin_types.entry_isize &&
                     actual_type->id == TypeTableEntryIdPointer)
                 {
+                    cast_node->op = CastOpPtrToInt;
                     return_type = wanted_type;
-                } else if (wanted_type == g->builtin_types.entry_isize &&
+                } else if (wanted_type->id == TypeTableEntryIdInt &&
                            actual_type->id == TypeTableEntryIdInt)
                 {
+                    cast_node->op = CastOpIntWidenOrShorten;
+                    return_type = wanted_type;
+                } else if (wanted_type == g->builtin_types.entry_string &&
+                           actual_type->id == TypeTableEntryIdArray &&
+                           actual_type->data.array.child_type == g->builtin_types.entry_u8)
+                {
+                    cast_node->op = CastOpArrayToString;
                     return_type = wanted_type;
                 } else {
                     add_node_error(g, node,
src/analyze.hpp
@@ -231,6 +231,21 @@ struct StructDeclNode {
     TypeTableEntry *type_entry;
 };
 
+struct FieldAccessNode {
+    int field_index;
+    TypeStructField *type_struct_field;
+};
+
+enum CastOp {
+    CastOpPtrToInt,
+    CastOpIntWidenOrShorten,
+    CastOpArrayToString,
+};
+
+struct CastNode {
+    CastOp op;
+};
+
 struct CodeGenNode {
     union {
         TypeNode type_node; // for NodeTypeType
@@ -240,6 +255,8 @@ struct CodeGenNode {
         AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign
         BlockNode block_node; // for NodeTypeBlock
         StructDeclNode struct_decl_node; // for NodeTypeStructDecl
+        FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr
+        CastNode cast_node; // for NodeTypeCastExpr
     } data;
     ExprNode expr_node; // for all the expression nodes
 };
src/codegen.cpp
@@ -197,6 +197,38 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {
     return LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, "");
 }
 
+static LLVMValueRef gen_field_val(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeFieldAccessExpr);
+
+    LLVMValueRef struct_val = gen_expr(g, node->data.field_access_expr.struct_expr);
+    assert(struct_val);
+
+    FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node;
+    assert(codegen_field_access->field_index >= 0);
+
+    return LLVMBuildExtractValue(g->builder, struct_val, codegen_field_access->field_index, "");
+}
+
+/*
+static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeFieldAccessExpr);
+
+    LLVMValueRef struct_ptr = gen_expr(g, node->data.field_access_expr.struct_expr);
+
+    assert(struct_ptr);
+
+    FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node;
+
+    assert(codegen_field_access->field_index >= 0);
+
+    LLVMValueRef indices[] = {
+        LLVMConstInt(LLVMInt32Type(), 0, false),
+        LLVMConstInt(LLVMInt32Type(), codegen_field_access->field_index, false)
+    };
+    return LLVMBuildStructGEP(g->builder, struct_ptr, indices, 2, "");
+}
+*/
+
 static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeArrayAccessExpr);
 
@@ -208,12 +240,8 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeFieldAccessExpr);
 
     TypeTableEntry *struct_type = get_expr_type(node->data.field_access_expr.struct_expr);
-    LLVMValueRef struct_ptr = gen_expr(g, node->data.field_access_expr.struct_expr);
     Buf *name = &node->data.field_access_expr.field_name;
 
-    // TODO add struct support
-    (void)struct_ptr;
-
     if (struct_type->id == TypeTableEntryIdArray) {
         if (buf_eql_str(name, "len")) {
             return LLVMConstInt(g->builtin_types.entry_usize->type_ref,
@@ -221,6 +249,12 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) {
         } else {
             zig_panic("gen_field_access_expr bad array field");
         }
+    } else if (struct_type->id == TypeTableEntryIdStruct) {
+        /*
+        LLVMValueRef ptr = gen_field_ptr(g, node);
+        return LLVMBuildLoad(g->builder, ptr, "");
+        */
+        return gen_field_val(g, node);
     } else {
         zig_panic("gen_field_access_expr bad struct type");
     }
@@ -259,30 +293,36 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
     TypeTableEntry *actual_type = get_expr_type(node->data.cast_expr.expr);
     TypeTableEntry *wanted_type = get_expr_type(node);
 
-    // this asserts are here only because no other casting codegen is supported currently
-    assert(wanted_type == g->builtin_types.entry_isize);
-
-    if (wanted_type->id == TypeTableEntryIdPointer) {
-        return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, "");
-    } else if (wanted_type->id == TypeTableEntryIdInt) {
-        if (actual_type->size_in_bits == wanted_type->size_in_bits) {
-            if (actual_type->id == TypeTableEntryIdPointer) {
-                return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
+    CastNode *cast_node = &node->codegen_node->data.cast_node;
+
+    switch (cast_node->op) {
+        case CastOpPtrToInt:
+            return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
+        case CastOpIntWidenOrShorten:
+            if (actual_type->size_in_bits == wanted_type->size_in_bits) {
+                return expr_val;
+            } else if (actual_type->size_in_bits < wanted_type->size_in_bits) {
+                if (actual_type->data.integral.is_signed && wanted_type->data.integral.is_signed) {
+                    return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, "");
+                } else {
+                    zig_panic("TODO gen_cast_expr sign mismatch");
+                }
             } else {
                 zig_panic("TODO gen_cast_expr");
             }
-        } else if (actual_type->size_in_bits < wanted_type->size_in_bits) {
-            if (actual_type->data.integral.is_signed && wanted_type->data.integral.is_signed) {
-                return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, "");
-            } else {
-                zig_panic("TODO gen_cast_expr sign mismatch");
+        case CastOpArrayToString:
+            {
+                LLVMValueRef struct_vals[] = {
+                    expr_val,
+                    LLVMConstInt(g->builtin_types.entry_usize->type_ref, actual_type->data.array.len, false)
+                };
+                unsigned field_count = g->builtin_types.entry_string->data.structure.field_count;
+                assert(field_count == 2);
+                return LLVMConstNamedStruct(g->builtin_types.entry_string->type_ref,
+                        struct_vals, field_count);
             }
-        } else {
-            zig_panic("TODO gen_cast_expr");
-        }
-    } else {
-        zig_panic("TODO gen_cast_expr");
     }
+    zig_unreachable();
 }
 
 static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) {
std/std.zig
@@ -14,14 +14,11 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize {
 }
 
 // TODO error handling
-// TODO zig strings instead of C strings
 // TODO handle buffering and flushing
 // TODO non-i32 integer literals so we can remove the casts
 // TODO constants for SYS_write and stdout_fileno
-//pub fn print_str(str : string) -> isize {
-pub fn print_str(str : *const u8, len: isize) -> isize {
+pub fn print_str(str : string) -> isize {
     let SYS_write = 1;
     let stdout_fileno = 1;
-    //return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize);
-    return syscall3(SYS_write as isize, stdout_fileno as isize, str as isize, len);
+    return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize);
 }
test/run_tests.cpp
@@ -403,7 +403,7 @@ loop_2_end:
 use "std.zig";
 
 export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 {
-    print_str(c"Hello, world!\n", 14 as isize);
+    print_str("Hello, world!\n" as string);
     return 0;
 }
     )SOURCE", "Hello, world!\n");