stage1 is now a hybrid of C++ and Zig
This modifies the build process of Zig to put all of the source files
into libcompiler.a, except main.cpp and userland.cpp.
Next, the build process links main.cpp, userland.cpp, and libcompiler.a
into zig1. userland.cpp is a shim for functions that will later be
replaced with self-hosted implementations.
Next, the build process uses zig1 to build src-self-hosted/stage1.zig
into libuserland.a, which does not depend on any of the things that
are shimmed in userland.cpp, such as translate-c.
Finally, the build process re-links main.cpp and libcompiler.a, except
with libuserland.a instead of userland.cpp. Now the shims are replaced
with .zig code. This provides all of the Zig standard library to the
stage1 C++ compiler, and enables us to move certain things to userland,
such as translate-c.
As a proof of concept I have made the `zig zen` command use text defined
in userland. I added `zig translate-c-2` which is a work-in-progress
reimplementation of translate-c in userland, which currently calls
`std.debug.panic("unimplemented")` and you can see the stack trace makes
it all the way back into the C++ main() function (Thanks LemonBoy for
improving that!).
This could potentially let us move other things into userland, such as
hashing algorithms, the entire cache system, .d file parsing, pretty
much anything that libuserland.a itself doesn't need to depend on.
This can also let us have `zig fmt` in stage1 without the overhead
of child process execution, and without the initial compilation delay
before it gets cached.
See #1964
2019-04-16 20:47:47 +00:00
|
|
|
// This is the userland implementation of translate-c which will be used by both stage1
|
2019-04-30 04:21:45 +00:00
|
|
|
// and stage2. Currently the only way it is used is with `zig translate-c-2`.
|
stage1 is now a hybrid of C++ and Zig
This modifies the build process of Zig to put all of the source files
into libcompiler.a, except main.cpp and userland.cpp.
Next, the build process links main.cpp, userland.cpp, and libcompiler.a
into zig1. userland.cpp is a shim for functions that will later be
replaced with self-hosted implementations.
Next, the build process uses zig1 to build src-self-hosted/stage1.zig
into libuserland.a, which does not depend on any of the things that
are shimmed in userland.cpp, such as translate-c.
Finally, the build process re-links main.cpp and libcompiler.a, except
with libuserland.a instead of userland.cpp. Now the shims are replaced
with .zig code. This provides all of the Zig standard library to the
stage1 C++ compiler, and enables us to move certain things to userland,
such as translate-c.
As a proof of concept I have made the `zig zen` command use text defined
in userland. I added `zig translate-c-2` which is a work-in-progress
reimplementation of translate-c in userland, which currently calls
`std.debug.panic("unimplemented")` and you can see the stack trace makes
it all the way back into the C++ main() function (Thanks LemonBoy for
improving that!).
This could potentially let us move other things into userland, such as
hashing algorithms, the entire cache system, .d file parsing, pretty
much anything that libuserland.a itself doesn't need to depend on.
This can also let us have `zig fmt` in stage1 without the overhead
of child process execution, and without the initial compilation delay
before it gets cached.
See #1964
2019-04-16 20:47:47 +00:00
|
|
|
|
|
|
|
const std = @import("std");
|
2019-05-10 20:03:54 +00:00
|
|
|
const builtin = @import("builtin");
|
2019-05-11 03:35:46 +00:00
|
|
|
const assert = std.debug.assert;
|
2019-04-21 21:24:58 +00:00
|
|
|
const ast = std.zig.ast;
|
2019-04-26 19:40:29 +00:00
|
|
|
const Token = std.zig.Token;
|
2019-04-21 21:24:58 +00:00
|
|
|
use @import("clang.zig");
|
stage1 is now a hybrid of C++ and Zig
This modifies the build process of Zig to put all of the source files
into libcompiler.a, except main.cpp and userland.cpp.
Next, the build process links main.cpp, userland.cpp, and libcompiler.a
into zig1. userland.cpp is a shim for functions that will later be
replaced with self-hosted implementations.
Next, the build process uses zig1 to build src-self-hosted/stage1.zig
into libuserland.a, which does not depend on any of the things that
are shimmed in userland.cpp, such as translate-c.
Finally, the build process re-links main.cpp and libcompiler.a, except
with libuserland.a instead of userland.cpp. Now the shims are replaced
with .zig code. This provides all of the Zig standard library to the
stage1 C++ compiler, and enables us to move certain things to userland,
such as translate-c.
As a proof of concept I have made the `zig zen` command use text defined
in userland. I added `zig translate-c-2` which is a work-in-progress
reimplementation of translate-c in userland, which currently calls
`std.debug.panic("unimplemented")` and you can see the stack trace makes
it all the way back into the C++ main() function (Thanks LemonBoy for
improving that!).
This could potentially let us move other things into userland, such as
hashing algorithms, the entire cache system, .d file parsing, pretty
much anything that libuserland.a itself doesn't need to depend on.
This can also let us have `zig fmt` in stage1 without the overhead
of child process execution, and without the initial compilation delay
before it gets cached.
See #1964
2019-04-16 20:47:47 +00:00
|
|
|
|
2019-04-21 21:24:58 +00:00
|
|
|
pub const Mode = enum {
|
|
|
|
import,
|
|
|
|
translate,
|
|
|
|
};
|
|
|
|
|
2019-05-09 20:52:30 +00:00
|
|
|
// TODO merge with Type.Fn.CallingConvention
|
2019-05-10 20:03:54 +00:00
|
|
|
const CallingConvention = builtin.TypeInfo.CallingConvention;
|
2019-05-09 20:52:30 +00:00
|
|
|
|
2019-04-21 23:37:39 +00:00
|
|
|
pub const ClangErrMsg = Stage2ErrorMsg;
|
|
|
|
|
2019-05-14 23:23:31 +00:00
|
|
|
pub const Error = error{OutOfMemory};
|
|
|
|
const TypeError = Error || error{UnsupportedType};
|
2019-05-28 06:55:48 +00:00
|
|
|
const TransError = TypeError || error{UnsupportedTranslation};
|
2019-04-30 04:21:45 +00:00
|
|
|
|
2019-05-10 20:03:54 +00:00
|
|
|
const DeclTable = std.HashMap(usize, void, addrHash, addrEql);
|
|
|
|
|
|
|
|
fn addrHash(x: usize) u32 {
|
|
|
|
switch (@typeInfo(usize).Int.bits) {
|
|
|
|
32 => return x,
|
|
|
|
// pointers are usually aligned so we ignore the bits that are probably all 0 anyway
|
|
|
|
// usually the larger bits of addr space are unused so we just chop em off
|
|
|
|
64 => return @truncate(u32, x >> 4),
|
|
|
|
else => @compileError("unreachable"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn addrEql(a: usize, b: usize) bool {
|
|
|
|
return a == b;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Scope = struct {
|
|
|
|
id: Id,
|
|
|
|
parent: ?*Scope,
|
|
|
|
|
|
|
|
const Id = enum {
|
|
|
|
Switch,
|
|
|
|
Var,
|
|
|
|
Block,
|
|
|
|
Root,
|
|
|
|
While,
|
|
|
|
};
|
|
|
|
const Switch = struct {
|
|
|
|
base: Scope,
|
|
|
|
};
|
|
|
|
|
|
|
|
const Var = struct {
|
|
|
|
base: Scope,
|
|
|
|
c_name: []const u8,
|
|
|
|
zig_name: []const u8,
|
|
|
|
};
|
|
|
|
|
|
|
|
const Block = struct {
|
|
|
|
base: Scope,
|
2019-05-11 03:35:46 +00:00
|
|
|
block_node: *ast.Node.Block,
|
|
|
|
|
|
|
|
/// Don't forget to set rbrace token later
|
|
|
|
fn create(c: *Context, parent: *Scope, lbrace_tok: ast.TokenIndex) !*Block {
|
|
|
|
const block = try c.a().create(Block);
|
|
|
|
block.* = Block{
|
|
|
|
.base = Scope{
|
|
|
|
.id = Id.Block,
|
|
|
|
.parent = parent,
|
|
|
|
},
|
|
|
|
.block_node = try c.a().create(ast.Node.Block),
|
|
|
|
};
|
|
|
|
block.block_node.* = ast.Node.Block{
|
|
|
|
.base = ast.Node{ .id = ast.Node.Id.Block },
|
|
|
|
.label = null,
|
|
|
|
.lbrace = lbrace_tok,
|
|
|
|
.statements = ast.Node.Block.StatementList.init(c.a()),
|
|
|
|
.rbrace = undefined,
|
|
|
|
};
|
|
|
|
return block;
|
|
|
|
}
|
2019-05-10 20:03:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const Root = struct {
|
|
|
|
base: Scope,
|
|
|
|
};
|
|
|
|
|
|
|
|
const While = struct {
|
|
|
|
base: Scope,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2019-05-11 03:35:46 +00:00
|
|
|
const TransResult = struct {
|
|
|
|
node: *ast.Node,
|
|
|
|
node_scope: *Scope,
|
|
|
|
child_scope: *Scope,
|
|
|
|
};
|
|
|
|
|
2019-04-30 04:21:45 +00:00
|
|
|
const Context = struct {
|
|
|
|
tree: *ast.Tree,
|
|
|
|
source_buffer: *std.Buffer,
|
|
|
|
err: Error,
|
2019-05-09 02:04:51 +00:00
|
|
|
source_manager: *ZigClangSourceManager,
|
2019-05-10 20:03:54 +00:00
|
|
|
decl_table: DeclTable,
|
|
|
|
global_scope: *Scope.Root,
|
|
|
|
mode: Mode,
|
2019-05-28 02:20:23 +00:00
|
|
|
ptr_params: std.BufSet,
|
2019-05-27 21:38:09 +00:00
|
|
|
clang_context: *ZigClangASTContext,
|
2019-05-09 00:49:07 +00:00
|
|
|
|
|
|
|
fn a(c: *Context) *std.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: [*]const u8) ![]u8 {
|
|
|
|
return std.mem.dupe(c.a(), u8, std.mem.toSliceConst(u8, s));
|
|
|
|
}
|
2019-05-09 02:04:51 +00:00
|
|
|
|
|
|
|
/// Convert a clang source location to a file:line:column string
|
|
|
|
fn locStr(c: *Context, loc: ZigClangSourceLocation) ![]u8 {
|
|
|
|
const spelling_loc = ZigClangSourceManager_getSpellingLoc(c.source_manager, loc);
|
|
|
|
const filename_c = ZigClangSourceManager_getFilename(c.source_manager, spelling_loc);
|
|
|
|
const filename = if (filename_c) |s| try c.str(s) else ([]const u8)("(no file)");
|
|
|
|
|
|
|
|
const line = ZigClangSourceManager_getSpellingLineNumber(c.source_manager, spelling_loc);
|
|
|
|
const column = ZigClangSourceManager_getSpellingColumnNumber(c.source_manager, spelling_loc);
|
|
|
|
return std.fmt.allocPrint(c.a(), "{}:{}:{}", filename, line, column);
|
|
|
|
}
|
2019-04-30 04:21:45 +00:00
|
|
|
};
|
|
|
|
|
2019-04-21 23:37:39 +00:00
|
|
|
pub fn translate(
|
2019-04-26 19:40:29 +00:00
|
|
|
backing_allocator: *std.mem.Allocator,
|
2019-04-21 23:37:39 +00:00
|
|
|
args_begin: [*]?[*]const u8,
|
|
|
|
args_end: [*]?[*]const u8,
|
|
|
|
mode: Mode,
|
|
|
|
errors: *[]ClangErrMsg,
|
2019-04-21 23:46:34 +00:00
|
|
|
resources_path: [*]const u8,
|
2019-04-21 23:37:39 +00:00
|
|
|
) !*ast.Tree {
|
2019-04-21 23:46:34 +00:00
|
|
|
const ast_unit = ZigClangLoadFromCommandLine(
|
|
|
|
args_begin,
|
|
|
|
args_end,
|
|
|
|
&errors.ptr,
|
|
|
|
&errors.len,
|
|
|
|
resources_path,
|
|
|
|
) orelse {
|
|
|
|
if (errors.len == 0) return error.OutOfMemory;
|
|
|
|
return error.SemanticAnalyzeFail;
|
|
|
|
};
|
2019-04-26 19:40:29 +00:00
|
|
|
defer ZigClangASTUnit_delete(ast_unit);
|
2019-04-21 23:46:34 +00:00
|
|
|
|
2019-04-26 19:40:29 +00:00
|
|
|
var tree_arena = std.heap.ArenaAllocator.init(backing_allocator);
|
|
|
|
errdefer tree_arena.deinit();
|
|
|
|
|
2019-05-28 00:22:15 +00:00
|
|
|
const tree = try tree_arena.allocator.create(ast.Tree);
|
|
|
|
tree.* = ast.Tree{
|
|
|
|
.source = undefined, // need to use Buffer.toOwnedSlice later
|
|
|
|
.root_node = undefined,
|
|
|
|
.arena_allocator = tree_arena,
|
|
|
|
.tokens = undefined, // can't reference the allocator yet
|
|
|
|
.errors = undefined, // can't reference the allocator yet
|
|
|
|
};
|
|
|
|
const arena = &tree.arena_allocator.allocator; // now we can reference the allocator
|
|
|
|
tree.tokens = ast.Tree.TokenList.init(arena);
|
|
|
|
tree.errors = ast.Tree.ErrorList.init(arena);
|
|
|
|
|
|
|
|
tree.root_node = try arena.create(ast.Node.Root);
|
|
|
|
tree.root_node.* = ast.Node.Root{
|
2019-04-26 19:40:29 +00:00
|
|
|
.base = ast.Node{ .id = ast.Node.Id.Root },
|
|
|
|
.decls = ast.Node.Root.DeclList.init(arena),
|
|
|
|
.doc_comments = null,
|
|
|
|
// initialized with the eof token at the end
|
|
|
|
.eof_token = undefined,
|
|
|
|
};
|
|
|
|
|
2019-05-10 20:03:54 +00:00
|
|
|
var source_buffer = try std.Buffer.initSize(arena, 0);
|
2019-04-26 19:40:29 +00:00
|
|
|
|
2019-04-30 04:21:45 +00:00
|
|
|
var context = Context{
|
|
|
|
.tree = tree,
|
|
|
|
.source_buffer = &source_buffer,
|
2019-05-09 02:04:51 +00:00
|
|
|
.source_manager = ZigClangASTUnit_getSourceManager(ast_unit),
|
2019-04-30 04:21:45 +00:00
|
|
|
.err = undefined,
|
2019-05-10 20:03:54 +00:00
|
|
|
.decl_table = DeclTable.init(arena),
|
|
|
|
.global_scope = try arena.create(Scope.Root),
|
|
|
|
.mode = mode,
|
2019-05-28 02:20:23 +00:00
|
|
|
.ptr_params = std.BufSet.init(arena),
|
2019-05-27 21:38:09 +00:00
|
|
|
.clang_context = ZigClangASTUnit_getASTContext(ast_unit).?,
|
2019-05-10 20:03:54 +00:00
|
|
|
};
|
|
|
|
context.global_scope.* = Scope.Root{
|
|
|
|
.base = Scope{
|
|
|
|
.id = Scope.Id.Root,
|
|
|
|
.parent = null,
|
|
|
|
},
|
2019-04-30 04:21:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if (!ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, &context, declVisitorC)) {
|
|
|
|
return context.err;
|
|
|
|
}
|
2019-04-26 19:40:29 +00:00
|
|
|
|
2019-05-28 06:55:48 +00:00
|
|
|
tree.root_node.eof_token = try appendToken(&context, .Eof, "");
|
2019-04-26 19:40:29 +00:00
|
|
|
tree.source = source_buffer.toOwnedSlice();
|
2019-05-11 03:35:46 +00:00
|
|
|
if (false) {
|
|
|
|
std.debug.warn("debug source:\n{}\n==EOF==\ntokens:\n", tree.source);
|
|
|
|
var i: usize = 0;
|
|
|
|
while (i < tree.tokens.len) : (i += 1) {
|
|
|
|
const token = tree.tokens.at(i);
|
|
|
|
std.debug.warn("{}\n", token);
|
|
|
|
}
|
|
|
|
}
|
2019-04-26 19:40:29 +00:00
|
|
|
return tree;
|
|
|
|
}
|
|
|
|
|
2019-04-30 04:21:45 +00:00
|
|
|
extern fn declVisitorC(context: ?*c_void, decl: *const ZigClangDecl) bool {
|
|
|
|
const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
|
|
|
|
declVisitor(c, decl) catch |err| {
|
|
|
|
c.err = err;
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void {
|
|
|
|
switch (ZigClangDecl_getKind(decl)) {
|
|
|
|
.Function => {
|
2019-05-09 00:49:07 +00:00
|
|
|
return visitFnDecl(c, @ptrCast(*const ZigClangFunctionDecl, decl));
|
2019-04-30 04:21:45 +00:00
|
|
|
},
|
|
|
|
.Typedef => {
|
2019-05-09 02:04:51 +00:00
|
|
|
try emitWarning(c, ZigClangDecl_getLocation(decl), "TODO implement translate-c for typedefs");
|
2019-04-30 04:21:45 +00:00
|
|
|
},
|
|
|
|
.Enum => {
|
2019-05-09 02:04:51 +00:00
|
|
|
try emitWarning(c, ZigClangDecl_getLocation(decl), "TODO implement translate-c for enums");
|
2019-04-30 04:21:45 +00:00
|
|
|
},
|
|
|
|
.Record => {
|
2019-05-09 02:04:51 +00:00
|
|
|
try emitWarning(c, ZigClangDecl_getLocation(decl), "TODO implement translate-c for structs");
|
2019-04-30 04:21:45 +00:00
|
|
|
},
|
|
|
|
.Var => {
|
2019-05-09 02:04:51 +00:00
|
|
|
try emitWarning(c, ZigClangDecl_getLocation(decl), "TODO implement translate-c for variables");
|
2019-04-30 04:21:45 +00:00
|
|
|
},
|
|
|
|
else => {
|
2019-05-09 02:04:51 +00:00
|
|
|
const decl_name = try c.str(ZigClangDecl_getDeclKindName(decl));
|
|
|
|
try emitWarning(c, ZigClangDecl_getLocation(decl), "ignoring {} declaration", decl_name);
|
2019-04-30 04:21:45 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-09 00:49:07 +00:00
|
|
|
fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
|
2019-05-10 20:03:54 +00:00
|
|
|
if (try c.decl_table.put(@ptrToInt(fn_decl), {})) |_| return; // Avoid processing this decl twice
|
2019-05-10 21:44:47 +00:00
|
|
|
const rp = makeRestorePoint(c);
|
2019-05-10 20:03:54 +00:00
|
|
|
const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl)));
|
2019-05-09 02:04:51 +00:00
|
|
|
const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl);
|
2019-05-10 20:03:54 +00:00
|
|
|
const fn_qt = ZigClangFunctionDecl_getType(fn_decl);
|
|
|
|
const fn_type = ZigClangQualType_getTypePtr(fn_qt);
|
2019-05-11 03:35:46 +00:00
|
|
|
var scope = &c.global_scope.base;
|
2019-05-11 16:05:33 +00:00
|
|
|
const has_body = ZigClangFunctionDecl_hasBody(fn_decl);
|
|
|
|
const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl);
|
2019-05-11 03:35:46 +00:00
|
|
|
const decl_ctx = FnDeclContext{
|
|
|
|
.fn_name = fn_name,
|
2019-05-11 16:05:33 +00:00
|
|
|
.has_body = has_body,
|
|
|
|
.storage_class = storage_class,
|
2019-05-11 03:35:46 +00:00
|
|
|
.scope = &scope,
|
2019-05-11 16:05:33 +00:00
|
|
|
.is_export = switch (storage_class) {
|
|
|
|
.None => has_body,
|
|
|
|
.Extern, .Static => false,
|
|
|
|
.PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern"),
|
|
|
|
.Auto => unreachable, // Not legal on functions
|
|
|
|
.Register => unreachable, // Not legal on functions
|
|
|
|
},
|
2019-05-11 03:35:46 +00:00
|
|
|
};
|
2019-05-10 20:03:54 +00:00
|
|
|
const proto_node = switch (ZigClangType_getTypeClass(fn_type)) {
|
2019-05-11 03:35:46 +00:00
|
|
|
.FunctionProto => blk: {
|
|
|
|
const fn_proto_type = @ptrCast(*const ZigClangFunctionProtoType, fn_type);
|
|
|
|
break :blk transFnProto(rp, fn_proto_type, fn_decl_loc, decl_ctx) catch |err| switch (err) {
|
|
|
|
error.UnsupportedType => {
|
|
|
|
return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function");
|
|
|
|
},
|
2019-05-14 23:23:31 +00:00
|
|
|
error.OutOfMemory => |e| return e,
|
2019-05-11 03:35:46 +00:00
|
|
|
};
|
2019-05-09 02:04:51 +00:00
|
|
|
},
|
2019-05-11 16:05:33 +00:00
|
|
|
.FunctionNoProto => blk: {
|
|
|
|
const fn_no_proto_type = @ptrCast(*const ZigClangFunctionType, fn_type);
|
|
|
|
break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx) catch |err| switch (err) {
|
|
|
|
error.UnsupportedType => {
|
|
|
|
return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function");
|
|
|
|
},
|
2019-05-14 23:23:31 +00:00
|
|
|
error.OutOfMemory => |e| return e,
|
2019-05-11 16:05:33 +00:00
|
|
|
};
|
|
|
|
},
|
2019-05-10 20:03:54 +00:00
|
|
|
else => unreachable,
|
2019-05-09 02:04:51 +00:00
|
|
|
};
|
|
|
|
|
2019-05-11 03:35:46 +00:00
|
|
|
if (!decl_ctx.has_body) {
|
2019-05-10 20:03:54 +00:00
|
|
|
const semi_tok = try appendToken(c, .Semicolon, ";");
|
|
|
|
return addTopLevelDecl(c, fn_name, &proto_node.base);
|
|
|
|
}
|
2019-05-10 05:23:22 +00:00
|
|
|
|
2019-05-11 03:35:46 +00:00
|
|
|
// actual function definition with body
|
|
|
|
const body_stmt = ZigClangFunctionDecl_getBody(fn_decl);
|
|
|
|
const result = transStmt(rp, scope, body_stmt, .unused, .r_value) catch |err| switch (err) {
|
2019-05-14 23:23:31 +00:00
|
|
|
error.OutOfMemory => |e| return e,
|
2019-05-28 06:55:48 +00:00
|
|
|
error.UnsupportedTranslation,
|
|
|
|
error.UnsupportedType,
|
|
|
|
=> return failDecl(c, fn_decl_loc, fn_name, "unable to translate function"),
|
2019-05-11 03:35:46 +00:00
|
|
|
};
|
|
|
|
assert(result.node.id == ast.Node.Id.Block);
|
|
|
|
proto_node.body_node = result.node;
|
|
|
|
|
|
|
|
return addTopLevelDecl(c, fn_name, &proto_node.base);
|
|
|
|
}
|
|
|
|
|
|
|
|
const ResultUsed = enum {
|
|
|
|
used,
|
|
|
|
unused,
|
|
|
|
};
|
|
|
|
|
|
|
|
const LRValue = enum {
|
|
|
|
l_value,
|
|
|
|
r_value,
|
|
|
|
};
|
|
|
|
|
|
|
|
fn transStmt(
|
|
|
|
rp: RestorePoint,
|
|
|
|
scope: *Scope,
|
|
|
|
stmt: *const ZigClangStmt,
|
|
|
|
result_used: ResultUsed,
|
|
|
|
lrvalue: LRValue,
|
|
|
|
) !TransResult {
|
|
|
|
const sc = ZigClangStmt_getStmtClass(stmt);
|
|
|
|
switch (sc) {
|
|
|
|
.CompoundStmtClass => return transCompoundStmt(rp, scope, @ptrCast(*const ZigClangCompoundStmt, stmt)),
|
2019-05-26 22:43:13 +00:00
|
|
|
.DeclStmtClass => return transDeclStmt(rp, scope, @ptrCast(*const ZigClangDeclStmt, stmt)),
|
2019-05-28 02:20:23 +00:00
|
|
|
.DeclRefExprClass => return transDeclRefExpr(rp, scope, @ptrCast(*const ZigClangDeclRefExpr, stmt), lrvalue),
|
2019-05-27 02:14:50 +00:00
|
|
|
.ImplicitCastExprClass => return transImplicitCastExpr(rp, scope, @ptrCast(*const ZigClangImplicitCastExpr, stmt)),
|
2019-05-11 03:35:46 +00:00
|
|
|
else => {
|
|
|
|
return revertAndWarn(
|
|
|
|
rp,
|
|
|
|
error.UnsupportedTranslation,
|
|
|
|
ZigClangStmt_getBeginLoc(stmt),
|
|
|
|
"TODO implement translation of stmt class {}",
|
|
|
|
@tagName(sc),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transCompoundStmtInline(
|
|
|
|
rp: RestorePoint,
|
|
|
|
parent_scope: *Scope,
|
|
|
|
stmt: *const ZigClangCompoundStmt,
|
|
|
|
block_node: *ast.Node.Block,
|
|
|
|
) TransError!TransResult {
|
|
|
|
var it = ZigClangCompoundStmt_body_begin(stmt);
|
|
|
|
const end_it = ZigClangCompoundStmt_body_end(stmt);
|
|
|
|
var scope = parent_scope;
|
|
|
|
while (it != end_it) : (it += 1) {
|
|
|
|
const result = try transStmt(rp, scope, it.*, .unused, .r_value);
|
|
|
|
scope = result.child_scope;
|
|
|
|
try block_node.statements.push(result.node);
|
|
|
|
}
|
|
|
|
return TransResult{
|
|
|
|
.node = &block_node.base,
|
|
|
|
.child_scope = scope,
|
|
|
|
.node_scope = scope,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) !TransResult {
|
|
|
|
const lbrace_tok = try appendToken(rp.c, .LBrace, "{");
|
|
|
|
const block_scope = try Scope.Block.create(rp.c, scope, lbrace_tok);
|
|
|
|
const inline_result = try transCompoundStmtInline(rp, &block_scope.base, stmt, block_scope.block_node);
|
|
|
|
block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
|
|
|
|
return TransResult{
|
|
|
|
.node = &block_scope.block_node.base,
|
|
|
|
.node_scope = inline_result.node_scope,
|
|
|
|
.child_scope = inline_result.child_scope,
|
|
|
|
};
|
2019-05-10 20:03:54 +00:00
|
|
|
}
|
|
|
|
|
2019-05-26 23:51:25 +00:00
|
|
|
fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDeclStmt) !TransResult {
|
2019-05-26 22:43:13 +00:00
|
|
|
const c = rp.c;
|
2019-05-26 23:51:25 +00:00
|
|
|
const block_scope = findBlockScope(parent_scope);
|
|
|
|
var scope = parent_scope;
|
|
|
|
|
2019-05-26 22:43:13 +00:00
|
|
|
var it = ZigClangDeclStmt_decl_begin(stmt);
|
|
|
|
const end_it = ZigClangDeclStmt_decl_end(stmt);
|
|
|
|
while (it != end_it) : (it += 1) {
|
|
|
|
switch (ZigClangDecl_getKind(it.*)) {
|
|
|
|
.Var => {
|
|
|
|
const var_decl = @ptrCast(*const ZigClangVarDecl, it.*);
|
|
|
|
|
2019-05-27 00:05:49 +00:00
|
|
|
const thread_local_token = if (ZigClangVarDecl_getTLSKind(var_decl) == .None)
|
|
|
|
null
|
|
|
|
else
|
|
|
|
try appendToken(c, .Keyword_threadlocal, "threadlocal");
|
2019-05-26 23:51:25 +00:00
|
|
|
const qual_type = ZigClangVarDecl_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 c_name = try c.str(ZigClangDecl_getName_bytes_begin(
|
|
|
|
@ptrCast(*const ZigClangDecl, var_decl),
|
|
|
|
));
|
|
|
|
const name_token = try appendToken(c, .Identifier, c_name);
|
2019-05-26 22:43:13 +00:00
|
|
|
|
2019-05-26 23:51:25 +00:00
|
|
|
const var_scope = try c.a().create(Scope.Var);
|
|
|
|
var_scope.* = Scope.Var{
|
|
|
|
.base = Scope{ .id = .Var, .parent = parent_scope },
|
|
|
|
.c_name = c_name,
|
|
|
|
.zig_name = c_name, // TODO: getWantedName
|
|
|
|
};
|
|
|
|
scope = &var_scope.base;
|
|
|
|
|
2019-05-27 00:36:47 +00:00
|
|
|
const eq_token = try appendToken(c, .Equal, "=");
|
|
|
|
const init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
|
2019-05-27 02:14:50 +00:00
|
|
|
(try transExpr(rp, scope, expr, .used, .r_value)).node
|
2019-05-27 00:36:47 +00:00
|
|
|
else
|
|
|
|
null;
|
|
|
|
const semicolon_token = try appendToken(c, .Semicolon, ";");
|
|
|
|
|
2019-05-27 22:58:21 +00:00
|
|
|
const node = try c.a().create(ast.Node.VarDecl);
|
2019-05-26 22:43:13 +00:00
|
|
|
node.* = ast.Node.VarDecl{
|
|
|
|
.base = ast.Node{ .id = .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,
|
2019-05-27 02:14:50 +00:00
|
|
|
.comptime_token = null,
|
2019-05-26 22:43:13 +00:00
|
|
|
.extern_export_token = null, // TODO ?TokenIndex,
|
|
|
|
.lib_name = null, // TODO ?*Node,
|
|
|
|
.type_node = null, // TODO ?*Node,
|
|
|
|
.align_node = null, // TODO ?*Node,
|
|
|
|
.section_node = null, // TODO ?*Node,
|
|
|
|
.init_node = init_node,
|
|
|
|
.semicolon_token = semicolon_token,
|
|
|
|
};
|
|
|
|
try block_scope.block_node.statements.push(&node.base);
|
|
|
|
},
|
|
|
|
|
|
|
|
else => |kind| return revertAndWarn(
|
|
|
|
rp,
|
|
|
|
error.UnsupportedTranslation,
|
|
|
|
ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)),
|
|
|
|
"TODO implement translation of DeclStmt kind {}",
|
|
|
|
@tagName(kind),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return TransResult{
|
|
|
|
.node = &block_scope.block_node.base,
|
|
|
|
.node_scope = scope,
|
|
|
|
.child_scope = scope,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-05-28 02:20:23 +00:00
|
|
|
fn transDeclRefExpr(
|
|
|
|
rp: RestorePoint,
|
|
|
|
scope: *Scope,
|
|
|
|
expr: *const ZigClangDeclRefExpr,
|
|
|
|
lrvalue: LRValue,
|
|
|
|
) !TransResult {
|
|
|
|
const value_decl = ZigClangDeclRefExpr_getDecl(expr);
|
|
|
|
const c_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, value_decl)));
|
|
|
|
const zig_name = transLookupZigIdentifier(scope, c_name);
|
|
|
|
if (lrvalue == .l_value) try rp.c.ptr_params.put(zig_name);
|
|
|
|
const node = try appendIdentifier(rp.c, zig_name);
|
|
|
|
return TransResult{
|
|
|
|
.node = node,
|
|
|
|
.node_scope = scope,
|
|
|
|
.child_scope = scope,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-05-27 02:14:50 +00:00
|
|
|
fn transImplicitCastExpr(
|
|
|
|
rp: RestorePoint,
|
|
|
|
scope: *Scope,
|
|
|
|
expr: *const ZigClangImplicitCastExpr,
|
|
|
|
) !TransResult {
|
2019-05-27 21:38:09 +00:00
|
|
|
const c = rp.c;
|
2019-05-28 01:18:27 +00:00
|
|
|
const sub_expr = ZigClangImplicitCastExpr_getSubExpr(expr);
|
2019-05-27 21:38:09 +00:00
|
|
|
switch (ZigClangImplicitCastExpr_getCastKind(expr)) {
|
|
|
|
.BitCast => {
|
2019-05-28 00:54:40 +00:00
|
|
|
const node = try transExpr(rp, scope, @ptrCast(*const ZigClangExpr, sub_expr), .used, .r_value);
|
2019-05-27 21:38:09 +00:00
|
|
|
const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr));
|
2019-05-28 00:54:40 +00:00
|
|
|
const src_type = getExprQualType(c, sub_expr);
|
2019-05-28 06:55:48 +00:00
|
|
|
return TransResult{
|
|
|
|
.node = try transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, node.node),
|
|
|
|
.node_scope = scope,
|
|
|
|
.child_scope = scope,
|
|
|
|
};
|
2019-05-27 21:38:09 +00:00
|
|
|
},
|
2019-05-28 01:18:27 +00:00
|
|
|
.FunctionToPointerDecay, .ArrayToPointerDecay => {
|
|
|
|
return maybeSuppressResult(
|
|
|
|
rp,
|
|
|
|
scope,
|
|
|
|
.used,
|
|
|
|
try transExpr(rp, scope, @ptrCast(*const ZigClangExpr, sub_expr), .used, .r_value),
|
|
|
|
);
|
|
|
|
},
|
2019-05-27 02:14:50 +00:00
|
|
|
else => |kind| return revertAndWarn(
|
|
|
|
rp,
|
|
|
|
error.UnsupportedTranslation,
|
|
|
|
ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, expr)),
|
|
|
|
"TODO implement translation of CastKind {}",
|
|
|
|
@tagName(kind),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-27 21:38:09 +00:00
|
|
|
fn transCCast(
|
|
|
|
rp: RestorePoint,
|
|
|
|
scope: *Scope,
|
|
|
|
loc: ZigClangSourceLocation,
|
|
|
|
dst_type: ZigClangQualType,
|
|
|
|
src_type: ZigClangQualType,
|
2019-05-28 06:55:48 +00:00
|
|
|
expr: *ast.Node,
|
|
|
|
) !*ast.Node {
|
|
|
|
if (ZigClangType_isVoidType(qualTypeCanon(dst_type))) return expr;
|
|
|
|
if (ZigClangQualType_eq(dst_type, src_type)) return expr;
|
|
|
|
if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type))
|
|
|
|
return transCPtrCast(rp, loc, dst_type, src_type, expr);
|
|
|
|
if (cIsUnsignedInteger(dst_type) and qualTypeIsPtr(src_type)) {
|
|
|
|
const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "ptrToInt");
|
|
|
|
try builtin_node.params.push(expr);
|
|
|
|
return &(try transCreateNodeFnCall(rp.c, try transQualType(rp, dst_type, loc), &builtin_node.base)).base;
|
|
|
|
}
|
|
|
|
if (cIsUnsignedInteger(src_type) and qualTypeIsPtr(dst_type)) {
|
|
|
|
const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "intToPtr");
|
|
|
|
try builtin_node.params.push(try transQualType(rp, dst_type, loc));
|
|
|
|
try builtin_node.params.push(expr);
|
|
|
|
return &builtin_node.base;
|
|
|
|
}
|
|
|
|
// TODO: maybe widen to increase size
|
|
|
|
// TODO: maybe bitcast to change sign
|
|
|
|
// TODO: maybe truncate to reduce size
|
|
|
|
return &(try transCreateNodeFnCall(rp.c, try transQualType(rp, dst_type, loc), expr)).base;
|
2019-05-27 21:38:09 +00:00
|
|
|
}
|
|
|
|
|
2019-05-27 02:14:50 +00:00
|
|
|
fn transExpr(
|
|
|
|
rp: RestorePoint,
|
|
|
|
scope: *Scope,
|
|
|
|
expr: *const ZigClangExpr,
|
|
|
|
used: ResultUsed,
|
|
|
|
lrvalue: LRValue,
|
|
|
|
) TransError!TransResult {
|
|
|
|
return transStmt(rp, scope, @ptrCast(*const ZigClangStmt, expr), used, lrvalue);
|
2019-05-27 00:36:47 +00:00
|
|
|
}
|
|
|
|
|
2019-05-26 22:43:13 +00:00
|
|
|
fn findBlockScope(inner: *Scope) *Scope.Block {
|
|
|
|
var scope = inner;
|
|
|
|
while (true) : (scope = scope.parent orelse unreachable) {
|
|
|
|
if (scope.id == .Block) return @fieldParentPtr(Scope.Block, "base", scope);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-28 02:20:23 +00:00
|
|
|
fn transLookupZigIdentifier(inner: *Scope, c_name: []const u8) []const u8 {
|
|
|
|
var scope = inner;
|
|
|
|
while (true) : (scope = scope.parent orelse return c_name) {
|
|
|
|
if (scope.id == .Var) {
|
|
|
|
const var_scope = @ptrCast(*const Scope.Var, scope);
|
|
|
|
if (std.mem.eql(u8, var_scope.c_name, c_name)) return var_scope.zig_name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-28 06:55:48 +00:00
|
|
|
fn transCPtrCast(
|
|
|
|
rp: RestorePoint,
|
|
|
|
loc: ZigClangSourceLocation,
|
|
|
|
dst_type: ZigClangQualType,
|
|
|
|
src_type: ZigClangQualType,
|
|
|
|
expr: *ast.Node,
|
|
|
|
) !*ast.Node {
|
|
|
|
const ty = ZigClangQualType_getTypePtr(dst_type);
|
|
|
|
const child_type = ZigClangType_getPointeeType(ty);
|
|
|
|
const dst_type_node = try transType(rp, ty, loc);
|
|
|
|
const child_type_node = try transQualType(rp, child_type, loc);
|
|
|
|
|
|
|
|
// Implicit downcasting from higher to lower alignment values is forbidden,
|
|
|
|
// use @alignCast to side-step this problem
|
|
|
|
const ptrcast_node = try transCreateNodeBuiltinFnCall(rp.c, "ptrCast");
|
|
|
|
try ptrcast_node.params.push(dst_type_node);
|
|
|
|
|
|
|
|
if (ZigClangType_isVoidType(qualTypeCanon(child_type))) {
|
|
|
|
// void has 1-byte alignment, so @alignCast is not needed
|
|
|
|
try ptrcast_node.params.push(expr);
|
|
|
|
} else {
|
|
|
|
const alignof_node = try transCreateNodeBuiltinFnCall(rp.c, "alignOf");
|
|
|
|
try alignof_node.params.push(child_type_node);
|
|
|
|
const aligncast_node = try transCreateNodeBuiltinFnCall(rp.c, "alignCast");
|
|
|
|
try aligncast_node.params.push(&alignof_node.base);
|
|
|
|
try aligncast_node.params.push(expr);
|
|
|
|
try ptrcast_node.params.push(&aligncast_node.base);
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ptrcast_node.base;
|
|
|
|
}
|
|
|
|
|
2019-05-28 01:18:27 +00:00
|
|
|
fn maybeSuppressResult(
|
|
|
|
rp: RestorePoint,
|
|
|
|
scope: *Scope,
|
|
|
|
used: ResultUsed,
|
|
|
|
result: TransResult,
|
|
|
|
) !TransResult {
|
|
|
|
if (used == .used) return result;
|
|
|
|
const lhs = try appendIdentifier(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{
|
|
|
|
.base = ast.Node{ .id = .InfixOp },
|
|
|
|
.op_token = op_token,
|
|
|
|
.lhs = lhs,
|
|
|
|
.op = .Assign,
|
|
|
|
.rhs = result.node,
|
|
|
|
};
|
|
|
|
return TransResult{
|
|
|
|
.node = &op_node.base,
|
|
|
|
.child_scope = scope,
|
|
|
|
.node_scope = scope,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-05-10 20:03:54 +00:00
|
|
|
fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void {
|
|
|
|
try c.tree.root_node.decls.push(decl_node);
|
2019-05-09 02:04:51 +00:00
|
|
|
}
|
|
|
|
|
2019-05-14 23:23:31 +00:00
|
|
|
fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) TypeError!*ast.Node {
|
2019-05-10 21:44:47 +00:00
|
|
|
return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc);
|
|
|
|
}
|
|
|
|
|
2019-05-28 06:55:48 +00:00
|
|
|
fn qualTypeIsPtr(qt: ZigClangQualType) bool {
|
|
|
|
return ZigClangType_getTypeClass(qualTypeCanon(qt)) == .Pointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn qualTypeChildIsFnProto(qt: ZigClangQualType) bool {
|
|
|
|
const ty = ZigClangQualType_getTypePtr(qt);
|
|
|
|
if (ZigClangType_getTypeClass(ty) == .Paren) {
|
|
|
|
const paren_type = @ptrCast(*const ZigClangParenType, ty);
|
|
|
|
const inner_type = ZigClangParenType_getInnerType(ty);
|
|
|
|
return ZigClangQualType_getTypeClass(inner_type) == .FunctionProto;
|
|
|
|
}
|
|
|
|
if (ZigClangType_getTypeClass(ty) == .Attributed) {
|
|
|
|
const attr_type = @ptrCast(*const ZigClangAttributedType, ty);
|
|
|
|
return qualTypeChildIsFnProto(bitcast(ZigClangAttributedType_getEquivalentType(attr_type)));
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-05-10 21:44:47 +00:00
|
|
|
fn qualTypeCanon(qt: ZigClangQualType) *const ZigClangType {
|
|
|
|
const canon = ZigClangQualType_getCanonicalType(qt);
|
|
|
|
return ZigClangQualType_getTypePtr(canon);
|
2019-05-09 02:04:51 +00:00
|
|
|
}
|
|
|
|
|
2019-05-27 21:38:09 +00:00
|
|
|
fn getExprQualType(c: *Context, expr: *const ZigClangExpr) ZigClangQualType {
|
|
|
|
blk: {
|
|
|
|
// If this is a C `char *`, turn it into a `const char *`
|
|
|
|
if (ZigClangExpr_getStmtClass(expr) != .ImplicitCastExprClass) break :blk;
|
|
|
|
const cast_expr = @ptrCast(*const ZigClangImplicitCastExpr, expr);
|
|
|
|
if (ZigClangImplicitCastExpr_getCastKind(cast_expr) != .ArrayToPointerDecay) break :blk;
|
|
|
|
const sub_expr = ZigClangImplicitCastExpr_getSubExpr(cast_expr);
|
|
|
|
if (ZigClangExpr_getStmtClass(sub_expr) != .StringLiteralClass) break :blk;
|
|
|
|
const array_qt = ZigClangExpr_getType(sub_expr);
|
|
|
|
const array_type = @ptrCast(*const ZigClangArrayType, ZigClangQualType_getTypePtr(array_qt));
|
|
|
|
var pointee_qt = ZigClangArrayType_getElementType(array_type);
|
|
|
|
ZigClangQualType_addConst(&pointee_qt);
|
|
|
|
return ZigClangASTContext_getPointerType(c.clang_context, pointee_qt);
|
|
|
|
}
|
|
|
|
return ZigClangExpr_getType(expr);
|
|
|
|
}
|
|
|
|
|
2019-05-28 06:55:48 +00:00
|
|
|
fn typeIsOpaque(c: *Context, ty: *const ZigClangType, loc: ZigClangSourceLocation) bool {
|
|
|
|
switch (ZigClangType_getTypeClass(ty)) {
|
|
|
|
.Builtin => {
|
|
|
|
const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
|
|
|
|
return ZigClangBuiltinType_getKind(builtin_ty) == .Void;
|
|
|
|
},
|
|
|
|
.Record => {
|
|
|
|
const record_ty = @ptrCast(*const ZigClangRecordType, ty);
|
|
|
|
const record_decl = ZigClangRecordType_getDecl(record_ty);
|
|
|
|
return (ZigClangRecordDecl_getDefinition(record_decl) == null);
|
|
|
|
},
|
|
|
|
.Elaborated => {
|
|
|
|
const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty);
|
|
|
|
const qt = ZigClangElaboratedType_getNamedType(elaborated_ty);
|
|
|
|
return typeIsOpaque(c, ZigClangQualType_getTypePtr(qt), loc);
|
|
|
|
},
|
|
|
|
.Typedef => {
|
|
|
|
const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
|
|
|
|
const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
|
|
|
|
const underlying_type = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
|
|
|
|
return typeIsOpaque(c, ZigClangQualType_getTypePtr(underlying_type), loc);
|
|
|
|
},
|
|
|
|
else => return false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cIsUnsignedInteger(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)) {
|
|
|
|
.Char_U,
|
|
|
|
.UChar,
|
|
|
|
.Char_S,
|
|
|
|
.UShort,
|
|
|
|
.UInt,
|
|
|
|
.ULong,
|
|
|
|
.ULongLong,
|
|
|
|
.UInt128,
|
|
|
|
.WChar_U,
|
|
|
|
=> true,
|
|
|
|
else => false,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.BuiltinCall {
|
|
|
|
const node = try c.a().create(ast.Node.BuiltinCall);
|
|
|
|
node.* = ast.Node.BuiltinCall{
|
|
|
|
.base = ast.Node{ .id = .BuiltinCall },
|
|
|
|
.builtin_token = try appendToken(c, .Builtin, name),
|
|
|
|
.params = ast.Node.BuiltinCall.ParamList.init(c.a()),
|
|
|
|
.rparen_token = undefined, // TODO TokenIndex,
|
|
|
|
};
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node, first_arg: *ast.Node) !*ast.Node.SuffixOp {
|
|
|
|
const node = try c.a().create(ast.Node.SuffixOp);
|
|
|
|
node.* = ast.Node.SuffixOp{
|
|
|
|
.base = ast.Node{ .id = .SuffixOp },
|
|
|
|
.lhs = fn_expr,
|
|
|
|
.op = ast.Node.SuffixOp.Op{
|
|
|
|
.Call = ast.Node.SuffixOp.Op.Call{
|
|
|
|
.params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()),
|
|
|
|
.async_attr = null,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
.rtoken = undefined, // TODO TokenIndex
|
|
|
|
};
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transCreateNodePrefixOp(c: *Context, op: ast.Node.PrefixOp.Op, rhs: *ast.Node) !*ast.Node {
|
|
|
|
const node = try c.a().create(ast.Node.PrefixOp);
|
|
|
|
node.* = ast.Node.PrefixOp{
|
|
|
|
.base = ast.Node{ .id = .PrefixOp },
|
|
|
|
.op_token = undefined, // TODO TokenIndex,
|
|
|
|
.op = op,
|
|
|
|
.rhs = rhs,
|
|
|
|
};
|
|
|
|
return &node.base;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transCreateNodePtrType(
|
|
|
|
c: *Context,
|
|
|
|
is_const: bool,
|
|
|
|
is_volatile: bool,
|
|
|
|
rhs: *ast.Node,
|
|
|
|
op_tok_id: std.zig.Token.Id,
|
|
|
|
) !*ast.Node {
|
|
|
|
const node = try c.a().create(ast.Node.PrefixOp);
|
|
|
|
node.* = ast.Node.PrefixOp{
|
|
|
|
.base = ast.Node{ .id = .PrefixOp },
|
|
|
|
.op_token = try appendToken(c, op_tok_id, ""), // TODO TokenIndex,
|
|
|
|
.op = ast.Node.PrefixOp.Op{
|
|
|
|
.PtrType = ast.Node.PrefixOp.PtrInfo{
|
|
|
|
.allowzero_token = null,
|
|
|
|
.align_info = null,
|
|
|
|
.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,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
.rhs = rhs,
|
|
|
|
};
|
|
|
|
return &node.base;
|
|
|
|
}
|
|
|
|
|
2019-05-10 05:23:22 +00:00
|
|
|
const RestorePoint = struct {
|
2019-05-10 21:44:47 +00:00
|
|
|
c: *Context,
|
2019-05-10 05:23:22 +00:00
|
|
|
token_index: ast.TokenIndex,
|
|
|
|
src_buf_index: usize,
|
|
|
|
|
|
|
|
fn activate(self: RestorePoint) void {
|
2019-05-10 21:44:47 +00:00
|
|
|
self.c.tree.tokens.shrink(self.token_index);
|
|
|
|
self.c.source_buffer.shrink(self.src_buf_index);
|
2019-05-10 05:23:22 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
fn makeRestorePoint(c: *Context) RestorePoint {
|
|
|
|
return RestorePoint{
|
2019-05-10 21:44:47 +00:00
|
|
|
.c = c,
|
2019-05-10 05:23:22 +00:00
|
|
|
.token_index = c.tree.tokens.len,
|
|
|
|
.src_buf_index = c.source_buffer.len(),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-05-14 23:23:31 +00:00
|
|
|
fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSourceLocation) TypeError!*ast.Node {
|
2019-05-09 20:52:30 +00:00
|
|
|
switch (ZigClangType_getTypeClass(ty)) {
|
|
|
|
.Builtin => {
|
|
|
|
const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
|
|
|
|
switch (ZigClangBuiltinType_getKind(builtin_ty)) {
|
2019-05-10 21:55:29 +00:00
|
|
|
.Void => return appendIdentifier(rp.c, "c_void"),
|
|
|
|
.Bool => return appendIdentifier(rp.c, "bool"),
|
|
|
|
.Char_U, .UChar, .Char_S, .Char8 => return appendIdentifier(rp.c, "u8"),
|
|
|
|
.SChar => return appendIdentifier(rp.c, "i8"),
|
|
|
|
.UShort => return appendIdentifier(rp.c, "c_ushort"),
|
|
|
|
.UInt => return appendIdentifier(rp.c, "c_uint"),
|
|
|
|
.ULong => return appendIdentifier(rp.c, "c_ulong"),
|
|
|
|
.ULongLong => return appendIdentifier(rp.c, "c_ulonglong"),
|
|
|
|
.Short => return appendIdentifier(rp.c, "c_short"),
|
|
|
|
.Int => return appendIdentifier(rp.c, "c_int"),
|
|
|
|
.Long => return appendIdentifier(rp.c, "c_long"),
|
|
|
|
.LongLong => return appendIdentifier(rp.c, "c_longlong"),
|
|
|
|
.UInt128 => return appendIdentifier(rp.c, "u128"),
|
|
|
|
.Int128 => return appendIdentifier(rp.c, "i128"),
|
|
|
|
.Float => return appendIdentifier(rp.c, "f32"),
|
|
|
|
.Double => return appendIdentifier(rp.c, "f64"),
|
|
|
|
.Float128 => return appendIdentifier(rp.c, "f128"),
|
|
|
|
.Float16 => return appendIdentifier(rp.c, "f16"),
|
|
|
|
.LongDouble => return appendIdentifier(rp.c, "c_longdouble"),
|
2019-05-10 05:23:22 +00:00
|
|
|
else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type"),
|
2019-05-09 20:52:30 +00:00
|
|
|
}
|
|
|
|
},
|
2019-05-28 06:55:48 +00:00
|
|
|
.Pointer => {
|
|
|
|
const child_qt = ZigClangType_getPointeeType(ty);
|
|
|
|
const child_node = try transQualType(rp, child_qt, source_loc);
|
|
|
|
if (qualTypeChildIsFnProto(child_qt))
|
|
|
|
return transCreateNodePrefixOp(rp.c, .OptionalType, child_node);
|
|
|
|
if (typeIsOpaque(rp.c, ZigClangQualType_getTypePtr(child_qt), source_loc)) {
|
|
|
|
const pointer_node = try transCreateNodePtrType(
|
|
|
|
rp.c,
|
|
|
|
ZigClangQualType_isConstQualified(child_qt),
|
|
|
|
ZigClangQualType_isVolatileQualified(child_qt),
|
|
|
|
child_node,
|
|
|
|
.Asterisk,
|
|
|
|
);
|
|
|
|
return transCreateNodePrefixOp(rp.c, .OptionalType, pointer_node);
|
|
|
|
}
|
|
|
|
return transCreateNodePtrType(
|
|
|
|
rp.c,
|
|
|
|
ZigClangQualType_isConstQualified(child_qt),
|
|
|
|
ZigClangQualType_isVolatileQualified(child_qt),
|
|
|
|
child_node,
|
|
|
|
.BracketStarCBracket,
|
|
|
|
);
|
|
|
|
},
|
2019-05-10 21:44:47 +00:00
|
|
|
.FunctionProto => {
|
|
|
|
const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty);
|
2019-05-11 03:35:46 +00:00
|
|
|
const fn_proto = try transFnProto(rp, fn_proto_ty, source_loc, null);
|
2019-05-10 21:44:47 +00:00
|
|
|
return &fn_proto.base;
|
|
|
|
},
|
2019-05-09 20:52:30 +00:00
|
|
|
else => {
|
2019-05-10 21:44:47 +00:00
|
|
|
const type_name = rp.c.str(ZigClangType_getTypeClassName(ty));
|
2019-05-10 05:23:22 +00:00
|
|
|
return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", type_name);
|
2019-05-09 20:52:30 +00:00
|
|
|
},
|
|
|
|
}
|
2019-05-09 02:04:51 +00:00
|
|
|
}
|
|
|
|
|
2019-05-11 03:35:46 +00:00
|
|
|
const FnDeclContext = struct {
|
|
|
|
fn_name: []const u8,
|
|
|
|
has_body: bool,
|
|
|
|
storage_class: ZigClangStorageClass,
|
|
|
|
scope: **Scope,
|
2019-05-11 16:05:33 +00:00
|
|
|
is_export: bool,
|
2019-05-11 03:35:46 +00:00
|
|
|
};
|
|
|
|
|
2019-05-11 16:05:33 +00:00
|
|
|
fn transCC(
|
|
|
|
rp: RestorePoint,
|
|
|
|
fn_ty: *const ZigClangFunctionType,
|
|
|
|
source_loc: ZigClangSourceLocation,
|
|
|
|
) !CallingConvention {
|
|
|
|
const clang_cc = ZigClangFunctionType_getCallConv(fn_ty);
|
|
|
|
switch (clang_cc) {
|
|
|
|
.C => return CallingConvention.C,
|
|
|
|
.X86StdCall => return CallingConvention.Stdcall,
|
|
|
|
else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: {}", @tagName(clang_cc)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-10 20:03:54 +00:00
|
|
|
fn transFnProto(
|
2019-05-10 21:44:47 +00:00
|
|
|
rp: RestorePoint,
|
2019-05-10 20:03:54 +00:00
|
|
|
fn_proto_ty: *const ZigClangFunctionProtoType,
|
|
|
|
source_loc: ZigClangSourceLocation,
|
2019-05-11 03:35:46 +00:00
|
|
|
fn_decl_context: ?FnDeclContext,
|
2019-05-10 20:03:54 +00:00
|
|
|
) !*ast.Node.FnProto {
|
|
|
|
const fn_ty = @ptrCast(*const ZigClangFunctionType, fn_proto_ty);
|
2019-05-11 16:05:33 +00:00
|
|
|
const cc = try transCC(rp, fn_ty, source_loc);
|
2019-05-10 20:03:54 +00:00
|
|
|
const is_var_args = ZigClangFunctionProtoType_isVariadic(fn_proto_ty);
|
|
|
|
const param_count: usize = ZigClangFunctionProtoType_getNumParams(fn_proto_ty);
|
|
|
|
var i: usize = 0;
|
|
|
|
while (i < param_count) : (i += 1) {
|
|
|
|
return revertAndWarn(rp, error.UnsupportedType, source_loc, "TODO: implement parameters for FunctionProto in transType");
|
|
|
|
}
|
2019-05-11 16:05:33 +00:00
|
|
|
|
|
|
|
return finishTransFnProto(rp, fn_ty, source_loc, fn_decl_context, is_var_args, cc);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transFnNoProto(
|
|
|
|
rp: RestorePoint,
|
|
|
|
fn_ty: *const ZigClangFunctionType,
|
|
|
|
source_loc: ZigClangSourceLocation,
|
|
|
|
fn_decl_context: ?FnDeclContext,
|
|
|
|
) !*ast.Node.FnProto {
|
|
|
|
const cc = try transCC(rp, fn_ty, source_loc);
|
|
|
|
const is_var_args = if (fn_decl_context) |ctx| !ctx.is_export else true;
|
|
|
|
return finishTransFnProto(rp, fn_ty, source_loc, fn_decl_context, is_var_args, cc);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn finishTransFnProto(
|
|
|
|
rp: RestorePoint,
|
|
|
|
fn_ty: *const ZigClangFunctionType,
|
|
|
|
source_loc: ZigClangSourceLocation,
|
|
|
|
fn_decl_context: ?FnDeclContext,
|
|
|
|
is_var_args: bool,
|
|
|
|
cc: CallingConvention,
|
|
|
|
) !*ast.Node.FnProto {
|
|
|
|
const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
|
|
|
|
|
2019-05-10 20:03:54 +00:00
|
|
|
// TODO check for always_inline attribute
|
|
|
|
// TODO check for align attribute
|
|
|
|
|
2019-05-10 21:55:29 +00:00
|
|
|
// pub extern fn name(...) T
|
|
|
|
const pub_tok = try appendToken(rp.c, .Keyword_pub, "pub");
|
2019-05-10 21:44:47 +00:00
|
|
|
const cc_tok = if (cc == .Stdcall) try appendToken(rp.c, .Keyword_stdcallcc, "stdcallcc") else null;
|
2019-05-10 20:03:54 +00:00
|
|
|
const extern_export_inline_tok = if (is_export)
|
2019-05-10 21:44:47 +00:00
|
|
|
try appendToken(rp.c, .Keyword_export, "export")
|
2019-05-10 20:03:54 +00:00
|
|
|
else if (cc == .C)
|
2019-05-10 21:44:47 +00:00
|
|
|
try appendToken(rp.c, .Keyword_extern, "extern")
|
2019-05-10 20:03:54 +00:00
|
|
|
else
|
|
|
|
null;
|
2019-05-10 21:44:47 +00:00
|
|
|
const fn_tok = try appendToken(rp.c, .Keyword_fn, "fn");
|
2019-05-11 03:35:46 +00:00
|
|
|
const name_tok = if (fn_decl_context) |ctx| try appendToken(rp.c, .Identifier, ctx.fn_name) else null;
|
2019-05-10 21:44:47 +00:00
|
|
|
const lparen_tok = try appendToken(rp.c, .LParen, "(");
|
|
|
|
const var_args_tok = if (is_var_args) try appendToken(rp.c, .Ellipsis3, "...") else null;
|
|
|
|
const rparen_tok = try appendToken(rp.c, .RParen, ")");
|
2019-05-10 20:03:54 +00:00
|
|
|
|
|
|
|
const return_type_node = blk: {
|
|
|
|
if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) {
|
2019-05-10 21:44:47 +00:00
|
|
|
break :blk try appendIdentifier(rp.c, "noreturn");
|
2019-05-10 20:03:54 +00:00
|
|
|
} else {
|
2019-05-10 21:44:47 +00:00
|
|
|
const return_qt = ZigClangFunctionType_getReturnType(fn_ty);
|
|
|
|
if (ZigClangType_isVoidType(qualTypeCanon(return_qt))) {
|
|
|
|
break :blk try appendIdentifier(rp.c, "void");
|
|
|
|
} else {
|
|
|
|
break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) {
|
|
|
|
error.UnsupportedType => {
|
|
|
|
try emitWarning(rp.c, source_loc, "unsupported function proto return type");
|
|
|
|
return err;
|
|
|
|
},
|
2019-05-14 23:23:31 +00:00
|
|
|
error.OutOfMemory => |e| return e,
|
2019-05-10 21:44:47 +00:00
|
|
|
};
|
|
|
|
}
|
2019-05-10 20:03:54 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-05-10 21:44:47 +00:00
|
|
|
const fn_proto = try rp.c.a().create(ast.Node.FnProto);
|
2019-05-10 20:03:54 +00:00
|
|
|
fn_proto.* = ast.Node.FnProto{
|
|
|
|
.base = ast.Node{ .id = ast.Node.Id.FnProto },
|
|
|
|
.doc_comments = null,
|
2019-05-10 21:55:29 +00:00
|
|
|
.visib_token = pub_tok,
|
2019-05-10 20:03:54 +00:00
|
|
|
.fn_token = fn_tok,
|
|
|
|
.name_token = name_tok,
|
2019-05-10 21:44:47 +00:00
|
|
|
.params = ast.Node.FnProto.ParamList.init(rp.c.a()),
|
2019-05-10 20:03:54 +00:00
|
|
|
.return_type = ast.Node.FnProto.ReturnType{ .Explicit = return_type_node },
|
2019-05-11 16:05:33 +00:00
|
|
|
.var_args_token = null, // TODO this field is broken in the AST data model
|
2019-05-10 20:03:54 +00:00
|
|
|
.extern_export_inline_token = extern_export_inline_tok,
|
|
|
|
.cc_token = cc_tok,
|
|
|
|
.async_attr = null,
|
|
|
|
.body_node = null,
|
|
|
|
.lib_name = null,
|
|
|
|
.align_expr = null,
|
|
|
|
.section_expr = null,
|
|
|
|
};
|
2019-05-11 16:05:33 +00:00
|
|
|
if (is_var_args) {
|
|
|
|
const var_arg_node = try rp.c.a().create(ast.Node.ParamDecl);
|
|
|
|
var_arg_node.* = ast.Node.ParamDecl{
|
|
|
|
.base = ast.Node{ .id = ast.Node.Id.ParamDecl },
|
|
|
|
.doc_comments = null,
|
|
|
|
.comptime_token = null,
|
|
|
|
.noalias_token = null,
|
|
|
|
.name_token = null,
|
|
|
|
.type_node = undefined,
|
|
|
|
.var_args_token = var_args_tok,
|
|
|
|
};
|
|
|
|
try fn_proto.params.push(&var_arg_node.base);
|
|
|
|
}
|
2019-05-10 20:03:54 +00:00
|
|
|
return fn_proto;
|
|
|
|
}
|
|
|
|
|
2019-05-10 05:23:22 +00:00
|
|
|
fn revertAndWarn(
|
2019-05-10 21:44:47 +00:00
|
|
|
rp: RestorePoint,
|
2019-05-10 05:23:22 +00:00
|
|
|
err: var,
|
|
|
|
source_loc: ZigClangSourceLocation,
|
|
|
|
comptime format: []const u8,
|
|
|
|
args: ...,
|
|
|
|
) (@typeOf(err) || error{OutOfMemory}) {
|
2019-05-10 21:44:47 +00:00
|
|
|
rp.activate();
|
|
|
|
try emitWarning(rp.c, source_loc, format, args);
|
2019-05-10 05:23:22 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-05-09 02:04:51 +00:00
|
|
|
fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []const u8, args: ...) !void {
|
2019-05-11 03:35:46 +00:00
|
|
|
_ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, c.locStr(loc), args);
|
2019-05-10 05:23:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: ...) !void {
|
|
|
|
// const name = @compileError(msg);
|
|
|
|
const const_tok = try appendToken(c, .Keyword_const, "const");
|
2019-05-11 03:35:46 +00:00
|
|
|
const name_tok = try appendToken(c, .Identifier, name);
|
2019-05-10 05:23:22 +00:00
|
|
|
const eq_tok = try appendToken(c, .Equal, "=");
|
|
|
|
const builtin_tok = try appendToken(c, .Builtin, "@compileError");
|
|
|
|
const lparen_tok = try appendToken(c, .LParen, "(");
|
2019-05-11 03:35:46 +00:00
|
|
|
const msg_tok = try appendTokenFmt(c, .StringLiteral, "\"" ++ format ++ "\"", args);
|
2019-05-10 05:23:22 +00:00
|
|
|
const rparen_tok = try appendToken(c, .RParen, ")");
|
|
|
|
const semi_tok = try appendToken(c, .Semicolon, ";");
|
|
|
|
|
|
|
|
const msg_node = try c.a().create(ast.Node.StringLiteral);
|
|
|
|
msg_node.* = ast.Node.StringLiteral{
|
|
|
|
.base = ast.Node{ .id = ast.Node.Id.StringLiteral },
|
|
|
|
.token = msg_tok,
|
|
|
|
};
|
|
|
|
|
|
|
|
const call_node = try c.a().create(ast.Node.BuiltinCall);
|
|
|
|
call_node.* = ast.Node.BuiltinCall{
|
|
|
|
.base = ast.Node{ .id = ast.Node.Id.BuiltinCall },
|
|
|
|
.builtin_token = builtin_tok,
|
|
|
|
.params = ast.Node.BuiltinCall.ParamList.init(c.a()),
|
|
|
|
.rparen_token = rparen_tok,
|
|
|
|
};
|
|
|
|
try call_node.params.push(&msg_node.base);
|
|
|
|
|
|
|
|
const var_decl_node = try c.a().create(ast.Node.VarDecl);
|
|
|
|
var_decl_node.* = ast.Node.VarDecl{
|
|
|
|
.base = ast.Node{ .id = ast.Node.Id.VarDecl },
|
|
|
|
.doc_comments = null,
|
|
|
|
.visib_token = null,
|
|
|
|
.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 = &call_node.base,
|
|
|
|
.semicolon_token = semi_tok,
|
|
|
|
};
|
|
|
|
try c.tree.root_node.decls.push(&var_decl_node.base);
|
2019-05-09 00:49:07 +00:00
|
|
|
}
|
|
|
|
|
2019-05-11 03:35:46 +00:00
|
|
|
fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex {
|
|
|
|
return appendTokenFmt(c, token_id, "{}", bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: ...) !ast.TokenIndex {
|
2019-05-09 00:49:07 +00:00
|
|
|
const S = struct {
|
2019-05-11 03:35:46 +00:00
|
|
|
fn callback(context: *Context, bytes: []const u8) error{OutOfMemory}!void {
|
2019-05-09 00:49:07 +00:00
|
|
|
return context.source_buffer.append(bytes);
|
|
|
|
}
|
|
|
|
};
|
2019-04-30 04:21:45 +00:00
|
|
|
const start_index = c.source_buffer.len();
|
2019-05-09 00:49:07 +00:00
|
|
|
errdefer c.source_buffer.shrink(start_index);
|
|
|
|
|
2019-05-11 03:35:46 +00:00
|
|
|
try std.fmt.format(c, error{OutOfMemory}, S.callback, format, args);
|
2019-04-30 04:21:45 +00:00
|
|
|
const end_index = c.source_buffer.len();
|
2019-05-10 05:23:22 +00:00
|
|
|
const token_index = c.tree.tokens.len;
|
2019-04-30 04:21:45 +00:00
|
|
|
const new_token = try c.tree.tokens.addOne();
|
2019-05-10 05:23:22 +00:00
|
|
|
errdefer c.tree.tokens.shrink(token_index);
|
2019-05-09 00:49:07 +00:00
|
|
|
|
2019-04-26 19:40:29 +00:00
|
|
|
new_token.* = Token{
|
|
|
|
.id = token_id,
|
|
|
|
.start = start_index,
|
|
|
|
.end = end_index,
|
|
|
|
};
|
2019-04-30 04:21:45 +00:00
|
|
|
try c.source_buffer.appendByte('\n');
|
2019-05-10 05:23:22 +00:00
|
|
|
|
|
|
|
return token_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn appendIdentifier(c: *Context, name: []const u8) !*ast.Node {
|
2019-05-11 03:35:46 +00:00
|
|
|
const token_index = try appendToken(c, .Identifier, name);
|
2019-05-10 05:23:22 +00:00
|
|
|
const identifier = try c.a().create(ast.Node.Identifier);
|
|
|
|
identifier.* = ast.Node.Identifier{
|
|
|
|
.base = ast.Node{ .id = ast.Node.Id.Identifier },
|
|
|
|
.token = token_index,
|
|
|
|
};
|
|
|
|
return &identifier.base;
|
stage1 is now a hybrid of C++ and Zig
This modifies the build process of Zig to put all of the source files
into libcompiler.a, except main.cpp and userland.cpp.
Next, the build process links main.cpp, userland.cpp, and libcompiler.a
into zig1. userland.cpp is a shim for functions that will later be
replaced with self-hosted implementations.
Next, the build process uses zig1 to build src-self-hosted/stage1.zig
into libuserland.a, which does not depend on any of the things that
are shimmed in userland.cpp, such as translate-c.
Finally, the build process re-links main.cpp and libcompiler.a, except
with libuserland.a instead of userland.cpp. Now the shims are replaced
with .zig code. This provides all of the Zig standard library to the
stage1 C++ compiler, and enables us to move certain things to userland,
such as translate-c.
As a proof of concept I have made the `zig zen` command use text defined
in userland. I added `zig translate-c-2` which is a work-in-progress
reimplementation of translate-c in userland, which currently calls
`std.debug.panic("unimplemented")` and you can see the stack trace makes
it all the way back into the C++ main() function (Thanks LemonBoy for
improving that!).
This could potentially let us move other things into userland, such as
hashing algorithms, the entire cache system, .d file parsing, pretty
much anything that libuserland.a itself doesn't need to depend on.
This can also let us have `zig fmt` in stage1 without the overhead
of child process execution, and without the initial compilation delay
before it gets cached.
See #1964
2019-04-16 20:47:47 +00:00
|
|
|
}
|
2019-04-21 23:37:39 +00:00
|
|
|
|
|
|
|
pub fn freeErrors(errors: []ClangErrMsg) void {
|
|
|
|
ZigClangErrorMsg_delete(errors.ptr, errors.len);
|
|
|
|
}
|