diff --git a/src/translate_c.zig b/src/translate_c.zig index 66b5752930..622b678343 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -5527,58 +5527,41 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { const ParseError = Error || error{ParseError}; fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { - const node = try parseCPrefixOpExpr(c, m, scope); - switch (m.next().?) { - .QuestionMark => { - // must come immediately after expr - _ = try appendToken(c, .RParen, ")"); - const if_node = try transCreateNodeIf(c); - if_node.condition = node; - if_node.body = try parseCPrimaryExpr(c, m, scope); - if (m.next().? != .Colon) { - try m.fail(c, "unable to translate C expr: expected ':'", .{}); - return error.ParseError; - } - if_node.@"else" = try transCreateNodeElse(c); - if_node.@"else".?.body = try parseCPrimaryExpr(c, m, scope); - return &if_node.base; - }, - .Comma => { - _ = try appendToken(c, .Semicolon, ";"); - var block_scope = try Scope.Block.init(c, scope, true); - defer block_scope.deinit(); - - var last = node; - while (true) { - // suppress result - const lhs = try transCreateNodeIdentifier(c, "_"); - const op_token = try appendToken(c, .Equal, "="); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = .Assign }, - .op_token = op_token, - .lhs = lhs, - .rhs = last, - }; - try block_scope.statements.append(&op_node.base); - - last = try parseCPrefixOpExpr(c, m, scope); - _ = try appendToken(c, .Semicolon, ";"); - if (m.next().? != .Comma) { - m.i -= 1; - break; - } - } - - const break_node = try transCreateNodeBreak(c, block_scope.label, last); - try block_scope.statements.append(&break_node.base); - return try block_scope.complete(c); - }, - else => { - m.i -= 1; - return node; - }, + // TODO parseCAssignExpr here + const node = try parseCCondExpr(c, m, scope); + if (m.next().? != .Comma) { + m.i -= 1; + return node; } + _ = try appendToken(c, .Semicolon, ";"); + var block_scope = try Scope.Block.init(c, scope, true); + defer block_scope.deinit(); + + var last = node; + while (true) { + // suppress result + const lhs = try transCreateNodeIdentifier(c, "_"); + const op_token = try appendToken(c, .Equal, "="); + const op_node = try c.arena.create(ast.Node.SimpleInfixOp); + op_node.* = .{ + .base = .{ .tag = .Assign }, + .op_token = op_token, + .lhs = lhs, + .rhs = last, + }; + try block_scope.statements.append(&op_node.base); + + last = try parseCCondExpr(c, m, scope); + _ = try appendToken(c, .Semicolon, ";"); + if (m.next().? != .Comma) { + m.i -= 1; + break; + } + } + + const break_node = try transCreateNodeBreak(c, block_scope.label, last); + try block_scope.statements.append(&break_node.base); + return try block_scope.complete(c); } fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { @@ -5805,7 +5788,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { return bytes[0..i]; } -fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { const tok = m.next().?; const slice = m.slice(); switch (tok) { @@ -5952,6 +5935,30 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N } } +fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + var node = try parseCPrimaryExprInner(c, m, scope); + // In C the preprocessor would handle concatting strings while expanding macros. + // This should do approximately the same by concatting any strings and identifiers + // after a primary expression. + while (true) { + var op_token: ast.TokenIndex = undefined; + var op_id: ast.Node.Tag = undefined; + switch (m.peek().?) { + .StringLiteral, .Identifier => {}, + else => break, + } + const op_node = try c.arena.create(ast.Node.SimpleInfixOp); + op_node.* = .{ + .base = .{ .tag = .ArrayCat }, + .op_token = try appendToken(c, .PlusPlus, "++"), + .lhs = node, + .rhs = try parseCPrimaryExprInner(c, m, scope), + }; + node = &op_node.base; + } + return node; +} + fn nodeIsInfixOp(tag: ast.Node.Tag) bool { return switch (tag) { .Add, @@ -6053,31 +6060,268 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node { return &group_node.base; } -fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { - var node = try parseCPrimaryExpr(c, m, scope); +fn macroGroup(c: *Context, node: *ast.Node) !*ast.Node { + if (!nodeIsInfixOp(node.tag)) return node; + + const group_node = try c.arena.create(ast.Node.GroupedExpression); + group_node.* = .{ + .lparen = try appendToken(c, .LParen, "("), + .expr = node, + .rparen = try appendToken(c, .RParen, ")"), + }; + return &group_node.base; +} + +fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + const node = try parseCOrExpr(c, m, scope); + if (m.peek().? != .QuestionMark) { + return node; + } + _ = m.next(); + + // must come immediately after expr + _ = try appendToken(c, .RParen, ")"); + const if_node = try transCreateNodeIf(c); + if_node.condition = node; + if_node.body = try parseCOrExpr(c, m, scope); + if (m.next().? != .Colon) { + try m.fail(c, "unable to translate C expr: expected ':'", .{}); + return error.ParseError; + } + if_node.@"else" = try transCreateNodeElse(c); + if_node.@"else".?.body = try parseCCondExpr(c, m, scope); + return &if_node.base; +} + +fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + var node = try parseCAndExpr(c, m, scope); + while (m.next().? == .PipePipe) { + const lhs_node = try macroIntToBool(c, node); + const op_token = try appendToken(c, .Keyword_or, "or"); + const rhs_node = try parseCAndExpr(c, m, scope); + const op_node = try c.arena.create(ast.Node.SimpleInfixOp); + op_node.* = .{ + .base = .{ .tag = .BoolOr }, + .op_token = op_token, + .lhs = lhs_node, + .rhs = try macroIntToBool(c, rhs_node), + }; + node = &op_node.base; + } + m.i -= 1; + return node; +} + +fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + var node = try parseCBitOrExpr(c, m, scope); + while (m.next().? == .AmpersandAmpersand) { + const lhs_node = try macroIntToBool(c, node); + const op_token = try appendToken(c, .Keyword_and, "and"); + const rhs_node = try parseCBitOrExpr(c, m, scope); + const op_node = try c.arena.create(ast.Node.SimpleInfixOp); + op_node.* = .{ + .base = .{ .tag = .BoolAnd }, + .op_token = op_token, + .lhs = lhs_node, + .rhs = try macroIntToBool(c, rhs_node), + }; + node = &op_node.base; + } + m.i -= 1; + return node; +} + +fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + var node = try parseCBitXorExpr(c, m, scope); + while (m.next().? == .Pipe) { + const lhs_node = try macroBoolToInt(c, node); + const op_token = try appendToken(c, .Pipe, "|"); + const rhs_node = try parseCBitXorExpr(c, m, scope); + const op_node = try c.arena.create(ast.Node.SimpleInfixOp); + op_node.* = .{ + .base = .{ .tag = .BitOr }, + .op_token = op_token, + .lhs = lhs_node, + .rhs = try macroBoolToInt(c, rhs_node), + }; + node = &op_node.base; + } + m.i -= 1; + return node; +} + +fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + var node = try parseCBitAndExpr(c, m, scope); + while (m.next().? == .Caret) { + const lhs_node = try macroBoolToInt(c, node); + const op_token = try appendToken(c, .Caret, "^"); + const rhs_node = try parseCBitAndExpr(c, m, scope); + const op_node = try c.arena.create(ast.Node.SimpleInfixOp); + op_node.* = .{ + .base = .{ .tag = .BitXor }, + .op_token = op_token, + .lhs = lhs_node, + .rhs = try macroBoolToInt(c, rhs_node), + }; + node = &op_node.base; + } + m.i -= 1; + return node; +} + +fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + var node = try parseCEqExpr(c, m, scope); + while (m.next().? == .Ampersand) { + const lhs_node = try macroBoolToInt(c, node); + const op_token = try appendToken(c, .Ampersand, "&"); + const rhs_node = try parseCEqExpr(c, m, scope); + const op_node = try c.arena.create(ast.Node.SimpleInfixOp); + op_node.* = .{ + .base = .{ .tag = .BitAnd }, + .op_token = op_token, + .lhs = lhs_node, + .rhs = try macroBoolToInt(c, rhs_node), + }; + node = &op_node.base; + } + m.i -= 1; + return node; +} + +fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + var node = try parseCRelExpr(c, m, scope); while (true) { var op_token: ast.TokenIndex = undefined; var op_id: ast.Node.Tag = undefined; - var bool_op = false; - switch (m.next().?) { - .Period => { - if (m.next().? != .Identifier) { - try m.fail(c, "unable to translate C expr: expected identifier", .{}); - return error.ParseError; - } + switch (m.peek().?) { + .BangEqual => { + op_token = try appendToken(c, .BangEqual, "!="); + op_id = .BangEqual; + }, + .EqualEqual => { + op_token = try appendToken(c, .EqualEqual, "=="); + op_id = .EqualEqual; + }, + else => return node, + } + _ = m.next(); + const lhs_node = try macroBoolToInt(c, node); + const rhs_node = try parseCRelExpr(c, m, scope); + const op_node = try c.arena.create(ast.Node.SimpleInfixOp); + op_node.* = .{ + .base = .{ .tag = op_id }, + .op_token = op_token, + .lhs = lhs_node, + .rhs = try macroBoolToInt(c, rhs_node), + }; + node = &op_node.base; + } +} - node = try transCreateNodeFieldAccess(c, node, m.slice()); - continue; +fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + var node = try parseCShiftExpr(c, m, scope); + while (true) { + var op_token: ast.TokenIndex = undefined; + var op_id: ast.Node.Tag = undefined; + switch (m.peek().?) { + .AngleBracketRight => { + op_token = try appendToken(c, .AngleBracketRight, ">"); + op_id = .GreaterThan; }, - .Arrow => { - if (m.next().? != .Identifier) { - try m.fail(c, "unable to translate C expr: expected identifier", .{}); - return error.ParseError; - } - const deref = try transCreateNodePtrDeref(c, node); - node = try transCreateNodeFieldAccess(c, deref, m.slice()); - continue; + .AngleBracketRightEqual => { + op_token = try appendToken(c, .AngleBracketRightEqual, ">="); + op_id = .GreaterOrEqual; }, + .AngleBracketLeft => { + op_token = try appendToken(c, .AngleBracketLeft, "<"); + op_id = .LessThan; + }, + .AngleBracketLeftEqual => { + op_token = try appendToken(c, .AngleBracketLeftEqual, "<="); + op_id = .LessOrEqual; + }, + else => return node, + } + _ = m.next(); + const lhs_node = try macroBoolToInt(c, node); + const rhs_node = try parseCShiftExpr(c, m, scope); + const op_node = try c.arena.create(ast.Node.SimpleInfixOp); + op_node.* = .{ + .base = .{ .tag = op_id }, + .op_token = op_token, + .lhs = lhs_node, + .rhs = try macroBoolToInt(c, rhs_node), + }; + node = &op_node.base; + } +} + +fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + var node = try parseCAddSubExpr(c, m, scope); + while (true) { + var op_token: ast.TokenIndex = undefined; + var op_id: ast.Node.Tag = undefined; + switch (m.peek().?) { + .AngleBracketAngleBracketLeft => { + op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<"); + op_id = .BitShiftLeft; + }, + .AngleBracketAngleBracketRight => { + op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>"); + op_id = .BitShiftRight; + }, + else => return node, + } + _ = m.next(); + const lhs_node = try macroBoolToInt(c, node); + const rhs_node = try parseCAddSubExpr(c, m, scope); + const op_node = try c.arena.create(ast.Node.SimpleInfixOp); + op_node.* = .{ + .base = .{ .tag = op_id }, + .op_token = op_token, + .lhs = lhs_node, + .rhs = try macroBoolToInt(c, rhs_node), + }; + node = &op_node.base; + } +} + +fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + var node = try parseCMulExpr(c, m, scope); + while (true) { + var op_token: ast.TokenIndex = undefined; + var op_id: ast.Node.Tag = undefined; + switch (m.peek().?) { + .Plus => { + op_token = try appendToken(c, .Plus, "+"); + op_id = .Add; + }, + .Minus => { + op_token = try appendToken(c, .Minus, "-"); + op_id = .Sub; + }, + else => return node, + } + _ = m.next(); + const lhs_node = try macroBoolToInt(c, node); + const rhs_node = try parseCMulExpr(c, m, scope); + const op_node = try c.arena.create(ast.Node.SimpleInfixOp); + op_node.* = .{ + .base = .{ .tag = op_id }, + .op_token = op_token, + .lhs = lhs_node, + .rhs = try macroBoolToInt(c, rhs_node), + }; + node = &op_node.base; + } +} + +fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + var node = try parseCUnaryExpr(c, m, scope); + while (true) { + var op_token: ast.TokenIndex = undefined; + var op_id: ast.Node.Tag = undefined; + switch (m.next().?) { .Asterisk => { if (m.peek().? == .RParen) { // type *) @@ -6105,59 +6349,57 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. op_id = .BitShiftLeft; } }, - .AngleBracketAngleBracketLeft => { - op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<"); - op_id = .BitShiftLeft; + .Slash => { + op_id = .Div; + op_token = try appendToken(c, .Slash, "/"); }, - .AngleBracketAngleBracketRight => { - op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>"); - op_id = .BitShiftRight; + .Percent => { + op_id = .Mod; + op_token = try appendToken(c, .Percent, "%"); }, - .Pipe => { - op_token = try appendToken(c, .Pipe, "|"); - op_id = .BitOr; + else => { + m.i -= 1; + return node; }, - .Ampersand => { - op_token = try appendToken(c, .Ampersand, "&"); - op_id = .BitAnd; + } + const lhs_node = try macroBoolToInt(c, node); + const rhs_node = try parseCUnaryExpr(c, m, scope); + const op_node = try c.arena.create(ast.Node.SimpleInfixOp); + op_node.* = .{ + .base = .{ .tag = op_id }, + .op_token = op_token, + .lhs = lhs_node, + .rhs = try macroBoolToInt(c, rhs_node), + }; + node = &op_node.base; + } +} + +fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { + var node = try parseCPrimaryExpr(c, m, scope); + while (true) { + switch (m.next().?) { + .Period => { + if (m.next().? != .Identifier) { + try m.fail(c, "unable to translate C expr: expected identifier", .{}); + return error.ParseError; + } + + node = try transCreateNodeFieldAccess(c, node, m.slice()); + continue; }, - .Plus => { - op_token = try appendToken(c, .Plus, "+"); - op_id = .Add; - }, - .Minus => { - op_token = try appendToken(c, .Minus, "-"); - op_id = .Sub; - }, - .AmpersandAmpersand => { - op_token = try appendToken(c, .Keyword_and, "and"); - op_id = .BoolAnd; - bool_op = true; - }, - .PipePipe => { - op_token = try appendToken(c, .Keyword_or, "or"); - op_id = .BoolOr; - bool_op = true; - }, - .AngleBracketRight => { - op_token = try appendToken(c, .AngleBracketRight, ">"); - op_id = .GreaterThan; - }, - .AngleBracketRightEqual => { - op_token = try appendToken(c, .AngleBracketRightEqual, ">="); - op_id = .GreaterOrEqual; - }, - .AngleBracketLeft => { - op_token = try appendToken(c, .AngleBracketLeft, "<"); - op_id = .LessThan; - }, - .AngleBracketLeftEqual => { - op_token = try appendToken(c, .AngleBracketLeftEqual, "<="); - op_id = .LessOrEqual; + .Arrow => { + if (m.next().? != .Identifier) { + try m.fail(c, "unable to translate C expr: expected identifier", .{}); + return error.ParseError; + } + const deref = try transCreateNodePtrDeref(c, node); + node = try transCreateNodeFieldAccess(c, deref, m.slice()); + continue; }, .LBracket => { const arr_node = try transCreateNodeArrayAccess(c, node); - arr_node.index_expr = try parseCPrefixOpExpr(c, m, scope); + arr_node.index_expr = try parseCExpr(c, m, scope); arr_node.rtoken = try appendToken(c, .RBracket, "]"); node = &arr_node.base; if (m.next().? != .RBracket) { @@ -6171,7 +6413,7 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. var call_params = std.ArrayList(*ast.Node).init(c.gpa); defer call_params.deinit(); while (true) { - const arg = try parseCPrefixOpExpr(c, m, scope); + const arg = try parseCCondExpr(c, m, scope); try call_params.append(arg); switch (m.next().?) { .Comma => _ = try appendToken(c, .Comma, ","), @@ -6204,7 +6446,7 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. defer init_vals.deinit(); while (true) { - const val = try parseCPrefixOpExpr(c, m, scope); + const val = try parseCCondExpr(c, m, scope); try init_vals.append(val); switch (m.next().?) { .Comma => _ = try appendToken(c, .Comma, ","), @@ -6239,90 +6481,57 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. node = &zero_init_call.base; continue; }, - .BangEqual => { - op_token = try appendToken(c, .BangEqual, "!="); - op_id = .BangEqual; - }, - .EqualEqual => { - op_token = try appendToken(c, .EqualEqual, "=="); - op_id = .EqualEqual; - }, - .Slash => { - op_id = .Div; - op_token = try appendToken(c, .Slash, "/"); - }, - .Percent => { - op_id = .Mod; - op_token = try appendToken(c, .Percent, "%"); - }, - .StringLiteral => { - op_id = .ArrayCat; - op_token = try appendToken(c, .PlusPlus, "++"); - - m.i -= 1; - }, - .Identifier => { - op_id = .ArrayCat; - op_token = try appendToken(c, .PlusPlus, "++"); - - m.i -= 1; + .PlusPlus, .MinusMinus => { + try m.fail(c, "TODO postfix inc/dec expr", .{}); + return error.ParseError; }, else => { m.i -= 1; return node; }, } - const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt; - const lhs_node = try cast_fn(c, node); - const rhs_node = try parseCPrefixOpExpr(c, m, scope); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = op_id }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = try cast_fn(c, rhs_node), - }; - node = &op_node.base; } } -fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { switch (m.next().?) { .Bang => { const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!"); - node.rhs = try parseCPrefixOpExpr(c, m, scope); + node.rhs = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope)); return &node.base; }, .Minus => { const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-"); - node.rhs = try parseCPrefixOpExpr(c, m, scope); + node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); return &node.base; }, - .Plus => return try parseCPrefixOpExpr(c, m, scope), + .Plus => return try parseCUnaryExpr(c, m, scope), .Tilde => { const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~"); - node.rhs = try parseCPrefixOpExpr(c, m, scope); + node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); return &node.base; }, .Asterisk => { - const node = try parseCPrefixOpExpr(c, m, scope); + const node = try macroGroup(c, try parseCUnaryExpr(c, m, scope)); return try transCreateNodePtrDeref(c, node); }, .Ampersand => { const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&"); - node.rhs = try parseCPrefixOpExpr(c, m, scope); + node.rhs = try macroGroup(c, try parseCUnaryExpr(c, m, scope)); return &node.base; }, .Keyword_sizeof => { const inner = if (m.peek().? == .LParen) blk: { _ = m.next(); - const inner = try parseCExpr(c, m, scope); + // C grammar says this should be 'type-name' but we have to + // use parseCMulExpr to correctly handle pointer types. + const inner = try parseCMulExpr(c, m, scope); if (m.next().? != .RParen) { try m.fail(c, "unable to translate C expr: expected ')'", .{}); return error.ParseError; } break :blk inner; - } else try parseCPrefixOpExpr(c, m, scope); + } else try parseCUnaryExpr(c, m, scope); //(@import("std").meta.sizeof(dest, x)) const import_fn_call = try c.createBuiltinCall("@import", 1); @@ -6344,7 +6553,9 @@ fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. try m.fail(c, "unable to translate C expr: expected '('", .{}); return error.ParseError; } - const inner = try parseCExpr(c, m, scope); + // C grammar says this should be 'type-name' but we have to + // use parseCMulExpr to correctly handle pointer types. + const inner = try parseCMulExpr(c, m, scope); if (m.next().? != .RParen) { try m.fail(c, "unable to translate C expr: expected ')'", .{}); return error.ParseError; @@ -6355,9 +6566,13 @@ fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. builtin_call.rparen_token = try appendToken(c, .RParen, ")"); return &builtin_call.base; }, + .PlusPlus, .MinusMinus => { + try m.fail(c, "TODO unary inc/dec expr", .{}); + return error.ParseError; + }, else => { m.i -= 1; - return try parseCSuffixOpExpr(c, m, scope); + return try parseCPostfixExpr(c, m, scope); }, } } diff --git a/test/translate_c.zig b/test/translate_c.zig index 83fdda8d81..4182dbaa13 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3,6 +3,22 @@ const std = @import("std"); const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("macro expressions respect C operator precedence", + \\#define FOO *((foo) + 2) + \\#define VALUE (1 + 2 * 3 + 4 * 5 + 6 << 7 | 8 == 9) + \\#define _AL_READ3BYTES(p) ((*(unsigned char *)(p)) \ + \\ | (*((unsigned char *)(p) + 1) << 8) \ + \\ | (*((unsigned char *)(p) + 2) << 16)) + , &[_][]const u8{ + \\pub const FOO = (foo + 2).*; + , + \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9); + , + \\pub inline fn _AL_READ3BYTES(p: anytype) @TypeOf(((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16)) { + \\ return ((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16); + \\} + }); + cases.add("extern variable in block scope", \\float bar; \\int foo() { @@ -2978,7 +2994,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("string concatenation in macros: three strings", \\#define FOO "a" "b" "c" , &[_][]const u8{ - \\pub const FOO = "a" ++ ("b" ++ "c"); + \\pub const FOO = "a" ++ "b" ++ "c"; }); cases.add("multibyte character literals",