authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2019-12-15 23:46:35+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2019-12-15 23:46:35+02:00
log9f0e83a5710a85273f12b7e9ecf68c93e0f763e8
tree3255d8973cafadcc5a5e3180b8b028bd6362488f
parented2a19dcecf5470ae2529e0fc7f86bf89d532162
signature Commit is signed but in an unrecognized format.

translate-c-2 macro functions


3 files changed, 162 insertions(+), 43 deletions(-)

src-self-hosted/c_tokenizer.zig+15-1
...@@ -26,6 +26,7 @@ pub const CToken = struct {...@@ -26,6 +26,7 @@ pub const CToken = struct {
26 Shl,26 Shl,
27 Lt,27 Lt,
28 Comma,28 Comma,
29 Fn,
29 };30 };
3031
31 pub const NumLitSuffix = enum {32 pub const NumLitSuffix = enum {
...@@ -41,11 +42,22 @@ pub const CToken = struct {...@@ -41,11 +42,22 @@ pub const CToken = struct {
4142
42pub fn tokenizeCMacro(tl: *TokenList, chars: [*]const u8) !void {43pub fn tokenizeCMacro(tl: *TokenList, chars: [*]const u8) !void {
43 var index: usize = 0;44 var index: usize = 0;
45 var first = true;
44 while (true) {46 while (true) {
45 const tok = try next(chars, &index);47 const tok = try next(chars, &index);
46 try tl.push(tok);48 try tl.push(tok);
47 if (tok.id == .Eof)49 if (tok.id == .Eof)
48 return;50 return;
51 if (first) {
52 // distinguish NAME (EXPR) from NAME(ARGS)
53 first = false;
54 if (chars[index] == '(') {
55 try tl.push(.{
56 .id = .Fn,
57 .bytes = "",
58 });
59 }
60 }
49 }61 }
50}62}
5163
...@@ -515,10 +527,12 @@ test "tokenize macro" {...@@ -515,10 +527,12 @@ test "tokenize macro" {
515 var tl = TokenList.init(std.heap.page_allocator);527 var tl = TokenList.init(std.heap.page_allocator);
516 defer tl.deinit();528 defer tl.deinit();
517529
518 const src = "TEST 0\n";530 const src = "TEST(0\n";
519 try tokenizeCMacro(&tl, src);531 try tokenizeCMacro(&tl, src);
520 var it = tl.iterator(0);532 var it = tl.iterator(0);
521 expect(it.next().?.id == .Identifier);533 expect(it.next().?.id == .Identifier);
534 expect(it.next().?.id == .Fn);
535 expect(it.next().?.id == .LParen);
522 expect(std.mem.eql(u8, it.next().?.bytes, "0"));536 expect(std.mem.eql(u8, it.next().?.bytes, "0"));
523 expect(it.next().?.id == .Eof);537 expect(it.next().?.id == .Eof);
524 expect(it.next() == null);538 expect(it.next() == null);
src-self-hosted/translate_c.zig+139-42
...@@ -2585,11 +2585,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {...@@ -2585,11 +2585,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
2585 while (it.I != it_end.I) : (it.I += 1) {2585 while (it.I != it_end.I) : (it.I += 1) {
2586 const entity = ZigClangPreprocessingRecord_iterator_deref(it);2586 const entity = ZigClangPreprocessingRecord_iterator_deref(it);
2587 tok_list.shrink(0);2587 tok_list.shrink(0);
2588
2589 switch (ZigClangPreprocessedEntity_getKind(entity)) {2588 switch (ZigClangPreprocessedEntity_getKind(entity)) {
2590 .MacroExpansionKind => {
2591 // TODO
2592 },
2593 .MacroDefinitionKind => {2589 .MacroDefinitionKind => {
2594 const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity);2590 const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity);
2595 const raw_name = ZigClangMacroDefinitionRecord_getName_getNameStart(macro);2591 const raw_name = ZigClangMacroDefinitionRecord_getName_getNameStart(macro);
...@@ -2599,54 +2595,64 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {...@@ -2599,54 +2595,64 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
2599 // if (name_exists_global(c, name)) { // TODO2595 // if (name_exists_global(c, name)) { // TODO
2600 // continue;2596 // continue;
2601 // }2597 // }
2602
2603 const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc);2598 const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc);
2604 try transMacroDefine(c, &tok_list, name, begin_c, begin_loc);2599 ctok.tokenizeCMacro(&tok_list, begin_c) catch |err| switch (err) {
2600 error.OutOfMemory => |e| return e,
2601 else => {
2602 try failDecl(c, begin_loc, name, "unable to tokenize macro definition", .{});
2603 continue;
2604 },
2605 };
2606
2607 var tok_it = tok_list.iterator(0);
2608 const first_tok = tok_it.next().?;
2609 assert(first_tok.id == .Identifier and std.mem.eql(u8, first_tok.bytes, name));
2610 const next = tok_it.peek().?;
2611 switch (next.id) {
2612 .Identifier => {
2613 // if it equals itself, ignore. for example, from stdio.h:
2614 // #define stdin stdin
2615 if (std.mem.eql(u8, name, next.bytes)) {
2616 continue;
2617 }
2618 },
2619 .Eof => {
2620 // this means it is a macro without a value
2621 // we don't care about such things
2622 continue;
2623 },
2624 else => {},
2625 }
2626 const macro_fn = if (tok_it.peek().?.id == .Fn) blk: {
2627 _ = tok_it.next();
2628 break :blk true;
2629 } else false;
2630
2631 (if (macro_fn)
2632 transMacroFnDefine(c, &tok_it, name, begin_c, begin_loc)
2633 else
2634 transMacroDefine(c, &tok_it, name, begin_c, begin_loc)) catch |err| switch (err) {
2635 error.UnsupportedTranslation,
2636 error.ParseError,
2637 => try failDecl(c, begin_loc, name, "unable to translate macro", .{}),
2638 error.OutOfMemory => |e| return e,
2639 };
2605 },2640 },
2606 else => {},2641 else => {},
2607 }2642 }
2608 }2643 }
2609}2644}
26102645
2611fn transMacroDefine(c: *Context, tok_list: *ctok.TokenList, name: []const u8, char_ptr: [*]const u8, source_loc: ZigClangSourceLocation) Error!void {2646fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, char_ptr: [*]const u8, source_loc: ZigClangSourceLocation) ParseError!void {
2612 ctok.tokenizeCMacro(tok_list, char_ptr) catch |err| switch (err) {
2613 error.OutOfMemory => |e| return e,
2614 else => return failDecl(c, source_loc, name, "unable to tokenize macro definition", .{}),
2615 };
2616 const rp = makeRestorePoint(c);2647 const rp = makeRestorePoint(c);
26172648
2618 var it = tok_list.iterator(0);
2619 const first_tok = it.next().?;
2620 assert(first_tok.id == .Identifier and std.mem.eql(u8, first_tok.bytes, name));
2621 const next = it.peek().?;
2622 switch (next.id) {
2623 .Identifier => {
2624 // if it equals itself, ignore. for example, from stdio.h:
2625 // #define stdin stdin
2626 if (std.mem.eql(u8, name, next.bytes)) {
2627 return;
2628 }
2629 },
2630 .Eof => {
2631 // this means it is a macro without a value
2632 // we don't care about such things
2633 return;
2634 },
2635 else => {},
2636 }
2637
2638 const visib_tok = try appendToken(c, .Keyword_pub, "pub");2649 const visib_tok = try appendToken(c, .Keyword_pub, "pub");
2639 const mut_tok = try appendToken(c, .Keyword_const, "const");2650 const mut_tok = try appendToken(c, .Keyword_const, "const");
2640 const name_tok = try appendIdentifier(c, name);2651 const name_tok = try appendIdentifier(c, name);
26412652
2642 const eq_tok = try appendToken(c, .Equal, "=");2653 const eq_tok = try appendToken(c, .Equal, "=");
26432654
2644 const init_node = parseCExpr(rp, &it, source_loc) catch |err| switch (err) {2655 const init_node = try parseCExpr(rp, it, source_loc);
2645 error.UnsupportedTranslation,
2646 error.ParseError,
2647 => return failDecl(c, source_loc, name, "unable to translate macro", .{}),
2648 error.OutOfMemory => |e| return e,
2649 };
26502656
2651 const node = try c.a().create(ast.Node.VarDecl);2657 const node = try c.a().create(ast.Node.VarDecl);
2652 node.* = ast.Node.VarDecl{2658 node.* = ast.Node.VarDecl{
...@@ -2668,6 +2674,97 @@ fn transMacroDefine(c: *Context, tok_list: *ctok.TokenList, name: []const u8, ch...@@ -2668,6 +2674,97 @@ fn transMacroDefine(c: *Context, tok_list: *ctok.TokenList, name: []const u8, ch
2668 _ = try c.macro_table.put(name, &node.base);2674 _ = try c.macro_table.put(name, &node.base);
2669}2675}
26702676
2677fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, char_ptr: [*]const u8, source_loc: ZigClangSourceLocation) ParseError!void {
2678 const rp = makeRestorePoint(c);
2679 const pub_tok = try appendToken(c, .Keyword_pub, "pub");
2680 const inline_tok = try appendToken(c, .Keyword_inline, "inline");
2681 const fn_tok = try appendToken(c, .Keyword_fn, "fn");
2682 const name_tok = try appendIdentifier(c, name);
2683 _ = try appendToken(c, .LParen, "(");
2684
2685 if (it.next().?.id != .LParen) {
2686 return error.ParseError;
2687 }
2688 var fn_params = ast.Node.FnProto.ParamList.init(c.a());
2689 while (true) {
2690 const param_tok = it.next().?;
2691 if (param_tok.id != .Identifier)
2692 return error.ParseError;
2693
2694 // TODO avoid name collisions
2695 const param_name_tok = try appendIdentifier(c, param_tok.bytes);
2696 _ = try appendToken(c, .Colon, ":");
2697
2698 const token_index = try appendToken(c, .Keyword_var, "var");
2699 const identifier = try c.a().create(ast.Node.Identifier);
2700 identifier.* = ast.Node.Identifier{
2701 .base = ast.Node{ .id = ast.Node.Id.Identifier },
2702 .token = token_index,
2703 };
2704
2705 const param_node = try c.a().create(ast.Node.ParamDecl);
2706 param_node.* = .{
2707 .doc_comments = null,
2708 .comptime_token = null,
2709 .noalias_token = null,
2710 .name_token = param_name_tok,
2711 .type_node = &identifier.base,
2712 .var_args_token = null,
2713 };
2714 try fn_params.push(&param_node.base);
2715
2716 if (it.peek().?.id != .Comma)
2717 break;
2718 _ = it.next();
2719 _ = try appendToken(c, .Comma, ",");
2720 }
2721
2722 if (it.next().?.id != .RParen) {
2723 return error.ParseError;
2724 }
2725
2726 _ = try appendToken(c, .RParen, ")");
2727
2728 const type_of = try transCreateNodeBuiltinFnCall(c, "@TypeOf");
2729 type_of.rparen_token = try appendToken(c, .LParen, ")");
2730
2731 const fn_proto = try c.a().create(ast.Node.FnProto);
2732 fn_proto.* = .{
2733 .visib_token = pub_tok,
2734 .extern_export_inline_token = inline_tok,
2735 .fn_token = fn_tok,
2736 .name_token = name_tok,
2737 .params = fn_params,
2738 .return_type = .{ .Explicit = &type_of.base },
2739 .doc_comments = null,
2740 .var_args_token = null,
2741 .cc_token = null,
2742 .body_node = null,
2743 .lib_name = null,
2744 .align_expr = null,
2745 .section_expr = null,
2746 };
2747
2748 const block = try c.a().create(ast.Node.Block);
2749 block.* = .{
2750 .label = null,
2751 .lbrace = try appendToken(c, .LBrace, "{"),
2752 .statements = ast.Node.Block.StatementList.init(c.a()),
2753 .rbrace = undefined,
2754 };
2755
2756 const return_expr = try transCreateNodeReturnExpr(c);
2757 const expr = try parseCExpr(rp, it, source_loc);
2758 _ = try appendToken(c, .Semicolon, ";");
2759 try type_of.params.push(expr);
2760 return_expr.rhs = expr;
2761
2762 block.rbrace = try appendToken(c, .RBrace, "}");
2763 try block.statements.push(&return_expr.base);
2764 fn_proto.body_node = &block.base;
2765 _ = try c.macro_table.put(name, &fn_proto.base);
2766}
2767
2671const ParseError = Error || error{2768const ParseError = Error || error{
2672 ParseError,2769 ParseError,
2673 UnsupportedTranslation,2770 UnsupportedTranslation,
...@@ -2762,14 +2859,14 @@ fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc:...@@ -2762,14 +2859,14 @@ fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc:
2762 .LParen => {2859 .LParen => {
2763 const inner_node = try parseCExpr(rp, it, source_loc);2860 const inner_node = try parseCExpr(rp, it, source_loc);
27642861
2765 // hack to get zig fmt to render a comma in builtin calls
2766 _ = try appendToken(rp.c, .Comma, ",");
2767
2768 if (it.peek().?.id == .RParen) {2862 if (it.peek().?.id == .RParen) {
2769 _ = it.next();2863 _ = it.next();
2770 return inner_node;2864 return inner_node;
2771 }2865 }
27722866
2867 // hack to get zig fmt to render a comma in builtin calls
2868 _ = try appendToken(rp.c, .Comma, ",");
2869
2773 const node_to_cast = try parseCExpr(rp, it, source_loc);2870 const node_to_cast = try parseCExpr(rp, it, source_loc);
27742871
2775 if (it.next().?.id != .RParen) {2872 if (it.next().?.id != .RParen) {
...@@ -2879,7 +2976,7 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc...@@ -2879,7 +2976,7 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
2879 );2976 );
28802977
2881 const op_token = try appendToken(rp.c, .Period, ".");2978 const op_token = try appendToken(rp.c, .Period, ".");
2882 const rhs = try transCreateNodeIdentifier(rp.c, tok.bytes);2979 const rhs = try transCreateNodeIdentifier(rp.c, name_tok.bytes);
2883 const access_node = try rp.c.a().create(ast.Node.InfixOp);2980 const access_node = try rp.c.a().create(ast.Node.InfixOp);
2884 access_node.* = .{2981 access_node.* = .{
2885 .op_token = op_token,2982 .op_token = op_token,
...@@ -2975,7 +3072,7 @@ fn tokenSlice(c: *Context, token: ast.TokenIndex) []const u8 {...@@ -2975,7 +3072,7 @@ fn tokenSlice(c: *Context, token: ast.TokenIndex) []const u8 {
2975}3072}
29763073
2977fn getFnDecl(c: *Context, ref: *ast.Node) ?*ast.Node {3074fn getFnDecl(c: *Context, ref: *ast.Node) ?*ast.Node {
2978 const init = ref.cast(ast.Node.VarDecl).?.init_node.?;3075 const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null;
2979 const name = if (init.cast(ast.Node.Identifier)) |id|3076 const name = if (init.cast(ast.Node.Identifier)) |id|
2980 tokenSlice(c, id.token)3077 tokenSlice(c, id.token)
2981 else3078 else
test/translate_c.zig+8
...@@ -403,6 +403,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -403,6 +403,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
403 \\pub const NRF_GPIO = if (@typeId(@TypeOf(NRF_GPIO_BASE)) == .Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@TypeOf(NRF_GPIO_BASE)) == .Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE);403 \\pub const NRF_GPIO = if (@typeId(@TypeOf(NRF_GPIO_BASE)) == .Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@TypeOf(NRF_GPIO_BASE)) == .Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE);
404 });404 });
405405
406 cases.add_2("basic macro function",
407 \\#define BASIC(c) (c*2)
408 , &[_][]const u8{
409 \\pub inline fn BASIC(c: var) @TypeOf(c * 2) {
410 \\ return c * 2;
411 \\}
412 });
413
406 /////////////// Cases for only stage1 which are TODO items for stage2 ////////////////414 /////////////// Cases for only stage1 which are TODO items for stage2 ////////////////
407415
408 cases.add_both("typedef of function in struct field",416 cases.add_both("typedef of function in struct field",