| author | |
| committer | |
| log | ac630d354d51488d895bc14e26a069ae954ac5c6 |
| tree | ff99cc11334991a76d7ab0837112f00b8b1b0c3c |
| parent | a10277bd949d47370e427d4457d93e907de9a6f7 |
add explicit casting support from array to string8 files changed, 125 insertions(+), 52 deletions(-)
example/hello_world/hello.zig+5-8| ... | @@ -1,12 +1,9 @@ | ... | @@ -1,12 +1,9 @@ |
| 1 | export executable "hello"; | 1 | export executable "hello"; |
| 2 | 2 | ||
| 3 | #link("c") | 3 | use "std.zig"; |
| 4 | extern { | ||
| 5 | fn printf(__format: *const u8, ...) -> i32; | ||
| 6 | fn exit(__status: i32) -> unreachable; | ||
| 7 | } | ||
| 8 | 4 | ||
| 9 | export fn _start() -> unreachable { | 5 | export fn main(argc: isize, argv: *mut *mut u8, env: *mut *mut u8) -> i32 { |
| 10 | printf("Hello, world!\n"); | 6 | // TODO implicit coercion from array to string |
| 11 | exit(0); | 7 | print_str("Hello, world!\n" as string); |
| 8 | return 0; | ||
| 12 | } | 9 | } |
example/hello_world/hello2.zig deleted-8| ... | @@ -1,8 +0,0 @@ | ||
| 1 | export executable "hello"; | ||
| 2 | |||
| 3 | use "std.zig"; | ||
| 4 | |||
| 5 | export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { | ||
| 6 | print_str("Hello, world!\n"); | ||
| 7 | return 0; | ||
| 8 | } | ||
example/hello_world/hello_libc.zig created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | export executable "hello"; | ||
| 2 | |||
| 3 | #link("c") | ||
| 4 | extern { | ||
| 5 | fn printf(__format: *const u8, ...) -> i32; | ||
| 6 | fn exit(__status: i32) -> unreachable; | ||
| 7 | } | ||
| 8 | |||
| 9 | export fn _start() -> unreachable { | ||
| 10 | printf("Hello, world!\n"); | ||
| 11 | exit(0); | ||
| 12 | } | ||
src/analyze.cpp+25-7| ... | @@ -599,14 +599,17 @@ LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name) { | ... | @@ -599,14 +599,17 @@ LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name) { |
| 599 | } | 599 | } |
| 600 | } | 600 | } |
| 601 | 601 | ||
| 602 | static TypeStructField *get_struct_field(TypeTableEntry *struct_type, Buf *name) { | 602 | static void get_struct_field(TypeTableEntry *struct_type, Buf *name, TypeStructField **out_tsf, int *out_i) { |
| 603 | for (int i = 0; i < struct_type->data.structure.field_count; i += 1) { | 603 | for (int i = 0; i < struct_type->data.structure.field_count; i += 1) { |
| 604 | TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; | 604 | TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; |
| 605 | if (buf_eql_buf(type_struct_field->name, name)) { | 605 | if (buf_eql_buf(type_struct_field->name, name)) { |
| 606 | return type_struct_field; | 606 | *out_tsf = type_struct_field; |
| 607 | *out_i = i; | ||
| 608 | return; | ||
| 607 | } | 609 | } |
| 608 | } | 610 | } |
| 609 | return nullptr; | 611 | *out_tsf = nullptr; |
| 612 | *out_i = -1; | ||
| 610 | } | 613 | } |
| 611 | 614 | ||
| 612 | static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 615 | 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 | ... | @@ -618,10 +621,15 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i |
| 618 | TypeTableEntry *return_type; | 621 | TypeTableEntry *return_type; |
| 619 | 622 | ||
| 620 | if (struct_type->id == TypeTableEntryIdStruct) { | 623 | if (struct_type->id == TypeTableEntryIdStruct) { |
| 624 | FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node; | ||
| 625 | |||
| 621 | Buf *field_name = &node->data.field_access_expr.field_name; | 626 | Buf *field_name = &node->data.field_access_expr.field_name; |
| 622 | TypeStructField *type_struct_field = get_struct_field(struct_type, field_name); | 627 | |
| 623 | if (type_struct_field) { | 628 | get_struct_field(struct_type, field_name, |
| 624 | return_type = type_struct_field->type_entry; | 629 | &codegen_field_access->type_struct_field, |
| 630 | &codegen_field_access->field_index); | ||
| 631 | if (codegen_field_access->type_struct_field) { | ||
| 632 | return_type = codegen_field_access->type_struct_field->type_entry; | ||
| 625 | } else { | 633 | } else { |
| 626 | add_node_error(g, node, | 634 | add_node_error(g, node, |
| 627 | buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name))); | 635 | 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, | ... | @@ -1022,14 +1030,24 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 1022 | break; | 1030 | break; |
| 1023 | } | 1031 | } |
| 1024 | 1032 | ||
| 1033 | CastNode *cast_node = &node->codegen_node->data.cast_node; | ||
| 1034 | |||
| 1025 | // special casing this for now, TODO think about casting and do a general solution | 1035 | // special casing this for now, TODO think about casting and do a general solution |
| 1026 | if (wanted_type == g->builtin_types.entry_isize && | 1036 | if (wanted_type == g->builtin_types.entry_isize && |
| 1027 | actual_type->id == TypeTableEntryIdPointer) | 1037 | actual_type->id == TypeTableEntryIdPointer) |
| 1028 | { | 1038 | { |
| 1039 | cast_node->op = CastOpPtrToInt; | ||
| 1029 | return_type = wanted_type; | 1040 | return_type = wanted_type; |
| 1030 | } else if (wanted_type == g->builtin_types.entry_isize && | 1041 | } else if (wanted_type->id == TypeTableEntryIdInt && |
| 1031 | actual_type->id == TypeTableEntryIdInt) | 1042 | actual_type->id == TypeTableEntryIdInt) |
| 1032 | { | 1043 | { |
| 1044 | cast_node->op = CastOpIntWidenOrShorten; | ||
| 1045 | return_type = wanted_type; | ||
| 1046 | } else if (wanted_type == g->builtin_types.entry_string && | ||
| 1047 | actual_type->id == TypeTableEntryIdArray && | ||
| 1048 | actual_type->data.array.child_type == g->builtin_types.entry_u8) | ||
| 1049 | { | ||
| 1050 | cast_node->op = CastOpArrayToString; | ||
| 1033 | return_type = wanted_type; | 1051 | return_type = wanted_type; |
| 1034 | } else { | 1052 | } else { |
| 1035 | add_node_error(g, node, | 1053 | add_node_error(g, node, |
src/analyze.hpp+17| ... | @@ -231,6 +231,21 @@ struct StructDeclNode { | ... | @@ -231,6 +231,21 @@ struct StructDeclNode { |
| 231 | TypeTableEntry *type_entry; | 231 | TypeTableEntry *type_entry; |
| 232 | }; | 232 | }; |
| 233 | 233 | ||
| 234 | struct FieldAccessNode { | ||
| 235 | int field_index; | ||
| 236 | TypeStructField *type_struct_field; | ||
| 237 | }; | ||
| 238 | |||
| 239 | enum CastOp { | ||
| 240 | CastOpPtrToInt, | ||
| 241 | CastOpIntWidenOrShorten, | ||
| 242 | CastOpArrayToString, | ||
| 243 | }; | ||
| 244 | |||
| 245 | struct CastNode { | ||
| 246 | CastOp op; | ||
| 247 | }; | ||
| 248 | |||
| 234 | struct CodeGenNode { | 249 | struct CodeGenNode { |
| 235 | union { | 250 | union { |
| 236 | TypeNode type_node; // for NodeTypeType | 251 | TypeNode type_node; // for NodeTypeType |
| ... | @@ -240,6 +255,8 @@ struct CodeGenNode { | ... | @@ -240,6 +255,8 @@ struct CodeGenNode { |
| 240 | AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign | 255 | AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign |
| 241 | BlockNode block_node; // for NodeTypeBlock | 256 | BlockNode block_node; // for NodeTypeBlock |
| 242 | StructDeclNode struct_decl_node; // for NodeTypeStructDecl | 257 | StructDeclNode struct_decl_node; // for NodeTypeStructDecl |
| 258 | FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr | ||
| 259 | CastNode cast_node; // for NodeTypeCastExpr | ||
| 243 | } data; | 260 | } data; |
| 244 | ExprNode expr_node; // for all the expression nodes | 261 | ExprNode expr_node; // for all the expression nodes |
| 245 | }; | 262 | }; |
src/codegen.cpp+63-23| ... | @@ -197,6 +197,38 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { | ... | @@ -197,6 +197,38 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { |
| 197 | return LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, ""); | 197 | return LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, ""); |
| 198 | } | 198 | } |
| 199 | 199 | ||
| 200 | static LLVMValueRef gen_field_val(CodeGen *g, AstNode *node) { | ||
| 201 | assert(node->type == NodeTypeFieldAccessExpr); | ||
| 202 | |||
| 203 | LLVMValueRef struct_val = gen_expr(g, node->data.field_access_expr.struct_expr); | ||
| 204 | assert(struct_val); | ||
| 205 | |||
| 206 | FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node; | ||
| 207 | assert(codegen_field_access->field_index >= 0); | ||
| 208 | |||
| 209 | return LLVMBuildExtractValue(g->builder, struct_val, codegen_field_access->field_index, ""); | ||
| 210 | } | ||
| 211 | |||
| 212 | /* | ||
| 213 | static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node) { | ||
| 214 | assert(node->type == NodeTypeFieldAccessExpr); | ||
| 215 | |||
| 216 | LLVMValueRef struct_ptr = gen_expr(g, node->data.field_access_expr.struct_expr); | ||
| 217 | |||
| 218 | assert(struct_ptr); | ||
| 219 | |||
| 220 | FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node; | ||
| 221 | |||
| 222 | assert(codegen_field_access->field_index >= 0); | ||
| 223 | |||
| 224 | LLVMValueRef indices[] = { | ||
| 225 | LLVMConstInt(LLVMInt32Type(), 0, false), | ||
| 226 | LLVMConstInt(LLVMInt32Type(), codegen_field_access->field_index, false) | ||
| 227 | }; | ||
| 228 | return LLVMBuildStructGEP(g->builder, struct_ptr, indices, 2, ""); | ||
| 229 | } | ||
| 230 | */ | ||
| 231 | |||
| 200 | static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) { | 232 | static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) { |
| 201 | assert(node->type == NodeTypeArrayAccessExpr); | 233 | assert(node->type == NodeTypeArrayAccessExpr); |
| 202 | 234 | ||
| ... | @@ -208,12 +240,8 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) { | ... | @@ -208,12 +240,8 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) { |
| 208 | assert(node->type == NodeTypeFieldAccessExpr); | 240 | assert(node->type == NodeTypeFieldAccessExpr); |
| 209 | 241 | ||
| 210 | TypeTableEntry *struct_type = get_expr_type(node->data.field_access_expr.struct_expr); | 242 | TypeTableEntry *struct_type = get_expr_type(node->data.field_access_expr.struct_expr); |
| 211 | LLVMValueRef struct_ptr = gen_expr(g, node->data.field_access_expr.struct_expr); | ||
| 212 | Buf *name = &node->data.field_access_expr.field_name; | 243 | Buf *name = &node->data.field_access_expr.field_name; |
| 213 | 244 | ||
| 214 | // TODO add struct support | ||
| 215 | (void)struct_ptr; | ||
| 216 | |||
| 217 | if (struct_type->id == TypeTableEntryIdArray) { | 245 | if (struct_type->id == TypeTableEntryIdArray) { |
| 218 | if (buf_eql_str(name, "len")) { | 246 | if (buf_eql_str(name, "len")) { |
| 219 | return LLVMConstInt(g->builtin_types.entry_usize->type_ref, | 247 | return LLVMConstInt(g->builtin_types.entry_usize->type_ref, |
| ... | @@ -221,6 +249,12 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) { | ... | @@ -221,6 +249,12 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) { |
| 221 | } else { | 249 | } else { |
| 222 | zig_panic("gen_field_access_expr bad array field"); | 250 | zig_panic("gen_field_access_expr bad array field"); |
| 223 | } | 251 | } |
| 252 | } else if (struct_type->id == TypeTableEntryIdStruct) { | ||
| 253 | /* | ||
| 254 | LLVMValueRef ptr = gen_field_ptr(g, node); | ||
| 255 | return LLVMBuildLoad(g->builder, ptr, ""); | ||
| 256 | */ | ||
| 257 | return gen_field_val(g, node); | ||
| 224 | } else { | 258 | } else { |
| 225 | zig_panic("gen_field_access_expr bad struct type"); | 259 | zig_panic("gen_field_access_expr bad struct type"); |
| 226 | } | 260 | } |
| ... | @@ -259,30 +293,36 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { | ... | @@ -259,30 +293,36 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { |
| 259 | TypeTableEntry *actual_type = get_expr_type(node->data.cast_expr.expr); | 293 | TypeTableEntry *actual_type = get_expr_type(node->data.cast_expr.expr); |
| 260 | TypeTableEntry *wanted_type = get_expr_type(node); | 294 | TypeTableEntry *wanted_type = get_expr_type(node); |
| 261 | 295 | ||
| 262 | // this asserts are here only because no other casting codegen is supported currently | 296 | CastNode *cast_node = &node->codegen_node->data.cast_node; |
| 263 | assert(wanted_type == g->builtin_types.entry_isize); | 297 | |
| 264 | 298 | switch (cast_node->op) { | |
| 265 | if (wanted_type->id == TypeTableEntryIdPointer) { | 299 | case CastOpPtrToInt: |
| 266 | return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, ""); | 300 | return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); |
| 267 | } else if (wanted_type->id == TypeTableEntryIdInt) { | 301 | case CastOpIntWidenOrShorten: |
| 268 | if (actual_type->size_in_bits == wanted_type->size_in_bits) { | 302 | if (actual_type->size_in_bits == wanted_type->size_in_bits) { |
| 269 | if (actual_type->id == TypeTableEntryIdPointer) { | 303 | return expr_val; |
| 270 | return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); | 304 | } else if (actual_type->size_in_bits < wanted_type->size_in_bits) { |
| 305 | if (actual_type->data.integral.is_signed && wanted_type->data.integral.is_signed) { | ||
| 306 | return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, ""); | ||
| 307 | } else { | ||
| 308 | zig_panic("TODO gen_cast_expr sign mismatch"); | ||
| 309 | } | ||
| 271 | } else { | 310 | } else { |
| 272 | zig_panic("TODO gen_cast_expr"); | 311 | zig_panic("TODO gen_cast_expr"); |
| 273 | } | 312 | } |
| 274 | } else if (actual_type->size_in_bits < wanted_type->size_in_bits) { | 313 | case CastOpArrayToString: |
| 275 | if (actual_type->data.integral.is_signed && wanted_type->data.integral.is_signed) { | 314 | { |
| 276 | return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, ""); | 315 | LLVMValueRef struct_vals[] = { |
| 277 | } else { | 316 | expr_val, |
| 278 | zig_panic("TODO gen_cast_expr sign mismatch"); | 317 | LLVMConstInt(g->builtin_types.entry_usize->type_ref, actual_type->data.array.len, false) |
| 318 | }; | ||
| 319 | unsigned field_count = g->builtin_types.entry_string->data.structure.field_count; | ||
| 320 | assert(field_count == 2); | ||
| 321 | return LLVMConstNamedStruct(g->builtin_types.entry_string->type_ref, | ||
| 322 | struct_vals, field_count); | ||
| 279 | } | 323 | } |
| 280 | } else { | ||
| 281 | zig_panic("TODO gen_cast_expr"); | ||
| 282 | } | ||
| 283 | } else { | ||
| 284 | zig_panic("TODO gen_cast_expr"); | ||
| 285 | } | 324 | } |
| 325 | zig_unreachable(); | ||
| 286 | } | 326 | } |
| 287 | 327 | ||
| 288 | static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) { | 328 | static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) { |
std/std.zig+2-5| ... | @@ -14,14 +14,11 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { | ... | @@ -14,14 +14,11 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | // TODO error handling | 16 | // TODO error handling |
| 17 | // TODO zig strings instead of C strings | ||
| 18 | // TODO handle buffering and flushing | 17 | // TODO handle buffering and flushing |
| 19 | // TODO non-i32 integer literals so we can remove the casts | 18 | // TODO non-i32 integer literals so we can remove the casts |
| 20 | // TODO constants for SYS_write and stdout_fileno | 19 | // TODO constants for SYS_write and stdout_fileno |
| 21 | //pub fn print_str(str : string) -> isize { | 20 | pub fn print_str(str : string) -> isize { |
| 22 | pub fn print_str(str : *const u8, len: isize) -> isize { | ||
| 23 | let SYS_write = 1; | 21 | let SYS_write = 1; |
| 24 | let stdout_fileno = 1; | 22 | let stdout_fileno = 1; |
| 25 | //return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize); | 23 | return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize); |
| 26 | return syscall3(SYS_write as isize, stdout_fileno as isize, str as isize, len); | ||
| 27 | } | 24 | } |
test/run_tests.cpp+1-1| ... | @@ -403,7 +403,7 @@ loop_2_end: | ... | @@ -403,7 +403,7 @@ loop_2_end: |
| 403 | use "std.zig"; | 403 | use "std.zig"; |
| 404 | 404 | ||
| 405 | export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { | 405 | export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { |
| 406 | print_str(c"Hello, world!\n", 14 as isize); | 406 | print_str("Hello, world!\n" as string); |
| 407 | return 0; | 407 | return 0; |
| 408 | } | 408 | } |
| 409 | )SOURCE", "Hello, world!\n"); | 409 | )SOURCE", "Hello, world!\n"); |