| ... | ... | @@ -54,7 +54,9 @@ const Scope = struct { |
| 54 | 54 | |
| 55 | 55 | const Switch = struct { |
| 56 | 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 | 62 | /// used when getting a member `a.b` |
| ... | ... | @@ -189,8 +191,8 @@ const Scope = struct { |
| 189 | 191 | .Ref => null, |
| 190 | 192 | .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name), |
| 191 | 193 | .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), |
| 194 | .Switch, |
| 192 | 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 | 202 | .Root => @fieldParentPtr(Root, "base", scope).contains(name), |
| 201 | 203 | .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name), |
| 202 | 204 | .Block => @fieldParentPtr(Block, "base", scope).contains(name), |
| 205 | .Switch, |
| 203 | 206 | .Condition => scope.parent.?.contains(name), |
| 204 | | else => @panic("TODO Scope.contains"), |
| 205 | 207 | }; |
| 206 | 208 | } |
| 207 | 209 | |
| 208 | 210 | fn getBreakableScope(inner: *Scope) *Scope { |
| 209 | 211 | var scope = inner; |
| 210 | | while (scope.id != .Switch and scope.id != .Root) : (scope = scope.parent.?) {} |
| 211 | | return scope; |
| 212 | while (true) { |
| 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 | 652 | .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const ZigClangForStmt, stmt)), |
| 635 | 653 | .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const ZigClangFloatingLiteral, stmt), result_used), |
| 636 | 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 | 659 | else => { |
| 638 | 660 | return revertAndWarn( |
| 639 | 661 | rp, |
| ... | ... | @@ -1374,6 +1396,154 @@ fn transForLoop( |
| 1374 | 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 | 1547 | fn transCPtrCast( |
| 1378 | 1548 | rp: RestorePoint, |
| 1379 | 1549 | loc: ZigClangSourceLocation, |
| ... | ... | @@ -1414,7 +1584,7 @@ fn transCPtrCast( |
| 1414 | 1584 | fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node { |
| 1415 | 1585 | const break_scope = scope.getBreakableScope(); |
| 1416 | 1586 | const br = try transCreateNodeBreak(rp.c, if (break_scope.id == .Switch) |
| 1417 | | @fieldParentPtr(Scope.Switch, "base", break_scope).label |
| 1587 | "__switch" |
| 1418 | 1588 | else |
| 1419 | 1589 | null); |
| 1420 | 1590 | return &br.base; |
| ... | ... | @@ -2339,6 +2509,42 @@ fn transCreateNodeContinue(c: *Context) !*ast.Node { |
| 2339 | 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 | 2548 | const RestorePoint = struct { |
| 2343 | 2549 | c: *Context, |
| 2344 | 2550 | token_index: ast.TokenIndex, |
| ... | ... | @@ -2818,7 +3024,7 @@ fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, |
| 2818 | 3024 | |
| 2819 | 3025 | // TODO hook up with codegen |
| 2820 | 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 | 3028 | for (name[1..]) |c| { |
| 2823 | 3029 | switch (c) { |
| 2824 | 3030 | '0'...'9' => {}, |
| ... | ... | @@ -2840,7 +3046,15 @@ fn isZigPrimitiveType(name: []const u8) bool { |
| 2840 | 3046 | std.mem.eql(u8, name, "c_longdouble") or |
| 2841 | 3047 | std.mem.eql(u8, name, "noreturn") or |
| 2842 | 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 | 3060 | fn isValidZigIdentifier(name: []const u8) bool { |