diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2015-12-03 15:59:14 -0700 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2015-12-03 16:00:14 -0700 |
| commit | ad3f98c615e688387c2d3580321ab17965b21378 (patch) | |
| tree | 41d6b7c5cee74b53fe112bbd1d7726925028f610 /src/parseh.cpp | |
| parent | 174e58a05f94eaee6e04e858d916d28d3f5be3b7 (diff) | |
| download | zig-ad3f98c615e688387c2d3580321ab17965b21378.tar.gz zig-ad3f98c615e688387c2d3580321ab17965b21378.zip | |
parseh command, parses a C .h file and produces extern decls
Diffstat (limited to 'src/parseh.cpp')
| -rw-r--r-- | src/parseh.cpp | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/src/parseh.cpp b/src/parseh.cpp new file mode 100644 index 0000000000..fc74f5dbdf --- /dev/null +++ b/src/parseh.cpp @@ -0,0 +1,351 @@ +#include "parseh.hpp" + +#include <clang-c/Index.h> + +#include <string.h> + +struct Arg { + Buf name; + Buf *type; +}; + +struct Fn { + Buf name; + Buf *return_type; + Arg *args; + int arg_count; +}; + +struct ParseH { + FILE *f; + ZigList<Fn *> fn_list; + Fn *cur_fn; + int arg_index; + int cur_indent; +}; + +static const int indent_size = 4; + +static bool str_has_prefix(const char *str, const char *prefix) { + while (*prefix) { + if (*str && *str == *prefix) { + str += 1; + prefix += 1; + } else { + return false; + } + } + return true; +} + +static const char *prefixes_stripped(CXType type) { + CXString name = clang_getTypeSpelling(type); + const char *c_name = clang_getCString(name); + + static const char *prefixes[] = { + "struct ", + "enum ", + "const ", + }; + +start_over: + + for (int i = 0; i < array_length(prefixes); i += 1) { + const char *prefix = prefixes[i]; + if (str_has_prefix(c_name, prefix)) { + c_name += strlen(prefix); + goto start_over; + } + } + return c_name; +} + +static Buf *to_zig_type(CXType raw_type) { + CXType canonical = clang_getCanonicalType(raw_type); + switch (canonical.kind) { + case CXType_Invalid: + zig_unreachable(); + case CXType_Unexposed: + zig_panic("clang C api insufficient"); + case CXType_Void: + return buf_create_from_str("void"); + case CXType_Bool: + return buf_create_from_str("bool"); + case CXType_SChar: + return buf_create_from_str("i8"); + case CXType_Char_U: + case CXType_Char_S: + case CXType_UChar: + return buf_create_from_str("u8"); + case CXType_WChar: + zig_panic("TODO"); + case CXType_Char16: + zig_panic("TODO"); + case CXType_Char32: + zig_panic("TODO"); + case CXType_UShort: + return buf_create_from_str("c_ushort"); + case CXType_UInt: + return buf_create_from_str("c_uint"); + case CXType_ULong: + return buf_create_from_str("c_ulong"); + case CXType_ULongLong: + return buf_create_from_str("c_ulonglong"); + case CXType_UInt128: + zig_panic("TODO"); + case CXType_Short: + return buf_create_from_str("c_short"); + case CXType_Int: + return buf_create_from_str("c_int"); + case CXType_Long: + return buf_create_from_str("c_long"); + case CXType_LongLong: + return buf_create_from_str("c_longlong"); + case CXType_Int128: + zig_panic("TODO"); + case CXType_Float: + return buf_create_from_str("f32"); + case CXType_Double: + return buf_create_from_str("f64"); + case CXType_LongDouble: + return buf_create_from_str("f128"); + case CXType_NullPtr: + zig_panic("TODO"); + case CXType_Overload: + zig_panic("TODO"); + case CXType_Dependent: + zig_panic("TODO"); + case CXType_ObjCId: + zig_panic("TODO"); + case CXType_ObjCClass: + zig_panic("TODO"); + case CXType_ObjCSel: + zig_panic("TODO"); + case CXType_Complex: + zig_panic("TODO"); + case CXType_Pointer: + { + CXType pointee_type = clang_getPointeeType(canonical); + Buf *pointee_buf = to_zig_type(pointee_type); + if (clang_isConstQualifiedType(pointee_type)) { + return buf_sprintf("*const %s", buf_ptr(pointee_buf)); + } else { + return buf_sprintf("*mut %s", buf_ptr(pointee_buf)); + } + } + case CXType_BlockPointer: + zig_panic("TODO"); + case CXType_LValueReference: + zig_panic("TODO"); + case CXType_RValueReference: + zig_panic("TODO"); + case CXType_Record: + { + const char *name = prefixes_stripped(canonical); + return buf_sprintf("%s", name); + } + case CXType_Enum: + { + const char *name = prefixes_stripped(canonical); + return buf_sprintf("%s", name); + } + case CXType_Typedef: + zig_panic("TODO"); + case CXType_ObjCInterface: + zig_panic("TODO"); + case CXType_ObjCObjectPointer: + zig_panic("TODO"); + case CXType_FunctionNoProto: + zig_panic("TODO"); + case CXType_FunctionProto: + zig_panic("TODO"); + case CXType_ConstantArray: + zig_panic("TODO"); + case CXType_Vector: + zig_panic("TODO"); + case CXType_IncompleteArray: + zig_panic("TODO"); + case CXType_VariableArray: + zig_panic("TODO"); + case CXType_DependentSizedArray: + zig_panic("TODO"); + case CXType_MemberPointer: + zig_panic("TODO"); + } + + zig_unreachable(); +} + +static bool is_storage_class_export(CX_StorageClass storage_class) { + switch (storage_class) { + case CX_SC_Invalid: + zig_unreachable(); + case CX_SC_None: + case CX_SC_Extern: + case CX_SC_Auto: + return true; + case CX_SC_Static: + case CX_SC_PrivateExtern: + case CX_SC_OpenCLWorkGroupLocal: + case CX_SC_Register: + return false; + } + zig_unreachable(); +} + +static void begin_fn(ParseH *p) { + assert(!p->cur_fn); + p->cur_fn = allocate<Fn>(1); +} + +static void end_fn(ParseH *p) { + if (p->cur_fn) { + p->fn_list.append(p->cur_fn); + p->cur_fn = nullptr; + } +} + +static enum CXChildVisitResult fn_visitor(CXCursor cursor, CXCursor parent, CXClientData client_data) { + ParseH *p = (ParseH*)client_data; + enum CXCursorKind kind = clang_getCursorKind(cursor); + CXString name = clang_getCursorSpelling(cursor); + + switch (kind) { + case CXCursor_FunctionDecl: + { + CX_StorageClass storage_class = clang_Cursor_getStorageClass(cursor); + if (!is_storage_class_export(storage_class)) + return CXChildVisit_Continue; + + end_fn(p); + begin_fn(p); + + CXType fn_type = clang_getCursorType(cursor); + if (clang_isFunctionTypeVariadic(fn_type)) { + zig_panic("TODO support variadic function"); + } + if (clang_getFunctionTypeCallingConv(fn_type) != CXCallingConv_C) { + zig_panic("TODO support non c calling convention"); + } + CXType return_type = clang_getResultType(fn_type); + p->cur_fn->return_type = to_zig_type(return_type); + + buf_init_from_str(&p->cur_fn->name, clang_getCString(name)); + + p->cur_fn->arg_count = clang_getNumArgTypes(fn_type); + p->cur_fn->args = allocate<Arg>(p->cur_fn->arg_count); + + for (int i = 0; i < p->cur_fn->arg_count; i += 1) { + CXType param_type = clang_getArgType(fn_type, i); + p->cur_fn->args[i].type = to_zig_type(param_type); + } + + p->arg_index = 0; + + return CXChildVisit_Recurse; + } + case CXCursor_ParmDecl: + { + assert(p->cur_fn); + assert(p->arg_index < p->cur_fn->arg_count); + buf_init_from_str(&p->cur_fn->args[p->arg_index].name, clang_getCString(name)); + p->arg_index += 1; + return CXChildVisit_Continue; + } + case CXCursor_UnexposedAttr: + case CXCursor_CompoundStmt: + case CXCursor_FieldDecl: + return CXChildVisit_Continue; + default: + return CXChildVisit_Recurse; + } +} + +static void print_indent(ParseH *p) { + for (int i = 0; i < p->cur_indent; i += 1) { + fprintf(p->f, " "); + } +} + +void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FILE *f) { + ParseH parse_h = {0}; + ParseH *p = &parse_h; + p->f = f; + CXTranslationUnit tu; + CXIndex index = clang_createIndex(1, 0); + + char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS"); + if (ZIG_PARSEH_CFLAGS) { + Buf tmp_buf = {0}; + char *start = ZIG_PARSEH_CFLAGS; + char *space = strstr(start, " "); + while (space) { + if (space - start > 0) { + buf_init_from_mem(&tmp_buf, start, space - start); + clang_argv->append(buf_ptr(buf_create_from_buf(&tmp_buf))); + } + start = space + 1; + space = strstr(start, " "); + } + buf_init_from_str(&tmp_buf, start); + clang_argv->append(buf_ptr(buf_create_from_buf(&tmp_buf))); + } + + clang_argv->append(nullptr); + + enum CXErrorCode err_code; + if ((err_code = clang_parseTranslationUnit2(index, target_path, + clang_argv->items, clang_argv->length - 1, + NULL, 0, CXTranslationUnit_None, &tu))) + { + zig_panic("parse translation unit failure"); + } + + + unsigned diag_count = clang_getNumDiagnostics(tu); + + if (diag_count > 0) { + for (unsigned i = 0; i < diag_count; i += 1) { + CXDiagnostic diagnostic = clang_getDiagnostic(tu, i); + CXSourceLocation location = clang_getDiagnosticLocation(diagnostic); + + CXFile file; + unsigned line, column, offset; + clang_getSpellingLocation(location, &file, &line, &column, &offset); + CXString text = clang_getDiagnosticSpelling(diagnostic); + CXString file_name = clang_getFileName(file); + fprintf(stderr, "%s line %u, column %u: %s\n", clang_getCString(file_name), + line, column, clang_getCString(text)); + } + + exit(1); + } + + + CXCursor cursor = clang_getTranslationUnitCursor(tu); + clang_visitChildren(cursor, fn_visitor, p); + end_fn(p); + + if (p->fn_list.length) { + fprintf(f, "extern {\n"); + p->cur_indent += indent_size; + for (int fn_i = 0; fn_i < p->fn_list.length; fn_i += 1) { + Fn *fn = p->fn_list.at(fn_i); + print_indent(p); + fprintf(p->f, "fn %s(", buf_ptr(&fn->name)); + for (int arg_i = 0; arg_i < fn->arg_count; arg_i += 1) { + Arg *arg = &fn->args[arg_i]; + fprintf(p->f, "%s: %s", buf_ptr(&arg->name), buf_ptr(arg->type)); + if (arg_i + 1 < fn->arg_count) { + fprintf(p->f, ", "); + } + } + fprintf(p->f, ")"); + if (!buf_eql_str(fn->return_type, "void")) { + fprintf(p->f, " -> %s", buf_ptr(fn->return_type)); + } + fprintf(p->f, ";\n"); + } + fprintf(f, "}\n"); + } +} |
