diff --git a/src-self-hosted/c_tokenizer.zig b/src-self-hosted/c_tokenizer.zig new file mode 100644 index 0000000000..9715661cd3 --- /dev/null +++ b/src-self-hosted/c_tokenizer.zig @@ -0,0 +1,656 @@ +const std = @import("std"); +const expect = std.testing.expect; + +pub const TokenList = std.SegmentedList(CToken, 32); + +pub const CToken = struct { + id: Id, + bytes: []const u8, + num_lit_suffix: NumLitSuffix = .None, + + pub const Id = enum { + CharLit, + StrLit, + NumLitInt, + NumLitFloat, + Identifier, + Minus, + Slash, + LParen, + RParen, + Eof, + Dot, + Asterisk, + Bang, + Tilde, + Shl, + Lt, + Comma, + Fn, + }; + + pub const NumLitSuffix = enum { + None, + F, + L, + U, + LU, + LL, + LLU, + }; +}; + +pub fn tokenizeCMacro(tl: *TokenList, chars: [*:0]const u8) !void { + var index: usize = 0; + var first = true; + while (true) { + const tok = try next(chars, &index); + if (tok.id == .StrLit or tok.id == .CharLit) + try tl.push(try zigifyEscapeSequences(tl.allocator, tok)) + else + try tl.push(tok); + if (tok.id == .Eof) + return; + if (first) { + // distinguish NAME (EXPR) from NAME(ARGS) + first = false; + if (chars[index] == '(') { + try tl.push(.{ + .id = .Fn, + .bytes = "", + }); + } + } + } +} + +fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken { + for (tok.bytes) |c| { + if (c == '\\') { + break; + } + } else return tok; + var bytes = try allocator.alloc(u8, tok.bytes.len * 2); + var escape = false; + var i: usize = 0; + for (tok.bytes) |c| { + if (escape) { + switch (c) { + 'n', 'r', 't', '\\', '\'', '\"', 'x' => { + bytes[i] = c; + }, + 'a' => { + bytes[i] = 'x'; + i += 1; + bytes[i] = '0'; + i += 1; + bytes[i] = '7'; + }, + 'b' => { + bytes[i] = 'x'; + i += 1; + bytes[i] = '0'; + i += 1; + bytes[i] = '8'; + }, + 'f' => { + bytes[i] = 'x'; + i += 1; + bytes[i] = '0'; + i += 1; + bytes[i] = 'C'; + }, + 'v' => { + bytes[i] = 'x'; + i += 1; + bytes[i] = '0'; + i += 1; + bytes[i] = 'B'; + }, + '?' => { + i -= 1; + bytes[i] = '?'; + }, + 'u', 'U' => { + // TODO unicode escape sequences + return error.TokenizingFailed; + }, + '0'...'7' => { + // TODO octal escape sequences + return error.TokenizingFailed; + }, + else => { + // unknown escape sequence + return error.TokenizingFailed; + }, + } + i += 1; + escape = false; + } else { + if (c == '\\') { + escape = true; + } + bytes[i] = c; + i += 1; + } + } + return CToken{ + .id = tok.id, + .bytes = bytes[0..i], + }; +} + +fn next(chars: [*:0]const u8, i: *usize) !CToken { + var state: enum { + Start, + GotLt, + CharLit, + OpenComment, + Comment, + CommentStar, + Backslash, + String, + Identifier, + Decimal, + Octal, + GotZero, + Hex, + Bin, + Float, + ExpSign, + FloatExp, + FloatExpFirst, + NumLitIntSuffixU, + NumLitIntSuffixL, + NumLitIntSuffixLL, + NumLitIntSuffixUL, + } = .Start; + + var result = CToken{ + .bytes = "", + .id = .Eof, + }; + var begin_index: usize = 0; + var digits: u8 = 0; + var pre_escape = state; + + while (true) { + const c = chars[i.*]; + if (c == 0) { + switch (state) { + .Start => { + return result; + }, + .Identifier, + .Decimal, + .Hex, + .Bin, + .Octal, + .GotZero, + .Float, + .FloatExp, + => { + result.bytes = chars[begin_index..i.*]; + return result; + }, + .NumLitIntSuffixU, + .NumLitIntSuffixL, + .NumLitIntSuffixUL, + .NumLitIntSuffixLL, + .GotLt, + => { + return result; + }, + .CharLit, + .OpenComment, + .Comment, + .CommentStar, + .Backslash, + .String, + .ExpSign, + .FloatExpFirst, + => return error.TokenizingFailed, + } + } + i.* += 1; + switch (state) { + .Start => { + switch (c) { + ' ', '\t', '\x0B', '\x0C' => {}, + '\'' => { + state = .CharLit; + result.id = .CharLit; + begin_index = i.* - 1; + }, + '\"' => { + state = .String; + result.id = .StrLit; + begin_index = i.* - 1; + }, + '/' => { + state = .OpenComment; + }, + '\\' => { + state = .Backslash; + }, + '\n', '\r' => { + return result; + }, + 'a'...'z', 'A'...'Z', '_' => { + state = .Identifier; + result.id = .Identifier; + begin_index = i.* - 1; + }, + '1'...'9' => { + state = .Decimal; + result.id = .NumLitInt; + begin_index = i.* - 1; + }, + '0' => { + state = .GotZero; + result.id = .NumLitInt; + begin_index = i.* - 1; + }, + '.' => { + result.id = .Dot; + return result; + }, + '<' => { + result.id = .Lt; + state = .GotLt; + }, + '(' => { + result.id = .LParen; + return result; + }, + ')' => { + result.id = .RParen; + return result; + }, + '*' => { + result.id = .Asterisk; + return result; + }, + '-' => { + result.id = .Minus; + return result; + }, + '!' => { + result.id = .Bang; + return result; + }, + '~' => { + result.id = .Tilde; + return result; + }, + ',' => { + result.id = .Comma; + return result; + }, + else => return error.TokenizingFailed, + } + }, + .GotLt => { + switch (c) { + '<' => { + result.id = .Shl; + return result; + }, + else => { + return result; + }, + } + }, + .Float => { + switch (c) { + '.', '0'...'9' => {}, + 'e', 'E' => { + state = .ExpSign; + }, + 'f', + 'F', + => { + i.* -= 1; + result.num_lit_suffix = .F; + result.bytes = chars[begin_index..i.*]; + return result; + }, + 'l', 'L' => { + i.* -= 1; + result.num_lit_suffix = .L; + result.bytes = chars[begin_index..i.*]; + return result; + }, + else => { + i.* -= 1; + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .ExpSign => { + switch (c) { + '+', '-' => { + state = .FloatExpFirst; + }, + '0'...'9' => { + state = .FloatExp; + }, + else => return error.TokenizingFailed, + } + }, + .FloatExpFirst => { + switch (c) { + '0'...'9' => { + state = .FloatExp; + }, + else => return error.TokenizingFailed, + } + }, + .FloatExp => { + switch (c) { + '0'...'9' => {}, + 'f', 'F' => { + result.num_lit_suffix = .F; + result.bytes = chars[begin_index .. i.* - 1]; + return result; + }, + 'l', 'L' => { + result.num_lit_suffix = .L; + result.bytes = chars[begin_index .. i.* - 1]; + return result; + }, + else => { + i.* -= 1; + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .Decimal => { + switch (c) { + '0'...'9' => {}, + '\'' => {}, + 'u', 'U' => { + state = .NumLitIntSuffixU; + result.num_lit_suffix = .U; + result.bytes = chars[begin_index .. i.* - 1]; + }, + 'l', 'L' => { + state = .NumLitIntSuffixL; + result.num_lit_suffix = .L; + result.bytes = chars[begin_index .. i.* - 1]; + }, + '.' => { + result.id = .NumLitFloat; + state = .Float; + }, + else => { + i.* -= 1; + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .GotZero => { + switch (c) { + 'x', 'X' => { + state = .Hex; + }, + 'b', 'B' => { + state = .Bin; + }, + '.' => { + state = .Float; + result.id = .NumLitFloat; + }, + 'u', 'U' => { + state = .NumLitIntSuffixU; + result.num_lit_suffix = .U; + result.bytes = chars[begin_index .. i.* - 1]; + }, + 'l', 'L' => { + state = .NumLitIntSuffixL; + result.num_lit_suffix = .L; + result.bytes = chars[begin_index .. i.* - 1]; + }, + else => { + i.* -= 1; + state = .Octal; + }, + } + }, + .Octal => { + switch (c) { + '0'...'7' => {}, + '8', '9' => return error.TokenizingFailed, + else => { + i.* -= 1; + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .Hex => { + switch (c) { + '0'...'9', 'a'...'f', 'A'...'F' => {}, + 'u', 'U' => { + // marks the number literal as unsigned + state = .NumLitIntSuffixU; + result.num_lit_suffix = .U; + result.bytes = chars[begin_index .. i.* - 1]; + }, + 'l', 'L' => { + // marks the number literal as long + state = .NumLitIntSuffixL; + result.num_lit_suffix = .L; + result.bytes = chars[begin_index .. i.* - 1]; + }, + else => { + i.* -= 1; + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .Bin => { + switch (c) { + '0'...'1' => {}, + '2'...'9' => return error.TokenizingFailed, + 'u', 'U' => { + // marks the number literal as unsigned + state = .NumLitIntSuffixU; + result.num_lit_suffix = .U; + result.bytes = chars[begin_index .. i.* - 1]; + }, + 'l', 'L' => { + // marks the number literal as long + state = .NumLitIntSuffixL; + result.num_lit_suffix = .L; + result.bytes = chars[begin_index .. i.* - 1]; + }, + else => { + i.* -= 1; + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .NumLitIntSuffixU => { + switch (c) { + 'l', 'L' => { + result.num_lit_suffix = .LU; + state = .NumLitIntSuffixUL; + }, + else => { + i.* -= 1; + return result; + }, + } + }, + .NumLitIntSuffixL => { + switch (c) { + 'l', 'L' => { + result.num_lit_suffix = .LL; + state = .NumLitIntSuffixLL; + }, + 'u', 'U' => { + result.num_lit_suffix = .LU; + return result; + }, + else => { + i.* -= 1; + return result; + }, + } + }, + .NumLitIntSuffixLL => { + switch (c) { + 'u', 'U' => { + result.num_lit_suffix = .LLU; + return result; + }, + else => { + i.* -= 1; + return result; + }, + } + }, + .NumLitIntSuffixUL => { + switch (c) { + 'l', 'L' => { + result.num_lit_suffix = .LLU; + return result; + }, + else => { + i.* -= 1; + return result; + }, + } + }, + .Identifier => { + switch (c) { + '_', 'a'...'z', 'A'...'Z', '0'...'9' => {}, + else => { + i.* -= 1; + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .String => { // TODO char escapes + switch (c) { + '\"' => { + result.bytes = chars[begin_index..i.*]; + return result; + }, + else => {}, + } + }, + .CharLit => { + switch (c) { + '\'' => { + result.bytes = chars[begin_index..i.*]; + return result; + }, + else => {}, + } + }, + .OpenComment => { + switch (c) { + '/' => { + return result; + }, + '*' => { + state = .Comment; + }, + else => { + result.id = .Slash; + return result; + }, + } + }, + .Comment => { + switch (c) { + '*' => { + state = .CommentStar; + }, + else => {}, + } + }, + .CommentStar => { + switch (c) { + '/' => { + state = .Start; + }, + else => { + state = .Comment; + }, + } + }, + .Backslash => { + switch (c) { + ' ', '\t', '\x0B', '\x0C' => {}, + '\n', '\r' => { + state = .Start; + }, + else => return error.TokenizingFailed, + } + }, + } + } + unreachable; +} + +test "tokenize macro" { + var tl = TokenList.init(std.heap.page_allocator); + defer tl.deinit(); + + const src = "TEST(0\n"; + try tokenizeCMacro(&tl, src); + var it = tl.iterator(0); + expect(it.next().?.id == .Identifier); + expect(it.next().?.id == .Fn); + expect(it.next().?.id == .LParen); + expect(std.mem.eql(u8, it.next().?.bytes, "0")); + expect(it.next().?.id == .Eof); + expect(it.next() == null); + tl.shrink(0); + + const src2 = "__FLT_MIN_10_EXP__ -37\n"; + try tokenizeCMacro(&tl, src2); + it = tl.iterator(0); + expect(std.mem.eql(u8, it.next().?.bytes, "__FLT_MIN_10_EXP__")); + expect(it.next().?.id == .Minus); + expect(std.mem.eql(u8, it.next().?.bytes, "37")); + expect(it.next().?.id == .Eof); + expect(it.next() == null); + tl.shrink(0); + + const src3 = "__llvm__ 1\n#define"; + try tokenizeCMacro(&tl, src3); + it = tl.iterator(0); + expect(std.mem.eql(u8, it.next().?.bytes, "__llvm__")); + expect(std.mem.eql(u8, it.next().?.bytes, "1")); + expect(it.next().?.id == .Eof); + expect(it.next() == null); + tl.shrink(0); + + const src4 = "TEST 2"; + try tokenizeCMacro(&tl, src4); + it = tl.iterator(0); + expect(it.next().?.id == .Identifier); + expect(std.mem.eql(u8, it.next().?.bytes, "2")); + expect(it.next().?.id == .Eof); + expect(it.next() == null); + tl.shrink(0); + + const src5 = "FOO 0l"; + try tokenizeCMacro(&tl, src5); + it = tl.iterator(0); + expect(it.next().?.id == .Identifier); + expect(std.mem.eql(u8, it.next().?.bytes, "0")); + expect(it.next().?.id == .Eof); + expect(it.next() == null); + tl.shrink(0); +} diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 4b3aa44fab..b9435f96f4 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -75,6 +75,7 @@ pub const struct_ZigClangWhileStmt = @OpaqueType(); pub const struct_ZigClangFunctionType = @OpaqueType(); pub const struct_ZigClangPredefinedExpr = @OpaqueType(); pub const struct_ZigClangInitListExpr = @OpaqueType(); +pub const ZigClangPreprocessingRecord = @OpaqueType(); pub const ZigClangBO = extern enum { PtrMemD, @@ -717,11 +718,23 @@ pub const ZigClangEnumDecl_enumerator_iterator = extern struct { opaque: *c_void, }; +pub const ZigClangPreprocessingRecord_iterator = extern struct { + I: c_int, + Self: *ZigClangPreprocessingRecord, +}; + +pub const ZigClangPreprocessedEntity_EntityKind = extern enum { + InvalidKind, + MacroExpansionKind, + MacroDefinitionKind, + InclusionDirectiveKind, +}; + pub extern fn ZigClangSourceManager_getSpellingLoc(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) struct_ZigClangSourceLocation; pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*:0]const u8; pub extern fn ZigClangSourceManager_getSpellingLineNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint; pub extern fn ZigClangSourceManager_getSpellingColumnNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint; -pub extern fn ZigClangSourceManager_getCharacterData(self: ?*const struct_ZigClangSourceManager, SL: struct_ZigClangSourceLocation) [*c]const u8; +pub extern fn ZigClangSourceManager_getCharacterData(self: ?*const struct_ZigClangSourceManager, SL: struct_ZigClangSourceLocation) [*:0]const u8; pub extern fn ZigClangASTContext_getPointerType(self: ?*const struct_ZigClangASTContext, T: struct_ZigClangQualType) struct_ZigClangQualType; pub extern fn ZigClangASTUnit_getASTContext(self: ?*struct_ZigClangASTUnit) ?*struct_ZigClangASTContext; pub extern fn ZigClangASTUnit_getSourceManager(self: *struct_ZigClangASTUnit) *struct_ZigClangSourceManager; @@ -751,14 +764,14 @@ pub extern fn ZigClangEnumDecl_enumerator_end(*const ZigClangEnumDecl) ZigClangE pub extern fn ZigClangEnumDecl_enumerator_iterator_next(ZigClangEnumDecl_enumerator_iterator) ZigClangEnumDecl_enumerator_iterator; pub extern fn ZigClangEnumDecl_enumerator_iterator_deref(ZigClangEnumDecl_enumerator_iterator) *const ZigClangEnumConstantDecl; pub extern fn ZigClangEnumDecl_enumerator_iterator_neq(ZigClangEnumDecl_enumerator_iterator, ZigClangEnumDecl_enumerator_iterator) bool; -pub extern fn ZigClangDecl_getName_bytes_begin(decl: ?*const struct_ZigClangDecl) [*c]const u8; +pub extern fn ZigClangDecl_getName_bytes_begin(decl: ?*const struct_ZigClangDecl) [*:0]const u8; pub extern fn ZigClangSourceLocation_eq(a: struct_ZigClangSourceLocation, b: struct_ZigClangSourceLocation) bool; pub extern fn ZigClangTypedefType_getDecl(self: ?*const struct_ZigClangTypedefType) *const struct_ZigClangTypedefNameDecl; pub extern fn ZigClangTypedefNameDecl_getUnderlyingType(self: ?*const struct_ZigClangTypedefNameDecl) struct_ZigClangQualType; pub extern fn ZigClangQualType_getCanonicalType(self: struct_ZigClangQualType) struct_ZigClangQualType; pub extern fn ZigClangQualType_getTypeClass(self: struct_ZigClangQualType) ZigClangTypeClass; pub extern fn ZigClangQualType_getTypePtr(self: struct_ZigClangQualType) *const struct_ZigClangType; -pub extern fn ZigClangQualType_addConst(self: [*c]struct_ZigClangQualType) void; +pub extern fn ZigClangQualType_addConst(self: *struct_ZigClangQualType) void; pub extern fn ZigClangQualType_eq(self: struct_ZigClangQualType, arg1: struct_ZigClangQualType) bool; pub extern fn ZigClangQualType_isConstQualified(self: struct_ZigClangQualType) bool; pub extern fn ZigClangQualType_isVolatileQualified(self: struct_ZigClangQualType) bool; @@ -786,7 +799,7 @@ pub extern fn ZigClangAPSInt_isSigned(self: ?*const struct_ZigClangAPSInt) bool; pub extern fn ZigClangAPSInt_isNegative(self: ?*const struct_ZigClangAPSInt) bool; pub extern fn ZigClangAPSInt_negate(self: ?*const struct_ZigClangAPSInt) ?*const struct_ZigClangAPSInt; pub extern fn ZigClangAPSInt_free(self: ?*const struct_ZigClangAPSInt) void; -pub extern fn ZigClangAPSInt_getRawData(self: ?*const struct_ZigClangAPSInt) [*c]const u64; +pub extern fn ZigClangAPSInt_getRawData(self: ?*const struct_ZigClangAPSInt) [*:0]const u64; pub extern fn ZigClangAPSInt_getNumWords(self: ?*const struct_ZigClangAPSInt) c_uint; pub extern fn ZigClangAPInt_getLimitedValue(self: *const struct_ZigClangAPInt, limit: u64) u64; @@ -918,25 +931,25 @@ pub const struct_ZigClangAPValueLValueBase = extern struct { Version: c_uint, }; -pub extern fn ZigClangErrorMsg_delete(ptr: [*c]Stage2ErrorMsg, len: usize) void; +pub extern fn ZigClangErrorMsg_delete(ptr: [*]Stage2ErrorMsg, len: usize) void; pub extern fn ZigClangLoadFromCommandLine( args_begin: [*]?[*]const u8, args_end: [*]?[*]const u8, errors_ptr: *[*]Stage2ErrorMsg, errors_len: *usize, - resources_path: [*c]const u8, + resources_path: [*:0]const u8, ) ?*ZigClangASTUnit; pub extern fn ZigClangDecl_getKind(decl: *const ZigClangDecl) ZigClangDeclKind; pub extern fn ZigClangDecl_getDeclKindName(decl: *const struct_ZigClangDecl) [*:0]const u8; -pub const ZigClangCompoundStmt_const_body_iterator = [*c]const *struct_ZigClangStmt; +pub const ZigClangCompoundStmt_const_body_iterator = [*]const *struct_ZigClangStmt; pub extern fn ZigClangCompoundStmt_body_begin(self: *const ZigClangCompoundStmt) ZigClangCompoundStmt_const_body_iterator; pub extern fn ZigClangCompoundStmt_body_end(self: *const ZigClangCompoundStmt) ZigClangCompoundStmt_const_body_iterator; -pub const ZigClangDeclStmt_const_decl_iterator = [*c]const *struct_ZigClangDecl; +pub const ZigClangDeclStmt_const_decl_iterator = [*]const *struct_ZigClangDecl; pub extern fn ZigClangDeclStmt_decl_begin(self: *const ZigClangDeclStmt) ZigClangDeclStmt_const_decl_iterator; pub extern fn ZigClangDeclStmt_decl_end(self: *const ZigClangDeclStmt) ZigClangDeclStmt_const_decl_iterator; @@ -1004,7 +1017,7 @@ pub extern fn ZigClangBinaryOperator_getType(*const ZigClangBinaryOperator) ZigC pub extern fn ZigClangDecayedType_getDecayedType(*const ZigClangDecayedType) ZigClangQualType; pub extern fn ZigClangStringLiteral_getKind(*const ZigClangStringLiteral) ZigClangStringLiteral_StringKind; -pub extern fn ZigClangStringLiteral_getString_bytes_begin_size(*const ZigClangStringLiteral, *usize) [*c]const u8; +pub extern fn ZigClangStringLiteral_getString_bytes_begin_size(*const ZigClangStringLiteral, *usize) [*]const u8; pub extern fn ZigClangParenExpr_getSubExpr(*const ZigClangParenExpr) *const ZigClangExpr; @@ -1014,3 +1027,12 @@ pub extern fn ZigClangFieldDecl_getLocation(*const struct_ZigClangFieldDecl) str pub extern fn ZigClangEnumConstantDecl_getInitExpr(*const ZigClangEnumConstantDecl) ?*const ZigClangExpr; pub extern fn ZigClangEnumConstantDecl_getInitVal(*const ZigClangEnumConstantDecl) *const ZigClangAPSInt; + +pub extern fn ZigClangASTUnit_getLocalPreprocessingEntities_begin(*ZigClangASTUnit) ZigClangPreprocessingRecord_iterator; +pub extern fn ZigClangASTUnit_getLocalPreprocessingEntities_end(*ZigClangASTUnit) ZigClangPreprocessingRecord_iterator; +pub extern fn ZigClangPreprocessingRecord_iterator_deref(ZigClangPreprocessingRecord_iterator) *ZigClangPreprocessedEntity; +pub extern fn ZigClangPreprocessedEntity_getKind(*const ZigClangPreprocessedEntity) ZigClangPreprocessedEntity_EntityKind; + +pub extern fn ZigClangMacroDefinitionRecord_getName_getNameStart(*const ZigClangMacroDefinitionRecord) [*:0]const u8; +pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getBegin(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation; +pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getEnd(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation; diff --git a/src-self-hosted/stage1.zig b/src-self-hosted/stage1.zig index baa4c17ed0..6f1faa3f3f 100644 --- a/src-self-hosted/stage1.zig +++ b/src-self-hosted/stage1.zig @@ -93,7 +93,7 @@ export fn stage2_translate_c( out_errors_len: *usize, args_begin: [*]?[*]const u8, args_end: [*]?[*]const u8, - resources_path: [*]const u8, + resources_path: [*:0]const u8, ) Error { var errors: []translate_c.ClangErrMsg = undefined; out_ast.* = translate_c.translate(std.heap.c_allocator, args_begin, args_end, &errors, resources_path) catch |err| switch (err) { diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 9d011b1255..54ef53353d 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -6,6 +6,8 @@ const assert = std.debug.assert; const ast = std.zig.ast; const Token = std.zig.Token; usingnamespace @import("clang.zig"); +const ctok = @import("c_tokenizer.zig"); +const CToken = ctok.CToken; const CallingConvention = std.builtin.TypeInfo.CallingConvention; @@ -31,7 +33,7 @@ fn addrEql(a: usize, b: usize) bool { return a == b; } -const SymbolTable = std.StringHashMap(void); +const SymbolTable = std.StringHashMap(*ast.Node); const AliasList = std.SegmentedList(struct { alias: []const u8, name: []const u8, @@ -43,59 +45,151 @@ const Scope = struct { const Id = enum { Switch, - Var, Block, Root, While, + FnDef, + Ref, }; + const Switch = struct { base: Scope, }; - const Var = struct { + /// used when getting a member `a.b` + const Ref = struct { base: Scope, - c_name: []const u8, - zig_name: []const u8, }; const Block = struct { base: Scope, block_node: *ast.Node.Block, + variables: AliasList, /// Don't forget to set rbrace token later - fn create(c: *Context, parent: *Scope, lbrace_tok: ast.TokenIndex) !*Block { + fn init(c: *Context, parent: *Scope, block_node: *ast.Node.Block) !*Block { const block = try c.a().create(Block); - block.* = Block{ - .base = Scope{ - .id = Id.Block, + block.* = .{ + .base = .{ + .id = .Block, .parent = parent, }, - .block_node = try c.a().create(ast.Node.Block), - }; - block.block_node.* = ast.Node.Block{ - .base = ast.Node{ .id = ast.Node.Id.Block }, - .label = null, - .lbrace = lbrace_tok, - .statements = ast.Node.Block.StatementList.init(c.a()), - .rbrace = undefined, + .block_node = block_node, + .variables = AliasList.init(c.a()), }; return block; } + + fn getAlias(scope: *Block, name: []const u8) ?[]const u8 { + var it = scope.variables.iterator(0); + while (it.next()) |p| { + if (std.mem.eql(u8, p.name, name)) + return p.alias; + } + return scope.base.parent.?.getAlias(name); + } + + fn contains(scope: *Block, name: []const u8) bool { + var it = scope.variables.iterator(0); + while (it.next()) |p| { + if (std.mem.eql(u8, p.name, name)) + return true; + } + return scope.base.parent.?.contains(name); + } }; const Root = struct { base: Scope, + sym_table: SymbolTable, + macro_table: SymbolTable, + + fn init(c: *Context) Root { + return .{ + .base = .{ + .id = .Root, + .parent = null, + }, + .sym_table = SymbolTable.init(c.a()), + .macro_table = SymbolTable.init(c.a()), + }; + } + + fn contains(scope: *Root, name: []const u8) bool { + return scope.sym_table.contains(name) or scope.macro_table.contains(name); + } }; const While = struct { base: Scope, }; -}; -const TransResult = struct { - node: *ast.Node, - node_scope: *Scope, - child_scope: *Scope, + const FnDef = struct { + base: Scope, + params: AliasList, + + fn init(c: *Context) FnDef { + return .{ + .base = .{ + .id = .FnDef, + .parent = &c.global_scope.base, + }, + .params = AliasList.init(c.a()), + }; + } + + fn getAlias(scope: *FnDef, name: []const u8) ?[]const u8 { + var it = scope.params.iterator(0); + while (it.next()) |p| { + if (std.mem.eql(u8, p.name, name)) + return p.alias; + } + return scope.base.parent.?.getAlias(name); + } + + fn contains(scope: *FnDef, name: []const u8) bool { + var it = scope.params.iterator(0); + while (it.next()) |p| { + if (std.mem.eql(u8, p.name, name)) + return true; + } + return scope.base.parent.?.contains(name); + } + }; + + fn findBlockScope(inner: *Scope) *Scope.Block { + var scope = inner; + while (true) : (scope = scope.parent orelse unreachable) { + if (scope.id == .Block) return @fieldParentPtr(Scope.Block, "base", scope); + } + } + + fn createAlias(scope: *Scope, c: *Context, name: []const u8) !?[]const u8 { + if (scope.contains(name)) { + return try std.fmt.allocPrint(c.a(), "{}_{}", .{ name, c.getMangle() }); + } + return null; + } + + fn getAlias(scope: *Scope, name: []const u8) ?[]const u8 { + return switch (scope.id) { + .Root => null, + .Ref => null, + .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name), + .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), + else => @panic("TODO Scope.getAlias"), + }; + } + + fn contains(scope: *Scope, name: []const u8) bool { + return switch (scope.id) { + .Ref => false, + .Root => @fieldParentPtr(Root, "base", scope).contains(name), + .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name), + .Block => @fieldParentPtr(Block, "base", scope).contains(name), + else => @panic("TODO Scope.contains"), + }; + } }; const Context = struct { @@ -105,7 +199,6 @@ const Context = struct { source_manager: *ZigClangSourceManager, decl_table: DeclTable, alias_list: AliasList, - sym_table: SymbolTable, global_scope: *Scope.Root, ptr_params: std.BufSet, clang_context: *ZigClangASTContext, @@ -142,7 +235,7 @@ pub fn translate( args_begin: [*]?[*]const u8, args_end: [*]?[*]const u8, errors: *[]ClangErrMsg, - resources_path: [*]const u8, + resources_path: [*:0]const u8, ) !*ast.Tree { const ast_unit = ZigClangLoadFromCommandLine( args_begin, @@ -192,24 +285,22 @@ pub fn translate( .err = undefined, .decl_table = DeclTable.init(arena), .alias_list = AliasList.init(arena), - .sym_table = SymbolTable.init(arena), .global_scope = try arena.create(Scope.Root), .ptr_params = std.BufSet.init(arena), .clang_context = ZigClangASTUnit_getASTContext(ast_unit).?, }; - context.global_scope.* = Scope.Root{ - .base = Scope{ - .id = Scope.Id.Root, - .parent = null, - }, - }; + context.global_scope.* = Scope.Root.init(&context); if (!ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, &context, declVisitorC)) { return context.err; } + + try transPreprocessorEntities(&context, ast_unit); + + try addMacros(&context); var it = context.alias_list.iterator(0); while (it.next()) |alias| { - if (!context.sym_table.contains(alias.alias)) { + if (!context.global_scope.sym_table.contains(alias.alias)) { try createAlias(&context, alias); } } @@ -268,7 +359,8 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl); const fn_qt = ZigClangFunctionDecl_getType(fn_decl); const fn_type = ZigClangQualType_getTypePtr(fn_qt); - var scope = &c.global_scope.base; + var fndef_scope = Scope.FnDef.init(c); + var scope = &fndef_scope.base; const has_body = ZigClangFunctionDecl_hasBody(fn_decl); const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl); const decl_ctx = FnDeclContext{ @@ -314,14 +406,14 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { // actual function definition with body const body_stmt = ZigClangFunctionDecl_getBody(fn_decl); - const result = transStmt(rp, scope, body_stmt, .unused, .r_value) catch |err| switch (err) { + const body_node = transStmt(rp, scope, body_stmt, .unused, .r_value) catch |err| switch (err) { error.OutOfMemory => |e| return e, error.UnsupportedTranslation, error.UnsupportedType, => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}), }; - assert(result.node.id == ast.Node.Id.Block); - proto_node.body_node = result.node; + assert(body_node.id == .Block); + proto_node.body_node = body_node; return addTopLevelDecl(c, fn_name, &proto_node.base); } @@ -336,7 +428,7 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { else try appendToken(c, .Keyword_threadlocal, "threadlocal"); - var scope = &c.global_scope.base; + const scope = &c.global_scope.base; const var_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, var_decl))); _ = try c.decl_table.put(@ptrToInt(var_decl), var_name); const var_decl_loc = ZigClangVarDecl_getLocation(var_decl); @@ -372,17 +464,16 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { if (ZigClangVarDecl_hasInit(var_decl)) { eq_tok = try appendToken(c, .Equal, "="); - init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| blk: { - var res = transExpr(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) { + init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| + transExpr(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) { error.UnsupportedTranslation, error.UnsupportedType, => { return failDecl(c, var_decl_loc, var_name, "unable to translate initializer", .{}); }, error.OutOfMemory => |e| return e, - }; - break :blk res.node; - } else + } + else try transCreateNodeUndefinedLiteral(c); } else if (storage_class != .Extern) { return failDecl(c, var_decl_loc, var_name, "non-extern variable has no initializer", .{}); @@ -548,7 +639,7 @@ fn transStmt( stmt: *const ZigClangStmt, result_used: ResultUsed, lrvalue: LRValue, -) TransError!TransResult { +) TransError!*ast.Node { const sc = ZigClangStmt_getStmtClass(stmt); switch (sc) { .BinaryOperatorClass => return transBinaryOperator(rp, scope, @ptrCast(*const ZigClangBinaryOperator, stmt), result_used), @@ -580,7 +671,7 @@ fn transBinaryOperator( scope: *Scope, stmt: *const ZigClangBinaryOperator, result_used: ResultUsed, -) TransError!TransResult { +) TransError!*ast.Node { const op = ZigClangBinaryOperator_getOpcode(stmt); const qt = ZigClangBinaryOperator_getType(stmt); switch (op) { @@ -591,67 +682,43 @@ fn transBinaryOperator( "TODO: handle more C binary operators: {}", .{op}, ), - .Assign => return TransResult{ - .node = &(try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt))).base, - .child_scope = scope, - .node_scope = scope, - }, + .Assign => return &(try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt))).base, .Add => { const node = if (cIsUnsignedInteger(qt)) try transCreateNodeInfixOp(rp, scope, stmt, .AddWrap, .PlusPercent, "+%", true) else try transCreateNodeInfixOp(rp, scope, stmt, .Add, .Plus, "+", true); - return maybeSuppressResult(rp, scope, result_used, TransResult{ - .node = node, - .child_scope = scope, - .node_scope = scope, - }); + return maybeSuppressResult(rp, scope, result_used, node); }, .Sub => { const node = if (cIsUnsignedInteger(qt)) try transCreateNodeInfixOp(rp, scope, stmt, .SubWrap, .MinusPercent, "-%", true) else try transCreateNodeInfixOp(rp, scope, stmt, .Sub, .Minus, "-", true); - return maybeSuppressResult(rp, scope, result_used, TransResult{ - .node = node, - .child_scope = scope, - .node_scope = scope, - }); + return maybeSuppressResult(rp, scope, result_used, node); }, .Mul => { const node = if (cIsUnsignedInteger(qt)) try transCreateNodeInfixOp(rp, scope, stmt, .MultWrap, .AsteriskPercent, "*%", true) else try transCreateNodeInfixOp(rp, scope, stmt, .Mult, .Asterisk, "*", true); - return maybeSuppressResult(rp, scope, result_used, TransResult{ - .node = node, - .child_scope = scope, - .node_scope = scope, - }); + return maybeSuppressResult(rp, scope, result_used, node); }, .Div => { if (!cIsUnsignedInteger(qt)) { // signed integer division uses @divTrunc const div_trunc_node = try transCreateNodeBuiltinFnCall(rp.c, "@divTrunc"); const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value); - try div_trunc_node.params.push(lhs.node); + try div_trunc_node.params.push(lhs); _ = try appendToken(rp.c, .Comma, ","); const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); - try div_trunc_node.params.push(rhs.node); + try div_trunc_node.params.push(rhs); div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return maybeSuppressResult(rp, scope, result_used, TransResult{ - .node = &div_trunc_node.base, - .child_scope = scope, - .node_scope = scope, - }); + return maybeSuppressResult(rp, scope, result_used, &div_trunc_node.base); } else { // unsigned/float division uses the operator const node = try transCreateNodeInfixOp(rp, scope, stmt, .Div, .Slash, "/", true); - return maybeSuppressResult(rp, scope, result_used, TransResult{ - .node = node, - .child_scope = scope, - .node_scope = scope, - }); + return maybeSuppressResult(rp, scope, result_used, node); } }, .Rem => { @@ -659,24 +726,16 @@ fn transBinaryOperator( // signed integer division uses @rem const rem_node = try transCreateNodeBuiltinFnCall(rp.c, "@rem"); const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value); - try rem_node.params.push(lhs.node); + try rem_node.params.push(lhs); _ = try appendToken(rp.c, .Comma, ","); const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); - try rem_node.params.push(rhs.node); + try rem_node.params.push(rhs); rem_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return maybeSuppressResult(rp, scope, result_used, TransResult{ - .node = &rem_node.base, - .child_scope = scope, - .node_scope = scope, - }); + return maybeSuppressResult(rp, scope, result_used, &rem_node.base); } else { // unsigned/float division uses the operator const node = try transCreateNodeInfixOp(rp, scope, stmt, .Mod, .Percent, "%", true); - return maybeSuppressResult(rp, scope, result_used, TransResult{ - .node = node, - .child_scope = scope, - .node_scope = scope, - }); + return maybeSuppressResult(rp, scope, result_used, node); } }, .Shl, @@ -720,33 +779,22 @@ fn transCompoundStmtInline( parent_scope: *Scope, stmt: *const ZigClangCompoundStmt, block_node: *ast.Node.Block, -) TransError!TransResult { +) TransError!void { var it = ZigClangCompoundStmt_body_begin(stmt); const end_it = ZigClangCompoundStmt_body_end(stmt); - var scope = parent_scope; while (it != end_it) : (it += 1) { - const result = try transStmt(rp, parent_scope, it.*, .unused, .r_value); - scope = result.child_scope; - if (result.node != &block_node.base) - try block_node.statements.push(result.node); + const result = try transStmt(rp, parent_scope, it[0], .unused, .r_value); + if (result != &block_node.base) + try block_node.statements.push(result); } - return TransResult{ - .node = &block_node.base, - .child_scope = scope, - .node_scope = scope, - }; } -fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) !TransResult { - const lbrace_tok = try appendToken(rp.c, .LBrace, "{"); - const block_scope = try Scope.Block.create(rp.c, scope, lbrace_tok); - const inline_result = try transCompoundStmtInline(rp, &block_scope.base, stmt, block_scope.block_node); - block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); - return TransResult{ - .node = &block_scope.block_node.base, - .node_scope = inline_result.node_scope, - .child_scope = inline_result.child_scope, - }; +fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node { + const block_node = try transCreateNodeBlock(rp.c, null); + const block_scope = try Scope.Block.init(rp.c, scope, block_node); + try transCompoundStmtInline(rp, &block_scope.base, stmt, block_node); + block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + return &block_node.base; } fn transCStyleCastExprClass( @@ -755,7 +803,7 @@ fn transCStyleCastExprClass( stmt: *const ZigClangCStyleCastExpr, result_used: ResultUsed, lrvalue: LRValue, -) !TransResult { +) TransError!*ast.Node { const sub_expr = ZigClangCStyleCastExpr_getSubExpr(stmt); const cast_node = (try transCCast( rp, @@ -763,27 +811,21 @@ fn transCStyleCastExprClass( ZigClangCStyleCastExpr_getBeginLoc(stmt), ZigClangCStyleCastExpr_getType(stmt), ZigClangExpr_getType(sub_expr), - (try transExpr(rp, scope, sub_expr, .used, lrvalue)).node, + try transExpr(rp, scope, sub_expr, .used, lrvalue), )); - const cast_res = TransResult{ - .node = cast_node, - .child_scope = scope, - .node_scope = scope, - }; - return maybeSuppressResult(rp, scope, result_used, cast_res); + return maybeSuppressResult(rp, scope, result_used, cast_node); } -fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDeclStmt) !TransResult { +fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) TransError!*ast.Node { const c = rp.c; - const block_scope = findBlockScope(parent_scope); - var scope = parent_scope; + const block_scope = scope.findBlockScope(); var it = ZigClangDeclStmt_decl_begin(stmt); const end_it = ZigClangDeclStmt_decl_end(stmt); while (it != end_it) : (it += 1) { - switch (ZigClangDecl_getKind(it.*)) { + switch (ZigClangDecl_getKind(it[0])) { .Var => { - const var_decl = @ptrCast(*const ZigClangVarDecl, it.*); + const var_decl = @ptrCast(*const ZigClangVarDecl, it[0]); const thread_local_token = if (ZigClangVarDecl_getTLSKind(var_decl) == .None) null @@ -794,18 +836,14 @@ fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDe try appendToken(c, .Keyword_const, "const") else try appendToken(c, .Keyword_var, "var"); - const c_name = try c.str(ZigClangDecl_getName_bytes_begin( + const name = try c.str(ZigClangDecl_getName_bytes_begin( @ptrCast(*const ZigClangDecl, var_decl), )); - const name_token = try appendIdentifier(c, c_name); - - const var_scope = try c.a().create(Scope.Var); - var_scope.* = Scope.Var{ - .base = Scope{ .id = .Var, .parent = scope }, - .c_name = c_name, - .zig_name = c_name, // TODO: getWantedName - }; - scope = &var_scope.base; + const checked_name = if (try scope.createAlias(c, name)) |a| blk: { + try block_scope.variables.push(.{ .name = name, .alias = a }); + break :blk a; + } else name; + const name_token = try appendIdentifier(c, checked_name); const colon_token = try appendToken(c, .Colon, ":"); const loc = ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)); @@ -813,7 +851,7 @@ fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDe const eq_token = try appendToken(c, .Equal, "="); const init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| - (try transExpr(rp, scope, expr, .used, .r_value)).node + try transExpr(rp, scope, expr, .used, .r_value) else try transCreateNodeUndefinedLiteral(c); const semicolon_token = try appendToken(c, .Semicolon, ";"); @@ -837,7 +875,6 @@ fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDe }; try block_scope.block_node.statements.push(&node.base); }, - else => |kind| return revertAndWarn( rp, error.UnsupportedTranslation, @@ -847,12 +884,7 @@ fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDe ), } } - - return TransResult{ - .node = &block_scope.block_node.base, - .node_scope = scope, - .child_scope = scope, - }; + return &block_scope.block_node.base; } fn transDeclRefExpr( @@ -860,17 +892,12 @@ fn transDeclRefExpr( scope: *Scope, expr: *const ZigClangDeclRefExpr, lrvalue: LRValue, -) !TransResult { +) TransError!*ast.Node { const value_decl = ZigClangDeclRefExpr_getDecl(expr); - const c_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, value_decl))); - const zig_name = transLookupZigIdentifier(scope, c_name); - if (lrvalue == .l_value) try rp.c.ptr_params.put(zig_name); - const node = try transCreateNodeIdentifier(rp.c, zig_name); - return TransResult{ - .node = node, - .node_scope = scope, - .child_scope = scope, - }; + const name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, value_decl))); + const checked_name = if (scope.getAlias(name)) |a| a else name; + if (lrvalue == .l_value) try rp.c.ptr_params.put(checked_name); + return transCreateNodeIdentifier(rp.c, checked_name); } fn transImplicitCastExpr( @@ -878,7 +905,7 @@ fn transImplicitCastExpr( scope: *Scope, expr: *const ZigClangImplicitCastExpr, result_used: ResultUsed, -) !TransResult { +) TransError!*ast.Node { const c = rp.c; const sub_expr = ZigClangImplicitCastExpr_getSubExpr(expr); const sub_expr_node = try transExpr(rp, scope, @ptrCast(*const ZigClangExpr, sub_expr), .used, .r_value); @@ -886,20 +913,12 @@ fn transImplicitCastExpr( .BitCast => { const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr)); const src_type = getExprQualType(c, sub_expr); - return TransResult{ - .node = try transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node.node), - .node_scope = scope, - .child_scope = scope, - }; + return transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node); }, .IntegralCast => { const dest_type = ZigClangExpr_getType(@ptrCast(*const ZigClangExpr, expr)); const src_type = ZigClangExpr_getType(sub_expr); - return TransResult{ - .node = try transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node.node), - .node_scope = scope, - .child_scope = scope, - }; + return transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node); }, .FunctionToPointerDecay, .ArrayToPointerDecay => { return maybeSuppressResult(rp, scope, result_used, sub_expr_node); @@ -908,11 +927,7 @@ fn transImplicitCastExpr( return transExpr(rp, scope, sub_expr, .used, .r_value); }, .NullToPointer => { - return TransResult{ - .node = try transCreateNodeNullLiteral(rp.c), - .node_scope = scope, - .child_scope = scope, - }; + return transCreateNodeNullLiteral(rp.c); }, else => |kind| return revertAndWarn( rp, @@ -929,37 +944,27 @@ fn transIntegerLiteral( scope: *Scope, expr: *const ZigClangIntegerLiteral, result_used: ResultUsed, -) !TransResult { +) TransError!*ast.Node { var eval_result: ZigClangExprEvalResult = undefined; if (!ZigClangIntegerLiteral_EvaluateAsInt(expr, &eval_result, rp.c.clang_context)) { const loc = ZigClangIntegerLiteral_getBeginLoc(expr); return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal", .{}); } const node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val)); - const res = TransResult{ - .node = node, - .child_scope = scope, - .node_scope = scope, - }; - return maybeSuppressResult(rp, scope, result_used, res); + return maybeSuppressResult(rp, scope, result_used, node); } fn transReturnStmt( rp: RestorePoint, scope: *Scope, expr: *const ZigClangReturnStmt, -) !TransResult { +) TransError!*ast.Node { const node = try transCreateNodeReturnExpr(rp.c); if (ZigClangReturnStmt_getRetValue(expr)) |val_expr| { - const ret_node = node.cast(ast.Node.ControlFlowExpression).?; - ret_node.rhs = (try transExpr(rp, scope, val_expr, .used, .r_value)).node; + node.rhs = try transExpr(rp, scope, val_expr, .used, .r_value); } _ = try appendToken(rp.c, .Semicolon, ";"); - return TransResult{ - .node = node, - .child_scope = scope, - .node_scope = scope, - }; + return &node.base; } fn transStringLiteral( @@ -967,7 +972,7 @@ fn transStringLiteral( scope: *Scope, stmt: *const ZigClangStringLiteral, result_used: ResultUsed, -) !TransResult { +) TransError!*ast.Node { const kind = ZigClangStringLiteral_getKind(stmt); switch (kind) { .Ascii, .UTF8 => { @@ -989,12 +994,7 @@ fn transStringLiteral( node.* = ast.Node.StringLiteral{ .token = token, }; - const res = TransResult{ - .node = &node.base, - .child_scope = scope, - .node_scope = scope, - }; - return maybeSuppressResult(rp, scope, result_used, res); + return maybeSuppressResult(rp, scope, result_used, &node.base); }, .UTF16, .UTF32, .Wide => return revertAndWarn( rp, @@ -1088,7 +1088,7 @@ fn transExpr( expr: *const ZigClangExpr, used: ResultUsed, lrvalue: LRValue, -) TransError!TransResult { +) TransError!*ast.Node { return transStmt(rp, scope, @ptrCast(*const ZigClangStmt, expr), used, lrvalue); } @@ -1097,7 +1097,7 @@ fn transInitListExpr( scope: *Scope, expr: *const ZigClangInitListExpr, used: ResultUsed, -) TransError!TransResult { +) TransError!*ast.Node { const qt = getExprQualType(rp.c, @ptrCast(*const ZigClangExpr, expr)); const qual_type = ZigClangQualType_getTypePtr(qt); const source_loc = ZigClangExpr_getBeginLoc(@ptrCast(*const ZigClangExpr, expr)); @@ -1128,16 +1128,12 @@ fn transInitListExpr( var i: c_uint = 0; while (i < init_count) : (i += 1) { const elem_expr = ZigClangInitListExpr_getInit(expr, i); - try init_node.op.ArrayInitializer.push((try transExpr(rp, scope, elem_expr, .used, .r_value)).node); + try init_node.op.ArrayInitializer.push(try transExpr(rp, scope, elem_expr, .used, .r_value)); _ = try appendToken(rp.c, .Comma, ","); } init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); if (leftover_count == 0) { - return TransResult{ - .node = &init_node.base, - .child_scope = scope, - .node_scope = scope, - }; + return &init_node.base; } cat_tok = try appendToken(rp.c, .PlusPlus, "++"); } @@ -1145,7 +1141,7 @@ fn transInitListExpr( const dot_tok = try appendToken(rp.c, .Period, "."); var filler_init_node = try transCreateNodeArrayInitializer(rp.c, dot_tok); const filler_val_expr = ZigClangInitListExpr_getArrayFiller(expr); - try filler_init_node.op.ArrayInitializer.push((try transExpr(rp, scope, filler_val_expr, .used, .r_value)).node); + try filler_init_node.op.ArrayInitializer.push(try transExpr(rp, scope, filler_val_expr, .used, .r_value)); filler_init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); const rhs_node = if (leftover_count == 1) @@ -1163,11 +1159,7 @@ fn transInitListExpr( }; if (init_count == 0) { - return TransResult{ - .node = rhs_node, - .child_scope = scope, - .node_scope = scope, - }; + return rhs_node; } const cat_node = try rp.c.a().create(ast.Node.InfixOp); @@ -1177,11 +1169,7 @@ fn transInitListExpr( .op = .ArrayCat, .rhs = rhs_node, }; - return TransResult{ - .node = &cat_node.base, - .child_scope = scope, - .node_scope = scope, - }; + return &cat_node.base; } fn transImplicitValueInitExpr( @@ -1189,7 +1177,7 @@ fn transImplicitValueInitExpr( scope: *Scope, expr: *const ZigClangExpr, used: ResultUsed, -) TransError!TransResult { +) TransError!*ast.Node { const source_loc = ZigClangExpr_getBeginLoc(expr); const qt = getExprQualType(rp.c, expr); const ty = ZigClangQualType_getTypePtr(qt); @@ -1197,9 +1185,7 @@ fn transImplicitValueInitExpr( .Builtin => blk: { const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); switch (ZigClangBuiltinType_getKind(builtin_ty)) { - .Bool => { - break :blk try transCreateNodeBoolLiteral(rp.c, false); - }, + .Bool => return transCreateNodeBoolLiteral(rp.c, false), .Char_U, .UChar, .Char_S, @@ -1220,37 +1206,13 @@ fn transImplicitValueInitExpr( .Float128, .Float16, .LongDouble, - => { - break :blk try transCreateNodeInt(rp.c, 0); - }, + => return transCreateNodeInt(rp.c, 0), else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), } }, - .Pointer => try transCreateNodeNullLiteral(rp.c), + .Pointer => return transCreateNodeNullLiteral(rp.c), else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{}), }; - return TransResult{ - .node = node, - .child_scope = scope, - .node_scope = scope, - }; -} - -fn findBlockScope(inner: *Scope) *Scope.Block { - var scope = inner; - while (true) : (scope = scope.parent orelse unreachable) { - if (scope.id == .Block) return @fieldParentPtr(Scope.Block, "base", scope); - } -} - -fn transLookupZigIdentifier(inner: *Scope, c_name: []const u8) []const u8 { - var scope = inner; - while (true) : (scope = scope.parent orelse return c_name) { - if (scope.id == .Var) { - const var_scope = @ptrCast(*const Scope.Var, scope); - if (std.mem.eql(u8, var_scope.c_name, c_name)) return var_scope.zig_name; - } - } } fn transCPtrCast( @@ -1294,8 +1256,8 @@ fn maybeSuppressResult( rp: RestorePoint, scope: *Scope, used: ResultUsed, - result: TransResult, -) !TransResult { + result: *ast.Node, +) TransError!*ast.Node { if (used == .used) return result; // NOTE: This is backwards, but the semicolon must immediately follow the node. _ = try appendToken(rp.c, .Semicolon, ";"); @@ -1306,18 +1268,14 @@ fn maybeSuppressResult( .op_token = op_token, .lhs = lhs, .op = .Assign, - .rhs = result.node, - }; - return TransResult{ - .node = &op_node.base, - .child_scope = scope, - .node_scope = scope, + .rhs = result, }; + return &op_node.base; } fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void { try c.tree.root_node.decls.push(decl_node); - _ = try c.sym_table.put(name, {}); + _ = try c.global_scope.sym_table.put(name, decl_node); } fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) TypeError!*ast.Node { @@ -1716,11 +1674,11 @@ fn transCreateNodeAssign( _ = try appendToken(rp.c, .Semicolon, ";"); const node = try rp.c.a().create(ast.Node.InfixOp); - node.* = ast.Node.InfixOp{ + node.* = .{ .op_token = eq_token, - .lhs = lhs_node.node, + .lhs = lhs_node, .op = .Assign, - .rhs = rhs_node.node, + .rhs = rhs_node, }; return node; } @@ -1757,7 +1715,7 @@ fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp { _ = try appendToken(c, .LParen, "("); const node = try c.a().create(ast.Node.SuffixOp); node.* = ast.Node.SuffixOp{ - .lhs = fn_expr, + .lhs = .{ .node = fn_expr }, .op = ast.Node.SuffixOp.Op{ .Call = ast.Node.SuffixOp.Op.Call{ .params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()), @@ -1800,9 +1758,9 @@ fn transCreateNodeInfixOp( const node = try rp.c.a().create(ast.Node.InfixOp); node.* = ast.Node.InfixOp{ .op_token = op_token, - .lhs = lhs.node, + .lhs = lhs, .op = op, - .rhs = rhs.node, + .rhs = rhs, }; if (!grouped) return &node.base; const rparen = try appendToken(rp.c, .RParen, ")"); @@ -1871,7 +1829,7 @@ fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node { return &node.base; } -fn transCreateNodeReturnExpr(c: *Context) !*ast.Node { +fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression { const ltoken = try appendToken(c, .Keyword_return, "return"); const node = try c.a().create(ast.Node.ControlFlowExpression); node.* = ast.Node.ControlFlowExpression{ @@ -1879,7 +1837,7 @@ fn transCreateNodeReturnExpr(c: *Context) !*ast.Node { .kind = .Return, .rhs = null, }; - return &node.base; + return node; } fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node { @@ -1934,21 +1892,160 @@ fn transCreateNodeInt(c: *Context, int: var) !*ast.Node { return &node.base; } -fn transCreateNodeOpaqueType(c: *Context) !*ast.Node { - const builtin_tok = try appendToken(c, .Builtin, "@OpaqueType"); - _ = try appendToken(c, .LParen, "("); - const rparen_tok = try appendToken(c, .RParen, ")"); - - const call_node = try c.a().create(ast.Node.BuiltinCall); - call_node.* = ast.Node.BuiltinCall{ - .base = ast.Node{ .id = ast.Node.Id.BuiltinCall }, - .builtin_token = builtin_tok, - .params = ast.Node.BuiltinCall.ParamList.init(c.a()), - .rparen_token = rparen_tok, +fn transCreateNodeFloat(c: *Context, int: var) !*ast.Node { + const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int}); + const node = try c.a().create(ast.Node.FloatLiteral); + node.* = .{ + .token = token, }; + return &node.base; +} + +fn transCreateNodeOpaqueType(c: *Context) !*ast.Node { + const call_node = try transCreateNodeBuiltinFnCall(c, "@OpaqueType"); + call_node.rparen_token = try appendToken(c, .RParen, ")"); return &call_node.base; } +fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias_node: *ast.Node) !*ast.Node { + const scope = &c.global_scope.base; + + const pub_tok = try appendToken(c, .Keyword_pub, "pub"); + const inline_tok = try appendToken(c, .Keyword_inline, "inline"); + const fn_tok = try appendToken(c, .Keyword_fn, "fn"); + const name_tok = try appendIdentifier(c, name); + _ = try appendToken(c, .LParen, "("); + + const proto_alias = proto_alias_node.cast(ast.Node.FnProto).?; + + var fn_params = ast.Node.FnProto.ParamList.init(c.a()); + var it = proto_alias.params.iterator(0); + while (it.next()) |pn| { + if (it.index != 0) { + _ = try appendToken(c, .Comma, ","); + } + const param = pn.*.cast(ast.Node.ParamDecl).?; + + const param_name_tok = param.name_token orelse + try appendTokenFmt(c, .Identifier, "arg_{}", .{c.getMangle()}); + + _ = try appendToken(c, .Colon, ":"); + + const param_node = try c.a().create(ast.Node.ParamDecl); + param_node.* = .{ + .doc_comments = null, + .comptime_token = null, + .noalias_token = param.noalias_token, + .name_token = param_name_tok, + .type_node = param.type_node, + .var_args_token = null, + }; + try fn_params.push(¶m_node.base); + } + + _ = try appendToken(c, .RParen, ")"); + + const fn_proto = try c.a().create(ast.Node.FnProto); + fn_proto.* = .{ + .doc_comments = null, + .visib_token = pub_tok, + .fn_token = fn_tok, + .name_token = name_tok, + .params = fn_params, + .return_type = proto_alias.return_type, + .var_args_token = null, + .extern_export_inline_token = inline_tok, + .cc_token = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + .section_expr = null, + }; + + const block = try transCreateNodeBlock(c, null); + + const return_expr = try transCreateNodeReturnExpr(c); + const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.init_node.?); + const call_expr = try transCreateNodeFnCall(c, unwrap_expr); + it = fn_params.iterator(0); + while (it.next()) |pn| { + if (it.index != 0) { + _ = try appendToken(c, .Comma, ","); + } + const param = pn.*.cast(ast.Node.ParamDecl).?; + try call_expr.op.Call.params.push(try transCreateNodeIdentifier(c, tokenSlice(c, param.name_token.?))); + } + call_expr.rtoken = try appendToken(c, .RParen, ")"); + return_expr.rhs = &call_expr.base; + _ = try appendToken(c, .Semicolon, ";"); + + block.rbrace = try appendToken(c, .RBrace, "}"); + try block.statements.push(&return_expr.base); + fn_proto.body_node = &block.base; + return &fn_proto.base; +} + +fn transCreateNodeUnwrapNull(c: *Context, wrapped: *ast.Node) !*ast.Node { + _ = try appendToken(c, .Period, "."); + const qm = try appendToken(c, .QuestionMark, "?"); + const node = try c.a().create(ast.Node.SuffixOp); + node.* = .{ + .op = .UnwrapOptional, + .lhs = .{ .node = wrapped }, + .rtoken = qm, + }; + return &node.base; +} + +fn transCreateNodeEnumLiteral(c: *Context, name: []const u8) !*ast.Node { + const node = try c.a().create(ast.Node.EnumLiteral); + node.* = .{ + .dot = try appendToken(c, .Period, "."), + .name = try appendIdentifier(c, name), + }; + return &node.base; +} + +fn transCreateNodeIf(c: *Context) !*ast.Node.If { + const if_tok = try appendToken(c, .Keyword_if, "if"); + _ = try appendToken(c, .LParen, "("); + const node = try c.a().create(ast.Node.If); + node.* = .{ + .if_token = if_tok, + .condition = undefined, + .payload = null, + .body = undefined, + .@"else" = null, + }; + return node; +} + +fn transCreateNodeElse(c: *Context) !*ast.Node.Else { + const node = try c.a().create(ast.Node.Else); + node.* = .{ + .else_token = try appendToken(c, .Keyword_else, "else"), + .payload = null, + .body = undefined, + }; + return node; +} + +fn transCreateNodeBlock(c: *Context, label: ?[]const u8) !*ast.Node.Block { + const label_node = if (label) |l| blk: { + const ll = try appendIdentifier(c, l); + _ = try appendToken(c, .Colon, ":"); + break :blk ll; + } else null; + const block_node = try c.a().create(ast.Node.Block); + block_node.* = .{ + .label = label_node, + .lbrace = try appendToken(c, .LBrace, "{"), + .statements = ast.Node.Block.StatementList.init(c.a()), + .rbrace = undefined, + }; + return block_node; +} + const RestorePoint = struct { c: *Context, token_index: ast.TokenIndex, @@ -1972,28 +2069,28 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour switch (ZigClangType_getTypeClass(ty)) { .Builtin => { const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); - switch (ZigClangBuiltinType_getKind(builtin_ty)) { - .Void => return transCreateNodeIdentifier(rp.c, "c_void"), - .Bool => return transCreateNodeIdentifier(rp.c, "bool"), - .Char_U, .UChar, .Char_S, .Char8 => return transCreateNodeIdentifier(rp.c, "u8"), - .SChar => return transCreateNodeIdentifier(rp.c, "i8"), - .UShort => return transCreateNodeIdentifier(rp.c, "c_ushort"), - .UInt => return transCreateNodeIdentifier(rp.c, "c_uint"), - .ULong => return transCreateNodeIdentifier(rp.c, "c_ulong"), - .ULongLong => return transCreateNodeIdentifier(rp.c, "c_ulonglong"), - .Short => return transCreateNodeIdentifier(rp.c, "c_short"), - .Int => return transCreateNodeIdentifier(rp.c, "c_int"), - .Long => return transCreateNodeIdentifier(rp.c, "c_long"), - .LongLong => return transCreateNodeIdentifier(rp.c, "c_longlong"), - .UInt128 => return transCreateNodeIdentifier(rp.c, "u128"), - .Int128 => return transCreateNodeIdentifier(rp.c, "i128"), - .Float => return transCreateNodeIdentifier(rp.c, "f32"), - .Double => return transCreateNodeIdentifier(rp.c, "f64"), - .Float128 => return transCreateNodeIdentifier(rp.c, "f128"), - .Float16 => return transCreateNodeIdentifier(rp.c, "f16"), - .LongDouble => return transCreateNodeIdentifier(rp.c, "c_longdouble"), + return transCreateNodeIdentifier(rp.c, switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .Void => "c_void", + .Bool => "bool", + .Char_U, .UChar, .Char_S, .Char8 => "u8", + .SChar => "i8", + .UShort => "c_ushort", + .UInt => "c_uint", + .ULong => "c_ulong", + .ULongLong => "c_ulonglong", + .Short => "c_short", + .Int => "c_int", + .Long => "c_long", + .LongLong => "c_longlong", + .UInt128 => "u128", + .Int128 => "i128", + .Float => "f32", + .Double => "f64", + .Float128 => "f128", + .Float16 => "f16", + .LongDouble => "c_longdouble", else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), - } + }); }, .FunctionProto => { const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty); @@ -2076,6 +2173,13 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour .Record => { const record_ty = @ptrCast(*const ZigClangRecordType, ty); + // TODO this sould get the name from decl_table + // struct Foo { + // struct Bar{ + // int b; + // }; + // struct Bar c; + // }; const record_decl = ZigClangRecordType_getDecl(record_ty); if (try getContainerName(rp, record_decl)) |name| return transCreateNodeIdentifier(rp.c, name) @@ -2203,6 +2307,9 @@ fn finishTransFnProto( // TODO check for always_inline attribute // TODO check for align attribute + var fndef_scope = Scope.FnDef.init(rp.c); + const scope = &fndef_scope.base; + // pub extern fn name(...) T const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null; const cc_tok = if (cc == .Stdcall) try appendToken(rp.c, .Keyword_stdcallcc, "stdcallcc") else null; @@ -2228,13 +2335,17 @@ fn finishTransFnProto( const param_name_tok: ?ast.TokenIndex = blk: { if (fn_decl != null) { const param = ZigClangFunctionDecl_getParamDecl(fn_decl.?, @intCast(c_uint, i)); - const param_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, param))); - if (param_name.len > 0) { - // TODO: If len == 0, auto-generate arg1, arg2, etc? Or leave the name blank? - const result = try appendIdentifier(rp.c, param_name); - _ = try appendToken(rp.c, .Colon, ":"); - break :blk result; - } + var param_name: []const u8 = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, param))); + if (param_name.len < 1) + param_name = "arg"[0..]; + const checked_param_name = if (try scope.createAlias(rp.c, param_name)) |a| blk: { + try fndef_scope.params.push(.{ .name = param_name, .alias = a }); + break :blk a; + } else param_name; + + const result = try appendIdentifier(rp.c, checked_param_name); + _ = try appendToken(rp.c, .Colon, ":"); + break :blk result; } break :blk null; }; @@ -2444,3 +2555,530 @@ fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node { pub fn freeErrors(errors: []ClangErrMsg) void { ZigClangErrorMsg_delete(errors.ptr, errors.len); } + +fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { + // TODO if we see #undef, delete it from the table + var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(unit); + const it_end = ZigClangASTUnit_getLocalPreprocessingEntities_end(unit); + var tok_list = ctok.TokenList.init(c.a()); + const scope = &c.global_scope.base; + + while (it.I != it_end.I) : (it.I += 1) { + const entity = ZigClangPreprocessingRecord_iterator_deref(it); + tok_list.shrink(0); + switch (ZigClangPreprocessedEntity_getKind(entity)) { + .MacroDefinitionKind => { + const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity); + const raw_name = ZigClangMacroDefinitionRecord_getName_getNameStart(macro); + const begin_loc = ZigClangMacroDefinitionRecord_getSourceRange_getBegin(macro); + + const name = try c.str(raw_name); + if (scope.contains(name)) { + continue; + } + const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc); + ctok.tokenizeCMacro(&tok_list, begin_c) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => { + try failDecl(c, begin_loc, name, "unable to tokenize macro definition", .{}); + continue; + }, + }; + + var tok_it = tok_list.iterator(0); + const first_tok = tok_it.next().?; + assert(first_tok.id == .Identifier and std.mem.eql(u8, first_tok.bytes, name)); + const next = tok_it.peek().?; + switch (next.id) { + .Identifier => { + // if it equals itself, ignore. for example, from stdio.h: + // #define stdin stdin + if (std.mem.eql(u8, name, next.bytes)) { + continue; + } + }, + .Eof => { + // this means it is a macro without a value + // we don't care about such things + continue; + }, + else => {}, + } + const macro_fn = if (tok_it.peek().?.id == .Fn) blk: { + _ = tok_it.next(); + break :blk true; + } else false; + + (if (macro_fn) + transMacroFnDefine(c, &tok_it, name, begin_loc) + else + transMacroDefine(c, &tok_it, name, begin_loc)) catch |err| switch (err) { + error.UnsupportedTranslation, + error.ParseError, + => try failDecl(c, begin_loc, name, "unable to translate macro", .{}), + error.OutOfMemory => |e| return e, + }; + }, + else => {}, + } + } +} + +fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void { + const rp = makeRestorePoint(c); + const scope = &c.global_scope.base; + + const visib_tok = try appendToken(c, .Keyword_pub, "pub"); + const mut_tok = try appendToken(c, .Keyword_const, "const"); + const name_tok = try appendIdentifier(c, name); + const eq_tok = try appendToken(c, .Equal, "="); + + const init_node = try parseCExpr(rp, it, source_loc, scope); + + const node = try c.a().create(ast.Node.VarDecl); + node.* = ast.Node.VarDecl{ + .doc_comments = null, + .visib_token = visib_tok, + .thread_local_token = null, + .name_token = name_tok, + .eq_token = eq_tok, + .mut_token = mut_tok, + .comptime_token = null, + .extern_export_token = null, + .lib_name = null, + .type_node = null, + .align_node = null, + .section_node = null, + .init_node = init_node, + .semicolon_token = try appendToken(c, .Semicolon, ";"), + }; + _ = try c.global_scope.macro_table.put(name, &node.base); +} + +fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void { + const rp = makeRestorePoint(c); + var fndef_scope = Scope.FnDef.init(c); + const scope = &fndef_scope.base; + + const pub_tok = try appendToken(c, .Keyword_pub, "pub"); + const inline_tok = try appendToken(c, .Keyword_inline, "inline"); + const fn_tok = try appendToken(c, .Keyword_fn, "fn"); + const name_tok = try appendIdentifier(c, name); + _ = try appendToken(c, .LParen, "("); + + if (it.next().?.id != .LParen) { + return error.ParseError; + } + var fn_params = ast.Node.FnProto.ParamList.init(c.a()); + while (true) { + const param_tok = it.next().?; + if (param_tok.id != .Identifier) + return error.ParseError; + + const checked_name = if (try scope.createAlias(c, param_tok.bytes)) |alias| blk: { + try fndef_scope.params.push(.{ .name = param_tok.bytes, .alias = alias }); + break :blk alias; + } else param_tok.bytes; + + const param_name_tok = try appendIdentifier(c, checked_name); + _ = try appendToken(c, .Colon, ":"); + + const token_index = try appendToken(c, .Keyword_var, "var"); + const identifier = try c.a().create(ast.Node.Identifier); + identifier.* = ast.Node.Identifier{ + .base = ast.Node{ .id = ast.Node.Id.Identifier }, + .token = token_index, + }; + + const param_node = try c.a().create(ast.Node.ParamDecl); + param_node.* = .{ + .doc_comments = null, + .comptime_token = null, + .noalias_token = null, + .name_token = param_name_tok, + .type_node = &identifier.base, + .var_args_token = null, + }; + try fn_params.push(¶m_node.base); + + if (it.peek().?.id != .Comma) + break; + _ = it.next(); + _ = try appendToken(c, .Comma, ","); + } + + if (it.next().?.id != .RParen) { + return error.ParseError; + } + + _ = try appendToken(c, .RParen, ")"); + + const type_of = try transCreateNodeBuiltinFnCall(c, "@TypeOf"); + type_of.rparen_token = try appendToken(c, .LParen, ")"); + + const fn_proto = try c.a().create(ast.Node.FnProto); + fn_proto.* = .{ + .visib_token = pub_tok, + .extern_export_inline_token = inline_tok, + .fn_token = fn_tok, + .name_token = name_tok, + .params = fn_params, + .return_type = .{ .Explicit = &type_of.base }, + .doc_comments = null, + .var_args_token = null, + .cc_token = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + .section_expr = null, + }; + + const block = try transCreateNodeBlock(c, null); + + const return_expr = try transCreateNodeReturnExpr(c); + const expr = try parseCExpr(rp, it, source_loc, scope); + _ = try appendToken(c, .Semicolon, ";"); + try type_of.params.push(expr); + return_expr.rhs = expr; + + block.rbrace = try appendToken(c, .RBrace, "}"); + try block.statements.push(&return_expr.base); + fn_proto.body_node = &block.base; + _ = try c.global_scope.macro_table.put(name, &fn_proto.base); +} + +const ParseError = Error || error{ + ParseError, + UnsupportedTranslation, +}; + +fn parseCExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { + return parseCPrefixOpExpr(rp, it, source_loc, scope); +} + +fn parseCNumLit(rp: RestorePoint, tok: *CToken, source_loc: ZigClangSourceLocation) ParseError!*ast.Node { + if (tok.id == .NumLitInt) { + if (tok.num_lit_suffix == .None) { + if (tok.bytes.len > 2 and tok.bytes[0] == '0') { + switch (tok.bytes[1]) { + '0'...'7' => { + // octal + return transCreateNodeInt(rp.c, try std.fmt.allocPrint(rp.c.a(), "0o{}", .{tok.bytes})); + }, + else => {}, + } + } + return transCreateNodeInt(rp.c, tok.bytes); + } + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + try cast_node.params.push(try transCreateNodeIdentifier(rp.c, switch (tok.num_lit_suffix) { + .U => "c_uint", + .L => "c_long", + .LU => "c_ulong", + .LL => "c_longlong", + .LLU => "c_ulonglong", + else => unreachable, + })); + _ = try appendToken(rp.c, .Comma, ","); + try cast_node.params.push(try transCreateNodeInt(rp.c, tok.bytes)); + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &cast_node.base; + } else if (tok.id == .NumLitFloat) { + if (tok.num_lit_suffix == .None) { + return transCreateNodeFloat(rp.c, tok.bytes); + } + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + try cast_node.params.push(try transCreateNodeIdentifier(rp.c, switch (tok.num_lit_suffix) { + .F => "f32", + .L => "f64", + else => unreachable, + })); + _ = try appendToken(rp.c, .Comma, ","); + try cast_node.params.push(try transCreateNodeFloat(rp.c, tok.bytes)); + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &cast_node.base; + } else + return revertAndWarn( + rp, + error.ParseError, + source_loc, + "expected number literal", + .{}, + ); +} + +fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { + const tok = it.next().?; + switch (tok.id) { + .CharLit => { + const token = try appendToken(rp.c, .CharLiteral, tok.bytes); + const node = try rp.c.a().create(ast.Node.CharLiteral); + node.* = ast.Node.CharLiteral{ + .token = token, + }; + return &node.base; + }, + .StrLit => { + const token = try appendToken(rp.c, .StringLiteral, tok.bytes); + const node = try rp.c.a().create(ast.Node.StringLiteral); + node.* = ast.Node.StringLiteral{ + .token = token, + }; + return &node.base; + }, + .NumLitInt, .NumLitFloat => { + return parseCNumLit(rp, tok, source_loc); + }, + .Identifier => { + const name = if (scope.getAlias(tok.bytes)) |a| a else tok.bytes; + return transCreateNodeIdentifier(rp.c, name); + }, + .LParen => { + const inner_node = try parseCExpr(rp, it, source_loc, scope); + + if (it.peek().?.id == .RParen) { + _ = it.next(); + return inner_node; + } + + // hack to get zig fmt to render a comma in builtin calls + _ = try appendToken(rp.c, .Comma, ","); + + const node_to_cast = try parseCExpr(rp, it, source_loc, scope); + + if (it.next().?.id != .RParen) { + return revertAndWarn( + rp, + error.ParseError, + source_loc, + "unable to translate C expr", + .{}, + ); + } + + //if (@typeId(@TypeOf(x)) == .Pointer) + // @ptrCast(dest, x) + //else if (@typeId(@TypeOf(x)) == .Integer) + // @intToPtr(dest, x) + //else + // @as(dest, x) + + const if_1 = try transCreateNodeIf(rp.c); + const type_id_1 = try transCreateNodeBuiltinFnCall(rp.c, "@typeId"); + const type_of_1 = try transCreateNodeBuiltinFnCall(rp.c, "@TypeOf"); + try type_id_1.params.push(&type_of_1.base); + try type_of_1.params.push(node_to_cast); + type_of_1.rparen_token = try appendToken(rp.c, .LParen, ")"); + type_id_1.rparen_token = try appendToken(rp.c, .LParen, ")"); + + const cmp_1 = try rp.c.a().create(ast.Node.InfixOp); + cmp_1.* = .{ + .op_token = try appendToken(rp.c, .EqualEqual, "=="), + .lhs = &type_id_1.base, + .op = .EqualEqual, + .rhs = try transCreateNodeEnumLiteral(rp.c, "Pointer"), + }; + if_1.condition = &cmp_1.base; + _ = try appendToken(rp.c, .LParen, ")"); + + const ptr_cast = try transCreateNodeBuiltinFnCall(rp.c, "@ptrCast"); + try ptr_cast.params.push(inner_node); + try ptr_cast.params.push(node_to_cast); + ptr_cast.rparen_token = try appendToken(rp.c, .LParen, ")"); + if_1.body = &ptr_cast.base; + + const else_1 = try transCreateNodeElse(rp.c); + if_1.@"else" = else_1; + + const if_2 = try transCreateNodeIf(rp.c); + const type_id_2 = try transCreateNodeBuiltinFnCall(rp.c, "@typeId"); + const type_of_2 = try transCreateNodeBuiltinFnCall(rp.c, "@TypeOf"); + try type_id_2.params.push(&type_of_2.base); + try type_of_2.params.push(node_to_cast); + type_of_2.rparen_token = try appendToken(rp.c, .LParen, ")"); + type_id_2.rparen_token = try appendToken(rp.c, .LParen, ")"); + + const cmp_2 = try rp.c.a().create(ast.Node.InfixOp); + cmp_2.* = .{ + .op_token = try appendToken(rp.c, .EqualEqual, "=="), + .lhs = &type_id_2.base, + .op = .EqualEqual, + .rhs = try transCreateNodeEnumLiteral(rp.c, "Int"), + }; + if_2.condition = &cmp_2.base; + else_1.body = &if_2.base; + _ = try appendToken(rp.c, .LParen, ")"); + + const int_to_ptr = try transCreateNodeBuiltinFnCall(rp.c, "@intToPtr"); + try int_to_ptr.params.push(inner_node); + try int_to_ptr.params.push(node_to_cast); + int_to_ptr.rparen_token = try appendToken(rp.c, .LParen, ")"); + if_2.body = &int_to_ptr.base; + + const else_2 = try transCreateNodeElse(rp.c); + if_2.@"else" = else_2; + + const as = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + try as.params.push(inner_node); + try as.params.push(node_to_cast); + as.rparen_token = try appendToken(rp.c, .LParen, ")"); + else_2.body = &as.base; + + return &if_1.base; + }, + else => return revertAndWarn( + rp, + error.UnsupportedTranslation, + source_loc, + "unable to translate C expr", + .{}, + ), + } +} + +fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { + var node = try parseCPrimaryExpr(rp, it, source_loc, scope); + while (true) { + const tok = it.next().?; + switch (tok.id) { + .Dot => { + const name_tok = it.next().?; + if (name_tok.id != .Identifier) + return revertAndWarn( + rp, + error.ParseError, + source_loc, + "unable to translate C expr", + .{}, + ); + + const op_token = try appendToken(rp.c, .Period, "."); + const rhs = try transCreateNodeIdentifier(rp.c, name_tok.bytes); + const access_node = try rp.c.a().create(ast.Node.InfixOp); + access_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .Period, + .rhs = rhs, + }; + node = &access_node.base; + }, + .Asterisk => { + if (it.peek().?.id == .RParen) { + // type *) + + // hack to get zig fmt to render a comma in builtin calls + _ = try appendToken(rp.c, .Comma, ","); + + const ptr = try transCreateNodePtrType(rp.c, false, false, .Identifier); + ptr.rhs = node; + return &ptr.base; + } else { + // expr * expr + const op_token = try appendToken(rp.c, .Asterisk, "*"); + const rhs = try parseCPrimaryExpr(rp, it, source_loc, scope); + const bitshift_node = try rp.c.a().create(ast.Node.InfixOp); + bitshift_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .BitShiftLeft, + .rhs = rhs, + }; + node = &bitshift_node.base; + } + }, + .Shl => { + const op_token = try appendToken(rp.c, .AngleBracketAngleBracketLeft, "<<"); + const rhs = try parseCPrimaryExpr(rp, it, source_loc, scope); + const bitshift_node = try rp.c.a().create(ast.Node.InfixOp); + bitshift_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .BitShiftLeft, + .rhs = rhs, + }; + node = &bitshift_node.base; + }, + else => { + _ = it.prev(); + return node; + }, + } + } +} + +fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { + const op_tok = it.next().?; + + switch (op_tok.id) { + .Bang => { + const node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); + node.rhs = try parseCPrefixOpExpr(rp, it, source_loc, scope); + return &node.base; + }, + .Minus => { + const node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-"); + node.rhs = try parseCPrefixOpExpr(rp, it, source_loc, scope); + return &node.base; + }, + .Tilde => { + const node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~"); + node.rhs = try parseCPrefixOpExpr(rp, it, source_loc, scope); + return &node.base; + }, + .Asterisk => { + const prefix_op_expr = try parseCPrefixOpExpr(rp, it, source_loc, scope); + const node = try rp.c.a().create(ast.Node.SuffixOp); + node.* = .{ + .lhs = .{ .node = prefix_op_expr }, + .op = .Deref, + .rtoken = try appendToken(rp.c, .PeriodAsterisk, ".*"), + }; + return &node.base; + }, + else => { + _ = it.prev(); + return try parseCSuffixOpExpr(rp, it, source_loc, scope); + }, + } +} + +fn tokenSlice(c: *Context, token: ast.TokenIndex) []const u8 { + const tok = c.tree.tokens.at(token); + return c.source_buffer.toSliceConst()[tok.start..tok.end]; +} + +fn getFnDecl(c: *Context, ref: *ast.Node) ?*ast.Node { + const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null; + const name = if (init.cast(ast.Node.Identifier)) |id| + tokenSlice(c, id.token) + else + return null; + // TODO a.b.c + if (c.global_scope.sym_table.get(name)) |kv| { + if (kv.value.cast(ast.Node.VarDecl)) |val| { + if (val.type_node) |type_node| { + if (type_node.cast(ast.Node.PrefixOp)) |casted| { + if (casted.rhs.id == .FnProto) { + return casted.rhs; + } + } + } + } + } + return null; +} + +fn addMacros(c: *Context) !void { + var macro_it = c.global_scope.macro_table.iterator(); + while (macro_it.next()) |kv| { + if (getFnDecl(c, kv.value)) |proto_node| { + // If a macro aliases a global variable which is a function pointer, we conclude that + // the macro is intended to represent a function that assumes the function pointer + // variable is non-null and calls it. + try addTopLevelDecl(c, kv.key, try transCreateNodeMacroFn(c, kv.key, kv.value, proto_node)); + } else { + try addTopLevelDecl(c, kv.key, kv.value); + } + } +} diff --git a/test/translate_c.zig b/test/translate_c.zig index 97cfc129f7..9a4c364d1d 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -162,6 +162,270 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_both("enums", + \\enum Foo { + \\ FooA, + \\ FooB, + \\ Foo1, + \\}; + , &[_][]const u8{ + \\pub const enum_Foo = extern enum { + \\ A, + \\ B, + \\ @"1", + \\}; + , + \\pub const FooA = enum_Foo.A; + , + \\pub const FooB = enum_Foo.B; + , + \\pub const Foo1 = enum_Foo.@"1"; + , + \\pub const Foo = enum_Foo; + }); + + cases.add_both("enums", + \\enum Foo { + \\ FooA = 2, + \\ FooB = 5, + \\ Foo1, + \\}; + , &[_][]const u8{ + \\pub const enum_Foo = extern enum { + \\ A = 2, + \\ B = 5, + \\ @"1" = 6, + \\}; + , + \\pub const FooA = enum_Foo.A; + , + \\pub const FooB = enum_Foo.B; + , + \\pub const Foo1 = enum_Foo.@"1"; + , + \\pub const Foo = enum_Foo; + }); + + cases.add_both("typedef of function in struct field", + \\typedef void lws_callback_function(void); + \\struct Foo { + \\ void (*func)(void); + \\ lws_callback_function *callback_http; + \\}; + , &[_][]const u8{ + \\pub const lws_callback_function = extern fn () void; + \\pub const struct_Foo = extern struct { + \\ func: ?extern fn () void, + \\ callback_http: ?lws_callback_function, + \\}; + }); + + cases.add_both("pointer to struct demoted to opaque due to bit fields", + \\struct Foo { + \\ unsigned int: 1; + \\}; + \\struct Bar { + \\ struct Foo *foo; + \\}; + , &[_][]const u8{ + \\pub const struct_Foo = @OpaqueType() + , + \\pub const struct_Bar = extern struct { + \\ foo: ?*struct_Foo, + \\}; + }); + + cases.add_both("macro with left shift", + \\#define REDISMODULE_READ (1<<0) + , &[_][]const u8{ + \\pub const REDISMODULE_READ = 1 << 0; + }); + + cases.add_both("double define struct", + \\typedef struct Bar Bar; + \\typedef struct Foo Foo; + \\ + \\struct Foo { + \\ Foo *a; + \\}; + \\ + \\struct Bar { + \\ Foo *a; + \\}; + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ a: [*c]Foo, + \\}; + , + \\pub const Foo = struct_Foo; + , + \\pub const struct_Bar = extern struct { + \\ a: [*c]Foo, + \\}; + , + \\pub const Bar = struct_Bar; + }); + + cases.add_both("simple struct", + \\struct Foo { + \\ int x; + \\ char *y; + \\}; + , &[_][]const u8{ + \\const struct_Foo = extern struct { + \\ x: c_int, + \\ y: [*c]u8, + \\}; + , + \\pub const Foo = struct_Foo; + }); + + cases.add_both("self referential struct with function pointer", + \\struct Foo { + \\ void (*derp)(struct Foo *foo); + \\}; + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ derp: ?extern fn ([*c]struct_Foo) void, + \\}; + , + \\pub const Foo = struct_Foo; + }); + + cases.add_both("struct prototype used in func", + \\struct Foo; + \\struct Foo *some_func(struct Foo *foo, int x); + , &[_][]const u8{ + \\pub const struct_Foo = @OpaqueType(); + , + \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo; + , + \\pub const Foo = struct_Foo; + }); + + cases.add_both("#define an unsigned integer literal", + \\#define CHANNEL_COUNT 24 + , &[_][]const u8{ + \\pub const CHANNEL_COUNT = 24; + }); + + cases.add_both("#define referencing another #define", + \\#define THING2 THING1 + \\#define THING1 1234 + , &[_][]const u8{ + \\pub const THING1 = 1234; + , + \\pub const THING2 = THING1; + }); + + cases.add_both("circular struct definitions", + \\struct Bar; + \\ + \\struct Foo { + \\ struct Bar *next; + \\}; + \\ + \\struct Bar { + \\ struct Foo *next; + \\}; + , &[_][]const u8{ + \\pub const struct_Bar = extern struct { + \\ next: [*c]struct_Foo, + \\}; + , + \\pub const struct_Foo = extern struct { + \\ next: [*c]struct_Bar, + \\}; + }); + + cases.add_both("#define string", + \\#define foo "a string" + , &[_][]const u8{ + \\pub const foo = "a string"; + }); + + cases.add_both("zig keywords in C code", + \\struct comptime { + \\ int defer; + \\}; + , &[_][]const u8{ + \\pub const struct_comptime = extern struct { + \\ @"defer": c_int, + \\}; + , + \\pub const @"comptime" = struct_comptime; + }); + + cases.add_both("macro with parens around negative number", + \\#define LUA_GLOBALSINDEX (-10002) + , &[_][]const u8{ + \\pub const LUA_GLOBALSINDEX = -10002; + }); + + cases.add_both( + "u integer suffix after 0 (zero) in macro definition", + "#define ZERO 0U", + &[_][]const u8{ + "pub const ZERO = @as(c_uint, 0);", + }, + ); + + cases.add_both( + "l integer suffix after 0 (zero) in macro definition", + "#define ZERO 0L", + &[_][]const u8{ + "pub const ZERO = @as(c_long, 0);", + }, + ); + + cases.add_both( + "ul integer suffix after 0 (zero) in macro definition", + "#define ZERO 0UL", + &[_][]const u8{ + "pub const ZERO = @as(c_ulong, 0);", + }, + ); + + cases.add_both( + "lu integer suffix after 0 (zero) in macro definition", + "#define ZERO 0LU", + &[_][]const u8{ + "pub const ZERO = @as(c_ulong, 0);", + }, + ); + + cases.add_both( + "ll integer suffix after 0 (zero) in macro definition", + "#define ZERO 0LL", + &[_][]const u8{ + "pub const ZERO = @as(c_longlong, 0);", + }, + ); + + cases.add_both( + "ull integer suffix after 0 (zero) in macro definition", + "#define ZERO 0ULL", + &[_][]const u8{ + "pub const ZERO = @as(c_ulonglong, 0);", + }, + ); + + cases.add_both( + "llu integer suffix after 0 (zero) in macro definition", + "#define ZERO 0LLU", + &[_][]const u8{ + "pub const ZERO = @as(c_ulonglong, 0);", + }, + ); + + cases.add_both( + "bitwise not on u-suffixed 0 (zero) in macro definition", + "#define NOT_ZERO (~0U)", + &[_][]const u8{ + "pub const NOT_ZERO = ~@as(c_uint, 0);", + }, + ); + /////////////// Cases that pass for only stage2 //////////////// cases.add_2("Parameterless function prototypes", @@ -202,21 +466,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add_2("field struct", - \\union OpenGLProcs { - \\ struct { - \\ int Clear; - \\ } gl; - \\}; - , &[_][]const u8{ - \\pub const union_OpenGLProcs = extern union { - \\ gl: extern struct { - \\ Clear: c_int, - \\ }, - \\}; - \\pub const OpenGLProcs = union_OpenGLProcs; - }); - cases.add_2("enums", \\typedef enum { \\ a, @@ -280,46 +529,178 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ o, \\ p, \\}; + , \\pub const Baz = struct_Baz; }); - /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// - - cases.add_both("typedef of function in struct field", - \\typedef void lws_callback_function(void); - \\struct Foo { - \\ void (*func)(void); - \\ lws_callback_function *callback_http; - \\}; + cases.add_2("#define a char literal", + \\#define A_CHAR 'a' , &[_][]const u8{ - \\pub const lws_callback_function = extern fn () void; - \\pub const struct_Foo = extern struct { - \\ func: ?extern fn () void, - \\ callback_http: ?lws_callback_function, - \\}; + \\pub const A_CHAR = 'a'; }); - cases.add_both("pointer to struct demoted to opaque due to bit fields", - \\struct Foo { - \\ unsigned int: 1; - \\}; - \\struct Bar { - \\ struct Foo *foo; - \\}; + cases.add_2("comment after integer literal", + \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ , &[_][]const u8{ - \\pub const struct_Foo = @OpaqueType() + \\pub const SDL_INIT_VIDEO = 0x00000020; + }); + + cases.add_2("u integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020u /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_uint, 0x00000020); + }); + + cases.add_2("l integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020l /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_long, 0x00000020); + }); + + cases.add_2("ul integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020ul /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020); + }); + + cases.add_2("lu integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020lu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020); + }); + + cases.add_2("ll integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020ll /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_longlong, 0x00000020); + }); + + cases.add_2("ull integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020ull /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020); + }); + + cases.add_2("llu integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020llu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020); + }); + + cases.add_2("generate inline func for #define global extern fn", + \\extern void (*fn_ptr)(void); + \\#define foo fn_ptr + \\ + \\extern char (*fn_ptr2)(int, float); + \\#define bar fn_ptr2 + , &[_][]const u8{ + \\pub extern var fn_ptr: ?extern fn () void; , - \\pub const struct_Bar = extern struct { - \\ foo: ?*struct_Foo, - \\}; + \\pub inline fn foo() void { + \\ return fn_ptr.?(); + \\} + , + \\pub extern var fn_ptr2: ?extern fn (c_int, f32) u8; + , + \\pub inline fn bar(arg_1: c_int, arg_2: f32) u8 { + \\ return fn_ptr2.?(arg_1, arg_2); + \\} }); - cases.add("macro with left shift", - \\#define REDISMODULE_READ (1<<0) + cases.add_2("macros with field targets", + \\typedef unsigned int GLbitfield; + \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask); + \\typedef void(*OpenGLProc)(void); + \\union OpenGLProcs { + \\ OpenGLProc ptr[1]; + \\ struct { + \\ PFNGLCLEARPROC Clear; + \\ } gl; + \\}; + \\extern union OpenGLProcs glProcs; + \\#define glClearUnion glProcs.gl.Clear + \\#define glClearPFN PFNGLCLEARPROC , &[_][]const u8{ - \\pub const REDISMODULE_READ = 1 << 0; + \\pub const GLbitfield = c_uint; + , + \\pub const PFNGLCLEARPROC = ?extern fn (GLbitfield) void; + , + \\pub const OpenGLProc = ?extern fn () void; + , + \\pub const union_OpenGLProcs = extern union { + \\ ptr: [1]OpenGLProc, + \\ gl: extern struct { + \\ Clear: PFNGLCLEARPROC, + \\ }, + \\}; + , + \\pub extern var glProcs: union_OpenGLProcs; + , + \\pub const glClearPFN = PFNGLCLEARPROC; + // , // TODO + // \\pub inline fn glClearUnion(arg_1: GLbitfield) void { + // \\ return glProcs.gl.Clear.?(arg_1); + // \\} + , + \\pub const OpenGLProcs = union_OpenGLProcs; }); + cases.add_2("macro pointer cast", + \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) + , &[_][]const u8{ + \\pub const NRF_GPIO = if (@typeId(@TypeOf(NRF_GPIO_BASE)) == .Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@TypeOf(NRF_GPIO_BASE)) == .Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE); + }); + + cases.add_2("basic macro function", + \\extern int c; + \\#define BASIC(c) (c*2) + , &[_][]const u8{ + \\pub extern var c: c_int; + , + \\pub inline fn BASIC(c_1: var) @TypeOf(c_1 * 2) { + \\ return c_1 * 2; + \\} + }); + + cases.add_2("macro escape sequences", + \\#define FOO "aoeu\xab derp" + \\#define FOO2 "aoeu\a derp" + , &[_][]const u8{ + \\pub const FOO = "aoeu\xab derp"; + , + \\pub const FOO2 = "aoeu\x07 derp"; + }); + + cases.add_2("variable aliasing", + \\static long a = 2; + \\static long b = 2; + \\static int c = 4; + \\void foo(char c) { + \\ int a; + \\ char b = 123; + \\ b = (char) a; + \\ { + \\ int d = 5; + \\ } + \\ unsigned d = 440; + \\} + , &[_][]const u8{ + \\pub var a: c_long = @as(c_long, 2); + \\pub var b: c_long = @as(c_long, 2); + \\pub var c: c_int = 4; + \\pub export fn foo(c_1: u8) void { + \\ var a_2: c_int = undefined; + \\ var b_3: u8 = @as(u8, 123); + \\ b_3 = @as(u8, a_2); + \\ { + \\ var d: c_int = 5; + \\ } + \\ var d: c_uint = @as(c_uint, 440); + \\} + }); + + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// + if (builtin.os != builtin.Os.windows) { // Windows treats this as an enum with type c_int cases.add("big negative enum init values when C ABI supports long long enums", @@ -457,31 +838,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add_both("double define struct", - \\typedef struct Bar Bar; - \\typedef struct Foo Foo; - \\ - \\struct Foo { - \\ Foo *a; - \\}; - \\ - \\struct Bar { - \\ Foo *a; - \\}; - , &[_][]const u8{ - \\pub const struct_Foo = extern struct { - \\ a: [*c]Foo, - \\}; - , - \\pub const Foo = struct_Foo; - , - \\pub const struct_Bar = extern struct { - \\ a: [*c]Foo, - \\}; - , - \\pub const Bar = struct_Bar; - }); - cases.addAllowWarnings("simple data types", \\#include \\int foo(char a, unsigned char b, signed char c); @@ -506,70 +862,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add_both("enums", - \\enum Foo { - \\ FooA, - \\ FooB, - \\ Foo1, - \\}; - , &[_][]const u8{ - \\pub const enum_Foo = extern enum { - \\ A, - \\ B, - \\ @"1", - \\}; - , - \\pub const FooA = enum_Foo.A; - , - \\pub const FooB = enum_Foo.B; - , - \\pub const Foo1 = enum_Foo.@"1"; - , - \\pub const Foo = enum_Foo; - }); - - cases.add_both("enums", - \\enum Foo { - \\ FooA = 2, - \\ FooB = 5, - \\ Foo1, - \\}; - , &[_][]const u8{ - \\pub const enum_Foo = extern enum { - \\ A = 2, - \\ B = 5, - \\ @"1" = 6, - \\}; - , - \\pub const FooA = enum_Foo.A; - , - \\pub const FooB = enum_Foo.B; - , - \\pub const Foo1 = enum_Foo.@"1"; - , - \\pub const Foo = enum_Foo; - }); - cases.add("restrict -> noalias", \\void foo(void *restrict bar, void *restrict); , &[_][]const u8{ \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; }); - cases.add_both("simple struct", - \\struct Foo { - \\ int x; - \\ char *y; - \\}; - , &[_][]const u8{ - \\const struct_Foo = extern struct { - \\ x: c_int, - \\ y: [*c]u8, - \\}; - , - \\pub const Foo = struct_Foo; - }); - cases.add("qualified struct and enum", \\struct Foo { \\ int x; @@ -608,162 +906,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub extern fn func(array: [*c]c_int) void; }); - cases.add_both("self referential struct with function pointer", - \\struct Foo { - \\ void (*derp)(struct Foo *foo); - \\}; - , &[_][]const u8{ - \\pub const struct_Foo = extern struct { - \\ derp: ?extern fn ([*c]struct_Foo) void, - \\}; - , - \\pub const Foo = struct_Foo; - }); - - cases.add_both("struct prototype used in func", - \\struct Foo; - \\struct Foo *some_func(struct Foo *foo, int x); - , &[_][]const u8{ - \\pub const struct_Foo = @OpaqueType(); - , - \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo; - , - \\pub const Foo = struct_Foo; - }); - - cases.add("#define a char literal", - \\#define A_CHAR 'a' - , &[_][]const u8{ - \\pub const A_CHAR = 97; - }); - - cases.add("#define an unsigned integer literal", - \\#define CHANNEL_COUNT 24 - , &[_][]const u8{ - \\pub const CHANNEL_COUNT = 24; - }); - - cases.add("#define referencing another #define", - \\#define THING2 THING1 - \\#define THING1 1234 - , &[_][]const u8{ - \\pub const THING1 = 1234; - , - \\pub const THING2 = THING1; - }); - - cases.add_both("circular struct definitions", - \\struct Bar; - \\ - \\struct Foo { - \\ struct Bar *next; - \\}; - \\ - \\struct Bar { - \\ struct Foo *next; - \\}; - , &[_][]const u8{ - \\pub const struct_Bar = extern struct { - \\ next: [*c]struct_Foo, - \\}; - , - \\pub const struct_Foo = extern struct { - \\ next: [*c]struct_Bar, - \\}; - }); - - cases.add("generate inline func for #define global extern fn", - \\extern void (*fn_ptr)(void); - \\#define foo fn_ptr - \\ - \\extern char (*fn_ptr2)(int, float); - \\#define bar fn_ptr2 - , &[_][]const u8{ - \\pub extern var fn_ptr: ?extern fn () void; - , - \\pub inline fn foo() void { - \\ return fn_ptr.?(); - \\} - , - \\pub extern var fn_ptr2: ?extern fn (c_int, f32) u8; - , - \\pub inline fn bar(arg0: c_int, arg1: f32) u8 { - \\ return fn_ptr2.?(arg0, arg1); - \\} - }); - - cases.add("#define string", - \\#define foo "a string" - , &[_][]const u8{ - \\pub const foo = "a string"; - }); - cases.add("__cdecl doesn't mess up function pointers", \\void foo(void (__cdecl *fn_ptr)(void)); , &[_][]const u8{ \\pub extern fn foo(fn_ptr: ?extern fn () void) void; }); - cases.add("comment after integer literal", - \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , &[_][]const u8{ - \\pub const SDL_INIT_VIDEO = 32; - }); - - cases.add("u integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020u /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , &[_][]const u8{ - \\pub const SDL_INIT_VIDEO = @as(c_uint, 32); - }); - - cases.add("l integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020l /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , &[_][]const u8{ - \\pub const SDL_INIT_VIDEO = @as(c_long, 32); - }); - - cases.add("ul integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020ul /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , &[_][]const u8{ - \\pub const SDL_INIT_VIDEO = @as(c_ulong, 32); - }); - - cases.add("lu integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020lu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , &[_][]const u8{ - \\pub const SDL_INIT_VIDEO = @as(c_ulong, 32); - }); - - cases.add("ll integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020ll /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , &[_][]const u8{ - \\pub const SDL_INIT_VIDEO = @as(c_longlong, 32); - }); - - cases.add("ull integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020ull /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , &[_][]const u8{ - \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 32); - }); - - cases.add("llu integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020llu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , &[_][]const u8{ - \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 32); - }); - - cases.add_both("zig keywords in C code", - \\struct comptime { - \\ int defer; - \\}; - , &[_][]const u8{ - \\pub const struct_comptime = extern struct { - \\ @"defer": c_int, - \\}; - , - \\pub const @"comptime" = struct_comptime; - }); - cases.add("macro defines string literal with hex", \\#define FOO "aoeu\xab derp" \\#define FOO2 "aoeu\x0007a derp" @@ -788,12 +936,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const FOO_CHAR = 63; }); - cases.add("macro with parens around negative number", - \\#define LUA_GLOBALSINDEX (-10002) - , &[_][]const u8{ - \\pub const LUA_GLOBALSINDEX = -10002; - }); - cases.addC("post increment", \\unsigned foo1(unsigned a) { \\ a++; @@ -1521,44 +1663,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("macros with field targets", - \\typedef unsigned int GLbitfield; - \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask); - \\typedef void(*OpenGLProc)(void); - \\union OpenGLProcs { - \\ OpenGLProc ptr[1]; - \\ struct { - \\ PFNGLCLEARPROC Clear; - \\ } gl; - \\}; - \\extern union OpenGLProcs glProcs; - \\#define glClearUnion glProcs.gl.Clear - \\#define glClearPFN PFNGLCLEARPROC - , &[_][]const u8{ - \\pub const GLbitfield = c_uint; - , - \\pub const PFNGLCLEARPROC = ?extern fn (GLbitfield) void; - , - \\pub const OpenGLProc = ?extern fn () void; - , - \\pub const union_OpenGLProcs = extern union { - \\ ptr: [1]OpenGLProc, - \\ gl: extern struct { - \\ Clear: PFNGLCLEARPROC, - \\ }, - \\}; - , - \\pub extern var glProcs: union_OpenGLProcs; - , - \\pub const glClearPFN = PFNGLCLEARPROC; - , - \\pub inline fn glClearUnion(arg0: GLbitfield) void { - \\ return glProcs.gl.Clear.?(arg0); - \\} - , - \\pub const OpenGLProcs = union_OpenGLProcs; - }); - cases.add("variable name shadowing", \\int foo(void) { \\ int x = 1; @@ -1625,12 +1729,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("macro pointer cast", - \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) - , &[_][]const u8{ - \\pub const NRF_GPIO = if (@typeId(@TypeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@TypeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE); - }); - cases.add("if on non-bool", \\enum SomeEnum { A, B, C }; \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) { @@ -1732,70 +1830,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC( - "u integer suffix after 0 (zero) in macro definition", - "#define ZERO 0U", - &[_][]const u8{ - "pub const ZERO = @as(c_uint, 0);", - }, - ); - - cases.addC( - "l integer suffix after 0 (zero) in macro definition", - "#define ZERO 0L", - &[_][]const u8{ - "pub const ZERO = @as(c_long, 0);", - }, - ); - - cases.addC( - "ul integer suffix after 0 (zero) in macro definition", - "#define ZERO 0UL", - &[_][]const u8{ - "pub const ZERO = @as(c_ulong, 0);", - }, - ); - - cases.addC( - "lu integer suffix after 0 (zero) in macro definition", - "#define ZERO 0LU", - &[_][]const u8{ - "pub const ZERO = @as(c_ulong, 0);", - }, - ); - - cases.addC( - "ll integer suffix after 0 (zero) in macro definition", - "#define ZERO 0LL", - &[_][]const u8{ - "pub const ZERO = @as(c_longlong, 0);", - }, - ); - - cases.addC( - "ull integer suffix after 0 (zero) in macro definition", - "#define ZERO 0ULL", - &[_][]const u8{ - "pub const ZERO = @as(c_ulonglong, 0);", - }, - ); - - cases.addC( - "llu integer suffix after 0 (zero) in macro definition", - "#define ZERO 0LLU", - &[_][]const u8{ - "pub const ZERO = @as(c_ulonglong, 0);", - }, - ); - - cases.addC( - "bitwise not on u-suffixed 0 (zero) in macro definition", - "#define NOT_ZERO (~0U)", - &[_][]const u8{ - "pub const NOT_ZERO = ~@as(c_uint, 0);", - }, - ); - cases.addC("implicit casts", \\#include \\ @@ -1936,4 +1970,121 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export fn foo() void {} \\pub export fn bar() void {} }); + + cases.add("#define a char literal", + \\#define A_CHAR 'a' + , &[_][]const u8{ + \\pub const A_CHAR = 97; + }); + + cases.add("generate inline func for #define global extern fn", + \\extern void (*fn_ptr)(void); + \\#define foo fn_ptr + \\ + \\extern char (*fn_ptr2)(int, float); + \\#define bar fn_ptr2 + , &[_][]const u8{ + \\pub extern var fn_ptr: ?extern fn () void; + , + \\pub inline fn foo() void { + \\ return fn_ptr.?(); + \\} + , + \\pub extern var fn_ptr2: ?extern fn (c_int, f32) u8; + , + \\pub inline fn bar(arg0: c_int, arg1: f32) u8 { + \\ return fn_ptr2.?(arg0, arg1); + \\} + }); + cases.add("comment after integer literal", + \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = 32; + }); + + cases.add("u integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020u /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_uint, 32); + }); + + cases.add("l integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020l /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_long, 32); + }); + + cases.add("ul integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020ul /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_ulong, 32); + }); + + cases.add("lu integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020lu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_ulong, 32); + }); + + cases.add("ll integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020ll /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_longlong, 32); + }); + + cases.add("ull integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020ull /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 32); + }); + + cases.add("llu integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020llu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 32); + }); + + cases.add("macros with field targets", + \\typedef unsigned int GLbitfield; + \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask); + \\typedef void(*OpenGLProc)(void); + \\union OpenGLProcs { + \\ OpenGLProc ptr[1]; + \\ struct { + \\ PFNGLCLEARPROC Clear; + \\ } gl; + \\}; + \\extern union OpenGLProcs glProcs; + \\#define glClearUnion glProcs.gl.Clear + \\#define glClearPFN PFNGLCLEARPROC + , &[_][]const u8{ + \\pub const GLbitfield = c_uint; + , + \\pub const PFNGLCLEARPROC = ?extern fn (GLbitfield) void; + , + \\pub const OpenGLProc = ?extern fn () void; + , + \\pub const union_OpenGLProcs = extern union { + \\ ptr: [1]OpenGLProc, + \\ gl: extern struct { + \\ Clear: PFNGLCLEARPROC, + \\ }, + \\}; + , + \\pub extern var glProcs: union_OpenGLProcs; + , + \\pub const glClearPFN = PFNGLCLEARPROC; + , + \\pub inline fn glClearUnion(arg0: GLbitfield) void { + \\ return glProcs.gl.Clear.?(arg0); + \\} + , + \\pub const OpenGLProcs = union_OpenGLProcs; + }); + + cases.add("macro pointer cast", + \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) + , &[_][]const u8{ + \\pub const NRF_GPIO = if (@typeId(@TypeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@TypeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE); + }); }