| ... | ... | @@ -6,6 +6,8 @@ const assert = std.debug.assert; |
| 6 | 6 | const ast = std.zig.ast; |
| 7 | 7 | const Token = std.zig.Token; |
| 8 | 8 | usingnamespace @import("clang.zig"); |
| 9 | const ctok = @import("c_tokenizer.zig"); |
| 10 | const CToken = ctok.CToken; |
| 9 | 11 | |
| 10 | 12 | const CallingConvention = std.builtin.TypeInfo.CallingConvention; |
| 11 | 13 | |
| ... | ... | @@ -31,7 +33,7 @@ fn addrEql(a: usize, b: usize) bool { |
| 31 | 33 | return a == b; |
| 32 | 34 | } |
| 33 | 35 | |
| 34 | | const SymbolTable = std.StringHashMap(void); |
| 36 | const SymbolTable = std.StringHashMap(*ast.Node); |
| 35 | 37 | const AliasList = std.SegmentedList(struct { |
| 36 | 38 | alias: []const u8, |
| 37 | 39 | name: []const u8, |
| ... | ... | @@ -43,59 +45,151 @@ const Scope = struct { |
| 43 | 45 | |
| 44 | 46 | const Id = enum { |
| 45 | 47 | Switch, |
| 46 | | Var, |
| 47 | 48 | Block, |
| 48 | 49 | Root, |
| 49 | 50 | While, |
| 51 | FnDef, |
| 52 | Ref, |
| 50 | 53 | }; |
| 54 | |
| 51 | 55 | const Switch = struct { |
| 52 | 56 | base: Scope, |
| 53 | 57 | }; |
| 54 | 58 | |
| 55 | | const Var = struct { |
| 59 | /// used when getting a member `a.b` |
| 60 | const Ref = struct { |
| 56 | 61 | base: Scope, |
| 57 | | c_name: []const u8, |
| 58 | | zig_name: []const u8, |
| 59 | 62 | }; |
| 60 | 63 | |
| 61 | 64 | const Block = struct { |
| 62 | 65 | base: Scope, |
| 63 | 66 | block_node: *ast.Node.Block, |
| 67 | variables: AliasList, |
| 64 | 68 | |
| 65 | 69 | /// Don't forget to set rbrace token later |
| 66 | | fn create(c: *Context, parent: *Scope, lbrace_tok: ast.TokenIndex) !*Block { |
| 70 | fn init(c: *Context, parent: *Scope, block_node: *ast.Node.Block) !*Block { |
| 67 | 71 | const block = try c.a().create(Block); |
| 68 | | block.* = Block{ |
| 69 | | .base = Scope{ |
| 70 | | .id = Id.Block, |
| 72 | block.* = .{ |
| 73 | .base = .{ |
| 74 | .id = .Block, |
| 71 | 75 | .parent = parent, |
| 72 | 76 | }, |
| 73 | | .block_node = try c.a().create(ast.Node.Block), |
| 74 | | }; |
| 75 | | block.block_node.* = ast.Node.Block{ |
| 76 | | .base = ast.Node{ .id = ast.Node.Id.Block }, |
| 77 | | .label = null, |
| 78 | | .lbrace = lbrace_tok, |
| 79 | | .statements = ast.Node.Block.StatementList.init(c.a()), |
| 80 | | .rbrace = undefined, |
| 77 | .block_node = block_node, |
| 78 | .variables = AliasList.init(c.a()), |
| 81 | 79 | }; |
| 82 | 80 | return block; |
| 83 | 81 | } |
| 82 | |
| 83 | fn getAlias(scope: *Block, name: []const u8) ?[]const u8 { |
| 84 | var it = scope.variables.iterator(0); |
| 85 | while (it.next()) |p| { |
| 86 | if (std.mem.eql(u8, p.name, name)) |
| 87 | return p.alias; |
| 88 | } |
| 89 | return scope.base.parent.?.getAlias(name); |
| 90 | } |
| 91 | |
| 92 | fn contains(scope: *Block, name: []const u8) bool { |
| 93 | var it = scope.variables.iterator(0); |
| 94 | while (it.next()) |p| { |
| 95 | if (std.mem.eql(u8, p.name, name)) |
| 96 | return true; |
| 97 | } |
| 98 | return scope.base.parent.?.contains(name); |
| 99 | } |
| 84 | 100 | }; |
| 85 | 101 | |
| 86 | 102 | const Root = struct { |
| 87 | 103 | base: Scope, |
| 104 | sym_table: SymbolTable, |
| 105 | macro_table: SymbolTable, |
| 106 | |
| 107 | fn init(c: *Context) Root { |
| 108 | return .{ |
| 109 | .base = .{ |
| 110 | .id = .Root, |
| 111 | .parent = null, |
| 112 | }, |
| 113 | .sym_table = SymbolTable.init(c.a()), |
| 114 | .macro_table = SymbolTable.init(c.a()), |
| 115 | }; |
| 116 | } |
| 117 | |
| 118 | fn contains(scope: *Root, name: []const u8) bool { |
| 119 | return scope.sym_table.contains(name) or scope.macro_table.contains(name); |
| 120 | } |
| 88 | 121 | }; |
| 89 | 122 | |
| 90 | 123 | const While = struct { |
| 91 | 124 | base: Scope, |
| 92 | 125 | }; |
| 93 | | }; |
| 94 | 126 | |
| 95 | | const TransResult = struct { |
| 96 | | node: *ast.Node, |
| 97 | | node_scope: *Scope, |
| 98 | | child_scope: *Scope, |
| 127 | const FnDef = struct { |
| 128 | base: Scope, |
| 129 | params: AliasList, |
| 130 | |
| 131 | fn init(c: *Context) FnDef { |
| 132 | return .{ |
| 133 | .base = .{ |
| 134 | .id = .FnDef, |
| 135 | .parent = &c.global_scope.base, |
| 136 | }, |
| 137 | .params = AliasList.init(c.a()), |
| 138 | }; |
| 139 | } |
| 140 | |
| 141 | fn getAlias(scope: *FnDef, name: []const u8) ?[]const u8 { |
| 142 | var it = scope.params.iterator(0); |
| 143 | while (it.next()) |p| { |
| 144 | if (std.mem.eql(u8, p.name, name)) |
| 145 | return p.alias; |
| 146 | } |
| 147 | return scope.base.parent.?.getAlias(name); |
| 148 | } |
| 149 | |
| 150 | fn contains(scope: *FnDef, name: []const u8) bool { |
| 151 | var it = scope.params.iterator(0); |
| 152 | while (it.next()) |p| { |
| 153 | if (std.mem.eql(u8, p.name, name)) |
| 154 | return true; |
| 155 | } |
| 156 | return scope.base.parent.?.contains(name); |
| 157 | } |
| 158 | }; |
| 159 | |
| 160 | fn findBlockScope(inner: *Scope) *Scope.Block { |
| 161 | var scope = inner; |
| 162 | while (true) : (scope = scope.parent orelse unreachable) { |
| 163 | if (scope.id == .Block) return @fieldParentPtr(Scope.Block, "base", scope); |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | fn createAlias(scope: *Scope, c: *Context, name: []const u8) !?[]const u8 { |
| 168 | if (scope.contains(name)) { |
| 169 | return try std.fmt.allocPrint(c.a(), "{}_{}", .{ name, c.getMangle() }); |
| 170 | } |
| 171 | return null; |
| 172 | } |
| 173 | |
| 174 | fn getAlias(scope: *Scope, name: []const u8) ?[]const u8 { |
| 175 | return switch (scope.id) { |
| 176 | .Root => null, |
| 177 | .Ref => null, |
| 178 | .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name), |
| 179 | .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), |
| 180 | else => @panic("TODO Scope.getAlias"), |
| 181 | }; |
| 182 | } |
| 183 | |
| 184 | fn contains(scope: *Scope, name: []const u8) bool { |
| 185 | return switch (scope.id) { |
| 186 | .Ref => false, |
| 187 | .Root => @fieldParentPtr(Root, "base", scope).contains(name), |
| 188 | .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name), |
| 189 | .Block => @fieldParentPtr(Block, "base", scope).contains(name), |
| 190 | else => @panic("TODO Scope.contains"), |
| 191 | }; |
| 192 | } |
| 99 | 193 | }; |
| 100 | 194 | |
| 101 | 195 | const Context = struct { |
| ... | ... | @@ -105,7 +199,6 @@ const Context = struct { |
| 105 | 199 | source_manager: *ZigClangSourceManager, |
| 106 | 200 | decl_table: DeclTable, |
| 107 | 201 | alias_list: AliasList, |
| 108 | | sym_table: SymbolTable, |
| 109 | 202 | global_scope: *Scope.Root, |
| 110 | 203 | ptr_params: std.BufSet, |
| 111 | 204 | clang_context: *ZigClangASTContext, |
| ... | ... | @@ -142,7 +235,7 @@ pub fn translate( |
| 142 | 235 | args_begin: [*]?[*]const u8, |
| 143 | 236 | args_end: [*]?[*]const u8, |
| 144 | 237 | errors: *[]ClangErrMsg, |
| 145 | | resources_path: [*]const u8, |
| 238 | resources_path: [*:0]const u8, |
| 146 | 239 | ) !*ast.Tree { |
| 147 | 240 | const ast_unit = ZigClangLoadFromCommandLine( |
| 148 | 241 | args_begin, |
| ... | ... | @@ -192,24 +285,22 @@ pub fn translate( |
| 192 | 285 | .err = undefined, |
| 193 | 286 | .decl_table = DeclTable.init(arena), |
| 194 | 287 | .alias_list = AliasList.init(arena), |
| 195 | | .sym_table = SymbolTable.init(arena), |
| 196 | 288 | .global_scope = try arena.create(Scope.Root), |
| 197 | 289 | .ptr_params = std.BufSet.init(arena), |
| 198 | 290 | .clang_context = ZigClangASTUnit_getASTContext(ast_unit).?, |
| 199 | 291 | }; |
| 200 | | context.global_scope.* = Scope.Root{ |
| 201 | | .base = Scope{ |
| 202 | | .id = Scope.Id.Root, |
| 203 | | .parent = null, |
| 204 | | }, |
| 205 | | }; |
| 292 | context.global_scope.* = Scope.Root.init(&context); |
| 206 | 293 | |
| 207 | 294 | if (!ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, &context, declVisitorC)) { |
| 208 | 295 | return context.err; |
| 209 | 296 | } |
| 297 | |
| 298 | try transPreprocessorEntities(&context, ast_unit); |
| 299 | |
| 300 | try addMacros(&context); |
| 210 | 301 | var it = context.alias_list.iterator(0); |
| 211 | 302 | while (it.next()) |alias| { |
| 212 | | if (!context.sym_table.contains(alias.alias)) { |
| 303 | if (!context.global_scope.sym_table.contains(alias.alias)) { |
| 213 | 304 | try createAlias(&context, alias); |
| 214 | 305 | } |
| 215 | 306 | } |
| ... | ... | @@ -268,7 +359,8 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { |
| 268 | 359 | const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl); |
| 269 | 360 | const fn_qt = ZigClangFunctionDecl_getType(fn_decl); |
| 270 | 361 | const fn_type = ZigClangQualType_getTypePtr(fn_qt); |
| 271 | | var scope = &c.global_scope.base; |
| 362 | var fndef_scope = Scope.FnDef.init(c); |
| 363 | var scope = &fndef_scope.base; |
| 272 | 364 | const has_body = ZigClangFunctionDecl_hasBody(fn_decl); |
| 273 | 365 | const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl); |
| 274 | 366 | const decl_ctx = FnDeclContext{ |
| ... | ... | @@ -314,14 +406,14 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { |
| 314 | 406 | |
| 315 | 407 | // actual function definition with body |
| 316 | 408 | const body_stmt = ZigClangFunctionDecl_getBody(fn_decl); |
| 317 | | const result = transStmt(rp, scope, body_stmt, .unused, .r_value) catch |err| switch (err) { |
| 409 | const body_node = transStmt(rp, scope, body_stmt, .unused, .r_value) catch |err| switch (err) { |
| 318 | 410 | error.OutOfMemory => |e| return e, |
| 319 | 411 | error.UnsupportedTranslation, |
| 320 | 412 | error.UnsupportedType, |
| 321 | 413 | => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}), |
| 322 | 414 | }; |
| 323 | | assert(result.node.id == ast.Node.Id.Block); |
| 324 | | proto_node.body_node = result.node; |
| 415 | assert(body_node.id == .Block); |
| 416 | proto_node.body_node = body_node; |
| 325 | 417 | |
| 326 | 418 | return addTopLevelDecl(c, fn_name, &proto_node.base); |
| 327 | 419 | } |
| ... | ... | @@ -336,7 +428,7 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { |
| 336 | 428 | else |
| 337 | 429 | try appendToken(c, .Keyword_threadlocal, "threadlocal"); |
| 338 | 430 | |
| 339 | | var scope = &c.global_scope.base; |
| 431 | const scope = &c.global_scope.base; |
| 340 | 432 | const var_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, var_decl))); |
| 341 | 433 | _ = try c.decl_table.put(@ptrToInt(var_decl), var_name); |
| 342 | 434 | const var_decl_loc = ZigClangVarDecl_getLocation(var_decl); |
| ... | ... | @@ -372,17 +464,16 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { |
| 372 | 464 | |
| 373 | 465 | if (ZigClangVarDecl_hasInit(var_decl)) { |
| 374 | 466 | eq_tok = try appendToken(c, .Equal, "="); |
| 375 | | init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| blk: { |
| 376 | | var res = transExpr(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) { |
| 467 | init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| |
| 468 | transExpr(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) { |
| 377 | 469 | error.UnsupportedTranslation, |
| 378 | 470 | error.UnsupportedType, |
| 379 | 471 | => { |
| 380 | 472 | return failDecl(c, var_decl_loc, var_name, "unable to translate initializer", .{}); |
| 381 | 473 | }, |
| 382 | 474 | error.OutOfMemory => |e| return e, |
| 383 | | }; |
| 384 | | break :blk res.node; |
| 385 | | } else |
| 475 | } |
| 476 | else |
| 386 | 477 | try transCreateNodeUndefinedLiteral(c); |
| 387 | 478 | } else if (storage_class != .Extern) { |
| 388 | 479 | return failDecl(c, var_decl_loc, var_name, "non-extern variable has no initializer", .{}); |
| ... | ... | @@ -548,7 +639,7 @@ fn transStmt( |
| 548 | 639 | stmt: *const ZigClangStmt, |
| 549 | 640 | result_used: ResultUsed, |
| 550 | 641 | lrvalue: LRValue, |
| 551 | | ) TransError!TransResult { |
| 642 | ) TransError!*ast.Node { |
| 552 | 643 | const sc = ZigClangStmt_getStmtClass(stmt); |
| 553 | 644 | switch (sc) { |
| 554 | 645 | .BinaryOperatorClass => return transBinaryOperator(rp, scope, @ptrCast(*const ZigClangBinaryOperator, stmt), result_used), |
| ... | ... | @@ -580,7 +671,7 @@ fn transBinaryOperator( |
| 580 | 671 | scope: *Scope, |
| 581 | 672 | stmt: *const ZigClangBinaryOperator, |
| 582 | 673 | result_used: ResultUsed, |
| 583 | | ) TransError!TransResult { |
| 674 | ) TransError!*ast.Node { |
| 584 | 675 | const op = ZigClangBinaryOperator_getOpcode(stmt); |
| 585 | 676 | const qt = ZigClangBinaryOperator_getType(stmt); |
| 586 | 677 | switch (op) { |
| ... | ... | @@ -591,67 +682,43 @@ fn transBinaryOperator( |
| 591 | 682 | "TODO: handle more C binary operators: {}", |
| 592 | 683 | .{op}, |
| 593 | 684 | ), |
| 594 | | .Assign => return TransResult{ |
| 595 | | .node = &(try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt))).base, |
| 596 | | .child_scope = scope, |
| 597 | | .node_scope = scope, |
| 598 | | }, |
| 685 | .Assign => return &(try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt))).base, |
| 599 | 686 | .Add => { |
| 600 | 687 | const node = if (cIsUnsignedInteger(qt)) |
| 601 | 688 | try transCreateNodeInfixOp(rp, scope, stmt, .AddWrap, .PlusPercent, "+%", true) |
| 602 | 689 | else |
| 603 | 690 | try transCreateNodeInfixOp(rp, scope, stmt, .Add, .Plus, "+", true); |
| 604 | | return maybeSuppressResult(rp, scope, result_used, TransResult{ |
| 605 | | .node = node, |
| 606 | | .child_scope = scope, |
| 607 | | .node_scope = scope, |
| 608 | | }); |
| 691 | return maybeSuppressResult(rp, scope, result_used, node); |
| 609 | 692 | }, |
| 610 | 693 | .Sub => { |
| 611 | 694 | const node = if (cIsUnsignedInteger(qt)) |
| 612 | 695 | try transCreateNodeInfixOp(rp, scope, stmt, .SubWrap, .MinusPercent, "-%", true) |
| 613 | 696 | else |
| 614 | 697 | try transCreateNodeInfixOp(rp, scope, stmt, .Sub, .Minus, "-", true); |
| 615 | | return maybeSuppressResult(rp, scope, result_used, TransResult{ |
| 616 | | .node = node, |
| 617 | | .child_scope = scope, |
| 618 | | .node_scope = scope, |
| 619 | | }); |
| 698 | return maybeSuppressResult(rp, scope, result_used, node); |
| 620 | 699 | }, |
| 621 | 700 | .Mul => { |
| 622 | 701 | const node = if (cIsUnsignedInteger(qt)) |
| 623 | 702 | try transCreateNodeInfixOp(rp, scope, stmt, .MultWrap, .AsteriskPercent, "*%", true) |
| 624 | 703 | else |
| 625 | 704 | try transCreateNodeInfixOp(rp, scope, stmt, .Mult, .Asterisk, "*", true); |
| 626 | | return maybeSuppressResult(rp, scope, result_used, TransResult{ |
| 627 | | .node = node, |
| 628 | | .child_scope = scope, |
| 629 | | .node_scope = scope, |
| 630 | | }); |
| 705 | return maybeSuppressResult(rp, scope, result_used, node); |
| 631 | 706 | }, |
| 632 | 707 | .Div => { |
| 633 | 708 | if (!cIsUnsignedInteger(qt)) { |
| 634 | 709 | // signed integer division uses @divTrunc |
| 635 | 710 | const div_trunc_node = try transCreateNodeBuiltinFnCall(rp.c, "@divTrunc"); |
| 636 | 711 | const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value); |
| 637 | | try div_trunc_node.params.push(lhs.node); |
| 712 | try div_trunc_node.params.push(lhs); |
| 638 | 713 | _ = try appendToken(rp.c, .Comma, ","); |
| 639 | 714 | const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); |
| 640 | | try div_trunc_node.params.push(rhs.node); |
| 715 | try div_trunc_node.params.push(rhs); |
| 641 | 716 | div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")"); |
| 642 | | return maybeSuppressResult(rp, scope, result_used, TransResult{ |
| 643 | | .node = &div_trunc_node.base, |
| 644 | | .child_scope = scope, |
| 645 | | .node_scope = scope, |
| 646 | | }); |
| 717 | return maybeSuppressResult(rp, scope, result_used, &div_trunc_node.base); |
| 647 | 718 | } else { |
| 648 | 719 | // unsigned/float division uses the operator |
| 649 | 720 | const node = try transCreateNodeInfixOp(rp, scope, stmt, .Div, .Slash, "/", true); |
| 650 | | return maybeSuppressResult(rp, scope, result_used, TransResult{ |
| 651 | | .node = node, |
| 652 | | .child_scope = scope, |
| 653 | | .node_scope = scope, |
| 654 | | }); |
| 721 | return maybeSuppressResult(rp, scope, result_used, node); |
| 655 | 722 | } |
| 656 | 723 | }, |
| 657 | 724 | .Rem => { |
| ... | ... | @@ -659,24 +726,16 @@ fn transBinaryOperator( |
| 659 | 726 | // signed integer division uses @rem |
| 660 | 727 | const rem_node = try transCreateNodeBuiltinFnCall(rp.c, "@rem"); |
| 661 | 728 | const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value); |
| 662 | | try rem_node.params.push(lhs.node); |
| 729 | try rem_node.params.push(lhs); |
| 663 | 730 | _ = try appendToken(rp.c, .Comma, ","); |
| 664 | 731 | const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); |
| 665 | | try rem_node.params.push(rhs.node); |
| 732 | try rem_node.params.push(rhs); |
| 666 | 733 | rem_node.rparen_token = try appendToken(rp.c, .RParen, ")"); |
| 667 | | return maybeSuppressResult(rp, scope, result_used, TransResult{ |
| 668 | | .node = &rem_node.base, |
| 669 | | .child_scope = scope, |
| 670 | | .node_scope = scope, |
| 671 | | }); |
| 734 | return maybeSuppressResult(rp, scope, result_used, &rem_node.base); |
| 672 | 735 | } else { |
| 673 | 736 | // unsigned/float division uses the operator |
| 674 | 737 | const node = try transCreateNodeInfixOp(rp, scope, stmt, .Mod, .Percent, "%", true); |
| 675 | | return maybeSuppressResult(rp, scope, result_used, TransResult{ |
| 676 | | .node = node, |
| 677 | | .child_scope = scope, |
| 678 | | .node_scope = scope, |
| 679 | | }); |
| 738 | return maybeSuppressResult(rp, scope, result_used, node); |
| 680 | 739 | } |
| 681 | 740 | }, |
| 682 | 741 | .Shl, |
| ... | ... | @@ -720,33 +779,22 @@ fn transCompoundStmtInline( |
| 720 | 779 | parent_scope: *Scope, |
| 721 | 780 | stmt: *const ZigClangCompoundStmt, |
| 722 | 781 | block_node: *ast.Node.Block, |
| 723 | | ) TransError!TransResult { |
| 782 | ) TransError!void { |
| 724 | 783 | var it = ZigClangCompoundStmt_body_begin(stmt); |
| 725 | 784 | const end_it = ZigClangCompoundStmt_body_end(stmt); |
| 726 | | var scope = parent_scope; |
| 727 | 785 | while (it != end_it) : (it += 1) { |
| 728 | | const result = try transStmt(rp, parent_scope, it.*, .unused, .r_value); |
| 729 | | scope = result.child_scope; |
| 730 | | if (result.node != &block_node.base) |
| 731 | | try block_node.statements.push(result.node); |
| 786 | const result = try transStmt(rp, parent_scope, it[0], .unused, .r_value); |
| 787 | if (result != &block_node.base) |
| 788 | try block_node.statements.push(result); |
| 732 | 789 | } |
| 733 | | return TransResult{ |
| 734 | | .node = &block_node.base, |
| 735 | | .child_scope = scope, |
| 736 | | .node_scope = scope, |
| 737 | | }; |
| 738 | 790 | } |
| 739 | 791 | |
| 740 | | fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) !TransResult { |
| 741 | | const lbrace_tok = try appendToken(rp.c, .LBrace, "{"); |
| 742 | | const block_scope = try Scope.Block.create(rp.c, scope, lbrace_tok); |
| 743 | | const inline_result = try transCompoundStmtInline(rp, &block_scope.base, stmt, block_scope.block_node); |
| 744 | | block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); |
| 745 | | return TransResult{ |
| 746 | | .node = &block_scope.block_node.base, |
| 747 | | .node_scope = inline_result.node_scope, |
| 748 | | .child_scope = inline_result.child_scope, |
| 749 | | }; |
| 792 | fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node { |
| 793 | const block_node = try transCreateNodeBlock(rp.c, null); |
| 794 | const block_scope = try Scope.Block.init(rp.c, scope, block_node); |
| 795 | try transCompoundStmtInline(rp, &block_scope.base, stmt, block_node); |
| 796 | block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); |
| 797 | return &block_node.base; |
| 750 | 798 | } |
| 751 | 799 | |
| 752 | 800 | fn transCStyleCastExprClass( |
| ... | ... | @@ -755,7 +803,7 @@ fn transCStyleCastExprClass( |
| 755 | 803 | stmt: *const ZigClangCStyleCastExpr, |
| 756 | 804 | result_used: ResultUsed, |
| 757 | 805 | lrvalue: LRValue, |
| 758 | | ) !TransResult { |
| 806 | ) TransError!*ast.Node { |
| 759 | 807 | const sub_expr = ZigClangCStyleCastExpr_getSubExpr(stmt); |
| 760 | 808 | const cast_node = (try transCCast( |
| 761 | 809 | rp, |
| ... | ... | @@ -763,27 +811,21 @@ fn transCStyleCastExprClass( |
| 763 | 811 | ZigClangCStyleCastExpr_getBeginLoc(stmt), |
| 764 | 812 | ZigClangCStyleCastExpr_getType(stmt), |
| 765 | 813 | ZigClangExpr_getType(sub_expr), |
| 766 | | (try transExpr(rp, scope, sub_expr, .used, lrvalue)).node, |
| 814 | try transExpr(rp, scope, sub_expr, .used, lrvalue), |
| 767 | 815 | )); |
| 768 | | const cast_res = TransResult{ |
| 769 | | .node = cast_node, |
| 770 | | .child_scope = scope, |
| 771 | | .node_scope = scope, |
| 772 | | }; |
| 773 | | return maybeSuppressResult(rp, scope, result_used, cast_res); |
| 816 | return maybeSuppressResult(rp, scope, result_used, cast_node); |
| 774 | 817 | } |
| 775 | 818 | |
| 776 | | fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDeclStmt) !TransResult { |
| 819 | fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) TransError!*ast.Node { |
| 777 | 820 | const c = rp.c; |
| 778 | | const block_scope = findBlockScope(parent_scope); |
| 779 | | var scope = parent_scope; |
| 821 | const block_scope = scope.findBlockScope(); |
| 780 | 822 | |
| 781 | 823 | var it = ZigClangDeclStmt_decl_begin(stmt); |
| 782 | 824 | const end_it = ZigClangDeclStmt_decl_end(stmt); |
| 783 | 825 | while (it != end_it) : (it += 1) { |
| 784 | | switch (ZigClangDecl_getKind(it.*)) { |
| 826 | switch (ZigClangDecl_getKind(it[0])) { |
| 785 | 827 | .Var => { |
| 786 | | const var_decl = @ptrCast(*const ZigClangVarDecl, it.*); |
| 828 | const var_decl = @ptrCast(*const ZigClangVarDecl, it[0]); |
| 787 | 829 | |
| 788 | 830 | const thread_local_token = if (ZigClangVarDecl_getTLSKind(var_decl) == .None) |
| 789 | 831 | null |
| ... | ... | @@ -794,18 +836,14 @@ fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDe |
| 794 | 836 | try appendToken(c, .Keyword_const, "const") |
| 795 | 837 | else |
| 796 | 838 | try appendToken(c, .Keyword_var, "var"); |
| 797 | | const c_name = try c.str(ZigClangDecl_getName_bytes_begin( |
| 839 | const name = try c.str(ZigClangDecl_getName_bytes_begin( |
| 798 | 840 | @ptrCast(*const ZigClangDecl, var_decl), |
| 799 | 841 | )); |
| 800 | | const name_token = try appendIdentifier(c, c_name); |
| 801 | | |
| 802 | | const var_scope = try c.a().create(Scope.Var); |
| 803 | | var_scope.* = Scope.Var{ |
| 804 | | .base = Scope{ .id = .Var, .parent = scope }, |
| 805 | | .c_name = c_name, |
| 806 | | .zig_name = c_name, // TODO: getWantedName |
| 807 | | }; |
| 808 | | scope = &var_scope.base; |
| 842 | const checked_name = if (try scope.createAlias(c, name)) |a| blk: { |
| 843 | try block_scope.variables.push(.{ .name = name, .alias = a }); |
| 844 | break :blk a; |
| 845 | } else name; |
| 846 | const name_token = try appendIdentifier(c, checked_name); |
| 809 | 847 | |
| 810 | 848 | const colon_token = try appendToken(c, .Colon, ":"); |
| 811 | 849 | const loc = ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)); |
| ... | ... | @@ -813,7 +851,7 @@ fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDe |
| 813 | 851 | |
| 814 | 852 | const eq_token = try appendToken(c, .Equal, "="); |
| 815 | 853 | const init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| |
| 816 | | (try transExpr(rp, scope, expr, .used, .r_value)).node |
| 854 | try transExpr(rp, scope, expr, .used, .r_value) |
| 817 | 855 | else |
| 818 | 856 | try transCreateNodeUndefinedLiteral(c); |
| 819 | 857 | const semicolon_token = try appendToken(c, .Semicolon, ";"); |
| ... | ... | @@ -837,7 +875,6 @@ fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDe |
| 837 | 875 | }; |
| 838 | 876 | try block_scope.block_node.statements.push(&node.base); |
| 839 | 877 | }, |
| 840 | | |
| 841 | 878 | else => |kind| return revertAndWarn( |
| 842 | 879 | rp, |
| 843 | 880 | error.UnsupportedTranslation, |
| ... | ... | @@ -847,12 +884,7 @@ fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDe |
| 847 | 884 | ), |
| 848 | 885 | } |
| 849 | 886 | } |
| 850 | | |
| 851 | | return TransResult{ |
| 852 | | .node = &block_scope.block_node.base, |
| 853 | | .node_scope = scope, |
| 854 | | .child_scope = scope, |
| 855 | | }; |
| 887 | return &block_scope.block_node.base; |
| 856 | 888 | } |
| 857 | 889 | |
| 858 | 890 | fn transDeclRefExpr( |
| ... | ... | @@ -860,17 +892,12 @@ fn transDeclRefExpr( |
| 860 | 892 | scope: *Scope, |
| 861 | 893 | expr: *const ZigClangDeclRefExpr, |
| 862 | 894 | lrvalue: LRValue, |
| 863 | | ) !TransResult { |
| 895 | ) TransError!*ast.Node { |
| 864 | 896 | const value_decl = ZigClangDeclRefExpr_getDecl(expr); |
| 865 | | const c_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, value_decl))); |
| 866 | | const zig_name = transLookupZigIdentifier(scope, c_name); |
| 867 | | if (lrvalue == .l_value) try rp.c.ptr_params.put(zig_name); |
| 868 | | const node = try transCreateNodeIdentifier(rp.c, zig_name); |
| 869 | | return TransResult{ |
| 870 | | .node = node, |
| 871 | | .node_scope = scope, |
| 872 | | .child_scope = scope, |
| 873 | | }; |
| 897 | const name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, value_decl))); |
| 898 | const checked_name = if (scope.getAlias(name)) |a| a else name; |
| 899 | if (lrvalue == .l_value) try rp.c.ptr_params.put(checked_name); |
| 900 | return transCreateNodeIdentifier(rp.c, checked_name); |
| 874 | 901 | } |
| 875 | 902 | |
| 876 | 903 | fn transImplicitCastExpr( |
| ... | ... | @@ -878,7 +905,7 @@ fn transImplicitCastExpr( |
| 878 | 905 | scope: *Scope, |
| 879 | 906 | expr: *const ZigClangImplicitCastExpr, |
| 880 | 907 | result_used: ResultUsed, |
| 881 | | ) !TransResult { |
| 908 | ) TransError!*ast.Node { |
| 882 | 909 | const c = rp.c; |
| 883 | 910 | const sub_expr = ZigClangImplicitCastExpr_getSubExpr(expr); |
| 884 | 911 | const sub_expr_node = try transExpr(rp, scope, @ptrCast(*const ZigClangExpr, sub_expr), .used, .r_value); |
| ... | ... | @@ -886,20 +913,12 @@ fn transImplicitCastExpr( |
| 886 | 913 | .BitCast => { |
| 887 | 914 | const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr)); |
| 888 | 915 | const src_type = getExprQualType(c, sub_expr); |
| 889 | | return TransResult{ |
| 890 | | .node = try transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node.node), |
| 891 | | .node_scope = scope, |
| 892 | | .child_scope = scope, |
| 893 | | }; |
| 916 | return transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node); |
| 894 | 917 | }, |
| 895 | 918 | .IntegralCast => { |
| 896 | 919 | const dest_type = ZigClangExpr_getType(@ptrCast(*const ZigClangExpr, expr)); |
| 897 | 920 | const src_type = ZigClangExpr_getType(sub_expr); |
| 898 | | return TransResult{ |
| 899 | | .node = try transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node.node), |
| 900 | | .node_scope = scope, |
| 901 | | .child_scope = scope, |
| 902 | | }; |
| 921 | return transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node); |
| 903 | 922 | }, |
| 904 | 923 | .FunctionToPointerDecay, .ArrayToPointerDecay => { |
| 905 | 924 | return maybeSuppressResult(rp, scope, result_used, sub_expr_node); |
| ... | ... | @@ -908,11 +927,7 @@ fn transImplicitCastExpr( |
| 908 | 927 | return transExpr(rp, scope, sub_expr, .used, .r_value); |
| 909 | 928 | }, |
| 910 | 929 | .NullToPointer => { |
| 911 | | return TransResult{ |
| 912 | | .node = try transCreateNodeNullLiteral(rp.c), |
| 913 | | .node_scope = scope, |
| 914 | | .child_scope = scope, |
| 915 | | }; |
| 930 | return transCreateNodeNullLiteral(rp.c); |
| 916 | 931 | }, |
| 917 | 932 | else => |kind| return revertAndWarn( |
| 918 | 933 | rp, |
| ... | ... | @@ -929,37 +944,27 @@ fn transIntegerLiteral( |
| 929 | 944 | scope: *Scope, |
| 930 | 945 | expr: *const ZigClangIntegerLiteral, |
| 931 | 946 | result_used: ResultUsed, |
| 932 | | ) !TransResult { |
| 947 | ) TransError!*ast.Node { |
| 933 | 948 | var eval_result: ZigClangExprEvalResult = undefined; |
| 934 | 949 | if (!ZigClangIntegerLiteral_EvaluateAsInt(expr, &eval_result, rp.c.clang_context)) { |
| 935 | 950 | const loc = ZigClangIntegerLiteral_getBeginLoc(expr); |
| 936 | 951 | return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal", .{}); |
| 937 | 952 | } |
| 938 | 953 | const node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val)); |
| 939 | | const res = TransResult{ |
| 940 | | .node = node, |
| 941 | | .child_scope = scope, |
| 942 | | .node_scope = scope, |
| 943 | | }; |
| 944 | | return maybeSuppressResult(rp, scope, result_used, res); |
| 954 | return maybeSuppressResult(rp, scope, result_used, node); |
| 945 | 955 | } |
| 946 | 956 | |
| 947 | 957 | fn transReturnStmt( |
| 948 | 958 | rp: RestorePoint, |
| 949 | 959 | scope: *Scope, |
| 950 | 960 | expr: *const ZigClangReturnStmt, |
| 951 | | ) !TransResult { |
| 961 | ) TransError!*ast.Node { |
| 952 | 962 | const node = try transCreateNodeReturnExpr(rp.c); |
| 953 | 963 | if (ZigClangReturnStmt_getRetValue(expr)) |val_expr| { |
| 954 | | const ret_node = node.cast(ast.Node.ControlFlowExpression).?; |
| 955 | | ret_node.rhs = (try transExpr(rp, scope, val_expr, .used, .r_value)).node; |
| 964 | node.rhs = try transExpr(rp, scope, val_expr, .used, .r_value); |
| 956 | 965 | } |
| 957 | 966 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 958 | | return TransResult{ |
| 959 | | .node = node, |
| 960 | | .child_scope = scope, |
| 961 | | .node_scope = scope, |
| 962 | | }; |
| 967 | return &node.base; |
| 963 | 968 | } |
| 964 | 969 | |
| 965 | 970 | fn transStringLiteral( |
| ... | ... | @@ -967,7 +972,7 @@ fn transStringLiteral( |
| 967 | 972 | scope: *Scope, |
| 968 | 973 | stmt: *const ZigClangStringLiteral, |
| 969 | 974 | result_used: ResultUsed, |
| 970 | | ) !TransResult { |
| 975 | ) TransError!*ast.Node { |
| 971 | 976 | const kind = ZigClangStringLiteral_getKind(stmt); |
| 972 | 977 | switch (kind) { |
| 973 | 978 | .Ascii, .UTF8 => { |
| ... | ... | @@ -989,12 +994,7 @@ fn transStringLiteral( |
| 989 | 994 | node.* = ast.Node.StringLiteral{ |
| 990 | 995 | .token = token, |
| 991 | 996 | }; |
| 992 | | const res = TransResult{ |
| 993 | | .node = &node.base, |
| 994 | | .child_scope = scope, |
| 995 | | .node_scope = scope, |
| 996 | | }; |
| 997 | | return maybeSuppressResult(rp, scope, result_used, res); |
| 997 | return maybeSuppressResult(rp, scope, result_used, &node.base); |
| 998 | 998 | }, |
| 999 | 999 | .UTF16, .UTF32, .Wide => return revertAndWarn( |
| 1000 | 1000 | rp, |
| ... | ... | @@ -1088,7 +1088,7 @@ fn transExpr( |
| 1088 | 1088 | expr: *const ZigClangExpr, |
| 1089 | 1089 | used: ResultUsed, |
| 1090 | 1090 | lrvalue: LRValue, |
| 1091 | | ) TransError!TransResult { |
| 1091 | ) TransError!*ast.Node { |
| 1092 | 1092 | return transStmt(rp, scope, @ptrCast(*const ZigClangStmt, expr), used, lrvalue); |
| 1093 | 1093 | } |
| 1094 | 1094 | |
| ... | ... | @@ -1097,7 +1097,7 @@ fn transInitListExpr( |
| 1097 | 1097 | scope: *Scope, |
| 1098 | 1098 | expr: *const ZigClangInitListExpr, |
| 1099 | 1099 | used: ResultUsed, |
| 1100 | | ) TransError!TransResult { |
| 1100 | ) TransError!*ast.Node { |
| 1101 | 1101 | const qt = getExprQualType(rp.c, @ptrCast(*const ZigClangExpr, expr)); |
| 1102 | 1102 | const qual_type = ZigClangQualType_getTypePtr(qt); |
| 1103 | 1103 | const source_loc = ZigClangExpr_getBeginLoc(@ptrCast(*const ZigClangExpr, expr)); |
| ... | ... | @@ -1128,16 +1128,12 @@ fn transInitListExpr( |
| 1128 | 1128 | var i: c_uint = 0; |
| 1129 | 1129 | while (i < init_count) : (i += 1) { |
| 1130 | 1130 | const elem_expr = ZigClangInitListExpr_getInit(expr, i); |
| 1131 | | try init_node.op.ArrayInitializer.push((try transExpr(rp, scope, elem_expr, .used, .r_value)).node); |
| 1131 | try init_node.op.ArrayInitializer.push(try transExpr(rp, scope, elem_expr, .used, .r_value)); |
| 1132 | 1132 | _ = try appendToken(rp.c, .Comma, ","); |
| 1133 | 1133 | } |
| 1134 | 1134 | init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); |
| 1135 | 1135 | if (leftover_count == 0) { |
| 1136 | | return TransResult{ |
| 1137 | | .node = &init_node.base, |
| 1138 | | .child_scope = scope, |
| 1139 | | .node_scope = scope, |
| 1140 | | }; |
| 1136 | return &init_node.base; |
| 1141 | 1137 | } |
| 1142 | 1138 | cat_tok = try appendToken(rp.c, .PlusPlus, "++"); |
| 1143 | 1139 | } |
| ... | ... | @@ -1145,7 +1141,7 @@ fn transInitListExpr( |
| 1145 | 1141 | const dot_tok = try appendToken(rp.c, .Period, "."); |
| 1146 | 1142 | var filler_init_node = try transCreateNodeArrayInitializer(rp.c, dot_tok); |
| 1147 | 1143 | const filler_val_expr = ZigClangInitListExpr_getArrayFiller(expr); |
| 1148 | | try filler_init_node.op.ArrayInitializer.push((try transExpr(rp, scope, filler_val_expr, .used, .r_value)).node); |
| 1144 | try filler_init_node.op.ArrayInitializer.push(try transExpr(rp, scope, filler_val_expr, .used, .r_value)); |
| 1149 | 1145 | filler_init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); |
| 1150 | 1146 | |
| 1151 | 1147 | const rhs_node = if (leftover_count == 1) |
| ... | ... | @@ -1163,11 +1159,7 @@ fn transInitListExpr( |
| 1163 | 1159 | }; |
| 1164 | 1160 | |
| 1165 | 1161 | if (init_count == 0) { |
| 1166 | | return TransResult{ |
| 1167 | | .node = rhs_node, |
| 1168 | | .child_scope = scope, |
| 1169 | | .node_scope = scope, |
| 1170 | | }; |
| 1162 | return rhs_node; |
| 1171 | 1163 | } |
| 1172 | 1164 | |
| 1173 | 1165 | const cat_node = try rp.c.a().create(ast.Node.InfixOp); |
| ... | ... | @@ -1177,11 +1169,7 @@ fn transInitListExpr( |
| 1177 | 1169 | .op = .ArrayCat, |
| 1178 | 1170 | .rhs = rhs_node, |
| 1179 | 1171 | }; |
| 1180 | | return TransResult{ |
| 1181 | | .node = &cat_node.base, |
| 1182 | | .child_scope = scope, |
| 1183 | | .node_scope = scope, |
| 1184 | | }; |
| 1172 | return &cat_node.base; |
| 1185 | 1173 | } |
| 1186 | 1174 | |
| 1187 | 1175 | fn transImplicitValueInitExpr( |
| ... | ... | @@ -1189,7 +1177,7 @@ fn transImplicitValueInitExpr( |
| 1189 | 1177 | scope: *Scope, |
| 1190 | 1178 | expr: *const ZigClangExpr, |
| 1191 | 1179 | used: ResultUsed, |
| 1192 | | ) TransError!TransResult { |
| 1180 | ) TransError!*ast.Node { |
| 1193 | 1181 | const source_loc = ZigClangExpr_getBeginLoc(expr); |
| 1194 | 1182 | const qt = getExprQualType(rp.c, expr); |
| 1195 | 1183 | const ty = ZigClangQualType_getTypePtr(qt); |
| ... | ... | @@ -1197,9 +1185,7 @@ fn transImplicitValueInitExpr( |
| 1197 | 1185 | .Builtin => blk: { |
| 1198 | 1186 | const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); |
| 1199 | 1187 | switch (ZigClangBuiltinType_getKind(builtin_ty)) { |
| 1200 | | .Bool => { |
| 1201 | | break :blk try transCreateNodeBoolLiteral(rp.c, false); |
| 1202 | | }, |
| 1188 | .Bool => return transCreateNodeBoolLiteral(rp.c, false), |
| 1203 | 1189 | .Char_U, |
| 1204 | 1190 | .UChar, |
| 1205 | 1191 | .Char_S, |
| ... | ... | @@ -1220,37 +1206,13 @@ fn transImplicitValueInitExpr( |
| 1220 | 1206 | .Float128, |
| 1221 | 1207 | .Float16, |
| 1222 | 1208 | .LongDouble, |
| 1223 | | => { |
| 1224 | | break :blk try transCreateNodeInt(rp.c, 0); |
| 1225 | | }, |
| 1209 | => return transCreateNodeInt(rp.c, 0), |
| 1226 | 1210 | else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), |
| 1227 | 1211 | } |
| 1228 | 1212 | }, |
| 1229 | | .Pointer => try transCreateNodeNullLiteral(rp.c), |
| 1213 | .Pointer => return transCreateNodeNullLiteral(rp.c), |
| 1230 | 1214 | else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{}), |
| 1231 | 1215 | }; |
| 1232 | | return TransResult{ |
| 1233 | | .node = node, |
| 1234 | | .child_scope = scope, |
| 1235 | | .node_scope = scope, |
| 1236 | | }; |
| 1237 | | } |
| 1238 | | |
| 1239 | | fn findBlockScope(inner: *Scope) *Scope.Block { |
| 1240 | | var scope = inner; |
| 1241 | | while (true) : (scope = scope.parent orelse unreachable) { |
| 1242 | | if (scope.id == .Block) return @fieldParentPtr(Scope.Block, "base", scope); |
| 1243 | | } |
| 1244 | | } |
| 1245 | | |
| 1246 | | fn transLookupZigIdentifier(inner: *Scope, c_name: []const u8) []const u8 { |
| 1247 | | var scope = inner; |
| 1248 | | while (true) : (scope = scope.parent orelse return c_name) { |
| 1249 | | if (scope.id == .Var) { |
| 1250 | | const var_scope = @ptrCast(*const Scope.Var, scope); |
| 1251 | | if (std.mem.eql(u8, var_scope.c_name, c_name)) return var_scope.zig_name; |
| 1252 | | } |
| 1253 | | } |
| 1254 | 1216 | } |
| 1255 | 1217 | |
| 1256 | 1218 | fn transCPtrCast( |
| ... | ... | @@ -1294,8 +1256,8 @@ fn maybeSuppressResult( |
| 1294 | 1256 | rp: RestorePoint, |
| 1295 | 1257 | scope: *Scope, |
| 1296 | 1258 | used: ResultUsed, |
| 1297 | | result: TransResult, |
| 1298 | | ) !TransResult { |
| 1259 | result: *ast.Node, |
| 1260 | ) TransError!*ast.Node { |
| 1299 | 1261 | if (used == .used) return result; |
| 1300 | 1262 | // NOTE: This is backwards, but the semicolon must immediately follow the node. |
| 1301 | 1263 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| ... | ... | @@ -1306,18 +1268,14 @@ fn maybeSuppressResult( |
| 1306 | 1268 | .op_token = op_token, |
| 1307 | 1269 | .lhs = lhs, |
| 1308 | 1270 | .op = .Assign, |
| 1309 | | .rhs = result.node, |
| 1310 | | }; |
| 1311 | | return TransResult{ |
| 1312 | | .node = &op_node.base, |
| 1313 | | .child_scope = scope, |
| 1314 | | .node_scope = scope, |
| 1271 | .rhs = result, |
| 1315 | 1272 | }; |
| 1273 | return &op_node.base; |
| 1316 | 1274 | } |
| 1317 | 1275 | |
| 1318 | 1276 | fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void { |
| 1319 | 1277 | try c.tree.root_node.decls.push(decl_node); |
| 1320 | | _ = try c.sym_table.put(name, {}); |
| 1278 | _ = try c.global_scope.sym_table.put(name, decl_node); |
| 1321 | 1279 | } |
| 1322 | 1280 | |
| 1323 | 1281 | fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) TypeError!*ast.Node { |
| ... | ... | @@ -1716,11 +1674,11 @@ fn transCreateNodeAssign( |
| 1716 | 1674 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 1717 | 1675 | |
| 1718 | 1676 | const node = try rp.c.a().create(ast.Node.InfixOp); |
| 1719 | | node.* = ast.Node.InfixOp{ |
| 1677 | node.* = .{ |
| 1720 | 1678 | .op_token = eq_token, |
| 1721 | | .lhs = lhs_node.node, |
| 1679 | .lhs = lhs_node, |
| 1722 | 1680 | .op = .Assign, |
| 1723 | | .rhs = rhs_node.node, |
| 1681 | .rhs = rhs_node, |
| 1724 | 1682 | }; |
| 1725 | 1683 | return node; |
| 1726 | 1684 | } |
| ... | ... | @@ -1757,7 +1715,7 @@ fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp { |
| 1757 | 1715 | _ = try appendToken(c, .LParen, "("); |
| 1758 | 1716 | const node = try c.a().create(ast.Node.SuffixOp); |
| 1759 | 1717 | node.* = ast.Node.SuffixOp{ |
| 1760 | | .lhs = fn_expr, |
| 1718 | .lhs = .{ .node = fn_expr }, |
| 1761 | 1719 | .op = ast.Node.SuffixOp.Op{ |
| 1762 | 1720 | .Call = ast.Node.SuffixOp.Op.Call{ |
| 1763 | 1721 | .params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()), |
| ... | ... | @@ -1800,9 +1758,9 @@ fn transCreateNodeInfixOp( |
| 1800 | 1758 | const node = try rp.c.a().create(ast.Node.InfixOp); |
| 1801 | 1759 | node.* = ast.Node.InfixOp{ |
| 1802 | 1760 | .op_token = op_token, |
| 1803 | | .lhs = lhs.node, |
| 1761 | .lhs = lhs, |
| 1804 | 1762 | .op = op, |
| 1805 | | .rhs = rhs.node, |
| 1763 | .rhs = rhs, |
| 1806 | 1764 | }; |
| 1807 | 1765 | if (!grouped) return &node.base; |
| 1808 | 1766 | const rparen = try appendToken(rp.c, .RParen, ")"); |
| ... | ... | @@ -1871,7 +1829,7 @@ fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node { |
| 1871 | 1829 | return &node.base; |
| 1872 | 1830 | } |
| 1873 | 1831 | |
| 1874 | | fn transCreateNodeReturnExpr(c: *Context) !*ast.Node { |
| 1832 | fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression { |
| 1875 | 1833 | const ltoken = try appendToken(c, .Keyword_return, "return"); |
| 1876 | 1834 | const node = try c.a().create(ast.Node.ControlFlowExpression); |
| 1877 | 1835 | node.* = ast.Node.ControlFlowExpression{ |
| ... | ... | @@ -1879,7 +1837,7 @@ fn transCreateNodeReturnExpr(c: *Context) !*ast.Node { |
| 1879 | 1837 | .kind = .Return, |
| 1880 | 1838 | .rhs = null, |
| 1881 | 1839 | }; |
| 1882 | | return &node.base; |
| 1840 | return node; |
| 1883 | 1841 | } |
| 1884 | 1842 | |
| 1885 | 1843 | fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node { |
| ... | ... | @@ -1934,19 +1892,158 @@ fn transCreateNodeInt(c: *Context, int: var) !*ast.Node { |
| 1934 | 1892 | return &node.base; |
| 1935 | 1893 | } |
| 1936 | 1894 | |
| 1895 | fn transCreateNodeFloat(c: *Context, int: var) !*ast.Node { |
| 1896 | const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int}); |
| 1897 | const node = try c.a().create(ast.Node.FloatLiteral); |
| 1898 | node.* = .{ |
| 1899 | .token = token, |
| 1900 | }; |
| 1901 | return &node.base; |
| 1902 | } |
| 1903 | |
| 1937 | 1904 | fn transCreateNodeOpaqueType(c: *Context) !*ast.Node { |
| 1938 | | const builtin_tok = try appendToken(c, .Builtin, "@OpaqueType"); |
| 1905 | const call_node = try transCreateNodeBuiltinFnCall(c, "@OpaqueType"); |
| 1906 | call_node.rparen_token = try appendToken(c, .RParen, ")"); |
| 1907 | return &call_node.base; |
| 1908 | } |
| 1909 | |
| 1910 | fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias_node: *ast.Node) !*ast.Node { |
| 1911 | const scope = &c.global_scope.base; |
| 1912 | |
| 1913 | const pub_tok = try appendToken(c, .Keyword_pub, "pub"); |
| 1914 | const inline_tok = try appendToken(c, .Keyword_inline, "inline"); |
| 1915 | const fn_tok = try appendToken(c, .Keyword_fn, "fn"); |
| 1916 | const name_tok = try appendIdentifier(c, name); |
| 1939 | 1917 | _ = try appendToken(c, .LParen, "("); |
| 1940 | | const rparen_tok = try appendToken(c, .RParen, ")"); |
| 1941 | 1918 | |
| 1942 | | const call_node = try c.a().create(ast.Node.BuiltinCall); |
| 1943 | | call_node.* = ast.Node.BuiltinCall{ |
| 1944 | | .base = ast.Node{ .id = ast.Node.Id.BuiltinCall }, |
| 1945 | | .builtin_token = builtin_tok, |
| 1946 | | .params = ast.Node.BuiltinCall.ParamList.init(c.a()), |
| 1947 | | .rparen_token = rparen_tok, |
| 1919 | const proto_alias = proto_alias_node.cast(ast.Node.FnProto).?; |
| 1920 | |
| 1921 | var fn_params = ast.Node.FnProto.ParamList.init(c.a()); |
| 1922 | var it = proto_alias.params.iterator(0); |
| 1923 | while (it.next()) |pn| { |
| 1924 | if (it.index != 0) { |
| 1925 | _ = try appendToken(c, .Comma, ","); |
| 1926 | } |
| 1927 | const param = pn.*.cast(ast.Node.ParamDecl).?; |
| 1928 | |
| 1929 | const param_name_tok = param.name_token orelse |
| 1930 | try appendTokenFmt(c, .Identifier, "arg_{}", .{c.getMangle()}); |
| 1931 | |
| 1932 | _ = try appendToken(c, .Colon, ":"); |
| 1933 | |
| 1934 | const param_node = try c.a().create(ast.Node.ParamDecl); |
| 1935 | param_node.* = .{ |
| 1936 | .doc_comments = null, |
| 1937 | .comptime_token = null, |
| 1938 | .noalias_token = param.noalias_token, |
| 1939 | .name_token = param_name_tok, |
| 1940 | .type_node = param.type_node, |
| 1941 | .var_args_token = null, |
| 1942 | }; |
| 1943 | try fn_params.push(&param_node.base); |
| 1944 | } |
| 1945 | |
| 1946 | _ = try appendToken(c, .RParen, ")"); |
| 1947 | |
| 1948 | const fn_proto = try c.a().create(ast.Node.FnProto); |
| 1949 | fn_proto.* = .{ |
| 1950 | .doc_comments = null, |
| 1951 | .visib_token = pub_tok, |
| 1952 | .fn_token = fn_tok, |
| 1953 | .name_token = name_tok, |
| 1954 | .params = fn_params, |
| 1955 | .return_type = proto_alias.return_type, |
| 1956 | .var_args_token = null, |
| 1957 | .extern_export_inline_token = inline_tok, |
| 1958 | .cc_token = null, |
| 1959 | .body_node = null, |
| 1960 | .lib_name = null, |
| 1961 | .align_expr = null, |
| 1962 | .section_expr = null, |
| 1948 | 1963 | }; |
| 1949 | | return &call_node.base; |
| 1964 | |
| 1965 | const block = try transCreateNodeBlock(c, null); |
| 1966 | |
| 1967 | const return_expr = try transCreateNodeReturnExpr(c); |
| 1968 | const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.init_node.?); |
| 1969 | const call_expr = try transCreateNodeFnCall(c, unwrap_expr); |
| 1970 | it = fn_params.iterator(0); |
| 1971 | while (it.next()) |pn| { |
| 1972 | if (it.index != 0) { |
| 1973 | _ = try appendToken(c, .Comma, ","); |
| 1974 | } |
| 1975 | const param = pn.*.cast(ast.Node.ParamDecl).?; |
| 1976 | try call_expr.op.Call.params.push(try transCreateNodeIdentifier(c, tokenSlice(c, param.name_token.?))); |
| 1977 | } |
| 1978 | call_expr.rtoken = try appendToken(c, .RParen, ")"); |
| 1979 | return_expr.rhs = &call_expr.base; |
| 1980 | _ = try appendToken(c, .Semicolon, ";"); |
| 1981 | |
| 1982 | block.rbrace = try appendToken(c, .RBrace, "}"); |
| 1983 | try block.statements.push(&return_expr.base); |
| 1984 | fn_proto.body_node = &block.base; |
| 1985 | return &fn_proto.base; |
| 1986 | } |
| 1987 | |
| 1988 | fn transCreateNodeUnwrapNull(c: *Context, wrapped: *ast.Node) !*ast.Node { |
| 1989 | _ = try appendToken(c, .Period, "."); |
| 1990 | const qm = try appendToken(c, .QuestionMark, "?"); |
| 1991 | const node = try c.a().create(ast.Node.SuffixOp); |
| 1992 | node.* = .{ |
| 1993 | .op = .UnwrapOptional, |
| 1994 | .lhs = .{ .node = wrapped }, |
| 1995 | .rtoken = qm, |
| 1996 | }; |
| 1997 | return &node.base; |
| 1998 | } |
| 1999 | |
| 2000 | fn transCreateNodeEnumLiteral(c: *Context, name: []const u8) !*ast.Node { |
| 2001 | const node = try c.a().create(ast.Node.EnumLiteral); |
| 2002 | node.* = .{ |
| 2003 | .dot = try appendToken(c, .Period, "."), |
| 2004 | .name = try appendIdentifier(c, name), |
| 2005 | }; |
| 2006 | return &node.base; |
| 2007 | } |
| 2008 | |
| 2009 | fn transCreateNodeIf(c: *Context) !*ast.Node.If { |
| 2010 | const if_tok = try appendToken(c, .Keyword_if, "if"); |
| 2011 | _ = try appendToken(c, .LParen, "("); |
| 2012 | const node = try c.a().create(ast.Node.If); |
| 2013 | node.* = .{ |
| 2014 | .if_token = if_tok, |
| 2015 | .condition = undefined, |
| 2016 | .payload = null, |
| 2017 | .body = undefined, |
| 2018 | .@"else" = null, |
| 2019 | }; |
| 2020 | return node; |
| 2021 | } |
| 2022 | |
| 2023 | fn transCreateNodeElse(c: *Context) !*ast.Node.Else { |
| 2024 | const node = try c.a().create(ast.Node.Else); |
| 2025 | node.* = .{ |
| 2026 | .else_token = try appendToken(c, .Keyword_else, "else"), |
| 2027 | .payload = null, |
| 2028 | .body = undefined, |
| 2029 | }; |
| 2030 | return node; |
| 2031 | } |
| 2032 | |
| 2033 | fn transCreateNodeBlock(c: *Context, label: ?[]const u8) !*ast.Node.Block { |
| 2034 | const label_node = if (label) |l| blk: { |
| 2035 | const ll = try appendIdentifier(c, l); |
| 2036 | _ = try appendToken(c, .Colon, ":"); |
| 2037 | break :blk ll; |
| 2038 | } else null; |
| 2039 | const block_node = try c.a().create(ast.Node.Block); |
| 2040 | block_node.* = .{ |
| 2041 | .label = label_node, |
| 2042 | .lbrace = try appendToken(c, .LBrace, "{"), |
| 2043 | .statements = ast.Node.Block.StatementList.init(c.a()), |
| 2044 | .rbrace = undefined, |
| 2045 | }; |
| 2046 | return block_node; |
| 1950 | 2047 | } |
| 1951 | 2048 | |
| 1952 | 2049 | const RestorePoint = struct { |
| ... | ... | @@ -1972,28 +2069,28 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour |
| 1972 | 2069 | switch (ZigClangType_getTypeClass(ty)) { |
| 1973 | 2070 | .Builtin => { |
| 1974 | 2071 | const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); |
| 1975 | | switch (ZigClangBuiltinType_getKind(builtin_ty)) { |
| 1976 | | .Void => return transCreateNodeIdentifier(rp.c, "c_void"), |
| 1977 | | .Bool => return transCreateNodeIdentifier(rp.c, "bool"), |
| 1978 | | .Char_U, .UChar, .Char_S, .Char8 => return transCreateNodeIdentifier(rp.c, "u8"), |
| 1979 | | .SChar => return transCreateNodeIdentifier(rp.c, "i8"), |
| 1980 | | .UShort => return transCreateNodeIdentifier(rp.c, "c_ushort"), |
| 1981 | | .UInt => return transCreateNodeIdentifier(rp.c, "c_uint"), |
| 1982 | | .ULong => return transCreateNodeIdentifier(rp.c, "c_ulong"), |
| 1983 | | .ULongLong => return transCreateNodeIdentifier(rp.c, "c_ulonglong"), |
| 1984 | | .Short => return transCreateNodeIdentifier(rp.c, "c_short"), |
| 1985 | | .Int => return transCreateNodeIdentifier(rp.c, "c_int"), |
| 1986 | | .Long => return transCreateNodeIdentifier(rp.c, "c_long"), |
| 1987 | | .LongLong => return transCreateNodeIdentifier(rp.c, "c_longlong"), |
| 1988 | | .UInt128 => return transCreateNodeIdentifier(rp.c, "u128"), |
| 1989 | | .Int128 => return transCreateNodeIdentifier(rp.c, "i128"), |
| 1990 | | .Float => return transCreateNodeIdentifier(rp.c, "f32"), |
| 1991 | | .Double => return transCreateNodeIdentifier(rp.c, "f64"), |
| 1992 | | .Float128 => return transCreateNodeIdentifier(rp.c, "f128"), |
| 1993 | | .Float16 => return transCreateNodeIdentifier(rp.c, "f16"), |
| 1994 | | .LongDouble => return transCreateNodeIdentifier(rp.c, "c_longdouble"), |
| 2072 | return transCreateNodeIdentifier(rp.c, switch (ZigClangBuiltinType_getKind(builtin_ty)) { |
| 2073 | .Void => "c_void", |
| 2074 | .Bool => "bool", |
| 2075 | .Char_U, .UChar, .Char_S, .Char8 => "u8", |
| 2076 | .SChar => "i8", |
| 2077 | .UShort => "c_ushort", |
| 2078 | .UInt => "c_uint", |
| 2079 | .ULong => "c_ulong", |
| 2080 | .ULongLong => "c_ulonglong", |
| 2081 | .Short => "c_short", |
| 2082 | .Int => "c_int", |
| 2083 | .Long => "c_long", |
| 2084 | .LongLong => "c_longlong", |
| 2085 | .UInt128 => "u128", |
| 2086 | .Int128 => "i128", |
| 2087 | .Float => "f32", |
| 2088 | .Double => "f64", |
| 2089 | .Float128 => "f128", |
| 2090 | .Float16 => "f16", |
| 2091 | .LongDouble => "c_longdouble", |
| 1995 | 2092 | else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), |
| 1996 | | } |
| 2093 | }); |
| 1997 | 2094 | }, |
| 1998 | 2095 | .FunctionProto => { |
| 1999 | 2096 | const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty); |
| ... | ... | @@ -2076,6 +2173,13 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour |
| 2076 | 2173 | .Record => { |
| 2077 | 2174 | const record_ty = @ptrCast(*const ZigClangRecordType, ty); |
| 2078 | 2175 | |
| 2176 | // TODO this sould get the name from decl_table |
| 2177 | // struct Foo { |
| 2178 | // struct Bar{ |
| 2179 | // int b; |
| 2180 | // }; |
| 2181 | // struct Bar c; |
| 2182 | // }; |
| 2079 | 2183 | const record_decl = ZigClangRecordType_getDecl(record_ty); |
| 2080 | 2184 | if (try getContainerName(rp, record_decl)) |name| |
| 2081 | 2185 | return transCreateNodeIdentifier(rp.c, name) |
| ... | ... | @@ -2203,6 +2307,9 @@ fn finishTransFnProto( |
| 2203 | 2307 | // TODO check for always_inline attribute |
| 2204 | 2308 | // TODO check for align attribute |
| 2205 | 2309 | |
| 2310 | var fndef_scope = Scope.FnDef.init(rp.c); |
| 2311 | const scope = &fndef_scope.base; |
| 2312 | |
| 2206 | 2313 | // pub extern fn name(...) T |
| 2207 | 2314 | const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null; |
| 2208 | 2315 | const cc_tok = if (cc == .Stdcall) try appendToken(rp.c, .Keyword_stdcallcc, "stdcallcc") else null; |
| ... | ... | @@ -2228,13 +2335,17 @@ fn finishTransFnProto( |
| 2228 | 2335 | const param_name_tok: ?ast.TokenIndex = blk: { |
| 2229 | 2336 | if (fn_decl != null) { |
| 2230 | 2337 | const param = ZigClangFunctionDecl_getParamDecl(fn_decl.?, @intCast(c_uint, i)); |
| 2231 | | const param_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, param))); |
| 2232 | | if (param_name.len > 0) { |
| 2233 | | // TODO: If len == 0, auto-generate arg1, arg2, etc? Or leave the name blank? |
| 2234 | | const result = try appendIdentifier(rp.c, param_name); |
| 2235 | | _ = try appendToken(rp.c, .Colon, ":"); |
| 2236 | | break :blk result; |
| 2237 | | } |
| 2338 | var param_name: []const u8 = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, param))); |
| 2339 | if (param_name.len < 1) |
| 2340 | param_name = "arg"[0..]; |
| 2341 | const checked_param_name = if (try scope.createAlias(rp.c, param_name)) |a| blk: { |
| 2342 | try fndef_scope.params.push(.{ .name = param_name, .alias = a }); |
| 2343 | break :blk a; |
| 2344 | } else param_name; |
| 2345 | |
| 2346 | const result = try appendIdentifier(rp.c, checked_param_name); |
| 2347 | _ = try appendToken(rp.c, .Colon, ":"); |
| 2348 | break :blk result; |
| 2238 | 2349 | } |
| 2239 | 2350 | break :blk null; |
| 2240 | 2351 | }; |
| ... | ... | @@ -2444,3 +2555,530 @@ fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node { |
| 2444 | 2555 | pub fn freeErrors(errors: []ClangErrMsg) void { |
| 2445 | 2556 | ZigClangErrorMsg_delete(errors.ptr, errors.len); |
| 2446 | 2557 | } |
| 2558 | |
| 2559 | fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { |
| 2560 | // TODO if we see #undef, delete it from the table |
| 2561 | var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(unit); |
| 2562 | const it_end = ZigClangASTUnit_getLocalPreprocessingEntities_end(unit); |
| 2563 | var tok_list = ctok.TokenList.init(c.a()); |
| 2564 | const scope = &c.global_scope.base; |
| 2565 | |
| 2566 | while (it.I != it_end.I) : (it.I += 1) { |
| 2567 | const entity = ZigClangPreprocessingRecord_iterator_deref(it); |
| 2568 | tok_list.shrink(0); |
| 2569 | switch (ZigClangPreprocessedEntity_getKind(entity)) { |
| 2570 | .MacroDefinitionKind => { |
| 2571 | const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity); |
| 2572 | const raw_name = ZigClangMacroDefinitionRecord_getName_getNameStart(macro); |
| 2573 | const begin_loc = ZigClangMacroDefinitionRecord_getSourceRange_getBegin(macro); |
| 2574 | |
| 2575 | const name = try c.str(raw_name); |
| 2576 | if (scope.contains(name)) { |
| 2577 | continue; |
| 2578 | } |
| 2579 | const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc); |
| 2580 | ctok.tokenizeCMacro(&tok_list, begin_c) catch |err| switch (err) { |
| 2581 | error.OutOfMemory => |e| return e, |
| 2582 | else => { |
| 2583 | try failDecl(c, begin_loc, name, "unable to tokenize macro definition", .{}); |
| 2584 | continue; |
| 2585 | }, |
| 2586 | }; |
| 2587 | |
| 2588 | var tok_it = tok_list.iterator(0); |
| 2589 | const first_tok = tok_it.next().?; |
| 2590 | assert(first_tok.id == .Identifier and std.mem.eql(u8, first_tok.bytes, name)); |
| 2591 | const next = tok_it.peek().?; |
| 2592 | switch (next.id) { |
| 2593 | .Identifier => { |
| 2594 | // if it equals itself, ignore. for example, from stdio.h: |
| 2595 | // #define stdin stdin |
| 2596 | if (std.mem.eql(u8, name, next.bytes)) { |
| 2597 | continue; |
| 2598 | } |
| 2599 | }, |
| 2600 | .Eof => { |
| 2601 | // this means it is a macro without a value |
| 2602 | // we don't care about such things |
| 2603 | continue; |
| 2604 | }, |
| 2605 | else => {}, |
| 2606 | } |
| 2607 | const macro_fn = if (tok_it.peek().?.id == .Fn) blk: { |
| 2608 | _ = tok_it.next(); |
| 2609 | break :blk true; |
| 2610 | } else false; |
| 2611 | |
| 2612 | (if (macro_fn) |
| 2613 | transMacroFnDefine(c, &tok_it, name, begin_loc) |
| 2614 | else |
| 2615 | transMacroDefine(c, &tok_it, name, begin_loc)) catch |err| switch (err) { |
| 2616 | error.UnsupportedTranslation, |
| 2617 | error.ParseError, |
| 2618 | => try failDecl(c, begin_loc, name, "unable to translate macro", .{}), |
| 2619 | error.OutOfMemory => |e| return e, |
| 2620 | }; |
| 2621 | }, |
| 2622 | else => {}, |
| 2623 | } |
| 2624 | } |
| 2625 | } |
| 2626 | |
| 2627 | fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void { |
| 2628 | const rp = makeRestorePoint(c); |
| 2629 | const scope = &c.global_scope.base; |
| 2630 | |
| 2631 | const visib_tok = try appendToken(c, .Keyword_pub, "pub"); |
| 2632 | const mut_tok = try appendToken(c, .Keyword_const, "const"); |
| 2633 | const name_tok = try appendIdentifier(c, name); |
| 2634 | const eq_tok = try appendToken(c, .Equal, "="); |
| 2635 | |
| 2636 | const init_node = try parseCExpr(rp, it, source_loc, scope); |
| 2637 | |
| 2638 | const node = try c.a().create(ast.Node.VarDecl); |
| 2639 | node.* = ast.Node.VarDecl{ |
| 2640 | .doc_comments = null, |
| 2641 | .visib_token = visib_tok, |
| 2642 | .thread_local_token = null, |
| 2643 | .name_token = name_tok, |
| 2644 | .eq_token = eq_tok, |
| 2645 | .mut_token = mut_tok, |
| 2646 | .comptime_token = null, |
| 2647 | .extern_export_token = null, |
| 2648 | .lib_name = null, |
| 2649 | .type_node = null, |
| 2650 | .align_node = null, |
| 2651 | .section_node = null, |
| 2652 | .init_node = init_node, |
| 2653 | .semicolon_token = try appendToken(c, .Semicolon, ";"), |
| 2654 | }; |
| 2655 | _ = try c.global_scope.macro_table.put(name, &node.base); |
| 2656 | } |
| 2657 | |
| 2658 | fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void { |
| 2659 | const rp = makeRestorePoint(c); |
| 2660 | var fndef_scope = Scope.FnDef.init(c); |
| 2661 | const scope = &fndef_scope.base; |
| 2662 | |
| 2663 | const pub_tok = try appendToken(c, .Keyword_pub, "pub"); |
| 2664 | const inline_tok = try appendToken(c, .Keyword_inline, "inline"); |
| 2665 | const fn_tok = try appendToken(c, .Keyword_fn, "fn"); |
| 2666 | const name_tok = try appendIdentifier(c, name); |
| 2667 | _ = try appendToken(c, .LParen, "("); |
| 2668 | |
| 2669 | if (it.next().?.id != .LParen) { |
| 2670 | return error.ParseError; |
| 2671 | } |
| 2672 | var fn_params = ast.Node.FnProto.ParamList.init(c.a()); |
| 2673 | while (true) { |
| 2674 | const param_tok = it.next().?; |
| 2675 | if (param_tok.id != .Identifier) |
| 2676 | return error.ParseError; |
| 2677 | |
| 2678 | const checked_name = if (try scope.createAlias(c, param_tok.bytes)) |alias| blk: { |
| 2679 | try fndef_scope.params.push(.{ .name = param_tok.bytes, .alias = alias }); |
| 2680 | break :blk alias; |
| 2681 | } else param_tok.bytes; |
| 2682 | |
| 2683 | const param_name_tok = try appendIdentifier(c, checked_name); |
| 2684 | _ = try appendToken(c, .Colon, ":"); |
| 2685 | |
| 2686 | const token_index = try appendToken(c, .Keyword_var, "var"); |
| 2687 | const identifier = try c.a().create(ast.Node.Identifier); |
| 2688 | identifier.* = ast.Node.Identifier{ |
| 2689 | .base = ast.Node{ .id = ast.Node.Id.Identifier }, |
| 2690 | .token = token_index, |
| 2691 | }; |
| 2692 | |
| 2693 | const param_node = try c.a().create(ast.Node.ParamDecl); |
| 2694 | param_node.* = .{ |
| 2695 | .doc_comments = null, |
| 2696 | .comptime_token = null, |
| 2697 | .noalias_token = null, |
| 2698 | .name_token = param_name_tok, |
| 2699 | .type_node = &identifier.base, |
| 2700 | .var_args_token = null, |
| 2701 | }; |
| 2702 | try fn_params.push(&param_node.base); |
| 2703 | |
| 2704 | if (it.peek().?.id != .Comma) |
| 2705 | break; |
| 2706 | _ = it.next(); |
| 2707 | _ = try appendToken(c, .Comma, ","); |
| 2708 | } |
| 2709 | |
| 2710 | if (it.next().?.id != .RParen) { |
| 2711 | return error.ParseError; |
| 2712 | } |
| 2713 | |
| 2714 | _ = try appendToken(c, .RParen, ")"); |
| 2715 | |
| 2716 | const type_of = try transCreateNodeBuiltinFnCall(c, "@TypeOf"); |
| 2717 | type_of.rparen_token = try appendToken(c, .LParen, ")"); |
| 2718 | |
| 2719 | const fn_proto = try c.a().create(ast.Node.FnProto); |
| 2720 | fn_proto.* = .{ |
| 2721 | .visib_token = pub_tok, |
| 2722 | .extern_export_inline_token = inline_tok, |
| 2723 | .fn_token = fn_tok, |
| 2724 | .name_token = name_tok, |
| 2725 | .params = fn_params, |
| 2726 | .return_type = .{ .Explicit = &type_of.base }, |
| 2727 | .doc_comments = null, |
| 2728 | .var_args_token = null, |
| 2729 | .cc_token = null, |
| 2730 | .body_node = null, |
| 2731 | .lib_name = null, |
| 2732 | .align_expr = null, |
| 2733 | .section_expr = null, |
| 2734 | }; |
| 2735 | |
| 2736 | const block = try transCreateNodeBlock(c, null); |
| 2737 | |
| 2738 | const return_expr = try transCreateNodeReturnExpr(c); |
| 2739 | const expr = try parseCExpr(rp, it, source_loc, scope); |
| 2740 | _ = try appendToken(c, .Semicolon, ";"); |
| 2741 | try type_of.params.push(expr); |
| 2742 | return_expr.rhs = expr; |
| 2743 | |
| 2744 | block.rbrace = try appendToken(c, .RBrace, "}"); |
| 2745 | try block.statements.push(&return_expr.base); |
| 2746 | fn_proto.body_node = &block.base; |
| 2747 | _ = try c.global_scope.macro_table.put(name, &fn_proto.base); |
| 2748 | } |
| 2749 | |
| 2750 | const ParseError = Error || error{ |
| 2751 | ParseError, |
| 2752 | UnsupportedTranslation, |
| 2753 | }; |
| 2754 | |
| 2755 | fn parseCExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { |
| 2756 | return parseCPrefixOpExpr(rp, it, source_loc, scope); |
| 2757 | } |
| 2758 | |
| 2759 | fn parseCNumLit(rp: RestorePoint, tok: *CToken, source_loc: ZigClangSourceLocation) ParseError!*ast.Node { |
| 2760 | if (tok.id == .NumLitInt) { |
| 2761 | if (tok.num_lit_suffix == .None) { |
| 2762 | if (tok.bytes.len > 2 and tok.bytes[0] == '0') { |
| 2763 | switch (tok.bytes[1]) { |
| 2764 | '0'...'7' => { |
| 2765 | // octal |
| 2766 | return transCreateNodeInt(rp.c, try std.fmt.allocPrint(rp.c.a(), "0o{}", .{tok.bytes})); |
| 2767 | }, |
| 2768 | else => {}, |
| 2769 | } |
| 2770 | } |
| 2771 | return transCreateNodeInt(rp.c, tok.bytes); |
| 2772 | } |
| 2773 | const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); |
| 2774 | try cast_node.params.push(try transCreateNodeIdentifier(rp.c, switch (tok.num_lit_suffix) { |
| 2775 | .U => "c_uint", |
| 2776 | .L => "c_long", |
| 2777 | .LU => "c_ulong", |
| 2778 | .LL => "c_longlong", |
| 2779 | .LLU => "c_ulonglong", |
| 2780 | else => unreachable, |
| 2781 | })); |
| 2782 | _ = try appendToken(rp.c, .Comma, ","); |
| 2783 | try cast_node.params.push(try transCreateNodeInt(rp.c, tok.bytes)); |
| 2784 | cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); |
| 2785 | return &cast_node.base; |
| 2786 | } else if (tok.id == .NumLitFloat) { |
| 2787 | if (tok.num_lit_suffix == .None) { |
| 2788 | return transCreateNodeFloat(rp.c, tok.bytes); |
| 2789 | } |
| 2790 | const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); |
| 2791 | try cast_node.params.push(try transCreateNodeIdentifier(rp.c, switch (tok.num_lit_suffix) { |
| 2792 | .F => "f32", |
| 2793 | .L => "f64", |
| 2794 | else => unreachable, |
| 2795 | })); |
| 2796 | _ = try appendToken(rp.c, .Comma, ","); |
| 2797 | try cast_node.params.push(try transCreateNodeFloat(rp.c, tok.bytes)); |
| 2798 | cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); |
| 2799 | return &cast_node.base; |
| 2800 | } else |
| 2801 | return revertAndWarn( |
| 2802 | rp, |
| 2803 | error.ParseError, |
| 2804 | source_loc, |
| 2805 | "expected number literal", |
| 2806 | .{}, |
| 2807 | ); |
| 2808 | } |
| 2809 | |
| 2810 | fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { |
| 2811 | const tok = it.next().?; |
| 2812 | switch (tok.id) { |
| 2813 | .CharLit => { |
| 2814 | const token = try appendToken(rp.c, .CharLiteral, tok.bytes); |
| 2815 | const node = try rp.c.a().create(ast.Node.CharLiteral); |
| 2816 | node.* = ast.Node.CharLiteral{ |
| 2817 | .token = token, |
| 2818 | }; |
| 2819 | return &node.base; |
| 2820 | }, |
| 2821 | .StrLit => { |
| 2822 | const token = try appendToken(rp.c, .StringLiteral, tok.bytes); |
| 2823 | const node = try rp.c.a().create(ast.Node.StringLiteral); |
| 2824 | node.* = ast.Node.StringLiteral{ |
| 2825 | .token = token, |
| 2826 | }; |
| 2827 | return &node.base; |
| 2828 | }, |
| 2829 | .NumLitInt, .NumLitFloat => { |
| 2830 | return parseCNumLit(rp, tok, source_loc); |
| 2831 | }, |
| 2832 | .Identifier => { |
| 2833 | const name = if (scope.getAlias(tok.bytes)) |a| a else tok.bytes; |
| 2834 | return transCreateNodeIdentifier(rp.c, name); |
| 2835 | }, |
| 2836 | .LParen => { |
| 2837 | const inner_node = try parseCExpr(rp, it, source_loc, scope); |
| 2838 | |
| 2839 | if (it.peek().?.id == .RParen) { |
| 2840 | _ = it.next(); |
| 2841 | return inner_node; |
| 2842 | } |
| 2843 | |
| 2844 | // hack to get zig fmt to render a comma in builtin calls |
| 2845 | _ = try appendToken(rp.c, .Comma, ","); |
| 2846 | |
| 2847 | const node_to_cast = try parseCExpr(rp, it, source_loc, scope); |
| 2848 | |
| 2849 | if (it.next().?.id != .RParen) { |
| 2850 | return revertAndWarn( |
| 2851 | rp, |
| 2852 | error.ParseError, |
| 2853 | source_loc, |
| 2854 | "unable to translate C expr", |
| 2855 | .{}, |
| 2856 | ); |
| 2857 | } |
| 2858 | |
| 2859 | //if (@typeId(@TypeOf(x)) == .Pointer) |
| 2860 | // @ptrCast(dest, x) |
| 2861 | //else if (@typeId(@TypeOf(x)) == .Integer) |
| 2862 | // @intToPtr(dest, x) |
| 2863 | //else |
| 2864 | // @as(dest, x) |
| 2865 | |
| 2866 | const if_1 = try transCreateNodeIf(rp.c); |
| 2867 | const type_id_1 = try transCreateNodeBuiltinFnCall(rp.c, "@typeId"); |
| 2868 | const type_of_1 = try transCreateNodeBuiltinFnCall(rp.c, "@TypeOf"); |
| 2869 | try type_id_1.params.push(&type_of_1.base); |
| 2870 | try type_of_1.params.push(node_to_cast); |
| 2871 | type_of_1.rparen_token = try appendToken(rp.c, .LParen, ")"); |
| 2872 | type_id_1.rparen_token = try appendToken(rp.c, .LParen, ")"); |
| 2873 | |
| 2874 | const cmp_1 = try rp.c.a().create(ast.Node.InfixOp); |
| 2875 | cmp_1.* = .{ |
| 2876 | .op_token = try appendToken(rp.c, .EqualEqual, "=="), |
| 2877 | .lhs = &type_id_1.base, |
| 2878 | .op = .EqualEqual, |
| 2879 | .rhs = try transCreateNodeEnumLiteral(rp.c, "Pointer"), |
| 2880 | }; |
| 2881 | if_1.condition = &cmp_1.base; |
| 2882 | _ = try appendToken(rp.c, .LParen, ")"); |
| 2883 | |
| 2884 | const ptr_cast = try transCreateNodeBuiltinFnCall(rp.c, "@ptrCast"); |
| 2885 | try ptr_cast.params.push(inner_node); |
| 2886 | try ptr_cast.params.push(node_to_cast); |
| 2887 | ptr_cast.rparen_token = try appendToken(rp.c, .LParen, ")"); |
| 2888 | if_1.body = &ptr_cast.base; |
| 2889 | |
| 2890 | const else_1 = try transCreateNodeElse(rp.c); |
| 2891 | if_1.@"else" = else_1; |
| 2892 | |
| 2893 | const if_2 = try transCreateNodeIf(rp.c); |
| 2894 | const type_id_2 = try transCreateNodeBuiltinFnCall(rp.c, "@typeId"); |
| 2895 | const type_of_2 = try transCreateNodeBuiltinFnCall(rp.c, "@TypeOf"); |
| 2896 | try type_id_2.params.push(&type_of_2.base); |
| 2897 | try type_of_2.params.push(node_to_cast); |
| 2898 | type_of_2.rparen_token = try appendToken(rp.c, .LParen, ")"); |
| 2899 | type_id_2.rparen_token = try appendToken(rp.c, .LParen, ")"); |
| 2900 | |
| 2901 | const cmp_2 = try rp.c.a().create(ast.Node.InfixOp); |
| 2902 | cmp_2.* = .{ |
| 2903 | .op_token = try appendToken(rp.c, .EqualEqual, "=="), |
| 2904 | .lhs = &type_id_2.base, |
| 2905 | .op = .EqualEqual, |
| 2906 | .rhs = try transCreateNodeEnumLiteral(rp.c, "Int"), |
| 2907 | }; |
| 2908 | if_2.condition = &cmp_2.base; |
| 2909 | else_1.body = &if_2.base; |
| 2910 | _ = try appendToken(rp.c, .LParen, ")"); |
| 2911 | |
| 2912 | const int_to_ptr = try transCreateNodeBuiltinFnCall(rp.c, "@intToPtr"); |
| 2913 | try int_to_ptr.params.push(inner_node); |
| 2914 | try int_to_ptr.params.push(node_to_cast); |
| 2915 | int_to_ptr.rparen_token = try appendToken(rp.c, .LParen, ")"); |
| 2916 | if_2.body = &int_to_ptr.base; |
| 2917 | |
| 2918 | const else_2 = try transCreateNodeElse(rp.c); |
| 2919 | if_2.@"else" = else_2; |
| 2920 | |
| 2921 | const as = try transCreateNodeBuiltinFnCall(rp.c, "@as"); |
| 2922 | try as.params.push(inner_node); |
| 2923 | try as.params.push(node_to_cast); |
| 2924 | as.rparen_token = try appendToken(rp.c, .LParen, ")"); |
| 2925 | else_2.body = &as.base; |
| 2926 | |
| 2927 | return &if_1.base; |
| 2928 | }, |
| 2929 | else => return revertAndWarn( |
| 2930 | rp, |
| 2931 | error.UnsupportedTranslation, |
| 2932 | source_loc, |
| 2933 | "unable to translate C expr", |
| 2934 | .{}, |
| 2935 | ), |
| 2936 | } |
| 2937 | } |
| 2938 | |
| 2939 | fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { |
| 2940 | var node = try parseCPrimaryExpr(rp, it, source_loc, scope); |
| 2941 | while (true) { |
| 2942 | const tok = it.next().?; |
| 2943 | switch (tok.id) { |
| 2944 | .Dot => { |
| 2945 | const name_tok = it.next().?; |
| 2946 | if (name_tok.id != .Identifier) |
| 2947 | return revertAndWarn( |
| 2948 | rp, |
| 2949 | error.ParseError, |
| 2950 | source_loc, |
| 2951 | "unable to translate C expr", |
| 2952 | .{}, |
| 2953 | ); |
| 2954 | |
| 2955 | const op_token = try appendToken(rp.c, .Period, "."); |
| 2956 | const rhs = try transCreateNodeIdentifier(rp.c, name_tok.bytes); |
| 2957 | const access_node = try rp.c.a().create(ast.Node.InfixOp); |
| 2958 | access_node.* = .{ |
| 2959 | .op_token = op_token, |
| 2960 | .lhs = node, |
| 2961 | .op = .Period, |
| 2962 | .rhs = rhs, |
| 2963 | }; |
| 2964 | node = &access_node.base; |
| 2965 | }, |
| 2966 | .Asterisk => { |
| 2967 | if (it.peek().?.id == .RParen) { |
| 2968 | // type *) |
| 2969 | |
| 2970 | // hack to get zig fmt to render a comma in builtin calls |
| 2971 | _ = try appendToken(rp.c, .Comma, ","); |
| 2972 | |
| 2973 | const ptr = try transCreateNodePtrType(rp.c, false, false, .Identifier); |
| 2974 | ptr.rhs = node; |
| 2975 | return &ptr.base; |
| 2976 | } else { |
| 2977 | // expr * expr |
| 2978 | const op_token = try appendToken(rp.c, .Asterisk, "*"); |
| 2979 | const rhs = try parseCPrimaryExpr(rp, it, source_loc, scope); |
| 2980 | const bitshift_node = try rp.c.a().create(ast.Node.InfixOp); |
| 2981 | bitshift_node.* = .{ |
| 2982 | .op_token = op_token, |
| 2983 | .lhs = node, |
| 2984 | .op = .BitShiftLeft, |
| 2985 | .rhs = rhs, |
| 2986 | }; |
| 2987 | node = &bitshift_node.base; |
| 2988 | } |
| 2989 | }, |
| 2990 | .Shl => { |
| 2991 | const op_token = try appendToken(rp.c, .AngleBracketAngleBracketLeft, "<<"); |
| 2992 | const rhs = try parseCPrimaryExpr(rp, it, source_loc, scope); |
| 2993 | const bitshift_node = try rp.c.a().create(ast.Node.InfixOp); |
| 2994 | bitshift_node.* = .{ |
| 2995 | .op_token = op_token, |
| 2996 | .lhs = node, |
| 2997 | .op = .BitShiftLeft, |
| 2998 | .rhs = rhs, |
| 2999 | }; |
| 3000 | node = &bitshift_node.base; |
| 3001 | }, |
| 3002 | else => { |
| 3003 | _ = it.prev(); |
| 3004 | return node; |
| 3005 | }, |
| 3006 | } |
| 3007 | } |
| 3008 | } |
| 3009 | |
| 3010 | fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { |
| 3011 | const op_tok = it.next().?; |
| 3012 | |
| 3013 | switch (op_tok.id) { |
| 3014 | .Bang => { |
| 3015 | const node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); |
| 3016 | node.rhs = try parseCPrefixOpExpr(rp, it, source_loc, scope); |
| 3017 | return &node.base; |
| 3018 | }, |
| 3019 | .Minus => { |
| 3020 | const node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-"); |
| 3021 | node.rhs = try parseCPrefixOpExpr(rp, it, source_loc, scope); |
| 3022 | return &node.base; |
| 3023 | }, |
| 3024 | .Tilde => { |
| 3025 | const node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~"); |
| 3026 | node.rhs = try parseCPrefixOpExpr(rp, it, source_loc, scope); |
| 3027 | return &node.base; |
| 3028 | }, |
| 3029 | .Asterisk => { |
| 3030 | const prefix_op_expr = try parseCPrefixOpExpr(rp, it, source_loc, scope); |
| 3031 | const node = try rp.c.a().create(ast.Node.SuffixOp); |
| 3032 | node.* = .{ |
| 3033 | .lhs = .{ .node = prefix_op_expr }, |
| 3034 | .op = .Deref, |
| 3035 | .rtoken = try appendToken(rp.c, .PeriodAsterisk, ".*"), |
| 3036 | }; |
| 3037 | return &node.base; |
| 3038 | }, |
| 3039 | else => { |
| 3040 | _ = it.prev(); |
| 3041 | return try parseCSuffixOpExpr(rp, it, source_loc, scope); |
| 3042 | }, |
| 3043 | } |
| 3044 | } |
| 3045 | |
| 3046 | fn tokenSlice(c: *Context, token: ast.TokenIndex) []const u8 { |
| 3047 | const tok = c.tree.tokens.at(token); |
| 3048 | return c.source_buffer.toSliceConst()[tok.start..tok.end]; |
| 3049 | } |
| 3050 | |
| 3051 | fn getFnDecl(c: *Context, ref: *ast.Node) ?*ast.Node { |
| 3052 | const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null; |
| 3053 | const name = if (init.cast(ast.Node.Identifier)) |id| |
| 3054 | tokenSlice(c, id.token) |
| 3055 | else |
| 3056 | return null; |
| 3057 | // TODO a.b.c |
| 3058 | if (c.global_scope.sym_table.get(name)) |kv| { |
| 3059 | if (kv.value.cast(ast.Node.VarDecl)) |val| { |
| 3060 | if (val.type_node) |type_node| { |
| 3061 | if (type_node.cast(ast.Node.PrefixOp)) |casted| { |
| 3062 | if (casted.rhs.id == .FnProto) { |
| 3063 | return casted.rhs; |
| 3064 | } |
| 3065 | } |
| 3066 | } |
| 3067 | } |
| 3068 | } |
| 3069 | return null; |
| 3070 | } |
| 3071 | |
| 3072 | fn addMacros(c: *Context) !void { |
| 3073 | var macro_it = c.global_scope.macro_table.iterator(); |
| 3074 | while (macro_it.next()) |kv| { |
| 3075 | if (getFnDecl(c, kv.value)) |proto_node| { |
| 3076 | // If a macro aliases a global variable which is a function pointer, we conclude that |
| 3077 | // the macro is intended to represent a function that assumes the function pointer |
| 3078 | // variable is non-null and calls it. |
| 3079 | try addTopLevelDecl(c, kv.key, try transCreateNodeMacroFn(c, kv.key, kv.value, proto_node)); |
| 3080 | } else { |
| 3081 | try addTopLevelDecl(c, kv.key, kv.value); |
| 3082 | } |
| 3083 | } |
| 3084 | } |