mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
1356 lines
50 KiB
Zig
Vendored
1356 lines
50 KiB
Zig
Vendored
const std = @import("std");
|
|
const math = std.math;
|
|
const mem = std.mem;
|
|
const assert = std.debug.assert;
|
|
|
|
const aro = @import("aro");
|
|
const CToken = aro.Tokenizer.Token;
|
|
|
|
const ast = @import("ast.zig");
|
|
const builtins = @import("builtins.zig");
|
|
const ZigNode = ast.Node;
|
|
const ZigTag = ZigNode.Tag;
|
|
const Scope = @import("Scope.zig");
|
|
const Translator = @import("Translator.zig");
|
|
|
|
const Error = Translator.Error;
|
|
pub const ParseError = Error || error{ParseError};
|
|
|
|
const MacroTranslator = @This();
|
|
|
|
t: *Translator,
|
|
macro: aro.Preprocessor.Macro,
|
|
name: []const u8,
|
|
|
|
tokens: []const CToken,
|
|
source: []const u8,
|
|
i: usize = 0,
|
|
/// If an object macro references a global var it needs to be converted into
|
|
/// an inline function.
|
|
refs_var_decl: bool = false,
|
|
|
|
fn peek(mt: *MacroTranslator) CToken.Id {
|
|
if (mt.i >= mt.tokens.len) return .eof;
|
|
return mt.tokens[mt.i].id;
|
|
}
|
|
|
|
fn eat(mt: *MacroTranslator, expected_id: CToken.Id) bool {
|
|
if (mt.peek() == expected_id) {
|
|
mt.i += 1;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
fn expect(mt: *MacroTranslator, expected_id: CToken.Id) ParseError!void {
|
|
const next_id = mt.peek();
|
|
if (next_id != expected_id and !(expected_id == .identifier and next_id == .extended_identifier)) {
|
|
try mt.fail(
|
|
"unable to translate C expr: expected '{s}' instead got '{s}'",
|
|
.{ expected_id.symbol(), next_id.symbol() },
|
|
);
|
|
return error.ParseError;
|
|
}
|
|
mt.i += 1;
|
|
}
|
|
|
|
fn fail(mt: *MacroTranslator, comptime fmt: []const u8, args: anytype) !void {
|
|
return mt.t.failDeclExtra(&mt.t.global_scope.base, mt.macro.loc, mt.name, fmt, args);
|
|
}
|
|
|
|
fn tokSlice(mt: *const MacroTranslator) []const u8 {
|
|
const tok = mt.tokens[mt.i];
|
|
return mt.source[tok.start..tok.end];
|
|
}
|
|
|
|
pub fn transFnMacro(mt: *MacroTranslator) ParseError!void {
|
|
var block_scope = try Scope.Block.init(mt.t, &mt.t.global_scope.base, false);
|
|
defer block_scope.deinit();
|
|
const scope = &block_scope.base;
|
|
|
|
const fn_params = try mt.t.arena.alloc(ast.Payload.Param, mt.macro.params.len);
|
|
for (fn_params, mt.macro.params) |*param, param_name| {
|
|
const mangled_name = try block_scope.makeMangledName(param_name);
|
|
param.* = .{
|
|
.is_noalias = false,
|
|
.name = mangled_name,
|
|
.type = ZigTag.@"anytype".init(),
|
|
};
|
|
try block_scope.discardVariable(mangled_name);
|
|
}
|
|
|
|
// #define FOO(x)
|
|
if (mt.peek() == .eof) {
|
|
try block_scope.statements.append(mt.t.gpa, ZigTag.return_void.init());
|
|
|
|
const fn_decl = try ZigTag.pub_inline_fn.create(mt.t.arena, .{
|
|
.name = mt.name,
|
|
.params = fn_params,
|
|
.return_type = ZigTag.void_type.init(),
|
|
.body = try block_scope.complete(),
|
|
});
|
|
try mt.t.addTopLevelDecl(mt.name, fn_decl);
|
|
return;
|
|
}
|
|
|
|
const expr = try mt.parseCExpr(scope);
|
|
const last = mt.peek();
|
|
if (last != .eof)
|
|
return mt.fail("unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
|
|
|
|
const typeof_arg = if (expr.castTag(.block)) |some| blk: {
|
|
const stmts = some.data.stmts;
|
|
const blk_last = stmts[stmts.len - 1];
|
|
const br = blk_last.castTag(.break_val).?;
|
|
break :blk br.data.val;
|
|
} else expr;
|
|
|
|
const return_type = ret: {
|
|
if (typeof_arg.castTag(.helper_call)) |some| {
|
|
if (std.mem.eql(u8, some.data.name, "cast")) {
|
|
break :ret some.data.args[0];
|
|
}
|
|
}
|
|
if (typeof_arg.castTag(.std_mem_zeroinit)) |some| break :ret some.data.lhs;
|
|
if (typeof_arg.castTag(.std_mem_zeroes)) |some| break :ret some.data;
|
|
break :ret try ZigTag.typeof.create(mt.t.arena, typeof_arg);
|
|
};
|
|
|
|
const return_expr = try ZigTag.@"return".create(mt.t.arena, expr);
|
|
try block_scope.statements.append(mt.t.gpa, return_expr);
|
|
|
|
const fn_decl = try ZigTag.pub_inline_fn.create(mt.t.arena, .{
|
|
.name = mt.name,
|
|
.params = fn_params,
|
|
.return_type = return_type,
|
|
.body = try block_scope.complete(),
|
|
});
|
|
try mt.t.addTopLevelDecl(mt.name, fn_decl);
|
|
}
|
|
|
|
pub fn transMacro(mt: *MacroTranslator) ParseError!void {
|
|
const scope = &mt.t.global_scope.base;
|
|
|
|
// Check if the macro only uses other blank macros.
|
|
while (true) {
|
|
switch (mt.peek()) {
|
|
.identifier, .extended_identifier => {
|
|
if (mt.t.global_scope.blank_macros.contains(mt.tokSlice())) {
|
|
mt.i += 1;
|
|
continue;
|
|
}
|
|
},
|
|
.eof, .nl => {
|
|
try mt.t.global_scope.blank_macros.put(mt.t.gpa, mt.name, {});
|
|
const init_node = try ZigTag.string_literal.create(mt.t.arena, "\"\"");
|
|
const var_decl = try ZigTag.pub_var_simple.create(mt.t.arena, .{ .name = mt.name, .init = init_node });
|
|
try mt.t.addTopLevelDecl(mt.name, var_decl);
|
|
return;
|
|
},
|
|
else => {},
|
|
}
|
|
break;
|
|
}
|
|
|
|
const init_node = try mt.parseCExpr(scope);
|
|
const last = mt.peek();
|
|
if (last != .eof)
|
|
return mt.fail("unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
|
|
|
|
const node = node: {
|
|
const var_decl = try ZigTag.pub_var_simple.create(mt.t.arena, .{ .name = mt.name, .init = init_node });
|
|
|
|
if (mt.t.getFnProto(var_decl)) |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.
|
|
break :node try mt.createMacroFn(mt.name, var_decl, proto_node);
|
|
} else if (mt.refs_var_decl) {
|
|
const return_type = try ZigTag.typeof.create(mt.t.arena, init_node);
|
|
const return_expr = try ZigTag.@"return".create(mt.t.arena, init_node);
|
|
const block = try ZigTag.block_single.create(mt.t.arena, return_expr);
|
|
|
|
const loc_str = try mt.t.locStr(mt.macro.loc);
|
|
const value = try std.fmt.allocPrint(mt.t.arena, "\n// {s}: warning: macro '{s}' contains a runtime value, translated to function", .{ loc_str, mt.name });
|
|
try scope.appendNode(try ZigTag.warning.create(mt.t.arena, value));
|
|
|
|
break :node try ZigTag.pub_inline_fn.create(mt.t.arena, .{
|
|
.name = mt.name,
|
|
.params = &.{},
|
|
.return_type = return_type,
|
|
.body = block,
|
|
});
|
|
}
|
|
|
|
break :node var_decl;
|
|
};
|
|
|
|
try mt.t.addTopLevelDecl(mt.name, node);
|
|
}
|
|
|
|
fn createMacroFn(mt: *MacroTranslator, name: []const u8, ref: ZigNode, proto_alias: *ast.Payload.Func) !ZigNode {
|
|
const gpa = mt.t.gpa;
|
|
const arena = mt.t.arena;
|
|
var fn_params: std.ArrayList(ast.Payload.Param) = .empty;
|
|
defer fn_params.deinit(gpa);
|
|
|
|
var block_scope = try Scope.Block.init(mt.t, &mt.t.global_scope.base, false);
|
|
defer block_scope.deinit();
|
|
|
|
for (proto_alias.data.params) |param| {
|
|
const param_name = try block_scope.makeMangledName(param.name orelse "arg");
|
|
|
|
try fn_params.append(gpa, .{
|
|
.name = param_name,
|
|
.type = param.type,
|
|
.is_noalias = param.is_noalias,
|
|
});
|
|
}
|
|
|
|
const init = if (ref.castTag(.var_decl)) |v|
|
|
v.data.init.?
|
|
else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v|
|
|
v.data.init
|
|
else
|
|
unreachable;
|
|
|
|
const unwrap_expr = try ZigTag.unwrap.create(arena, init);
|
|
const args = try arena.alloc(ZigNode, fn_params.items.len);
|
|
for (fn_params.items, 0..) |param, i| {
|
|
args[i] = try ZigTag.identifier.create(arena, param.name.?);
|
|
}
|
|
const call_expr = try ZigTag.call.create(arena, .{
|
|
.lhs = unwrap_expr,
|
|
.args = args,
|
|
});
|
|
const return_expr = try ZigTag.@"return".create(arena, call_expr);
|
|
const block = try ZigTag.block_single.create(arena, return_expr);
|
|
|
|
return ZigTag.pub_inline_fn.create(arena, .{
|
|
.name = name,
|
|
.params = try arena.dupe(ast.Payload.Param, fn_params.items),
|
|
.return_type = proto_alias.data.return_type,
|
|
.body = block,
|
|
});
|
|
}
|
|
|
|
fn parseCExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
const arena = mt.t.arena;
|
|
// TODO parseCAssignExpr here
|
|
var block_scope = try Scope.Block.init(mt.t, scope, true);
|
|
defer block_scope.deinit();
|
|
|
|
const node = try mt.parseCCondExpr(&block_scope.base);
|
|
if (!mt.eat(.comma)) return node;
|
|
|
|
var last = node;
|
|
while (true) {
|
|
// suppress result
|
|
const ignore = try ZigTag.discard.create(arena, .{ .should_skip = false, .value = last });
|
|
try block_scope.statements.append(mt.t.gpa, ignore);
|
|
|
|
last = try mt.parseCCondExpr(&block_scope.base);
|
|
if (!mt.eat(.comma)) break;
|
|
}
|
|
|
|
const break_node = try ZigTag.break_val.create(arena, .{
|
|
.label = block_scope.label,
|
|
.val = last,
|
|
});
|
|
try block_scope.statements.append(mt.t.gpa, break_node);
|
|
return try block_scope.complete();
|
|
}
|
|
|
|
fn parseCNumLit(mt: *MacroTranslator) ParseError!ZigNode {
|
|
const arena = mt.t.arena;
|
|
const lit_bytes = mt.tokSlice();
|
|
mt.i += 1;
|
|
|
|
var bytes = try std.ArrayList(u8).initCapacity(arena, lit_bytes.len + 3);
|
|
|
|
const prefix = aro.Tree.Token.NumberPrefix.fromString(lit_bytes);
|
|
switch (prefix) {
|
|
.binary => bytes.appendSliceAssumeCapacity("0b"),
|
|
.octal => bytes.appendSliceAssumeCapacity("0o"),
|
|
.hex => bytes.appendSliceAssumeCapacity("0x"),
|
|
.decimal => {},
|
|
}
|
|
|
|
const after_prefix = lit_bytes[prefix.stringLen()..];
|
|
const after_int = for (after_prefix, 0..) |c, i| switch (c) {
|
|
'.' => {
|
|
if (i == 0) {
|
|
bytes.appendAssumeCapacity('0');
|
|
}
|
|
break after_prefix[i..];
|
|
},
|
|
'e', 'E' => {
|
|
if (prefix != .hex) break after_prefix[i..];
|
|
bytes.appendAssumeCapacity(c);
|
|
},
|
|
'p', 'P' => break after_prefix[i..],
|
|
'0'...'9', 'a'...'d', 'A'...'D', 'f', 'F' => {
|
|
if (!prefix.digitAllowed(c)) break after_prefix[i..];
|
|
bytes.appendAssumeCapacity(c);
|
|
},
|
|
'\'' => {
|
|
bytes.appendAssumeCapacity('_');
|
|
},
|
|
else => break after_prefix[i..],
|
|
} else "";
|
|
|
|
const after_frac = frac: {
|
|
if (after_int.len == 0 or after_int[0] != '.') break :frac after_int;
|
|
bytes.appendAssumeCapacity('.');
|
|
for (after_int[1..], 1..) |c, i| {
|
|
if (c == '\'') {
|
|
bytes.appendAssumeCapacity('_');
|
|
continue;
|
|
}
|
|
if (!prefix.digitAllowed(c)) break :frac after_int[i..];
|
|
bytes.appendAssumeCapacity(c);
|
|
}
|
|
break :frac "";
|
|
};
|
|
|
|
const suffix_str = exponent: {
|
|
if (after_frac.len == 0) break :exponent after_frac;
|
|
switch (after_frac[0]) {
|
|
'e', 'E' => {},
|
|
'p', 'P' => if (prefix != .hex) break :exponent after_frac,
|
|
else => break :exponent after_frac,
|
|
}
|
|
bytes.appendAssumeCapacity(after_frac[0]);
|
|
for (after_frac[1..], 1..) |c, i| switch (c) {
|
|
'+', '-', '0'...'9' => {
|
|
bytes.appendAssumeCapacity(c);
|
|
},
|
|
'\'' => {
|
|
bytes.appendAssumeCapacity('_');
|
|
},
|
|
else => break :exponent after_frac[i..],
|
|
};
|
|
break :exponent "";
|
|
};
|
|
|
|
const is_float = after_int.len != suffix_str.len;
|
|
const suffix = aro.Tree.Token.NumberSuffix.fromString(suffix_str, if (is_float) .float else .int) orelse {
|
|
try mt.fail("invalid number suffix: '{s}'", .{suffix_str});
|
|
return error.ParseError;
|
|
};
|
|
if (suffix.isImaginary()) {
|
|
try mt.fail("TODO: imaginary literals", .{});
|
|
return error.ParseError;
|
|
}
|
|
if (suffix.isBitInt()) {
|
|
try mt.fail("TODO: _BitInt literals", .{});
|
|
return error.ParseError;
|
|
}
|
|
|
|
if (is_float) {
|
|
const type_node = try ZigTag.type.create(arena, switch (suffix) {
|
|
.F16 => "f16",
|
|
.F => "f32",
|
|
.None => "f64",
|
|
.L => "c_longdouble",
|
|
.W => "f80",
|
|
.Q, .F128 => "f128",
|
|
else => unreachable,
|
|
});
|
|
const rhs = try ZigTag.float_literal.create(arena, bytes.items);
|
|
return ZigTag.as.create(arena, .{ .lhs = type_node, .rhs = rhs });
|
|
} else {
|
|
const type_node = try ZigTag.type.create(arena, switch (suffix) {
|
|
.None => "c_int",
|
|
.U => "c_uint",
|
|
.L => "c_long",
|
|
.UL => "c_ulong",
|
|
.LL => "c_longlong",
|
|
.ULL => "c_ulonglong",
|
|
else => unreachable,
|
|
});
|
|
const value = std.fmt.parseInt(i128, bytes.items, 0) catch math.maxInt(i128);
|
|
|
|
// make the output less noisy by skipping promoteIntLiteral where
|
|
// it's guaranteed to not be required because of C standard type constraints
|
|
const guaranteed_to_fit = switch (suffix) {
|
|
.None => math.cast(i16, value) != null,
|
|
.U => math.cast(u16, value) != null,
|
|
.L => math.cast(i32, value) != null,
|
|
.UL => math.cast(u32, value) != null,
|
|
.LL => math.cast(i64, value) != null,
|
|
.ULL => math.cast(u64, value) != null,
|
|
else => unreachable,
|
|
};
|
|
|
|
const literal_node = try ZigTag.integer_literal.create(arena, bytes.items);
|
|
if (guaranteed_to_fit) {
|
|
return ZigTag.as.create(arena, .{ .lhs = type_node, .rhs = literal_node });
|
|
} else {
|
|
return mt.t.createHelperCallNode(.promoteIntLiteral, &.{ type_node, literal_node, try ZigTag.enum_literal.create(arena, @tagName(prefix)) });
|
|
}
|
|
}
|
|
}
|
|
|
|
fn zigifyEscapeSequences(mt: *MacroTranslator, slice: []const u8) ![]const u8 {
|
|
var source = slice;
|
|
for (source, 0..) |c, i| {
|
|
if (c == '\"' or c == '\'') {
|
|
source = source[i..];
|
|
break;
|
|
}
|
|
}
|
|
for (source) |c| {
|
|
if (c == '\\' or c == '\t') {
|
|
break;
|
|
}
|
|
} else return source;
|
|
const bytes = try mt.t.arena.alloc(u8, source.len * 2);
|
|
var state: enum {
|
|
start,
|
|
escape,
|
|
hex,
|
|
octal,
|
|
} = .start;
|
|
var i: usize = 0;
|
|
var count: u8 = 0;
|
|
var num: u8 = 0;
|
|
for (source) |c| {
|
|
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' => {
|
|
try mt.fail("macro tokenizing failed: TODO unicode escape sequences", .{});
|
|
return error.ParseError;
|
|
},
|
|
else => {
|
|
try mt.fail("macro tokenizing failed: unknown escape sequence", .{});
|
|
return error.ParseError;
|
|
},
|
|
}
|
|
i += 1;
|
|
if (state == .escape)
|
|
state = .start;
|
|
},
|
|
.start => {
|
|
if (c == '\t') {
|
|
bytes[i] = '\\';
|
|
i += 1;
|
|
bytes[i] = 't';
|
|
i += 1;
|
|
continue;
|
|
}
|
|
if (c == '\\') {
|
|
state = .escape;
|
|
}
|
|
bytes[i] = c;
|
|
i += 1;
|
|
},
|
|
.hex => {
|
|
switch (c) {
|
|
'0'...'9' => {
|
|
num = std.math.mul(u8, num, 16) catch {
|
|
try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
|
|
return error.ParseError;
|
|
};
|
|
num += c - '0';
|
|
},
|
|
'a'...'f' => {
|
|
num = std.math.mul(u8, num, 16) catch {
|
|
try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
|
|
return error.ParseError;
|
|
};
|
|
num += c - 'a' + 10;
|
|
},
|
|
'A'...'F' => {
|
|
num = std.math.mul(u8, num, 16) catch {
|
|
try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
|
|
return error.ParseError;
|
|
};
|
|
num += c - 'A' + 10;
|
|
},
|
|
else => {
|
|
i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
|
|
num = 0;
|
|
if (c == '\\')
|
|
state = .escape
|
|
else
|
|
state = .start;
|
|
bytes[i] = c;
|
|
i += 1;
|
|
},
|
|
}
|
|
},
|
|
.octal => {
|
|
const accept_digit = switch (c) {
|
|
// The maximum length of a octal literal is 3 digits
|
|
'0'...'7' => count < 3,
|
|
else => false,
|
|
};
|
|
|
|
if (accept_digit) {
|
|
count += 1;
|
|
num = std.math.mul(u8, num, 8) catch {
|
|
try mt.fail("macro tokenizing failed: octal literal overflowed", .{});
|
|
return error.ParseError;
|
|
};
|
|
num += c - '0';
|
|
} else {
|
|
i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
|
|
num = 0;
|
|
count = 0;
|
|
if (c == '\\')
|
|
state = .escape
|
|
else
|
|
state = .start;
|
|
bytes[i] = c;
|
|
i += 1;
|
|
}
|
|
},
|
|
}
|
|
}
|
|
if (state == .hex or state == .octal) {
|
|
i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
|
|
}
|
|
|
|
return bytes[0..i];
|
|
}
|
|
|
|
/// non-ASCII characters (mt > 127) are also treated as non-printable by fmtSliceEscapeLower.
|
|
/// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape
|
|
/// non-ASCII characters so that the Zig source we output will itself be UTF-8.
|
|
fn escapeUnprintables(mt: *MacroTranslator) ![]const u8 {
|
|
const slice = mt.tokSlice();
|
|
mt.i += 1;
|
|
|
|
const zigified = try mt.zigifyEscapeSequences(slice);
|
|
if (std.unicode.utf8ValidateSlice(zigified)) return zigified;
|
|
|
|
const formatter = std.ascii.hexEscape(zigified, .lower);
|
|
const encoded_size = @as(usize, @intCast(std.fmt.count("{f}", .{formatter})));
|
|
const output = try mt.t.arena.alloc(u8, encoded_size);
|
|
return std.fmt.bufPrint(output, "{f}", .{formatter}) catch |err| switch (err) {
|
|
error.NoSpaceLeft => unreachable,
|
|
else => |e| return e,
|
|
};
|
|
}
|
|
|
|
fn parseCPrimaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
const arena = mt.t.arena;
|
|
const tok = mt.peek();
|
|
switch (tok) {
|
|
.char_literal,
|
|
.char_literal_utf_8,
|
|
.char_literal_utf_16,
|
|
.char_literal_utf_32,
|
|
.char_literal_wide,
|
|
=> {
|
|
const slice = mt.tokSlice();
|
|
if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
|
|
return ZigTag.char_literal.create(arena, try mt.escapeUnprintables());
|
|
} else {
|
|
mt.i += 1;
|
|
|
|
const str = try std.fmt.allocPrint(arena, "0x{x}", .{slice[1 .. slice.len - 1]});
|
|
return ZigTag.integer_literal.create(arena, str);
|
|
}
|
|
},
|
|
.string_literal,
|
|
.string_literal_utf_16,
|
|
.string_literal_utf_8,
|
|
.string_literal_utf_32,
|
|
.string_literal_wide,
|
|
=> return ZigTag.string_literal.create(arena, try mt.escapeUnprintables()),
|
|
.pp_num => return mt.parseCNumLit(),
|
|
.l_paren => {
|
|
mt.i += 1;
|
|
const inner_node = try mt.parseCExpr(scope);
|
|
|
|
try mt.expect(.r_paren);
|
|
return inner_node;
|
|
},
|
|
.macro_param, .macro_param_no_expand => {
|
|
const param = mt.macro.params[mt.tokens[mt.i].end];
|
|
mt.i += 1;
|
|
|
|
const mangled_name = scope.getAlias(param) orelse param;
|
|
return try ZigTag.identifier.create(arena, mangled_name);
|
|
},
|
|
.identifier, .extended_identifier => {
|
|
const slice = mt.tokSlice();
|
|
mt.i += 1;
|
|
|
|
const mangled_name = scope.getAlias(slice) orelse slice;
|
|
if (Translator.builtin_typedef_map.get(mangled_name)) |ty| {
|
|
return ZigTag.type.create(arena, ty);
|
|
}
|
|
if (builtins.map.get(mangled_name)) |builtin| {
|
|
const builtin_identifier = try ZigTag.identifier.create(arena, "__builtin");
|
|
return ZigTag.field_access.create(arena, .{
|
|
.lhs = builtin_identifier,
|
|
.field_name = builtin.name,
|
|
});
|
|
}
|
|
|
|
const identifier = try ZigTag.identifier.create(arena, mangled_name);
|
|
scope.skipVariableDiscard(mangled_name);
|
|
refs_var: {
|
|
const ident_node = mt.t.global_scope.sym_table.get(slice) orelse break :refs_var;
|
|
const var_decl_node = ident_node.castTag(.var_decl) orelse break :refs_var;
|
|
if (!var_decl_node.data.is_const) mt.refs_var_decl = true;
|
|
}
|
|
return identifier;
|
|
},
|
|
else => {},
|
|
}
|
|
|
|
// for handling type macros (EVIL)
|
|
// TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList?
|
|
if (try mt.parseCTypeName(scope)) |type_name| {
|
|
return type_name;
|
|
}
|
|
|
|
try mt.fail("unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
|
|
return error.ParseError;
|
|
}
|
|
|
|
fn macroIntFromBool(mt: *MacroTranslator, node: ZigNode) !ZigNode {
|
|
if (!node.isBoolRes()) return node;
|
|
|
|
return ZigTag.int_from_bool.create(mt.t.arena, node);
|
|
}
|
|
|
|
fn macroIntToBool(mt: *MacroTranslator, node: ZigNode) !ZigNode {
|
|
if (node.isBoolRes()) return node;
|
|
|
|
if (node.tag() == .string_literal) {
|
|
// @intFromPtr(node) != 0
|
|
const int_from_ptr = try ZigTag.int_from_ptr.create(mt.t.arena, node);
|
|
return ZigTag.not_equal.create(mt.t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() });
|
|
}
|
|
// node != 0
|
|
return ZigTag.not_equal.create(mt.t.arena, .{ .lhs = node, .rhs = ZigTag.zero_literal.init() });
|
|
}
|
|
|
|
fn parseCCondExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
const node = try mt.parseCOrExpr(scope);
|
|
if (!mt.eat(.question_mark)) return node;
|
|
|
|
const then_body = try mt.parseCOrExpr(scope);
|
|
try mt.expect(.colon);
|
|
const else_body = try mt.parseCCondExpr(scope);
|
|
return ZigTag.@"if".create(mt.t.arena, .{ .cond = node, .then = then_body, .@"else" = else_body });
|
|
}
|
|
|
|
fn parseCOrExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
var node = try mt.parseCAndExpr(scope);
|
|
while (mt.eat(.pipe_pipe)) {
|
|
const lhs = try mt.macroIntToBool(node);
|
|
const rhs = try mt.macroIntToBool(try mt.parseCAndExpr(scope));
|
|
node = try ZigTag.@"or".create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
}
|
|
return node;
|
|
}
|
|
|
|
fn parseCAndExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
var node = try mt.parseCBitOrExpr(scope);
|
|
while (mt.eat(.ampersand_ampersand)) {
|
|
const lhs = try mt.macroIntToBool(node);
|
|
const rhs = try mt.macroIntToBool(try mt.parseCBitOrExpr(scope));
|
|
node = try ZigTag.@"and".create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
}
|
|
return node;
|
|
}
|
|
|
|
fn parseCBitOrExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
var node = try mt.parseCBitXorExpr(scope);
|
|
while (mt.eat(.pipe)) {
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCBitXorExpr(scope));
|
|
node = try ZigTag.bit_or.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
}
|
|
return node;
|
|
}
|
|
|
|
fn parseCBitXorExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
var node = try mt.parseCBitAndExpr(scope);
|
|
while (mt.eat(.caret)) {
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCBitAndExpr(scope));
|
|
node = try ZigTag.bit_xor.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
}
|
|
return node;
|
|
}
|
|
|
|
fn parseCBitAndExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
var node = try mt.parseCEqExpr(scope);
|
|
while (mt.eat(.ampersand)) {
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCEqExpr(scope));
|
|
node = try ZigTag.bit_and.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
}
|
|
return node;
|
|
}
|
|
|
|
fn parseCEqExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
var node = try mt.parseCRelExpr(scope);
|
|
while (true) {
|
|
switch (mt.peek()) {
|
|
.bang_equal => {
|
|
mt.i += 1;
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCRelExpr(scope));
|
|
node = try ZigTag.not_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
},
|
|
.equal_equal => {
|
|
mt.i += 1;
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCRelExpr(scope));
|
|
node = try ZigTag.equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
},
|
|
else => return node,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parseCRelExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
var node = try mt.parseCShiftExpr(scope);
|
|
while (true) {
|
|
switch (mt.peek()) {
|
|
.angle_bracket_right => {
|
|
mt.i += 1;
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
|
|
node = try ZigTag.greater_than.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
},
|
|
.angle_bracket_right_equal => {
|
|
mt.i += 1;
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
|
|
node = try ZigTag.greater_than_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
},
|
|
.angle_bracket_left => {
|
|
mt.i += 1;
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
|
|
node = try ZigTag.less_than.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
},
|
|
.angle_bracket_left_equal => {
|
|
mt.i += 1;
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
|
|
node = try ZigTag.less_than_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
},
|
|
else => return node,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parseCShiftExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
var node = try mt.parseCAddSubExpr(scope);
|
|
while (true) {
|
|
switch (mt.peek()) {
|
|
.angle_bracket_angle_bracket_left => {
|
|
mt.i += 1;
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCAddSubExpr(scope));
|
|
node = try ZigTag.shl.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
},
|
|
.angle_bracket_angle_bracket_right => {
|
|
mt.i += 1;
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCAddSubExpr(scope));
|
|
node = try ZigTag.shr.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
},
|
|
else => return node,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parseCAddSubExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
var node = try mt.parseCMulExpr(scope);
|
|
while (true) {
|
|
switch (mt.peek()) {
|
|
.plus => {
|
|
mt.i += 1;
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCMulExpr(scope));
|
|
node = try ZigTag.add.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
},
|
|
.minus => {
|
|
mt.i += 1;
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCMulExpr(scope));
|
|
node = try ZigTag.sub.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
},
|
|
else => return node,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parseCMulExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
var node = try mt.parseCCastExpr(scope);
|
|
while (true) {
|
|
switch (mt.peek()) {
|
|
.asterisk => {
|
|
mt.i += 1;
|
|
switch (mt.peek()) {
|
|
.comma, .r_paren, .eof => {
|
|
// This is probably a pointer type
|
|
return ZigTag.c_pointer.create(mt.t.arena, .{
|
|
.is_const = false,
|
|
.is_volatile = false,
|
|
.is_allowzero = false,
|
|
.elem_type = node,
|
|
});
|
|
},
|
|
else => {},
|
|
}
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
|
|
node = try ZigTag.mul.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
|
|
},
|
|
.slash => {
|
|
mt.i += 1;
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
|
|
node = try mt.t.createHelperCallNode(.div, &.{ lhs, rhs });
|
|
},
|
|
.percent => {
|
|
mt.i += 1;
|
|
const lhs = try mt.macroIntFromBool(node);
|
|
const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
|
|
node = try mt.t.createHelperCallNode(.rem, &.{ lhs, rhs });
|
|
},
|
|
else => return node,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parseCCastExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
if (mt.eat(.l_paren)) {
|
|
if (try mt.parseCTypeName(scope)) |type_name| {
|
|
while (true) {
|
|
const next_tok = mt.peek();
|
|
if (next_tok == .r_paren) {
|
|
mt.i += 1;
|
|
break;
|
|
}
|
|
// Skip trailing blank defined before the RParen.
|
|
if ((next_tok == .identifier or next_tok == .extended_identifier) and
|
|
mt.t.global_scope.blank_macros.contains(mt.tokSlice()))
|
|
{
|
|
mt.i += 1;
|
|
continue;
|
|
}
|
|
|
|
try mt.fail(
|
|
"unable to translate C expr: expected ')' instead got '{s}'",
|
|
.{next_tok.symbol()},
|
|
);
|
|
return error.ParseError;
|
|
}
|
|
if (mt.peek() == .l_brace) {
|
|
// initializer list
|
|
return mt.parseCPostfixExpr(scope, type_name);
|
|
}
|
|
const node_to_cast = try mt.parseCCastExpr(scope);
|
|
return mt.t.createHelperCallNode(.cast, &.{ type_name, node_to_cast });
|
|
}
|
|
mt.i -= 1; // l_paren
|
|
}
|
|
return mt.parseCUnaryExpr(scope);
|
|
}
|
|
|
|
// allow_fail is set when unsure if we are parsing a type-name
|
|
fn parseCTypeName(mt: *MacroTranslator, scope: *Scope) ParseError!?ZigNode {
|
|
if (try mt.parseCSpecifierQualifierList(scope)) |node| {
|
|
return try mt.parseCAbstractDeclarator(node);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope) ParseError!?ZigNode {
|
|
const tok = mt.peek();
|
|
switch (tok) {
|
|
.macro_param, .macro_param_no_expand => {
|
|
const param = mt.macro.params[mt.tokens[mt.i].end];
|
|
|
|
// Assume that this is only a cast if the next token is ')'
|
|
// e.g. param)identifier
|
|
if (mt.macro.tokens.len < mt.i + 3 or
|
|
mt.macro.tokens[mt.i + 1].id != .r_paren or
|
|
mt.macro.tokens[mt.i + 2].id != .identifier)
|
|
return null;
|
|
|
|
mt.i += 1;
|
|
const mangled_name = scope.getAlias(param) orelse param;
|
|
return try ZigTag.identifier.create(mt.t.arena, mangled_name);
|
|
},
|
|
.identifier, .extended_identifier => {
|
|
const slice = mt.tokSlice();
|
|
const mangled_name = scope.getAlias(slice) orelse slice;
|
|
|
|
if (mt.t.global_scope.blank_macros.contains(slice)) {
|
|
mt.i += 1;
|
|
return try mt.parseCSpecifierQualifierList(scope);
|
|
}
|
|
|
|
if (mt.t.typedefs.contains(mangled_name)) {
|
|
mt.i += 1;
|
|
if (Translator.builtin_typedef_map.get(mangled_name)) |ty| {
|
|
return try ZigTag.type.create(mt.t.arena, ty);
|
|
}
|
|
if (builtins.map.get(mangled_name)) |builtin| {
|
|
const builtin_identifier = try ZigTag.identifier.create(mt.t.arena, "__builtin");
|
|
return try ZigTag.field_access.create(mt.t.arena, .{
|
|
.lhs = builtin_identifier,
|
|
.field_name = builtin.name,
|
|
});
|
|
}
|
|
|
|
return try ZigTag.identifier.create(mt.t.arena, mangled_name);
|
|
}
|
|
},
|
|
.keyword_void => {
|
|
mt.i += 1;
|
|
return try ZigTag.type.create(mt.t.arena, "anyopaque");
|
|
},
|
|
.keyword_bool => {
|
|
mt.i += 1;
|
|
return try ZigTag.type.create(mt.t.arena, "bool");
|
|
},
|
|
.keyword_char,
|
|
.keyword_int,
|
|
.keyword_short,
|
|
.keyword_long,
|
|
.keyword_float,
|
|
.keyword_double,
|
|
.keyword_signed,
|
|
.keyword_unsigned,
|
|
.keyword_complex,
|
|
=> return try mt.parseCNumericType(),
|
|
.keyword_enum, .keyword_struct, .keyword_union => {
|
|
const tag_name = mt.tokSlice();
|
|
mt.i += 1;
|
|
if (mt.peek() != .identifier) {
|
|
mt.i -= 1;
|
|
return null;
|
|
}
|
|
|
|
// struct Foo will be declared as struct_Foo by transRecordDecl
|
|
const identifier = mt.tokSlice();
|
|
try mt.expect(.identifier);
|
|
|
|
const name = try std.fmt.allocPrint(mt.t.arena, "{s}_{s}", .{ tag_name, identifier });
|
|
if (!mt.t.global_scope.contains(name)) {
|
|
try mt.fail("unable to translate C expr: '{s}' not found", .{name});
|
|
return error.ParseError;
|
|
}
|
|
|
|
return try ZigTag.identifier.create(mt.t.arena, name);
|
|
},
|
|
else => {},
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
fn parseCNumericType(mt: *MacroTranslator) ParseError!ZigNode {
|
|
const KwCounter = struct {
|
|
double: u8 = 0,
|
|
long: u8 = 0,
|
|
int: u8 = 0,
|
|
float: u8 = 0,
|
|
short: u8 = 0,
|
|
char: u8 = 0,
|
|
unsigned: u8 = 0,
|
|
signed: u8 = 0,
|
|
complex: u8 = 0,
|
|
|
|
fn eql(self: @This(), other: @This()) bool {
|
|
return std.meta.eql(self, other);
|
|
}
|
|
};
|
|
|
|
// Yes, these can be in *any* order
|
|
// This still doesn't cover cases where for example volatile is intermixed
|
|
|
|
var kw = KwCounter{};
|
|
// prevent overflow
|
|
var i: u8 = 0;
|
|
while (i < math.maxInt(u8)) : (i += 1) {
|
|
switch (mt.peek()) {
|
|
.keyword_double => kw.double += 1,
|
|
.keyword_long => kw.long += 1,
|
|
.keyword_int => kw.int += 1,
|
|
.keyword_float => kw.float += 1,
|
|
.keyword_short => kw.short += 1,
|
|
.keyword_char => kw.char += 1,
|
|
.keyword_unsigned => kw.unsigned += 1,
|
|
.keyword_signed => kw.signed += 1,
|
|
.keyword_complex => kw.complex += 1,
|
|
else => break,
|
|
}
|
|
mt.i += 1;
|
|
}
|
|
|
|
if (kw.eql(.{ .int = 1 }) or kw.eql(.{ .signed = 1 }) or kw.eql(.{ .signed = 1, .int = 1 }))
|
|
return ZigTag.type.create(mt.t.arena, "c_int");
|
|
|
|
if (kw.eql(.{ .unsigned = 1 }) or kw.eql(.{ .unsigned = 1, .int = 1 }))
|
|
return ZigTag.type.create(mt.t.arena, "c_uint");
|
|
|
|
if (kw.eql(.{ .long = 1 }) or kw.eql(.{ .signed = 1, .long = 1 }) or kw.eql(.{ .long = 1, .int = 1 }) or kw.eql(.{ .signed = 1, .long = 1, .int = 1 }))
|
|
return ZigTag.type.create(mt.t.arena, "c_long");
|
|
|
|
if (kw.eql(.{ .unsigned = 1, .long = 1 }) or kw.eql(.{ .unsigned = 1, .long = 1, .int = 1 }))
|
|
return ZigTag.type.create(mt.t.arena, "c_ulong");
|
|
|
|
if (kw.eql(.{ .long = 2 }) or kw.eql(.{ .signed = 1, .long = 2 }) or kw.eql(.{ .long = 2, .int = 1 }) or kw.eql(.{ .signed = 1, .long = 2, .int = 1 }))
|
|
return ZigTag.type.create(mt.t.arena, "c_longlong");
|
|
|
|
if (kw.eql(.{ .unsigned = 1, .long = 2 }) or kw.eql(.{ .unsigned = 1, .long = 2, .int = 1 }))
|
|
return ZigTag.type.create(mt.t.arena, "c_ulonglong");
|
|
|
|
if (kw.eql(.{ .signed = 1, .char = 1 }))
|
|
return ZigTag.type.create(mt.t.arena, "i8");
|
|
|
|
if (kw.eql(.{ .char = 1 }) or kw.eql(.{ .unsigned = 1, .char = 1 }))
|
|
return ZigTag.type.create(mt.t.arena, "u8");
|
|
|
|
if (kw.eql(.{ .short = 1 }) or kw.eql(.{ .signed = 1, .short = 1 }) or kw.eql(.{ .short = 1, .int = 1 }) or kw.eql(.{ .signed = 1, .short = 1, .int = 1 }))
|
|
return ZigTag.type.create(mt.t.arena, "c_short");
|
|
|
|
if (kw.eql(.{ .unsigned = 1, .short = 1 }) or kw.eql(.{ .unsigned = 1, .short = 1, .int = 1 }))
|
|
return ZigTag.type.create(mt.t.arena, "c_ushort");
|
|
|
|
if (kw.eql(.{ .float = 1 }))
|
|
return ZigTag.type.create(mt.t.arena, "f32");
|
|
|
|
if (kw.eql(.{ .double = 1 }))
|
|
return ZigTag.type.create(mt.t.arena, "f64");
|
|
|
|
if (kw.eql(.{ .long = 1, .double = 1 })) {
|
|
try mt.fail("unable to translate: TODO long double", .{});
|
|
return error.ParseError;
|
|
}
|
|
|
|
if (kw.eql(.{ .float = 1, .complex = 1 })) {
|
|
try mt.fail("unable to translate: TODO _Complex", .{});
|
|
return error.ParseError;
|
|
}
|
|
|
|
if (kw.eql(.{ .double = 1, .complex = 1 })) {
|
|
try mt.fail("unable to translate: TODO _Complex", .{});
|
|
return error.ParseError;
|
|
}
|
|
|
|
if (kw.eql(.{ .long = 1, .double = 1, .complex = 1 })) {
|
|
try mt.fail("unable to translate: TODO _Complex", .{});
|
|
return error.ParseError;
|
|
}
|
|
|
|
try mt.fail("unable to translate: invalid numeric type", .{});
|
|
return error.ParseError;
|
|
}
|
|
|
|
fn parseCAbstractDeclarator(mt: *MacroTranslator, node: ZigNode) ParseError!ZigNode {
|
|
if (mt.eat(.asterisk)) {
|
|
if (node.castTag(.type)) |some| {
|
|
if (std.mem.eql(u8, some.data, "anyopaque")) {
|
|
const ptr = try ZigTag.single_pointer.create(mt.t.arena, .{
|
|
.is_const = false,
|
|
.is_volatile = false,
|
|
.is_allowzero = false,
|
|
.elem_type = node,
|
|
});
|
|
return ZigTag.optional_type.create(mt.t.arena, ptr);
|
|
}
|
|
}
|
|
return ZigTag.c_pointer.create(mt.t.arena, .{
|
|
.is_const = false,
|
|
.is_volatile = false,
|
|
.is_allowzero = false,
|
|
.elem_type = node,
|
|
});
|
|
}
|
|
return node;
|
|
}
|
|
|
|
fn parseCPostfixExpr(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNode) ParseError!ZigNode {
|
|
var node = try mt.parseCPostfixExprInner(scope, type_name);
|
|
// In C the preprocessor would handle concatting strings while expanding macros.
|
|
// This should do approximately the same by concatting any strings and identifiers
|
|
// after a primary or postfix expression.
|
|
while (true) {
|
|
switch (mt.peek()) {
|
|
.string_literal,
|
|
.string_literal_utf_16,
|
|
.string_literal_utf_8,
|
|
.string_literal_utf_32,
|
|
.string_literal_wide,
|
|
=> {},
|
|
.identifier, .extended_identifier => {
|
|
if (mt.t.global_scope.blank_macros.contains(mt.tokSlice())) {
|
|
mt.i += 1;
|
|
continue;
|
|
}
|
|
},
|
|
else => break,
|
|
}
|
|
const rhs = try mt.parseCPostfixExprInner(scope, type_name);
|
|
node = try ZigTag.array_cat.create(mt.t.arena, .{ .lhs = node, .rhs = rhs });
|
|
}
|
|
return node;
|
|
}
|
|
|
|
fn parseCPostfixExprInner(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNode) ParseError!ZigNode {
|
|
const gpa = mt.t.gpa;
|
|
const arena = mt.t.arena;
|
|
var node = type_name orelse try mt.parseCPrimaryExpr(scope);
|
|
while (true) {
|
|
switch (mt.peek()) {
|
|
.period => {
|
|
mt.i += 1;
|
|
const tok = mt.tokens[mt.i];
|
|
if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {
|
|
try mt.fail("unable to translate C expr: field access using macro parameter", .{});
|
|
return error.ParseError;
|
|
}
|
|
const field_name = mt.tokSlice();
|
|
try mt.expect(.identifier);
|
|
|
|
node = try ZigTag.field_access.create(arena, .{ .lhs = node, .field_name = field_name });
|
|
},
|
|
.arrow => {
|
|
mt.i += 1;
|
|
const tok = mt.tokens[mt.i];
|
|
if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {
|
|
try mt.fail("unable to translate C expr: field access using macro parameter", .{});
|
|
return error.ParseError;
|
|
}
|
|
const field_name = mt.tokSlice();
|
|
try mt.expect(.identifier);
|
|
|
|
const deref = try ZigTag.deref.create(arena, node);
|
|
node = try ZigTag.field_access.create(arena, .{ .lhs = deref, .field_name = field_name });
|
|
},
|
|
.l_bracket => {
|
|
mt.i += 1;
|
|
|
|
const index_val = try mt.macroIntFromBool(try mt.parseCExpr(scope));
|
|
const index = try ZigTag.as.create(arena, .{
|
|
.lhs = try ZigTag.type.create(arena, "usize"),
|
|
.rhs = try ZigTag.int_cast.create(arena, index_val),
|
|
});
|
|
node = try ZigTag.array_access.create(arena, .{ .lhs = node, .rhs = index });
|
|
try mt.expect(.r_bracket);
|
|
},
|
|
.l_paren => {
|
|
mt.i += 1;
|
|
|
|
if (mt.eat(.r_paren)) {
|
|
node = try ZigTag.call.create(arena, .{ .lhs = node, .args = &.{} });
|
|
} else {
|
|
var args: std.ArrayList(ZigNode) = .empty;
|
|
defer args.deinit(gpa);
|
|
|
|
while (true) {
|
|
const arg = try mt.parseCCondExpr(scope);
|
|
try args.append(gpa, arg);
|
|
|
|
const next_id = mt.peek();
|
|
switch (next_id) {
|
|
.comma => {
|
|
mt.i += 1;
|
|
},
|
|
.r_paren => {
|
|
mt.i += 1;
|
|
break;
|
|
},
|
|
else => {
|
|
try mt.fail("unable to translate C expr: expected ',' or ')' instead got '{s}'", .{next_id.symbol()});
|
|
return error.ParseError;
|
|
},
|
|
}
|
|
}
|
|
node = try ZigTag.call.create(arena, .{ .lhs = node, .args = try arena.dupe(ZigNode, args.items) });
|
|
}
|
|
},
|
|
.l_brace => {
|
|
mt.i += 1;
|
|
|
|
// Check for designated field initializers
|
|
if (mt.peek() == .period) {
|
|
var init_vals: std.ArrayList(ast.Payload.ContainerInitDot.Initializer) = .empty;
|
|
defer init_vals.deinit(gpa);
|
|
|
|
while (true) {
|
|
try mt.expect(.period);
|
|
const name = mt.tokSlice();
|
|
try mt.expect(.identifier);
|
|
try mt.expect(.equal);
|
|
|
|
const val = try mt.parseCCondExpr(scope);
|
|
try init_vals.append(gpa, .{ .name = name, .value = val });
|
|
|
|
const next_id = mt.peek();
|
|
switch (next_id) {
|
|
.comma => {
|
|
mt.i += 1;
|
|
},
|
|
.r_brace => {
|
|
mt.i += 1;
|
|
break;
|
|
},
|
|
else => {
|
|
try mt.fail("unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
|
|
return error.ParseError;
|
|
},
|
|
}
|
|
}
|
|
const tuple_node = try ZigTag.container_init_dot.create(arena, try arena.dupe(ast.Payload.ContainerInitDot.Initializer, init_vals.items));
|
|
node = try ZigTag.std_mem_zeroinit.create(arena, .{ .lhs = node, .rhs = tuple_node });
|
|
continue;
|
|
}
|
|
|
|
var init_vals: std.ArrayList(ZigNode) = .empty;
|
|
defer init_vals.deinit(gpa);
|
|
|
|
while (true) {
|
|
const val = try mt.parseCCondExpr(scope);
|
|
try init_vals.append(gpa, val);
|
|
|
|
const next_id = mt.peek();
|
|
switch (next_id) {
|
|
.comma => {
|
|
mt.i += 1;
|
|
},
|
|
.r_brace => {
|
|
mt.i += 1;
|
|
break;
|
|
},
|
|
else => {
|
|
try mt.fail("unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
|
|
return error.ParseError;
|
|
},
|
|
}
|
|
}
|
|
const tuple_node = try ZigTag.tuple.create(arena, try arena.dupe(ZigNode, init_vals.items));
|
|
node = try ZigTag.std_mem_zeroinit.create(arena, .{ .lhs = node, .rhs = tuple_node });
|
|
},
|
|
.plus_plus, .minus_minus => {
|
|
try mt.fail("TODO postfix inc/dec expr", .{});
|
|
return error.ParseError;
|
|
},
|
|
else => return node,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parseCUnaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
|
|
switch (mt.peek()) {
|
|
.bang => {
|
|
mt.i += 1;
|
|
const operand = try mt.macroIntToBool(try mt.parseCCastExpr(scope));
|
|
return ZigTag.not.create(mt.t.arena, operand);
|
|
},
|
|
.minus => {
|
|
mt.i += 1;
|
|
const operand = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
|
|
return ZigTag.negate.create(mt.t.arena, operand);
|
|
},
|
|
.plus => {
|
|
mt.i += 1;
|
|
return try mt.parseCCastExpr(scope);
|
|
},
|
|
.tilde => {
|
|
mt.i += 1;
|
|
const operand = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
|
|
return ZigTag.bit_not.create(mt.t.arena, operand);
|
|
},
|
|
.asterisk => {
|
|
mt.i += 1;
|
|
const operand = try mt.parseCCastExpr(scope);
|
|
return ZigTag.deref.create(mt.t.arena, operand);
|
|
},
|
|
.ampersand => {
|
|
mt.i += 1;
|
|
const operand = try mt.parseCCastExpr(scope);
|
|
return ZigTag.address_of.create(mt.t.arena, operand);
|
|
},
|
|
.keyword_sizeof => {
|
|
mt.i += 1;
|
|
const operand = if (mt.eat(.l_paren)) blk: {
|
|
const inner = (try mt.parseCTypeName(scope)) orelse try mt.parseCUnaryExpr(scope);
|
|
try mt.expect(.r_paren);
|
|
break :blk inner;
|
|
} else try mt.parseCUnaryExpr(scope);
|
|
|
|
return mt.t.createHelperCallNode(.sizeof, &.{operand});
|
|
},
|
|
.keyword_alignof => {
|
|
mt.i += 1;
|
|
// TODO this won't work if using <stdalign.h>'s
|
|
// #define alignof _Alignof
|
|
try mt.expect(.l_paren);
|
|
const operand = (try mt.parseCTypeName(scope)) orelse try mt.parseCUnaryExpr(scope);
|
|
try mt.expect(.r_paren);
|
|
|
|
return ZigTag.alignof.create(mt.t.arena, operand);
|
|
},
|
|
.plus_plus, .minus_minus => {
|
|
try mt.fail("TODO unary inc/dec expr", .{});
|
|
return error.ParseError;
|
|
},
|
|
else => {},
|
|
}
|
|
|
|
return try mt.parseCPostfixExpr(scope, null);
|
|
}
|