translate-c: better typename parsing

This commit is contained in:
xackus 2021-06-12 15:54:39 +02:00 committed by Veikka Tuominen
parent 46b9e061e0
commit aa83cbb697
2 changed files with 164 additions and 93 deletions

View File

@ -280,6 +280,8 @@ pub const Context = struct {
opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{},
/// Table of unnamed enums and records that are child types of typedefs.
unnamed_typedefs: std.AutoHashMapUnmanaged(usize, []const u8) = .{},
/// Needed to decide if we are parsing a typename
typedefs: std.StringArrayHashMapUnmanaged(void) = .{},
/// This one is different than the root scope's name table. This contains
/// a list of names that we found by visiting all the top level decls without
@ -348,6 +350,7 @@ pub fn translate(
context.global_names.deinit(gpa);
context.opaque_demotes.deinit(gpa);
context.unnamed_typedefs.deinit(gpa);
context.typedefs.deinit(gpa);
context.global_scope.deinit();
}
@ -461,6 +464,7 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void {
result.value_ptr.* = name;
// Put this typedef in the decl_table to avoid redefinitions.
try c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), name);
try c.typedefs.put(c.gpa, name, {});
}
}
}
@ -792,6 +796,8 @@ fn transTypeDef(c: *Context, scope: *Scope, typedef_decl: *const clang.TypedefNa
// TODO https://github.com/ziglang/zig/issues/3756
// TODO https://github.com/ziglang/zig/issues/1802
var name: []const u8 = if (isZigPrimitiveType(bare_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ bare_name, c.getMangle() }) else bare_name;
try c.typedefs.put(c.gpa, name, {});
if (builtin_typedef_map.get(name)) |builtin| {
return c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin);
}
@ -5303,56 +5309,6 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N
.IntegerLiteral, .FloatLiteral => {
return parseCNumLit(c, m);
},
// eventually this will be replaced by std.c.parse which will handle these correctly
.Keyword_void => return Tag.type.create(c.arena, "c_void"),
.Keyword_bool => return Tag.type.create(c.arena, "bool"),
.Keyword_double => return Tag.type.create(c.arena, "f64"),
.Keyword_long => return Tag.type.create(c.arena, "c_long"),
.Keyword_int => return Tag.type.create(c.arena, "c_int"),
.Keyword_float => return Tag.type.create(c.arena, "f32"),
.Keyword_short => return Tag.type.create(c.arena, "c_short"),
.Keyword_char => return Tag.type.create(c.arena, "u8"),
.Keyword_unsigned => if (m.next()) |t| switch (t) {
.Keyword_char => return Tag.type.create(c.arena, "u8"),
.Keyword_short => return Tag.type.create(c.arena, "c_ushort"),
.Keyword_int => return Tag.type.create(c.arena, "c_uint"),
.Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
_ = m.next();
return Tag.type.create(c.arena, "c_ulonglong");
} else return Tag.type.create(c.arena, "c_ulong"),
else => {
m.i -= 1;
return Tag.type.create(c.arena, "c_uint");
},
} else {
return Tag.type.create(c.arena, "c_uint");
},
.Keyword_signed => if (m.next()) |t| switch (t) {
.Keyword_char => return Tag.type.create(c.arena, "i8"),
.Keyword_short => return Tag.type.create(c.arena, "c_short"),
.Keyword_int => return Tag.type.create(c.arena, "c_int"),
.Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
_ = m.next();
return Tag.type.create(c.arena, "c_longlong");
} else return Tag.type.create(c.arena, "c_long"),
else => {
m.i -= 1;
return Tag.type.create(c.arena, "c_int");
},
} else {
return Tag.type.create(c.arena, "c_int");
},
.Keyword_enum, .Keyword_struct, .Keyword_union => {
// struct Foo will be declared as struct_Foo by transRecordDecl
const next_id = m.next().?;
if (next_id != .Identifier) {
try m.fail(c, "unable to translate C expr: expected Identifier instead got: {s}", .{@tagName(next_id)});
return error.ParseError;
}
const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() });
return Tag.identifier.create(c.arena, name);
},
.Identifier => {
const mangled_name = scope.getAlias(slice);
if (mem.startsWith(u8, mangled_name, "__builtin_") and !isBuiltinDefined(mangled_name)) {
@ -5369,37 +5325,15 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N
try m.fail(c, "unable to translate C expr: expected ')' instead got: {s}", .{@tagName(next_id)});
return error.ParseError;
}
var saw_l_paren = false;
var saw_integer_literal = false;
switch (m.peek().?) {
// (type)(to_cast)
.LParen => {
saw_l_paren = true;
_ = m.next();
},
// (type)sizeof(x)
.Keyword_sizeof,
// (type)alignof(x)
.Keyword_alignof,
// (type)identifier
.Identifier,
=> {},
// (type)integer
.IntegerLiteral => {
saw_integer_literal = true;
},
else => return inner_node,
}
const node_to_cast = try parseCExpr(c, m, scope);
if (saw_l_paren and m.next().? != .RParen) {
try m.fail(c, "unable to translate C expr: expected ')'", .{});
return error.ParseError;
}
return Tag.std_meta_cast.create(c.arena, .{ .lhs = inner_node, .rhs = node_to_cast });
return inner_node;
},
else => {
// for handling type macros (EVIL)
// TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList?
m.i -= 1;
if (try parseCTypeName(c, m, scope)) |type_name| {
return type_name;
}
try m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(tok)});
return error.ParseError;
},
@ -5605,7 +5539,7 @@ fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
}
fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
var node = try parseCUnaryExpr(c, m, scope);
var node = try parseCCastExpr(c, m, scope);
while (true) {
switch (m.next().?) {
.Asterisk => {
@ -5633,18 +5567,18 @@ fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
} else {
// expr * expr
const lhs = try macroBoolToInt(c, node);
const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
const rhs = try macroBoolToInt(c, try parseCCastExpr(c, m, scope));
node = try Tag.mul.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
}
},
.Slash => {
const lhs = try macroBoolToInt(c, node);
const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
const rhs = try macroBoolToInt(c, try parseCCastExpr(c, m, scope));
node = try Tag.div.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
},
.Percent => {
const lhs = try macroBoolToInt(c, node);
const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
const rhs = try macroBoolToInt(c, try parseCCastExpr(c, m, scope));
node = try Tag.mod.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
},
else => {
@ -5655,8 +5589,133 @@ fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
}
}
fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
var node = try parseCPrimaryExpr(c, m, scope);
fn parseCCastExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
switch (m.next().?) {
.LParen => {
if (try parseCTypeName(c, m, scope)) |type_name| {
if (m.next().? != .RParen) {
try m.fail(c, "unable to translate C expr: expected ')'", .{});
return error.ParseError;
}
if (m.peek().? == .LBrace) {
// initializer list
return parseCPostfixExpr(c, m, scope, type_name);
}
const node_to_cast = try parseCCastExpr(c, m, scope);
return Tag.std_meta_cast.create(c.arena, .{ .lhs = type_name, .rhs = node_to_cast });
}
},
else => {},
}
m.i -= 1;
return parseCUnaryExpr(c, m, scope);
}
fn parseCTypeName(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!?Node {
if (try parseCSpecifierQualifierList(c, m, scope)) |node| {
return try parseCAbstractDeclarator(c, m, scope, node);
} else {
return null;
}
}
fn parseCSpecifierQualifierList(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!?Node {
switch (m.next().?) {
.Identifier => {
const mangled_name = scope.getAlias(m.slice());
if (c.typedefs.contains(mangled_name)) {
return try Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name);
}
},
// eventually this will be replaced by std.c.parse which will handle these correctly
.Keyword_void => return try Tag.type.create(c.arena, "c_void"),
.Keyword_bool => return try Tag.type.create(c.arena, "bool"),
.Keyword_double => return try Tag.type.create(c.arena, "f64"),
.Keyword_long => return try Tag.type.create(c.arena, "c_long"),
.Keyword_int => return try Tag.type.create(c.arena, "c_int"),
.Keyword_float => return try Tag.type.create(c.arena, "f32"),
.Keyword_short => return try Tag.type.create(c.arena, "c_short"),
.Keyword_char => return try Tag.type.create(c.arena, "u8"),
.Keyword_unsigned => if (m.next()) |t| switch (t) {
.Keyword_char => return try Tag.type.create(c.arena, "u8"),
.Keyword_short => return try Tag.type.create(c.arena, "c_ushort"),
.Keyword_int => return try Tag.type.create(c.arena, "c_uint"),
.Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
_ = m.next();
return try Tag.type.create(c.arena, "c_ulonglong");
} else return try Tag.type.create(c.arena, "c_ulong"),
else => {
m.i -= 1;
return try Tag.type.create(c.arena, "c_uint");
},
} else {
return try Tag.type.create(c.arena, "c_uint");
},
.Keyword_signed => if (m.next()) |t| switch (t) {
.Keyword_char => return try Tag.type.create(c.arena, "i8"),
.Keyword_short => return try Tag.type.create(c.arena, "c_short"),
.Keyword_int => return try Tag.type.create(c.arena, "c_int"),
.Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
_ = m.next();
return try Tag.type.create(c.arena, "c_longlong");
} else return try Tag.type.create(c.arena, "c_long"),
else => {
m.i -= 1;
return try Tag.type.create(c.arena, "c_int");
},
} else {
return try Tag.type.create(c.arena, "c_int");
},
.Keyword_enum, .Keyword_struct, .Keyword_union => {
// struct Foo will be declared as struct_Foo by transRecordDecl
const slice = m.slice();
const next_id = m.next().?;
if (next_id != .Identifier) {
try m.fail(c, "unable to translate C expr: expected Identifier instead got: {s}", .{@tagName(next_id)});
return error.ParseError;
}
const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() });
return try Tag.identifier.create(c.arena, name);
},
.Keyword_complex => {}, // TODO
else => {},
}
m.i -= 1;
return null;
}
fn parseCAbstractDeclarator(c: *Context, m: *MacroCtx, scope: *Scope, node: Node) ParseError!Node {
switch (m.next().?) {
.Asterisk => {
// last token of `node`
const prev_id = m.list[m.i - 1].id;
if (prev_id == .Keyword_void) {
const ptr = try Tag.single_pointer.create(c.arena, .{
.is_const = false,
.is_volatile = false,
.elem_type = node,
});
return Tag.optional_type.create(c.arena, ptr);
} else {
return Tag.c_pointer.create(c.arena, .{
.is_const = false,
.is_volatile = false,
.elem_type = node,
});
}
},
else => {
m.i -= 1;
return node;
},
}
}
fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?Node) ParseError!Node {
var node = type_name orelse try parseCPrimaryExpr(c, m, scope);
while (true) {
switch (m.next().?) {
.Period => {
@ -5776,24 +5835,24 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
switch (m.next().?) {
.Bang => {
const operand = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope));
const operand = try macroIntToBool(c, try parseCCastExpr(c, m, scope));
return Tag.not.create(c.arena, operand);
},
.Minus => {
const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
const operand = try macroBoolToInt(c, try parseCCastExpr(c, m, scope));
return Tag.negate.create(c.arena, operand);
},
.Plus => return try parseCUnaryExpr(c, m, scope),
.Plus => return try parseCCastExpr(c, m, scope),
.Tilde => {
const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
const operand = try macroBoolToInt(c, try parseCCastExpr(c, m, scope));
return Tag.bit_not.create(c.arena, operand);
},
.Asterisk => {
const operand = try parseCUnaryExpr(c, m, scope);
const operand = try parseCCastExpr(c, m, scope);
return Tag.deref.create(c.arena, operand);
},
.Ampersand => {
const operand = try parseCUnaryExpr(c, m, scope);
const operand = try parseCCastExpr(c, m, scope);
return Tag.address_of.create(c.arena, operand);
},
.Keyword_sizeof => {
@ -5834,7 +5893,7 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
},
else => {
m.i -= 1;
return try parseCPostfixExpr(c, m, scope);
return try parseCPostfixExpr(c, m, scope, null);
},
}
}

View File

@ -238,7 +238,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
});
cases.add("use cast param as macro fn return type",
\\#define MEM_PHYSICAL_TO_K0(x) (void*)((u32)(x) + SYS_BASE_CACHED)
\\#include <stdint.h>
\\#define MEM_PHYSICAL_TO_K0(x) (void*)((uint32_t)(x) + SYS_BASE_CACHED)
, &[_][]const u8{
\\pub inline fn MEM_PHYSICAL_TO_K0(x: anytype) ?*c_void {
\\ return @import("std").meta.cast(?*c_void, @import("std").meta.cast(u32, x) + SYS_BASE_CACHED);
@ -1878,6 +1879,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
});
cases.add("macro pointer cast",
\\typedef struct { int dummy; } NRF_GPIO_Type;
\\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
, &[_][]const u8{
\\pub const NRF_GPIO = @import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE);
@ -3175,6 +3177,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
});
cases.add("macro cast",
\\#include <stdint.h>
\\#define FOO(bar) baz((void *)(baz))
\\#define BAR (void*) a
\\#define BAZ (uint32_t)(2)
@ -3633,4 +3636,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return foo.static.x;
\\}
});
cases.add("macro with nontrivial cast",
\\#define MAP_FAILED ((void *) -1)
\\typedef long long LONG_PTR;
\\#define INVALID_HANDLE_VALUE ((void *)(LONG_PTR)-1)
, &[_][]const u8{
\\pub const MAP_FAILED = @import("std").meta.cast(?*c_void, -@as(c_int, 1));
\\pub const INVALID_HANDLE_VALUE = @import("std").meta.cast(?*c_void, @import("std").meta.cast(LONG_PTR, -@as(c_int, 1)));
});
}