| author | |
| committer | |
| log | 0dbee2300ed28c18ecccaf71f10f68eb2da71266 |
| tree | ac804032eccb2f07258b45023739b694ee937d8c |
| parent | 3e8a98fa619516d0617a827a79601264a554d88b |
15 files changed, 383 insertions(+), 20 deletions(-)
CMakeLists.txt+6| ... | ... | @@ -115,7 +115,12 @@ set(C_HEADERS |
| 115 | 115 | "${CMAKE_SOURCE_DIR}/c_headers/xtestintrin.h" |
| 116 | 116 | ) |
| 117 | 117 | |
| 118 | set(ZIG_STD_SRC | |
| 119 | "${CMAKE_SOURCE_DIR}/std/bootstrap.zig" | |
| 120 | ) | |
| 121 | ||
| 118 | 122 | set(C_HEADERS_DEST "lib/zig/include") |
| 123 | set(ZIG_STD_DEST "lib/zig/std") | |
| 119 | 124 | set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h") |
| 120 | 125 | configure_file ( |
| 121 | 126 | "${CMAKE_SOURCE_DIR}/src/config.h.in" |
| ... | ... | @@ -142,6 +147,7 @@ target_link_libraries(zig LINK_PUBLIC |
| 142 | 147 | install(TARGETS zig DESTINATION bin) |
| 143 | 148 | |
| 144 | 149 | install(FILES ${C_HEADERS} DESTINATION ${C_HEADERS_DEST}) |
| 150 | install(FILES ${ZIG_STD_SRC} DESTINATION ${ZIG_STD_DEST}) | |
| 145 | 151 | |
| 146 | 152 | add_executable(run_tests ${TEST_SOURCES}) |
| 147 | 153 | target_link_libraries(run_tests) |
README.md+14-2| ... | ... | @@ -58,7 +58,6 @@ compromises backward compatibility. |
| 58 | 58 | * structs |
| 59 | 59 | * loops |
| 60 | 60 | * enums |
| 61 | * inline assembly and syscalls | |
| 62 | 61 | * conditional compilation and ability to check target platform and architecture |
| 63 | 62 | * main function with command line arguments |
| 64 | 63 | * void pointer constant |
| ... | ... | @@ -83,10 +82,23 @@ compromises backward compatibility. |
| 83 | 82 | |
| 84 | 83 | ## Building |
| 85 | 84 | |
| 85 | ### Debug / Development Build | |
| 86 | ||
| 86 | 87 | ``` |
| 87 | 88 | mkdir build |
| 88 | 89 | cd build |
| 89 | cmake .. | |
| 90 | cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) | |
| 90 | 91 | make |
| 92 | make install | |
| 91 | 93 | ./run_tests |
| 92 | 94 | ``` |
| 95 | ||
| 96 | ### Release / Install Build | |
| 97 | ||
| 98 | ``` | |
| 99 | mkdir build | |
| 100 | cd build | |
| 101 | cmake .. -DCMAKE_BUILD_TYPE=Release | |
| 102 | make | |
| 103 | sudo make install | |
| 104 | ``` |
doc/langref.md+9-1| ... | ... | @@ -70,7 +70,15 @@ VariableDeclaration : token(Let) option(token(Mut)) token(Symbol) (token(Eq) Exp |
| 70 | 70 | |
| 71 | 71 | Expression : BlockExpression | NonBlockExpression |
| 72 | 72 | |
| 73 | NonBlockExpression : ReturnExpression | AssignmentExpression | |
| 73 | NonBlockExpression : ReturnExpression | AssignmentExpression | AsmExpression | |
| 74 | ||
| 75 | AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen) | |
| 76 | ||
| 77 | AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput) | |
| 78 | ||
| 79 | AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers) | |
| 80 | ||
| 81 | AsmClobbers: token(Colon) list(token(String), token(Comma)) | |
| 74 | 82 | |
| 75 | 83 | AssignmentExpression : BoolOrExpression token(Equal) BoolOrExpression | BoolOrExpression |
| 76 | 84 |
doc/vim/syntax/zig.vim+2-2| ... | ... | @@ -7,8 +7,8 @@ if exists("b:current_syntax") |
| 7 | 7 | finish |
| 8 | 8 | endif |
| 9 | 9 | |
| 10 | syn keyword zigKeyword fn return mut const extern unreachable export pub as use while | |
| 11 | syn keyword zigKeyword if else let void goto type enum struct continue break match | |
| 10 | syn keyword zigKeyword fn return mut const extern unreachable export pub as use while asm | |
| 11 | syn keyword zigKeyword if else let void goto type enum struct continue break match volatile | |
| 12 | 12 | syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 |
| 13 | 13 | |
| 14 | 14 | syn keyword zigConstant null |
example/hello_world/hello2.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | export executable "hello"; | |
| 2 | ||
| 3 | #link("c") | |
| 4 | extern { | |
| 5 | fn printf(__format: *const u8, ...) -> i32; | |
| 6 | } | |
| 7 | ||
| 8 | export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { | |
| 9 | printf("argc = %zu\n", argc); | |
| 10 | return 0; | |
| 11 | } |
src/analyze.cpp+29-2| ... | ... | @@ -43,6 +43,7 @@ static AstNode *first_executing_node(AstNode *node) { |
| 43 | 43 | case NodeTypeIfExpr: |
| 44 | 44 | case NodeTypeLabel: |
| 45 | 45 | case NodeTypeGoto: |
| 46 | case NodeTypeAsmExpr: | |
| 46 | 47 | return node; |
| 47 | 48 | } |
| 48 | 49 | zig_panic("unreachable"); |
| ... | ... | @@ -205,8 +206,26 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 205 | 206 | for (int i = 0; i < node->data.fn_proto.directives->length; i += 1) { |
| 206 | 207 | AstNode *directive_node = node->data.fn_proto.directives->at(i); |
| 207 | 208 | Buf *name = &directive_node->data.directive.name; |
| 208 | add_node_error(g, directive_node, | |
| 209 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 209 | ||
| 210 | if (buf_eql_str(name, "attribute")) { | |
| 211 | Buf *attr_name = &directive_node->data.directive.param; | |
| 212 | if (fn_table_entry->fn_def_node) { | |
| 213 | if (buf_eql_str(attr_name, "naked")) { | |
| 214 | fn_table_entry->fn_attr_list.append(FnAttrIdNaked); | |
| 215 | } else if (buf_eql_str(attr_name, "alwaysinline")) { | |
| 216 | fn_table_entry->fn_attr_list.append(FnAttrIdAlwaysInline); | |
| 217 | } else { | |
| 218 | add_node_error(g, directive_node, | |
| 219 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | |
| 220 | } | |
| 221 | } else { | |
| 222 | add_node_error(g, directive_node, | |
| 223 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | |
| 224 | } | |
| 225 | } else { | |
| 226 | add_node_error(g, directive_node, | |
| 227 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 228 | } | |
| 210 | 229 | } |
| 211 | 230 | |
| 212 | 231 | for (int i = 0; i < node->data.fn_proto.params.length; i += 1) { |
| ... | ... | @@ -338,6 +357,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 338 | 357 | |
| 339 | 358 | resolve_function_proto(g, proto_node, fn_table_entry); |
| 340 | 359 | |
| 360 | ||
| 341 | 361 | assert(!proto_node->codegen_node); |
| 342 | 362 | proto_node->codegen_node = allocate<CodeGenNode>(1); |
| 343 | 363 | proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry; |
| ... | ... | @@ -415,6 +435,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 415 | 435 | case NodeTypeIfExpr: |
| 416 | 436 | case NodeTypeLabel: |
| 417 | 437 | case NodeTypeGoto: |
| 438 | case NodeTypeAsmExpr: | |
| 418 | 439 | zig_unreachable(); |
| 419 | 440 | } |
| 420 | 441 | } |
| ... | ... | @@ -626,6 +647,11 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 626 | 647 | return_type = g->builtin_types.entry_unreachable; |
| 627 | 648 | break; |
| 628 | 649 | } |
| 650 | case NodeTypeAsmExpr: | |
| 651 | { | |
| 652 | return_type = g->builtin_types.entry_void; | |
| 653 | break; | |
| 654 | } | |
| 629 | 655 | case NodeTypeBinOpExpr: |
| 630 | 656 | { |
| 631 | 657 | switch (node->data.bin_op_expr.bin_op) { |
| ... | ... | @@ -1004,6 +1030,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 1004 | 1030 | case NodeTypeIfExpr: |
| 1005 | 1031 | case NodeTypeLabel: |
| 1006 | 1032 | case NodeTypeGoto: |
| 1033 | case NodeTypeAsmExpr: | |
| 1007 | 1034 | zig_unreachable(); |
| 1008 | 1035 | } |
| 1009 | 1036 | } |
src/analyze.hpp+8| ... | ... | @@ -82,6 +82,11 @@ struct LabelTableEntry { |
| 82 | 82 | bool entered_from_fallthrough; |
| 83 | 83 | }; |
| 84 | 84 | |
| 85 | enum FnAttrId { | |
| 86 | FnAttrIdNaked, | |
| 87 | FnAttrIdAlwaysInline, | |
| 88 | }; | |
| 89 | ||
| 85 | 90 | struct FnTableEntry { |
| 86 | 91 | LLVMValueRef fn_value; |
| 87 | 92 | AstNode *proto_node; |
| ... | ... | @@ -90,6 +95,7 @@ struct FnTableEntry { |
| 90 | 95 | bool internal_linkage; |
| 91 | 96 | unsigned calling_convention; |
| 92 | 97 | ImportTableEntry *import_entry; |
| 98 | ZigList<FnAttrId> fn_attr_list; | |
| 93 | 99 | |
| 94 | 100 | // reminder: hash tables must be initialized before use |
| 95 | 101 | HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table; |
| ... | ... | @@ -113,6 +119,7 @@ struct CodeGen { |
| 113 | 119 | TypeTableEntry *entry_bool; |
| 114 | 120 | TypeTableEntry *entry_u8; |
| 115 | 121 | TypeTableEntry *entry_i32; |
| 122 | TypeTableEntry *entry_isize; | |
| 116 | 123 | TypeTableEntry *entry_f32; |
| 117 | 124 | TypeTableEntry *entry_string_literal; |
| 118 | 125 | TypeTableEntry *entry_void; |
| ... | ... | @@ -124,6 +131,7 @@ struct CodeGen { |
| 124 | 131 | unsigned pointer_size_bytes; |
| 125 | 132 | bool is_static; |
| 126 | 133 | bool strip_debug_symbols; |
| 134 | bool insert_bootstrap_code; | |
| 127 | 135 | CodeGenBuildType build_type; |
| 128 | 136 | LLVMTargetMachineRef target_machine; |
| 129 | 137 | bool is_native_target; |
src/codegen.cpp+101-12| ... | ... | @@ -609,6 +609,41 @@ static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *i |
| 609 | 609 | return return_value; |
| 610 | 610 | } |
| 611 | 611 | |
| 612 | static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { | |
| 613 | assert(node->type == NodeTypeAsmExpr); | |
| 614 | ||
| 615 | Buf *src_template = &node->data.asm_expr.asm_template; | |
| 616 | ||
| 617 | Buf llvm_template = BUF_INIT; | |
| 618 | buf_resize(&llvm_template, 0); | |
| 619 | ||
| 620 | for (int token_i = 0; token_i < node->data.asm_expr.token_list.length; token_i += 1) { | |
| 621 | AsmToken *asm_token = &node->data.asm_expr.token_list.at(token_i); | |
| 622 | switch (asm_token->id) { | |
| 623 | case AsmTokenIdTemplate: | |
| 624 | for (int offset = asm_token->start; offset < asm_token->end; offset += 1) { | |
| 625 | uint8_t c = *((uint8_t*)(buf_ptr(src_template) + offset)); | |
| 626 | if (c == '$') { | |
| 627 | buf_append_str(&llvm_template, "$$"); | |
| 628 | } else { | |
| 629 | buf_append_char(&llvm_template, c); | |
| 630 | } | |
| 631 | } | |
| 632 | break; | |
| 633 | case AsmTokenIdPercent: | |
| 634 | buf_append_char(&llvm_template, '%'); | |
| 635 | break; | |
| 636 | } | |
| 637 | } | |
| 638 | ||
| 639 | LLVMTypeRef function_type = LLVMFunctionType(LLVMVoidType(), nullptr, 0, false); | |
| 640 | ||
| 641 | LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template), "", true, false); | |
| 642 | ||
| 643 | add_debug_source_node(g, node); | |
| 644 | return LLVMBuildCall(g->builder, asm_fn, nullptr, 0, ""); | |
| 645 | } | |
| 646 | ||
| 612 | 647 | static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 613 | 648 | switch (node->type) { |
| 614 | 649 | case NodeTypeBinOpExpr: |
| ... | ... | @@ -663,6 +698,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 663 | 698 | return LLVMConstNull(LLVMInt1Type()); |
| 664 | 699 | case NodeTypeIfExpr: |
| 665 | 700 | return gen_if_expr(g, node); |
| 701 | case NodeTypeAsmExpr: | |
| 702 | return gen_asm_expr(g, node); | |
| 666 | 703 | case NodeTypeNumberLiteral: |
| 667 | 704 | { |
| 668 | 705 | Buf *number_str = &node->data.number; |
| ... | ... | @@ -762,6 +799,16 @@ static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnPro |
| 762 | 799 | return LLVMZigCreateSubroutineType(g->dbuilder, di_file, types, types_len, 0); |
| 763 | 800 | } |
| 764 | 801 | |
| 802 | static LLVMAttribute to_llvm_fn_attr(FnAttrId attr_id) { | |
| 803 | switch (attr_id) { | |
| 804 | case FnAttrIdNaked: | |
| 805 | return LLVMNakedAttribute; | |
| 806 | case FnAttrIdAlwaysInline: | |
| 807 | return LLVMAlwaysInlineAttribute; | |
| 808 | } | |
| 809 | zig_unreachable(); | |
| 810 | } | |
| 811 | ||
| 765 | 812 | static void do_code_gen(CodeGen *g) { |
| 766 | 813 | assert(!g->errors.length); |
| 767 | 814 | |
| ... | ... | @@ -789,6 +836,11 @@ static void do_code_gen(CodeGen *g) { |
| 789 | 836 | LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, param_count, fn_proto->is_var_args); |
| 790 | 837 | LLVMValueRef fn = LLVMAddFunction(g->module, buf_ptr(&fn_proto->name), function_type); |
| 791 | 838 | |
| 839 | for (int attr_i = 0; attr_i < fn_table_entry->fn_attr_list.length; attr_i += 1) { | |
| 840 | FnAttrId attr_id = fn_table_entry->fn_attr_list.at(attr_i); | |
| 841 | LLVMAddFunctionAttr(fn, to_llvm_fn_attr(attr_id)); | |
| 842 | } | |
| 843 | ||
| 792 | 844 | LLVMSetLinkage(fn, fn_table_entry->internal_linkage ? LLVMInternalLinkage : LLVMExternalLinkage); |
| 793 | 845 | |
| 794 | 846 | if (type_is_unreachable(g, fn_proto->return_type)) { |
| ... | ... | @@ -966,6 +1018,19 @@ static void define_primitive_types(CodeGen *g) { |
| 966 | 1018 | g->type_table.put(&entry->name, entry); |
| 967 | 1019 | g->builtin_types.entry_i32 = entry; |
| 968 | 1020 | } |
| 1021 | { | |
| 1022 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); | |
| 1023 | entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); | |
| 1024 | buf_init_from_str(&entry->name, "isize"); | |
| 1025 | entry->size_in_bits = g->pointer_size_bytes * 8; | |
| 1026 | entry->align_in_bits = g->pointer_size_bytes * 8; | |
| 1027 | entry->data.integral.is_signed = true; | |
| 1028 | entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), | |
| 1029 | entry->size_in_bits, entry->align_in_bits, | |
| 1030 | LLVMZigEncoding_DW_ATE_signed()); | |
| 1031 | g->type_table.put(&entry->name, entry); | |
| 1032 | g->builtin_types.entry_isize = entry; | |
| 1033 | } | |
| 969 | 1034 | { |
| 970 | 1035 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); |
| 971 | 1036 | entry->type_ref = LLVMFloatType(); |
| ... | ... | @@ -1121,20 +1186,30 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *source_path, Buf *sou |
| 1121 | 1186 | assert(import_entry->root->type == NodeTypeRoot); |
| 1122 | 1187 | for (int decl_i = 0; decl_i < import_entry->root->data.root.top_level_decls.length; decl_i += 1) { |
| 1123 | 1188 | AstNode *top_level_decl = import_entry->root->data.root.top_level_decls.at(decl_i); |
| 1124 | if (top_level_decl->type != NodeTypeUse) | |
| 1125 | continue; | |
| 1126 | 1189 | |
| 1127 | auto entry = g->import_table.maybe_get(&top_level_decl->data.use.path); | |
| 1128 | if (!entry) { | |
| 1129 | Buf full_path = BUF_INIT; | |
| 1130 | os_path_join(g->root_source_dir, &top_level_decl->data.use.path, &full_path); | |
| 1131 | Buf *import_code = buf_alloc(); | |
| 1132 | if ((err = os_fetch_file_path(&full_path, import_code))) { | |
| 1133 | add_node_error(g, top_level_decl, | |
| 1134 | buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); | |
| 1135 | break; | |
| 1190 | if (top_level_decl->type == NodeTypeUse) { | |
| 1191 | auto entry = g->import_table.maybe_get(&top_level_decl->data.use.path); | |
| 1192 | if (!entry) { | |
| 1193 | Buf full_path = BUF_INIT; | |
| 1194 | os_path_join(g->root_source_dir, &top_level_decl->data.use.path, &full_path); | |
| 1195 | Buf *import_code = buf_alloc(); | |
| 1196 | if ((err = os_fetch_file_path(&full_path, import_code))) { | |
| 1197 | add_node_error(g, top_level_decl, | |
| 1198 | buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); | |
| 1199 | break; | |
| 1200 | } | |
| 1201 | codegen_add_code(g, &top_level_decl->data.use.path, import_code); | |
| 1202 | } | |
| 1203 | } else if (top_level_decl->type == NodeTypeFnDef) { | |
| 1204 | AstNode *proto_node = top_level_decl->data.fn_def.fn_proto; | |
| 1205 | assert(proto_node->type == NodeTypeFnProto); | |
| 1206 | Buf *proto_name = &proto_node->data.fn_proto.name; | |
| 1207 | ||
| 1208 | bool is_exported = (proto_node->data.fn_proto.visib_mod == FnProtoVisibModExport); | |
| 1209 | ||
| 1210 | if (buf_eql_str(proto_name, "main") && is_exported) { | |
| 1211 | g->insert_bootstrap_code = true; | |
| 1136 | 1212 | } |
| 1137 | codegen_add_code(g, &top_level_decl->data.use.path, import_code); | |
| 1138 | 1213 | } |
| 1139 | 1214 | } |
| 1140 | 1215 | |
| ... | ... | @@ -1146,6 +1221,17 @@ void codegen_add_root_code(CodeGen *g, Buf *source_path, Buf *source_code) { |
| 1146 | 1221 | |
| 1147 | 1222 | g->root_import = codegen_add_code(g, source_path, source_code); |
| 1148 | 1223 | |
| 1224 | if (g->insert_bootstrap_code) { | |
| 1225 | Buf *path_to_bootstrap_src = buf_sprintf("%s/bootstrap.zig", ZIG_STD_DIR); | |
| 1226 | Buf *import_code = buf_alloc(); | |
| 1227 | int err; | |
| 1228 | if ((err = os_fetch_file_path(path_to_bootstrap_src, import_code))) { | |
| 1229 | zig_panic("unable to open '%s': %s", buf_ptr(path_to_bootstrap_src), err_str(err)); | |
| 1230 | } | |
| 1231 | ||
| 1232 | codegen_add_code(g, path_to_bootstrap_src, import_code); | |
| 1233 | } | |
| 1234 | ||
| 1149 | 1235 | if (g->verbose) { |
| 1150 | 1236 | fprintf(stderr, "\nSemantic Analysis:\n"); |
| 1151 | 1237 | fprintf(stderr, "--------------------\n"); |
| ... | ... | @@ -1185,6 +1271,9 @@ static void to_c_type(CodeGen *g, AstNode *type_node, Buf *out_buf) { |
| 1185 | 1271 | } else if (type_entry == g->builtin_types.entry_i32) { |
| 1186 | 1272 | g->c_stdint_used = true; |
| 1187 | 1273 | buf_init_from_str(out_buf, "int32_t"); |
| 1274 | } else if (type_entry == g->builtin_types.entry_isize) { | |
| 1275 | g->c_stdint_used = true; | |
| 1276 | buf_init_from_str(out_buf, "intptr_t"); | |
| 1188 | 1277 | } else if (type_entry == g->builtin_types.entry_f32) { |
| 1189 | 1278 | buf_init_from_str(out_buf, "float"); |
| 1190 | 1279 | } else if (type_entry == g->builtin_types.entry_unreachable) { |
src/config.h.in+1| ... | ... | @@ -7,5 +7,6 @@ |
| 7 | 7 | #define ZIG_VERSION_STRING "@ZIG_VERSION@" |
| 8 | 8 | |
| 9 | 9 | #define ZIG_HEADERS_DIR "@CMAKE_INSTALL_PREFIX@/@C_HEADERS_DEST@" |
| 10 | #define ZIG_STD_DIR "@CMAKE_INSTALL_PREFIX@/@ZIG_STD_DEST@" | |
| 10 | 11 | |
| 11 | 12 | #endif |
src/parser.cpp+118-1| ... | ... | @@ -104,6 +104,8 @@ const char *node_type_str(NodeType node_type) { |
| 104 | 104 | return "Label"; |
| 105 | 105 | case NodeTypeGoto: |
| 106 | 106 | return "Label"; |
| 107 | case NodeTypeAsmExpr: | |
| 108 | return "AsmExpr"; | |
| 107 | 109 | } |
| 108 | 110 | zig_unreachable(); |
| 109 | 111 | } |
| ... | ... | @@ -290,6 +292,9 @@ void ast_print(AstNode *node, int indent) { |
| 290 | 292 | case NodeTypeGoto: |
| 291 | 293 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.go_to.name)); |
| 292 | 294 | break; |
| 295 | case NodeTypeAsmExpr: | |
| 296 | fprintf(stderr, "%s\n", node_type_str(node->type)); | |
| 297 | break; | |
| 293 | 298 | } |
| 294 | 299 | } |
| 295 | 300 | |
| ... | ... | @@ -360,6 +365,71 @@ static void ast_buf_from_token(ParseContext *pc, Token *token, Buf *buf) { |
| 360 | 365 | buf_init_from_mem(buf, buf_ptr(pc->buf) + token->start_pos, token->end_pos - token->start_pos); |
| 361 | 366 | } |
| 362 | 367 | |
| 368 | static void parse_asm_template(ParseContext *pc, AstNode *node) { | |
| 369 | Buf *asm_template = &node->data.asm_expr.asm_template; | |
| 370 | ||
| 371 | enum State { | |
| 372 | StateStart, | |
| 373 | StatePercent, | |
| 374 | StateTemplate, | |
| 375 | }; | |
| 376 | ||
| 377 | ZigList<AsmToken> *tok_list = &node->data.asm_expr.token_list; | |
| 378 | assert(tok_list->length == 0); | |
| 379 | ||
| 380 | AsmToken *cur_tok = nullptr; | |
| 381 | ||
| 382 | enum State state = StateStart; | |
| 383 | ||
| 384 | for (int i = 0; i < buf_len(asm_template); i += 1) { | |
| 385 | uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i); | |
| 386 | switch (state) { | |
| 387 | case StateStart: | |
| 388 | if (c == '%') { | |
| 389 | tok_list->add_one(); | |
| 390 | cur_tok = &tok_list->last(); | |
| 391 | cur_tok->id = AsmTokenIdPercent; | |
| 392 | cur_tok->start = i; | |
| 393 | state = StatePercent; | |
| 394 | } else { | |
| 395 | tok_list->add_one(); | |
| 396 | cur_tok = &tok_list->last(); | |
| 397 | cur_tok->id = AsmTokenIdTemplate; | |
| 398 | cur_tok->start = i; | |
| 399 | state = StateTemplate; | |
| 400 | } | |
| 401 | break; | |
| 402 | case StatePercent: | |
| 403 | if (c == '%') { | |
| 404 | cur_tok->end = i; | |
| 405 | state = StateStart; | |
| 406 | } else { | |
| 407 | zig_panic("TODO handle assembly tokenize error"); | |
| 408 | } | |
| 409 | break; | |
| 410 | case StateTemplate: | |
| 411 | if (c == '%') { | |
| 412 | cur_tok->end = i; | |
| 413 | i -= 1; | |
| 414 | cur_tok = nullptr; | |
| 415 | state = StateStart; | |
| 416 | } | |
| 417 | break; | |
| 418 | } | |
| 419 | } | |
| 420 | ||
| 421 | switch (state) { | |
| 422 | case StateStart: | |
| 423 | break; | |
| 424 | case StatePercent: | |
| 425 | zig_panic("TODO handle assembly tokenize error eof"); | |
| 426 | break; | |
| 427 | case StateTemplate: | |
| 428 | cur_tok->end = buf_len(asm_template); | |
| 429 | break; | |
| 430 | } | |
| 431 | } | |
| 432 | ||
| 363 | 433 | static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf) { |
| 364 | 434 | // skip the double quotes at beginning and end |
| 365 | 435 | // convert escape sequences |
| ... | ... | @@ -1264,7 +1334,50 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand |
| 1264 | 1334 | } |
| 1265 | 1335 | |
| 1266 | 1336 | /* |
| 1267 | NonBlockExpression : ReturnExpression | AssignmentExpression | |
| 1337 | AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen) | |
| 1338 | */ | |
| 1339 | static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mandatory) { | |
| 1340 | Token *asm_token = &pc->tokens->at(*token_index); | |
| 1341 | ||
| 1342 | if (asm_token->id != TokenIdKeywordAsm) { | |
| 1343 | if (mandatory) { | |
| 1344 | ast_invalid_token_error(pc, asm_token); | |
| 1345 | } else { | |
| 1346 | return nullptr; | |
| 1347 | } | |
| 1348 | } | |
| 1349 | ||
| 1350 | AstNode *node = ast_create_node(pc, NodeTypeAsmExpr, asm_token); | |
| 1351 | ||
| 1352 | *token_index += 1; | |
| 1353 | Token *lparen_tok = &pc->tokens->at(*token_index); | |
| 1354 | ||
| 1355 | if (lparen_tok->id == TokenIdKeywordVolatile) { | |
| 1356 | node->data.asm_expr.is_volatile = true; | |
| 1357 | ||
| 1358 | *token_index += 1; | |
| 1359 | lparen_tok = &pc->tokens->at(*token_index); | |
| 1360 | } | |
| 1361 | ||
| 1362 | ast_expect_token(pc, lparen_tok, TokenIdLParen); | |
| 1363 | *token_index += 1; | |
| 1364 | ||
| 1365 | Token *template_tok = &pc->tokens->at(*token_index); | |
| 1366 | ast_expect_token(pc, template_tok, TokenIdStringLiteral); | |
| 1367 | *token_index += 1; | |
| 1368 | ||
| 1369 | parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template); | |
| 1370 | parse_asm_template(pc, node); | |
| 1371 | ||
| 1372 | Token *rparen_tok = &pc->tokens->at(*token_index); | |
| 1373 | ast_expect_token(pc, rparen_tok, TokenIdRParen); | |
| 1374 | *token_index += 1; | |
| 1375 | ||
| 1376 | return node; | |
| 1377 | } | |
| 1378 | ||
| 1379 | /* | |
| 1380 | NonBlockExpression : ReturnExpression | AssignmentExpression | AsmExpression | |
| 1268 | 1381 | */ |
| 1269 | 1382 | static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 1270 | 1383 | Token *token = &pc->tokens->at(*token_index); |
| ... | ... | @@ -1277,6 +1390,10 @@ static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, boo |
| 1277 | 1390 | if (ass_expr) |
| 1278 | 1391 | return ass_expr; |
| 1279 | 1392 | |
| 1393 | AstNode *asm_expr = ast_parse_asm_expr(pc, token_index, false); | |
| 1394 | if (asm_expr) | |
| 1395 | return asm_expr; | |
| 1396 | ||
| 1280 | 1397 | if (mandatory) |
| 1281 | 1398 | ast_invalid_token_error(pc, token); |
| 1282 | 1399 |
src/parser.hpp+20| ... | ... | @@ -16,6 +16,7 @@ |
| 16 | 16 | struct AstNode; |
| 17 | 17 | struct CodeGenNode; |
| 18 | 18 | struct ImportTableEntry; |
| 19 | struct AsmToken; | |
| 19 | 20 | |
| 20 | 21 | enum NodeType { |
| 21 | 22 | NodeTypeRoot, |
| ... | ... | @@ -45,6 +46,7 @@ enum NodeType { |
| 45 | 46 | NodeTypeIfExpr, |
| 46 | 47 | NodeTypeLabel, |
| 47 | 48 | NodeTypeGoto, |
| 49 | NodeTypeAsmExpr, | |
| 48 | 50 | }; |
| 49 | 51 | |
| 50 | 52 | struct AstNodeRoot { |
| ... | ... | @@ -203,6 +205,12 @@ struct AstNodeGoto { |
| 203 | 205 | Buf name; |
| 204 | 206 | }; |
| 205 | 207 | |
| 208 | struct AstNodeAsmExpr { | |
| 209 | bool is_volatile; | |
| 210 | Buf asm_template; | |
| 211 | ZigList<AsmToken> token_list; | |
| 212 | }; | |
| 213 | ||
| 206 | 214 | struct AstNode { |
| 207 | 215 | enum NodeType type; |
| 208 | 216 | int line; |
| ... | ... | @@ -231,6 +239,7 @@ struct AstNode { |
| 231 | 239 | AstNodeIfExpr if_expr; |
| 232 | 240 | AstNodeLabel label; |
| 233 | 241 | AstNodeGoto go_to; |
| 242 | AstNodeAsmExpr asm_expr; | |
| 234 | 243 | Buf number; |
| 235 | 244 | Buf string; |
| 236 | 245 | Buf symbol; |
| ... | ... | @@ -238,6 +247,17 @@ struct AstNode { |
| 238 | 247 | } data; |
| 239 | 248 | }; |
| 240 | 249 | |
| 250 | enum AsmTokenId { | |
| 251 | AsmTokenIdTemplate, | |
| 252 | AsmTokenIdPercent, | |
| 253 | }; | |
| 254 | ||
| 255 | struct AsmToken { | |
| 256 | enum AsmTokenId id; | |
| 257 | int start; | |
| 258 | int end; | |
| 259 | }; | |
| 260 | ||
| 241 | 261 | __attribute__ ((format (printf, 2, 3))) |
| 242 | 262 | void ast_token_error(Token *token, const char *format, ...); |
| 243 | 263 |
src/tokenizer.cpp+43| ... | ... | @@ -197,6 +197,10 @@ static void end_token(Tokenize *t) { |
| 197 | 197 | t->cur_tok->id = TokenIdKeywordElse; |
| 198 | 198 | } else if (mem_eql_str(token_mem, token_len, "goto")) { |
| 199 | 199 | t->cur_tok->id = TokenIdKeywordGoto; |
| 200 | } else if (mem_eql_str(token_mem, token_len, "volatile")) { | |
| 201 | t->cur_tok->id = TokenIdKeywordVolatile; | |
| 202 | } else if (mem_eql_str(token_mem, token_len, "asm")) { | |
| 203 | t->cur_tok->id = TokenIdKeywordAsm; | |
| 200 | 204 | } |
| 201 | 205 | |
| 202 | 206 | t->cur_tok = nullptr; |
| ... | ... | @@ -637,6 +641,8 @@ static const char * token_name(Token *token) { |
| 637 | 641 | case TokenIdKeywordIf: return "If"; |
| 638 | 642 | case TokenIdKeywordElse: return "Else"; |
| 639 | 643 | case TokenIdKeywordGoto: return "Goto"; |
| 644 | case TokenIdKeywordVolatile: return "Volatile"; | |
| 645 | case TokenIdKeywordAsm: return "Asm"; | |
| 640 | 646 | case TokenIdLParen: return "LParen"; |
| 641 | 647 | case TokenIdRParen: return "RParen"; |
| 642 | 648 | case TokenIdComma: return "Comma"; |
| ... | ... | @@ -687,3 +693,40 @@ void print_tokens(Buf *buf, ZigList<Token> *tokens) { |
| 687 | 693 | fprintf(stderr, "\n"); |
| 688 | 694 | } |
| 689 | 695 | } |
| 696 | ||
| 697 | bool is_printable(uint8_t c) { | |
| 698 | switch (c) { | |
| 699 | default: | |
| 700 | return false; | |
| 701 | case DIGIT: | |
| 702 | case ALPHA: | |
| 703 | case '!': | |
| 704 | case '#': | |
| 705 | case '$': | |
| 706 | case '%': | |
| 707 | case '&': | |
| 708 | case '\'': | |
| 709 | case '(': | |
| 710 | case ')': | |
| 711 | case '*': | |
| 712 | case '+': | |
| 713 | case ',': | |
| 714 | case '-': | |
| 715 | case '.': | |
| 716 | case '/': | |
| 717 | case ':': | |
| 718 | case ';': | |
| 719 | case '<': | |
| 720 | case '=': | |
| 721 | case '>': | |
| 722 | case '?': | |
| 723 | case '@': | |
| 724 | case '^': | |
| 725 | case '_': | |
| 726 | case '`': | |
| 727 | case '~': | |
| 728 | case ' ': | |
| 729 | return true; | |
| 730 | } | |
| 731 | } | |
| 732 |
src/tokenizer.hpp+4| ... | ... | @@ -30,6 +30,8 @@ enum TokenId { |
| 30 | 30 | TokenIdKeywordIf, |
| 31 | 31 | TokenIdKeywordElse, |
| 32 | 32 | TokenIdKeywordGoto, |
| 33 | TokenIdKeywordAsm, | |
| 34 | TokenIdKeywordVolatile, | |
| 33 | 35 | TokenIdLParen, |
| 34 | 36 | TokenIdRParen, |
| 35 | 37 | TokenIdComma, |
| ... | ... | @@ -90,4 +92,6 @@ void tokenize(Buf *buf, Tokenization *out_tokenization); |
| 90 | 92 | |
| 91 | 93 | void print_tokens(Buf *buf, ZigList<Token> *tokens); |
| 92 | 94 | |
| 95 | bool is_printable(uint8_t c); | |
| 96 | ||
| 93 | 97 | #endif |
std/bootstrap.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | ||
| 2 | // TODO conditionally compile this differently for non-ELF | |
| 3 | #attribute("naked") | |
| 4 | export fn _start() -> unreachable { | |
| 5 | // TODO conditionally compile this differently for other architectures and other OSes | |
| 6 | asm volatile (" | |
| 7 | mov (%%rsp), %%rdi // first parameter is argc | |
| 8 | lea 0x8(%%rsp), %%rsi // second parameter is argv | |
| 9 | lea 0x10(%%rsp,%%rdi,8), %%rdx // third paremeter is env | |
| 10 | callq main | |
| 11 | mov %%rax, %%rdi // return value is the parameter to exit syscall | |
| 12 | mov $60, %%rax // 60 is exit syscall number | |
| 13 | syscall | |
| 14 | "); | |
| 15 | unreachable | |
| 16 | } |
test/run_tests.cpp+1| ... | ... | @@ -397,6 +397,7 @@ loop_2_end: |
| 397 | 397 | exit(0); |
| 398 | 398 | } |
| 399 | 399 | )SOURCE", "OK\n"); |
| 400 | ||
| 400 | 401 | } |
| 401 | 402 | |
| 402 | 403 | static void add_compile_failure_test_cases(void) { |