| ... | @@ -54,7 +54,9 @@ const Scope = struct { | ... | @@ -54,7 +54,9 @@ const Scope = struct { |
| 54 | | 54 | |
| 55 | const Switch = struct { | 55 | const Switch = struct { |
| 56 | base: Scope, | 56 | base: Scope, |
| 57 | label: []const u8, | 57 | pending_block: *ast.Node.Block, |
| | 58 | cases: *ast.Node.Switch.CaseList, |
| | 59 | has_default: bool = false, |
| 58 | }; | 60 | }; |
| 59 | | 61 | |
| 60 | /// used when getting a member `a.b` | 62 | /// used when getting a member `a.b` |
| ... | @@ -189,8 +191,8 @@ const Scope = struct { | ... | @@ -189,8 +191,8 @@ const Scope = struct { |
| 189 | .Ref => null, | 191 | .Ref => null, |
| 190 | .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name), | 192 | .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name), |
| 191 | .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), | 193 | .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), |
| | 194 | .Switch, |
| 192 | .Condition => scope.parent.?.getAlias(name), | 195 | .Condition => scope.parent.?.getAlias(name), |
| 193 | else => @panic("TODO Scope.getAlias"), | | |
| 194 | }; | 196 | }; |
| 195 | } | 197 | } |
| 196 | | 198 | |
| ... | @@ -200,15 +202,31 @@ const Scope = struct { | ... | @@ -200,15 +202,31 @@ const Scope = struct { |
| 200 | .Root => @fieldParentPtr(Root, "base", scope).contains(name), | 202 | .Root => @fieldParentPtr(Root, "base", scope).contains(name), |
| 201 | .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name), | 203 | .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name), |
| 202 | .Block => @fieldParentPtr(Block, "base", scope).contains(name), | 204 | .Block => @fieldParentPtr(Block, "base", scope).contains(name), |
| | 205 | .Switch, |
| 203 | .Condition => scope.parent.?.contains(name), | 206 | .Condition => scope.parent.?.contains(name), |
| 204 | else => @panic("TODO Scope.contains"), | | |
| 205 | }; | 207 | }; |
| 206 | } | 208 | } |
| 207 | | 209 | |
| 208 | fn getBreakableScope(inner: *Scope) *Scope { | 210 | fn getBreakableScope(inner: *Scope) *Scope { |
| 209 | var scope = inner; | 211 | var scope = inner; |
| 210 | while (scope.id != .Switch and scope.id != .Root) : (scope = scope.parent.?) {} | 212 | while (true) { |
| 211 | return scope; | 213 | switch (scope.id) { |
| | 214 | .FnDef => unreachable, |
| | 215 | .Switch => return scope, |
| | 216 | else => scope = scope.parent.?, |
| | 217 | } |
| | 218 | } |
| | 219 | } |
| | 220 | |
| | 221 | fn getSwitch(inner: *Scope) *Scope.Switch { |
| | 222 | var scope = inner; |
| | 223 | while (true) { |
| | 224 | switch (scope.id) { |
| | 225 | .FnDef => unreachable, |
| | 226 | .Switch => return @fieldParentPtr(Switch, "base", scope), |
| | 227 | else => scope = scope.parent.?, |
| | 228 | } |
| | 229 | } |
| 212 | } | 230 | } |
| 213 | }; | 231 | }; |
| 214 | | 232 | |
| ... | @@ -634,6 +652,10 @@ fn transStmt( | ... | @@ -634,6 +652,10 @@ fn transStmt( |
| 634 | .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const ZigClangForStmt, stmt)), | 652 | .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const ZigClangForStmt, stmt)), |
| 635 | .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const ZigClangFloatingLiteral, stmt), result_used), | 653 | .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const ZigClangFloatingLiteral, stmt), result_used), |
| 636 | .ConditionalOperatorClass => return transConditionalOperator(rp, scope, @ptrCast(*const ZigClangConditionalOperator, stmt), result_used), | 654 | .ConditionalOperatorClass => return transConditionalOperator(rp, scope, @ptrCast(*const ZigClangConditionalOperator, stmt), result_used), |
| | 655 | .SwitchStmtClass => return transSwitch(rp, scope, @ptrCast(*const ZigClangSwitchStmt, stmt)), |
| | 656 | .CaseStmtClass => return transCase(rp, scope, @ptrCast(*const ZigClangCaseStmt, stmt)), |
| | 657 | .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const ZigClangDefaultStmt, stmt)), |
| | 658 | .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used), |
| 637 | else => { | 659 | else => { |
| 638 | return revertAndWarn( | 660 | return revertAndWarn( |
| 639 | rp, | 661 | rp, |
| ... | @@ -1374,6 +1396,154 @@ fn transForLoop( | ... | @@ -1374,6 +1396,154 @@ fn transForLoop( |
| 1374 | return &while_node.base; | 1396 | return &while_node.base; |
| 1375 | } | 1397 | } |
| 1376 | | 1398 | |
| | 1399 | fn transSwitch( |
| | 1400 | rp: RestorePoint, |
| | 1401 | scope: *Scope, |
| | 1402 | stmt: *const ZigClangSwitchStmt, |
| | 1403 | ) TransError!*ast.Node { |
| | 1404 | const switch_node = try transCreateNodeSwitch(rp.c); |
| | 1405 | var switch_scope = Scope.Switch{ |
| | 1406 | .base = .{ |
| | 1407 | .id = .Switch, |
| | 1408 | .parent = scope, |
| | 1409 | }, |
| | 1410 | .cases = &switch_node.cases, |
| | 1411 | .pending_block = undefined, |
| | 1412 | }; |
| | 1413 | |
| | 1414 | var cond_scope = Scope.Condition{ |
| | 1415 | .base = .{ |
| | 1416 | .parent = scope, |
| | 1417 | .id = .Condition, |
| | 1418 | }, |
| | 1419 | }; |
| | 1420 | switch_node.expr = try transExpr(rp, &cond_scope.base, ZigClangSwitchStmt_getCond(stmt), .used, .r_value); |
| | 1421 | _ = try appendToken(rp.c, .RParen, ")"); |
| | 1422 | _ = try appendToken(rp.c, .LBrace, "{"); |
| | 1423 | switch_node.rbrace = try appendToken(rp.c, .RBrace, "}"); |
| | 1424 | |
| | 1425 | const block_scope = try Scope.Block.init(rp.c, &switch_scope.base, null); |
| | 1426 | // tmp block that all statements will go before being picked up by a case or default |
| | 1427 | const block = try transCreateNodeBlock(rp.c, null); |
| | 1428 | block_scope.block_node = block; |
| | 1429 | |
| | 1430 | const switch_block = try transCreateNodeBlock(rp.c, null); |
| | 1431 | try switch_block.statements.push(&switch_node.base); |
| | 1432 | switch_scope.pending_block = switch_block; |
| | 1433 | |
| | 1434 | |
| | 1435 | const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value); |
| | 1436 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| | 1437 | |
| | 1438 | // take all pending statements |
| | 1439 | var it = last.cast(ast.Node.Block).?.statements.iterator(0); |
| | 1440 | while (it.next()) |n| { |
| | 1441 | try switch_scope.pending_block.statements.push(n.*); |
| | 1442 | } |
| | 1443 | |
| | 1444 | switch_scope.pending_block.label = try appendIdentifier(rp.c, "__switch"); |
| | 1445 | _ = try appendToken(rp.c, .Colon, ":"); |
| | 1446 | if (!switch_scope.has_default) { |
| | 1447 | const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c)); |
| | 1448 | else_prong.expr = &(try transCreateNodeBreak(rp.c, "__switch")).base; |
| | 1449 | _ = try appendToken(rp.c, .Comma, ","); |
| | 1450 | try switch_node.cases.push(&else_prong.base); |
| | 1451 | } |
| | 1452 | switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}"); |
| | 1453 | return &switch_scope.pending_block.base; |
| | 1454 | } |
| | 1455 | |
| | 1456 | |
| | 1457 | fn transCase( |
| | 1458 | rp: RestorePoint, |
| | 1459 | scope: *Scope, |
| | 1460 | stmt: *const ZigClangCaseStmt, |
| | 1461 | ) TransError!*ast.Node { |
| | 1462 | const block_scope = scope.findBlockScope(rp.c) catch unreachable; |
| | 1463 | const switch_scope = scope.getSwitch(); |
| | 1464 | const label = try std.fmt.allocPrint(rp.c.a(), "__case_{}", .{switch_scope.cases.len - @boolToInt(switch_scope.has_default)}); |
| | 1465 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| | 1466 | |
| | 1467 | const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: { |
| | 1468 | const lhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value); |
| | 1469 | const ellips = try appendToken(rp.c, .Ellipsis3, "..."); |
| | 1470 | const rhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value); |
| | 1471 | |
| | 1472 | const node = try rp.c.a().create(ast.Node.InfixOp); |
| | 1473 | node.* = .{ |
| | 1474 | .op_token = ellips, |
| | 1475 | .lhs = lhs_node, |
| | 1476 | .op = .Range, |
| | 1477 | .rhs = rhs_node, |
| | 1478 | }; |
| | 1479 | break :blk &node.base; |
| | 1480 | } else |
| | 1481 | try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value); |
| | 1482 | |
| | 1483 | |
| | 1484 | const switch_prong = try transCreateNodeSwitchCase(rp.c, expr); |
| | 1485 | switch_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base; |
| | 1486 | _ = try appendToken(rp.c, .Comma, ","); |
| | 1487 | try switch_scope.cases.push(&switch_prong.base); |
| | 1488 | |
| | 1489 | const block = try transCreateNodeBlock(rp.c, null); |
| | 1490 | switch_scope.pending_block.label = try appendIdentifier(rp.c, label); |
| | 1491 | _ = try appendToken(rp.c, .Colon, ":"); |
| | 1492 | switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}"); |
| | 1493 | try block.statements.push(&switch_scope.pending_block.base); |
| | 1494 | |
| | 1495 | // take all pending statements |
| | 1496 | var it = block_scope.block_node.statements.iterator(0); |
| | 1497 | while (it.next()) |n| { |
| | 1498 | try switch_scope.pending_block.statements.push(n.*); |
| | 1499 | } |
| | 1500 | block_scope.block_node.statements.shrink(0); |
| | 1501 | |
| | 1502 | switch_scope.pending_block = block; |
| | 1503 | |
| | 1504 | return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value); |
| | 1505 | } |
| | 1506 | |
| | 1507 | fn transDefault( |
| | 1508 | rp: RestorePoint, |
| | 1509 | scope: *Scope, |
| | 1510 | stmt: *const ZigClangDefaultStmt, |
| | 1511 | ) TransError!*ast.Node { |
| | 1512 | const block_scope = scope.findBlockScope(rp.c) catch unreachable; |
| | 1513 | const switch_scope = scope.getSwitch(); |
| | 1514 | const label = "__default"; |
| | 1515 | switch_scope.has_default = true; |
| | 1516 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| | 1517 | |
| | 1518 | const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c)); |
| | 1519 | else_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base; |
| | 1520 | _ = try appendToken(rp.c, .Comma, ","); |
| | 1521 | try switch_scope.cases.push(&else_prong.base); |
| | 1522 | |
| | 1523 | const block = try transCreateNodeBlock(rp.c, null); |
| | 1524 | switch_scope.pending_block.label = try appendIdentifier(rp.c, label); |
| | 1525 | _ = try appendToken(rp.c, .Colon, ":"); |
| | 1526 | switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}"); |
| | 1527 | try block.statements.push(&switch_scope.pending_block.base); |
| | 1528 | |
| | 1529 | // take all pending statements |
| | 1530 | var it = block_scope.block_node.statements.iterator(0); |
| | 1531 | while (it.next()) |n| { |
| | 1532 | try switch_scope.pending_block.statements.push(n.*); |
| | 1533 | } |
| | 1534 | block_scope.block_node.statements.shrink(0); |
| | 1535 | |
| | 1536 | switch_scope.pending_block = block; |
| | 1537 | return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value); |
| | 1538 | } |
| | 1539 | |
| | 1540 | fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangExpr, used: ResultUsed) TransError!*ast.Node { |
| | 1541 | var result: ZigClangExprEvalResult = undefined; |
| | 1542 | if (!ZigClangExpr_EvaluateAsConstantExpr(expr, &result, .EvaluateForCodeGen, rp.c.clang_context)) |
| | 1543 | return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "invalid constant expression", .{}); |
| | 1544 | return maybeSuppressResult(rp, scope, used, try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&result.Val))); |
| | 1545 | } |
| | 1546 | |
| 1377 | fn transCPtrCast( | 1547 | fn transCPtrCast( |
| 1378 | rp: RestorePoint, | 1548 | rp: RestorePoint, |
| 1379 | loc: ZigClangSourceLocation, | 1549 | loc: ZigClangSourceLocation, |
| ... | @@ -1414,7 +1584,7 @@ fn transCPtrCast( | ... | @@ -1414,7 +1584,7 @@ fn transCPtrCast( |
| 1414 | fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node { | 1584 | fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node { |
| 1415 | const break_scope = scope.getBreakableScope(); | 1585 | const break_scope = scope.getBreakableScope(); |
| 1416 | const br = try transCreateNodeBreak(rp.c, if (break_scope.id == .Switch) | 1586 | const br = try transCreateNodeBreak(rp.c, if (break_scope.id == .Switch) |
| 1417 | @fieldParentPtr(Scope.Switch, "base", break_scope).label | 1587 | "__switch" |
| 1418 | else | 1588 | else |
| 1419 | null); | 1589 | null); |
| 1420 | return &br.base; | 1590 | return &br.base; |
| ... | @@ -2339,6 +2509,42 @@ fn transCreateNodeContinue(c: *Context) !*ast.Node { | ... | @@ -2339,6 +2509,42 @@ fn transCreateNodeContinue(c: *Context) !*ast.Node { |
| 2339 | return &node.base; | 2509 | return &node.base; |
| 2340 | } | 2510 | } |
| 2341 | | 2511 | |
| | 2512 | fn transCreateNodeSwitch(c: *Context) !*ast.Node.Switch { |
| | 2513 | const switch_tok = try appendToken(c, .Keyword_switch, "switch"); |
| | 2514 | _ = try appendToken(c, .LParen, "("); |
| | 2515 | |
| | 2516 | const node = try c.a().create(ast.Node.Switch); |
| | 2517 | node.* = .{ |
| | 2518 | .switch_token = switch_tok, |
| | 2519 | .expr = undefined, |
| | 2520 | .cases = ast.Node.Switch.CaseList.init(c.a()), |
| | 2521 | .rbrace = undefined, |
| | 2522 | }; |
| | 2523 | return node; |
| | 2524 | } |
| | 2525 | |
| | 2526 | fn transCreateNodeSwitchCase(c: *Context, lhs: *ast.Node) !*ast.Node.SwitchCase { |
| | 2527 | const arrow_tok = try appendToken(c, .EqualAngleBracketRight, "=>"); |
| | 2528 | |
| | 2529 | const node = try c.a().create(ast.Node.SwitchCase); |
| | 2530 | node.* = .{ |
| | 2531 | .items = ast.Node.SwitchCase.ItemList.init(c.a()), |
| | 2532 | .arrow_token = arrow_tok, |
| | 2533 | .payload = null, |
| | 2534 | .expr = undefined, |
| | 2535 | }; |
| | 2536 | try node.items.push(lhs); |
| | 2537 | return node; |
| | 2538 | } |
| | 2539 | |
| | 2540 | fn transCreateNodeSwitchElse(c: *Context) !*ast.Node { |
| | 2541 | const node = try c.a().create(ast.Node.SwitchElse); |
| | 2542 | node.* = .{ |
| | 2543 | .token = try appendToken(c, .Keyword_else, "else"), |
| | 2544 | }; |
| | 2545 | return &node.base; |
| | 2546 | } |
| | 2547 | |
| 2342 | const RestorePoint = struct { | 2548 | const RestorePoint = struct { |
| 2343 | c: *Context, | 2549 | c: *Context, |
| 2344 | token_index: ast.TokenIndex, | 2550 | token_index: ast.TokenIndex, |
| ... | @@ -2818,7 +3024,7 @@ fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, | ... | @@ -2818,7 +3024,7 @@ fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, |
| 2818 | | 3024 | |
| 2819 | // TODO hook up with codegen | 3025 | // TODO hook up with codegen |
| 2820 | fn isZigPrimitiveType(name: []const u8) bool { | 3026 | fn isZigPrimitiveType(name: []const u8) bool { |
| 2821 | if (name.len > 1 and std.mem.startsWith(u8, name, "u") or std.mem.startsWith(u8, name, "u")) { | 3027 | if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) { |
| 2822 | for (name[1..]) |c| { | 3028 | for (name[1..]) |c| { |
| 2823 | switch (c) { | 3029 | switch (c) { |
| 2824 | '0'...'9' => {}, | 3030 | '0'...'9' => {}, |
| ... | @@ -2840,7 +3046,15 @@ fn isZigPrimitiveType(name: []const u8) bool { | ... | @@ -2840,7 +3046,15 @@ fn isZigPrimitiveType(name: []const u8) bool { |
| 2840 | std.mem.eql(u8, name, "c_longdouble") or | 3046 | std.mem.eql(u8, name, "c_longdouble") or |
| 2841 | std.mem.eql(u8, name, "noreturn") or | 3047 | std.mem.eql(u8, name, "noreturn") or |
| 2842 | std.mem.eql(u8, name, "type") or | 3048 | std.mem.eql(u8, name, "type") or |
| 2843 | std.mem.eql(u8, name, "anyerror"); | 3049 | std.mem.eql(u8, name, "anyerror") or |
| | 3050 | std.mem.eql(u8, name, "c_short") or |
| | 3051 | std.mem.eql(u8, name, "c_ushort") or |
| | 3052 | std.mem.eql(u8, name, "c_int") or |
| | 3053 | std.mem.eql(u8, name, "c_uint") or |
| | 3054 | std.mem.eql(u8, name, "c_long") or |
| | 3055 | std.mem.eql(u8, name, "c_ulong") or |
| | 3056 | std.mem.eql(u8, name, "c_longlong") or |
| | 3057 | std.mem.eql(u8, name, "c_ulonglong"); |
| 2844 | } | 3058 | } |
| 2845 | | 3059 | |
| 2846 | fn isValidZigIdentifier(name: []const u8) bool { | 3060 | fn isValidZigIdentifier(name: []const u8) bool { |