| author | |
| committer | |
| log | 0278468479d52d6ea64521ab78574b593a6832ae |
| tree | d7438d5c45de76b453efa7d3fab5a33ea27db347 |
| parent | ac085a869d068ca60548ad5298682ff054e91141 |
c_import creates a tmp .h file and parses it with libclang,
reporting any errors found.
See #8816 files changed, 373 insertions(+), 684 deletions(-)
CMakeLists.txt+2-2| ... | ... | @@ -17,7 +17,7 @@ include_directories(${LLVM_INCLUDE_DIRS}) |
| 17 | 17 | link_directories(${LLVM_LIBDIRS}) |
| 18 | 18 | |
| 19 | 19 | find_package(clang) |
| 20 | include_directories(${CLANG_INCLUDE_DIR}) | |
| 20 | include_directories(${CLANG_INCLUDE_DIRS}) | |
| 21 | 21 | |
| 22 | 22 | include_directories( |
| 23 | 23 | ${CMAKE_SOURCE_DIR} |
| ... | ... | @@ -148,7 +148,7 @@ set_target_properties(zig PROPERTIES |
| 148 | 148 | COMPILE_FLAGS ${EXE_CFLAGS}) |
| 149 | 149 | target_link_libraries(zig LINK_PUBLIC |
| 150 | 150 | ${LLVM_LIBRARIES} |
| 151 | ${CLANG_LIBRARY} | |
| 151 | ${CLANG_LIBRARIES} | |
| 152 | 152 | ) |
| 153 | 153 | install(TARGETS zig DESTINATION bin) |
| 154 | 154 |
README.md+1-1| ... | ... | @@ -75,7 +75,7 @@ be set to (example below). |
| 75 | 75 | ``` |
| 76 | 76 | mkdir build |
| 77 | 77 | cd build |
| 78 | cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_DIR=$(dirname $(cc -print-file-name=crt1.o)) | |
| 78 | cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_DIR=$(dirname $(dirname $(cc -print-file-name=crt1.o))) | |
| 79 | 79 | make |
| 80 | 80 | make install |
| 81 | 81 | ./run_tests |
cmake/Findclang.cmake+25-7| ... | ... | @@ -1,16 +1,34 @@ |
| 1 | # Copyright (c) 2015 Andrew Kelley | |
| 1 | # Copyright (c) 2016 Andrew Kelley | |
| 2 | 2 | # This file is MIT licensed. |
| 3 | 3 | # See http://opensource.org/licenses/MIT |
| 4 | 4 | |
| 5 | 5 | # CLANG_FOUND |
| 6 | # CLANG_INCLUDE_DIR | |
| 7 | # CLANG_LIBRARY | |
| 6 | # CLANG_INCLUDE_DIRS | |
| 7 | # CLANG_LIBRARIES | |
| 8 | 8 | |
| 9 | find_path(CLANG_INCLUDE_DIR NAMES clang-c/Index.h PATHS /usr/lib/llvm-3.7/include/) | |
| 9 | find_path(CLANG_INCLUDE_DIRS NAMES clang/Frontend/ASTUnit.h) | |
| 10 | 10 | |
| 11 | find_library(CLANG_LIBRARY NAMES clang PATHS /usr/lib/llvm-3.7/lib/) | |
| 11 | macro(FIND_AND_ADD_CLANG_LIB _libname_) | |
| 12 | string(TOUPPER ${_libname_} _prettylibname_) | |
| 13 | find_library(CLANG_${_prettylibname_}_LIB NAMES ${_libname_}) | |
| 14 | if(CLANG_${_prettylibname_}_LIB) | |
| 15 | set(CLANG_LIBRARIES ${CLANG_LIBRARIES} ${CLANG_${_prettylibname_}_LIB}) | |
| 16 | endif() | |
| 17 | endmacro(FIND_AND_ADD_CLANG_LIB) | |
| 18 | ||
| 19 | FIND_AND_ADD_CLANG_LIB(clangFrontend) | |
| 20 | FIND_AND_ADD_CLANG_LIB(clangDriver) | |
| 21 | FIND_AND_ADD_CLANG_LIB(clangSerialization) | |
| 22 | FIND_AND_ADD_CLANG_LIB(clangSema) | |
| 23 | FIND_AND_ADD_CLANG_LIB(clangAnalysis) | |
| 24 | FIND_AND_ADD_CLANG_LIB(clangAST) | |
| 25 | FIND_AND_ADD_CLANG_LIB(clangParse) | |
| 26 | FIND_AND_ADD_CLANG_LIB(clangSema) | |
| 27 | FIND_AND_ADD_CLANG_LIB(clangBasic) | |
| 28 | FIND_AND_ADD_CLANG_LIB(clangEdit) | |
| 29 | FIND_AND_ADD_CLANG_LIB(clangLex) | |
| 12 | 30 | |
| 13 | 31 | include(FindPackageHandleStandardArgs) |
| 14 | find_package_handle_standard_args(CLANG DEFAULT_MSG CLANG_LIBRARY CLANG_INCLUDE_DIR) | |
| 32 | find_package_handle_standard_args(CLANG DEFAULT_MSG CLANG_LIBRARIES CLANG_INCLUDE_DIRS) | |
| 15 | 33 | |
| 16 | mark_as_advanced(CLANG_INCLUDE_DIR CLANG_LIBRARY) | |
| 34 | mark_as_advanced(CLANG_INCLUDE_DIRS CLANG_LIBRARIES) |
src/all_types.hpp+10| ... | ... | @@ -995,6 +995,8 @@ struct CodeGen { |
| 995 | 995 | bool have_exported_main; |
| 996 | 996 | bool link_libc; |
| 997 | 997 | Buf *libc_path; |
| 998 | Buf *libc_lib_path; | |
| 999 | Buf *libc_include_path; | |
| 998 | 1000 | CodeGenBuildType build_type; |
| 999 | 1001 | LLVMTargetMachineRef target_machine; |
| 1000 | 1002 | LLVMZigDIFile *dummy_di_file; |
| ... | ... | @@ -1068,4 +1070,12 @@ struct BlockContext { |
| 1068 | 1070 | Buf *c_import_buf; |
| 1069 | 1071 | }; |
| 1070 | 1072 | |
| 1073 | struct ParseH { | |
| 1074 | ZigList<ErrorMsg*> errors; | |
| 1075 | ZigList<AstNode *> fn_list; | |
| 1076 | ZigList<AstNode *> struct_list; | |
| 1077 | ZigList<AstNode *> var_list; | |
| 1078 | ZigList<AstNode *> incomplete_struct_list; | |
| 1079 | }; | |
| 1080 | ||
| 1071 | 1081 | #endif |
src/analyze.cpp+43-14| ... | ... | @@ -10,6 +10,8 @@ |
| 10 | 10 | #include "error.hpp" |
| 11 | 11 | #include "zig_llvm.hpp" |
| 12 | 12 | #include "os.hpp" |
| 13 | #include "parseh.hpp" | |
| 14 | #include "config.h" | |
| 13 | 15 | |
| 14 | 16 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 15 | 17 | TypeTableEntry *expected_type, AstNode *node); |
| ... | ... | @@ -83,18 +85,12 @@ static AstNode *first_executing_node(AstNode *node) { |
| 83 | 85 | zig_unreachable(); |
| 84 | 86 | } |
| 85 | 87 | |
| 86 | void add_node_error(CodeGen *g, AstNode *node, Buf *msg) { | |
| 87 | ErrorMsg *err = allocate<ErrorMsg>(1); | |
| 88 | err->line_start = node->line; | |
| 89 | err->column_start = node->column; | |
| 90 | err->line_end = -1; | |
| 91 | err->column_end = -1; | |
| 92 | err->msg = msg; | |
| 93 | err->path = node->owner->path; | |
| 94 | err->source = node->owner->source_code; | |
| 95 | err->line_offsets = node->owner->line_offsets; | |
| 88 | ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { | |
| 89 | ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, | |
| 90 | node->owner->source_code, node->owner->line_offsets, msg); | |
| 96 | 91 | |
| 97 | 92 | g->errors.append(err); |
| 93 | return err; | |
| 98 | 94 | } |
| 99 | 95 | |
| 100 | 96 | TypeTableEntry *new_type_table_entry(TypeTableEntryId id) { |
| ... | ... | @@ -1036,8 +1032,22 @@ static void resolve_c_import_decl(CodeGen *g, ImportTableEntry *import, AstNode |
| 1036 | 1032 | return; |
| 1037 | 1033 | } |
| 1038 | 1034 | |
| 1039 | fprintf(stderr, "c import buf:\n%s\n", buf_ptr(child_context->c_import_buf)); | |
| 1040 | zig_panic("TODO"); | |
| 1035 | find_libc_path(g); | |
| 1036 | int err; | |
| 1037 | ParseH parse_h = {{0}}; | |
| 1038 | if ((err = parse_h_buf(&parse_h, child_context->c_import_buf, buf_ptr(g->libc_include_path)))) { | |
| 1039 | zig_panic("unable to parse h file: %s\n", err_str(err)); | |
| 1040 | } | |
| 1041 | ||
| 1042 | if (parse_h.errors.length > 0) { | |
| 1043 | ErrorMsg *parent_err_msg = add_node_error(g, node, buf_sprintf("C import failed")); | |
| 1044 | for (int i = 0; i < parse_h.errors.length; i += 1) { | |
| 1045 | ErrorMsg *err_msg = parse_h.errors.at(i); | |
| 1046 | err_msg_add_note(parent_err_msg, err_msg); | |
| 1047 | } | |
| 1048 | } else { | |
| 1049 | zig_panic("TODO integrate the parsed AST"); | |
| 1050 | } | |
| 1041 | 1051 | } |
| 1042 | 1052 | |
| 1043 | 1053 | static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| ... | ... | @@ -3444,7 +3454,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 3444 | 3454 | return g->builtin_types.entry_void; |
| 3445 | 3455 | } |
| 3446 | 3456 | |
| 3447 | buf_appendf(context->c_import_buf, "#include \""); | |
| 3457 | buf_appendf(context->c_import_buf, "#include <"); | |
| 3448 | 3458 | ConstExprValue *ptr_field = const_str_val->data.x_struct.fields[0]; |
| 3449 | 3459 | uint64_t len = ptr_field->data.x_ptr.len; |
| 3450 | 3460 | for (uint64_t i = 0; i < len; i += 1) { |
| ... | ... | @@ -3454,7 +3464,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 3454 | 3464 | uint8_t c = big_c; |
| 3455 | 3465 | buf_appendf(context->c_import_buf, "%c", c); |
| 3456 | 3466 | } |
| 3457 | buf_appendf(context->c_import_buf, "\"\n"); | |
| 3467 | buf_appendf(context->c_import_buf, ">\n"); | |
| 3458 | 3468 | |
| 3459 | 3469 | return g->builtin_types.entry_void; |
| 3460 | 3470 | } |
| ... | ... | @@ -4976,3 +4986,22 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { |
| 4976 | 4986 | } |
| 4977 | 4987 | zig_unreachable(); |
| 4978 | 4988 | } |
| 4989 | ||
| 4990 | void find_libc_path(CodeGen *g) { | |
| 4991 | if (!g->libc_path || buf_len(g->libc_path) == 0) { | |
| 4992 | g->libc_path = buf_create_from_str(ZIG_LIBC_DIR); | |
| 4993 | if (!g->libc_path || buf_len(g->libc_path) == 0) { | |
| 4994 | // later we can handle this better by reporting an error via the normal mechanism | |
| 4995 | zig_panic("Unable to determine libc path. You can use `--libc-path`"); | |
| 4996 | } | |
| 4997 | } | |
| 4998 | if (!g->libc_lib_path) { | |
| 4999 | g->libc_lib_path = buf_alloc(); | |
| 5000 | os_path_join(g->libc_path, buf_create_from_str("lib"), g->libc_lib_path); | |
| 5001 | } | |
| 5002 | if (!g->libc_include_path) { | |
| 5003 | g->libc_include_path = buf_alloc(); | |
| 5004 | os_path_join(g->libc_path, buf_create_from_str("include"), g->libc_include_path); | |
| 5005 | } | |
| 5006 | } | |
| 5007 |
src/analyze.hpp+3-1| ... | ... | @@ -11,7 +11,7 @@ |
| 11 | 11 | #include "all_types.hpp" |
| 12 | 12 | |
| 13 | 13 | void semantic_analyze(CodeGen *g); |
| 14 | void add_node_error(CodeGen *g, AstNode *node, Buf *msg); | |
| 14 | ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg); | |
| 15 | 15 | TypeTableEntry *new_type_table_entry(TypeTableEntryId id); |
| 16 | 16 | TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const); |
| 17 | 17 | VariableTableEntry *find_variable(BlockContext *context, Buf *name); |
| ... | ... | @@ -23,4 +23,6 @@ bool is_node_void_expr(AstNode *node); |
| 23 | 23 | TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits); |
| 24 | 24 | TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits); |
| 25 | 25 | bool handle_is_ptr(TypeTableEntry *type_entry); |
| 26 | void find_libc_path(CodeGen *g); | |
| 27 | ||
| 26 | 28 | #endif |
src/codegen.cpp+3-20| ... | ... | @@ -3051,15 +3051,8 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path, |
| 3051 | 3051 | tokenize(source_code, &tokenization); |
| 3052 | 3052 | |
| 3053 | 3053 | if (tokenization.err) { |
| 3054 | ErrorMsg *err = allocate<ErrorMsg>(1); | |
| 3055 | err->line_start = tokenization.err_line; | |
| 3056 | err->column_start = tokenization.err_column; | |
| 3057 | err->line_end = -1; | |
| 3058 | err->column_end = -1; | |
| 3059 | err->msg = tokenization.err; | |
| 3060 | err->path = full_path; | |
| 3061 | err->source = source_code; | |
| 3062 | err->line_offsets = tokenization.line_offsets; | |
| 3054 | ErrorMsg *err = err_msg_create_with_line(full_path, tokenization.err_line, tokenization.err_column, | |
| 3055 | source_code, tokenization.line_offsets, tokenization.err); | |
| 3063 | 3056 | |
| 3064 | 3057 | print_err_msg(err, g->err_color); |
| 3065 | 3058 | exit(1); |
| ... | ... | @@ -3401,19 +3394,9 @@ static void generate_h_file(CodeGen *g) { |
| 3401 | 3394 | zig_panic("unable to close h file: %s", strerror(errno)); |
| 3402 | 3395 | } |
| 3403 | 3396 | |
| 3404 | static void find_libc_path(CodeGen *g) { | |
| 3405 | if (g->libc_path && buf_len(g->libc_path)) | |
| 3406 | return; | |
| 3407 | g->libc_path = buf_create_from_str(ZIG_LIBC_DIR); | |
| 3408 | if (g->libc_path && buf_len(g->libc_path)) | |
| 3409 | return; | |
| 3410 | fprintf(stderr, "Unable to determine libc path. Consider using `--libc-path [path]`\n"); | |
| 3411 | exit(1); | |
| 3412 | } | |
| 3413 | ||
| 3414 | 3397 | static const char *get_libc_file(CodeGen *g, const char *file) { |
| 3415 | 3398 | Buf *out_buf = buf_alloc(); |
| 3416 | os_path_join(g->libc_path, buf_create_from_str(file), out_buf); | |
| 3399 | os_path_join(g->libc_lib_path, buf_create_from_str(file), out_buf); | |
| 3417 | 3400 | return buf_ptr(out_buf); |
| 3418 | 3401 | } |
| 3419 | 3402 |
src/errmsg.cpp+58-10| ... | ... | @@ -15,16 +15,7 @@ void print_err_msg(ErrorMsg *err, ErrColor color) { |
| 15 | 15 | err->line_start + 1, err->column_start + 1, |
| 16 | 16 | buf_ptr(err->msg)); |
| 17 | 17 | |
| 18 | assert(err->source); | |
| 19 | assert(err->line_offsets); | |
| 20 | ||
| 21 | int line_start_offset = err->line_offsets->at(err->line_start); | |
| 22 | int end_line = err->line_start + 1; | |
| 23 | int line_end_offset = (end_line >= err->line_offsets->length) ? | |
| 24 | buf_len(err->source) : err->line_offsets->at(err->line_start + 1); | |
| 25 | ||
| 26 | fwrite(buf_ptr(err->source) + line_start_offset, 1, line_end_offset - line_start_offset - 1, stderr); | |
| 27 | fprintf(stderr, "\n"); | |
| 18 | fprintf(stderr, "%s\n", buf_ptr(&err->line_buf)); | |
| 28 | 19 | for (int i = 0; i < err->column_start; i += 1) { |
| 29 | 20 | fprintf(stderr, " "); |
| 30 | 21 | } |
| ... | ... | @@ -36,5 +27,62 @@ void print_err_msg(ErrorMsg *err, ErrColor color) { |
| 36 | 27 | err->line_start + 1, err->column_start + 1, |
| 37 | 28 | buf_ptr(err->msg)); |
| 38 | 29 | } |
| 30 | ||
| 31 | for (int i = 0; i < err->notes.length; i += 1) { | |
| 32 | ErrorMsg *note = err->notes.at(i); | |
| 33 | print_err_msg(note, color); | |
| 34 | } | |
| 35 | } | |
| 36 | ||
| 37 | void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note) { | |
| 38 | parent->notes.append(note); | |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | ErrorMsg *err_msg_create_with_offset(Buf *path, int line, int column, int offset, | |
| 42 | const char *source, Buf *msg) | |
| 43 | { | |
| 44 | ErrorMsg *err_msg = allocate<ErrorMsg>(1); | |
| 45 | err_msg->path = path; | |
| 46 | err_msg->line_start = line; | |
| 47 | err_msg->column_start = column; | |
| 48 | err_msg->msg = msg; | |
| 49 | ||
| 50 | int line_start_offset = offset; | |
| 51 | for (;;) { | |
| 52 | if (line_start_offset == 0) { | |
| 53 | break; | |
| 54 | } else if (source[line_start_offset] == '\n') { | |
| 55 | line_start_offset += 1; | |
| 56 | break; | |
| 57 | } | |
| 58 | line_start_offset -= 1; | |
| 59 | } | |
| 60 | ||
| 61 | int line_end_offset = offset; | |
| 62 | while (source[line_end_offset] && source[line_end_offset] != '\n') { | |
| 63 | line_end_offset += 1; | |
| 64 | } | |
| 65 | ||
| 66 | buf_init_from_mem(&err_msg->line_buf, source + line_start_offset, line_end_offset - line_start_offset); | |
| 67 | ||
| 68 | return err_msg; | |
| 69 | } | |
| 70 | ||
| 71 | ErrorMsg *err_msg_create_with_line(Buf *path, int line, int column, | |
| 72 | Buf *source, ZigList<int> *line_offsets, Buf *msg) | |
| 73 | { | |
| 74 | ErrorMsg *err_msg = allocate<ErrorMsg>(1); | |
| 75 | err_msg->path = path; | |
| 76 | err_msg->line_start = line; | |
| 77 | err_msg->column_start = column; | |
| 78 | err_msg->msg = msg; | |
| 79 | ||
| 80 | int line_start_offset = line_offsets->at(line); | |
| 81 | int end_line = line + 1; | |
| 82 | int line_end_offset = (end_line >= line_offsets->length) ? buf_len(source) : line_offsets->at(line + 1); | |
| 83 | ||
| 84 | buf_init_from_mem(&err_msg->line_buf, buf_ptr(source) + line_start_offset, | |
| 85 | line_end_offset - line_start_offset - 1); | |
| 86 | ||
| 87 | return err_msg; | |
| 88 | } |
src/errmsg.hpp+10-4| ... | ... | @@ -20,14 +20,20 @@ enum ErrColor { |
| 20 | 20 | struct ErrorMsg { |
| 21 | 21 | int line_start; |
| 22 | 22 | int column_start; |
| 23 | int line_end; | |
| 24 | int column_end; | |
| 25 | 23 | Buf *msg; |
| 26 | 24 | Buf *path; |
| 27 | Buf *source; | |
| 28 | ZigList<int> *line_offsets; | |
| 25 | Buf line_buf; | |
| 26 | ||
| 27 | ZigList<ErrorMsg *> notes; | |
| 29 | 28 | }; |
| 30 | 29 | |
| 31 | 30 | void print_err_msg(ErrorMsg *msg, ErrColor color); |
| 32 | 31 | |
| 32 | void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note); | |
| 33 | ErrorMsg *err_msg_create_with_offset(Buf *path, int line, int column, int offset, | |
| 34 | const char *source, Buf *msg); | |
| 35 | ||
| 36 | ErrorMsg *err_msg_create_with_line(Buf *path, int line, int column, | |
| 37 | Buf *source, ZigList<int> *line_offsets, Buf *msg); | |
| 38 | ||
| 33 | 39 | #endif |
src/main.cpp+86-1| ... | ... | @@ -156,6 +156,25 @@ static int build(const char *arg0, int argc, char **argv) { |
| 156 | 156 | return 0; |
| 157 | 157 | } |
| 158 | 158 | |
| 159 | struct ParseHPrint { | |
| 160 | ParseH parse_h; | |
| 161 | FILE *f; | |
| 162 | int cur_indent; | |
| 163 | }; | |
| 164 | ||
| 165 | static const int indent_size = 4; | |
| 166 | ||
| 167 | static void print_indent(ParseHPrint *p) { | |
| 168 | for (int i = 0; i < p->cur_indent; i += 1) { | |
| 169 | fprintf(p->f, " "); | |
| 170 | } | |
| 171 | } | |
| 172 | ||
| 173 | static const char *type_node_to_name(AstNode *type_node) { | |
| 174 | assert(type_node->type == NodeTypeSymbol); | |
| 175 | return buf_ptr(&type_node->data.symbol_expr.symbol); | |
| 176 | } | |
| 177 | ||
| 159 | 178 | static int parseh(const char *arg0, int argc, char **argv) { |
| 160 | 179 | char *in_file = nullptr; |
| 161 | 180 | ZigList<const char *> clang_argv = {0}; |
| ... | ... | @@ -187,7 +206,73 @@ static int parseh(const char *arg0, int argc, char **argv) { |
| 187 | 206 | return usage(arg0); |
| 188 | 207 | } |
| 189 | 208 | |
| 190 | parse_h_file(in_file, &clang_argv, stdout); | |
| 209 | clang_argv.append(in_file); | |
| 210 | ||
| 211 | ParseHPrint parse_h_print = {{{0}}}; | |
| 212 | ParseHPrint *p = &parse_h_print; | |
| 213 | p->f = stdout; | |
| 214 | p->cur_indent = 0; | |
| 215 | ||
| 216 | parse_h_file(&p->parse_h, &clang_argv); | |
| 217 | ||
| 218 | if (p->parse_h.errors.length > 0) { | |
| 219 | for (int i = 0; i < p->parse_h.errors.length; i += 1) { | |
| 220 | ErrorMsg *err_msg = p->parse_h.errors.at(i); | |
| 221 | // TODO respect --color arg | |
| 222 | print_err_msg(err_msg, ErrColorAuto); | |
| 223 | } | |
| 224 | return EXIT_FAILURE; | |
| 225 | } | |
| 226 | ||
| 227 | for (int struct_i = 0; struct_i < p->parse_h.struct_list.length; struct_i += 1) { | |
| 228 | AstNode *struct_decl = p->parse_h.struct_list.at(struct_i); | |
| 229 | assert(struct_decl->type == NodeTypeStructDecl); | |
| 230 | const char *struct_name = buf_ptr(&struct_decl->data.struct_decl.name); | |
| 231 | print_indent(p); | |
| 232 | fprintf(p->f, "struct %s {\n", struct_name); | |
| 233 | p->cur_indent += indent_size; | |
| 234 | for (int field_i = 0; field_i < struct_decl->data.struct_decl.fields.length; field_i += 1) { | |
| 235 | AstNode *field_node = struct_decl->data.struct_decl.fields.at(field_i); | |
| 236 | assert(field_node->type == NodeTypeStructField); | |
| 237 | const char *field_name = buf_ptr(&field_node->data.struct_field.name); | |
| 238 | const char *type_name = type_node_to_name(field_node->data.struct_field.type); | |
| 239 | print_indent(p); | |
| 240 | fprintf(p->f, "%s: %s,\n", field_name, type_name); | |
| 241 | } | |
| 242 | ||
| 243 | p->cur_indent -= indent_size; | |
| 244 | fprintf(p->f, "}\n\n"); | |
| 245 | } | |
| 246 | ||
| 247 | for (int fn_i = 0; fn_i < p->parse_h.fn_list.length; fn_i += 1) { | |
| 248 | AstNode *fn_proto = p->parse_h.fn_list.at(fn_i); | |
| 249 | assert(fn_proto->type == NodeTypeFnProto); | |
| 250 | print_indent(p); | |
| 251 | const char *fn_name = buf_ptr(&fn_proto->data.fn_proto.name); | |
| 252 | fprintf(p->f, "extern fn %s(", fn_name); | |
| 253 | int arg_count = fn_proto->data.fn_proto.params.length; | |
| 254 | bool is_var_args = fn_proto->data.fn_proto.is_var_args; | |
| 255 | for (int arg_i = 0; arg_i < arg_count; arg_i += 1) { | |
| 256 | AstNode *param_decl = fn_proto->data.fn_proto.params.at(arg_i); | |
| 257 | assert(param_decl->type == NodeTypeParamDecl); | |
| 258 | const char *arg_name = buf_ptr(&param_decl->data.param_decl.name); | |
| 259 | const char *arg_type = type_node_to_name(param_decl->data.param_decl.type); | |
| 260 | fprintf(p->f, "%s: %s", arg_name, arg_type); | |
| 261 | if (arg_i + 1 < arg_count || is_var_args) { | |
| 262 | fprintf(p->f, ", "); | |
| 263 | } | |
| 264 | } | |
| 265 | if (is_var_args) { | |
| 266 | fprintf(p->f, "..."); | |
| 267 | } | |
| 268 | fprintf(p->f, ")"); | |
| 269 | const char *return_type_name = type_node_to_name(fn_proto->data.fn_proto.return_type); | |
| 270 | if (strcmp(return_type_name, "void") != 0) { | |
| 271 | fprintf(p->f, " -> %s", return_type_name); | |
| 272 | } | |
| 273 | fprintf(p->f, ";\n"); | |
| 274 | } | |
| 275 | ||
| 191 | 276 | return 0; |
| 192 | 277 | } |
| 193 | 278 |
src/os.cpp+26| ... | ... | @@ -208,3 +208,29 @@ int os_get_cwd(Buf *out_cwd) { |
| 208 | 208 | bool os_stderr_tty(void) { |
| 209 | 209 | return isatty(STDERR_FILENO); |
| 210 | 210 | } |
| 211 | ||
| 212 | int os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path) { | |
| 213 | buf_resize(out_tmp_path, 0); | |
| 214 | buf_appendf(out_tmp_path, "/tmp/XXXXXX%s", buf_ptr(suffix)); | |
| 215 | ||
| 216 | int fd = mkstemps(buf_ptr(out_tmp_path), buf_len(suffix)); | |
| 217 | if (fd < 0) { | |
| 218 | return ErrorFileSystem; | |
| 219 | } | |
| 220 | ||
| 221 | ssize_t amt_written = write(fd, buf_ptr(contents), buf_len(contents)); | |
| 222 | if (amt_written != buf_len(contents)) | |
| 223 | zig_panic("write failed: %s", strerror(errno)); | |
| 224 | if (close(fd) == -1) | |
| 225 | zig_panic("close failed"); | |
| 226 | ||
| 227 | return 0; | |
| 228 | } | |
| 229 | ||
| 230 | int os_delete_file(Buf *path) { | |
| 231 | if (remove(buf_ptr(path))) { | |
| 232 | return ErrorFileSystem; | |
| 233 | } else { | |
| 234 | return 0; | |
| 235 | } | |
| 236 | } |
src/os.hpp+3-1| ... | ... | @@ -29,7 +29,9 @@ int os_fetch_file_path(Buf *full_path, Buf *out_contents); |
| 29 | 29 | |
| 30 | 30 | int os_get_cwd(Buf *out_cwd); |
| 31 | 31 | |
| 32 | ||
| 33 | 32 | bool os_stderr_tty(void); |
| 34 | 33 | |
| 34 | int os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path); | |
| 35 | int os_delete_file(Buf *path); | |
| 36 | ||
| 35 | 37 | #endif |
src/parseh.cpp+84-599| ... | ... | @@ -7,540 +7,52 @@ |
| 7 | 7 | |
| 8 | 8 | #include "parseh.hpp" |
| 9 | 9 | #include "config.h" |
| 10 | #include "os.hpp" | |
| 11 | #include "error.hpp" | |
| 10 | 12 | |
| 11 | #include <clang-c/Index.h> | |
| 13 | #include <clang/Frontend/ASTUnit.h> | |
| 14 | #include <clang/Frontend/CompilerInstance.h> | |
| 12 | 15 | |
| 13 | 16 | #include <string.h> |
| 14 | 17 | |
| 15 | struct TypeDef { | |
| 16 | Buf alias; | |
| 17 | Buf target; | |
| 18 | }; | |
| 19 | ||
| 20 | struct Arg { | |
| 21 | Buf name; | |
| 22 | Buf *type; | |
| 23 | }; | |
| 24 | ||
| 25 | struct Fn { | |
| 26 | Buf name; | |
| 27 | Buf *return_type; | |
| 28 | Arg *args; | |
| 29 | int arg_count; | |
| 30 | bool is_variadic; | |
| 31 | }; | |
| 32 | ||
| 33 | struct Field { | |
| 34 | Buf name; | |
| 35 | Buf *type; | |
| 36 | }; | |
| 37 | ||
| 38 | struct Struct { | |
| 39 | Buf name; | |
| 40 | ZigList<Field*> fields; | |
| 41 | bool have_def; | |
| 42 | }; | |
| 43 | ||
| 44 | struct ParseH { | |
| 45 | CXTranslationUnit tu; | |
| 46 | FILE *f; | |
| 47 | ZigList<Fn *> fn_list; | |
| 48 | ZigList<Struct *> struct_list; | |
| 49 | ZigList<TypeDef *> type_def_list; | |
| 50 | ZigList<Struct *> incomplete_struct_list; | |
| 51 | Fn *cur_fn; | |
| 52 | Struct *cur_struct; | |
| 53 | int arg_index; | |
| 54 | int cur_indent; | |
| 55 | CXSourceRange range; | |
| 56 | CXSourceLocation location; | |
| 57 | }; | |
| 58 | ||
| 59 | static const int indent_size = 4; | |
| 18 | using namespace clang; | |
| 60 | 19 | |
| 61 | struct TypeMapping { | |
| 62 | const char *c_name; | |
| 63 | const char *zig_name; | |
| 20 | struct Context { | |
| 21 | ParseH *parse_h; | |
| 64 | 22 | }; |
| 65 | 23 | |
| 66 | static const TypeMapping type_mappings[] = { | |
| 67 | { | |
| 68 | "int8_t", | |
| 69 | "i8", | |
| 70 | }, | |
| 71 | { | |
| 72 | "uint8_t", | |
| 73 | "u8", | |
| 74 | }, | |
| 75 | { | |
| 76 | "uint16_t", | |
| 77 | "u16", | |
| 78 | }, | |
| 79 | { | |
| 80 | "uint32_t", | |
| 81 | "u32", | |
| 82 | }, | |
| 83 | { | |
| 84 | "uint64_t", | |
| 85 | "u64", | |
| 86 | }, | |
| 87 | { | |
| 88 | "int16_t", | |
| 89 | "i16", | |
| 90 | }, | |
| 91 | { | |
| 92 | "int32_t", | |
| 93 | "i32", | |
| 94 | }, | |
| 95 | { | |
| 96 | "int64_t", | |
| 97 | "i64", | |
| 98 | }, | |
| 99 | { | |
| 100 | "intptr_t", | |
| 101 | "isize", | |
| 102 | }, | |
| 103 | { | |
| 104 | "uintptr_t", | |
| 105 | "usize", | |
| 106 | }, | |
| 107 | }; | |
| 108 | ||
| 109 | static bool have_struct_def(ParseH *p, Buf *name) { | |
| 110 | for (int i = 0; i < p->struct_list.length; i += 1) { | |
| 111 | Struct *struc = p->struct_list.at(i); | |
| 112 | if (struc->fields.length > 0 && buf_eql_buf(&struc->name, name)) { | |
| 113 | return true; | |
| 114 | } | |
| 115 | } | |
| 116 | return false; | |
| 117 | } | |
| 24 | static bool decl_visitor(void *context, const Decl *decl) { | |
| 25 | //Context *c = (Context*)context; | |
| 118 | 26 | |
| 119 | static const char *c_to_zig_name(const char *name) { | |
| 120 | for (int i = 0; i < array_length(type_mappings); i += 1) { | |
| 121 | const TypeMapping *mapping = &type_mappings[i]; | |
| 122 | if (strcmp(mapping->c_name, name) == 0) | |
| 123 | return mapping->zig_name; | |
| 124 | } | |
| 125 | return nullptr; | |
| 126 | } | |
| 27 | fprintf(stderr, "got top level decl\n"); | |
| 127 | 28 | |
| 128 | static bool str_has_prefix(const char *str, const char *prefix) { | |
| 129 | while (*prefix) { | |
| 130 | if (*str && *str == *prefix) { | |
| 131 | str += 1; | |
| 132 | prefix += 1; | |
| 133 | } else { | |
| 134 | return false; | |
| 135 | } | |
| 136 | } | |
| 137 | 29 | return true; |
| 138 | 30 | } |
| 139 | 31 | |
| 140 | static const char *prefixes_stripped(CXType type) { | |
| 141 | CXString name = clang_getTypeSpelling(type); | |
| 142 | const char *c_name = clang_getCString(name); | |
| 143 | ||
| 144 | static const char *prefixes[] = { | |
| 145 | "struct ", | |
| 146 | "enum ", | |
| 147 | "const ", | |
| 148 | }; | |
| 149 | ||
| 150 | start_over: | |
| 151 | ||
| 152 | for (int i = 0; i < array_length(prefixes); i += 1) { | |
| 153 | const char *prefix = prefixes[i]; | |
| 154 | if (str_has_prefix(c_name, prefix)) { | |
| 155 | c_name += strlen(prefix); | |
| 156 | goto start_over; | |
| 157 | } | |
| 158 | } | |
| 159 | return c_name; | |
| 160 | } | |
| 161 | ||
| 162 | static void print_location(ParseH *p) { | |
| 163 | CXFile file; | |
| 164 | unsigned line, column, offset; | |
| 165 | clang_getFileLocation(p->location, &file, &line, &column, &offset); | |
| 166 | CXString file_name = clang_getFileName(file); | |
| 167 | ||
| 168 | fprintf(stderr, "%s line %u, column %u\n", clang_getCString(file_name), line, column); | |
| 169 | } | |
| 170 | ||
| 171 | static bool resolves_to_void(ParseH *p, CXType raw_type) { | |
| 172 | if (raw_type.kind == CXType_Unexposed) { | |
| 173 | CXType canonical = clang_getCanonicalType(raw_type); | |
| 174 | if (canonical.kind == CXType_Unexposed) | |
| 175 | zig_panic("clang C api insufficient"); | |
| 176 | else | |
| 177 | return resolves_to_void(p, canonical); | |
| 178 | } | |
| 179 | if (raw_type.kind == CXType_Void) { | |
| 180 | return true; | |
| 181 | } else if (raw_type.kind == CXType_Typedef) { | |
| 182 | CXCursor typedef_cursor = clang_getTypeDeclaration(raw_type); | |
| 183 | CXType underlying_type = clang_getTypedefDeclUnderlyingType(typedef_cursor); | |
| 184 | return resolves_to_void(p, underlying_type); | |
| 185 | } | |
| 186 | return false; | |
| 187 | } | |
| 188 | ||
| 189 | static Buf *to_zig_type(ParseH *p, CXType raw_type) { | |
| 190 | if (raw_type.kind == CXType_Unexposed) { | |
| 191 | CXType canonical = clang_getCanonicalType(raw_type); | |
| 192 | if (canonical.kind == CXType_Unexposed) | |
| 193 | zig_panic("clang C api insufficient"); | |
| 194 | else | |
| 195 | return to_zig_type(p, canonical); | |
| 196 | } | |
| 197 | switch (raw_type.kind) { | |
| 198 | case CXType_Invalid: | |
| 199 | case CXType_Unexposed: | |
| 200 | zig_unreachable(); | |
| 201 | case CXType_Void: | |
| 202 | zig_panic("void type encountered"); | |
| 203 | case CXType_Bool: | |
| 204 | return buf_create_from_str("bool"); | |
| 205 | case CXType_SChar: | |
| 206 | return buf_create_from_str("i8"); | |
| 207 | case CXType_UChar: | |
| 208 | case CXType_Char_U: | |
| 209 | case CXType_Char_S: | |
| 210 | return buf_create_from_str("u8"); | |
| 211 | case CXType_WChar: | |
| 212 | print_location(p); | |
| 213 | zig_panic("TODO wchar"); | |
| 214 | case CXType_Char16: | |
| 215 | print_location(p); | |
| 216 | zig_panic("TODO char16"); | |
| 217 | case CXType_Char32: | |
| 218 | print_location(p); | |
| 219 | zig_panic("TODO char32"); | |
| 220 | case CXType_UShort: | |
| 221 | return buf_create_from_str("c_ushort"); | |
| 222 | case CXType_UInt: | |
| 223 | return buf_create_from_str("c_uint"); | |
| 224 | case CXType_ULong: | |
| 225 | return buf_create_from_str("c_ulong"); | |
| 226 | case CXType_ULongLong: | |
| 227 | return buf_create_from_str("c_ulonglong"); | |
| 228 | case CXType_UInt128: | |
| 229 | print_location(p); | |
| 230 | zig_panic("TODO uint128"); | |
| 231 | case CXType_Short: | |
| 232 | return buf_create_from_str("c_short"); | |
| 233 | case CXType_Int: | |
| 234 | return buf_create_from_str("c_int"); | |
| 235 | case CXType_Long: | |
| 236 | return buf_create_from_str("c_long"); | |
| 237 | case CXType_LongLong: | |
| 238 | return buf_create_from_str("c_longlong"); | |
| 239 | case CXType_Int128: | |
| 240 | print_location(p); | |
| 241 | zig_panic("TODO int128"); | |
| 242 | case CXType_Float: | |
| 243 | return buf_create_from_str("f32"); | |
| 244 | case CXType_Double: | |
| 245 | return buf_create_from_str("f64"); | |
| 246 | case CXType_LongDouble: | |
| 247 | return buf_create_from_str("f128"); | |
| 248 | case CXType_IncompleteArray: | |
| 249 | { | |
| 250 | CXType pointee_type = clang_getArrayElementType(raw_type); | |
| 251 | Buf *pointee_buf = to_zig_type(p, pointee_type); | |
| 252 | if (clang_isConstQualifiedType(pointee_type)) { | |
| 253 | return buf_sprintf("&const %s", buf_ptr(pointee_buf)); | |
| 254 | } else { | |
| 255 | return buf_sprintf("&%s", buf_ptr(pointee_buf)); | |
| 256 | } | |
| 257 | } | |
| 258 | case CXType_Pointer: | |
| 259 | { | |
| 260 | CXType pointee_type = clang_getPointeeType(raw_type); | |
| 261 | Buf *pointee_buf; | |
| 262 | if (resolves_to_void(p, pointee_type)) { | |
| 263 | pointee_buf = buf_create_from_str("u8"); | |
| 264 | } else { | |
| 265 | pointee_buf = to_zig_type(p, pointee_type); | |
| 266 | } | |
| 267 | if (clang_isConstQualifiedType(pointee_type)) { | |
| 268 | return buf_sprintf("&const %s", buf_ptr(pointee_buf)); | |
| 269 | } else { | |
| 270 | return buf_sprintf("&%s", buf_ptr(pointee_buf)); | |
| 271 | } | |
| 272 | } | |
| 273 | case CXType_Record: | |
| 274 | { | |
| 275 | const char *name = prefixes_stripped(raw_type); | |
| 276 | return buf_sprintf("%s", name); | |
| 277 | } | |
| 278 | case CXType_Enum: | |
| 279 | { | |
| 280 | const char *name = prefixes_stripped(raw_type); | |
| 281 | return buf_sprintf("%s", name); | |
| 282 | } | |
| 283 | case CXType_Typedef: | |
| 284 | { | |
| 285 | const char *name = prefixes_stripped(raw_type); | |
| 286 | const char *zig_name = c_to_zig_name(name); | |
| 287 | if (zig_name) { | |
| 288 | return buf_create_from_str(zig_name); | |
| 289 | } else { | |
| 290 | CXCursor typedef_cursor = clang_getTypeDeclaration(raw_type); | |
| 291 | CXType underlying_type = clang_getTypedefDeclUnderlyingType(typedef_cursor); | |
| 292 | if (resolves_to_void(p, underlying_type)) { | |
| 293 | return buf_create_from_str("u8"); | |
| 294 | } else { | |
| 295 | return buf_create_from_str(name); | |
| 296 | } | |
| 297 | } | |
| 298 | } | |
| 299 | case CXType_ConstantArray: | |
| 300 | { | |
| 301 | CXType child_type = clang_getArrayElementType(raw_type); | |
| 302 | Buf *zig_child_type = to_zig_type(p, child_type); | |
| 303 | long size = (long)clang_getArraySize(raw_type); | |
| 304 | return buf_sprintf("[%s; %ld]", buf_ptr(zig_child_type), size); | |
| 305 | } | |
| 306 | case CXType_FunctionProto: | |
| 307 | fprintf(stderr, "warning: TODO function proto\n"); | |
| 308 | print_location(p); | |
| 309 | return buf_create_from_str("u8"); | |
| 310 | case CXType_FunctionNoProto: | |
| 311 | print_location(p); | |
| 312 | zig_panic("TODO function no proto"); | |
| 313 | case CXType_BlockPointer: | |
| 314 | print_location(p); | |
| 315 | zig_panic("TODO block pointer"); | |
| 316 | case CXType_Vector: | |
| 317 | print_location(p); | |
| 318 | zig_panic("TODO vector"); | |
| 319 | case CXType_LValueReference: | |
| 320 | case CXType_RValueReference: | |
| 321 | case CXType_VariableArray: | |
| 322 | case CXType_DependentSizedArray: | |
| 323 | case CXType_MemberPointer: | |
| 324 | case CXType_ObjCInterface: | |
| 325 | case CXType_ObjCObjectPointer: | |
| 326 | case CXType_NullPtr: | |
| 327 | case CXType_Overload: | |
| 328 | case CXType_Dependent: | |
| 329 | case CXType_ObjCId: | |
| 330 | case CXType_ObjCClass: | |
| 331 | case CXType_ObjCSel: | |
| 332 | case CXType_Complex: | |
| 333 | print_location(p); | |
| 334 | zig_panic("TODO"); | |
| 335 | } | |
| 336 | ||
| 337 | zig_unreachable(); | |
| 338 | } | |
| 339 | ||
| 340 | static bool is_storage_class_export(CX_StorageClass storage_class) { | |
| 341 | switch (storage_class) { | |
| 342 | case CX_SC_Invalid: | |
| 343 | zig_unreachable(); | |
| 344 | case CX_SC_None: | |
| 345 | case CX_SC_Extern: | |
| 346 | case CX_SC_Auto: | |
| 347 | return true; | |
| 348 | case CX_SC_Static: | |
| 349 | case CX_SC_PrivateExtern: | |
| 350 | case CX_SC_OpenCLWorkGroupLocal: | |
| 351 | case CX_SC_Register: | |
| 352 | return false; | |
| 353 | } | |
| 354 | zig_unreachable(); | |
| 355 | } | |
| 356 | ||
| 357 | static enum CXChildVisitResult visit_fn_children(CXCursor cursor, CXCursor parent, CXClientData client_data) { | |
| 358 | ParseH *p = (ParseH*)client_data; | |
| 359 | enum CXCursorKind kind = clang_getCursorKind(cursor); | |
| 360 | ||
| 361 | switch (kind) { | |
| 362 | case CXCursor_ParmDecl: | |
| 363 | { | |
| 364 | assert(p->cur_fn); | |
| 365 | assert(p->arg_index < p->cur_fn->arg_count); | |
| 366 | CXString name = clang_getCursorSpelling(cursor); | |
| 367 | Buf *arg_name = &p->cur_fn->args[p->arg_index].name; | |
| 368 | buf_init_from_str(arg_name, clang_getCString(name)); | |
| 369 | if (buf_len(arg_name) == 0) { | |
| 370 | buf_appendf(arg_name, "arg%d", p->arg_index); | |
| 371 | } | |
| 372 | ||
| 373 | p->arg_index += 1; | |
| 374 | return CXChildVisit_Continue; | |
| 375 | } | |
| 376 | default: | |
| 377 | return CXChildVisit_Recurse; | |
| 378 | } | |
| 379 | } | |
| 380 | ||
| 381 | static enum CXChildVisitResult visit_struct_children(CXCursor cursor, CXCursor parent, CXClientData client_data) { | |
| 382 | ParseH *p = (ParseH*)client_data; | |
| 383 | enum CXCursorKind kind = clang_getCursorKind(cursor); | |
| 384 | ||
| 385 | switch (kind) { | |
| 386 | case CXCursor_FieldDecl: | |
| 387 | { | |
| 388 | assert(p->cur_struct); | |
| 389 | CXString name = clang_getCursorSpelling(cursor); | |
| 390 | Field *field = allocate<Field>(1); | |
| 391 | buf_init_from_str(&field->name, clang_getCString(name)); | |
| 392 | CXType cursor_type = clang_getCursorType(cursor); | |
| 393 | field->type = to_zig_type(p, cursor_type); | |
| 394 | ||
| 395 | p->cur_struct->fields.append(field); | |
| 396 | ||
| 397 | return CXChildVisit_Continue; | |
| 398 | } | |
| 399 | default: | |
| 400 | return CXChildVisit_Recurse; | |
| 401 | } | |
| 402 | } | |
| 403 | ||
| 404 | static bool handle_struct_cursor(ParseH *p, CXCursor cursor, const char *name, bool expect_name) { | |
| 405 | p->cur_struct = allocate<Struct>(1); | |
| 406 | ||
| 407 | buf_init_from_str(&p->cur_struct->name, name); | |
| 408 | ||
| 409 | bool got_name = (buf_len(&p->cur_struct->name) != 0); | |
| 410 | if (expect_name != got_name) | |
| 411 | return false; | |
| 412 | ||
| 413 | clang_visitChildren(cursor, visit_struct_children, p); | |
| 414 | ||
| 415 | if (p->cur_struct->fields.length > 0) { | |
| 416 | p->struct_list.append(p->cur_struct); | |
| 417 | } else { | |
| 418 | p->incomplete_struct_list.append(p->cur_struct); | |
| 32 | int parse_h_buf(ParseH *parse_h, Buf *source, const char *libc_include_path) { | |
| 33 | int err; | |
| 34 | Buf tmp_file_path = BUF_INIT; | |
| 35 | if ((err = os_buf_to_tmp_file(source, buf_create_from_str(".h"), &tmp_file_path))) { | |
| 36 | return err; | |
| 419 | 37 | } |
| 38 | ZigList<const char *> clang_argv = {0}; | |
| 39 | clang_argv.append(buf_ptr(&tmp_file_path)); | |
| 420 | 40 | |
| 421 | p->cur_struct = nullptr; | |
| 422 | ||
| 423 | return true; | |
| 424 | } | |
| 425 | ||
| 426 | ||
| 427 | static enum CXChildVisitResult fn_visitor(CXCursor cursor, CXCursor parent, CXClientData client_data) { | |
| 428 | ParseH *p = (ParseH*)client_data; | |
| 429 | enum CXCursorKind kind = clang_getCursorKind(cursor); | |
| 430 | CXString name = clang_getCursorSpelling(cursor); | |
| 431 | ||
| 432 | p->range = clang_getCursorExtent(cursor); | |
| 433 | p->location = clang_getRangeStart(p->range); | |
| 434 | ||
| 435 | switch (kind) { | |
| 436 | case CXCursor_FunctionDecl: | |
| 437 | { | |
| 438 | CX_StorageClass storage_class = clang_Cursor_getStorageClass(cursor); | |
| 439 | if (!is_storage_class_export(storage_class)) | |
| 440 | return CXChildVisit_Continue; | |
| 441 | ||
| 442 | CXType fn_type = clang_getCursorType(cursor); | |
| 443 | if (clang_getFunctionTypeCallingConv(fn_type) != CXCallingConv_C) { | |
| 444 | print_location(p); | |
| 445 | fprintf(stderr, "warning: skipping non c calling convention function, not yet supported\n"); | |
| 446 | return CXChildVisit_Continue; | |
| 447 | } | |
| 448 | ||
| 449 | assert(!p->cur_fn); | |
| 450 | p->cur_fn = allocate<Fn>(1); | |
| 451 | ||
| 452 | p->cur_fn->is_variadic = clang_isFunctionTypeVariadic(fn_type); | |
| 453 | ||
| 454 | CXType return_type = clang_getResultType(fn_type); | |
| 455 | if (!resolves_to_void(p, return_type)) { | |
| 456 | p->cur_fn->return_type = to_zig_type(p, return_type); | |
| 457 | } | |
| 458 | ||
| 459 | buf_init_from_str(&p->cur_fn->name, clang_getCString(name)); | |
| 460 | ||
| 461 | p->cur_fn->arg_count = clang_getNumArgTypes(fn_type); | |
| 462 | p->cur_fn->args = allocate<Arg>(p->cur_fn->arg_count); | |
| 463 | ||
| 464 | for (int i = 0; i < p->cur_fn->arg_count; i += 1) { | |
| 465 | CXType param_type = clang_getArgType(fn_type, i); | |
| 466 | p->cur_fn->args[i].type = to_zig_type(p, param_type); | |
| 467 | } | |
| 468 | ||
| 469 | p->arg_index = 0; | |
| 470 | ||
| 471 | clang_visitChildren(cursor, visit_fn_children, p); | |
| 472 | ||
| 473 | p->fn_list.append(p->cur_fn); | |
| 474 | p->cur_fn = nullptr; | |
| 475 | ||
| 476 | return CXChildVisit_Recurse; | |
| 477 | } | |
| 478 | case CXCursor_CompoundStmt: | |
| 479 | case CXCursor_FieldDecl: | |
| 480 | case CXCursor_TypedefDecl: | |
| 481 | { | |
| 482 | CXType underlying_type = clang_getTypedefDeclUnderlyingType(cursor); | |
| 483 | ||
| 484 | if (resolves_to_void(p, underlying_type)) { | |
| 485 | return CXChildVisit_Continue; | |
| 486 | } | |
| 487 | ||
| 488 | if (underlying_type.kind == CXType_Unexposed) { | |
| 489 | underlying_type = clang_getCanonicalType(underlying_type); | |
| 490 | } | |
| 491 | bool skip_typedef; | |
| 492 | if (underlying_type.kind == CXType_Unexposed) { | |
| 493 | fprintf(stderr, "warning: unexposed type\n"); | |
| 494 | print_location(p); | |
| 495 | skip_typedef = true; | |
| 496 | } else if (underlying_type.kind == CXType_Record) { | |
| 497 | CXCursor decl_cursor = clang_getTypeDeclaration(underlying_type); | |
| 498 | skip_typedef = handle_struct_cursor(p, decl_cursor, clang_getCString(name), false); | |
| 499 | } else if (underlying_type.kind == CXType_Invalid) { | |
| 500 | fprintf(stderr, "warning: invalid type\n"); | |
| 501 | print_location(p); | |
| 502 | skip_typedef = true; | |
| 503 | } else { | |
| 504 | skip_typedef = false; | |
| 505 | } | |
| 506 | ||
| 507 | CXType typedef_type = clang_getCursorType(cursor); | |
| 508 | const char *name_str = prefixes_stripped(typedef_type); | |
| 509 | if (!skip_typedef && c_to_zig_name(name_str)) { | |
| 510 | skip_typedef = true; | |
| 511 | } | |
| 41 | clang_argv.append("-isystem"); | |
| 42 | clang_argv.append(libc_include_path); | |
| 512 | 43 | |
| 513 | if (!skip_typedef) { | |
| 514 | TypeDef *type_def = allocate<TypeDef>(1); | |
| 515 | buf_init_from_str(&type_def->alias, name_str); | |
| 516 | buf_init_from_buf(&type_def->target, to_zig_type(p, underlying_type)); | |
| 517 | p->type_def_list.append(type_def); | |
| 518 | } | |
| 519 | ||
| 520 | return CXChildVisit_Continue; | |
| 521 | } | |
| 522 | case CXCursor_StructDecl: | |
| 523 | { | |
| 524 | handle_struct_cursor(p, cursor, clang_getCString(name), true); | |
| 44 | err = parse_h_file(parse_h, &clang_argv); | |
| 525 | 45 | |
| 526 | return CXChildVisit_Continue; | |
| 527 | } | |
| 528 | default: | |
| 529 | return CXChildVisit_Recurse; | |
| 530 | } | |
| 531 | } | |
| 46 | os_delete_file(&tmp_file_path); | |
| 532 | 47 | |
| 533 | static void print_indent(ParseH *p) { | |
| 534 | for (int i = 0; i < p->cur_indent; i += 1) { | |
| 535 | fprintf(p->f, " "); | |
| 536 | } | |
| 48 | return err; | |
| 49 | // write to temp file, parse it, delete it | |
| 537 | 50 | } |
| 538 | 51 | |
| 539 | void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FILE *f) { | |
| 540 | ParseH parse_h = {0}; | |
| 541 | ParseH *p = &parse_h; | |
| 542 | p->f = f; | |
| 543 | CXIndex index = clang_createIndex(1, 0); | |
| 52 | int parse_h_file(ParseH *parse_h, ZigList<const char *> *clang_argv) { | |
| 53 | Context context = {0}; | |
| 54 | Context *c = &context; | |
| 55 | c->parse_h = parse_h; | |
| 544 | 56 | |
| 545 | 57 | char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS"); |
| 546 | 58 | if (ZIG_PARSEH_CFLAGS) { |
| ... | ... | @@ -558,105 +70,78 @@ void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FI |
| 558 | 70 | buf_init_from_str(&tmp_buf, start); |
| 559 | 71 | clang_argv->append(buf_ptr(buf_create_from_buf(&tmp_buf))); |
| 560 | 72 | } |
| 73 | ||
| 561 | 74 | clang_argv->append("-isystem"); |
| 562 | 75 | clang_argv->append(ZIG_HEADERS_DIR); |
| 563 | 76 | |
| 77 | // we don't need spell checking and it slows things down | |
| 78 | clang_argv->append("-fno-spell-checking"); | |
| 79 | // to make the end argument work | |
| 564 | 80 | clang_argv->append(nullptr); |
| 565 | 81 | |
| 566 | enum CXErrorCode err_code; | |
| 567 | if ((err_code = clang_parseTranslationUnit2(index, target_path, | |
| 568 | clang_argv->items, clang_argv->length - 1, | |
| 569 | NULL, 0, CXTranslationUnit_None, &p->tu))) | |
| 570 | { | |
| 571 | zig_panic("parse translation unit failure"); | |
| 572 | } | |
| 573 | ||
| 574 | ||
| 575 | unsigned diag_count = clang_getNumDiagnostics(p->tu); | |
| 82 | IntrusiveRefCntPtr<DiagnosticsEngine> diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); | |
| 576 | 83 | |
| 577 | if (diag_count > 0) { | |
| 578 | for (unsigned i = 0; i < diag_count; i += 1) { | |
| 579 | CXDiagnostic diagnostic = clang_getDiagnostic(p->tu, i); | |
| 580 | CXSourceLocation location = clang_getDiagnosticLocation(diagnostic); | |
| 581 | ||
| 582 | CXFile file; | |
| 583 | unsigned line, column, offset; | |
| 584 | clang_getSpellingLocation(location, &file, &line, &column, &offset); | |
| 585 | CXString text = clang_getDiagnosticSpelling(diagnostic); | |
| 586 | CXString file_name = clang_getFileName(file); | |
| 587 | fprintf(stderr, "%s line %u, column %u: %s\n", clang_getCString(file_name), | |
| 588 | line, column, clang_getCString(text)); | |
| 589 | } | |
| 590 | ||
| 591 | exit(1); | |
| 592 | } | |
| 84 | std::shared_ptr<PCHContainerOperations> pch_container_ops = std::make_shared<PCHContainerOperations>(); | |
| 593 | 85 | |
| 86 | bool skip_function_bodies = true; | |
| 87 | bool only_local_decls = true; | |
| 88 | bool capture_diagnostics = true; | |
| 89 | bool user_files_are_volatile = true; | |
| 90 | bool allow_pch_with_compiler_errors = false; | |
| 91 | const char *resources_path = ZIG_HEADERS_DIR; | |
| 92 | std::unique_ptr<ASTUnit> err_unit; | |
| 93 | std::unique_ptr<ASTUnit> ast_unit(ASTUnit::LoadFromCommandLine( | |
| 94 | &clang_argv->at(0), &clang_argv->last(), | |
| 95 | pch_container_ops, diags, resources_path, | |
| 96 | only_local_decls, capture_diagnostics, None, true, false, TU_Complete, | |
| 97 | false, false, allow_pch_with_compiler_errors, skip_function_bodies, | |
| 98 | user_files_are_volatile, false, &err_unit)); | |
| 594 | 99 | |
| 595 | CXCursor cursor = clang_getTranslationUnitCursor(p->tu); | |
| 596 | clang_visitChildren(cursor, fn_visitor, p); | |
| 597 | ||
| 598 | for (int struct_i = 0; struct_i < p->struct_list.length; struct_i += 1) { | |
| 599 | Struct *struc = p->struct_list.at(struct_i); | |
| 600 | fprintf(f, "struct %s {\n", buf_ptr(&struc->name)); | |
| 601 | p->cur_indent += indent_size; | |
| 602 | for (int field_i = 0; field_i < struc->fields.length; field_i += 1) { | |
| 603 | Field *field = struc->fields.at(field_i); | |
| 604 | print_indent(p); | |
| 605 | fprintf(f, "%s: %s,\n", buf_ptr(&field->name), buf_ptr(field->type)); | |
| 606 | } | |
| 607 | ||
| 608 | p->cur_indent -= indent_size; | |
| 609 | fprintf(f, "}\n\n"); | |
| 610 | } | |
| 611 | 100 | |
| 612 | int total_typedef_count = p->type_def_list.length; | |
| 613 | for (int i = 0; i < p->incomplete_struct_list.length; i += 1) { | |
| 614 | Struct *struc = p->incomplete_struct_list.at(i); | |
| 615 | struc->have_def = have_struct_def(p, &struc->name); | |
| 616 | total_typedef_count += (int)!struc->have_def; | |
| 101 | // Early failures in LoadFromCommandLine may return with ErrUnit unset. | |
| 102 | if (!ast_unit && !err_unit) { | |
| 103 | return ErrorFileSystem; | |
| 617 | 104 | } |
| 618 | 105 | |
| 619 | if (total_typedef_count) { | |
| 620 | for (int i = 0; i < p->incomplete_struct_list.length; i += 1) { | |
| 621 | Struct *struc = p->incomplete_struct_list.at(i); | |
| 622 | if (struc->have_def) | |
| 623 | continue; | |
| 624 | ||
| 625 | fprintf(f, "struct %s;\n", buf_ptr(&struc->name)); | |
| 106 | if (diags->getClient()->getNumErrors() > 0) { | |
| 107 | if (ast_unit) { | |
| 108 | err_unit = std::move(ast_unit); | |
| 626 | 109 | } |
| 627 | 110 | |
| 628 | for (int type_def_i = 0; type_def_i < p->type_def_list.length; type_def_i += 1) { | |
| 629 | TypeDef *type_def = p->type_def_list.at(type_def_i); | |
| 630 | fprintf(f, "type %s = %s;\n", buf_ptr(&type_def->alias), buf_ptr(&type_def->target)); | |
| 111 | for (ASTUnit::stored_diag_iterator it = err_unit->stored_diag_begin(), | |
| 112 | it_end = err_unit->stored_diag_end(); | |
| 113 | it != it_end; ++it) | |
| 114 | { | |
| 115 | switch (it->getLevel()) { | |
| 116 | case DiagnosticsEngine::Ignored: | |
| 117 | case DiagnosticsEngine::Note: | |
| 118 | case DiagnosticsEngine::Remark: | |
| 119 | case DiagnosticsEngine::Warning: | |
| 120 | continue; | |
| 121 | case DiagnosticsEngine::Error: | |
| 122 | case DiagnosticsEngine::Fatal: | |
| 123 | break; | |
| 124 | } | |
| 125 | StringRef msg_str_ref = it->getMessage(); | |
| 126 | FullSourceLoc fsl = it->getLocation(); | |
| 127 | FileID file_id = fsl.getManager().getFileID(fsl); | |
| 128 | StringRef filename = fsl.getManager().getFilename(fsl); | |
| 129 | unsigned line = fsl.getSpellingLineNumber() - 1; | |
| 130 | unsigned column = fsl.getSpellingColumnNumber() - 1; | |
| 131 | unsigned offset = fsl.getManager().getFileOffset(fsl); | |
| 132 | const char *source = (const char *)fsl.getManager().getBufferData(file_id).bytes_begin(); | |
| 133 | Buf *msg = buf_create_from_str((const char *)msg_str_ref.bytes_begin()); | |
| 134 | Buf *path = buf_create_from_str((const char *)filename.bytes_begin()); | |
| 135 | ||
| 136 | ErrorMsg *err_msg = err_msg_create_with_offset(path, line, column, offset, source, msg); | |
| 137 | ||
| 138 | parse_h->errors.append(err_msg); | |
| 631 | 139 | } |
| 632 | 140 | |
| 633 | fprintf(f, "\n"); | |
| 141 | return 0; | |
| 634 | 142 | } |
| 635 | 143 | |
| 636 | if (p->fn_list.length) { | |
| 637 | fprintf(f, "extern {\n"); | |
| 638 | p->cur_indent += indent_size; | |
| 639 | for (int fn_i = 0; fn_i < p->fn_list.length; fn_i += 1) { | |
| 640 | Fn *fn = p->fn_list.at(fn_i); | |
| 641 | print_indent(p); | |
| 642 | fprintf(p->f, "fn %s(", buf_ptr(&fn->name)); | |
| 643 | for (int arg_i = 0; arg_i < fn->arg_count; arg_i += 1) { | |
| 644 | Arg *arg = &fn->args[arg_i]; | |
| 645 | fprintf(p->f, "%s: %s", buf_ptr(&arg->name), buf_ptr(arg->type)); | |
| 646 | if (arg_i + 1 < fn->arg_count || fn->is_variadic) { | |
| 647 | fprintf(p->f, ", "); | |
| 648 | } | |
| 649 | } | |
| 650 | if (fn->is_variadic) { | |
| 651 | fprintf(p->f, "..."); | |
| 652 | } | |
| 653 | fprintf(p->f, ")"); | |
| 654 | if (fn->return_type) { | |
| 655 | fprintf(p->f, " -> %s", buf_ptr(fn->return_type)); | |
| 656 | } | |
| 657 | fprintf(p->f, ";\n"); | |
| 658 | } | |
| 659 | p->cur_indent -= indent_size; | |
| 660 | fprintf(f, "}\n"); | |
| 661 | } | |
| 144 | ast_unit->visitLocalTopLevelDecls(c, decl_visitor); | |
| 145 | ||
| 146 | return 0; | |
| 662 | 147 | } |
src/parseh.hpp+3-4| ... | ... | @@ -9,10 +9,9 @@ |
| 9 | 9 | #ifndef ZIG_PARSEH_HPP |
| 10 | 10 | #define ZIG_PARSEH_HPP |
| 11 | 11 | |
| 12 | #include "buffer.hpp" | |
| 12 | #include "all_types.hpp" | |
| 13 | 13 | |
| 14 | #include <stdio.h> | |
| 15 | ||
| 16 | void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FILE *f); | |
| 14 | int parse_h_file(ParseH *parse_h, ZigList<const char *> *clang_argv); | |
| 15 | int parse_h_buf(ParseH *parse_h, Buf *buf, const char *libc_include_path); | |
| 17 | 16 | |
| 18 | 17 | #endif |
src/parser.cpp+9-20| ... | ... | @@ -488,23 +488,16 @@ __attribute__ ((noreturn)) |
| 488 | 488 | static void ast_asm_error(ParseContext *pc, AstNode *node, int offset, const char *format, ...) { |
| 489 | 489 | assert(node->type == NodeTypeAsmExpr); |
| 490 | 490 | |
| 491 | ErrorMsg *err = allocate<ErrorMsg>(1); | |
| 492 | 491 | |
| 493 | 492 | SrcPos pos = node->data.asm_expr.offset_map.at(offset); |
| 494 | 493 | |
| 495 | err->line_start = pos.line; | |
| 496 | err->column_start = pos.column; | |
| 497 | err->line_end = -1; | |
| 498 | err->column_end = -1; | |
| 499 | ||
| 500 | 494 | va_list ap; |
| 501 | 495 | va_start(ap, format); |
| 502 | err->msg = buf_vprintf(format, ap); | |
| 496 | Buf *msg = buf_vprintf(format, ap); | |
| 503 | 497 | va_end(ap); |
| 504 | 498 | |
| 505 | err->path = pc->owner->path; | |
| 506 | err->source = pc->owner->source_code; | |
| 507 | err->line_offsets = pc->owner->line_offsets; | |
| 499 | ErrorMsg *err = err_msg_create_with_line(pc->owner->path, pos.line, pos.column, | |
| 500 | pc->owner->source_code, pc->owner->line_offsets, msg); | |
| 508 | 501 | |
| 509 | 502 | print_err_msg(err, pc->err_color); |
| 510 | 503 | exit(EXIT_FAILURE); |
| ... | ... | @@ -513,20 +506,16 @@ static void ast_asm_error(ParseContext *pc, AstNode *node, int offset, const cha |
| 513 | 506 | __attribute__ ((format (printf, 3, 4))) |
| 514 | 507 | __attribute__ ((noreturn)) |
| 515 | 508 | static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { |
| 516 | ErrorMsg *err = allocate<ErrorMsg>(1); | |
| 517 | err->line_start = token->start_line; | |
| 518 | err->column_start = token->start_column; | |
| 519 | err->line_end = -1; | |
| 520 | err->column_end = -1; | |
| 521 | ||
| 522 | 509 | va_list ap; |
| 523 | 510 | va_start(ap, format); |
| 524 | err->msg = buf_vprintf(format, ap); | |
| 511 | Buf *msg = buf_vprintf(format, ap); | |
| 525 | 512 | va_end(ap); |
| 526 | 513 | |
| 527 | err->path = pc->owner->path; | |
| 528 | err->source = pc->owner->source_code; | |
| 529 | err->line_offsets = pc->owner->line_offsets; | |
| 514 | ||
| 515 | ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, | |
| 516 | pc->owner->source_code, pc->owner->line_offsets, msg); | |
| 517 | err->line_start = token->start_line; | |
| 518 | err->column_start = token->start_column; | |
| 530 | 519 | |
| 531 | 520 | print_err_msg(err, pc->err_color); |
| 532 | 521 | exit(EXIT_FAILURE); |
test/run_tests.cpp+7| ... | ... | @@ -1673,6 +1673,13 @@ fn f(s: []u8) -> []u8 { |
| 1673 | 1673 | } |
| 1674 | 1674 | )SOURCE", 1, ".tmp_source.zig:3:5: error: string concatenation requires constant expression"); |
| 1675 | 1675 | |
| 1676 | add_compile_fail_case("c_import with bogus include", R"SOURCE( | |
| 1677 | c_import { | |
| 1678 | @c_include("bogus.h"); | |
| 1679 | } | |
| 1680 | )SOURCE", 2, ".tmp_source.zig:2:1: error: C import failed", | |
| 1681 | ".h:1:10: error: 'bogus.h' file not found"); | |
| 1682 | ||
| 1676 | 1683 | } |
| 1677 | 1684 | |
| 1678 | 1685 | static void print_compiler_invocation(TestCase *test_case) { |