mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-05-07 02:12:40 +03:00
Merge pull request #3935 from Vexu/translate-c-2
Translate-c-2 the rest
This commit is contained in:
+3
-1
@@ -582,7 +582,9 @@ pub fn formatAsciiChar(
|
||||
comptime Errors: type,
|
||||
output: fn (@TypeOf(context), []const u8) Errors!void,
|
||||
) Errors!void {
|
||||
return output(context, @as(*const [1]u8, &c)[0..]);
|
||||
if (std.ascii.isPrint(c))
|
||||
return output(context, @as(*const [1]u8, &c)[0..]);
|
||||
return format(context, Errors, output, "\\x{x:0<2}", .{c});
|
||||
}
|
||||
|
||||
pub fn formatBuf(
|
||||
|
||||
+16
-16
@@ -1431,13 +1431,13 @@ pub const Node = struct {
|
||||
AssignBitShiftRight,
|
||||
AssignBitXor,
|
||||
AssignDiv,
|
||||
AssignMinus,
|
||||
AssignMinusWrap,
|
||||
AssignSub,
|
||||
AssignSubWrap,
|
||||
AssignMod,
|
||||
AssignPlus,
|
||||
AssignPlusWrap,
|
||||
AssignTimes,
|
||||
AssignTimesWarp,
|
||||
AssignAdd,
|
||||
AssignAddWrap,
|
||||
AssignMul,
|
||||
AssignMulWrap,
|
||||
BangEqual,
|
||||
BitAnd,
|
||||
BitOr,
|
||||
@@ -1456,8 +1456,8 @@ pub const Node = struct {
|
||||
LessThan,
|
||||
MergeErrorSets,
|
||||
Mod,
|
||||
Mult,
|
||||
MultWrap,
|
||||
Mul,
|
||||
MulWrap,
|
||||
Period,
|
||||
Range,
|
||||
Sub,
|
||||
@@ -1490,13 +1490,13 @@ pub const Node = struct {
|
||||
Op.AssignBitShiftRight,
|
||||
Op.AssignBitXor,
|
||||
Op.AssignDiv,
|
||||
Op.AssignMinus,
|
||||
Op.AssignMinusWrap,
|
||||
Op.AssignSub,
|
||||
Op.AssignSubWrap,
|
||||
Op.AssignMod,
|
||||
Op.AssignPlus,
|
||||
Op.AssignPlusWrap,
|
||||
Op.AssignTimes,
|
||||
Op.AssignTimesWarp,
|
||||
Op.AssignAdd,
|
||||
Op.AssignAddWrap,
|
||||
Op.AssignMul,
|
||||
Op.AssignMulWrap,
|
||||
Op.BangEqual,
|
||||
Op.BitAnd,
|
||||
Op.BitOr,
|
||||
@@ -1514,8 +1514,8 @@ pub const Node = struct {
|
||||
Op.LessThan,
|
||||
Op.MergeErrorSets,
|
||||
Op.Mod,
|
||||
Op.Mult,
|
||||
Op.MultWrap,
|
||||
Op.Mul,
|
||||
Op.MulWrap,
|
||||
Op.Period,
|
||||
Op.Range,
|
||||
Op.Sub,
|
||||
|
||||
@@ -1981,19 +1981,19 @@ fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
|
||||
|
||||
const token = nextToken(it);
|
||||
const op = switch (token.ptr.id) {
|
||||
.AsteriskEqual => Op{ .AssignTimes = {} },
|
||||
.AsteriskEqual => Op{ .AssignMul = {} },
|
||||
.SlashEqual => Op{ .AssignDiv = {} },
|
||||
.PercentEqual => Op{ .AssignMod = {} },
|
||||
.PlusEqual => Op{ .AssignPlus = {} },
|
||||
.MinusEqual => Op{ .AssignMinus = {} },
|
||||
.PlusEqual => Op{ .AssignAdd = {} },
|
||||
.MinusEqual => Op{ .AssignSub = {} },
|
||||
.AngleBracketAngleBracketLeftEqual => Op{ .AssignBitShiftLeft = {} },
|
||||
.AngleBracketAngleBracketRightEqual => Op{ .AssignBitShiftRight = {} },
|
||||
.AmpersandEqual => Op{ .AssignBitAnd = {} },
|
||||
.CaretEqual => Op{ .AssignBitXor = {} },
|
||||
.PipeEqual => Op{ .AssignBitOr = {} },
|
||||
.AsteriskPercentEqual => Op{ .AssignTimesWarp = {} },
|
||||
.PlusPercentEqual => Op{ .AssignPlusWrap = {} },
|
||||
.MinusPercentEqual => Op{ .AssignMinusWrap = {} },
|
||||
.AsteriskPercentEqual => Op{ .AssignMulWrap = {} },
|
||||
.PlusPercentEqual => Op{ .AssignAddWrap = {} },
|
||||
.MinusPercentEqual => Op{ .AssignSubWrap = {} },
|
||||
.Equal => Op{ .Assign = {} },
|
||||
else => {
|
||||
putBackToken(it, token.index);
|
||||
@@ -2120,11 +2120,11 @@ fn parseMultiplyOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
|
||||
const token = nextToken(it);
|
||||
const op = switch (token.ptr.id) {
|
||||
.PipePipe => ops{ .BoolOr = {} },
|
||||
.Asterisk => ops{ .Mult = {} },
|
||||
.Asterisk => ops{ .Mul = {} },
|
||||
.Slash => ops{ .Div = {} },
|
||||
.Percent => ops{ .Mod = {} },
|
||||
.AsteriskAsterisk => ops{ .ArrayMult = {} },
|
||||
.AsteriskPercent => ops{ .MultWrap = {} },
|
||||
.AsteriskPercent => ops{ .MulWrap = {} },
|
||||
else => {
|
||||
putBackToken(it, token.index);
|
||||
return null;
|
||||
|
||||
@@ -1635,7 +1635,7 @@ fn renderExpression(
|
||||
.If => {
|
||||
const if_node = @fieldParentPtr(ast.Node.If, "base", base);
|
||||
|
||||
const lparen = tree.prevToken(if_node.condition.firstToken());
|
||||
const lparen = tree.nextToken(if_node.if_token);
|
||||
const rparen = tree.nextToken(if_node.condition.lastToken());
|
||||
|
||||
try renderToken(tree, stream, if_node.if_token, indent, start_col, Space.Space); // if
|
||||
|
||||
+214
-114
@@ -27,6 +27,10 @@ pub const CToken = struct {
|
||||
Lt,
|
||||
Comma,
|
||||
Fn,
|
||||
Arrow,
|
||||
LBrace,
|
||||
RBrace,
|
||||
Pipe,
|
||||
};
|
||||
|
||||
pub const NumLitSuffix = enum {
|
||||
@@ -71,69 +75,130 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken {
|
||||
}
|
||||
} else return tok;
|
||||
var bytes = try allocator.alloc(u8, tok.bytes.len * 2);
|
||||
var escape = false;
|
||||
var state: enum {
|
||||
Start,
|
||||
Escape,
|
||||
Hex,
|
||||
Octal,
|
||||
} = .Start;
|
||||
var i: usize = 0;
|
||||
var count: u8 = 0;
|
||||
var num: u8 = 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;
|
||||
switch (state) {
|
||||
.Escape => {
|
||||
switch (c) {
|
||||
'n', 'r', 't', '\\', '\'', '\"' => {
|
||||
bytes[i] = c;
|
||||
},
|
||||
'0'...'7' => {
|
||||
count += 1;
|
||||
num += c - '0';
|
||||
state = .Octal;
|
||||
bytes[i] = 'x';
|
||||
},
|
||||
'x' => {
|
||||
state = .Hex;
|
||||
bytes[i] = 'x';
|
||||
},
|
||||
'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;
|
||||
},
|
||||
else => {
|
||||
// unknown escape sequence
|
||||
return error.TokenizingFailed;
|
||||
},
|
||||
}
|
||||
i += 1;
|
||||
if (state == .Escape)
|
||||
state = .Start;
|
||||
},
|
||||
.Start => {
|
||||
if (c == '\\') {
|
||||
state = .Escape;
|
||||
}
|
||||
bytes[i] = c;
|
||||
i += 1;
|
||||
},
|
||||
.Hex => {
|
||||
switch (c) {
|
||||
'0'...'9' => {
|
||||
num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed;
|
||||
num += c - '0';
|
||||
},
|
||||
'a'...'f' => {
|
||||
num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed;
|
||||
num += c - 'a' + 10;
|
||||
},
|
||||
'A'...'F' => {
|
||||
num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed;
|
||||
num += c - 'A' + 10;
|
||||
},
|
||||
else => {
|
||||
i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2});
|
||||
num = 0;
|
||||
if (c == '\\')
|
||||
state = .Escape
|
||||
else
|
||||
state = .Start;
|
||||
bytes[i] = c;
|
||||
i += 1;
|
||||
},
|
||||
}
|
||||
},
|
||||
.Octal => {
|
||||
switch (c) {
|
||||
'0'...'7' => {
|
||||
count += 1;
|
||||
num = std.math.mul(u8, num, 8) catch return error.TokenizingFailed;
|
||||
num += c - '0';
|
||||
if (count < 3)
|
||||
continue;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2});
|
||||
state = .Start;
|
||||
count = 0;
|
||||
num = 0;
|
||||
},
|
||||
}
|
||||
}
|
||||
if (state == .Hex or state == .Octal)
|
||||
i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2});
|
||||
return CToken{
|
||||
.id = tok.id,
|
||||
.bytes = bytes[0..i],
|
||||
@@ -164,6 +229,8 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
NumLitIntSuffixL,
|
||||
NumLitIntSuffixLL,
|
||||
NumLitIntSuffixUL,
|
||||
Minus,
|
||||
Done,
|
||||
} = .Start;
|
||||
|
||||
var result = CToken{
|
||||
@@ -178,9 +245,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
const c = chars[i.*];
|
||||
if (c == 0) {
|
||||
switch (state) {
|
||||
.Start => {
|
||||
return result;
|
||||
},
|
||||
.Identifier,
|
||||
.Decimal,
|
||||
.Hex,
|
||||
@@ -193,6 +257,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
return result;
|
||||
},
|
||||
.Start,
|
||||
.Minus,
|
||||
.Done,
|
||||
.NumLitIntSuffixU,
|
||||
.NumLitIntSuffixL,
|
||||
.NumLitIntSuffixUL,
|
||||
@@ -212,7 +279,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
=> return error.TokenizingFailed,
|
||||
}
|
||||
}
|
||||
i.* += 1;
|
||||
switch (state) {
|
||||
.Start => {
|
||||
switch (c) {
|
||||
@@ -220,12 +286,12 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
'\'' => {
|
||||
state = .CharLit;
|
||||
result.id = .CharLit;
|
||||
begin_index = i.* - 1;
|
||||
begin_index = i.*;
|
||||
},
|
||||
'\"' => {
|
||||
state = .String;
|
||||
result.id = .StrLit;
|
||||
begin_index = i.* - 1;
|
||||
begin_index = i.*;
|
||||
},
|
||||
'/' => {
|
||||
state = .OpenComment;
|
||||
@@ -239,21 +305,21 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
'a'...'z', 'A'...'Z', '_' => {
|
||||
state = .Identifier;
|
||||
result.id = .Identifier;
|
||||
begin_index = i.* - 1;
|
||||
begin_index = i.*;
|
||||
},
|
||||
'1'...'9' => {
|
||||
state = .Decimal;
|
||||
result.id = .NumLitInt;
|
||||
begin_index = i.* - 1;
|
||||
begin_index = i.*;
|
||||
},
|
||||
'0' => {
|
||||
state = .GotZero;
|
||||
result.id = .NumLitInt;
|
||||
begin_index = i.* - 1;
|
||||
begin_index = i.*;
|
||||
},
|
||||
'.' => {
|
||||
result.id = .Dot;
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
'<' => {
|
||||
result.id = .Lt;
|
||||
@@ -261,40 +327,64 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
},
|
||||
'(' => {
|
||||
result.id = .LParen;
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
')' => {
|
||||
result.id = .RParen;
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
'*' => {
|
||||
result.id = .Asterisk;
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
'-' => {
|
||||
state = .Minus;
|
||||
result.id = .Minus;
|
||||
return result;
|
||||
},
|
||||
'!' => {
|
||||
result.id = .Bang;
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
'~' => {
|
||||
result.id = .Tilde;
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
',' => {
|
||||
result.id = .Comma;
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
'[' => {
|
||||
result.id = .LBrace;
|
||||
state = .Done;
|
||||
},
|
||||
']' => {
|
||||
result.id = .RBrace;
|
||||
state = .Done;
|
||||
},
|
||||
'|' => {
|
||||
result.id = .Pipe;
|
||||
state = .Done;
|
||||
},
|
||||
else => return error.TokenizingFailed,
|
||||
}
|
||||
},
|
||||
.Done => return result,
|
||||
.Minus => {
|
||||
switch (c) {
|
||||
'>' => {
|
||||
result.id = .Arrow;
|
||||
state = .Done;
|
||||
},
|
||||
else => {
|
||||
return result;
|
||||
},
|
||||
}
|
||||
},
|
||||
.GotLt => {
|
||||
switch (c) {
|
||||
'<' => {
|
||||
result.id = .Shl;
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
else => {
|
||||
return result;
|
||||
@@ -310,19 +400,16 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
'f',
|
||||
'F',
|
||||
=> {
|
||||
i.* -= 1;
|
||||
result.num_lit_suffix = .F;
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
'l', 'L' => {
|
||||
i.* -= 1;
|
||||
result.num_lit_suffix = .L;
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
else => {
|
||||
i.* -= 1;
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
return result;
|
||||
},
|
||||
@@ -352,16 +439,15 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
'0'...'9' => {},
|
||||
'f', 'F' => {
|
||||
result.num_lit_suffix = .F;
|
||||
result.bytes = chars[begin_index .. i.* - 1];
|
||||
return result;
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
state = .Done;
|
||||
},
|
||||
'l', 'L' => {
|
||||
result.num_lit_suffix = .L;
|
||||
result.bytes = chars[begin_index .. i.* - 1];
|
||||
return result;
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
state = .Done;
|
||||
},
|
||||
else => {
|
||||
i.* -= 1;
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
return result;
|
||||
},
|
||||
@@ -374,19 +460,18 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
'u', 'U' => {
|
||||
state = .NumLitIntSuffixU;
|
||||
result.num_lit_suffix = .U;
|
||||
result.bytes = chars[begin_index .. i.* - 1];
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
},
|
||||
'l', 'L' => {
|
||||
state = .NumLitIntSuffixL;
|
||||
result.num_lit_suffix = .L;
|
||||
result.bytes = chars[begin_index .. i.* - 1];
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
},
|
||||
'.' => {
|
||||
result.id = .NumLitFloat;
|
||||
state = .Float;
|
||||
},
|
||||
else => {
|
||||
i.* -= 1;
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
return result;
|
||||
},
|
||||
@@ -407,12 +492,12 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
'u', 'U' => {
|
||||
state = .NumLitIntSuffixU;
|
||||
result.num_lit_suffix = .U;
|
||||
result.bytes = chars[begin_index .. i.* - 1];
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
},
|
||||
'l', 'L' => {
|
||||
state = .NumLitIntSuffixL;
|
||||
result.num_lit_suffix = .L;
|
||||
result.bytes = chars[begin_index .. i.* - 1];
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
},
|
||||
else => {
|
||||
i.* -= 1;
|
||||
@@ -425,7 +510,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
'0'...'7' => {},
|
||||
'8', '9' => return error.TokenizingFailed,
|
||||
else => {
|
||||
i.* -= 1;
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
return result;
|
||||
},
|
||||
@@ -438,16 +522,15 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
// marks the number literal as unsigned
|
||||
state = .NumLitIntSuffixU;
|
||||
result.num_lit_suffix = .U;
|
||||
result.bytes = chars[begin_index .. i.* - 1];
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
},
|
||||
'l', 'L' => {
|
||||
// marks the number literal as long
|
||||
state = .NumLitIntSuffixL;
|
||||
result.num_lit_suffix = .L;
|
||||
result.bytes = chars[begin_index .. i.* - 1];
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
},
|
||||
else => {
|
||||
i.* -= 1;
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
return result;
|
||||
},
|
||||
@@ -461,16 +544,15 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
// marks the number literal as unsigned
|
||||
state = .NumLitIntSuffixU;
|
||||
result.num_lit_suffix = .U;
|
||||
result.bytes = chars[begin_index .. i.* - 1];
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
},
|
||||
'l', 'L' => {
|
||||
// marks the number literal as long
|
||||
state = .NumLitIntSuffixL;
|
||||
result.num_lit_suffix = .L;
|
||||
result.bytes = chars[begin_index .. i.* - 1];
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
},
|
||||
else => {
|
||||
i.* -= 1;
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
return result;
|
||||
},
|
||||
@@ -483,7 +565,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
state = .NumLitIntSuffixUL;
|
||||
},
|
||||
else => {
|
||||
i.* -= 1;
|
||||
return result;
|
||||
},
|
||||
}
|
||||
@@ -496,10 +577,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
},
|
||||
'u', 'U' => {
|
||||
result.num_lit_suffix = .LU;
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
else => {
|
||||
i.* -= 1;
|
||||
return result;
|
||||
},
|
||||
}
|
||||
@@ -508,10 +588,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
switch (c) {
|
||||
'u', 'U' => {
|
||||
result.num_lit_suffix = .LLU;
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
else => {
|
||||
i.* -= 1;
|
||||
return result;
|
||||
},
|
||||
}
|
||||
@@ -520,10 +599,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
switch (c) {
|
||||
'l', 'L' => {
|
||||
result.num_lit_suffix = .LLU;
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
else => {
|
||||
i.* -= 1;
|
||||
return result;
|
||||
},
|
||||
}
|
||||
@@ -532,17 +610,16 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
switch (c) {
|
||||
'_', 'a'...'z', 'A'...'Z', '0'...'9' => {},
|
||||
else => {
|
||||
i.* -= 1;
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
return result;
|
||||
},
|
||||
}
|
||||
},
|
||||
.String => { // TODO char escapes
|
||||
.String => {
|
||||
switch (c) {
|
||||
'\"' => {
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
return result;
|
||||
result.bytes = chars[begin_index .. i.* + 1];
|
||||
state = .Done;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@@ -550,8 +627,8 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
.CharLit => {
|
||||
switch (c) {
|
||||
'\'' => {
|
||||
result.bytes = chars[begin_index..i.*];
|
||||
return result;
|
||||
result.bytes = chars[begin_index .. i.* + 1];
|
||||
state = .Done;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@@ -566,7 +643,7 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
},
|
||||
else => {
|
||||
result.id = .Slash;
|
||||
return result;
|
||||
state = .Done;
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -598,6 +675,7 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
|
||||
}
|
||||
},
|
||||
}
|
||||
i.* += 1;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
@@ -645,7 +723,7 @@ test "tokenize macro" {
|
||||
expect(it.next() == null);
|
||||
tl.shrink(0);
|
||||
|
||||
const src5 = "FOO 0l";
|
||||
const src5 = "FOO 0ull";
|
||||
try tokenizeCMacro(&tl, src5);
|
||||
it = tl.iterator(0);
|
||||
expect(it.next().?.id == .Identifier);
|
||||
@@ -654,3 +732,25 @@ test "tokenize macro" {
|
||||
expect(it.next() == null);
|
||||
tl.shrink(0);
|
||||
}
|
||||
|
||||
test "escape sequences" {
|
||||
var buf: [1024]u8 = undefined;
|
||||
var alloc = std.heap.FixedBufferAllocator.init(buf[0..]);
|
||||
const a = &alloc.allocator;
|
||||
expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{
|
||||
.id = .StrLit,
|
||||
.bytes = "\\x0077",
|
||||
})).bytes, "\\x77"));
|
||||
expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{
|
||||
.id = .StrLit,
|
||||
.bytes = "\\24500",
|
||||
})).bytes, "\\xa500"));
|
||||
expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{
|
||||
.id = .StrLit,
|
||||
.bytes = "\\x0077 abc",
|
||||
})).bytes, "\\x77 abc"));
|
||||
expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{
|
||||
.id = .StrLit,
|
||||
.bytes = "\\045abc",
|
||||
})).bytes, "\\x25abc"));
|
||||
}
|
||||
|
||||
@@ -76,6 +76,10 @@ pub const struct_ZigClangFunctionType = @OpaqueType();
|
||||
pub const struct_ZigClangPredefinedExpr = @OpaqueType();
|
||||
pub const struct_ZigClangInitListExpr = @OpaqueType();
|
||||
pub const ZigClangPreprocessingRecord = @OpaqueType();
|
||||
pub const ZigClangFloatingLiteral = @OpaqueType();
|
||||
pub const ZigClangConstantExpr = @OpaqueType();
|
||||
pub const ZigClangCharacterLiteral = @OpaqueType();
|
||||
pub const ZigClangStmtExpr = @OpaqueType();
|
||||
|
||||
pub const ZigClangBO = extern enum {
|
||||
PtrMemD,
|
||||
@@ -710,6 +714,14 @@ pub const ZigClangStringLiteral_StringKind = extern enum {
|
||||
UTF32,
|
||||
};
|
||||
|
||||
pub const ZigClangCharacterLiteral_CharacterKind = extern enum {
|
||||
Ascii,
|
||||
Wide,
|
||||
UTF8,
|
||||
UTF16,
|
||||
UTF32,
|
||||
};
|
||||
|
||||
pub const ZigClangRecordDecl_field_iterator = extern struct {
|
||||
opaque: *c_void,
|
||||
};
|
||||
@@ -730,6 +742,11 @@ pub const ZigClangPreprocessedEntity_EntityKind = extern enum {
|
||||
InclusionDirectiveKind,
|
||||
};
|
||||
|
||||
pub const ZigClangExpr_ConstExprUsage = extern enum {
|
||||
EvaluateForCodeGen,
|
||||
EvaluateForMangling,
|
||||
};
|
||||
|
||||
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;
|
||||
@@ -744,6 +761,8 @@ pub extern fn ZigClangEnumType_getDecl(record_ty: ?*const struct_ZigClangEnumTyp
|
||||
pub extern fn ZigClangRecordDecl_getCanonicalDecl(record_decl: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangTagDecl;
|
||||
pub extern fn ZigClangEnumDecl_getCanonicalDecl(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangTagDecl;
|
||||
pub extern fn ZigClangTypedefNameDecl_getCanonicalDecl(self: ?*const struct_ZigClangTypedefNameDecl) ?*const struct_ZigClangTypedefNameDecl;
|
||||
pub extern fn ZigClangFunctionDecl_getCanonicalDecl(self: ?*const struct_ZigClangFunctionDecl) ?*const struct_ZigClangFunctionDecl;
|
||||
pub extern fn ZigClangVarDecl_getCanonicalDecl(self: ?*const struct_ZigClangVarDecl) ?*const struct_ZigClangVarDecl;
|
||||
pub extern fn ZigClangRecordDecl_getDefinition(self: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangRecordDecl;
|
||||
pub extern fn ZigClangEnumDecl_getDefinition(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangEnumDecl;
|
||||
pub extern fn ZigClangRecordDecl_getLocation(self: ?*const struct_ZigClangRecordDecl) struct_ZigClangSourceLocation;
|
||||
@@ -791,16 +810,16 @@ pub extern fn ZigClangInitListExpr_getInit(self: ?*const struct_ZigClangInitList
|
||||
pub extern fn ZigClangInitListExpr_getArrayFiller(self: ?*const struct_ZigClangInitListExpr) *const ZigClangExpr;
|
||||
pub extern fn ZigClangInitListExpr_getNumInits(self: ?*const struct_ZigClangInitListExpr) c_uint;
|
||||
pub extern fn ZigClangAPValue_getKind(self: ?*const struct_ZigClangAPValue) ZigClangAPValueKind;
|
||||
pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) ?*const struct_ZigClangAPSInt;
|
||||
pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) *const struct_ZigClangAPSInt;
|
||||
pub extern fn ZigClangAPValue_getArrayInitializedElts(self: ?*const struct_ZigClangAPValue) c_uint;
|
||||
pub extern fn ZigClangAPValue_getArraySize(self: ?*const struct_ZigClangAPValue) c_uint;
|
||||
pub extern fn ZigClangAPValue_getLValueBase(self: ?*const struct_ZigClangAPValue) struct_ZigClangAPValueLValueBase;
|
||||
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) [*:0]const u64;
|
||||
pub extern fn ZigClangAPSInt_getNumWords(self: ?*const struct_ZigClangAPSInt) c_uint;
|
||||
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) [*: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;
|
||||
pub extern fn ZigClangAPValueLValueBase_dyn_cast_Expr(self: struct_ZigClangAPValueLValueBase) ?*const struct_ZigClangExpr;
|
||||
@@ -822,6 +841,7 @@ pub extern fn ZigClangFunctionType_getReturnType(self: *const ZigClangFunctionTy
|
||||
pub extern fn ZigClangFunctionProtoType_isVariadic(self: *const struct_ZigClangFunctionProtoType) bool;
|
||||
pub extern fn ZigClangFunctionProtoType_getNumParams(self: *const struct_ZigClangFunctionProtoType) c_uint;
|
||||
pub extern fn ZigClangFunctionProtoType_getParamType(self: *const struct_ZigClangFunctionProtoType, i: c_uint) ZigClangQualType;
|
||||
pub extern fn ZigClangFunctionProtoType_getReturnType(self: *const ZigClangFunctionProtoType) ZigClangQualType;
|
||||
|
||||
pub const ZigClangSourceLocation = struct_ZigClangSourceLocation;
|
||||
pub const ZigClangQualType = struct_ZigClangQualType;
|
||||
@@ -976,6 +996,7 @@ pub extern fn ZigClangIncompleteArrayType_getElementType(*const ZigClangIncomple
|
||||
pub extern fn ZigClangConstantArrayType_getElementType(self: *const struct_ZigClangConstantArrayType) ZigClangQualType;
|
||||
pub extern fn ZigClangConstantArrayType_getSize(self: *const struct_ZigClangConstantArrayType) *const struct_ZigClangAPInt;
|
||||
pub extern fn ZigClangDeclRefExpr_getDecl(*const ZigClangDeclRefExpr) *const ZigClangValueDecl;
|
||||
pub extern fn ZigClangDeclRefExpr_getFoundDecl(*const ZigClangDeclRefExpr) *const ZigClangNamedDecl;
|
||||
|
||||
pub extern fn ZigClangParenType_getInnerType(*const ZigClangParenType) ZigClangQualType;
|
||||
|
||||
@@ -1036,3 +1057,74 @@ pub extern fn ZigClangPreprocessedEntity_getKind(*const ZigClangPreprocessedEnti
|
||||
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;
|
||||
|
||||
pub extern fn ZigClangIfStmt_getThen(*const ZigClangIfStmt) *const ZigClangStmt;
|
||||
pub extern fn ZigClangIfStmt_getElse(*const ZigClangIfStmt) ?*const ZigClangStmt;
|
||||
pub extern fn ZigClangIfStmt_getCond(*const ZigClangIfStmt) *const ZigClangStmt;
|
||||
|
||||
pub extern fn ZigClangWhileStmt_getCond(*const ZigClangWhileStmt) *const ZigClangExpr;
|
||||
pub extern fn ZigClangWhileStmt_getBody(*const ZigClangWhileStmt) *const ZigClangStmt;
|
||||
|
||||
pub extern fn ZigClangDoStmt_getCond(*const ZigClangDoStmt) *const ZigClangExpr;
|
||||
pub extern fn ZigClangDoStmt_getBody(*const ZigClangDoStmt) *const ZigClangStmt;
|
||||
|
||||
pub extern fn ZigClangForStmt_getInit(*const ZigClangForStmt) ?*const ZigClangStmt;
|
||||
pub extern fn ZigClangForStmt_getCond(*const ZigClangForStmt) ?*const ZigClangExpr;
|
||||
pub extern fn ZigClangForStmt_getInc(*const ZigClangForStmt) ?*const ZigClangExpr;
|
||||
pub extern fn ZigClangForStmt_getBody(*const ZigClangForStmt) *const ZigClangStmt;
|
||||
|
||||
pub extern fn ZigClangAPFloat_toString(self: *const ZigClangAPFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8;
|
||||
pub extern fn ZigClangAPFloat_getValueAsApproximateDouble(*const ZigClangFloatingLiteral) f64;
|
||||
|
||||
pub extern fn ZigClangConditionalOperator_getCond(*const ZigClangConditionalOperator) *const ZigClangExpr;
|
||||
pub extern fn ZigClangConditionalOperator_getTrueExpr(*const ZigClangConditionalOperator) *const ZigClangExpr;
|
||||
pub extern fn ZigClangConditionalOperator_getFalseExpr(*const ZigClangConditionalOperator) *const ZigClangExpr;
|
||||
|
||||
pub extern fn ZigClangSwitchStmt_getConditionVariableDeclStmt(*const ZigClangSwitchStmt) ?*const ZigClangDeclStmt;
|
||||
pub extern fn ZigClangSwitchStmt_getCond(*const ZigClangSwitchStmt) *const ZigClangExpr;
|
||||
pub extern fn ZigClangSwitchStmt_getBody(*const ZigClangSwitchStmt) *const ZigClangStmt;
|
||||
pub extern fn ZigClangSwitchStmt_isAllEnumCasesCovered(*const ZigClangSwitchStmt) bool;
|
||||
|
||||
pub extern fn ZigClangCaseStmt_getLHS(*const ZigClangCaseStmt) *const ZigClangExpr;
|
||||
pub extern fn ZigClangCaseStmt_getRHS(*const ZigClangCaseStmt) ?*const ZigClangExpr;
|
||||
pub extern fn ZigClangCaseStmt_getBeginLoc(*const ZigClangCaseStmt) ZigClangSourceLocation;
|
||||
pub extern fn ZigClangCaseStmt_getSubStmt(*const ZigClangCaseStmt) *const ZigClangStmt;
|
||||
|
||||
pub extern fn ZigClangDefaultStmt_getSubStmt(*const ZigClangDefaultStmt) *const ZigClangStmt;
|
||||
|
||||
pub extern fn ZigClangExpr_EvaluateAsConstantExpr(*const ZigClangExpr, *ZigClangExprEvalResult, ZigClangExpr_ConstExprUsage, *const ZigClangASTContext) bool;
|
||||
|
||||
pub extern fn ZigClangPredefinedExpr_getFunctionName(*const ZigClangPredefinedExpr) *const ZigClangStringLiteral;
|
||||
|
||||
pub extern fn ZigClangCharacterLiteral_getBeginLoc(*const ZigClangCharacterLiteral) ZigClangSourceLocation;
|
||||
pub extern fn ZigClangCharacterLiteral_getKind(*const ZigClangCharacterLiteral) ZigClangCharacterLiteral_CharacterKind;
|
||||
pub extern fn ZigClangCharacterLiteral_getValue(*const ZigClangCharacterLiteral) c_uint;
|
||||
|
||||
pub extern fn ZigClangStmtExpr_getSubStmt(*const ZigClangStmtExpr) *const ZigClangCompoundStmt;
|
||||
|
||||
pub extern fn ZigClangMemberExpr_getBase(*const ZigClangMemberExpr) *const ZigClangExpr;
|
||||
pub extern fn ZigClangMemberExpr_isArrow(*const ZigClangMemberExpr) bool;
|
||||
pub extern fn ZigClangMemberExpr_getMemberDecl(*const ZigClangMemberExpr) *const ZigClangValueDecl;
|
||||
|
||||
pub extern fn ZigClangArraySubscriptExpr_getBase(*const ZigClangArraySubscriptExpr) *const ZigClangExpr;
|
||||
pub extern fn ZigClangArraySubscriptExpr_getIdx(*const ZigClangArraySubscriptExpr) *const ZigClangExpr;
|
||||
|
||||
pub extern fn ZigClangCallExpr_getCallee(*const ZigClangCallExpr) *const ZigClangExpr;
|
||||
pub extern fn ZigClangCallExpr_getNumArgs(*const ZigClangCallExpr) c_uint;
|
||||
pub extern fn ZigClangCallExpr_getArgs(*const ZigClangCallExpr) [*]const *const ZigClangExpr;
|
||||
|
||||
pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangQualType;
|
||||
pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangSourceLocation;
|
||||
|
||||
pub extern fn ZigClangUnaryOperator_getOpcode(*const ZigClangUnaryOperator) ZigClangUO;
|
||||
pub extern fn ZigClangUnaryOperator_getType(*const ZigClangUnaryOperator) ZigClangQualType;
|
||||
pub extern fn ZigClangUnaryOperator_getSubExpr(*const ZigClangUnaryOperator) *const ZigClangExpr;
|
||||
pub extern fn ZigClangUnaryOperator_getBeginLoc(*const ZigClangUnaryOperator) ZigClangSourceLocation;
|
||||
|
||||
pub extern fn ZigClangCompoundAssignOperator_getType(*const ZigClangCompoundAssignOperator) ZigClangQualType;
|
||||
pub extern fn ZigClangCompoundAssignOperator_getComputationLHSType(*const ZigClangCompoundAssignOperator) ZigClangQualType;
|
||||
pub extern fn ZigClangCompoundAssignOperator_getComputationResultType(*const ZigClangCompoundAssignOperator) ZigClangQualType;
|
||||
pub extern fn ZigClangCompoundAssignOperator_getBeginLoc(*const ZigClangCompoundAssignOperator) ZigClangSourceLocation;
|
||||
pub extern fn ZigClangCompoundAssignOperator_getOpcode(*const ZigClangCompoundAssignOperator) ZigClangBO;
|
||||
pub extern fn ZigClangCompoundAssignOperator_getLHS(*const ZigClangCompoundAssignOperator) *const ZigClangExpr;
|
||||
pub extern fn ZigClangCompoundAssignOperator_getRHS(*const ZigClangCompoundAssignOperator) *const ZigClangExpr;
|
||||
|
||||
+2310
-746
@@ -8,6 +8,7 @@ const Token = std.zig.Token;
|
||||
usingnamespace @import("clang.zig");
|
||||
const ctok = @import("c_tokenizer.zig");
|
||||
const CToken = ctok.CToken;
|
||||
const mem = std.mem;
|
||||
|
||||
const CallingConvention = std.builtin.TypeInfo.CallingConvention;
|
||||
|
||||
@@ -47,35 +48,34 @@ const Scope = struct {
|
||||
Switch,
|
||||
Block,
|
||||
Root,
|
||||
While,
|
||||
FnDef,
|
||||
Ref,
|
||||
Condition,
|
||||
Loop,
|
||||
};
|
||||
|
||||
const Switch = struct {
|
||||
base: Scope,
|
||||
};
|
||||
|
||||
/// used when getting a member `a.b`
|
||||
const Ref = struct {
|
||||
base: Scope,
|
||||
pending_block: *ast.Node.Block,
|
||||
cases: *ast.Node.Switch.CaseList,
|
||||
has_default: bool = false,
|
||||
};
|
||||
|
||||
const Block = struct {
|
||||
base: Scope,
|
||||
block_node: *ast.Node.Block,
|
||||
variables: AliasList,
|
||||
label: ?[]const u8,
|
||||
|
||||
/// Don't forget to set rbrace token later
|
||||
fn init(c: *Context, parent: *Scope, block_node: *ast.Node.Block) !*Block {
|
||||
/// Don't forget to set rbrace token and block_node later
|
||||
fn init(c: *Context, parent: *Scope, label: ?[]const u8) !*Block {
|
||||
const block = try c.a().create(Block);
|
||||
block.* = .{
|
||||
.base = .{
|
||||
.id = .Block,
|
||||
.parent = parent,
|
||||
},
|
||||
.block_node = block_node,
|
||||
.block_node = undefined,
|
||||
.variables = AliasList.init(c.a()),
|
||||
.label = label,
|
||||
};
|
||||
return block;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ const Scope = struct {
|
||||
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))
|
||||
if (mem.eql(u8, p.name, name))
|
||||
return p.alias;
|
||||
}
|
||||
return scope.base.parent.?.getAlias(name);
|
||||
@@ -92,7 +92,7 @@ const Scope = struct {
|
||||
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))
|
||||
if (mem.eql(u8, p.name, name))
|
||||
return true;
|
||||
}
|
||||
return scope.base.parent.?.contains(name);
|
||||
@@ -120,52 +120,23 @@ const Scope = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const While = struct {
|
||||
base: 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 {
|
||||
fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
|
||||
var scope = inner;
|
||||
while (true) : (scope = scope.parent orelse unreachable) {
|
||||
if (scope.id == .Block) return @fieldParentPtr(Scope.Block, "base", scope);
|
||||
while (true) {
|
||||
switch (scope.id) {
|
||||
.Root => unreachable,
|
||||
.Block => return @fieldParentPtr(Block, "base", scope),
|
||||
.Condition => {
|
||||
// comma operator used
|
||||
return try Block.init(c, scope, "blk");
|
||||
},
|
||||
else => scope = scope.parent.?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn createAlias(scope: *Scope, c: *Context, name: []const u8) !?[]const u8 {
|
||||
if (scope.contains(name)) {
|
||||
if (isZigPrimitiveType(name) or scope.contains(name)) {
|
||||
return try std.fmt.allocPrint(c.a(), "{}_{}", .{ name, c.getMangle() });
|
||||
}
|
||||
return null;
|
||||
@@ -174,22 +145,41 @@ const Scope = struct {
|
||||
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"),
|
||||
.Switch, .Loop, .Condition => scope.parent.?.getAlias(name),
|
||||
};
|
||||
}
|
||||
|
||||
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"),
|
||||
.Switch, .Loop, .Condition => scope.parent.?.contains(name),
|
||||
};
|
||||
}
|
||||
|
||||
fn getBreakableScope(inner: *Scope) *Scope {
|
||||
var scope = inner;
|
||||
while (true) {
|
||||
switch (scope.id) {
|
||||
.Root => unreachable,
|
||||
.Switch => return scope,
|
||||
.Loop => return scope,
|
||||
else => scope = scope.parent.?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn getSwitch(inner: *Scope) *Scope.Switch {
|
||||
var scope = inner;
|
||||
while (true) {
|
||||
switch (scope.id) {
|
||||
.Root => unreachable,
|
||||
.Switch => return @fieldParentPtr(Switch, "base", scope),
|
||||
else => scope = scope.parent.?,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Context = struct {
|
||||
@@ -200,7 +190,6 @@ const Context = struct {
|
||||
decl_table: DeclTable,
|
||||
alias_list: AliasList,
|
||||
global_scope: *Scope.Root,
|
||||
ptr_params: std.BufSet,
|
||||
clang_context: *ZigClangASTContext,
|
||||
mangle_count: u64 = 0,
|
||||
|
||||
@@ -209,13 +198,13 @@ const Context = struct {
|
||||
return c.mangle_count;
|
||||
}
|
||||
|
||||
fn a(c: *Context) *std.mem.Allocator {
|
||||
fn a(c: *Context) *mem.Allocator {
|
||||
return &c.tree.arena_allocator.allocator;
|
||||
}
|
||||
|
||||
/// Convert a null-terminated C string to a slice allocated in the arena
|
||||
fn str(c: *Context, s: [*:0]const u8) ![]u8 {
|
||||
return std.mem.dupe(c.a(), u8, std.mem.toSliceConst(u8, s));
|
||||
return mem.dupe(c.a(), u8, mem.toSliceConst(u8, s));
|
||||
}
|
||||
|
||||
/// Convert a clang source location to a file:line:column string
|
||||
@@ -231,7 +220,7 @@ const Context = struct {
|
||||
};
|
||||
|
||||
pub fn translate(
|
||||
backing_allocator: *std.mem.Allocator,
|
||||
backing_allocator: *mem.Allocator,
|
||||
args_begin: [*]?[*]const u8,
|
||||
args_end: [*]?[*]const u8,
|
||||
errors: *[]ClangErrMsg,
|
||||
@@ -286,7 +275,6 @@ pub fn translate(
|
||||
.decl_table = DeclTable.init(arena),
|
||||
.alias_list = AliasList.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.init(&context);
|
||||
@@ -333,13 +321,13 @@ fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void {
|
||||
return visitFnDecl(c, @ptrCast(*const ZigClangFunctionDecl, decl));
|
||||
},
|
||||
.Typedef => {
|
||||
return resolveTypeDef(c, @ptrCast(*const ZigClangTypedefNameDecl, decl));
|
||||
_ = try transTypeDef(c, @ptrCast(*const ZigClangTypedefNameDecl, decl));
|
||||
},
|
||||
.Enum => {
|
||||
_ = try transEnumDecl(c, @ptrCast(*const ZigClangEnumDecl, decl));
|
||||
},
|
||||
.Record => {
|
||||
return resolveRecordDecl(c, @ptrCast(*const ZigClangRecordDecl, decl));
|
||||
_ = try transRecordDecl(c, @ptrCast(*const ZigClangRecordDecl, decl));
|
||||
},
|
||||
.Var => {
|
||||
return visitVarDecl(c, @ptrCast(*const ZigClangVarDecl, decl));
|
||||
@@ -352,22 +340,17 @@ fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void {
|
||||
}
|
||||
|
||||
fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
|
||||
if (c.decl_table.contains(@ptrToInt(fn_decl))) return; // Avoid processing this decl twice
|
||||
const rp = makeRestorePoint(c);
|
||||
const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl)));
|
||||
_ = try c.decl_table.put(@ptrToInt(fn_decl), fn_name);
|
||||
if (c.global_scope.sym_table.contains(fn_name))
|
||||
return; // Avoid processing this decl twice
|
||||
const rp = makeRestorePoint(c);
|
||||
const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl);
|
||||
const fn_qt = ZigClangFunctionDecl_getType(fn_decl);
|
||||
const fn_type = ZigClangQualType_getTypePtr(fn_qt);
|
||||
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{
|
||||
.fn_name = fn_name,
|
||||
.has_body = has_body,
|
||||
.storage_class = storage_class,
|
||||
.scope = &scope,
|
||||
.is_export = switch (storage_class) {
|
||||
.None => has_body,
|
||||
.Extern, .Static => false,
|
||||
@@ -377,6 +360,15 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
|
||||
else => unreachable,
|
||||
},
|
||||
};
|
||||
|
||||
var fn_qt = ZigClangFunctionDecl_getType(fn_decl);
|
||||
var fn_type = ZigClangQualType_getTypePtr(fn_qt);
|
||||
if (ZigClangType_getTypeClass(fn_type) == .Attributed) {
|
||||
const attr_type = @ptrCast(*const ZigClangAttributedType, fn_type);
|
||||
fn_qt = ZigClangAttributedType_getEquivalentType(attr_type);
|
||||
fn_type = ZigClangQualType_getTypePtr(fn_qt);
|
||||
}
|
||||
|
||||
const proto_node = switch (ZigClangType_getTypeClass(fn_type)) {
|
||||
.FunctionProto => blk: {
|
||||
const fn_proto_type = @ptrCast(*const ZigClangFunctionProtoType, fn_type);
|
||||
@@ -396,7 +388,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
},
|
||||
else => unreachable,
|
||||
else => return failDecl(c, fn_decl_loc, fn_name, "unable to resolve function type {}", .{ZigClangType_getTypeClass(fn_type)}),
|
||||
};
|
||||
|
||||
if (!decl_ctx.has_body) {
|
||||
@@ -406,20 +398,47 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
|
||||
|
||||
// actual function definition with body
|
||||
const body_stmt = ZigClangFunctionDecl_getBody(fn_decl);
|
||||
const body_node = transStmt(rp, scope, body_stmt, .unused, .r_value) catch |err| switch (err) {
|
||||
const block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, null);
|
||||
var scope = &block_scope.base;
|
||||
const block_node = try transCreateNodeBlock(rp.c, null);
|
||||
block_scope.block_node = block_node;
|
||||
|
||||
var it = proto_node.params.iterator(0);
|
||||
while (it.next()) |p| {
|
||||
const param = @fieldParentPtr(ast.Node.ParamDecl, "base", p.*);
|
||||
const param_name = tokenSlice(c, param.name_token orelse
|
||||
return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name}));
|
||||
|
||||
const checked_param_name = if (try scope.createAlias(rp.c, param_name)) |a| blk: {
|
||||
try block_scope.variables.push(.{ .name = param_name, .alias = a });
|
||||
break :blk a;
|
||||
} else param_name;
|
||||
const arg_name = try std.fmt.allocPrint(c.a(), "_arg_{}", .{checked_param_name});
|
||||
|
||||
const node = try transCreateNodeVarDecl(c, false, false, checked_param_name);
|
||||
node.eq_token = try appendToken(c, .Equal, "=");
|
||||
node.init_node = try transCreateNodeIdentifier(c, arg_name);
|
||||
node.semicolon_token = try appendToken(c, .Semicolon, ";");
|
||||
try block_node.statements.push(&node.base);
|
||||
param.name_token = try appendIdentifier(c, arg_name);
|
||||
_ = try appendToken(c, .Colon, ":");
|
||||
}
|
||||
|
||||
transCompoundStmtInline(rp, &block_scope.base, @ptrCast(*const ZigClangCompoundStmt, body_stmt), block_node) 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(body_node.id == .Block);
|
||||
proto_node.body_node = body_node;
|
||||
|
||||
block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
proto_node.body_node = &block_node.base;
|
||||
return addTopLevelDecl(c, fn_name, &proto_node.base);
|
||||
}
|
||||
|
||||
fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
|
||||
if (c.decl_table.contains(@ptrToInt(var_decl))) return; // Avoid processing this decl twice
|
||||
const var_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, var_decl)));
|
||||
if (c.global_scope.sym_table.contains(var_name))
|
||||
return; // Avoid processing this decl twice
|
||||
const rp = makeRestorePoint(c);
|
||||
const visib_tok = try appendToken(c, .Keyword_pub, "pub");
|
||||
|
||||
@@ -429,8 +448,10 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
|
||||
try appendToken(c, .Keyword_threadlocal, "threadlocal");
|
||||
|
||||
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);
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/3756
|
||||
// TODO https://github.com/ziglang/zig/issues/1802
|
||||
const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.a(), "_{}", .{var_name}) else var_name;
|
||||
const var_decl_loc = ZigClangVarDecl_getLocation(var_decl);
|
||||
|
||||
const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl);
|
||||
@@ -449,12 +470,12 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
|
||||
else
|
||||
try appendToken(c, .Keyword_var, "var");
|
||||
|
||||
const name_tok = try appendIdentifier(c, var_name);
|
||||
const name_tok = try appendIdentifier(c, checked_name);
|
||||
|
||||
_ = try appendToken(c, .Colon, ":");
|
||||
const type_node = transQualType(rp, qual_type, var_decl_loc) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
return failDecl(c, var_decl_loc, var_name, "unable to resolve variable type", .{});
|
||||
return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{});
|
||||
},
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
@@ -469,14 +490,14 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
|
||||
error.UnsupportedTranslation,
|
||||
error.UnsupportedType,
|
||||
=> {
|
||||
return failDecl(c, var_decl_loc, var_name, "unable to translate initializer", .{});
|
||||
return failDecl(c, var_decl_loc, checked_name, "unable to translate initializer", .{});
|
||||
},
|
||||
error.OutOfMemory => |e| return e,
|
||||
}
|
||||
else
|
||||
try transCreateNodeUndefinedLiteral(c);
|
||||
} else if (storage_class != .Extern) {
|
||||
return failDecl(c, var_decl_loc, var_name, "non-extern variable has no initializer", .{});
|
||||
return failDecl(c, var_decl_loc, checked_name, "non-extern variable has no initializer", .{});
|
||||
}
|
||||
|
||||
const node = try c.a().create(ast.Node.VarDecl);
|
||||
@@ -496,130 +517,295 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
|
||||
.init_node = init_node,
|
||||
.semicolon_token = try appendToken(c, .Semicolon, ";"),
|
||||
};
|
||||
return addTopLevelDecl(c, var_name, &node.base);
|
||||
return addTopLevelDecl(c, checked_name, &node.base);
|
||||
}
|
||||
|
||||
fn resolveTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl) Error!void {
|
||||
if (c.decl_table.contains(
|
||||
@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)),
|
||||
)) return; // Avoid processing this decl twice
|
||||
fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, builtin_name: []const u8) !*ast.Node {
|
||||
_ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), builtin_name);
|
||||
return transCreateNodeIdentifier(c, builtin_name);
|
||||
}
|
||||
|
||||
fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl) Error!?*ast.Node {
|
||||
if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |kv|
|
||||
return try transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice
|
||||
const rp = makeRestorePoint(c);
|
||||
const visib_tok = try appendToken(c, .Keyword_pub, "pub");
|
||||
const const_tok = try appendToken(c, .Keyword_const, "const");
|
||||
|
||||
const typedef_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl)));
|
||||
|
||||
if (mem.eql(u8, typedef_name, "uint8_t"))
|
||||
return transTypeDefAsBuiltin(c, typedef_decl, "u8")
|
||||
else if (mem.eql(u8, typedef_name, "int8_t"))
|
||||
return transTypeDefAsBuiltin(c, typedef_decl, "i8")
|
||||
else if (mem.eql(u8, typedef_name, "uint16_t"))
|
||||
return transTypeDefAsBuiltin(c, typedef_decl, "u16")
|
||||
else if (mem.eql(u8, typedef_name, "int16_t"))
|
||||
return transTypeDefAsBuiltin(c, typedef_decl, "i16")
|
||||
else if (mem.eql(u8, typedef_name, "uint32_t"))
|
||||
return transTypeDefAsBuiltin(c, typedef_decl, "u32")
|
||||
else if (mem.eql(u8, typedef_name, "int32_t"))
|
||||
return transTypeDefAsBuiltin(c, typedef_decl, "i32")
|
||||
else if (mem.eql(u8, typedef_name, "uint64_t"))
|
||||
return transTypeDefAsBuiltin(c, typedef_decl, "u64")
|
||||
else if (mem.eql(u8, typedef_name, "int64_t"))
|
||||
return transTypeDefAsBuiltin(c, typedef_decl, "i64")
|
||||
else if (mem.eql(u8, typedef_name, "intptr_t"))
|
||||
return transTypeDefAsBuiltin(c, typedef_decl, "isize")
|
||||
else if (mem.eql(u8, typedef_name, "uintptr_t"))
|
||||
return transTypeDefAsBuiltin(c, typedef_decl, "usize")
|
||||
else if (mem.eql(u8, typedef_name, "ssize_t"))
|
||||
return transTypeDefAsBuiltin(c, typedef_decl, "isize")
|
||||
else if (mem.eql(u8, typedef_name, "size_t"))
|
||||
return transTypeDefAsBuiltin(c, typedef_decl, "usize");
|
||||
|
||||
_ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), typedef_name);
|
||||
const name_tok = try appendIdentifier(c, typedef_name);
|
||||
const eq_tok = try appendToken(c, .Equal, "=");
|
||||
const visib_tok = try appendToken(c, .Keyword_pub, "pub");
|
||||
const const_tok = try appendToken(c, .Keyword_const, "const");
|
||||
const node = try transCreateNodeVarDecl(c, true, true, typedef_name);
|
||||
node.eq_token = try appendToken(c, .Equal, "=");
|
||||
|
||||
const child_qt = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
|
||||
const typedef_loc = ZigClangTypedefNameDecl_getLocation(typedef_decl);
|
||||
const type_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) {
|
||||
node.init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
return failDecl(c, typedef_loc, typedef_name, "unable to resolve typedef child type", .{});
|
||||
try failDecl(c, typedef_loc, typedef_name, "unable to resolve typedef child type", .{});
|
||||
return null;
|
||||
},
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
|
||||
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 = const_tok,
|
||||
.comptime_token = null,
|
||||
.extern_export_token = null,
|
||||
.lib_name = null,
|
||||
.type_node = null,
|
||||
.align_node = null,
|
||||
.section_node = null,
|
||||
.init_node = type_node,
|
||||
.semicolon_token = try appendToken(c, .Semicolon, ";"),
|
||||
};
|
||||
node.semicolon_token = try appendToken(c, .Semicolon, ";");
|
||||
try addTopLevelDecl(c, typedef_name, &node.base);
|
||||
return transCreateNodeIdentifier(c, typedef_name);
|
||||
}
|
||||
|
||||
fn resolveRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!void {
|
||||
if (c.decl_table.contains(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) return; // Avoid processing this decl twice
|
||||
const rp = makeRestorePoint(c);
|
||||
fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node {
|
||||
if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |kv|
|
||||
return try transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice
|
||||
const record_loc = ZigClangRecordDecl_getLocation(record_decl);
|
||||
|
||||
const bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, record_decl)));
|
||||
var bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, record_decl)));
|
||||
var is_unnamed = false;
|
||||
if (ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) or bare_name.len == 0) {
|
||||
bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()});
|
||||
is_unnamed = true;
|
||||
}
|
||||
|
||||
const container_kind_name = if (ZigClangRecordDecl_isUnion(record_decl))
|
||||
"union"
|
||||
else if (ZigClangRecordDecl_isStruct(record_decl))
|
||||
"struct"
|
||||
else
|
||||
return emitWarning(c, ZigClangRecordDecl_getLocation(record_decl), "record {} is not a struct or union", .{bare_name});
|
||||
|
||||
if (ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) or bare_name.len == 0)
|
||||
return;
|
||||
|
||||
const visib_tok = try appendToken(c, .Keyword_pub, "pub");
|
||||
const const_tok = try appendToken(c, .Keyword_const, "const");
|
||||
var container_kind_name: []const u8 = undefined;
|
||||
var container_kind: std.zig.Token.Id = undefined;
|
||||
if (ZigClangRecordDecl_isUnion(record_decl)) {
|
||||
container_kind_name = "union";
|
||||
container_kind = .Keyword_union;
|
||||
} else if (ZigClangRecordDecl_isStruct(record_decl)) {
|
||||
container_kind_name = "struct";
|
||||
container_kind = .Keyword_struct;
|
||||
} else {
|
||||
try emitWarning(c, record_loc, "record {} is not a struct or union", .{bare_name});
|
||||
return null;
|
||||
}
|
||||
|
||||
const name = try std.fmt.allocPrint(c.a(), "{}_{}", .{ container_kind_name, bare_name });
|
||||
_ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name);
|
||||
const name_tok = try appendIdentifier(c, name);
|
||||
|
||||
const eq_tok = try appendToken(c, .Equal, "=");
|
||||
const init_node = transRecordDecl(c, record_decl) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
return failDecl(c, ZigClangRecordDecl_getLocation(record_decl), name, "unable to resolve record type", .{});
|
||||
},
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
const semicolon_token = try appendToken(c, .Semicolon, ";");
|
||||
const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name);
|
||||
|
||||
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 = const_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 = semicolon_token,
|
||||
node.eq_token = try appendToken(c, .Equal, "=");
|
||||
|
||||
var semicolon: ast.TokenIndex = undefined;
|
||||
node.init_node = blk: {
|
||||
const rp = makeRestorePoint(c);
|
||||
const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse {
|
||||
const opaque = try transCreateNodeOpaqueType(c);
|
||||
semicolon = try appendToken(c, .Semicolon, ";");
|
||||
break :blk opaque;
|
||||
};
|
||||
|
||||
const extern_tok = try appendToken(c, .Keyword_extern, "extern");
|
||||
const container_tok = try appendToken(c, container_kind, container_kind_name);
|
||||
const lbrace_token = try appendToken(c, .LBrace, "{");
|
||||
|
||||
const container_node = try c.a().create(ast.Node.ContainerDecl);
|
||||
container_node.* = .{
|
||||
.layout_token = extern_tok,
|
||||
.kind_token = container_tok,
|
||||
.init_arg_expr = .None,
|
||||
.fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()),
|
||||
.lbrace_token = lbrace_token,
|
||||
.rbrace_token = undefined,
|
||||
};
|
||||
|
||||
var it = ZigClangRecordDecl_field_begin(record_def);
|
||||
const end_it = ZigClangRecordDecl_field_end(record_def);
|
||||
while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
|
||||
const field_decl = ZigClangRecordDecl_field_iterator_deref(it);
|
||||
const field_loc = ZigClangFieldDecl_getLocation(field_decl);
|
||||
|
||||
if (ZigClangFieldDecl_isBitField(field_decl)) {
|
||||
const opaque = try transCreateNodeOpaqueType(c);
|
||||
semicolon = try appendToken(c, .Semicolon, ";");
|
||||
try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name});
|
||||
break :blk opaque;
|
||||
}
|
||||
const raw_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, field_decl)));
|
||||
if (raw_name.len < 1) continue; // fix weird windows bug?
|
||||
const field_name = try appendIdentifier(c, raw_name);
|
||||
_ = try appendToken(c, .Colon, ":");
|
||||
const field_type = transQualType(rp, ZigClangFieldDecl_getType(field_decl), field_loc) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
try failDecl(c, record_loc, name, "unable to translate {} member type", .{container_kind_name});
|
||||
return null;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
const field_node = try c.a().create(ast.Node.ContainerField);
|
||||
field_node.* = .{
|
||||
.doc_comments = null,
|
||||
.comptime_token = null,
|
||||
.name_token = field_name,
|
||||
.type_expr = field_type,
|
||||
.value_expr = null,
|
||||
.align_expr = null,
|
||||
};
|
||||
|
||||
try container_node.fields_and_decls.push(&field_node.base);
|
||||
_ = try appendToken(c, .Comma, ",");
|
||||
}
|
||||
container_node.rbrace_token = try appendToken(c, .RBrace, "}");
|
||||
semicolon = try appendToken(c, .Semicolon, ";");
|
||||
break :blk &container_node.base;
|
||||
};
|
||||
node.semicolon_token = semicolon;
|
||||
|
||||
try addTopLevelDecl(c, name, &node.base);
|
||||
try c.alias_list.push(.{ .alias = bare_name, .name = name });
|
||||
if (!is_unnamed)
|
||||
try c.alias_list.push(.{ .alias = bare_name, .name = name });
|
||||
return transCreateNodeIdentifier(c, name);
|
||||
}
|
||||
|
||||
fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node {
|
||||
if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name|
|
||||
return try transCreateNodeIdentifier(c, name.value); // Avoid processing this decl twice
|
||||
const rp = makeRestorePoint(c);
|
||||
const enum_loc = ZigClangEnumDecl_getLocation(enum_decl);
|
||||
|
||||
var bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_decl)));
|
||||
var is_unnamed = false;
|
||||
if (bare_name.len == 0) {
|
||||
bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()});
|
||||
is_unnamed = true;
|
||||
}
|
||||
|
||||
const name = try std.fmt.allocPrint(c.a(), "enum_{}", .{bare_name});
|
||||
_ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name);
|
||||
const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name);
|
||||
node.eq_token = try appendToken(c, .Equal, "=");
|
||||
|
||||
node.init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: {
|
||||
var pure_enum = true;
|
||||
var it = ZigClangEnumDecl_enumerator_begin(enum_def);
|
||||
var end_it = ZigClangEnumDecl_enumerator_end(enum_def);
|
||||
while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) {
|
||||
const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it);
|
||||
if (ZigClangEnumConstantDecl_getInitExpr(enum_const)) |_| {
|
||||
pure_enum = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const extern_tok = try appendToken(c, .Keyword_extern, "extern");
|
||||
const container_tok = try appendToken(c, .Keyword_enum, "enum");
|
||||
|
||||
const container_node = try c.a().create(ast.Node.ContainerDecl);
|
||||
container_node.* = .{
|
||||
.layout_token = extern_tok,
|
||||
.kind_token = container_tok,
|
||||
.init_arg_expr = .None,
|
||||
.fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()),
|
||||
.lbrace_token = undefined,
|
||||
.rbrace_token = undefined,
|
||||
};
|
||||
|
||||
const int_type = ZigClangEnumDecl_getIntegerType(enum_decl);
|
||||
|
||||
// TODO only emit this tag type if the enum tag type is not the default.
|
||||
// I don't know what the default is, need to figure out how clang is deciding.
|
||||
// it appears to at least be different across gcc/msvc
|
||||
if (!isCBuiltinType(int_type, .UInt) and
|
||||
!isCBuiltinType(int_type, .Int))
|
||||
{
|
||||
_ = try appendToken(c, .LParen, "(");
|
||||
container_node.init_arg_expr = .{
|
||||
.Type = transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
|
||||
return null;
|
||||
},
|
||||
else => |e| return e,
|
||||
},
|
||||
};
|
||||
_ = try appendToken(c, .RParen, ")");
|
||||
}
|
||||
|
||||
container_node.lbrace_token = try appendToken(c, .LBrace, "{");
|
||||
|
||||
it = ZigClangEnumDecl_enumerator_begin(enum_def);
|
||||
end_it = ZigClangEnumDecl_enumerator_end(enum_def);
|
||||
while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) {
|
||||
const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it);
|
||||
|
||||
const enum_val_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_const)));
|
||||
|
||||
const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name))
|
||||
enum_val_name[bare_name.len..]
|
||||
else
|
||||
enum_val_name;
|
||||
|
||||
const field_name_tok = try appendIdentifier(c, field_name);
|
||||
|
||||
const int_node = if (!pure_enum) blk: {
|
||||
_ = try appendToken(c, .Colon, "=");
|
||||
break :blk try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const));
|
||||
} else
|
||||
null;
|
||||
|
||||
const field_node = try c.a().create(ast.Node.ContainerField);
|
||||
field_node.* = .{
|
||||
.doc_comments = null,
|
||||
.comptime_token = null,
|
||||
.name_token = field_name_tok,
|
||||
.type_expr = null,
|
||||
.value_expr = int_node,
|
||||
.align_expr = null,
|
||||
};
|
||||
|
||||
try container_node.fields_and_decls.push(&field_node.base);
|
||||
_ = try appendToken(c, .Comma, ",");
|
||||
// In C each enum value is in the global namespace. So we put them there too.
|
||||
// At this point we can rely on the enum emitting successfully.
|
||||
const tld_node = try transCreateNodeVarDecl(c, true, true, enum_val_name);
|
||||
tld_node.eq_token = try appendToken(c, .Equal, "=");
|
||||
tld_node.init_node = try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const));
|
||||
tld_node.semicolon_token = try appendToken(c, .Semicolon, ";");
|
||||
try addTopLevelDecl(c, field_name, &tld_node.base);
|
||||
}
|
||||
container_node.rbrace_token = try appendToken(c, .RBrace, "}");
|
||||
|
||||
break :blk &container_node.base;
|
||||
} else
|
||||
try transCreateNodeOpaqueType(c);
|
||||
|
||||
node.semicolon_token = try appendToken(c, .Semicolon, ";");
|
||||
|
||||
try addTopLevelDecl(c, name, &node.base);
|
||||
if (!is_unnamed)
|
||||
try c.alias_list.push(.{ .alias = bare_name, .name = name });
|
||||
return transCreateNodeIdentifier(c, name);
|
||||
}
|
||||
|
||||
fn createAlias(c: *Context, alias: var) !void {
|
||||
const visib_tok = try appendToken(c, .Keyword_pub, "pub");
|
||||
const mut_tok = try appendToken(c, .Keyword_const, "const");
|
||||
const name_tok = try appendIdentifier(c, alias.alias);
|
||||
|
||||
const eq_tok = try appendToken(c, .Equal, "=");
|
||||
const init_node = try transCreateNodeIdentifier(c, alias.name);
|
||||
|
||||
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, ";"),
|
||||
};
|
||||
const node = try transCreateNodeVarDecl(c, true, true, alias.alias);
|
||||
node.eq_token = try appendToken(c, .Equal, "=");
|
||||
node.init_node = try transCreateNodeIdentifier(c, alias.name);
|
||||
node.semicolon_token = try appendToken(c, .Semicolon, ";");
|
||||
return addTopLevelDecl(c, alias.alias, &node.base);
|
||||
}
|
||||
|
||||
@@ -651,9 +837,45 @@ fn transStmt(
|
||||
.IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, stmt), result_used),
|
||||
.ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const ZigClangReturnStmt, stmt)),
|
||||
.StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const ZigClangStringLiteral, stmt), result_used),
|
||||
.ParenExprClass => return transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), result_used, lrvalue),
|
||||
.ParenExprClass => {
|
||||
const expr = try transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), result_used, lrvalue);
|
||||
if (expr.id == .GroupedExpression) return expr;
|
||||
const node = try rp.c.a().create(ast.Node.GroupedExpression);
|
||||
node.* = .{
|
||||
.lparen = try appendToken(rp.c, .LParen, "("),
|
||||
.expr = expr,
|
||||
.rparen = try appendToken(rp.c, .RParen, ")"),
|
||||
};
|
||||
return &node.base;
|
||||
},
|
||||
.InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const ZigClangInitListExpr, stmt), result_used),
|
||||
.ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used),
|
||||
.IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt)),
|
||||
.WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)),
|
||||
.DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)),
|
||||
.NullStmtClass => {
|
||||
const block = try transCreateNodeBlock(rp.c, null);
|
||||
block.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
return &block.base;
|
||||
},
|
||||
.ContinueStmtClass => return try transCreateNodeContinue(rp.c),
|
||||
.BreakStmtClass => return transBreak(rp, scope),
|
||||
.ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const ZigClangForStmt, stmt)),
|
||||
.FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const ZigClangFloatingLiteral, stmt), result_used),
|
||||
.ConditionalOperatorClass => return transConditionalOperator(rp, scope, @ptrCast(*const ZigClangConditionalOperator, stmt), result_used),
|
||||
.SwitchStmtClass => return transSwitch(rp, scope, @ptrCast(*const ZigClangSwitchStmt, stmt)),
|
||||
.CaseStmtClass => return transCase(rp, scope, @ptrCast(*const ZigClangCaseStmt, stmt)),
|
||||
.DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const ZigClangDefaultStmt, stmt)),
|
||||
.ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used),
|
||||
.PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used),
|
||||
.CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used),
|
||||
.StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const ZigClangStmtExpr, stmt), result_used),
|
||||
.MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const ZigClangMemberExpr, stmt), result_used),
|
||||
.ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const ZigClangArraySubscriptExpr, stmt), result_used),
|
||||
.CallExprClass => return transCallExpr(rp, scope, @ptrCast(*const ZigClangCallExpr, stmt), result_used),
|
||||
.UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(rp, scope, @ptrCast(*const ZigClangUnaryExprOrTypeTraitExpr, stmt), result_used),
|
||||
.UnaryOperatorClass => return transUnaryOperator(rp, scope, @ptrCast(*const ZigClangUnaryOperator, stmt), result_used),
|
||||
.CompoundAssignOperatorClass => return transCompoundAssignOperator(rp, scope, @ptrCast(*const ZigClangCompoundAssignOperator, stmt), result_used),
|
||||
else => {
|
||||
return revertAndWarn(
|
||||
rp,
|
||||
@@ -674,91 +896,159 @@ fn transBinaryOperator(
|
||||
) TransError!*ast.Node {
|
||||
const op = ZigClangBinaryOperator_getOpcode(stmt);
|
||||
const qt = ZigClangBinaryOperator_getType(stmt);
|
||||
var op_token: ast.TokenIndex = undefined;
|
||||
var op_id: ast.Node.InfixOp.Op = undefined;
|
||||
switch (op) {
|
||||
.PtrMemD, .PtrMemI, .Cmp => return revertAndWarn(
|
||||
rp,
|
||||
error.UnsupportedTranslation,
|
||||
ZigClangBinaryOperator_getBeginLoc(stmt),
|
||||
"TODO: handle more C binary operators: {}",
|
||||
.{op},
|
||||
),
|
||||
.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, 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, 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, node);
|
||||
.Assign => return transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)),
|
||||
.Comma => {
|
||||
const block_scope = try scope.findBlockScope(rp.c);
|
||||
const expr = block_scope.base.parent == scope;
|
||||
const lparen = if (expr) blk: {
|
||||
const l = try appendToken(rp.c, .LParen, "(");
|
||||
block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label);
|
||||
break :blk l;
|
||||
} else undefined;
|
||||
|
||||
const lhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getLHS(stmt), .unused, .r_value);
|
||||
try block_scope.block_node.statements.push(lhs);
|
||||
|
||||
const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
|
||||
if (expr) {
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
const break_node = try transCreateNodeBreak(rp.c, block_scope.label);
|
||||
break_node.rhs = rhs;
|
||||
try block_scope.block_node.statements.push(&break_node.base);
|
||||
block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
const rparen = try appendToken(rp.c, .RParen, ")");
|
||||
const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
|
||||
grouped_expr.* = .{
|
||||
.lparen = lparen,
|
||||
.expr = &block_scope.block_node.base,
|
||||
.rparen = rparen,
|
||||
};
|
||||
return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base);
|
||||
} else {
|
||||
return maybeSuppressResult(rp, scope, result_used, rhs);
|
||||
}
|
||||
},
|
||||
.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);
|
||||
try div_trunc_node.params.push(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value));
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
|
||||
try div_trunc_node.params.push(rhs);
|
||||
div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
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, node);
|
||||
}
|
||||
},
|
||||
.Rem => {
|
||||
if (!cIsUnsignedInteger(qt)) {
|
||||
// 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);
|
||||
try rem_node.params.push(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value));
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
|
||||
try rem_node.params.push(rhs);
|
||||
rem_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
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, node);
|
||||
}
|
||||
},
|
||||
.Shl,
|
||||
.Shr,
|
||||
.LT,
|
||||
.GT,
|
||||
.LE,
|
||||
.GE,
|
||||
.EQ,
|
||||
.NE,
|
||||
.And,
|
||||
.Xor,
|
||||
.Or,
|
||||
.LAnd,
|
||||
.LOr,
|
||||
.Comma,
|
||||
=> return revertAndWarn(
|
||||
rp,
|
||||
error.UnsupportedTranslation,
|
||||
ZigClangBinaryOperator_getBeginLoc(stmt),
|
||||
"TODO: handle more C binary operators: {}",
|
||||
.{op},
|
||||
),
|
||||
.Shl => {
|
||||
const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<");
|
||||
return maybeSuppressResult(rp, scope, result_used, node);
|
||||
},
|
||||
.Shr => {
|
||||
const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>");
|
||||
return maybeSuppressResult(rp, scope, result_used, node);
|
||||
},
|
||||
.LAnd => {
|
||||
const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, result_used, true);
|
||||
return maybeSuppressResult(rp, scope, result_used, node);
|
||||
},
|
||||
.LOr => {
|
||||
const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, result_used, true);
|
||||
return maybeSuppressResult(rp, scope, result_used, node);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
const lhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
|
||||
switch (op) {
|
||||
.Add => {
|
||||
if (cIsUnsignedInteger(qt)) {
|
||||
op_token = try appendToken(rp.c, .PlusPercent, "+%");
|
||||
op_id = .AddWrap;
|
||||
} else {
|
||||
op_token = try appendToken(rp.c, .Plus, "+");
|
||||
op_id = .Add;
|
||||
}
|
||||
},
|
||||
.Sub => {
|
||||
if (cIsUnsignedInteger(qt)) {
|
||||
op_token = try appendToken(rp.c, .MinusPercent, "-%");
|
||||
op_id = .SubWrap;
|
||||
} else {
|
||||
op_token = try appendToken(rp.c, .Minus, "-");
|
||||
op_id = .Sub;
|
||||
}
|
||||
},
|
||||
.Mul => {
|
||||
if (cIsUnsignedInteger(qt)) {
|
||||
op_token = try appendToken(rp.c, .AsteriskPercent, "*%");
|
||||
op_id = .MulWrap;
|
||||
} else {
|
||||
op_token = try appendToken(rp.c, .Asterisk, "*");
|
||||
op_id = .Mul;
|
||||
}
|
||||
},
|
||||
.Div => {
|
||||
// unsigned/float division uses the operator
|
||||
op_id = .Div;
|
||||
op_token = try appendToken(rp.c, .Slash, "/");
|
||||
},
|
||||
.Rem => {
|
||||
// unsigned/float division uses the operator
|
||||
op_id = .Mod;
|
||||
op_token = try appendToken(rp.c, .Percent, "%");
|
||||
},
|
||||
.LT => {
|
||||
op_id = .LessThan;
|
||||
op_token = try appendToken(rp.c, .AngleBracketLeft, "<");
|
||||
},
|
||||
.GT => {
|
||||
op_id = .GreaterThan;
|
||||
op_token = try appendToken(rp.c, .AngleBracketRight, ">");
|
||||
},
|
||||
.LE => {
|
||||
op_id = .LessOrEqual;
|
||||
op_token = try appendToken(rp.c, .AngleBracketLeftEqual, "<=");
|
||||
},
|
||||
.GE => {
|
||||
op_id = .GreaterOrEqual;
|
||||
op_token = try appendToken(rp.c, .AngleBracketRightEqual, ">=");
|
||||
},
|
||||
.EQ => {
|
||||
op_id = .EqualEqual;
|
||||
op_token = try appendToken(rp.c, .EqualEqual, "==");
|
||||
},
|
||||
.NE => {
|
||||
op_id = .BangEqual;
|
||||
op_token = try appendToken(rp.c, .BangEqual, "!=");
|
||||
},
|
||||
.And => {
|
||||
op_id = .BitAnd;
|
||||
op_token = try appendToken(rp.c, .Ampersand, "&");
|
||||
},
|
||||
.Xor => {
|
||||
op_id = .BitXor;
|
||||
op_token = try appendToken(rp.c, .Caret, "^");
|
||||
},
|
||||
.Or => {
|
||||
op_id = .BitOr;
|
||||
op_token = try appendToken(rp.c, .Pipe, "|");
|
||||
},
|
||||
.Assign,
|
||||
.MulAssign,
|
||||
.DivAssign,
|
||||
.RemAssign,
|
||||
@@ -772,6 +1062,9 @@ fn transBinaryOperator(
|
||||
=> unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
const rhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
|
||||
return transCreateNodeInfixOp(rp, scope, lhs_node, op_id, op_token, rhs_node, result_used, true);
|
||||
}
|
||||
|
||||
fn transCompoundStmtInline(
|
||||
@@ -790,11 +1083,11 @@ fn transCompoundStmtInline(
|
||||
}
|
||||
|
||||
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;
|
||||
const block_scope = try Scope.Block.init(rp.c, scope, null);
|
||||
block_scope.block_node = try transCreateNodeBlock(rp.c, null);
|
||||
try transCompoundStmtInline(rp, &block_scope.base, stmt, block_scope.block_node);
|
||||
block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
return &block_scope.block_node.base;
|
||||
}
|
||||
|
||||
fn transCStyleCastExprClass(
|
||||
@@ -818,7 +1111,7 @@ fn transCStyleCastExprClass(
|
||||
|
||||
fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) TransError!*ast.Node {
|
||||
const c = rp.c;
|
||||
const block_scope = scope.findBlockScope();
|
||||
const block_scope = scope.findBlockScope(c) catch unreachable;
|
||||
|
||||
var it = ZigClangDeclStmt_decl_begin(stmt);
|
||||
const end_it = ZigClangDeclStmt_decl_end(stmt);
|
||||
@@ -832,10 +1125,6 @@ fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt)
|
||||
else
|
||||
try appendToken(c, .Keyword_threadlocal, "threadlocal");
|
||||
const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl);
|
||||
const mut_token = if (ZigClangQualType_isConstQualified(qual_type))
|
||||
try appendToken(c, .Keyword_const, "const")
|
||||
else
|
||||
try appendToken(c, .Keyword_var, "var");
|
||||
const name = try c.str(ZigClangDecl_getName_bytes_begin(
|
||||
@ptrCast(*const ZigClangDecl, var_decl),
|
||||
));
|
||||
@@ -843,36 +1132,25 @@ fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt)
|
||||
try block_scope.variables.push(.{ .name = name, .alias = a });
|
||||
break :blk a;
|
||||
} else name;
|
||||
const name_token = try appendIdentifier(c, checked_name);
|
||||
const node = try transCreateNodeVarDecl(c, false, ZigClangQualType_isConstQualified(qual_type), checked_name);
|
||||
|
||||
const colon_token = try appendToken(c, .Colon, ":");
|
||||
_ = try appendToken(c, .Colon, ":");
|
||||
const loc = ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt));
|
||||
const type_node = try transQualType(rp, qual_type, loc);
|
||||
node.type_node = try transQualType(rp, qual_type, loc);
|
||||
|
||||
const eq_token = try appendToken(c, .Equal, "=");
|
||||
const init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
|
||||
node.eq_token = try appendToken(c, .Equal, "=");
|
||||
var init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
|
||||
try transExpr(rp, scope, expr, .used, .r_value)
|
||||
else
|
||||
try transCreateNodeUndefinedLiteral(c);
|
||||
const semicolon_token = try appendToken(c, .Semicolon, ";");
|
||||
|
||||
const node = try c.a().create(ast.Node.VarDecl);
|
||||
node.* = ast.Node.VarDecl{
|
||||
.doc_comments = null,
|
||||
.visib_token = null,
|
||||
.thread_local_token = thread_local_token,
|
||||
.name_token = name_token,
|
||||
.eq_token = eq_token,
|
||||
.mut_token = mut_token,
|
||||
.comptime_token = null,
|
||||
.extern_export_token = null,
|
||||
.lib_name = null,
|
||||
.type_node = type_node,
|
||||
.align_node = null, // TODO ?*Node,
|
||||
.section_node = null,
|
||||
.init_node = init_node,
|
||||
.semicolon_token = semicolon_token,
|
||||
};
|
||||
if (isBoolRes(init_node)) {
|
||||
const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt");
|
||||
try builtin_node.params.push(init_node);
|
||||
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
init_node = &builtin_node.base;
|
||||
}
|
||||
node.init_node = init_node;
|
||||
node.semicolon_token = try appendToken(c, .Semicolon, ";");
|
||||
try block_scope.block_node.statements.push(&node.base);
|
||||
},
|
||||
else => |kind| return revertAndWarn(
|
||||
@@ -896,7 +1174,6 @@ fn transDeclRefExpr(
|
||||
const value_decl = ZigClangDeclRefExpr_getDecl(expr);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -909,25 +1186,58 @@ fn transImplicitCastExpr(
|
||||
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);
|
||||
const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr));
|
||||
const src_type = getExprQualType(c, sub_expr);
|
||||
switch (ZigClangImplicitCastExpr_getCastKind(expr)) {
|
||||
.BitCast => {
|
||||
const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr));
|
||||
const src_type = getExprQualType(c, sub_expr);
|
||||
.BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast => {
|
||||
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 transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node);
|
||||
},
|
||||
.FunctionToPointerDecay, .ArrayToPointerDecay => {
|
||||
.LValueToRValue, .NoOp, .FunctionToPointerDecay, .ArrayToPointerDecay => {
|
||||
return maybeSuppressResult(rp, scope, result_used, sub_expr_node);
|
||||
},
|
||||
.LValueToRValue, .NoOp => {
|
||||
return transExpr(rp, scope, sub_expr, .used, .r_value);
|
||||
},
|
||||
.NullToPointer => {
|
||||
return transCreateNodeNullLiteral(rp.c);
|
||||
return try transCreateNodeNullLiteral(rp.c);
|
||||
},
|
||||
.PointerToBoolean => {
|
||||
// @ptrToInt(val) != 0
|
||||
const ptr_to_int = try transCreateNodeBuiltinFnCall(rp.c, "@ptrToInt");
|
||||
try ptr_to_int.params.push(try transExpr(rp, scope, sub_expr, .used, .r_value));
|
||||
ptr_to_int.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
|
||||
const op_token = try appendToken(rp.c, .BangEqual, "!=");
|
||||
const rhs_node = try transCreateNodeInt(rp.c, 0);
|
||||
return transCreateNodeInfixOp(rp, scope, &ptr_to_int.base, .BangEqual, op_token, rhs_node, result_used, false);
|
||||
},
|
||||
.IntegralToBoolean => {
|
||||
// val != 0
|
||||
const node = try transExpr(rp, scope, sub_expr, .used, .r_value);
|
||||
|
||||
const op_token = try appendToken(rp.c, .BangEqual, "!=");
|
||||
const rhs_node = try transCreateNodeInt(rp.c, 0);
|
||||
return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, result_used, false);
|
||||
},
|
||||
.PointerToIntegral => {
|
||||
// @intCast(dest_type, @ptrToInt(val))
|
||||
const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast");
|
||||
try cast_node.params.push(try transQualType(rp, dest_type, ZigClangImplicitCastExpr_getBeginLoc(expr)));
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
|
||||
const ptr_to_int = try transCreateNodeBuiltinFnCall(rp.c, "@ptrToInt");
|
||||
try ptr_to_int.params.push(try transExpr(rp, scope, sub_expr, .used, .r_value));
|
||||
ptr_to_int.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
try cast_node.params.push(&ptr_to_int.base);
|
||||
cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
return maybeSuppressResult(rp, scope, result_used, &cast_node.base);
|
||||
},
|
||||
.IntegralToPointer => {
|
||||
// @intToPtr(dest_type, val)
|
||||
const int_to_ptr = try transCreateNodeBuiltinFnCall(rp.c, "@intToPtr");
|
||||
try int_to_ptr.params.push(try transQualType(rp, dest_type, ZigClangImplicitCastExpr_getBeginLoc(expr)));
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
|
||||
try int_to_ptr.params.push(try transExpr(rp, scope, sub_expr, .used, .r_value));
|
||||
int_to_ptr.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
return maybeSuppressResult(rp, scope, result_used, &int_to_ptr.base);
|
||||
},
|
||||
else => |kind| return revertAndWarn(
|
||||
rp,
|
||||
@@ -939,6 +1249,151 @@ fn transImplicitCastExpr(
|
||||
}
|
||||
}
|
||||
|
||||
fn transBoolExpr(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
expr: *const ZigClangExpr,
|
||||
used: ResultUsed,
|
||||
lrvalue: LRValue,
|
||||
grouped: bool,
|
||||
) TransError!*ast.Node {
|
||||
const lparen = if (grouped)
|
||||
try appendToken(rp.c, .LParen, "(")
|
||||
else
|
||||
undefined;
|
||||
var res = try transExpr(rp, scope, expr, used, lrvalue);
|
||||
|
||||
if (isBoolRes(res)) {
|
||||
if (!grouped and res.id == .GroupedExpression) {
|
||||
const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res);
|
||||
res = group.expr;
|
||||
// get zig fmt to work properly
|
||||
tokenSlice(rp.c, group.lparen)[0] = ')';
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const ty = ZigClangQualType_getTypePtr(getExprQualTypeBeforeImplicitCast(rp.c, expr));
|
||||
const node = try finishBoolExpr(rp, scope, ZigClangExpr_getBeginLoc(expr), ty, res, used);
|
||||
|
||||
if (grouped) {
|
||||
const rparen = try appendToken(rp.c, .RParen, ")");
|
||||
const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
|
||||
grouped_expr.* = .{
|
||||
.lparen = lparen,
|
||||
.expr = node,
|
||||
.rparen = rparen,
|
||||
};
|
||||
return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
|
||||
} else {
|
||||
return maybeSuppressResult(rp, scope, used, node);
|
||||
}
|
||||
}
|
||||
|
||||
fn isBoolRes(res: *ast.Node) bool {
|
||||
switch (res.id) {
|
||||
.InfixOp => switch (@fieldParentPtr(ast.Node.InfixOp, "base", res).op) {
|
||||
.BoolOr,
|
||||
.BoolAnd,
|
||||
.EqualEqual,
|
||||
.BangEqual,
|
||||
.LessThan,
|
||||
.GreaterThan,
|
||||
.LessOrEqual,
|
||||
.GreaterOrEqual,
|
||||
=> return true,
|
||||
|
||||
else => {},
|
||||
},
|
||||
.PrefixOp => switch (@fieldParentPtr(ast.Node.PrefixOp, "base", res).op) {
|
||||
.BoolNot => return true,
|
||||
|
||||
else => {},
|
||||
},
|
||||
.BoolLiteral => return true,
|
||||
.GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),
|
||||
else => {},
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn finishBoolExpr(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
loc: ZigClangSourceLocation,
|
||||
ty: *const ZigClangType,
|
||||
node: *ast.Node,
|
||||
used: ResultUsed,
|
||||
) TransError!*ast.Node {
|
||||
switch (ZigClangType_getTypeClass(ty)) {
|
||||
.Builtin => {
|
||||
const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
|
||||
|
||||
switch (ZigClangBuiltinType_getKind(builtin_ty)) {
|
||||
.Bool => return node,
|
||||
.Char_U,
|
||||
.UChar,
|
||||
.Char_S,
|
||||
.SChar,
|
||||
.UShort,
|
||||
.UInt,
|
||||
.ULong,
|
||||
.ULongLong,
|
||||
.Short,
|
||||
.Int,
|
||||
.Long,
|
||||
.LongLong,
|
||||
.UInt128,
|
||||
.Int128,
|
||||
.Float,
|
||||
.Double,
|
||||
.Float128,
|
||||
.LongDouble,
|
||||
.WChar_U,
|
||||
.Char8,
|
||||
.Char16,
|
||||
.Char32,
|
||||
.WChar_S,
|
||||
.Float16,
|
||||
=> {
|
||||
const op_token = try appendToken(rp.c, .BangEqual, "!=");
|
||||
const rhs_node = try transCreateNodeInt(rp.c, 0);
|
||||
return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
|
||||
},
|
||||
.NullPtr => {
|
||||
const op_token = try appendToken(rp.c, .EqualEqual, "==");
|
||||
const rhs_node = try transCreateNodeNullLiteral(rp.c);
|
||||
return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, false);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
.Pointer => {
|
||||
const op_token = try appendToken(rp.c, .BangEqual, "!=");
|
||||
const rhs_node = try transCreateNodeNullLiteral(rp.c);
|
||||
return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
|
||||
},
|
||||
.Typedef => {
|
||||
const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
|
||||
const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
|
||||
const underlying_type = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
|
||||
return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(underlying_type), node, used);
|
||||
},
|
||||
.Enum => {
|
||||
const op_token = try appendToken(rp.c, .BangEqual, "!=");
|
||||
const rhs_node = try transCreateNodeInt(rp.c, 0);
|
||||
return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
|
||||
},
|
||||
.Elaborated => {
|
||||
const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty);
|
||||
const named_type = ZigClangElaboratedType_getNamedType(elaborated_ty);
|
||||
return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(named_type), node, used);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return revertAndWarn(rp, error.UnsupportedType, loc, "unsupported bool expression type", .{});
|
||||
}
|
||||
|
||||
fn transIntegerLiteral(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
@@ -991,7 +1446,7 @@ fn transStringLiteral(
|
||||
|
||||
const token = try appendToken(rp.c, .StringLiteral, buf);
|
||||
const node = try rp.c.a().create(ast.Node.StringLiteral);
|
||||
node.* = ast.Node.StringLiteral{
|
||||
node.* = .{
|
||||
.token = token,
|
||||
};
|
||||
return maybeSuppressResult(rp, scope, result_used, &node.base);
|
||||
@@ -1019,25 +1474,22 @@ fn writeEscapedString(buf: []u8, s: []const u8) void {
|
||||
var i: usize = 0;
|
||||
for (s) |c| {
|
||||
const escaped = escapeChar(c, &char_buf);
|
||||
std.mem.copy(u8, buf[i..], escaped);
|
||||
mem.copy(u8, buf[i..], escaped);
|
||||
i += escaped.len;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns either a string literal or a slice of `buf`.
|
||||
fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 {
|
||||
// TODO: https://github.com/ziglang/zig/issues/2749
|
||||
const escaped = switch (c) {
|
||||
// Printable ASCII except for ' " \
|
||||
' ', '!', '#'...'&', '('...'[', ']'...'~' => ([_]u8{c})[0..],
|
||||
'\'', '\"', '\\' => ([_]u8{ '\\', c })[0..],
|
||||
'\n' => return "\\n"[0..],
|
||||
'\r' => return "\\r"[0..],
|
||||
'\t' => return "\\t"[0..],
|
||||
else => return std.fmt.bufPrint(char_buf[0..], "\\x{x:2}", .{c}) catch unreachable,
|
||||
return switch (c) {
|
||||
'\"' => "\\\""[0..],
|
||||
'\'' => "\\'"[0..],
|
||||
'\\' => "\\\\"[0..],
|
||||
'\n' => "\\n"[0..],
|
||||
'\r' => "\\r"[0..],
|
||||
'\t' => "\\t"[0..],
|
||||
else => std.fmt.bufPrint(char_buf[0..], "{c}", .{c}) catch unreachable,
|
||||
};
|
||||
std.mem.copy(u8, char_buf, escaped);
|
||||
return char_buf[0..escaped.len];
|
||||
}
|
||||
|
||||
fn transCCast(
|
||||
@@ -1071,6 +1523,55 @@ fn transCCast(
|
||||
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
return &builtin_node.base;
|
||||
}
|
||||
if (cIsFloating(src_type) and cIsFloating(dst_type)) {
|
||||
const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@floatCast");
|
||||
try builtin_node.params.push(try transQualType(rp, dst_type, loc));
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
try builtin_node.params.push(expr);
|
||||
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
return &builtin_node.base;
|
||||
}
|
||||
if (cIsFloating(src_type) and !cIsFloating(dst_type)) {
|
||||
const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@floatToInt");
|
||||
try builtin_node.params.push(try transQualType(rp, dst_type, loc));
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
try builtin_node.params.push(expr);
|
||||
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
return &builtin_node.base;
|
||||
}
|
||||
if (!cIsFloating(src_type) and cIsFloating(dst_type)) {
|
||||
const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToFloat");
|
||||
try builtin_node.params.push(try transQualType(rp, dst_type, loc));
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
try builtin_node.params.push(expr);
|
||||
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
return &builtin_node.base;
|
||||
}
|
||||
if (ZigClangQualType_getTypeClass(src_type) == .Elaborated) {
|
||||
const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ZigClangQualType_getTypePtr(src_type));
|
||||
return transCCast(rp, scope, loc, dst_type, ZigClangElaboratedType_getNamedType(elaborated_ty), expr);
|
||||
}
|
||||
if (ZigClangQualType_getTypeClass(dst_type) == .Elaborated) {
|
||||
const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ZigClangQualType_getTypePtr(dst_type));
|
||||
return transCCast(rp, scope, loc, ZigClangElaboratedType_getNamedType(elaborated_ty), src_type, expr);
|
||||
}
|
||||
if (ZigClangQualType_getTypeClass(dst_type) == .Enum)
|
||||
{
|
||||
const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToEnum");
|
||||
try builtin_node.params.push(try transQualType(rp, dst_type, loc));
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
try builtin_node.params.push(expr);
|
||||
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
return &builtin_node.base;
|
||||
}
|
||||
if (ZigClangQualType_getTypeClass(src_type) == .Enum and
|
||||
ZigClangQualType_getTypeClass(dst_type) != .Enum)
|
||||
{
|
||||
const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@enumToInt");
|
||||
try builtin_node.params.push(expr);
|
||||
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
return &builtin_node.base;
|
||||
}
|
||||
// TODO: maybe widen to increase size
|
||||
// TODO: maybe bitcast to change sign
|
||||
// TODO: maybe truncate to reduce size
|
||||
@@ -1124,7 +1625,7 @@ fn transInitListExpr(
|
||||
var cat_tok: ast.TokenIndex = undefined;
|
||||
if (init_count != 0) {
|
||||
const dot_tok = try appendToken(rp.c, .Period, ".");
|
||||
init_node = try transCreateNodeArrayInitializer(rp.c, dot_tok);
|
||||
init_node = try transCreateNodeContainerInitializer(rp.c, dot_tok);
|
||||
var i: c_uint = 0;
|
||||
while (i < init_count) : (i += 1) {
|
||||
const elem_expr = ZigClangInitListExpr_getInit(expr, i);
|
||||
@@ -1139,7 +1640,7 @@ fn transInitListExpr(
|
||||
}
|
||||
|
||||
const dot_tok = try appendToken(rp.c, .Period, ".");
|
||||
var filler_init_node = try transCreateNodeArrayInitializer(rp.c, dot_tok);
|
||||
var filler_init_node = try transCreateNodeContainerInitializer(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));
|
||||
filler_init_node.rtoken = try appendToken(rp.c, .RBrace, "}");
|
||||
@@ -1185,7 +1686,7 @@ fn transImplicitValueInitExpr(
|
||||
.Builtin => blk: {
|
||||
const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
|
||||
switch (ZigClangBuiltinType_getKind(builtin_ty)) {
|
||||
.Bool => return transCreateNodeBoolLiteral(rp.c, false),
|
||||
.Bool => return try transCreateNodeBoolLiteral(rp.c, false),
|
||||
.Char_U,
|
||||
.UChar,
|
||||
.Char_S,
|
||||
@@ -1215,6 +1716,813 @@ fn transImplicitValueInitExpr(
|
||||
};
|
||||
}
|
||||
|
||||
fn transIfStmt(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangIfStmt,
|
||||
) TransError!*ast.Node {
|
||||
// if (c) t
|
||||
// if (c) t else e
|
||||
const if_node = try transCreateNodeIf(rp.c);
|
||||
|
||||
var cond_scope = Scope{
|
||||
.parent = scope,
|
||||
.id = .Condition,
|
||||
};
|
||||
if_node.condition = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value, false);
|
||||
_ = try appendToken(rp.c, .RParen, ")");
|
||||
|
||||
if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .unused, .r_value);
|
||||
|
||||
if (ZigClangIfStmt_getElse(stmt)) |expr| {
|
||||
if_node.@"else" = try transCreateNodeElse(rp.c);
|
||||
if_node.@"else".?.body = try transStmt(rp, scope, expr, .unused, .r_value);
|
||||
}
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
return &if_node.base;
|
||||
}
|
||||
|
||||
fn transWhileLoop(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangWhileStmt,
|
||||
) TransError!*ast.Node {
|
||||
const while_node = try transCreateNodeWhile(rp.c);
|
||||
|
||||
var cond_scope = Scope{
|
||||
.parent = scope,
|
||||
.id = .Condition,
|
||||
};
|
||||
while_node.condition = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt)), .used, .r_value, false);
|
||||
_ = try appendToken(rp.c, .RParen, ")");
|
||||
|
||||
var loop_scope = Scope{
|
||||
.parent = scope,
|
||||
.id = .Loop,
|
||||
};
|
||||
while_node.body = try transStmt(rp, &loop_scope, ZigClangWhileStmt_getBody(stmt), .unused, .r_value);
|
||||
return &while_node.base;
|
||||
}
|
||||
|
||||
fn transDoWhileLoop(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangDoStmt,
|
||||
) TransError!*ast.Node {
|
||||
const while_node = try transCreateNodeWhile(rp.c);
|
||||
|
||||
while_node.condition = try transCreateNodeBoolLiteral(rp.c, true);
|
||||
_ = try appendToken(rp.c, .RParen, ")");
|
||||
var new = false;
|
||||
var loop_scope = Scope{
|
||||
.parent = scope,
|
||||
.id = .Loop,
|
||||
};
|
||||
|
||||
const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: {
|
||||
// there's already a block in C, so we'll append our condition to it.
|
||||
// c: do {
|
||||
// c: a;
|
||||
// c: b;
|
||||
// c: } while(c);
|
||||
// zig: while (true) {
|
||||
// zig: a;
|
||||
// zig: b;
|
||||
// zig: if (!cond) break;
|
||||
// zig: }
|
||||
break :blk (try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?;
|
||||
} else blk: {
|
||||
// the C statement is without a block, so we need to create a block to contain it.
|
||||
// c: do
|
||||
// c: a;
|
||||
// c: while(c);
|
||||
// zig: while (true) {
|
||||
// zig: a;
|
||||
// zig: if (!cond) break;
|
||||
// zig: }
|
||||
new = true;
|
||||
const block = try transCreateNodeBlock(rp.c, null);
|
||||
try block.statements.push(try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value));
|
||||
break :blk block;
|
||||
};
|
||||
|
||||
// if (!cond) break;
|
||||
const if_node = try transCreateNodeIf(rp.c);
|
||||
var cond_scope = Scope{
|
||||
.parent = scope,
|
||||
.id = .Condition,
|
||||
};
|
||||
const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
|
||||
prefix_op.rhs = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
|
||||
_ = try appendToken(rp.c, .RParen, ")");
|
||||
if_node.condition = &prefix_op.base;
|
||||
if_node.body = &(try transCreateNodeBreak(rp.c, null)).base;
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
|
||||
try body_node.statements.push(&if_node.base);
|
||||
if (new)
|
||||
body_node.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
while_node.body = &body_node.base;
|
||||
return &while_node.base;
|
||||
}
|
||||
|
||||
fn transForLoop(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangForStmt,
|
||||
) TransError!*ast.Node {
|
||||
var loop_scope = Scope{
|
||||
.parent = scope,
|
||||
.id = .Loop,
|
||||
};
|
||||
var block = false;
|
||||
var block_scope: ?*Scope.Block = null;
|
||||
if (ZigClangForStmt_getInit(stmt)) |init| {
|
||||
block_scope = try Scope.Block.init(rp.c, scope, null);
|
||||
block_scope.?.block_node = try transCreateNodeBlock(rp.c, null);
|
||||
loop_scope.parent = &block_scope.?.base;
|
||||
_ = try transStmt(rp, &loop_scope, init, .unused, .r_value);
|
||||
}
|
||||
var cond_scope = Scope{
|
||||
.parent = scope,
|
||||
.id = .Condition,
|
||||
};
|
||||
|
||||
const while_node = try transCreateNodeWhile(rp.c);
|
||||
while_node.condition = if (ZigClangForStmt_getCond(stmt)) |cond|
|
||||
try transBoolExpr(rp, &cond_scope, cond, .used, .r_value, false)
|
||||
else
|
||||
try transCreateNodeBoolLiteral(rp.c, true);
|
||||
_ = try appendToken(rp.c, .RParen, ")");
|
||||
|
||||
if (ZigClangForStmt_getInc(stmt)) |incr| {
|
||||
_ = try appendToken(rp.c, .Colon, ":");
|
||||
_ = try appendToken(rp.c, .LParen, "(");
|
||||
while_node.continue_expr = try transExpr(rp, &cond_scope, incr, .unused, .r_value);
|
||||
_ = try appendToken(rp.c, .RParen, ")");
|
||||
}
|
||||
|
||||
while_node.body = try transStmt(rp, &loop_scope, ZigClangForStmt_getBody(stmt), .unused, .r_value);
|
||||
if (block_scope != null) {
|
||||
try block_scope.?.block_node.statements.push(&while_node.base);
|
||||
block_scope.?.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
return &block_scope.?.block_node.base;
|
||||
} else
|
||||
return &while_node.base;
|
||||
}
|
||||
|
||||
fn transSwitch(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangSwitchStmt,
|
||||
) TransError!*ast.Node {
|
||||
const switch_node = try transCreateNodeSwitch(rp.c);
|
||||
var switch_scope = Scope.Switch{
|
||||
.base = .{
|
||||
.id = .Switch,
|
||||
.parent = scope,
|
||||
},
|
||||
.cases = &switch_node.cases,
|
||||
.pending_block = undefined,
|
||||
};
|
||||
|
||||
var cond_scope = Scope{
|
||||
.parent = scope,
|
||||
.id = .Condition,
|
||||
};
|
||||
switch_node.expr = try transExpr(rp, &cond_scope, ZigClangSwitchStmt_getCond(stmt), .used, .r_value);
|
||||
_ = try appendToken(rp.c, .RParen, ")");
|
||||
_ = try appendToken(rp.c, .LBrace, "{");
|
||||
switch_node.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
|
||||
const block_scope = try Scope.Block.init(rp.c, &switch_scope.base, null);
|
||||
// tmp block that all statements will go before being picked up by a case or default
|
||||
const block = try transCreateNodeBlock(rp.c, null);
|
||||
block_scope.block_node = block;
|
||||
|
||||
const switch_block = try transCreateNodeBlock(rp.c, null);
|
||||
try switch_block.statements.push(&switch_node.base);
|
||||
switch_scope.pending_block = switch_block;
|
||||
|
||||
const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
|
||||
// take all pending statements
|
||||
var it = last.cast(ast.Node.Block).?.statements.iterator(0);
|
||||
while (it.next()) |n| {
|
||||
try switch_scope.pending_block.statements.push(n.*);
|
||||
}
|
||||
|
||||
switch_scope.pending_block.label = try appendIdentifier(rp.c, "__switch");
|
||||
_ = try appendToken(rp.c, .Colon, ":");
|
||||
if (!switch_scope.has_default) {
|
||||
const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
|
||||
else_prong.expr = &(try transCreateNodeBreak(rp.c, "__switch")).base;
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
try switch_node.cases.push(&else_prong.base);
|
||||
}
|
||||
switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
return &switch_scope.pending_block.base;
|
||||
}
|
||||
|
||||
fn transCase(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangCaseStmt,
|
||||
) TransError!*ast.Node {
|
||||
const block_scope = scope.findBlockScope(rp.c) catch unreachable;
|
||||
const switch_scope = scope.getSwitch();
|
||||
const label = try std.fmt.allocPrint(rp.c.a(), "__case_{}", .{switch_scope.cases.len - @boolToInt(switch_scope.has_default)});
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
|
||||
const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: {
|
||||
const lhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
|
||||
const ellips = try appendToken(rp.c, .Ellipsis3, "...");
|
||||
const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
|
||||
|
||||
const node = try rp.c.a().create(ast.Node.InfixOp);
|
||||
node.* = .{
|
||||
.op_token = ellips,
|
||||
.lhs = lhs_node,
|
||||
.op = .Range,
|
||||
.rhs = rhs_node,
|
||||
};
|
||||
break :blk &node.base;
|
||||
} else
|
||||
try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
|
||||
|
||||
const switch_prong = try transCreateNodeSwitchCase(rp.c, expr);
|
||||
switch_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base;
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
try switch_scope.cases.push(&switch_prong.base);
|
||||
|
||||
const block = try transCreateNodeBlock(rp.c, null);
|
||||
switch_scope.pending_block.label = try appendIdentifier(rp.c, label);
|
||||
_ = try appendToken(rp.c, .Colon, ":");
|
||||
switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
try block.statements.push(&switch_scope.pending_block.base);
|
||||
|
||||
// take all pending statements
|
||||
var it = block_scope.block_node.statements.iterator(0);
|
||||
while (it.next()) |n| {
|
||||
try switch_scope.pending_block.statements.push(n.*);
|
||||
}
|
||||
block_scope.block_node.statements.shrink(0);
|
||||
|
||||
switch_scope.pending_block = block;
|
||||
|
||||
return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value);
|
||||
}
|
||||
|
||||
fn transDefault(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangDefaultStmt,
|
||||
) TransError!*ast.Node {
|
||||
const block_scope = scope.findBlockScope(rp.c) catch unreachable;
|
||||
const switch_scope = scope.getSwitch();
|
||||
const label = "__default";
|
||||
switch_scope.has_default = true;
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
|
||||
const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
|
||||
else_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base;
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
try switch_scope.cases.push(&else_prong.base);
|
||||
|
||||
const block = try transCreateNodeBlock(rp.c, null);
|
||||
switch_scope.pending_block.label = try appendIdentifier(rp.c, label);
|
||||
_ = try appendToken(rp.c, .Colon, ":");
|
||||
switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
try block.statements.push(&switch_scope.pending_block.base);
|
||||
|
||||
// take all pending statements
|
||||
var it = block_scope.block_node.statements.iterator(0);
|
||||
while (it.next()) |n| {
|
||||
try switch_scope.pending_block.statements.push(n.*);
|
||||
}
|
||||
block_scope.block_node.statements.shrink(0);
|
||||
|
||||
switch_scope.pending_block = block;
|
||||
return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value);
|
||||
}
|
||||
|
||||
fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangExpr, used: ResultUsed) TransError!*ast.Node {
|
||||
var result: ZigClangExprEvalResult = undefined;
|
||||
if (!ZigClangExpr_EvaluateAsConstantExpr(expr, &result, .EvaluateForCodeGen, rp.c.clang_context))
|
||||
return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "invalid constant expression", .{});
|
||||
return maybeSuppressResult(rp, scope, used, try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&result.Val)));
|
||||
}
|
||||
|
||||
fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangPredefinedExpr, used: ResultUsed) TransError!*ast.Node {
|
||||
return transStringLiteral(rp, scope, ZigClangPredefinedExpr_getFunctionName(expr), used);
|
||||
}
|
||||
|
||||
fn transCharLiteral(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangCharacterLiteral,
|
||||
result_used: ResultUsed,
|
||||
) TransError!*ast.Node {
|
||||
const kind = ZigClangCharacterLiteral_getKind(stmt);
|
||||
switch (kind) {
|
||||
.Ascii, .UTF8 => {
|
||||
const val = ZigClangCharacterLiteral_getValue(stmt);
|
||||
if (kind == .Ascii) {
|
||||
// C has a somewhat obscure feature called multi-character character
|
||||
// constant
|
||||
if (val > 255)
|
||||
return transCreateNodeInt(rp.c, val);
|
||||
}
|
||||
var char_buf: [4]u8 = undefined;
|
||||
const token = try appendTokenFmt(rp.c, .CharLiteral, "'{}'", .{escapeChar(@intCast(u8, val), &char_buf)});
|
||||
const node = try rp.c.a().create(ast.Node.CharLiteral);
|
||||
node.* = .{
|
||||
.token = token,
|
||||
};
|
||||
return maybeSuppressResult(rp, scope, result_used, &node.base);
|
||||
},
|
||||
.UTF16, .UTF32, .Wide => return revertAndWarn(
|
||||
rp,
|
||||
error.UnsupportedTranslation,
|
||||
ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)),
|
||||
"TODO: support character literal kind {}",
|
||||
.{kind},
|
||||
),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr, used: ResultUsed) TransError!*ast.Node {
|
||||
const comp = ZigClangStmtExpr_getSubStmt(stmt);
|
||||
if (used == .unused) {
|
||||
return transCompoundStmt(rp, scope, comp);
|
||||
}
|
||||
const lparen = try appendToken(rp.c, .LParen, "(");
|
||||
const block_scope = try Scope.Block.init(rp.c, scope, "blk");
|
||||
const block = try transCreateNodeBlock(rp.c, "blk");
|
||||
block_scope.block_node = block;
|
||||
|
||||
var it = ZigClangCompoundStmt_body_begin(comp);
|
||||
const end_it = ZigClangCompoundStmt_body_end(comp);
|
||||
while (it != end_it - 1) : (it += 1) {
|
||||
const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value);
|
||||
if (result != &block.base)
|
||||
try block.statements.push(result);
|
||||
}
|
||||
const break_node = try transCreateNodeBreak(rp.c, "blk");
|
||||
break_node.rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
try block.statements.push(&break_node.base);
|
||||
block.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
const rparen = try appendToken(rp.c, .RParen, ")");
|
||||
const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
|
||||
grouped_expr.* = .{
|
||||
.lparen = lparen,
|
||||
.expr = &block.base,
|
||||
.rparen = rparen,
|
||||
};
|
||||
return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
|
||||
}
|
||||
|
||||
fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberExpr, result_used: ResultUsed) TransError!*ast.Node {
|
||||
var container_node = try transExpr(rp, scope, ZigClangMemberExpr_getBase(stmt), .used, .r_value);
|
||||
|
||||
if (ZigClangMemberExpr_isArrow(stmt)) {
|
||||
container_node = try transCreateNodePtrDeref(rp.c, container_node);
|
||||
}
|
||||
|
||||
const name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, ZigClangMemberExpr_getMemberDecl(stmt))));
|
||||
const node = try transCreateNodeFieldAccess(rp.c, container_node, name);
|
||||
return maybeSuppressResult(rp, scope, result_used, node);
|
||||
}
|
||||
|
||||
fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangArraySubscriptExpr, result_used: ResultUsed) TransError!*ast.Node {
|
||||
const container_node = try transExpr(rp, scope, ZigClangArraySubscriptExpr_getBase(stmt), .used, .r_value);
|
||||
const node = try transCreateNodeArrayAccess(rp.c, container_node);
|
||||
node.op.ArrayAccess = try transExpr(rp, scope, ZigClangArraySubscriptExpr_getIdx(stmt), .used, .r_value);
|
||||
node.rtoken = try appendToken(rp.c, .RBrace, "]");
|
||||
return maybeSuppressResult(rp, scope, result_used, &node.base);
|
||||
}
|
||||
|
||||
fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCallExpr, result_used: ResultUsed) TransError!*ast.Node {
|
||||
const callee = ZigClangCallExpr_getCallee(stmt);
|
||||
var raw_fn_expr = try transExpr(rp, scope, callee, .used, .r_value);
|
||||
|
||||
var is_ptr = false;
|
||||
const fn_ty = qualTypeGetFnProto(ZigClangExpr_getType(callee), &is_ptr);
|
||||
|
||||
const fn_expr = if (is_ptr and fn_ty != null) blk: {
|
||||
if (ZigClangExpr_getStmtClass(callee) == .ImplicitCastExprClass) {
|
||||
const implicit_cast = @ptrCast(*const ZigClangImplicitCastExpr, callee);
|
||||
|
||||
if (ZigClangImplicitCastExpr_getCastKind(implicit_cast) == .FunctionToPointerDecay) {
|
||||
const subexpr = ZigClangImplicitCastExpr_getSubExpr(implicit_cast);
|
||||
if (ZigClangExpr_getStmtClass(subexpr) == .DeclRefExprClass) {
|
||||
const decl_ref = @ptrCast(*const ZigClangDeclRefExpr, subexpr);
|
||||
const named_decl = ZigClangDeclRefExpr_getFoundDecl(decl_ref);
|
||||
if (ZigClangDecl_getKind(@ptrCast(*const ZigClangDecl, named_decl)) == .Function) {
|
||||
break :blk raw_fn_expr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break :blk try transCreateNodeUnwrapNull(rp.c, raw_fn_expr);
|
||||
} else
|
||||
raw_fn_expr;
|
||||
const node = try transCreateNodeFnCall(rp.c, fn_expr);
|
||||
|
||||
const num_args = ZigClangCallExpr_getNumArgs(stmt);
|
||||
const args = ZigClangCallExpr_getArgs(stmt);
|
||||
var i: usize = 0;
|
||||
while (i < num_args) : (i += 1) {
|
||||
if (i != 0) {
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
}
|
||||
const arg = try transExpr(rp, scope, args[i], .used, .r_value);
|
||||
try node.op.Call.params.push(arg);
|
||||
}
|
||||
node.rtoken = try appendToken(rp.c, .RParen, ")");
|
||||
|
||||
if (fn_ty) |ty| {
|
||||
const canon = ZigClangQualType_getCanonicalType(ZigClangFunctionProtoType_getReturnType(ty));
|
||||
const ret_ty = ZigClangQualType_getTypePtr(canon);
|
||||
if (ZigClangType_isVoidType(ret_ty)) {
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
return &node.base;
|
||||
}
|
||||
}
|
||||
|
||||
return maybeSuppressResult(rp, scope, result_used, &node.base);
|
||||
}
|
||||
|
||||
fn qualTypeGetFnProto(qt: ZigClangQualType, is_ptr: *bool) ?*const ZigClangFunctionProtoType {
|
||||
const canon = ZigClangQualType_getCanonicalType(qt);
|
||||
var ty = ZigClangQualType_getTypePtr(canon);
|
||||
is_ptr.* = false;
|
||||
|
||||
if (ZigClangType_getTypeClass(ty) == .Pointer) {
|
||||
is_ptr.* = true;
|
||||
const child_qt = ZigClangType_getPointeeType(ty);
|
||||
ty = ZigClangQualType_getTypePtr(child_qt);
|
||||
}
|
||||
if (ZigClangType_getTypeClass(ty) == .FunctionProto) {
|
||||
return @ptrCast(*const ZigClangFunctionProtoType, ty);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn transUnaryExprOrTypeTraitExpr(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangUnaryExprOrTypeTraitExpr,
|
||||
result_used: ResultUsed,
|
||||
) TransError!*ast.Node {
|
||||
const type_node = try transQualType(
|
||||
rp,
|
||||
ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(stmt),
|
||||
ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt),
|
||||
);
|
||||
|
||||
const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@sizeOf");
|
||||
try builtin_node.params.push(type_node);
|
||||
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
return maybeSuppressResult(rp, scope, result_used, &builtin_node.base);
|
||||
}
|
||||
|
||||
fn qualTypeHaswrappingOverflow(qt: ZigClangQualType) bool {
|
||||
if (cIsSignedInteger(qt) or cIsFloating(qt)) {
|
||||
// float and signed integer overflow is undefined behavior.
|
||||
return false;
|
||||
} else {
|
||||
// unsigned integer overflow wraps around.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnaryOperator, used: ResultUsed) TransError!*ast.Node {
|
||||
const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
|
||||
switch (ZigClangUnaryOperator_getOpcode(stmt)) {
|
||||
.PostInc => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
|
||||
return transCreatePostCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)
|
||||
else
|
||||
return transCreatePostCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),
|
||||
.PostDec => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
|
||||
return transCreatePostCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)
|
||||
else
|
||||
return transCreatePostCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
|
||||
.PreInc => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
|
||||
return transCreatePreCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)
|
||||
else
|
||||
return transCreatePreCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),
|
||||
.PreDec => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
|
||||
return transCreatePreCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)
|
||||
else
|
||||
return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
|
||||
.AddrOf => {
|
||||
const op_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
|
||||
op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);
|
||||
return &op_node.base;
|
||||
},
|
||||
.Deref => {
|
||||
const value_node = try transExpr(rp, scope, op_expr, used, .r_value);
|
||||
var is_ptr = false;
|
||||
const fn_ty = qualTypeGetFnProto(ZigClangExpr_getType(op_expr), &is_ptr);
|
||||
if (fn_ty != null and is_ptr)
|
||||
return value_node;
|
||||
const unwrapped = try transCreateNodeUnwrapNull(rp.c, value_node);
|
||||
return transCreateNodePtrDeref(rp.c, unwrapped);
|
||||
},
|
||||
.Plus => return transExpr(rp, scope, op_expr, used, .r_value),
|
||||
.Minus => {
|
||||
if (!qualTypeHaswrappingOverflow(ZigClangExpr_getType(op_expr))) {
|
||||
const op_node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-");
|
||||
op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
|
||||
return &op_node.base;
|
||||
} else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) {
|
||||
// we gotta emit 0 -% x
|
||||
const zero = try transCreateNodeInt(rp.c, 0);
|
||||
const token = try appendToken(rp.c, .MinusPercent, "-%");
|
||||
const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
|
||||
return transCreateNodeInfixOp(rp, scope, zero, .SubWrap, token, expr, used, true);
|
||||
} else
|
||||
return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{});
|
||||
},
|
||||
.Not => {
|
||||
const op_node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~");
|
||||
op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
|
||||
return &op_node.base;
|
||||
},
|
||||
.LNot => {
|
||||
const op_node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
|
||||
op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);
|
||||
return &op_node.base;
|
||||
},
|
||||
.Extension => {
|
||||
return transExpr(rp, scope, ZigClangUnaryOperator_getSubExpr(stmt), used, .l_value);
|
||||
},
|
||||
else => return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "unsupported C translation {}", .{ZigClangUnaryOperator_getOpcode(stmt)}),
|
||||
}
|
||||
}
|
||||
|
||||
fn transCreatePreCrement(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangUnaryOperator,
|
||||
op: ast.Node.InfixOp.Op,
|
||||
op_tok_id: std.zig.Token.Id,
|
||||
bytes: []const u8,
|
||||
used: ResultUsed,
|
||||
) TransError!*ast.Node {
|
||||
const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
|
||||
|
||||
if (used == .unused) {
|
||||
// common case
|
||||
// c: ++expr
|
||||
// zig: expr += 1
|
||||
const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
|
||||
const token = try appendToken(rp.c, op_tok_id, bytes);
|
||||
const one = try transCreateNodeInt(rp.c, 1);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
|
||||
}
|
||||
// worst case
|
||||
// c: ++expr
|
||||
// zig: (blk: {
|
||||
// zig: const _ref = &expr;
|
||||
// zig: _ref.* += 1;
|
||||
// zig: break :blk _ref.*
|
||||
// zig: })
|
||||
const block_scope = try Scope.Block.init(rp.c, scope, "blk");
|
||||
block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label);
|
||||
const ref = try std.fmt.allocPrint(rp.c.a(), "_ref_{}", .{rp.c.getMangle()});
|
||||
|
||||
const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
|
||||
node.eq_token = try appendToken(rp.c, .Equal, "=");
|
||||
const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
|
||||
rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
|
||||
node.init_node = &rhs_node.base;
|
||||
node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
|
||||
try block_scope.block_node.statements.push(&node.base);
|
||||
|
||||
const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
|
||||
const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
const token = try appendToken(rp.c, op_tok_id, bytes);
|
||||
const one = try transCreateNodeInt(rp.c, 1);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
|
||||
try block_scope.block_node.statements.push(assign);
|
||||
|
||||
const break_node = try transCreateNodeBreak(rp.c, block_scope.label);
|
||||
break_node.rhs = ref_node;
|
||||
try block_scope.block_node.statements.push(&break_node.base);
|
||||
block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
// semicolon must immediately follow rbrace because it is the last token in a block
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
|
||||
grouped_expr.* = .{
|
||||
.lparen = try appendToken(rp.c, .LParen, "("),
|
||||
.expr = &block_scope.block_node.base,
|
||||
.rparen = try appendToken(rp.c, .RParen, ")"),
|
||||
};
|
||||
return &grouped_expr.base;
|
||||
}
|
||||
|
||||
fn transCreatePostCrement(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangUnaryOperator,
|
||||
op: ast.Node.InfixOp.Op,
|
||||
op_tok_id: std.zig.Token.Id,
|
||||
bytes: []const u8,
|
||||
used: ResultUsed,
|
||||
) TransError!*ast.Node {
|
||||
const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
|
||||
|
||||
if (used == .unused) {
|
||||
// common case
|
||||
// c: ++expr
|
||||
// zig: expr += 1
|
||||
const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
|
||||
const token = try appendToken(rp.c, op_tok_id, bytes);
|
||||
const one = try transCreateNodeInt(rp.c, 1);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
|
||||
}
|
||||
// worst case
|
||||
// c: expr++
|
||||
// zig: (blk: {
|
||||
// zig: const _ref = &expr;
|
||||
// zig: const _tmp = _ref.*;
|
||||
// zig: _ref.* += 1;
|
||||
// zig: break :blk _tmp
|
||||
// zig: })
|
||||
const block_scope = try Scope.Block.init(rp.c, scope, "blk");
|
||||
block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label);
|
||||
const ref = try std.fmt.allocPrint(rp.c.a(), "_ref_{}", .{rp.c.getMangle()});
|
||||
|
||||
const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
|
||||
node.eq_token = try appendToken(rp.c, .Equal, "=");
|
||||
const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
|
||||
rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
|
||||
node.init_node = &rhs_node.base;
|
||||
node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
|
||||
try block_scope.block_node.statements.push(&node.base);
|
||||
|
||||
const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
|
||||
const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
|
||||
const tmp = try std.fmt.allocPrint(rp.c.a(), "_tmp_{}", .{rp.c.getMangle()});
|
||||
const tmp_node = try transCreateNodeVarDecl(rp.c, false, true, tmp);
|
||||
tmp_node.eq_token = try appendToken(rp.c, .Equal, "=");
|
||||
tmp_node.init_node = ref_node;
|
||||
tmp_node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
|
||||
try block_scope.block_node.statements.push(&tmp_node.base);
|
||||
|
||||
const token = try appendToken(rp.c, op_tok_id, bytes);
|
||||
const one = try transCreateNodeInt(rp.c, 1);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
|
||||
try block_scope.block_node.statements.push(assign);
|
||||
|
||||
const break_node = try transCreateNodeBreak(rp.c, block_scope.label);
|
||||
break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp);
|
||||
try block_scope.block_node.statements.push(&break_node.base);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
|
||||
grouped_expr.* = .{
|
||||
.lparen = try appendToken(rp.c, .LParen, "("),
|
||||
.expr = &block_scope.block_node.base,
|
||||
.rparen = try appendToken(rp.c, .RParen, ")"),
|
||||
};
|
||||
return &grouped_expr.base;
|
||||
}
|
||||
|
||||
fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundAssignOperator, used: ResultUsed) TransError!*ast.Node {
|
||||
switch (ZigClangCompoundAssignOperator_getOpcode(stmt)) {
|
||||
.MulAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt)))
|
||||
return transCreateCompoundAssign(rp, scope, stmt, .AssignMulWrap, .AsteriskPercentEqual, "*%=", .MulWrap, .AsteriskPercent, "*%", used)
|
||||
else
|
||||
return transCreateCompoundAssign(rp, scope, stmt, .AssignMul, .AsteriskEqual, "*=", .Mul, .Asterisk, "*", used),
|
||||
.AddAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt)))
|
||||
return transCreateCompoundAssign(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", .AddWrap, .PlusPercent, "+%", used)
|
||||
else
|
||||
return transCreateCompoundAssign(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", .Add, .Plus, "+", used),
|
||||
.SubAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt)))
|
||||
return transCreateCompoundAssign(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", .SubWrap, .MinusPercent, "-%", used)
|
||||
else
|
||||
return transCreateCompoundAssign(rp, scope, stmt, .AssignSub, .MinusPercentEqual, "-=", .Sub, .Minus, "-", used),
|
||||
.ShlAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftLeft, .AngleBracketAngleBracketLeftEqual, "<<=", .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<", used),
|
||||
.ShrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftRight, .AngleBracketAngleBracketRightEqual, ">>=", .BitShiftRight, .AngleBracketAngleBracketRight, ">>", used),
|
||||
.AndAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitAnd, .AmpersandEqual, "&=", .BitAnd, .Ampersand, "&", used),
|
||||
.XorAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitXor, .CaretEqual, "^=", .BitXor, .Caret, "^", used),
|
||||
.OrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitOr, .PipeEqual, "|=", .BitOr, .Pipe, "|", used),
|
||||
else => return revertAndWarn(
|
||||
rp,
|
||||
error.UnsupportedTranslation,
|
||||
ZigClangCompoundAssignOperator_getBeginLoc(stmt),
|
||||
"unsupported C translation {}",
|
||||
.{ZigClangCompoundAssignOperator_getOpcode(stmt)},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn transCreateCompoundAssign(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangCompoundAssignOperator,
|
||||
assign_op: ast.Node.InfixOp.Op,
|
||||
assign_tok_id: std.zig.Token.Id,
|
||||
assign_bytes: []const u8,
|
||||
bin_op: ast.Node.InfixOp.Op,
|
||||
bin_tok_id: std.zig.Token.Id,
|
||||
bin_bytes: []const u8,
|
||||
used: ResultUsed,
|
||||
) TransError!*ast.Node {
|
||||
const is_shift = bin_op == .BitShiftLeft or bin_op == .BitShiftRight;
|
||||
const lhs = ZigClangCompoundAssignOperator_getLHS(stmt);
|
||||
const rhs = ZigClangCompoundAssignOperator_getRHS(stmt);
|
||||
const loc = ZigClangCompoundAssignOperator_getBeginLoc(stmt);
|
||||
if (used == .unused) {
|
||||
// common case
|
||||
// c: lhs += rhs
|
||||
// zig: lhs += rhs
|
||||
const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
|
||||
const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes);
|
||||
var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
|
||||
|
||||
if (is_shift) {
|
||||
const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
|
||||
const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc);
|
||||
try as_node.params.push(rhs_type);
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
try as_node.params.push(rhs_node);
|
||||
as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
rhs_node = &as_node.base;
|
||||
}
|
||||
if (scope.id != .Condition)
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
return transCreateNodeInfixOp(rp, scope, lhs_node, assign_op, eq_token, rhs_node, .used, false);
|
||||
}
|
||||
// worst case
|
||||
// c: lhs += rhs
|
||||
// zig: (blk: {
|
||||
// zig: const _ref = &lhs;
|
||||
// zig: _ref.* = _ref.* + rhs;
|
||||
// zig: break :blk _ref.*
|
||||
// zig: })
|
||||
const block_scope = try Scope.Block.init(rp.c, scope, "blk");
|
||||
block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label);
|
||||
const ref = try std.fmt.allocPrint(rp.c.a(), "_ref_{}", .{rp.c.getMangle()});
|
||||
|
||||
const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
|
||||
node.eq_token = try appendToken(rp.c, .Equal, "=");
|
||||
const addr_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
|
||||
addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value);
|
||||
node.init_node = &addr_node.base;
|
||||
node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
|
||||
try block_scope.block_node.statements.push(&node.base);
|
||||
|
||||
const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
|
||||
const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes);
|
||||
var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
|
||||
if (is_shift) {
|
||||
const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
|
||||
const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc);
|
||||
try as_node.params.push(rhs_type);
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
try as_node.params.push(rhs_node);
|
||||
as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
rhs_node = &as_node.base;
|
||||
}
|
||||
const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false);
|
||||
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
|
||||
const eq_token = try appendToken(rp.c, .Equal, "=");
|
||||
const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, eq_token, rhs_bin, .used, false);
|
||||
try block_scope.block_node.statements.push(assign);
|
||||
|
||||
const break_node = try transCreateNodeBreak(rp.c, block_scope.label);
|
||||
break_node.rhs = ref_node;
|
||||
try block_scope.block_node.statements.push(&break_node.base);
|
||||
block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
// semicolon must immediately follow rbrace because it is the last token in a block
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
|
||||
grouped_expr.* = .{
|
||||
.lparen = try appendToken(rp.c, .LParen, "("),
|
||||
.expr = &block_scope.block_node.base,
|
||||
.rparen = try appendToken(rp.c, .RParen, ")"),
|
||||
};
|
||||
return &grouped_expr.base;
|
||||
}
|
||||
|
||||
fn transCPtrCast(
|
||||
rp: RestorePoint,
|
||||
loc: ZigClangSourceLocation,
|
||||
@@ -1252,6 +2560,61 @@ fn transCPtrCast(
|
||||
return &ptrcast_node.base;
|
||||
}
|
||||
|
||||
fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {
|
||||
const break_scope = scope.getBreakableScope();
|
||||
const br = try transCreateNodeBreak(rp.c, if (break_scope.id == .Switch)
|
||||
"__switch"
|
||||
else
|
||||
null);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
return &br.base;
|
||||
}
|
||||
|
||||
fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangFloatingLiteral, used: ResultUsed) TransError!*ast.Node {
|
||||
// TODO use something more accurate
|
||||
const dbl = ZigClangAPFloat_getValueAsApproximateDouble(stmt);
|
||||
const node = try rp.c.a().create(ast.Node.FloatLiteral);
|
||||
node.* = .{
|
||||
.token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}),
|
||||
};
|
||||
return maybeSuppressResult(rp, scope, used, &node.base);
|
||||
}
|
||||
|
||||
fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangConditionalOperator, used: ResultUsed) TransError!*ast.Node {
|
||||
const grouped = scope.id == .Condition;
|
||||
const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined;
|
||||
const if_node = try transCreateNodeIf(rp.c);
|
||||
var cond_scope = Scope{
|
||||
.parent = scope,
|
||||
.id = .Condition,
|
||||
};
|
||||
|
||||
const cond_expr = ZigClangConditionalOperator_getCond(stmt);
|
||||
const true_expr = ZigClangConditionalOperator_getTrueExpr(stmt);
|
||||
const false_expr = ZigClangConditionalOperator_getFalseExpr(stmt);
|
||||
|
||||
if_node.condition = try transBoolExpr(rp, &cond_scope, cond_expr, .used, .r_value, false);
|
||||
_ = try appendToken(rp.c, .RParen, ")");
|
||||
|
||||
if_node.body = try transExpr(rp, scope, true_expr, .used, .r_value);
|
||||
|
||||
if_node.@"else" = try transCreateNodeElse(rp.c);
|
||||
if_node.@"else".?.body = try transExpr(rp, scope, false_expr, .used, .r_value);
|
||||
|
||||
if (grouped) {
|
||||
const rparen = try appendToken(rp.c, .RParen, ")");
|
||||
const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
|
||||
grouped_expr.* = .{
|
||||
.lparen = lparen,
|
||||
.expr = &if_node.base,
|
||||
.rparen = rparen,
|
||||
};
|
||||
return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
|
||||
} else {
|
||||
return maybeSuppressResult(rp, scope, used, &if_node.base);
|
||||
}
|
||||
}
|
||||
|
||||
fn maybeSuppressResult(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
@@ -1259,12 +2622,20 @@ fn maybeSuppressResult(
|
||||
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, ";");
|
||||
if (scope.id != .Condition) {
|
||||
// NOTE: This is backwards, but the semicolon must immediately follow the node.
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
} else { // TODO is there a way to avoid this hack?
|
||||
// this parenthesis must come immediately following the node
|
||||
_ = try appendToken(rp.c, .RParen, ")");
|
||||
// these need to come before _
|
||||
_ = try appendToken(rp.c, .Colon, ":");
|
||||
_ = try appendToken(rp.c, .LParen, "(");
|
||||
}
|
||||
const lhs = try transCreateNodeIdentifier(rp.c, "_");
|
||||
const op_token = try appendToken(rp.c, .Equal, "=");
|
||||
const op_node = try rp.c.a().create(ast.Node.InfixOp);
|
||||
op_node.* = ast.Node.InfixOp{
|
||||
op_node.* = .{
|
||||
.op_token = op_token,
|
||||
.lhs = lhs,
|
||||
.op = .Assign,
|
||||
@@ -1282,264 +2653,6 @@ fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSou
|
||||
return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc);
|
||||
}
|
||||
|
||||
fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) TypeError!*ast.Node {
|
||||
const rp = makeRestorePoint(c);
|
||||
|
||||
const record_loc = ZigClangRecordDecl_getLocation(record_decl);
|
||||
|
||||
var container_kind_name: []const u8 = undefined;
|
||||
var container_kind: std.zig.Token.Id = undefined;
|
||||
|
||||
if (ZigClangRecordDecl_isUnion(record_decl)) {
|
||||
container_kind_name = "union";
|
||||
container_kind = .Keyword_union;
|
||||
} else if (ZigClangRecordDecl_isStruct(record_decl)) {
|
||||
container_kind_name = "struct";
|
||||
container_kind = .Keyword_struct;
|
||||
} else {
|
||||
return revertAndWarn(
|
||||
rp,
|
||||
error.UnsupportedType,
|
||||
record_loc,
|
||||
"unsupported record type",
|
||||
.{},
|
||||
);
|
||||
}
|
||||
|
||||
const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse {
|
||||
return transCreateNodeOpaqueType(c);
|
||||
};
|
||||
|
||||
const extern_tok = try appendToken(c, .Keyword_extern, "extern");
|
||||
const container_tok = try appendToken(c, container_kind, container_kind_name);
|
||||
const lbrace_token = try appendToken(c, .LBrace, "{");
|
||||
|
||||
const container_node = try c.a().create(ast.Node.ContainerDecl);
|
||||
container_node.* = .{
|
||||
.layout_token = extern_tok,
|
||||
.kind_token = container_tok,
|
||||
.init_arg_expr = .None,
|
||||
.fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()),
|
||||
.lbrace_token = lbrace_token,
|
||||
.rbrace_token = undefined,
|
||||
};
|
||||
|
||||
var it = ZigClangRecordDecl_field_begin(record_def);
|
||||
const end_it = ZigClangRecordDecl_field_end(record_def);
|
||||
while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
|
||||
const field_decl = ZigClangRecordDecl_field_iterator_deref(it);
|
||||
const field_loc = ZigClangFieldDecl_getLocation(field_decl);
|
||||
|
||||
if (ZigClangFieldDecl_isBitField(field_decl)) {
|
||||
rp.activate();
|
||||
const node = try transCreateNodeOpaqueType(c);
|
||||
try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name});
|
||||
return node;
|
||||
}
|
||||
|
||||
const field_name = try appendIdentifier(c, try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, field_decl))));
|
||||
_ = try appendToken(c, .Colon, ":");
|
||||
const field_type = try transQualType(rp, ZigClangFieldDecl_getType(field_decl), field_loc);
|
||||
|
||||
const field_node = try c.a().create(ast.Node.ContainerField);
|
||||
field_node.* = .{
|
||||
.doc_comments = null,
|
||||
.comptime_token = null,
|
||||
.name_token = field_name,
|
||||
.type_expr = field_type,
|
||||
.value_expr = null,
|
||||
.align_expr = null,
|
||||
};
|
||||
|
||||
try container_node.fields_and_decls.push(&field_node.base);
|
||||
_ = try appendToken(c, .Comma, ",");
|
||||
}
|
||||
|
||||
container_node.rbrace_token = try appendToken(c, .RBrace, "}");
|
||||
return &container_node.base;
|
||||
}
|
||||
|
||||
fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node {
|
||||
if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name|
|
||||
return try transCreateNodeIdentifier(c, name.value); // Avoid processing this decl twice
|
||||
const rp = makeRestorePoint(c);
|
||||
const enum_loc = ZigClangEnumDecl_getLocation(enum_decl);
|
||||
|
||||
const visib_tok = try appendToken(c, .Keyword_pub, "pub");
|
||||
const const_tok = try appendToken(c, .Keyword_const, "const");
|
||||
|
||||
var bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_decl)));
|
||||
var is_unnamed = false;
|
||||
if (bare_name.len == 0) {
|
||||
bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()});
|
||||
is_unnamed = true;
|
||||
}
|
||||
|
||||
const name = try std.fmt.allocPrint(c.a(), "enum_{}", .{bare_name});
|
||||
_ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name);
|
||||
const name_tok = try appendIdentifier(c, name);
|
||||
const eq_tok = try appendToken(c, .Equal, "=");
|
||||
|
||||
const init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: {
|
||||
var pure_enum = true;
|
||||
var it = ZigClangEnumDecl_enumerator_begin(enum_def);
|
||||
var end_it = ZigClangEnumDecl_enumerator_end(enum_def);
|
||||
while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) {
|
||||
const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it);
|
||||
if (ZigClangEnumConstantDecl_getInitExpr(enum_const)) |_| {
|
||||
pure_enum = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const extern_tok = try appendToken(c, .Keyword_extern, "extern");
|
||||
const container_tok = try appendToken(c, .Keyword_enum, "enum");
|
||||
|
||||
const container_node = try c.a().create(ast.Node.ContainerDecl);
|
||||
container_node.* = .{
|
||||
.layout_token = extern_tok,
|
||||
.kind_token = container_tok,
|
||||
.init_arg_expr = .None,
|
||||
.fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()),
|
||||
.lbrace_token = undefined,
|
||||
.rbrace_token = undefined,
|
||||
};
|
||||
|
||||
const int_type = ZigClangEnumDecl_getIntegerType(enum_decl);
|
||||
|
||||
// TODO only emit this tag type if the enum tag type is not the default.
|
||||
// I don't know what the default is, need to figure out how clang is deciding.
|
||||
// it appears to at least be different across gcc/msvc
|
||||
if (!isCBuiltinType(int_type, .UInt) and
|
||||
!isCBuiltinType(int_type, .Int))
|
||||
{
|
||||
_ = try appendToken(c, .LParen, "(");
|
||||
container_node.init_arg_expr = .{
|
||||
.Type = transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
if (is_unnamed) {
|
||||
try emitWarning(c, enum_loc, "unable to translate enum tag type", .{});
|
||||
} else {
|
||||
try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
|
||||
}
|
||||
return null;
|
||||
},
|
||||
else => |e| return e,
|
||||
},
|
||||
};
|
||||
_ = try appendToken(c, .RParen, ")");
|
||||
}
|
||||
|
||||
container_node.lbrace_token = try appendToken(c, .LBrace, "{");
|
||||
|
||||
it = ZigClangEnumDecl_enumerator_begin(enum_def);
|
||||
end_it = ZigClangEnumDecl_enumerator_end(enum_def);
|
||||
while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) {
|
||||
const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it);
|
||||
|
||||
const enum_val_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_const)));
|
||||
|
||||
const field_name = if (!is_unnamed and std.mem.startsWith(u8, enum_val_name, bare_name))
|
||||
enum_val_name[bare_name.len..]
|
||||
else
|
||||
enum_val_name;
|
||||
|
||||
const field_name_tok = try appendIdentifier(c, field_name);
|
||||
|
||||
const int_node = if (!pure_enum) blk: {
|
||||
_ = try appendToken(c, .Colon, "=");
|
||||
break :blk try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const));
|
||||
} else
|
||||
null;
|
||||
|
||||
const field_node = try c.a().create(ast.Node.ContainerField);
|
||||
field_node.* = .{
|
||||
.doc_comments = null,
|
||||
.comptime_token = null,
|
||||
.name_token = field_name_tok,
|
||||
.type_expr = null,
|
||||
.value_expr = int_node,
|
||||
.align_expr = null,
|
||||
};
|
||||
|
||||
try container_node.fields_and_decls.push(&field_node.base);
|
||||
_ = try appendToken(c, .Comma, ",");
|
||||
// In C each enum value is in the global namespace. So we put them there too.
|
||||
// At this point we can rely on the enum emitting successfully.
|
||||
try addEnumTopLevel(c, name, field_name, enum_val_name);
|
||||
}
|
||||
container_node.rbrace_token = try appendToken(c, .RBrace, "}");
|
||||
|
||||
break :blk &container_node.base;
|
||||
} else
|
||||
try transCreateNodeOpaqueType(c);
|
||||
|
||||
const semicolon_token = try appendToken(c, .Semicolon, ";");
|
||||
|
||||
const node = try c.a().create(ast.Node.VarDecl);
|
||||
node.* = ast.Node.VarDecl{
|
||||
.visib_token = visib_tok,
|
||||
.mut_token = const_tok,
|
||||
.name_token = name_tok,
|
||||
.eq_token = eq_tok,
|
||||
.init_node = init_node,
|
||||
.semicolon_token = semicolon_token,
|
||||
.doc_comments = null,
|
||||
.comptime_token = null,
|
||||
.extern_export_token = null,
|
||||
.thread_local_token = null,
|
||||
.lib_name = null,
|
||||
.type_node = null,
|
||||
.align_node = null,
|
||||
.section_node = null,
|
||||
};
|
||||
|
||||
try addTopLevelDecl(c, name, &node.base);
|
||||
if (!is_unnamed)
|
||||
try c.alias_list.push(.{ .alias = bare_name, .name = name });
|
||||
return transCreateNodeIdentifier(c, name);
|
||||
}
|
||||
|
||||
fn addEnumTopLevel(c: *Context, enum_name: []const u8, field_name: []const u8, enum_val_name: []const u8) !void {
|
||||
const visib_tok = try appendToken(c, .Keyword_pub, "pub");
|
||||
const const_tok = try appendToken(c, .Keyword_const, "const");
|
||||
const name_tok = try appendIdentifier(c, enum_val_name);
|
||||
const eq_tok = try appendToken(c, .Equal, "=");
|
||||
|
||||
const enum_ident = try transCreateNodeIdentifier(c, enum_name);
|
||||
const period_tok = try appendToken(c, .Period, ".");
|
||||
const field_ident = try transCreateNodeIdentifier(c, field_name);
|
||||
|
||||
const field_access_node = try c.a().create(ast.Node.InfixOp);
|
||||
field_access_node.* = .{
|
||||
.op_token = period_tok,
|
||||
.lhs = enum_ident,
|
||||
.op = .Period,
|
||||
.rhs = field_ident,
|
||||
};
|
||||
const semicolon_token = try appendToken(c, .Semicolon, ";");
|
||||
|
||||
const node = try c.a().create(ast.Node.VarDecl);
|
||||
node.* = ast.Node.VarDecl{
|
||||
.visib_token = visib_tok,
|
||||
.mut_token = const_tok,
|
||||
.name_token = name_tok,
|
||||
.eq_token = eq_tok,
|
||||
.init_node = &field_access_node.base,
|
||||
.semicolon_token = semicolon_token,
|
||||
.thread_local_token = null,
|
||||
.doc_comments = null,
|
||||
.comptime_token = null,
|
||||
.extern_export_token = null,
|
||||
.lib_name = null,
|
||||
.type_node = null,
|
||||
.align_node = null,
|
||||
.section_node = null,
|
||||
};
|
||||
|
||||
try addTopLevelDecl(c, field_name, &node.base);
|
||||
}
|
||||
|
||||
fn isCBuiltinType(qt: ZigClangQualType, kind: ZigClangBuiltinTypeKind) bool {
|
||||
const c_type = qualTypeCanon(qt);
|
||||
if (ZigClangType_getTypeClass(c_type) != .Builtin)
|
||||
@@ -1552,6 +2665,95 @@ fn qualTypeIsPtr(qt: ZigClangQualType) bool {
|
||||
return ZigClangType_getTypeClass(qualTypeCanon(qt)) == .Pointer;
|
||||
}
|
||||
|
||||
fn qualTypeIntBitWidth(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !u32 {
|
||||
const ty = ZigClangQualType_getTypePtr(qt);
|
||||
|
||||
switch (ZigClangType_getTypeClass(ty)) {
|
||||
.Builtin => {
|
||||
const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
|
||||
|
||||
switch (ZigClangBuiltinType_getKind(builtin_ty)) {
|
||||
.Char_U,
|
||||
.UChar,
|
||||
.Char_S,
|
||||
.SChar,
|
||||
=> return 8,
|
||||
.UInt128,
|
||||
.Int128,
|
||||
=> return 128,
|
||||
else => return 0,
|
||||
}
|
||||
|
||||
unreachable;
|
||||
},
|
||||
.Typedef => {
|
||||
const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
|
||||
const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
|
||||
const type_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl)));
|
||||
|
||||
if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) {
|
||||
return 8;
|
||||
} else if (mem.eql(u8, type_name, "uint16_t") or mem.eql(u8, type_name, "int16_t")) {
|
||||
return 16;
|
||||
} else if (mem.eql(u8, type_name, "uint32_t") or mem.eql(u8, type_name, "int32_t")) {
|
||||
return 32;
|
||||
} else if (mem.eql(u8, type_name, "uint64_t") or mem.eql(u8, type_name, "int64_t")) {
|
||||
return 64;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
else => return 0,
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !*ast.Node {
|
||||
const int_bit_width = try qualTypeIntBitWidth(rp, qt, source_loc);
|
||||
|
||||
if (int_bit_width != 0) {
|
||||
// we can perform the log2 now.
|
||||
const cast_bit_width = std.math.log2_int(u64, int_bit_width);
|
||||
const node = try rp.c.a().create(ast.Node.IntegerLiteral);
|
||||
node.* = ast.Node.IntegerLiteral{
|
||||
.token = try appendTokenFmt(rp.c, .Identifier, "u{}", .{cast_bit_width}),
|
||||
};
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
const zig_type_node = try transQualType(rp, qt, source_loc);
|
||||
|
||||
// @import("std").math.Log2Int(c_long);
|
||||
//
|
||||
// FnCall
|
||||
// FieldAccess
|
||||
// FieldAccess
|
||||
// FnCall (.builtin = true)
|
||||
// Symbol "import"
|
||||
// StringLiteral "std"
|
||||
// Symbol "math"
|
||||
// Symbol "Log2Int"
|
||||
// Symbol <zig_type_node> (var from above)
|
||||
|
||||
const import_fn_call = try transCreateNodeBuiltinFnCall(rp.c, "@import");
|
||||
const std_token = try appendToken(rp.c, .StringLiteral, "\"std\"");
|
||||
const std_node = try rp.c.a().create(ast.Node.StringLiteral);
|
||||
std_node.* = ast.Node.StringLiteral{
|
||||
.token = std_token,
|
||||
};
|
||||
try import_fn_call.params.push(&std_node.base);
|
||||
import_fn_call.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
|
||||
const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math");
|
||||
const outer_field_access = try transCreateNodeFieldAccess(rp.c, inner_field_access, "Log2Int");
|
||||
const log2int_fn_call = try transCreateNodeFnCall(rp.c, outer_field_access);
|
||||
try @fieldParentPtr(ast.Node.SuffixOp, "base", &log2int_fn_call.base).op.Call.params.push(zig_type_node);
|
||||
log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")");
|
||||
|
||||
return &log2int_fn_call.base;
|
||||
}
|
||||
|
||||
fn qualTypeChildIsFnProto(qt: ZigClangQualType) bool {
|
||||
const ty = ZigClangQualType_getTypePtr(qt);
|
||||
|
||||
@@ -1601,6 +2803,14 @@ fn getExprQualType(c: *Context, expr: *const ZigClangExpr) ZigClangQualType {
|
||||
return ZigClangExpr_getType(expr);
|
||||
}
|
||||
|
||||
fn getExprQualTypeBeforeImplicitCast(c: *Context, expr: *const ZigClangExpr) ZigClangQualType {
|
||||
if (ZigClangExpr_getStmtClass(expr) == .ImplicitCastExprClass) {
|
||||
const cast_expr = @ptrCast(*const ZigClangImplicitCastExpr, expr);
|
||||
return getExprQualType(c, ZigClangImplicitCastExpr_getSubExpr(cast_expr));
|
||||
}
|
||||
return ZigClangExpr_getType(expr);
|
||||
}
|
||||
|
||||
fn typeIsOpaque(c: *Context, ty: *const ZigClangType, loc: ZigClangSourceLocation) bool {
|
||||
switch (ZigClangType_getTypeClass(ty)) {
|
||||
.Builtin => {
|
||||
@@ -1657,53 +2867,109 @@ fn cIsUnsignedInteger(qt: ZigClangQualType) bool {
|
||||
};
|
||||
}
|
||||
|
||||
fn cIsSignedInteger(qt: ZigClangQualType) bool {
|
||||
const c_type = qualTypeCanon(qt);
|
||||
if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
|
||||
const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
|
||||
return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
|
||||
.SChar,
|
||||
.Short,
|
||||
.Int,
|
||||
.Long,
|
||||
.LongLong,
|
||||
.Int128,
|
||||
.WChar_S,
|
||||
=> true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn cIsFloating(qt: ZigClangQualType) bool {
|
||||
const c_type = qualTypeCanon(qt);
|
||||
if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
|
||||
const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
|
||||
return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
|
||||
.Float,
|
||||
.Double,
|
||||
.Float128,
|
||||
.LongDouble,
|
||||
=> true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn transCreateNodeAssign(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
result_used: ResultUsed,
|
||||
lhs: *const ZigClangExpr,
|
||||
rhs: *const ZigClangExpr,
|
||||
) !*ast.Node.InfixOp {
|
||||
) !*ast.Node {
|
||||
// common case
|
||||
// c: lhs = rhs
|
||||
// zig: lhs = rhs
|
||||
if (result_used == .unused) {
|
||||
const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
|
||||
const eq_token = try appendToken(rp.c, .Equal, "=");
|
||||
const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
|
||||
const node = try rp.c.a().create(ast.Node.InfixOp);
|
||||
node.* = .{
|
||||
.op_token = eq_token,
|
||||
.lhs = lhs_node,
|
||||
.op = .Assign,
|
||||
.rhs = rhs_node,
|
||||
};
|
||||
return node;
|
||||
var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
|
||||
if (isBoolRes(rhs_node)) {
|
||||
const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt");
|
||||
try builtin_node.params.push(rhs_node);
|
||||
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
rhs_node = &builtin_node.base;
|
||||
}
|
||||
if (scope.id != .Condition)
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
return transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, rhs_node, .used, false);
|
||||
}
|
||||
|
||||
// worst case
|
||||
// c: lhs = rhs
|
||||
// zig: (x: {
|
||||
// zig: (blk: {
|
||||
// zig: const _tmp = rhs;
|
||||
// zig: lhs = _tmp;
|
||||
// zig: break :x _tmp
|
||||
// zig: break :blk _tmp
|
||||
// zig: })
|
||||
return revertAndWarn(
|
||||
rp,
|
||||
error.UnsupportedTranslation,
|
||||
ZigClangExpr_getBeginLoc(lhs),
|
||||
"TODO: worst case assign op expr",
|
||||
.{},
|
||||
);
|
||||
const block_scope = try Scope.Block.init(rp.c, scope, "blk");
|
||||
block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label);
|
||||
const tmp = try std.fmt.allocPrint(rp.c.a(), "_tmp_{}", .{rp.c.getMangle()});
|
||||
|
||||
const node = try transCreateNodeVarDecl(rp.c, false, true, tmp);
|
||||
node.eq_token = try appendToken(rp.c, .Equal, "=");
|
||||
var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
|
||||
if (isBoolRes(rhs_node)) {
|
||||
const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt");
|
||||
try builtin_node.params.push(rhs_node);
|
||||
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
rhs_node = &builtin_node.base;
|
||||
}
|
||||
node.init_node = rhs_node;
|
||||
node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
|
||||
try block_scope.block_node.statements.push(&node.base);
|
||||
|
||||
const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
|
||||
const eq_token = try appendToken(rp.c, .Equal, "=");
|
||||
const ident = try transCreateNodeIdentifier(rp.c, tmp);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
|
||||
const assign = try transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, ident, .used, false);
|
||||
try block_scope.block_node.statements.push(assign);
|
||||
|
||||
const break_node = try transCreateNodeBreak(rp.c, block_scope.label);
|
||||
break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp);
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
try block_scope.block_node.statements.push(&break_node.base);
|
||||
block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
|
||||
// semicolon must immediately follow rbrace because it is the last token in a block
|
||||
_ = try appendToken(rp.c, .Semicolon, ";");
|
||||
return &block_scope.block_node.base;
|
||||
}
|
||||
|
||||
fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.BuiltinCall {
|
||||
const builtin_token = try appendToken(c, .Builtin, name);
|
||||
_ = try appendToken(c, .LParen, "(");
|
||||
const node = try c.a().create(ast.Node.BuiltinCall);
|
||||
node.* = ast.Node.BuiltinCall{
|
||||
node.* = .{
|
||||
.builtin_token = builtin_token,
|
||||
.params = ast.Node.BuiltinCall.ParamList.init(c.a()),
|
||||
.rparen_token = undefined, // set after appending args
|
||||
@@ -1714,10 +2980,10 @@ fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.Builti
|
||||
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{
|
||||
node.* = .{
|
||||
.lhs = .{ .node = fn_expr },
|
||||
.op = ast.Node.SuffixOp.Op{
|
||||
.Call = ast.Node.SuffixOp.Op.Call{
|
||||
.op = .{
|
||||
.Call = .{
|
||||
.params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()),
|
||||
.async_token = null,
|
||||
},
|
||||
@@ -1727,6 +2993,17 @@ fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp {
|
||||
return node;
|
||||
}
|
||||
|
||||
fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node {
|
||||
const field_access_node = try c.a().create(ast.Node.InfixOp);
|
||||
field_access_node.* = .{
|
||||
.op_token = try appendToken(c, .Period, "."),
|
||||
.lhs = container,
|
||||
.op = .Period,
|
||||
.rhs = try transCreateNodeIdentifier(c, field_name),
|
||||
};
|
||||
return &field_access_node.base;
|
||||
}
|
||||
|
||||
fn transCreateNodePrefixOp(
|
||||
c: *Context,
|
||||
op: ast.Node.PrefixOp.Op,
|
||||
@@ -1745,32 +3022,62 @@ fn transCreateNodePrefixOp(
|
||||
fn transCreateNodeInfixOp(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangBinaryOperator,
|
||||
lhs_node: *ast.Node,
|
||||
op: ast.Node.InfixOp.Op,
|
||||
op_tok_id: std.zig.Token.Id,
|
||||
bytes: []const u8,
|
||||
op_token: ast.TokenIndex,
|
||||
rhs_node: *ast.Node,
|
||||
used: ResultUsed,
|
||||
grouped: bool,
|
||||
) !*ast.Node {
|
||||
const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined;
|
||||
const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
|
||||
const op_token = try appendToken(rp.c, op_tok_id, bytes);
|
||||
const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
|
||||
var lparen = if (grouped)
|
||||
try appendToken(rp.c, .LParen, "(")
|
||||
else
|
||||
null;
|
||||
const node = try rp.c.a().create(ast.Node.InfixOp);
|
||||
node.* = ast.Node.InfixOp{
|
||||
node.* = .{
|
||||
.op_token = op_token,
|
||||
.lhs = lhs,
|
||||
.lhs = lhs_node,
|
||||
.op = op,
|
||||
.rhs = rhs,
|
||||
.rhs = rhs_node,
|
||||
};
|
||||
if (!grouped) return &node.base;
|
||||
if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base);
|
||||
const rparen = try appendToken(rp.c, .RParen, ")");
|
||||
const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
|
||||
grouped_expr.* = ast.Node.GroupedExpression{
|
||||
.lparen = lparen,
|
||||
grouped_expr.* = .{
|
||||
.lparen = lparen.?,
|
||||
.expr = &node.base,
|
||||
.rparen = rparen,
|
||||
};
|
||||
return &grouped_expr.base;
|
||||
return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
|
||||
}
|
||||
|
||||
fn transCreateNodeBoolInfixOp(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangBinaryOperator,
|
||||
op: ast.Node.InfixOp.Op,
|
||||
used: ResultUsed,
|
||||
grouped: bool,
|
||||
) !*ast.Node {
|
||||
std.debug.assert(op == .BoolAnd or op == .BoolOr);
|
||||
|
||||
const lhs_hode = try transBoolExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value, true);
|
||||
const op_token = if (op == .BoolAnd)
|
||||
try appendToken(rp.c, .Keyword_and, "and")
|
||||
else
|
||||
try appendToken(rp.c, .Keyword_or, "or");
|
||||
const rhs = try transBoolExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value, true);
|
||||
|
||||
return transCreateNodeInfixOp(
|
||||
rp,
|
||||
scope,
|
||||
lhs_hode,
|
||||
op,
|
||||
op_token,
|
||||
rhs,
|
||||
used,
|
||||
grouped,
|
||||
);
|
||||
}
|
||||
|
||||
fn transCreateNodePtrType(
|
||||
@@ -1797,9 +3104,9 @@ fn transCreateNodePtrType(
|
||||
.Asterisk => try appendToken(c, .Asterisk, "*"),
|
||||
else => unreachable,
|
||||
};
|
||||
node.* = ast.Node.PrefixOp{
|
||||
node.* = .{
|
||||
.op_token = op_token,
|
||||
.op = ast.Node.PrefixOp.Op{
|
||||
.op = .{
|
||||
.PtrType = .{
|
||||
.const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,
|
||||
.volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
|
||||
@@ -1810,11 +3117,17 @@ fn transCreateNodePtrType(
|
||||
return node;
|
||||
}
|
||||
|
||||
fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node {
|
||||
const num_limbs = ZigClangAPSInt_getNumWords(int.?);
|
||||
fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
|
||||
const num_limbs = ZigClangAPSInt_getNumWords(int);
|
||||
var aps_int = int;
|
||||
const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int);
|
||||
if (is_negative)
|
||||
aps_int = ZigClangAPSInt_negate(aps_int);
|
||||
var big = try std.math.big.Int.initCapacity(c.a(), num_limbs);
|
||||
if (is_negative)
|
||||
big.negate();
|
||||
defer big.deinit();
|
||||
const data = ZigClangAPSInt_getRawData(int.?);
|
||||
const data = ZigClangAPSInt_getRawData(aps_int);
|
||||
var i: @TypeOf(num_limbs) = 0;
|
||||
while (i < num_limbs) : (i += 1) big.limbs[i] = data[i];
|
||||
const str = big.toString(c.a(), 10) catch |err| switch (err) {
|
||||
@@ -1823,16 +3136,18 @@ fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node {
|
||||
};
|
||||
const token = try appendToken(c, .IntegerLiteral, str);
|
||||
const node = try c.a().create(ast.Node.IntegerLiteral);
|
||||
node.* = ast.Node.IntegerLiteral{
|
||||
node.* = .{
|
||||
.token = token,
|
||||
};
|
||||
if (is_negative)
|
||||
ZigClangAPSInt_free(aps_int);
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
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{
|
||||
node.* = .{
|
||||
.ltoken = ltoken,
|
||||
.kind = .Return,
|
||||
.rhs = null,
|
||||
@@ -1843,7 +3158,7 @@ fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression {
|
||||
fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node {
|
||||
const token = try appendToken(c, .Keyword_undefined, "undefined");
|
||||
const node = try c.a().create(ast.Node.UndefinedLiteral);
|
||||
node.* = ast.Node.UndefinedLiteral{
|
||||
node.* = .{
|
||||
.token = token,
|
||||
};
|
||||
return &node.base;
|
||||
@@ -1852,7 +3167,7 @@ fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node {
|
||||
fn transCreateNodeNullLiteral(c: *Context) !*ast.Node {
|
||||
const token = try appendToken(c, .Keyword_null, "null");
|
||||
const node = try c.a().create(ast.Node.NullLiteral);
|
||||
node.* = ast.Node.NullLiteral{
|
||||
node.* = .{
|
||||
.token = token,
|
||||
};
|
||||
return &node.base;
|
||||
@@ -1864,13 +3179,13 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node {
|
||||
else
|
||||
try appendToken(c, .Keyword_false, "false");
|
||||
const node = try c.a().create(ast.Node.BoolLiteral);
|
||||
node.* = ast.Node.BoolLiteral{
|
||||
node.* = .{
|
||||
.token = token,
|
||||
};
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
fn transCreateNodeArrayInitializer(c: *Context, dot_tok: ast.TokenIndex) !*ast.Node.SuffixOp {
|
||||
fn transCreateNodeContainerInitializer(c: *Context, dot_tok: ast.TokenIndex) !*ast.Node.SuffixOp {
|
||||
_ = try appendToken(c, .LBrace, "{");
|
||||
const node = try c.a().create(ast.Node.SuffixOp);
|
||||
node.* = ast.Node.SuffixOp{
|
||||
@@ -1886,7 +3201,7 @@ fn transCreateNodeArrayInitializer(c: *Context, dot_tok: ast.TokenIndex) !*ast.N
|
||||
fn transCreateNodeInt(c: *Context, int: var) !*ast.Node {
|
||||
const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int});
|
||||
const node = try c.a().create(ast.Node.IntegerLiteral);
|
||||
node.* = ast.Node.IntegerLiteral{
|
||||
node.* = .{
|
||||
.token = token,
|
||||
};
|
||||
return &node.base;
|
||||
@@ -1907,7 +3222,7 @@ fn transCreateNodeOpaqueType(c: *Context) !*ast.Node {
|
||||
return &call_node.base;
|
||||
}
|
||||
|
||||
fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias_node: *ast.Node) !*ast.Node {
|
||||
fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias: *ast.Node.FnProto) !*ast.Node {
|
||||
const scope = &c.global_scope.base;
|
||||
|
||||
const pub_tok = try appendToken(c, .Keyword_pub, "pub");
|
||||
@@ -1916,8 +3231,6 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
|
||||
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| {
|
||||
@@ -2046,6 +3359,172 @@ fn transCreateNodeBlock(c: *Context, label: ?[]const u8) !*ast.Node.Block {
|
||||
return block_node;
|
||||
}
|
||||
|
||||
fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowExpression {
|
||||
const ltoken = try appendToken(c, .Keyword_break, "break");
|
||||
const label_node = if (label) |l| blk: {
|
||||
_ = try appendToken(c, .Colon, ":");
|
||||
break :blk try transCreateNodeIdentifier(c, l);
|
||||
} else null;
|
||||
const node = try c.a().create(ast.Node.ControlFlowExpression);
|
||||
node.* = .{
|
||||
.ltoken = ltoken,
|
||||
.kind = .{ .Break = label_node },
|
||||
.rhs = null,
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
fn transCreateNodeVarDecl(c: *Context, is_pub: bool, is_const: bool, name: []const u8) !*ast.Node.VarDecl {
|
||||
const visib_tok = if (is_pub) try appendToken(c, .Keyword_pub, "pub") else null;
|
||||
const mut_tok = if (is_const) try appendToken(c, .Keyword_const, "const") else try appendToken(c, .Keyword_var, "var");
|
||||
const name_tok = try appendIdentifier(c, name);
|
||||
|
||||
const node = try c.a().create(ast.Node.VarDecl);
|
||||
node.* = .{
|
||||
.doc_comments = null,
|
||||
.visib_token = visib_tok,
|
||||
.thread_local_token = null,
|
||||
.name_token = name_tok,
|
||||
.eq_token = undefined,
|
||||
.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 = null,
|
||||
.semicolon_token = undefined,
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
fn transCreateNodeWhile(c: *Context) !*ast.Node.While {
|
||||
const while_tok = try appendToken(c, .Keyword_while, "while");
|
||||
_ = try appendToken(c, .LParen, "(");
|
||||
|
||||
const node = try c.a().create(ast.Node.While);
|
||||
node.* = .{
|
||||
.label = null,
|
||||
.inline_token = null,
|
||||
.while_token = while_tok,
|
||||
.condition = undefined,
|
||||
.payload = null,
|
||||
.continue_expr = null,
|
||||
.body = undefined,
|
||||
.@"else" = null,
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
fn transCreateNodeContinue(c: *Context) !*ast.Node {
|
||||
const ltoken = try appendToken(c, .Keyword_continue, "continue");
|
||||
const node = try c.a().create(ast.Node.ControlFlowExpression);
|
||||
node.* = .{
|
||||
.ltoken = ltoken,
|
||||
.kind = .{ .Continue = null },
|
||||
.rhs = null,
|
||||
};
|
||||
_ = try appendToken(c, .Semicolon, ";");
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
fn transCreateNodeSwitch(c: *Context) !*ast.Node.Switch {
|
||||
const switch_tok = try appendToken(c, .Keyword_switch, "switch");
|
||||
_ = try appendToken(c, .LParen, "(");
|
||||
|
||||
const node = try c.a().create(ast.Node.Switch);
|
||||
node.* = .{
|
||||
.switch_token = switch_tok,
|
||||
.expr = undefined,
|
||||
.cases = ast.Node.Switch.CaseList.init(c.a()),
|
||||
.rbrace = undefined,
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
fn transCreateNodeSwitchCase(c: *Context, lhs: *ast.Node) !*ast.Node.SwitchCase {
|
||||
const arrow_tok = try appendToken(c, .EqualAngleBracketRight, "=>");
|
||||
|
||||
const node = try c.a().create(ast.Node.SwitchCase);
|
||||
node.* = .{
|
||||
.items = ast.Node.SwitchCase.ItemList.init(c.a()),
|
||||
.arrow_token = arrow_tok,
|
||||
.payload = null,
|
||||
.expr = undefined,
|
||||
};
|
||||
try node.items.push(lhs);
|
||||
return node;
|
||||
}
|
||||
|
||||
fn transCreateNodeSwitchElse(c: *Context) !*ast.Node {
|
||||
const node = try c.a().create(ast.Node.SwitchElse);
|
||||
node.* = .{
|
||||
.token = try appendToken(c, .Keyword_else, "else"),
|
||||
};
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
fn transCreateNodeShiftOp(
|
||||
rp: RestorePoint,
|
||||
scope: *Scope,
|
||||
stmt: *const ZigClangBinaryOperator,
|
||||
op: ast.Node.InfixOp.Op,
|
||||
op_tok_id: std.zig.Token.Id,
|
||||
bytes: []const u8,
|
||||
) !*ast.Node {
|
||||
std.debug.assert(op == .BitShiftLeft or op == .BitShiftRight);
|
||||
|
||||
const lhs_expr = ZigClangBinaryOperator_getLHS(stmt);
|
||||
const rhs_expr = ZigClangBinaryOperator_getRHS(stmt);
|
||||
const rhs_location = ZigClangExpr_getBeginLoc(rhs_expr);
|
||||
// lhs >> @as(u5, rh)
|
||||
|
||||
const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value);
|
||||
const op_token = try appendToken(rp.c, op_tok_id, bytes);
|
||||
|
||||
const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
|
||||
const rhs_type = try qualTypeToLog2IntRef(rp, ZigClangBinaryOperator_getType(stmt), rhs_location);
|
||||
try as_node.params.push(rhs_type);
|
||||
_ = try appendToken(rp.c, .Comma, ",");
|
||||
const rhs = try transExpr(rp, scope, rhs_expr, .used, .r_value);
|
||||
try as_node.params.push(rhs);
|
||||
as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
|
||||
const node = try rp.c.a().create(ast.Node.InfixOp);
|
||||
node.* = ast.Node.InfixOp{
|
||||
.op_token = op_token,
|
||||
.lhs = lhs,
|
||||
.op = op,
|
||||
.rhs = &as_node.base,
|
||||
};
|
||||
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node {
|
||||
const node = try c.a().create(ast.Node.SuffixOp);
|
||||
node.* = .{
|
||||
.lhs = .{ .node = lhs },
|
||||
.op = .Deref,
|
||||
.rtoken = try appendToken(c, .PeriodAsterisk, ".*"),
|
||||
};
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.SuffixOp {
|
||||
_ = try appendToken(c, .LBrace, "[");
|
||||
const node = try c.a().create(ast.Node.SuffixOp);
|
||||
node.* = .{
|
||||
.lhs = .{ .node = lhs },
|
||||
.op = .{
|
||||
.ArrayAccess = undefined,
|
||||
},
|
||||
.rtoken = undefined,
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
const RestorePoint = struct {
|
||||
c: *Context,
|
||||
token_index: ast.TokenIndex,
|
||||
@@ -2092,7 +3571,7 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
|
||||
else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
|
||||
});
|
||||
},
|
||||
.FunctionProto => {
|
||||
.FunctionProto, .FunctionNoProto => {
|
||||
const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty);
|
||||
const fn_proto = try transFnProto(rp, null, fn_proto_ty, source_loc, null, false);
|
||||
return &fn_proto.base;
|
||||
@@ -2167,24 +3646,15 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
|
||||
const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
|
||||
|
||||
const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
|
||||
const typedef_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl)));
|
||||
return transCreateNodeIdentifier(rp.c, typedef_name);
|
||||
return (try transTypeDef(rp.c, typedef_decl)) orelse
|
||||
revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{});
|
||||
},
|
||||
.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)
|
||||
else
|
||||
return transRecordDecl(rp.c, record_decl);
|
||||
return (try transRecordDecl(rp.c, record_decl)) orelse
|
||||
revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{});
|
||||
},
|
||||
.Enum => {
|
||||
const enum_ty = @ptrCast(*const ZigClangEnumType, ty);
|
||||
@@ -2205,6 +3675,10 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
|
||||
const attributed_ty = @ptrCast(*const ZigClangAttributedType, ty);
|
||||
return transQualType(rp, ZigClangAttributedType_getEquivalentType(attributed_ty), source_loc);
|
||||
},
|
||||
.MacroQualified => {
|
||||
const macroqualified_ty = @ptrCast(*const ZigClangMacroQualifiedType, ty);
|
||||
return transQualType(rp, ZigClangMacroQualifiedType_getModifiedType(macroqualified_ty), source_loc);
|
||||
},
|
||||
else => {
|
||||
const type_name = rp.c.str(ZigClangType_getTypeClassName(ty));
|
||||
return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", .{type_name});
|
||||
@@ -2212,22 +3686,6 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
|
||||
}
|
||||
}
|
||||
|
||||
fn getContainerName(rp: RestorePoint, record_decl: *const ZigClangRecordDecl) !?[]const u8 {
|
||||
const bare_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, record_decl)));
|
||||
|
||||
const container_kind_name = if (ZigClangRecordDecl_isUnion(record_decl))
|
||||
"union"
|
||||
else if (ZigClangRecordDecl_isStruct(record_decl))
|
||||
"struct"
|
||||
else
|
||||
return revertAndWarn(rp, error.UnsupportedType, ZigClangRecordDecl_getLocation(record_decl), "record {} is not a struct or union", .{bare_name});
|
||||
|
||||
if (ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) or bare_name.len == 0)
|
||||
return null;
|
||||
|
||||
return try std.fmt.allocPrint(rp.c.a(), "{}_{}", .{ container_kind_name, bare_name });
|
||||
}
|
||||
|
||||
fn isCVoid(qt: ZigClangQualType) bool {
|
||||
const ty = ZigClangQualType_getTypePtr(qt);
|
||||
if (ZigClangType_getTypeClass(ty) == .Builtin) {
|
||||
@@ -2241,7 +3699,6 @@ const FnDeclContext = struct {
|
||||
fn_name: []const u8,
|
||||
has_body: bool,
|
||||
storage_class: ZigClangStorageClass,
|
||||
scope: **Scope,
|
||||
is_export: bool,
|
||||
};
|
||||
|
||||
@@ -2307,9 +3764,6 @@ 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;
|
||||
@@ -2335,15 +3789,11 @@ fn finishTransFnProto(
|
||||
const param_name_tok: ?ast.TokenIndex = blk: {
|
||||
if (fn_decl != null) {
|
||||
const param = ZigClangFunctionDecl_getParamDecl(fn_decl.?, @intCast(c_uint, i));
|
||||
var param_name: []const u8 = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, param)));
|
||||
const 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;
|
||||
break :blk null;
|
||||
|
||||
const result = try appendIdentifier(rp.c, checked_param_name);
|
||||
const result = try appendIdentifier(rp.c, param_name);
|
||||
_ = try appendToken(rp.c, .Colon, ":");
|
||||
break :blk result;
|
||||
}
|
||||
@@ -2410,14 +3860,13 @@ fn finishTransFnProto(
|
||||
};
|
||||
|
||||
const fn_proto = try rp.c.a().create(ast.Node.FnProto);
|
||||
fn_proto.* = ast.Node.FnProto{
|
||||
.base = ast.Node{ .id = ast.Node.Id.FnProto },
|
||||
fn_proto.* = .{
|
||||
.doc_comments = null,
|
||||
.visib_token = pub_tok,
|
||||
.fn_token = fn_tok,
|
||||
.name_token = name_tok,
|
||||
.params = fn_params,
|
||||
.return_type = ast.Node.FnProto.ReturnType{ .Explicit = return_type_node },
|
||||
.return_type = .{ .Explicit = return_type_node },
|
||||
.var_args_token = null, // TODO this field is broken in the AST data model
|
||||
.extern_export_inline_token = extern_export_inline_tok,
|
||||
.cc_token = cc_tok,
|
||||
@@ -2523,6 +3972,41 @@ fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8,
|
||||
return token_index;
|
||||
}
|
||||
|
||||
// TODO hook up with codegen
|
||||
fn isZigPrimitiveType(name: []const u8) bool {
|
||||
if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) {
|
||||
for (name[1..]) |c| {
|
||||
switch (c) {
|
||||
'0'...'9' => {},
|
||||
else => return false,
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// void is invalid in c so it doesn't need to be checked.
|
||||
return mem.eql(u8, name, "comptime_float") or
|
||||
mem.eql(u8, name, "comptime_int") or
|
||||
mem.eql(u8, name, "bool") or
|
||||
mem.eql(u8, name, "isize") or
|
||||
mem.eql(u8, name, "usize") or
|
||||
mem.eql(u8, name, "f16") or
|
||||
mem.eql(u8, name, "f32") or
|
||||
mem.eql(u8, name, "f64") or
|
||||
mem.eql(u8, name, "f128") or
|
||||
mem.eql(u8, name, "c_longdouble") or
|
||||
mem.eql(u8, name, "noreturn") or
|
||||
mem.eql(u8, name, "type") or
|
||||
mem.eql(u8, name, "anyerror") or
|
||||
mem.eql(u8, name, "c_short") or
|
||||
mem.eql(u8, name, "c_ushort") or
|
||||
mem.eql(u8, name, "c_int") or
|
||||
mem.eql(u8, name, "c_uint") or
|
||||
mem.eql(u8, name, "c_long") or
|
||||
mem.eql(u8, name, "c_ulong") or
|
||||
mem.eql(u8, name, "c_longlong") or
|
||||
mem.eql(u8, name, "c_ulonglong");
|
||||
}
|
||||
|
||||
fn isValidZigIdentifier(name: []const u8) bool {
|
||||
for (name) |c, i| {
|
||||
switch (c) {
|
||||
@@ -2573,27 +4057,31 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
|
||||
const begin_loc = ZigClangMacroDefinitionRecord_getSourceRange_getBegin(macro);
|
||||
|
||||
const name = try c.str(raw_name);
|
||||
if (scope.contains(name)) {
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/3756
|
||||
// TODO https://github.com/ziglang/zig/issues/1802
|
||||
const checked_name = if (isZigPrimitiveType(name)) try std.fmt.allocPrint(c.a(), "_{}", .{name}) else name;
|
||||
if (scope.contains(checked_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", .{});
|
||||
try failDecl(c, begin_loc, checked_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));
|
||||
assert(first_tok.id == .Identifier and 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)) {
|
||||
if (mem.eql(u8, checked_name, next.bytes)) {
|
||||
continue;
|
||||
}
|
||||
},
|
||||
@@ -2610,12 +4098,12 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
|
||||
} else false;
|
||||
|
||||
(if (macro_fn)
|
||||
transMacroFnDefine(c, &tok_it, name, begin_loc)
|
||||
transMacroFnDefine(c, &tok_it, checked_name, begin_loc)
|
||||
else
|
||||
transMacroDefine(c, &tok_it, name, begin_loc)) catch |err| switch (err) {
|
||||
transMacroDefine(c, &tok_it, checked_name, begin_loc)) catch |err| switch (err) {
|
||||
error.UnsupportedTranslation,
|
||||
error.ParseError,
|
||||
=> try failDecl(c, begin_loc, name, "unable to translate macro", .{}),
|
||||
=> try failDecl(c, begin_loc, checked_name, "unable to translate macro", .{}),
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
},
|
||||
@@ -2628,37 +4116,28 @@ fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8,
|
||||
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 node = try transCreateNodeVarDecl(c, true, true, name);
|
||||
node.eq_token = try appendToken(c, .Equal, "=");
|
||||
|
||||
const init_node = try parseCExpr(rp, it, source_loc, scope);
|
||||
node.init_node = try parseCExpr(rp, it, source_loc, scope);
|
||||
const last = it.next().?;
|
||||
if (last.id != .Eof)
|
||||
return revertAndWarn(
|
||||
rp,
|
||||
error.UnsupportedTranslation,
|
||||
source_loc,
|
||||
"unable to translate C expr, unexpected token: {}",
|
||||
.{last.id},
|
||||
);
|
||||
|
||||
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, ";"),
|
||||
};
|
||||
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 block_scope = try Scope.Block.init(c, &c.global_scope.base, null);
|
||||
const scope = &block_scope.base;
|
||||
|
||||
const pub_tok = try appendToken(c, .Keyword_pub, "pub");
|
||||
const inline_tok = try appendToken(c, .Keyword_inline, "inline");
|
||||
@@ -2676,7 +4155,7 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u
|
||||
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 });
|
||||
try block_scope.variables.push(.{ .name = param_tok.bytes, .alias = alias });
|
||||
break :blk alias;
|
||||
} else param_tok.bytes;
|
||||
|
||||
@@ -2737,6 +4216,15 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u
|
||||
|
||||
const return_expr = try transCreateNodeReturnExpr(c);
|
||||
const expr = try parseCExpr(rp, it, source_loc, scope);
|
||||
const last = it.next().?;
|
||||
if (last.id != .Eof)
|
||||
return revertAndWarn(
|
||||
rp,
|
||||
error.UnsupportedTranslation,
|
||||
source_loc,
|
||||
"unable to translate C expr, unexpected token: {}",
|
||||
.{last.id},
|
||||
);
|
||||
_ = try appendToken(c, .Semicolon, ";");
|
||||
try type_of.params.push(expr);
|
||||
return_expr.rhs = expr;
|
||||
@@ -2838,7 +4326,10 @@ fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc:
|
||||
|
||||
if (it.peek().?.id == .RParen) {
|
||||
_ = it.next();
|
||||
return inner_node;
|
||||
if (it.peek().?.id != .LParen) {
|
||||
return inner_node;
|
||||
}
|
||||
_ = it.next();
|
||||
}
|
||||
|
||||
// hack to get zig fmt to render a comma in builtin calls
|
||||
@@ -2930,8 +4421,8 @@ fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc:
|
||||
rp,
|
||||
error.UnsupportedTranslation,
|
||||
source_loc,
|
||||
"unable to translate C expr",
|
||||
.{},
|
||||
"unable to translate C expr, unexpected token: {}",
|
||||
.{tok.id},
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -2944,24 +4435,17 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
|
||||
.Dot => {
|
||||
const name_tok = it.next().?;
|
||||
if (name_tok.id != .Identifier)
|
||||
return revertAndWarn(
|
||||
rp,
|
||||
error.ParseError,
|
||||
source_loc,
|
||||
"unable to translate C expr",
|
||||
.{},
|
||||
);
|
||||
return error.ParseError;
|
||||
|
||||
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;
|
||||
node = try transCreateNodeFieldAccess(rp.c, node, name_tok.bytes);
|
||||
},
|
||||
.Arrow => {
|
||||
const name_tok = it.next().?;
|
||||
if (name_tok.id != .Identifier)
|
||||
return error.ParseError;
|
||||
|
||||
const deref = try transCreateNodePtrDeref(rp.c, node);
|
||||
node = try transCreateNodeFieldAccess(rp.c, deref, name_tok.bytes);
|
||||
},
|
||||
.Asterisk => {
|
||||
if (it.peek().?.id == .RParen) {
|
||||
@@ -2977,19 +4461,19 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
|
||||
// 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.* = .{
|
||||
const mul_node = try rp.c.a().create(ast.Node.InfixOp);
|
||||
mul_node.* = .{
|
||||
.op_token = op_token,
|
||||
.lhs = node,
|
||||
.op = .BitShiftLeft,
|
||||
.rhs = rhs,
|
||||
};
|
||||
node = &bitshift_node.base;
|
||||
node = &mul_node.base;
|
||||
}
|
||||
},
|
||||
.Shl => {
|
||||
const op_token = try appendToken(rp.c, .AngleBracketAngleBracketLeft, "<<");
|
||||
const rhs = try parseCPrimaryExpr(rp, it, source_loc, scope);
|
||||
const rhs = try parseCExpr(rp, it, source_loc, scope);
|
||||
const bitshift_node = try rp.c.a().create(ast.Node.InfixOp);
|
||||
bitshift_node.* = .{
|
||||
.op_token = op_token,
|
||||
@@ -2999,6 +4483,42 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
|
||||
};
|
||||
node = &bitshift_node.base;
|
||||
},
|
||||
.Pipe => {
|
||||
const op_token = try appendToken(rp.c, .Pipe, "|");
|
||||
const rhs = try parseCExpr(rp, it, source_loc, scope);
|
||||
const or_node = try rp.c.a().create(ast.Node.InfixOp);
|
||||
or_node.* = .{
|
||||
.op_token = op_token,
|
||||
.lhs = node,
|
||||
.op = .BitOr,
|
||||
.rhs = rhs,
|
||||
};
|
||||
node = &or_node.base;
|
||||
},
|
||||
.LBrace => {
|
||||
const arr_node = try transCreateNodeArrayAccess(rp.c, node);
|
||||
arr_node.op.ArrayAccess = try parseCExpr(rp, it, source_loc, scope);
|
||||
arr_node.rtoken = try appendToken(rp.c, .RBrace, "]");
|
||||
node = &arr_node.base;
|
||||
if (it.next().?.id != .RBrace)
|
||||
return error.ParseError;
|
||||
},
|
||||
.LParen => {
|
||||
const call_node = try transCreateNodeFnCall(rp.c, node);
|
||||
while (true) {
|
||||
const arg = try parseCExpr(rp, it, source_loc, scope);
|
||||
try call_node.op.Call.params.push(arg);
|
||||
const next = it.next().?;
|
||||
if (next.id == .Comma)
|
||||
_ = try appendToken(rp.c, .Comma, ",")
|
||||
else if (next.id == .RParen)
|
||||
break
|
||||
else
|
||||
return error.ParseError;
|
||||
}
|
||||
call_node.rtoken = try appendToken(rp.c, .RParen, ")");
|
||||
node = &call_node.base;
|
||||
},
|
||||
else => {
|
||||
_ = it.prev();
|
||||
return node;
|
||||
@@ -3028,13 +4548,7 @@ fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
|
||||
},
|
||||
.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;
|
||||
return try transCreateNodePtrDeref(rp.c, prefix_op_expr);
|
||||
},
|
||||
else => {
|
||||
_ = it.prev();
|
||||
@@ -3043,24 +4557,32 @@ fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
|
||||
}
|
||||
}
|
||||
|
||||
fn tokenSlice(c: *Context, token: ast.TokenIndex) []const u8 {
|
||||
fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {
|
||||
const tok = c.tree.tokens.at(token);
|
||||
return c.source_buffer.toSliceConst()[tok.start..tok.end];
|
||||
return c.source_buffer.toSlice()[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;
|
||||
fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
|
||||
if (node.id == .ContainerDecl) {
|
||||
return node;
|
||||
} else if (node.id == .PrefixOp) {
|
||||
return node;
|
||||
} else if (node.cast(ast.Node.Identifier)) |ident| {
|
||||
if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| {
|
||||
if (kv.value.cast(ast.Node.VarDecl)) |var_decl|
|
||||
return getContainer(c, var_decl.init_node.?);
|
||||
}
|
||||
} else if (node.cast(ast.Node.InfixOp)) |infix| {
|
||||
if (infix.op != .Period)
|
||||
return null;
|
||||
if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
|
||||
if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
|
||||
var it = container.fields_and_decls.iterator(0);
|
||||
while (it.next()) |field_ref| {
|
||||
const field = field_ref.*.cast(ast.Node.ContainerField).?;
|
||||
const ident = infix.rhs.cast(ast.Node.Identifier).?;
|
||||
if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
|
||||
return getContainer(c, field.type_expr.?);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3069,10 +4591,52 @@ fn getFnDecl(c: *Context, ref: *ast.Node) ?*ast.Node {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
|
||||
if (ref.cast(ast.Node.Identifier)) |ident| {
|
||||
if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| {
|
||||
if (kv.value.cast(ast.Node.VarDecl)) |var_decl| {
|
||||
if (var_decl.type_node) |ty|
|
||||
return getContainer(c, ty);
|
||||
}
|
||||
}
|
||||
} else if (ref.cast(ast.Node.InfixOp)) |infix| {
|
||||
if (infix.op != .Period)
|
||||
return null;
|
||||
if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
|
||||
if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
|
||||
var it = container.fields_and_decls.iterator(0);
|
||||
while (it.next()) |field_ref| {
|
||||
const field = field_ref.*.cast(ast.Node.ContainerField).?;
|
||||
const ident = infix.rhs.cast(ast.Node.Identifier).?;
|
||||
if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
|
||||
return getContainer(c, field.type_expr.?);
|
||||
}
|
||||
}
|
||||
} else
|
||||
return ty_node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
|
||||
const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null;
|
||||
if (getContainerTypeOf(c, init)) |ty_node| {
|
||||
if (ty_node.cast(ast.Node.PrefixOp)) |prefix| {
|
||||
if (prefix.op == .OptionalType) {
|
||||
if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
|
||||
return fn_proto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 (getFnProto(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.
|
||||
|
||||
@@ -1571,6 +1571,16 @@ const ZigClangTypedefNameDecl *ZigClangTypedefNameDecl_getCanonicalDecl(const Zi
|
||||
return reinterpret_cast<const ZigClangTypedefNameDecl *>(decl);
|
||||
}
|
||||
|
||||
const ZigClangFunctionDecl *ZigClangFunctionDecl_getCanonicalDecl(const ZigClangFunctionDecl *self) {
|
||||
const clang::FunctionDecl *decl = reinterpret_cast<const clang::FunctionDecl*>(self)->getCanonicalDecl();
|
||||
return reinterpret_cast<const ZigClangFunctionDecl *>(decl);
|
||||
}
|
||||
|
||||
const ZigClangVarDecl *ZigClangVarDecl_getCanonicalDecl(const ZigClangVarDecl *self) {
|
||||
const clang::VarDecl *decl = reinterpret_cast<const clang::VarDecl*>(self)->getCanonicalDecl();
|
||||
return reinterpret_cast<const ZigClangVarDecl *>(decl);
|
||||
}
|
||||
|
||||
const ZigClangRecordDecl *ZigClangRecordDecl_getDefinition(const ZigClangRecordDecl *zig_record_decl) {
|
||||
const clang::RecordDecl *record_decl = reinterpret_cast<const clang::RecordDecl *>(zig_record_decl);
|
||||
const clang::RecordDecl *definition = record_decl->getDefinition();
|
||||
@@ -2161,6 +2171,11 @@ unsigned ZigClangAPFloat_convertToHexString(const ZigClangAPFloat *self, char *D
|
||||
return casted->convertToHexString(DST, HexDigits, UpperCase, (llvm::APFloat::roundingMode)RM);
|
||||
}
|
||||
|
||||
double ZigClangAPFloat_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self) {
|
||||
auto casted = reinterpret_cast<const clang::FloatingLiteral *>(self);
|
||||
return casted->getValueAsApproximateDouble();
|
||||
}
|
||||
|
||||
enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self) {
|
||||
auto casted = reinterpret_cast<const clang::StringLiteral *>(self);
|
||||
return (ZigClangStringLiteral_StringKind)casted->getKind();
|
||||
|
||||
@@ -856,6 +856,8 @@ ZIG_EXTERN_C const struct ZigClangEnumDecl *ZigClangEnumType_getDecl(const struc
|
||||
ZIG_EXTERN_C const struct ZigClangTagDecl *ZigClangRecordDecl_getCanonicalDecl(const struct ZigClangRecordDecl *record_decl);
|
||||
ZIG_EXTERN_C const struct ZigClangTagDecl *ZigClangEnumDecl_getCanonicalDecl(const struct ZigClangEnumDecl *);
|
||||
ZIG_EXTERN_C const struct ZigClangTypedefNameDecl *ZigClangTypedefNameDecl_getCanonicalDecl(const struct ZigClangTypedefNameDecl *);
|
||||
ZIG_EXTERN_C const struct ZigClangFunctionDecl *ZigClangFunctionDecl_getCanonicalDecl(const ZigClangFunctionDecl *self);
|
||||
ZIG_EXTERN_C const struct ZigClangVarDecl *ZigClangVarDecl_getCanonicalDecl(const ZigClangVarDecl *self);
|
||||
|
||||
ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangRecordDecl_getDefinition(const struct ZigClangRecordDecl *);
|
||||
ZIG_EXTERN_C const struct ZigClangEnumDecl *ZigClangEnumDecl_getDefinition(const struct ZigClangEnumDecl *);
|
||||
@@ -985,6 +987,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st
|
||||
|
||||
ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST,
|
||||
unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM);
|
||||
ZIG_EXTERN_C double ZigClangAPFloat_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self);
|
||||
|
||||
ZIG_EXTERN_C enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self);
|
||||
ZIG_EXTERN_C const char *ZigClangStringLiteral_getString_bytes_begin_size(const struct ZigClangStringLiteral *self,
|
||||
|
||||
+2124
-1042
@@ -127,16 +127,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
});
|
||||
|
||||
cases.addC_both("add, sub, mul, div, rem",
|
||||
\\int s(int a, int b) {
|
||||
\\ int c;
|
||||
\\int s() {
|
||||
\\ int a, b, c;
|
||||
\\ c = a + b;
|
||||
\\ c = a - b;
|
||||
\\ c = a * b;
|
||||
\\ c = a / b;
|
||||
\\ c = a % b;
|
||||
\\}
|
||||
\\unsigned u(unsigned a, unsigned b) {
|
||||
\\ unsigned c;
|
||||
\\unsigned u() {
|
||||
\\ unsigned a, b, c;
|
||||
\\ c = a + b;
|
||||
\\ c = a - b;
|
||||
\\ c = a * b;
|
||||
@@ -144,7 +144,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\ c = a % b;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn s(a: c_int, b: c_int) c_int {
|
||||
\\pub export fn s() c_int {
|
||||
\\ var a: c_int = undefined;
|
||||
\\ var b: c_int = undefined;
|
||||
\\ var c: c_int = undefined;
|
||||
\\ c = (a + b);
|
||||
\\ c = (a - b);
|
||||
@@ -152,7 +154,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\ c = @divTrunc(a, b);
|
||||
\\ c = @rem(a, b);
|
||||
\\}
|
||||
\\pub export fn u(a: c_uint, b: c_uint) c_uint {
|
||||
\\pub export fn u() c_uint {
|
||||
\\ var a: c_uint = undefined;
|
||||
\\ var b: c_uint = undefined;
|
||||
\\ var c: c_uint = undefined;
|
||||
\\ c = (a +% b);
|
||||
\\ c = (a -% b);
|
||||
@@ -162,50 +166,6 @@ 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 {
|
||||
@@ -228,7 +188,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\ struct Foo *foo;
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_Foo = @OpaqueType()
|
||||
\\pub const struct_Foo = @OpaqueType();
|
||||
,
|
||||
\\pub const struct_Bar = extern struct {
|
||||
\\ foo: ?*struct_Foo,
|
||||
@@ -426,6 +386,432 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
},
|
||||
);
|
||||
|
||||
cases.addC_both("null statements",
|
||||
\\void foo(void) {
|
||||
\\ ;;;;;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ {}
|
||||
\\ {}
|
||||
\\ {}
|
||||
\\ {}
|
||||
\\ {}
|
||||
\\}
|
||||
});
|
||||
|
||||
if (builtin.os != builtin.Os.windows) {
|
||||
// Windows treats this as an enum with type c_int
|
||||
cases.add_both("big negative enum init values when C ABI supports long long enums",
|
||||
\\enum EnumWithInits {
|
||||
\\ VAL01 = 0,
|
||||
\\ VAL02 = 1,
|
||||
\\ VAL03 = 2,
|
||||
\\ VAL04 = 3,
|
||||
\\ VAL05 = -1,
|
||||
\\ VAL06 = -2,
|
||||
\\ VAL07 = -3,
|
||||
\\ VAL08 = -4,
|
||||
\\ VAL09 = VAL02 + VAL08,
|
||||
\\ VAL10 = -1000012000,
|
||||
\\ VAL11 = -1000161000,
|
||||
\\ VAL12 = -1000174001,
|
||||
\\ VAL13 = VAL09,
|
||||
\\ VAL14 = VAL10,
|
||||
\\ VAL15 = VAL11,
|
||||
\\ VAL16 = VAL13,
|
||||
\\ VAL17 = (VAL16 - VAL10 + 1),
|
||||
\\ VAL18 = 0x1000000000000000L,
|
||||
\\ VAL19 = VAL18 + VAL18 + VAL18 - 1,
|
||||
\\ VAL20 = VAL19 + VAL19,
|
||||
\\ VAL21 = VAL20 + 0xFFFFFFFFFFFFFFFF,
|
||||
\\ VAL22 = 0xFFFFFFFFFFFFFFFF + 1,
|
||||
\\ VAL23 = 0xFFFFFFFFFFFFFFFF,
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const enum_EnumWithInits = extern enum(c_longlong) {
|
||||
\\ VAL01 = 0,
|
||||
\\ VAL02 = 1,
|
||||
\\ VAL03 = 2,
|
||||
\\ VAL04 = 3,
|
||||
\\ VAL05 = -1,
|
||||
\\ VAL06 = -2,
|
||||
\\ VAL07 = -3,
|
||||
\\ VAL08 = -4,
|
||||
\\ VAL09 = -3,
|
||||
\\ VAL10 = -1000012000,
|
||||
\\ VAL11 = -1000161000,
|
||||
\\ VAL12 = -1000174001,
|
||||
\\ VAL13 = -3,
|
||||
\\ VAL14 = -1000012000,
|
||||
\\ VAL15 = -1000161000,
|
||||
\\ VAL16 = -3,
|
||||
\\ VAL17 = 1000011998,
|
||||
\\ VAL18 = 1152921504606846976,
|
||||
\\ VAL19 = 3458764513820540927,
|
||||
\\ VAL20 = 6917529027641081854,
|
||||
\\ VAL21 = 6917529027641081853,
|
||||
\\ VAL22 = 0,
|
||||
\\ VAL23 = -1,
|
||||
\\};
|
||||
});
|
||||
}
|
||||
|
||||
cases.addC_both("predefined expressions",
|
||||
\\void foo(void) {
|
||||
\\ __func__;
|
||||
\\ __FUNCTION__;
|
||||
\\ __PRETTY_FUNCTION__;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ _ = "foo";
|
||||
\\ _ = "foo";
|
||||
\\ _ = "void foo(void)";
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("ignore result, no function arguments",
|
||||
\\void foo() {
|
||||
\\ int a;
|
||||
\\ 1;
|
||||
\\ "hey";
|
||||
\\ 1 + 1;
|
||||
\\ 1 - 1;
|
||||
\\ a = 1;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ var a: c_int = undefined;
|
||||
\\ _ = 1;
|
||||
\\ _ = "hey";
|
||||
\\ _ = (1 + 1);
|
||||
\\ _ = (1 - 1);
|
||||
\\ a = 1;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_both("constant size array",
|
||||
\\void func(int array[20]);
|
||||
, &[_][]const u8{
|
||||
\\pub extern fn func(array: [*c]c_int) void;
|
||||
});
|
||||
|
||||
cases.add_both("__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.addC_both("void cast",
|
||||
\\void foo() {
|
||||
\\ int a;
|
||||
\\ (void) a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ var a: c_int = undefined;
|
||||
\\ _ = a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("implicit cast to void *",
|
||||
\\void *foo() {
|
||||
\\ unsigned short *x;
|
||||
\\ return x;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() ?*c_void {
|
||||
\\ var x: [*c]c_ushort = undefined;
|
||||
\\ return @ptrCast(?*c_void, x);
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("null pointer implicit cast",
|
||||
\\int* foo(void) {
|
||||
\\ return 0;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() [*c]c_int {
|
||||
\\ return null;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_both("simple union",
|
||||
\\union Foo {
|
||||
\\ int x;
|
||||
\\ double y;
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const union_Foo = extern union {
|
||||
\\ x: c_int,
|
||||
\\ y: f64,
|
||||
\\};
|
||||
,
|
||||
\\pub const Foo = union_Foo;
|
||||
});
|
||||
|
||||
cases.addC_both("string literal",
|
||||
\\const char *foo(void) {
|
||||
\\ return "bar";
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() [*c]const u8 {
|
||||
\\ return "bar";
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("return void",
|
||||
\\void foo(void) {
|
||||
\\ return;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ return;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("for loop",
|
||||
\\void foo(void) {
|
||||
\\ for (int i = 0; i; i = i + 1) { }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ {
|
||||
\\ var i: c_int = 0;
|
||||
\\ while (i != 0) : (i = (i + 1)) {}
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("empty for loop",
|
||||
\\void foo(void) {
|
||||
\\ for (;;) { }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ while (true) {}
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("break statement",
|
||||
\\void foo(void) {
|
||||
\\ for (;;) {
|
||||
\\ break;
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ while (true) {
|
||||
\\ break;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("continue statement",
|
||||
\\void foo(void) {
|
||||
\\ for (;;) {
|
||||
\\ continue;
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ while (true) {
|
||||
\\ continue;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("pointer casting",
|
||||
\\float *ptrcast() {
|
||||
\\ int *a;
|
||||
\\ return (float *)a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn ptrcast() [*c]f32 {
|
||||
\\ var a: [*c]c_int = undefined;
|
||||
\\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("pointer conversion with different alignment",
|
||||
\\void test_ptr_cast() {
|
||||
\\ void *p;
|
||||
\\ {
|
||||
\\ char *to_char = (char *)p;
|
||||
\\ short *to_short = (short *)p;
|
||||
\\ int *to_int = (int *)p;
|
||||
\\ long long *to_longlong = (long long *)p;
|
||||
\\ }
|
||||
\\ {
|
||||
\\ char *to_char = p;
|
||||
\\ short *to_short = p;
|
||||
\\ int *to_int = p;
|
||||
\\ long long *to_longlong = p;
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn test_ptr_cast() void {
|
||||
\\ var p: ?*c_void = undefined;
|
||||
\\ {
|
||||
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
|
||||
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
|
||||
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
|
||||
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
|
||||
\\ }
|
||||
\\ {
|
||||
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
|
||||
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
|
||||
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
|
||||
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("while on non-bool",
|
||||
\\int while_none_bool() {
|
||||
\\ int a;
|
||||
\\ float b;
|
||||
\\ void *c;
|
||||
\\ while (a) return 0;
|
||||
\\ while (b) return 1;
|
||||
\\ while (c) return 2;
|
||||
\\ return 3;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn while_none_bool() c_int {
|
||||
\\ var a: c_int = undefined;
|
||||
\\ var b: f32 = undefined;
|
||||
\\ var c: ?*c_void = undefined;
|
||||
\\ while (a != 0) return 0;
|
||||
\\ while (b != 0) return 1;
|
||||
\\ while (c != null) return 2;
|
||||
\\ return 3;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("for on non-bool",
|
||||
\\int for_none_bool() {
|
||||
\\ int a;
|
||||
\\ float b;
|
||||
\\ void *c;
|
||||
\\ for (;a;) return 0;
|
||||
\\ for (;b;) return 1;
|
||||
\\ for (;c;) return 2;
|
||||
\\ return 3;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn for_none_bool() c_int {
|
||||
\\ var a: c_int = undefined;
|
||||
\\ var b: f32 = undefined;
|
||||
\\ var c: ?*c_void = undefined;
|
||||
\\ while (a != 0) return 0;
|
||||
\\ while (b != 0) return 1;
|
||||
\\ while (c != null) return 2;
|
||||
\\ return 3;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("bitshift",
|
||||
\\int foo(void) {
|
||||
\\ return (1 << 2) >> 1;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() c_int {
|
||||
\\ return (1 << @as(@import("std").math.Log2Int(c_int), 2)) >> @as(@import("std").math.Log2Int(c_int), 1);
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("sizeof",
|
||||
\\#include <stddef.h>
|
||||
\\size_t size_of(void) {
|
||||
\\ return sizeof(int);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn size_of() usize {
|
||||
\\ return @sizeOf(c_int);
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("normal deref",
|
||||
\\void foo() {
|
||||
\\ int *x;
|
||||
\\ *x = 1;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ var x: [*c]c_int = undefined;
|
||||
\\ x.?.* = 1;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("address of operator",
|
||||
\\int foo(void) {
|
||||
\\ int x = 1234;
|
||||
\\ int *ptr = &x;
|
||||
\\ return *ptr;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() c_int {
|
||||
\\ var x: c_int = 1234;
|
||||
\\ var ptr: [*c]c_int = &x;
|
||||
\\ return ptr.?.*;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("bin not",
|
||||
\\int foo() {
|
||||
\\ int x;
|
||||
\\ return ~x;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() c_int {
|
||||
\\ var x: c_int = undefined;
|
||||
\\ return ~x;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC_both("bool not",
|
||||
\\int foo() {
|
||||
\\ int a;
|
||||
\\ float b;
|
||||
\\ void *c;
|
||||
\\ return !(a == 0);
|
||||
\\ return !a;
|
||||
\\ return !b;
|
||||
\\ return !c;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() c_int {
|
||||
\\ var a: c_int = undefined;
|
||||
\\ var b: f32 = undefined;
|
||||
\\ var c: ?*c_void = undefined;
|
||||
\\ return !(a == 0);
|
||||
\\ return !(a != 0);
|
||||
\\ return !(b != 0);
|
||||
\\ return !(c != null);
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("__extension__ cast",
|
||||
\\int foo(void) {
|
||||
\\ return __extension__ 1;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() c_int {
|
||||
\\ return 1;
|
||||
\\}
|
||||
});
|
||||
|
||||
if (builtin.os != builtin.Os.windows) {
|
||||
// sysv_abi not currently supported on windows
|
||||
cases.add_both("Macro qualified functions",
|
||||
\\void __attribute__((sysv_abi)) foo(void);
|
||||
, &[_][]const u8{
|
||||
\\pub extern fn foo() void;
|
||||
});
|
||||
}
|
||||
|
||||
/////////////// Cases that pass for only stage2 ////////////////
|
||||
|
||||
cases.add_2("Parameterless function prototypes",
|
||||
@@ -491,28 +877,28 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\ p,
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const a = enum_unnamed_1.a;
|
||||
\\pub const b = enum_unnamed_1.b;
|
||||
\\pub const c = enum_unnamed_1.c;
|
||||
\\pub const enum_unnamed_1 = extern enum {
|
||||
\\pub const a = 0;
|
||||
\\pub const b = 1;
|
||||
\\pub const c = 2;
|
||||
\\const enum_unnamed_1 = extern enum {
|
||||
\\ a,
|
||||
\\ b,
|
||||
\\ c,
|
||||
\\};
|
||||
\\pub const d = enum_unnamed_1;
|
||||
\\pub const e = enum_unnamed_2.e;
|
||||
\\pub const f = enum_unnamed_2.f;
|
||||
\\pub const g = enum_unnamed_2.g;
|
||||
\\pub const enum_unnamed_2 = extern enum {
|
||||
\\pub const e = 0;
|
||||
\\pub const f = 4;
|
||||
\\pub const g = 5;
|
||||
\\const enum_unnamed_2 = extern enum {
|
||||
\\ e = 0,
|
||||
\\ f = 4,
|
||||
\\ g = 5,
|
||||
\\};
|
||||
\\pub export var h: enum_unnamed_2 = @as(enum_unnamed_2, e);
|
||||
\\pub const i = enum_unnamed_3.i;
|
||||
\\pub const j = enum_unnamed_3.j;
|
||||
\\pub const k = enum_unnamed_3.k;
|
||||
\\pub const enum_unnamed_3 = extern enum {
|
||||
\\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);
|
||||
\\pub const i = 0;
|
||||
\\pub const j = 1;
|
||||
\\pub const k = 2;
|
||||
\\const enum_unnamed_3 = extern enum {
|
||||
\\ i,
|
||||
\\ j,
|
||||
\\ k,
|
||||
@@ -521,9 +907,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\ l: enum_unnamed_3,
|
||||
\\ m: d,
|
||||
\\};
|
||||
\\pub const n = enum_i.n;
|
||||
\\pub const o = enum_i.o;
|
||||
\\pub const p = enum_i.p;
|
||||
\\pub const n = 0;
|
||||
\\pub const o = 1;
|
||||
\\pub const p = 2;
|
||||
\\pub const enum_i = extern enum {
|
||||
\\ n,
|
||||
\\ o,
|
||||
@@ -622,25 +1008,22 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\#define glClearPFN PFNGLCLEARPROC
|
||||
, &[_][]const u8{
|
||||
\\pub const GLbitfield = c_uint;
|
||||
,
|
||||
\\pub const PFNGLCLEARPROC = ?extern fn (GLbitfield) void;
|
||||
,
|
||||
\\pub const OpenGLProc = ?extern fn () void;
|
||||
,
|
||||
\\const struct_unnamed_1 = extern struct {
|
||||
\\ Clear: PFNGLCLEARPROC,
|
||||
\\};
|
||||
\\pub const union_OpenGLProcs = extern union {
|
||||
\\ ptr: [1]OpenGLProc,
|
||||
\\ gl: extern struct {
|
||||
\\ Clear: PFNGLCLEARPROC,
|
||||
\\ },
|
||||
\\ gl: struct_unnamed_1,
|
||||
\\};
|
||||
,
|
||||
\\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 inline fn glClearUnion(arg_2: GLbitfield) void {
|
||||
\\ return glProcs.gl.Clear.?(arg_2);
|
||||
\\}
|
||||
,
|
||||
\\pub const OpenGLProcs = union_OpenGLProcs;
|
||||
});
|
||||
@@ -662,13 +1045,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("macro escape sequences",
|
||||
cases.add_2("macro defines string literal with hex",
|
||||
\\#define FOO "aoeu\xab derp"
|
||||
\\#define FOO2 "aoeu\a derp"
|
||||
\\#define FOO2 "aoeu\x0007a derp"
|
||||
\\#define FOO_CHAR '\xfF'
|
||||
, &[_][]const u8{
|
||||
\\pub const FOO = "aoeu\xab derp";
|
||||
,
|
||||
\\pub const FOO2 = "aoeu\x07 derp";
|
||||
\\pub const FOO2 = "aoeu\x7a derp";
|
||||
,
|
||||
\\pub const FOO_CHAR = '\xff';
|
||||
});
|
||||
|
||||
cases.add_2("variable aliasing",
|
||||
@@ -688,7 +1074,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\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 {
|
||||
\\pub export fn foo(_arg_c_1: u8) void {
|
||||
\\ var c_1 = _arg_c_1;
|
||||
\\ var a_2: c_int = undefined;
|
||||
\\ var b_3: u8 = @as(u8, 123);
|
||||
\\ b_3 = @as(u8, a_2);
|
||||
@@ -699,96 +1086,1258 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
});
|
||||
|
||||
/////////////// 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",
|
||||
\\enum EnumWithInits {
|
||||
\\ VAL01 = 0,
|
||||
\\ VAL02 = 1,
|
||||
\\ VAL03 = 2,
|
||||
\\ VAL04 = 3,
|
||||
\\ VAL05 = -1,
|
||||
\\ VAL06 = -2,
|
||||
\\ VAL07 = -3,
|
||||
\\ VAL08 = -4,
|
||||
\\ VAL09 = VAL02 + VAL08,
|
||||
\\ VAL10 = -1000012000,
|
||||
\\ VAL11 = -1000161000,
|
||||
\\ VAL12 = -1000174001,
|
||||
\\ VAL13 = VAL09,
|
||||
\\ VAL14 = VAL10,
|
||||
\\ VAL15 = VAL11,
|
||||
\\ VAL16 = VAL13,
|
||||
\\ VAL17 = (VAL16 - VAL10 + 1),
|
||||
\\ VAL18 = 0x1000000000000000L,
|
||||
\\ VAL19 = VAL18 + VAL18 + VAL18 - 1,
|
||||
\\ VAL20 = VAL19 + VAL19,
|
||||
\\ VAL21 = VAL20 + 0xFFFFFFFFFFFFFFFF,
|
||||
\\ VAL22 = 0xFFFFFFFFFFFFFFFF + 1,
|
||||
\\ VAL23 = 0xFFFFFFFFFFFFFFFF,
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const enum_EnumWithInits = extern enum(c_longlong) {
|
||||
\\ VAL01 = 0,
|
||||
\\ VAL02 = 1,
|
||||
\\ VAL03 = 2,
|
||||
\\ VAL04 = 3,
|
||||
\\ VAL05 = -1,
|
||||
\\ VAL06 = -2,
|
||||
\\ VAL07 = -3,
|
||||
\\ VAL08 = -4,
|
||||
\\ VAL09 = -3,
|
||||
\\ VAL10 = -1000012000,
|
||||
\\ VAL11 = -1000161000,
|
||||
\\ VAL12 = -1000174001,
|
||||
\\ VAL13 = -3,
|
||||
\\ VAL14 = -1000012000,
|
||||
\\ VAL15 = -1000161000,
|
||||
\\ VAL16 = -3,
|
||||
\\ VAL17 = 1000011998,
|
||||
\\ VAL18 = 1152921504606846976,
|
||||
\\ VAL19 = 3458764513820540927,
|
||||
\\ VAL20 = 6917529027641081854,
|
||||
\\ VAL21 = 6917529027641081853,
|
||||
\\ VAL22 = 0,
|
||||
\\ VAL23 = -1,
|
||||
\\};
|
||||
});
|
||||
}
|
||||
|
||||
cases.add("predefined expressions",
|
||||
\\void foo(void) {
|
||||
\\ __func__;
|
||||
\\ __FUNCTION__;
|
||||
\\ __PRETTY_FUNCTION__;
|
||||
cases.add_2("comma operator",
|
||||
\\int foo(char c) {
|
||||
\\ 2, 4;
|
||||
\\ return 2, 4, 6;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo() void {
|
||||
\\ _ = "foo";
|
||||
\\ _ = "foo";
|
||||
\\ _ = "void foo(void)";
|
||||
\\pub export fn foo(_arg_c: u8) c_int {
|
||||
\\ var c = _arg_c;
|
||||
\\ _ = 2;
|
||||
\\ _ = 4;
|
||||
\\ _ = 2;
|
||||
\\ _ = 4;
|
||||
\\ return 6;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("ignore result, no function arguments",
|
||||
\\void foo() {
|
||||
cases.add_2("wors-case assign",
|
||||
\\int foo(char c) {
|
||||
\\ int a;
|
||||
\\ 1;
|
||||
\\ "hey";
|
||||
\\ 1 + 1;
|
||||
\\ 1 - 1;
|
||||
\\ a = 1;
|
||||
\\ int b;
|
||||
\\ a = b = 2;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo() void {
|
||||
\\pub export fn foo(_arg_c: u8) c_int {
|
||||
\\ var c = _arg_c;
|
||||
\\ var a: c_int = undefined;
|
||||
\\ _ = 1;
|
||||
\\ _ = "hey";
|
||||
\\ _ = (1 + 1);
|
||||
\\ _ = (1 - 1);
|
||||
\\ a = 1;
|
||||
\\ var b: c_int = undefined;
|
||||
\\ a = blk: {
|
||||
\\ const _tmp_1 = 2;
|
||||
\\ b = _tmp_1;
|
||||
\\ break :blk _tmp_1;
|
||||
\\ };
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("if statements",
|
||||
\\int foo(char c) {
|
||||
\\ if (2) {
|
||||
\\ int a = 2;
|
||||
\\ }
|
||||
\\ if (2, 5) {
|
||||
\\ int a = 2;
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo(_arg_c: u8) c_int {
|
||||
\\ var c = _arg_c;
|
||||
\\ if (2 != 0) {
|
||||
\\ var a: c_int = 2;
|
||||
\\ }
|
||||
\\ if ((blk: {
|
||||
\\ _ = 2;
|
||||
\\ break :blk 5;
|
||||
\\ }) != 0) {
|
||||
\\ var a: c_int = 2;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("while loops",
|
||||
\\int foo() {
|
||||
\\ int a = 5;
|
||||
\\ while (2)
|
||||
\\ a = 2;
|
||||
\\ while (4) {
|
||||
\\ int a = 4;
|
||||
\\ a = 9;
|
||||
\\ return 6, a;
|
||||
\\ }
|
||||
\\ do {
|
||||
\\ int a = 2;
|
||||
\\ a = 12;
|
||||
\\ } while (4);
|
||||
\\ do
|
||||
\\ a = 7;
|
||||
\\ while (4);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() c_int {
|
||||
\\ var a: c_int = 5;
|
||||
\\ while (2 != 0) a = 2;
|
||||
\\ while (4 != 0) {
|
||||
\\ var a: c_int = 4;
|
||||
\\ a = 9;
|
||||
\\ _ = 6;
|
||||
\\ return a;
|
||||
\\ }
|
||||
\\ while (true) {
|
||||
\\ var a: c_int = 2;
|
||||
\\ a = 12;
|
||||
\\ if (!(4 != 0)) break;
|
||||
\\ }
|
||||
\\ while (true) {
|
||||
\\ a = 7;
|
||||
\\ if (!(4 != 0)) break;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("for loops",
|
||||
\\int foo() {
|
||||
\\ for (int i = 2, b = 4; i + 2; i = 2) {
|
||||
\\ int a = 2;
|
||||
\\ a = 6, 5, 7;
|
||||
\\ }
|
||||
\\ char i = 2;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() c_int {
|
||||
\\ {
|
||||
\\ var i: c_int = 2;
|
||||
\\ var b: c_int = 4;
|
||||
\\ while ((i + 2) != 0) : (i = 2) {
|
||||
\\ var a: c_int = 2;
|
||||
\\ a = 6;
|
||||
\\ _ = 5;
|
||||
\\ _ = 7;
|
||||
\\ }
|
||||
\\ }
|
||||
\\ var i: u8 = @as(u8, 2);
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("shadowing primitive types",
|
||||
\\unsigned anyerror = 2;
|
||||
, &[_][]const u8{
|
||||
\\pub export var _anyerror: c_uint = @as(c_uint, 2);
|
||||
});
|
||||
|
||||
cases.add_2("floats",
|
||||
\\float a = 3.1415;
|
||||
\\double b = 3.1415;
|
||||
\\int c = 3.1415;
|
||||
\\double d = 3;
|
||||
, &[_][]const u8{
|
||||
\\pub export var a: f32 = @floatCast(f32, 3.1415);
|
||||
\\pub export var b: f64 = 3.1415;
|
||||
\\pub export var c: c_int = @floatToInt(c_int, 3.1415);
|
||||
\\pub export var d: f64 = @intToFloat(f64, 3);
|
||||
});
|
||||
|
||||
cases.add_2("conditional operator",
|
||||
\\int bar(void) {
|
||||
\\ if (2 ? 5 : 5 ? 4 : 6) 2;
|
||||
\\ return 2 ? 5 : 5 ? 4 : 6;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn bar() c_int {
|
||||
\\ if ((if (2 != 0) 5 else (if (5 != 0) 4 else 6)) != 0) _ = 2;
|
||||
\\ return if (2 != 0) 5 else if (5 != 0) 4 else 6;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("switch on int",
|
||||
\\int switch_fn(int i) {
|
||||
\\ int res = 0;
|
||||
\\ switch (i) {
|
||||
\\ case 0:
|
||||
\\ res = 1;
|
||||
\\ case 1 ... 3:
|
||||
\\ res = 2;
|
||||
\\ default:
|
||||
\\ res = 3 * i;
|
||||
\\ break;
|
||||
\\ case 4:
|
||||
\\ res = 5;
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn switch_fn(_arg_i: c_int) c_int {
|
||||
\\ var i = _arg_i;
|
||||
\\ var res: c_int = 0;
|
||||
\\ __switch: {
|
||||
\\ __case_2: {
|
||||
\\ __default: {
|
||||
\\ __case_1: {
|
||||
\\ __case_0: {
|
||||
\\ switch (i) {
|
||||
\\ 0 => break :__case_0,
|
||||
\\ 1...3 => break :__case_1,
|
||||
\\ else => break :__default,
|
||||
\\ 4 => break :__case_2,
|
||||
\\ }
|
||||
\\ }
|
||||
\\ res = 1;
|
||||
\\ }
|
||||
\\ res = 2;
|
||||
\\ }
|
||||
\\ res = (3 * i);
|
||||
\\ break :__switch;
|
||||
\\ }
|
||||
\\ res = 5;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("type referenced struct",
|
||||
\\struct Foo {
|
||||
\\ struct Bar{
|
||||
\\ int b;
|
||||
\\ };
|
||||
\\ struct Bar c;
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_Bar = extern struct {
|
||||
\\ b: c_int,
|
||||
\\};
|
||||
\\pub const struct_Foo = extern struct {
|
||||
\\ c: struct_Bar,
|
||||
\\};
|
||||
});
|
||||
|
||||
cases.add_2("undefined array global",
|
||||
\\int array[100] = {};
|
||||
, &[_][]const u8{
|
||||
\\pub export var array: [100]c_int = .{0} ** 100;
|
||||
});
|
||||
|
||||
cases.add_2("restrict -> noalias",
|
||||
\\void foo(void *restrict bar, void *restrict);
|
||||
, &[_][]const u8{
|
||||
\\pub extern fn foo(noalias bar: ?*c_void, noalias ?*c_void) void;
|
||||
});
|
||||
|
||||
cases.add_2("assign",
|
||||
\\int max(int a) {
|
||||
\\ int tmp;
|
||||
\\ tmp = a;
|
||||
\\ a = tmp;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(_arg_a: c_int) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var tmp: c_int = undefined;
|
||||
\\ tmp = a;
|
||||
\\ a = tmp;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("chaining assign",
|
||||
\\void max(int a) {
|
||||
\\ int b, c;
|
||||
\\ c = b = a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(_arg_a: c_int) void {
|
||||
\\ var a = _arg_a;
|
||||
\\ var b: c_int = undefined;
|
||||
\\ var c: c_int = undefined;
|
||||
\\ c = blk: {
|
||||
\\ const _tmp_1 = a;
|
||||
\\ b = _tmp_1;
|
||||
\\ break :blk _tmp_1;
|
||||
\\ };
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("anonymous enum",
|
||||
\\enum {
|
||||
\\ One,
|
||||
\\ Two,
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const One = 0;
|
||||
\\pub const Two = 1;
|
||||
\\const enum_unnamed_1 = extern enum {
|
||||
\\ One,
|
||||
\\ Two,
|
||||
\\};
|
||||
});
|
||||
|
||||
cases.add_2("c style cast",
|
||||
\\int float_to_int(float a) {
|
||||
\\ return (int)a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn float_to_int(_arg_a: f32) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ return @floatToInt(c_int, a);
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("escape sequences",
|
||||
\\const char *escapes() {
|
||||
\\char a = '\'',
|
||||
\\ b = '\\',
|
||||
\\ c = '\a',
|
||||
\\ d = '\b',
|
||||
\\ e = '\f',
|
||||
\\ f = '\n',
|
||||
\\ g = '\r',
|
||||
\\ h = '\t',
|
||||
\\ i = '\v',
|
||||
\\ j = '\0',
|
||||
\\ k = '\"';
|
||||
\\ return "\'\\\a\b\f\n\r\t\v\0\"";
|
||||
\\}
|
||||
\\
|
||||
, &[_][]const u8{
|
||||
\\pub export fn escapes() [*c]const u8 {
|
||||
\\ var a: u8 = @as(u8, '\'');
|
||||
\\ var b: u8 = @as(u8, '\\');
|
||||
\\ var c: u8 = @as(u8, '\x07');
|
||||
\\ var d: u8 = @as(u8, '\x08');
|
||||
\\ var e: u8 = @as(u8, '\x0c');
|
||||
\\ var f: u8 = @as(u8, '\n');
|
||||
\\ var g: u8 = @as(u8, '\r');
|
||||
\\ var h: u8 = @as(u8, '\t');
|
||||
\\ var i: u8 = @as(u8, '\x0b');
|
||||
\\ var j: u8 = @as(u8, '\x00');
|
||||
\\ var k: u8 = @as(u8, '\"');
|
||||
\\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("do loop",
|
||||
\\void foo(void) {
|
||||
\\ int a = 2;
|
||||
\\ do {
|
||||
\\ a = a - 1;
|
||||
\\ } while (a);
|
||||
\\
|
||||
\\ int b = 2;
|
||||
\\ do
|
||||
\\ b = b -1;
|
||||
\\ while (b);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ var a: c_int = 2;
|
||||
\\ while (true) {
|
||||
\\ a = (a - 1);
|
||||
\\ if (!(a != 0)) break;
|
||||
\\ }
|
||||
\\ var b: c_int = 2;
|
||||
\\ while (true) {
|
||||
\\ b = (b - 1);
|
||||
\\ if (!(b != 0)) break;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("logical and, logical or, on non-bool values, extra parens",
|
||||
\\enum Foo {
|
||||
\\ FooA,
|
||||
\\ FooB,
|
||||
\\ FooC,
|
||||
\\};
|
||||
\\typedef int SomeTypedef;
|
||||
\\int and_or_non_bool(int a, float b, void *c) {
|
||||
\\ enum Foo d = FooA;
|
||||
\\ int e = (a && b);
|
||||
\\ int f = (b && c);
|
||||
\\ int g = (a && c);
|
||||
\\ int h = (a || b);
|
||||
\\ int i = (b || c);
|
||||
\\ int j = (a || c);
|
||||
\\ int k = (a || d);
|
||||
\\ int l = (d && b);
|
||||
\\ int m = (c || d);
|
||||
\\ SomeTypedef td = 44;
|
||||
\\ int o = (td || b);
|
||||
\\ int p = (c && td);
|
||||
\\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub const enum_Foo = extern enum {
|
||||
\\ A,
|
||||
\\ B,
|
||||
\\ C,
|
||||
\\};
|
||||
\\pub const SomeTypedef = c_int;
|
||||
\\pub export fn and_or_non_bool(_arg_a: c_int, _arg_b: f32, _arg_c: ?*c_void) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var b = _arg_b;
|
||||
\\ var c = _arg_c;
|
||||
\\ var d: enum_Foo = @intToEnum(enum_Foo, FooA);
|
||||
\\ var e: c_int = @boolToInt(((a != 0) and (b != 0)));
|
||||
\\ var f: c_int = @boolToInt(((b != 0) and (c != null)));
|
||||
\\ var g: c_int = @boolToInt(((a != 0) and (c != null)));
|
||||
\\ var h: c_int = @boolToInt(((a != 0) or (b != 0)));
|
||||
\\ var i: c_int = @boolToInt(((b != 0) or (c != null)));
|
||||
\\ var j: c_int = @boolToInt(((a != 0) or (c != null)));
|
||||
\\ var k: c_int = @boolToInt(((a != 0) or (@enumToInt(d) != 0)));
|
||||
\\ var l: c_int = @boolToInt(((@enumToInt(d) != 0) and (b != 0)));
|
||||
\\ var m: c_int = @boolToInt(((c != null) or (@enumToInt(d) != 0)));
|
||||
\\ var td: SomeTypedef = 44;
|
||||
\\ var o: c_int = @boolToInt(((td != 0) or (b != 0)));
|
||||
\\ var p: c_int = @boolToInt(((c != null) and (td != 0)));
|
||||
\\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
|
||||
\\}
|
||||
,
|
||||
\\pub const Foo = enum_Foo;
|
||||
});
|
||||
|
||||
cases.add_2("qualified struct and enum",
|
||||
\\struct Foo {
|
||||
\\ int x;
|
||||
\\ int y;
|
||||
\\};
|
||||
\\enum Bar {
|
||||
\\ BarA,
|
||||
\\ BarB,
|
||||
\\};
|
||||
\\void func(struct Foo *a, enum Bar **b);
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_Foo = extern struct {
|
||||
\\ x: c_int,
|
||||
\\ y: c_int,
|
||||
\\};
|
||||
,
|
||||
\\pub const enum_Bar = extern enum {
|
||||
\\ A,
|
||||
\\ B,
|
||||
\\};
|
||||
\\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
|
||||
,
|
||||
\\pub const Foo = struct_Foo;
|
||||
\\pub const Bar = enum_Bar;
|
||||
});
|
||||
|
||||
cases.add_2("bitwise binary operators, simpler parens",
|
||||
\\int max(int a, int b) {
|
||||
\\ return (a & b) ^ (a | b);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(_arg_a: c_int, _arg_b: c_int) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var b = _arg_b;
|
||||
\\ return ((a & b) ^ (a | b));
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("comparison operators (no if)", // TODO Come up with less contrived tests? Make sure to cover all these comparisons.
|
||||
\\int test_comparisons(int a, int b) {
|
||||
\\ int c = (a < b);
|
||||
\\ int d = (a > b);
|
||||
\\ int e = (a <= b);
|
||||
\\ int f = (a >= b);
|
||||
\\ int g = (c < d);
|
||||
\\ int h = (e < f);
|
||||
\\ int i = (g < h);
|
||||
\\ return i;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn test_comparisons(_arg_a: c_int, _arg_b: c_int) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var b = _arg_b;
|
||||
\\ var c: c_int = @boolToInt((a < b));
|
||||
\\ var d: c_int = @boolToInt((a > b));
|
||||
\\ var e: c_int = @boolToInt((a <= b));
|
||||
\\ var f: c_int = @boolToInt((a >= b));
|
||||
\\ var g: c_int = @boolToInt((c < d));
|
||||
\\ var h: c_int = @boolToInt((e < f));
|
||||
\\ var i: c_int = @boolToInt((g < h));
|
||||
\\ return i;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("==, !=",
|
||||
\\int max(int a, int b) {
|
||||
\\ if (a == b)
|
||||
\\ return a;
|
||||
\\ if (a != b)
|
||||
\\ return b;
|
||||
\\ return a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(_arg_a: c_int, _arg_b: c_int) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var b = _arg_b;
|
||||
\\ if (a == b) return a;
|
||||
\\ if (a != b) return b;
|
||||
\\ return a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("typedeffed bool expression",
|
||||
\\typedef char* yes;
|
||||
\\void foo(void) {
|
||||
\\ yes a;
|
||||
\\ if (a) 2;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub const yes = [*c]u8;
|
||||
\\pub export fn foo() void {
|
||||
\\ var a: yes = undefined;
|
||||
\\ if (a != null) _ = 2;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("statement expression",
|
||||
\\int foo(void) {
|
||||
\\ return ({
|
||||
\\ int a = 1;
|
||||
\\ a;
|
||||
\\ a;
|
||||
\\ });
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() c_int {
|
||||
\\ return (blk: {
|
||||
\\ var a: c_int = 1;
|
||||
\\ _ = a;
|
||||
\\ break :blk a;
|
||||
\\ });
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("field access expression",
|
||||
\\#define ARROW a->b
|
||||
\\#define DOT a.b
|
||||
\\extern struct Foo {
|
||||
\\ int b;
|
||||
\\}a;
|
||||
\\float b = 2.0f;
|
||||
\\int foo(void) {
|
||||
\\ struct Foo *c;
|
||||
\\ a.b;
|
||||
\\ c->b;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_Foo = extern struct {
|
||||
\\ b: c_int,
|
||||
\\};
|
||||
\\pub extern var a: struct_Foo;
|
||||
\\pub export var b: f32 = 2;
|
||||
\\pub export fn foo() c_int {
|
||||
\\ var c: [*c]struct_Foo = undefined;
|
||||
\\ _ = a.b;
|
||||
\\ _ = c.*.b;
|
||||
\\}
|
||||
,
|
||||
\\pub const DOT = a.b;
|
||||
,
|
||||
\\pub const ARROW = a.*.b;
|
||||
});
|
||||
|
||||
cases.add_2("array access",
|
||||
\\#define ACCESS array[2]
|
||||
\\int array[100] = {};
|
||||
\\int foo(int index) {
|
||||
\\ return array[index];
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export var array: [100]c_int = .{0} ** 100;
|
||||
\\pub export fn foo(_arg_index: c_int) c_int {
|
||||
\\ var index = _arg_index;
|
||||
\\ return array[index];
|
||||
\\}
|
||||
,
|
||||
\\pub const ACCESS = array[2];
|
||||
});
|
||||
|
||||
cases.add_2("macro call",
|
||||
\\#define CALL(arg) bar(arg)
|
||||
, &[_][]const u8{
|
||||
\\pub inline fn CALL(arg: var) @TypeOf(bar(arg)) {
|
||||
\\ return bar(arg);
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("logical and, logical or",
|
||||
\\int max(int a, int b) {
|
||||
\\ if (a < b || a == b)
|
||||
\\ return b;
|
||||
\\ if (a >= b && a == b)
|
||||
\\ return a;
|
||||
\\ return a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(_arg_a: c_int, _arg_b: c_int) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var b = _arg_b;
|
||||
\\ if ((a < b) or (a == b)) return b;
|
||||
\\ if ((a >= b) and (a == b)) return a;
|
||||
\\ return a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("if statement",
|
||||
\\int max(int a, int b) {
|
||||
\\ if (a < b)
|
||||
\\ return b;
|
||||
\\
|
||||
\\ if (a < b)
|
||||
\\ return b;
|
||||
\\ else
|
||||
\\ return a;
|
||||
\\
|
||||
\\ if (a < b) ; else ;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(_arg_a: c_int, _arg_b: c_int) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var b = _arg_b;
|
||||
\\ if (a < b) return b;
|
||||
\\ if (a < b) return b else return a;
|
||||
\\ if (a < b) {} else {}
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("if on non-bool",
|
||||
\\enum SomeEnum { A, B, C };
|
||||
\\int if_none_bool(int a, float b, void *c, enum SomeEnum d) {
|
||||
\\ if (a) return 0;
|
||||
\\ if (b) return 1;
|
||||
\\ if (c) return 2;
|
||||
\\ if (d) return 3;
|
||||
\\ return 4;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub const enum_SomeEnum = extern enum {
|
||||
\\ A,
|
||||
\\ B,
|
||||
\\ C,
|
||||
\\};
|
||||
\\pub export fn if_none_bool(_arg_a: c_int, _arg_b: f32, _arg_c: ?*c_void, _arg_d: enum_SomeEnum) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var b = _arg_b;
|
||||
\\ var c = _arg_c;
|
||||
\\ var d = _arg_d;
|
||||
\\ if (a != 0) return 0;
|
||||
\\ if (b != 0) return 1;
|
||||
\\ if (c != null) return 2;
|
||||
\\ if (d != 0) return 3;
|
||||
\\ return 4;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("simple data types",
|
||||
\\#include <stdint.h>
|
||||
\\int foo(char a, unsigned char b, signed char c);
|
||||
\\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype
|
||||
\\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
|
||||
\\void baz(int8_t a, int16_t b, int32_t c, int64_t d);
|
||||
, &[_][]const u8{
|
||||
\\pub extern fn foo(a: u8, b: u8, c: i8) c_int;
|
||||
\\pub extern fn bar(a: u8, b: u16, c: u32, d: u64) void;
|
||||
\\pub extern fn baz(a: i8, b: i16, c: i32, d: i64) void;
|
||||
});
|
||||
|
||||
cases.add_2("simple function",
|
||||
\\int abs(int a) {
|
||||
\\ return a < 0 ? -a : a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn abs(_arg_a: c_int) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ return if (a < 0) -a else a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("post increment",
|
||||
\\unsigned foo1(unsigned a) {
|
||||
\\ a++;
|
||||
\\ return a;
|
||||
\\}
|
||||
\\int foo2(int a) {
|
||||
\\ a++;
|
||||
\\ return a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo1(_arg_a: c_uint) c_uint {
|
||||
\\ var a = _arg_a;
|
||||
\\ a +%= 1;
|
||||
\\ return a;
|
||||
\\}
|
||||
\\pub export fn foo2(_arg_a: c_int) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ a += 1;
|
||||
\\ return a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("deref function pointer",
|
||||
\\void foo(void) {}
|
||||
\\int baz(void) { return 0; }
|
||||
\\void bar(void) {
|
||||
\\ void(*f)(void) = foo;
|
||||
\\ int(*b)(void) = baz;
|
||||
\\ f();
|
||||
\\ (*(f))();
|
||||
\\ foo();
|
||||
\\ b();
|
||||
\\ (*(b))();
|
||||
\\ baz();
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {}
|
||||
\\pub export fn baz() c_int {
|
||||
\\ return 0;
|
||||
\\}
|
||||
\\pub export fn bar() void {
|
||||
\\ var f: ?extern fn () void = foo;
|
||||
\\ var b: ?extern fn () c_int = baz;
|
||||
\\ f.?();
|
||||
\\ (f).?();
|
||||
\\ foo();
|
||||
\\ _ = b.?();
|
||||
\\ _ = (b).?();
|
||||
\\ _ = baz();
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("pre increment/decrement",
|
||||
\\void foo(void) {
|
||||
\\ int i = 0;
|
||||
\\ unsigned u = 0;
|
||||
\\ ++i;
|
||||
\\ --i;
|
||||
\\ ++u;
|
||||
\\ --u;
|
||||
\\ i = ++i;
|
||||
\\ i = --i;
|
||||
\\ u = ++u;
|
||||
\\ u = --u;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ var i: c_int = 0;
|
||||
\\ var u: c_uint = @as(c_uint, 0);
|
||||
\\ i += 1;
|
||||
\\ i -= 1;
|
||||
\\ u +%= 1;
|
||||
\\ u -%= 1;
|
||||
\\ i = (blk: {
|
||||
\\ const _ref_1 = &i;
|
||||
\\ _ref_1.* += 1;
|
||||
\\ break :blk _ref_1.*;
|
||||
\\ });
|
||||
\\ i = (blk: {
|
||||
\\ const _ref_2 = &i;
|
||||
\\ _ref_2.* -= 1;
|
||||
\\ break :blk _ref_2.*;
|
||||
\\ });
|
||||
\\ u = (blk: {
|
||||
\\ const _ref_3 = &u;
|
||||
\\ _ref_3.* +%= 1;
|
||||
\\ break :blk _ref_3.*;
|
||||
\\ });
|
||||
\\ u = (blk: {
|
||||
\\ const _ref_4 = &u;
|
||||
\\ _ref_4.* -%= 1;
|
||||
\\ break :blk _ref_4.*;
|
||||
\\ });
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("shift right assign",
|
||||
\\int log2(unsigned a) {
|
||||
\\ int i = 0;
|
||||
\\ while (a > 0) {
|
||||
\\ a >>= 1;
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn log2(_arg_a: c_uint) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var i: c_int = 0;
|
||||
\\ while (a > @as(c_uint, 0)) {
|
||||
\\ a >>= @as(@import("std").math.Log2Int(c_int), 1);
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("shift right assign with a fixed size type",
|
||||
\\#include <stdint.h>
|
||||
\\int log2(uint32_t a) {
|
||||
\\ int i = 0;
|
||||
\\ while (a > 0) {
|
||||
\\ a >>= 1;
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn log2(_arg_a: u32) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var i: c_int = 0;
|
||||
\\ while (a > @as(c_uint, 0)) {
|
||||
\\ a >>= @as(@import("std").math.Log2Int(c_int), 1);
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("compound assignment operators",
|
||||
\\void foo(void) {
|
||||
\\ int a = 0;
|
||||
\\ a += (a += 1);
|
||||
\\ a -= (a -= 1);
|
||||
\\ a *= (a *= 1);
|
||||
\\ a &= (a &= 1);
|
||||
\\ a |= (a |= 1);
|
||||
\\ a ^= (a ^= 1);
|
||||
\\ a >>= (a >>= 1);
|
||||
\\ a <<= (a <<= 1);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ var a: c_int = 0;
|
||||
\\ a += (blk: {
|
||||
\\ const _ref_1 = &a;
|
||||
\\ _ref_1.* = _ref_1.* + 1;
|
||||
\\ break :blk _ref_1.*;
|
||||
\\ });
|
||||
\\ a -= (blk: {
|
||||
\\ const _ref_2 = &a;
|
||||
\\ _ref_2.* = _ref_2.* - 1;
|
||||
\\ break :blk _ref_2.*;
|
||||
\\ });
|
||||
\\ a *= (blk: {
|
||||
\\ const _ref_3 = &a;
|
||||
\\ _ref_3.* = _ref_3.* * 1;
|
||||
\\ break :blk _ref_3.*;
|
||||
\\ });
|
||||
\\ a &= (blk: {
|
||||
\\ const _ref_4 = &a;
|
||||
\\ _ref_4.* = _ref_4.* & 1;
|
||||
\\ break :blk _ref_4.*;
|
||||
\\ });
|
||||
\\ a |= (blk: {
|
||||
\\ const _ref_5 = &a;
|
||||
\\ _ref_5.* = _ref_5.* | 1;
|
||||
\\ break :blk _ref_5.*;
|
||||
\\ });
|
||||
\\ a ^= (blk: {
|
||||
\\ const _ref_6 = &a;
|
||||
\\ _ref_6.* = _ref_6.* ^ 1;
|
||||
\\ break :blk _ref_6.*;
|
||||
\\ });
|
||||
\\ a >>= @as(@import("std").math.Log2Int(c_int), (blk: {
|
||||
\\ const _ref_7 = &a;
|
||||
\\ _ref_7.* = _ref_7.* >> @as(@import("std").math.Log2Int(c_int), 1);
|
||||
\\ break :blk _ref_7.*;
|
||||
\\ }));
|
||||
\\ a <<= @as(@import("std").math.Log2Int(c_int), (blk: {
|
||||
\\ const _ref_8 = &a;
|
||||
\\ _ref_8.* = _ref_8.* << @as(@import("std").math.Log2Int(c_int), 1);
|
||||
\\ break :blk _ref_8.*;
|
||||
\\ }));
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("compound assignment operators unsigned",
|
||||
\\void foo(void) {
|
||||
\\ unsigned a = 0;
|
||||
\\ a += (a += 1);
|
||||
\\ a -= (a -= 1);
|
||||
\\ a *= (a *= 1);
|
||||
\\ a &= (a &= 1);
|
||||
\\ a |= (a |= 1);
|
||||
\\ a ^= (a ^= 1);
|
||||
\\ a >>= (a >>= 1);
|
||||
\\ a <<= (a <<= 1);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ var a: c_uint = @as(c_uint, 0);
|
||||
\\ a +%= (blk: {
|
||||
\\ const _ref_1 = &a;
|
||||
\\ _ref_1.* = _ref_1.* +% @as(c_uint, 1);
|
||||
\\ break :blk _ref_1.*;
|
||||
\\ });
|
||||
\\ a -%= (blk: {
|
||||
\\ const _ref_2 = &a;
|
||||
\\ _ref_2.* = _ref_2.* -% @as(c_uint, 1);
|
||||
\\ break :blk _ref_2.*;
|
||||
\\ });
|
||||
\\ a *%= (blk: {
|
||||
\\ const _ref_3 = &a;
|
||||
\\ _ref_3.* = _ref_3.* *% @as(c_uint, 1);
|
||||
\\ break :blk _ref_3.*;
|
||||
\\ });
|
||||
\\ a &= (blk: {
|
||||
\\ const _ref_4 = &a;
|
||||
\\ _ref_4.* = _ref_4.* & @as(c_uint, 1);
|
||||
\\ break :blk _ref_4.*;
|
||||
\\ });
|
||||
\\ a |= (blk: {
|
||||
\\ const _ref_5 = &a;
|
||||
\\ _ref_5.* = _ref_5.* | @as(c_uint, 1);
|
||||
\\ break :blk _ref_5.*;
|
||||
\\ });
|
||||
\\ a ^= (blk: {
|
||||
\\ const _ref_6 = &a;
|
||||
\\ _ref_6.* = _ref_6.* ^ @as(c_uint, 1);
|
||||
\\ break :blk _ref_6.*;
|
||||
\\ });
|
||||
\\ a >>= @as(@import("std").math.Log2Int(c_uint), (blk: {
|
||||
\\ const _ref_7 = &a;
|
||||
\\ _ref_7.* = _ref_7.* >> @as(@import("std").math.Log2Int(c_int), 1);
|
||||
\\ break :blk _ref_7.*;
|
||||
\\ }));
|
||||
\\ a <<= @as(@import("std").math.Log2Int(c_uint), (blk: {
|
||||
\\ const _ref_8 = &a;
|
||||
\\ _ref_8.* = _ref_8.* << @as(@import("std").math.Log2Int(c_int), 1);
|
||||
\\ break :blk _ref_8.*;
|
||||
\\ }));
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("post increment/decrement",
|
||||
\\void foo(void) {
|
||||
\\ int i = 0;
|
||||
\\ unsigned u = 0;
|
||||
\\ i++;
|
||||
\\ i--;
|
||||
\\ u++;
|
||||
\\ u--;
|
||||
\\ i = i++;
|
||||
\\ i = i--;
|
||||
\\ u = u++;
|
||||
\\ u = u--;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ var i: c_int = 0;
|
||||
\\ var u: c_uint = @as(c_uint, 0);
|
||||
\\ i += 1;
|
||||
\\ i -= 1;
|
||||
\\ u +%= 1;
|
||||
\\ u -%= 1;
|
||||
\\ i = (blk: {
|
||||
\\ const _ref_1 = &i;
|
||||
\\ const _tmp_2 = _ref_1.*;
|
||||
\\ _ref_1.* += 1;
|
||||
\\ break :blk _tmp_2;
|
||||
\\ });
|
||||
\\ i = (blk: {
|
||||
\\ const _ref_3 = &i;
|
||||
\\ const _tmp_4 = _ref_3.*;
|
||||
\\ _ref_3.* -= 1;
|
||||
\\ break :blk _tmp_4;
|
||||
\\ });
|
||||
\\ u = (blk: {
|
||||
\\ const _ref_5 = &u;
|
||||
\\ const _tmp_6 = _ref_5.*;
|
||||
\\ _ref_5.* +%= 1;
|
||||
\\ break :blk _tmp_6;
|
||||
\\ });
|
||||
\\ u = (blk: {
|
||||
\\ const _ref_7 = &u;
|
||||
\\ const _tmp_8 = _ref_7.*;
|
||||
\\ _ref_7.* -%= 1;
|
||||
\\ break :blk _tmp_8;
|
||||
\\ });
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("implicit casts",
|
||||
\\#include <stdbool.h>
|
||||
\\
|
||||
\\void fn_int(int x);
|
||||
\\void fn_f32(float x);
|
||||
\\void fn_f64(double x);
|
||||
\\void fn_char(char x);
|
||||
\\void fn_bool(bool x);
|
||||
\\void fn_ptr(void *x);
|
||||
\\
|
||||
\\void call(int q) {
|
||||
\\ fn_int(3.0f);
|
||||
\\ fn_int(3.0);
|
||||
\\ fn_int(3.0L);
|
||||
\\ fn_int('ABCD');
|
||||
\\ fn_f32(3);
|
||||
\\ fn_f64(3);
|
||||
\\ fn_char('3');
|
||||
\\ fn_char('\x1');
|
||||
\\ fn_char(0);
|
||||
\\ fn_f32(3.0f);
|
||||
\\ fn_f64(3.0);
|
||||
\\ fn_bool(123);
|
||||
\\ fn_bool(0);
|
||||
\\ fn_bool(&fn_int);
|
||||
\\ fn_int(&fn_int);
|
||||
\\ fn_ptr(42);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub extern fn fn_int(x: c_int) void;
|
||||
\\pub extern fn fn_f32(x: f32) void;
|
||||
\\pub extern fn fn_f64(x: f64) void;
|
||||
\\pub extern fn fn_char(x: u8) void;
|
||||
\\pub extern fn fn_bool(x: bool) void;
|
||||
\\pub extern fn fn_ptr(x: ?*c_void) void;
|
||||
\\pub export fn call(_arg_q: c_int) void {
|
||||
\\ var q = _arg_q;
|
||||
\\ fn_int(@floatToInt(c_int, 3));
|
||||
\\ fn_int(@floatToInt(c_int, 3));
|
||||
\\ fn_int(@floatToInt(c_int, 3));
|
||||
\\ fn_int(1094861636);
|
||||
\\ fn_f32(@intToFloat(f32, 3));
|
||||
\\ fn_f64(@intToFloat(f64, 3));
|
||||
\\ fn_char(@as(u8, '3'));
|
||||
\\ fn_char(@as(u8, '\x01'));
|
||||
\\ fn_char(@as(u8, 0));
|
||||
\\ fn_f32(3);
|
||||
\\ fn_f64(3);
|
||||
\\ fn_bool(123 != 0);
|
||||
\\ fn_bool(0 != 0);
|
||||
\\ fn_bool(@ptrToInt(&fn_int) != 0);
|
||||
\\ fn_int(@intCast(c_int, @ptrToInt(&fn_int)));
|
||||
\\ fn_ptr(@intToPtr(?*c_void, 42));
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("function call",
|
||||
\\static void bar(void) { }
|
||||
\\void foo(int *(baz)(void)) {
|
||||
\\ bar();
|
||||
\\ baz();
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn bar() void {}
|
||||
\\pub export fn foo(_arg_baz: ?extern fn () [*c]c_int) void {
|
||||
\\ var baz = _arg_baz;
|
||||
\\ bar();
|
||||
\\ _ = baz.?();
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add_2("macro defines string literal with octal",
|
||||
\\#define FOO "aoeu\023 derp"
|
||||
\\#define FOO2 "aoeu\0234 derp"
|
||||
\\#define FOO_CHAR '\077'
|
||||
, &[_][]const u8{
|
||||
\\pub const FOO = "aoeu\x13 derp";
|
||||
,
|
||||
\\pub const FOO2 = "aoeu\x134 derp";
|
||||
,
|
||||
\\pub const FOO_CHAR = '\x3f';
|
||||
});
|
||||
|
||||
cases.add_2("enums",
|
||||
\\enum Foo {
|
||||
\\ FooA,
|
||||
\\ FooB,
|
||||
\\ Foo1,
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const enum_Foo = extern enum {
|
||||
\\ A,
|
||||
\\ B,
|
||||
\\ @"1",
|
||||
\\};
|
||||
,
|
||||
\\pub const FooA = 0;
|
||||
,
|
||||
\\pub const FooB = 1;
|
||||
,
|
||||
\\pub const Foo1 = 2;
|
||||
,
|
||||
\\pub const Foo = enum_Foo;
|
||||
});
|
||||
|
||||
cases.add_2("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 = 2;
|
||||
,
|
||||
\\pub const FooB = 5;
|
||||
,
|
||||
\\pub const Foo1 = 6;
|
||||
,
|
||||
\\pub const Foo = enum_Foo;
|
||||
});
|
||||
|
||||
cases.add_2("macro cast",
|
||||
\\#define FOO(bar) baz((void *)(baz))
|
||||
, &[_][]const u8{
|
||||
\\pub inline fn FOO(bar: var) @TypeOf(baz(if (@typeId(@TypeOf(baz)) == .Pointer) @ptrCast([*c]void, baz) else if (@typeId(@TypeOf(baz)) == .Int) @intToPtr([*c]void, baz) else @as([*c]void, baz))) {
|
||||
\\ return baz(if (@typeId(@TypeOf(baz)) == .Pointer) @ptrCast([*c]void, baz) else if (@typeId(@TypeOf(baz)) == .Int) @intToPtr([*c]void, baz) else @as([*c]void, baz));
|
||||
\\}
|
||||
});
|
||||
|
||||
/////////////// Cases for only stage1 because stage2 behavior is better ////////////////
|
||||
cases.addC("Parameterless function prototypes",
|
||||
\\void foo() {}
|
||||
\\void bar(void) {}
|
||||
, &[_][]const u8{
|
||||
\\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);
|
||||
});
|
||||
|
||||
cases.add("switch on int",
|
||||
\\int switch_fn(int i) {
|
||||
\\ int res = 0;
|
||||
\\ switch (i) {
|
||||
\\ case 0:
|
||||
\\ res = 1;
|
||||
\\ case 1:
|
||||
\\ res = 2;
|
||||
\\ default:
|
||||
\\ res = 3 * i;
|
||||
\\ break;
|
||||
\\ case 2:
|
||||
\\ res = 5;
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn switch_fn(i: c_int) c_int {
|
||||
\\ var res: c_int = 0;
|
||||
\\ __switch: {
|
||||
\\ __case_2: {
|
||||
\\ __default: {
|
||||
\\ __case_1: {
|
||||
\\ __case_0: {
|
||||
\\ switch (i) {
|
||||
\\ 0 => break :__case_0,
|
||||
\\ 1 => break :__case_1,
|
||||
\\ else => break :__default,
|
||||
\\ 2 => break :__case_2,
|
||||
\\ }
|
||||
\\ }
|
||||
\\ res = 1;
|
||||
\\ }
|
||||
\\ res = 2;
|
||||
\\ }
|
||||
\\ res = (3 * i);
|
||||
\\ break :__switch;
|
||||
\\ }
|
||||
\\ res = 5;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
@@ -838,34 +2387,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addAllowWarnings("simple data types",
|
||||
\\#include <stdint.h>
|
||||
\\int foo(char a, unsigned char b, signed char c);
|
||||
\\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype
|
||||
\\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
|
||||
\\void baz(int8_t a, int16_t b, int32_t c, int64_t d);
|
||||
cases.add("undefined array global",
|
||||
\\int array[100];
|
||||
, &[_][]const u8{
|
||||
\\pub extern fn foo(a: u8, b: u8, c: i8) c_int;
|
||||
,
|
||||
\\pub extern fn bar(a: u8, b: u16, c: u32, d: u64) void;
|
||||
,
|
||||
\\pub extern fn baz(a: i8, b: i16, c: i32, d: i64) void;
|
||||
});
|
||||
|
||||
cases.addC("simple function",
|
||||
\\int abs(int a) {
|
||||
\\ return a < 0 ? -a : a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\export fn abs(a: c_int) c_int {
|
||||
\\ return if (a < 0) -a else a;
|
||||
\\}
|
||||
});
|
||||
|
||||
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;
|
||||
\\pub var array: [100]c_int = undefined;
|
||||
});
|
||||
|
||||
cases.add("qualified struct and enum",
|
||||
@@ -900,165 +2425,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\pub const Bar = enum_Bar;
|
||||
});
|
||||
|
||||
cases.add("constant size array",
|
||||
\\void func(int array[20]);
|
||||
cases.add("restrict -> noalias",
|
||||
\\void foo(void *restrict bar, void *restrict);
|
||||
, &[_][]const u8{
|
||||
\\pub extern fn func(array: [*c]c_int) void;
|
||||
});
|
||||
|
||||
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("macro defines string literal with hex",
|
||||
\\#define FOO "aoeu\xab derp"
|
||||
\\#define FOO2 "aoeu\x0007a derp"
|
||||
\\#define FOO_CHAR '\xfF'
|
||||
, &[_][]const u8{
|
||||
\\pub const FOO = "aoeu\xab derp";
|
||||
,
|
||||
\\pub const FOO2 = "aoeuz derp";
|
||||
,
|
||||
\\pub const FOO_CHAR = 255;
|
||||
});
|
||||
|
||||
cases.add("macro defines string literal with octal",
|
||||
\\#define FOO "aoeu\023 derp"
|
||||
\\#define FOO2 "aoeu\0234 derp"
|
||||
\\#define FOO_CHAR '\077'
|
||||
, &[_][]const u8{
|
||||
\\pub const FOO = "aoeu\x13 derp";
|
||||
,
|
||||
\\pub const FOO2 = "aoeu\x134 derp";
|
||||
,
|
||||
\\pub const FOO_CHAR = 63;
|
||||
});
|
||||
|
||||
cases.addC("post increment",
|
||||
\\unsigned foo1(unsigned a) {
|
||||
\\ a++;
|
||||
\\ return a;
|
||||
\\}
|
||||
\\int foo2(int a) {
|
||||
\\ a++;
|
||||
\\ return a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo1(_arg_a: c_uint) c_uint {
|
||||
\\ var a = _arg_a;
|
||||
\\ a +%= 1;
|
||||
\\ return a;
|
||||
\\}
|
||||
\\pub export fn foo2(_arg_a: c_int) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ a += 1;
|
||||
\\ return a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("shift right assign",
|
||||
\\int log2(unsigned a) {
|
||||
\\ int i = 0;
|
||||
\\ while (a > 0) {
|
||||
\\ a >>= 1;
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn log2(_arg_a: c_uint) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var i: c_int = 0;
|
||||
\\ while (a > @as(c_uint, 0)) {
|
||||
\\ a >>= @as(@import("std").math.Log2Int(c_uint), 1);
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("if statement",
|
||||
\\int max(int a, int b) {
|
||||
\\ if (a < b)
|
||||
\\ return b;
|
||||
\\
|
||||
\\ if (a < b)
|
||||
\\ return b;
|
||||
\\ else
|
||||
\\ return a;
|
||||
\\
|
||||
\\ if (a < b) ; else ;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(a: c_int, b: c_int) c_int {
|
||||
\\ if (a < b) return b;
|
||||
\\ if (a < b) return b else return a;
|
||||
\\ if (a < b) {} else {}
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("==, !=",
|
||||
\\int max(int a, int b) {
|
||||
\\ if (a == b)
|
||||
\\ return a;
|
||||
\\ if (a != b)
|
||||
\\ return b;
|
||||
\\ return a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(a: c_int, b: c_int) c_int {
|
||||
\\ if (a == b) return a;
|
||||
\\ if (a != b) return b;
|
||||
\\ return a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("bitwise binary operators",
|
||||
\\int max(int a, int b) {
|
||||
\\ return (a & b) ^ (a | b);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(a: c_int, b: c_int) c_int {
|
||||
\\ return (a & b) ^ (a | b);
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("logical and, logical or",
|
||||
\\int max(int a, int b) {
|
||||
\\ if (a < b || a == b)
|
||||
\\ return b;
|
||||
\\ if (a >= b && a == b)
|
||||
\\ return a;
|
||||
\\ return a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(a: c_int, b: c_int) c_int {
|
||||
\\ if ((a < b) or (a == b)) return b;
|
||||
\\ if ((a >= b) and (a == b)) return a;
|
||||
\\ return a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("logical and, logical or on none bool values",
|
||||
\\int and_or_none_bool(int a, float b, void *c) {
|
||||
\\ if (a && b) return 0;
|
||||
\\ if (b && c) return 1;
|
||||
\\ if (a && c) return 2;
|
||||
\\ if (a || b) return 3;
|
||||
\\ if (b || c) return 4;
|
||||
\\ if (a || c) return 5;
|
||||
\\ return 6;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
|
||||
\\ if ((a != 0) and (b != 0)) return 0;
|
||||
\\ if ((b != 0) and (c != null)) return 1;
|
||||
\\ if ((a != 0) and (c != null)) return 2;
|
||||
\\ if ((a != 0) or (b != 0)) return 3;
|
||||
\\ if ((b != 0) or (c != null)) return 4;
|
||||
\\ if ((a != 0) or (c != null)) return 5;
|
||||
\\ return 6;
|
||||
\\}
|
||||
\\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void;
|
||||
});
|
||||
|
||||
cases.addC("assign",
|
||||
@@ -1093,26 +2463,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("shift right assign with a fixed size type",
|
||||
\\#include <stdint.h>
|
||||
\\int log2(uint32_t a) {
|
||||
\\ int i = 0;
|
||||
\\ while (a > 0) {
|
||||
\\ a >>= 1;
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn log2(_arg_a: u32) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var i: c_int = 0;
|
||||
\\ while (a > @as(c_uint, 0)) {
|
||||
\\ a >>= @as(u5, 1);
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("anonymous enum",
|
||||
\\enum {
|
||||
\\ One,
|
||||
@@ -1123,72 +2473,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\pub const Two = 1;
|
||||
});
|
||||
|
||||
cases.addC("function call",
|
||||
\\static void bar(void) { }
|
||||
\\static int baz(void) { return 0; }
|
||||
\\void foo(void) {
|
||||
\\ bar();
|
||||
\\ baz();
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn bar() void {}
|
||||
\\pub fn baz() c_int {
|
||||
\\ return 0;
|
||||
\\}
|
||||
\\pub export fn foo() void {
|
||||
\\ bar();
|
||||
\\ _ = baz();
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("field access expression",
|
||||
\\struct Foo {
|
||||
\\ int field;
|
||||
\\};
|
||||
\\int read_field(struct Foo *foo) {
|
||||
\\ return foo->field;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_Foo = extern struct {
|
||||
\\ field: c_int,
|
||||
\\};
|
||||
\\pub export fn read_field(foo: [*c]struct_Foo) c_int {
|
||||
\\ return foo.*.field;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("null statements",
|
||||
\\void foo(void) {
|
||||
\\ ;;;;;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ {}
|
||||
\\ {}
|
||||
\\ {}
|
||||
\\ {}
|
||||
\\ {}
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("undefined array global",
|
||||
\\int array[100];
|
||||
, &[_][]const u8{
|
||||
\\pub var array: [100]c_int = undefined;
|
||||
});
|
||||
|
||||
cases.addC("array access",
|
||||
\\int array[100];
|
||||
\\int foo(int index) {
|
||||
\\ return array[index];
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub var array: [100]c_int = undefined;
|
||||
\\pub export fn foo(index: c_int) c_int {
|
||||
\\ return array[index];
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("c style cast",
|
||||
\\int float_to_int(float a) {
|
||||
\\ return (int)a;
|
||||
@@ -1199,47 +2483,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("void cast",
|
||||
\\void foo(int a) {
|
||||
\\ (void) a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo(a: c_int) void {
|
||||
\\ _ = a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("implicit cast to void *",
|
||||
\\void *foo(unsigned short *x) {
|
||||
\\ return x;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo(x: [*c]c_ushort) ?*c_void {
|
||||
\\ return @ptrCast(?*c_void, x);
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("sizeof",
|
||||
\\#include <stddef.h>
|
||||
\\size_t size_of(void) {
|
||||
\\ return sizeof(int);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn size_of() usize {
|
||||
\\ return @sizeOf(c_int);
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("null pointer implicit cast",
|
||||
\\int* foo(void) {
|
||||
\\ return 0;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() [*c]c_int {
|
||||
\\ return null;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("comma operator",
|
||||
\\int foo(void) {
|
||||
\\ return 1, 2;
|
||||
@@ -1253,6 +2496,93 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("escape sequences",
|
||||
\\const char *escapes() {
|
||||
\\char a = '\'',
|
||||
\\ b = '\\',
|
||||
\\ c = '\a',
|
||||
\\ d = '\b',
|
||||
\\ e = '\f',
|
||||
\\ f = '\n',
|
||||
\\ g = '\r',
|
||||
\\ h = '\t',
|
||||
\\ i = '\v',
|
||||
\\ j = '\0',
|
||||
\\ k = '\"';
|
||||
\\ return "\'\\\a\b\f\n\r\t\v\0\"";
|
||||
\\}
|
||||
\\
|
||||
, &[_][]const u8{
|
||||
\\pub export fn escapes() [*c]const u8 {
|
||||
\\ var a: u8 = @as(u8, '\'');
|
||||
\\ var b: u8 = @as(u8, '\\');
|
||||
\\ var c: u8 = @as(u8, '\x07');
|
||||
\\ var d: u8 = @as(u8, '\x08');
|
||||
\\ var e: u8 = @as(u8, '\x0c');
|
||||
\\ var f: u8 = @as(u8, '\n');
|
||||
\\ var g: u8 = @as(u8, '\r');
|
||||
\\ var h: u8 = @as(u8, '\t');
|
||||
\\ var i: u8 = @as(u8, '\x0b');
|
||||
\\ var j: u8 = @as(u8, '\x00');
|
||||
\\ var k: u8 = @as(u8, '\"');
|
||||
\\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
|
||||
\\}
|
||||
\\
|
||||
});
|
||||
|
||||
cases.addC("do loop",
|
||||
\\void foo(void) {
|
||||
\\ int a = 2;
|
||||
\\ do {
|
||||
\\ a--;
|
||||
\\ } while (a != 0);
|
||||
\\
|
||||
\\ int b = 2;
|
||||
\\ do
|
||||
\\ b--;
|
||||
\\ while (b != 0);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ var a: c_int = 2;
|
||||
\\ while (true) {
|
||||
\\ a -= 1;
|
||||
\\ if (!(a != 0)) break;
|
||||
\\ }
|
||||
\\ var b: c_int = 2;
|
||||
\\ while (true) {
|
||||
\\ b -= 1;
|
||||
\\ if (!(b != 0)) break;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("==, !=",
|
||||
\\int max(int a, int b) {
|
||||
\\ if (a == b)
|
||||
\\ return a;
|
||||
\\ if (a != b)
|
||||
\\ return b;
|
||||
\\ return a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(a: c_int, b: c_int) c_int {
|
||||
\\ if (a == b) return a;
|
||||
\\ if (a != b) return b;
|
||||
\\ return a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("bitwise binary operators",
|
||||
\\int max(int a, int b) {
|
||||
\\ return (a & b) ^ (a | b);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(a: c_int, b: c_int) c_int {
|
||||
\\ return (a & b) ^ (a | b);
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("statement expression",
|
||||
\\int foo(void) {
|
||||
\\ return ({
|
||||
@@ -1269,23 +2599,273 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("__extension__ cast",
|
||||
\\int foo(void) {
|
||||
\\ return __extension__ 1;
|
||||
cases.addC("field access expression",
|
||||
\\struct Foo {
|
||||
\\ int field;
|
||||
\\};
|
||||
\\int read_field(struct Foo *foo) {
|
||||
\\ return foo->field;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() c_int {
|
||||
\\ return 1;
|
||||
\\pub const struct_Foo = extern struct {
|
||||
\\ field: c_int,
|
||||
\\};
|
||||
\\pub export fn read_field(foo: [*c]struct_Foo) c_int {
|
||||
\\ return foo.*.field;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("bitshift",
|
||||
\\int foo(void) {
|
||||
\\ return (1 << 2) >> 1;
|
||||
cases.addC("array access",
|
||||
\\int array[100];
|
||||
\\int foo(int index) {
|
||||
\\ return array[index];
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() c_int {
|
||||
\\ return (1 << @as(@import("std").math.Log2Int(c_int), 2)) >> @as(@import("std").math.Log2Int(c_int), 1);
|
||||
\\pub var array: [100]c_int = undefined;
|
||||
\\pub export fn foo(index: c_int) c_int {
|
||||
\\ return array[index];
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("logical and, logical or",
|
||||
\\int max(int a, int b) {
|
||||
\\ if (a < b || a == b)
|
||||
\\ return b;
|
||||
\\ if (a >= b && a == b)
|
||||
\\ return a;
|
||||
\\ return a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(a: c_int, b: c_int) c_int {
|
||||
\\ if ((a < b) or (a == b)) return b;
|
||||
\\ if ((a >= b) and (a == b)) return a;
|
||||
\\ return a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("if statement",
|
||||
\\int max(int a, int b) {
|
||||
\\ if (a < b)
|
||||
\\ return b;
|
||||
\\
|
||||
\\ if (a < b)
|
||||
\\ return b;
|
||||
\\ else
|
||||
\\ return a;
|
||||
\\
|
||||
\\ if (a < b) ; else ;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn max(a: c_int, b: c_int) c_int {
|
||||
\\ if (a < b) return b;
|
||||
\\ if (a < b) return b else return a;
|
||||
\\ if (a < b) {} else {}
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("variable name shadowing",
|
||||
\\int foo(void) {
|
||||
\\ int x = 1;
|
||||
\\ {
|
||||
\\ int x = 2;
|
||||
\\ x += 1;
|
||||
\\ }
|
||||
\\ return x;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo() c_int {
|
||||
\\ var x: c_int = 1;
|
||||
\\ {
|
||||
\\ var x_0: c_int = 2;
|
||||
\\ x_0 += 1;
|
||||
\\ }
|
||||
\\ return x;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("if on non-bool",
|
||||
\\enum SomeEnum { A, B, C };
|
||||
\\int if_none_bool(int a, float b, void *c, enum SomeEnum d) {
|
||||
\\ if (a) return 0;
|
||||
\\ if (b) return 1;
|
||||
\\ if (c) return 2;
|
||||
\\ if (d) return 3;
|
||||
\\ return 4;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub const A = enum_SomeEnum.A;
|
||||
\\pub const B = enum_SomeEnum.B;
|
||||
\\pub const C = enum_SomeEnum.C;
|
||||
\\pub const enum_SomeEnum = extern enum {
|
||||
\\ A,
|
||||
\\ B,
|
||||
\\ C,
|
||||
\\};
|
||||
\\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int {
|
||||
\\ if (a != 0) return 0;
|
||||
\\ if (b != 0) return 1;
|
||||
\\ if (c != null) return 2;
|
||||
\\ if (d != @bitCast(enum_SomeEnum, @as(@TagType(enum_SomeEnum), 0))) return 3;
|
||||
\\ return 4;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addAllowWarnings("simple data types",
|
||||
\\#include <stdint.h>
|
||||
\\int foo(char a, unsigned char b, signed char c);
|
||||
\\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype
|
||||
\\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
|
||||
\\void baz(int8_t a, int16_t b, int32_t c, int64_t d);
|
||||
, &[_][]const u8{
|
||||
\\pub extern fn foo(a: u8, b: u8, c: i8) c_int;
|
||||
,
|
||||
\\pub extern fn bar(a: u8, b: u16, c: u32, d: u64) void;
|
||||
,
|
||||
\\pub extern fn baz(a: i8, b: i16, c: i32, d: i64) void;
|
||||
});
|
||||
|
||||
cases.addC("simple function",
|
||||
\\int abs(int a) {
|
||||
\\ return a < 0 ? -a : a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn abs(a: c_int) c_int {
|
||||
\\ return if (a < 0) -a else a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("post increment",
|
||||
\\unsigned foo1(unsigned a) {
|
||||
\\ a++;
|
||||
\\ return a;
|
||||
\\}
|
||||
\\int foo2(int a) {
|
||||
\\ a++;
|
||||
\\ return a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo1(_arg_a: c_uint) c_uint {
|
||||
\\ var a = _arg_a;
|
||||
\\ a +%= 1;
|
||||
\\ return a;
|
||||
\\}
|
||||
\\pub export fn foo2(_arg_a: c_int) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ a += 1;
|
||||
\\ return a;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("deref function pointer",
|
||||
\\void foo(void) {}
|
||||
\\int baz(void) { return 0; }
|
||||
\\void bar(void) {
|
||||
\\ void(*f)(void) = foo;
|
||||
\\ int(*b)(void) = baz;
|
||||
\\ f();
|
||||
\\ (*(f))();
|
||||
\\ foo();
|
||||
\\ b();
|
||||
\\ (*(b))();
|
||||
\\ baz();
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {}
|
||||
\\pub export fn baz() c_int {
|
||||
\\ return 0;
|
||||
\\}
|
||||
\\pub export fn bar() void {
|
||||
\\ var f: ?extern fn () void = foo;
|
||||
\\ var b: ?extern fn () c_int = baz;
|
||||
\\ f.?();
|
||||
\\ f.?();
|
||||
\\ foo();
|
||||
\\ _ = b.?();
|
||||
\\ _ = b.?();
|
||||
\\ _ = baz();
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("pre increment/decrement",
|
||||
\\void foo(void) {
|
||||
\\ int i = 0;
|
||||
\\ unsigned u = 0;
|
||||
\\ ++i;
|
||||
\\ --i;
|
||||
\\ ++u;
|
||||
\\ --u;
|
||||
\\ i = ++i;
|
||||
\\ i = --i;
|
||||
\\ u = ++u;
|
||||
\\ u = --u;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ var i: c_int = 0;
|
||||
\\ var u: c_uint = @as(c_uint, 0);
|
||||
\\ i += 1;
|
||||
\\ i -= 1;
|
||||
\\ u +%= 1;
|
||||
\\ u -%= 1;
|
||||
\\ i = (x: {
|
||||
\\ const _ref = &i;
|
||||
\\ _ref.* += 1;
|
||||
\\ break :x _ref.*;
|
||||
\\ });
|
||||
\\ i = (x: {
|
||||
\\ const _ref = &i;
|
||||
\\ _ref.* -= 1;
|
||||
\\ break :x _ref.*;
|
||||
\\ });
|
||||
\\ u = (x: {
|
||||
\\ const _ref = &u;
|
||||
\\ _ref.* +%= 1;
|
||||
\\ break :x _ref.*;
|
||||
\\ });
|
||||
\\ u = (x: {
|
||||
\\ const _ref = &u;
|
||||
\\ _ref.* -%= 1;
|
||||
\\ break :x _ref.*;
|
||||
\\ });
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("shift right assign",
|
||||
\\int log2(unsigned a) {
|
||||
\\ int i = 0;
|
||||
\\ while (a > 0) {
|
||||
\\ a >>= 1;
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn log2(_arg_a: c_uint) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var i: c_int = 0;
|
||||
\\ while (a > @as(c_uint, 0)) {
|
||||
\\ a >>= @as(@import("std").math.Log2Int(c_uint), 1);
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("shift right assign with a fixed size type",
|
||||
\\#include <stdint.h>
|
||||
\\int log2(uint32_t a) {
|
||||
\\ int i = 0;
|
||||
\\ while (a > 0) {
|
||||
\\ a >>= 1;
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn log2(_arg_a: u32) c_int {
|
||||
\\ var a = _arg_a;
|
||||
\\ var i: c_int = 0;
|
||||
\\ while (a > @as(c_uint, 0)) {
|
||||
\\ a >>= @as(u5, 1);
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
});
|
||||
|
||||
@@ -1453,383 +3033,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("pre increment/decrement",
|
||||
\\void foo(void) {
|
||||
\\ int i = 0;
|
||||
\\ unsigned u = 0;
|
||||
\\ ++i;
|
||||
\\ --i;
|
||||
\\ ++u;
|
||||
\\ --u;
|
||||
\\ i = ++i;
|
||||
\\ i = --i;
|
||||
\\ u = ++u;
|
||||
\\ u = --u;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ var i: c_int = 0;
|
||||
\\ var u: c_uint = @as(c_uint, 0);
|
||||
\\ i += 1;
|
||||
\\ i -= 1;
|
||||
\\ u +%= 1;
|
||||
\\ u -%= 1;
|
||||
\\ i = (x: {
|
||||
\\ const _ref = &i;
|
||||
\\ _ref.* += 1;
|
||||
\\ break :x _ref.*;
|
||||
\\ });
|
||||
\\ i = (x: {
|
||||
\\ const _ref = &i;
|
||||
\\ _ref.* -= 1;
|
||||
\\ break :x _ref.*;
|
||||
\\ });
|
||||
\\ u = (x: {
|
||||
\\ const _ref = &u;
|
||||
\\ _ref.* +%= 1;
|
||||
\\ break :x _ref.*;
|
||||
\\ });
|
||||
\\ u = (x: {
|
||||
\\ const _ref = &u;
|
||||
\\ _ref.* -%= 1;
|
||||
\\ break :x _ref.*;
|
||||
\\ });
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("do loop",
|
||||
\\void foo(void) {
|
||||
\\ int a = 2;
|
||||
\\ do {
|
||||
\\ a--;
|
||||
\\ } while (a != 0);
|
||||
\\
|
||||
\\ int b = 2;
|
||||
\\ do
|
||||
\\ b--;
|
||||
\\ while (b != 0);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ var a: c_int = 2;
|
||||
\\ while (true) {
|
||||
\\ a -= 1;
|
||||
\\ if (!(a != 0)) break;
|
||||
\\ }
|
||||
\\ var b: c_int = 2;
|
||||
\\ while (true) {
|
||||
\\ b -= 1;
|
||||
\\ if (!(b != 0)) break;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("deref function pointer",
|
||||
\\void foo(void) {}
|
||||
\\int baz(void) { return 0; }
|
||||
\\void bar(void) {
|
||||
\\ void(*f)(void) = foo;
|
||||
\\ int(*b)(void) = baz;
|
||||
\\ f();
|
||||
\\ (*(f))();
|
||||
\\ foo();
|
||||
\\ b();
|
||||
\\ (*(b))();
|
||||
\\ baz();
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {}
|
||||
\\pub export fn baz() c_int {
|
||||
\\ return 0;
|
||||
\\}
|
||||
\\pub export fn bar() void {
|
||||
\\ var f: ?extern fn () void = foo;
|
||||
\\ var b: ?extern fn () c_int = baz;
|
||||
\\ f.?();
|
||||
\\ f.?();
|
||||
\\ foo();
|
||||
\\ _ = b.?();
|
||||
\\ _ = b.?();
|
||||
\\ _ = baz();
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("normal deref",
|
||||
\\void foo(int *x) {
|
||||
\\ *x = 1;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo(x: [*c]c_int) void {
|
||||
\\ x.?.* = 1;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("simple union",
|
||||
\\union Foo {
|
||||
\\ int x;
|
||||
\\ double y;
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const union_Foo = extern union {
|
||||
\\ x: c_int,
|
||||
\\ y: f64,
|
||||
\\};
|
||||
,
|
||||
\\pub const Foo = union_Foo;
|
||||
});
|
||||
|
||||
cases.add("address of operator",
|
||||
\\int foo(void) {
|
||||
\\ int x = 1234;
|
||||
\\ int *ptr = &x;
|
||||
\\ return *ptr;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo() c_int {
|
||||
\\ var x: c_int = 1234;
|
||||
\\ var ptr: [*c]c_int = &x;
|
||||
\\ return ptr.?.*;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("string literal",
|
||||
\\const char *foo(void) {
|
||||
\\ return "bar";
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo() [*c]const u8 {
|
||||
\\ return "bar";
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("return void",
|
||||
\\void foo(void) {
|
||||
\\ return;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo() void {
|
||||
\\ return;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("for loop",
|
||||
\\void foo(void) {
|
||||
\\ for (int i = 0; i < 10; i += 1) { }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo() void {
|
||||
\\ {
|
||||
\\ var i: c_int = 0;
|
||||
\\ while (i < 10) : (i += 1) {}
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("empty for loop",
|
||||
\\void foo(void) {
|
||||
\\ for (;;) { }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo() void {
|
||||
\\ while (true) {}
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("break statement",
|
||||
\\void foo(void) {
|
||||
\\ for (;;) {
|
||||
\\ break;
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo() void {
|
||||
\\ while (true) {
|
||||
\\ break;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("continue statement",
|
||||
\\void foo(void) {
|
||||
\\ for (;;) {
|
||||
\\ continue;
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo() void {
|
||||
\\ while (true) {
|
||||
\\ continue;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("variable name shadowing",
|
||||
\\int foo(void) {
|
||||
\\ int x = 1;
|
||||
\\ {
|
||||
\\ int x = 2;
|
||||
\\ x += 1;
|
||||
\\ }
|
||||
\\ return x;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo() c_int {
|
||||
\\ var x: c_int = 1;
|
||||
\\ {
|
||||
\\ var x_0: c_int = 2;
|
||||
\\ x_0 += 1;
|
||||
\\ }
|
||||
\\ return x;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("pointer casting",
|
||||
\\float *ptrcast(int *a) {
|
||||
\\ return (float *)a;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\fn ptrcast(a: [*c]c_int) [*c]f32 {
|
||||
\\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("bin not",
|
||||
\\int foo(int x) {
|
||||
\\ return ~x;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo(x: c_int) c_int {
|
||||
\\ return ~x;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("bool not",
|
||||
\\int foo(int a, float b, void *c) {
|
||||
\\ return !(a == 0);
|
||||
\\ return !a;
|
||||
\\ return !b;
|
||||
\\ return !c;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int {
|
||||
\\ return !(a == 0);
|
||||
\\ return !(a != 0);
|
||||
\\ return !(b != 0);
|
||||
\\ return !(c != null);
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("primitive types included in defined symbols",
|
||||
\\int foo(int u32) {
|
||||
\\ return u32;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn foo(u32_0: c_int) c_int {
|
||||
\\ return u32_0;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("if on non-bool",
|
||||
\\enum SomeEnum { A, B, C };
|
||||
\\int if_none_bool(int a, float b, void *c, enum SomeEnum d) {
|
||||
\\ if (a) return 0;
|
||||
\\ if (b) return 1;
|
||||
\\ if (c) return 2;
|
||||
\\ if (d) return 3;
|
||||
\\ return 4;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub const A = enum_SomeEnum.A;
|
||||
\\pub const B = enum_SomeEnum.B;
|
||||
\\pub const C = enum_SomeEnum.C;
|
||||
\\pub const enum_SomeEnum = extern enum {
|
||||
\\ A,
|
||||
\\ B,
|
||||
\\ C,
|
||||
\\};
|
||||
\\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int {
|
||||
\\ if (a != 0) return 0;
|
||||
\\ if (b != 0) return 1;
|
||||
\\ if (c != null) return 2;
|
||||
\\ if (d != @bitCast(enum_SomeEnum, @as(@TagType(enum_SomeEnum), 0))) return 3;
|
||||
\\ return 4;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("while on non-bool",
|
||||
\\int while_none_bool(int a, float b, void *c) {
|
||||
\\ while (a) return 0;
|
||||
\\ while (b) return 1;
|
||||
\\ while (c) return 2;
|
||||
\\ return 3;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
|
||||
\\ while (a != 0) return 0;
|
||||
\\ while (b != 0) return 1;
|
||||
\\ while (c != null) return 2;
|
||||
\\ return 3;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("for on non-bool",
|
||||
\\int for_none_bool(int a, float b, void *c) {
|
||||
\\ for (;a;) return 0;
|
||||
\\ for (;b;) return 1;
|
||||
\\ for (;c;) return 2;
|
||||
\\ return 3;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
|
||||
\\ while (a != 0) return 0;
|
||||
\\ while (b != 0) return 1;
|
||||
\\ while (c != null) return 2;
|
||||
\\ return 3;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("switch on int",
|
||||
\\int switch_fn(int i) {
|
||||
\\ int res = 0;
|
||||
\\ switch (i) {
|
||||
\\ case 0:
|
||||
\\ res = 1;
|
||||
\\ case 1:
|
||||
\\ res = 2;
|
||||
\\ default:
|
||||
\\ res = 3 * i;
|
||||
\\ break;
|
||||
\\ case 2:
|
||||
\\ res = 5;
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub fn switch_fn(i: c_int) c_int {
|
||||
\\ var res: c_int = 0;
|
||||
\\ __switch: {
|
||||
\\ __case_2: {
|
||||
\\ __default: {
|
||||
\\ __case_1: {
|
||||
\\ __case_0: {
|
||||
\\ switch (i) {
|
||||
\\ 0 => break :__case_0,
|
||||
\\ 1 => break :__case_1,
|
||||
\\ else => break :__default,
|
||||
\\ 2 => break :__case_2,
|
||||
\\ }
|
||||
\\ }
|
||||
\\ res = 1;
|
||||
\\ }
|
||||
\\ res = 2;
|
||||
\\ }
|
||||
\\ res = (3 * i);
|
||||
\\ break :__switch;
|
||||
\\ }
|
||||
\\ res = 5;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("implicit casts",
|
||||
\\#include <stdbool.h>
|
||||
\\
|
||||
@@ -1885,206 +3088,85 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("pointer conversion with different alignment",
|
||||
\\void test_ptr_cast() {
|
||||
\\ void *p;
|
||||
\\ {
|
||||
\\ char *to_char = (char *)p;
|
||||
\\ short *to_short = (short *)p;
|
||||
\\ int *to_int = (int *)p;
|
||||
\\ long long *to_longlong = (long long *)p;
|
||||
\\ }
|
||||
\\ {
|
||||
\\ char *to_char = p;
|
||||
\\ short *to_short = p;
|
||||
\\ int *to_int = p;
|
||||
\\ long long *to_longlong = p;
|
||||
\\ }
|
||||
cases.addC("function call",
|
||||
\\static void bar(void) { }
|
||||
\\void foo(int *(baz)(void)) {
|
||||
\\ bar();
|
||||
\\ baz();
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn test_ptr_cast() void {
|
||||
\\ var p: ?*c_void = undefined;
|
||||
\\ {
|
||||
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
|
||||
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
|
||||
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
|
||||
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
|
||||
\\ }
|
||||
\\ {
|
||||
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
|
||||
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
|
||||
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
|
||||
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
|
||||
\\ }
|
||||
\\pub fn bar() void {}
|
||||
\\pub export fn foo(baz: ?extern fn () [*c]c_int) void {
|
||||
\\ bar();
|
||||
\\ _ = baz.?();
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.addC("escape sequences",
|
||||
\\const char *escapes() {
|
||||
\\char a = '\'',
|
||||
\\ b = '\\',
|
||||
\\ c = '\a',
|
||||
\\ d = '\b',
|
||||
\\ e = '\f',
|
||||
\\ f = '\n',
|
||||
\\ g = '\r',
|
||||
\\ h = '\t',
|
||||
\\ i = '\v',
|
||||
\\ j = '\0',
|
||||
\\ k = '\"';
|
||||
\\ return "\'\\\a\b\f\n\r\t\v\0\"";
|
||||
\\}
|
||||
\\
|
||||
cases.add("macro defines string literal with hex",
|
||||
\\#define FOO "aoeu\xab derp"
|
||||
\\#define FOO2 "aoeu\x0007a derp"
|
||||
\\#define FOO_CHAR '\xfF'
|
||||
, &[_][]const u8{
|
||||
\\pub export fn escapes() [*c]const u8 {
|
||||
\\ var a: u8 = @as(u8, '\'');
|
||||
\\ var b: u8 = @as(u8, '\\');
|
||||
\\ var c: u8 = @as(u8, '\x07');
|
||||
\\ var d: u8 = @as(u8, '\x08');
|
||||
\\ var e: u8 = @as(u8, '\x0c');
|
||||
\\ var f: u8 = @as(u8, '\n');
|
||||
\\ var g: u8 = @as(u8, '\r');
|
||||
\\ var h: u8 = @as(u8, '\t');
|
||||
\\ var i: u8 = @as(u8, '\x0b');
|
||||
\\ var j: u8 = @as(u8, '\x00');
|
||||
\\ var k: u8 = @as(u8, '\"');
|
||||
\\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
|
||||
\\}
|
||||
\\
|
||||
});
|
||||
|
||||
if (builtin.os != builtin.Os.windows) {
|
||||
// sysv_abi not currently supported on windows
|
||||
cases.add("Macro qualified functions",
|
||||
\\void __attribute__((sysv_abi)) foo(void);
|
||||
, &[_][]const u8{
|
||||
\\pub extern fn foo() void;
|
||||
});
|
||||
}
|
||||
|
||||
/////////////// Cases for only stage1 because stage2 behavior is better ////////////////
|
||||
cases.addC("Parameterless function prototypes",
|
||||
\\void foo() {}
|
||||
\\void bar(void) {}
|
||||
, &[_][]const u8{
|
||||
\\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 const FOO = "aoeu\xab derp";
|
||||
,
|
||||
\\pub inline fn foo() void {
|
||||
\\ return fn_ptr.?();
|
||||
\\}
|
||||
\\pub const FOO2 = "aoeuz derp";
|
||||
,
|
||||
\\pub extern var fn_ptr2: ?extern fn (c_int, f32) u8;
|
||||
\\pub const FOO_CHAR = 255;
|
||||
});
|
||||
|
||||
cases.add("macro defines string literal with octal",
|
||||
\\#define FOO "aoeu\023 derp"
|
||||
\\#define FOO2 "aoeu\0234 derp"
|
||||
\\#define FOO_CHAR '\077'
|
||||
, &[_][]const u8{
|
||||
\\pub const FOO = "aoeu\x13 derp";
|
||||
,
|
||||
\\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;
|
||||
\\pub const FOO2 = "aoeu\x134 derp";
|
||||
,
|
||||
\\pub const FOO_CHAR = 63;
|
||||
});
|
||||
|
||||
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;
|
||||
cases.add("enums",
|
||||
\\enum Foo {
|
||||
\\ FooA,
|
||||
\\ FooB,
|
||||
\\ Foo1,
|
||||
\\};
|
||||
\\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 const enum_Foo = extern enum {
|
||||
\\ A,
|
||||
\\ B,
|
||||
\\ @"1",
|
||||
\\};
|
||||
,
|
||||
\\pub extern var glProcs: union_OpenGLProcs;
|
||||
\\pub const FooA = enum_Foo.A;
|
||||
,
|
||||
\\pub const glClearPFN = PFNGLCLEARPROC;
|
||||
\\pub const FooB = enum_Foo.B;
|
||||
,
|
||||
\\pub inline fn glClearUnion(arg0: GLbitfield) void {
|
||||
\\ return glProcs.gl.Clear.?(arg0);
|
||||
\\}
|
||||
\\pub const Foo1 = enum_Foo.@"1";
|
||||
,
|
||||
\\pub const OpenGLProcs = union_OpenGLProcs;
|
||||
\\pub const Foo = enum_Foo;
|
||||
});
|
||||
|
||||
cases.add("macro pointer cast",
|
||||
\\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
|
||||
cases.add("enums",
|
||||
\\enum Foo {
|
||||
\\ FooA = 2,
|
||||
\\ FooB = 5,
|
||||
\\ Foo1,
|
||||
\\};
|
||||
, &[_][]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);
|
||||
\\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;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user