diff options
| -rw-r--r-- | src-self-hosted/c_tokenizer.zig | 88 | ||||
| -rw-r--r-- | src-self-hosted/translate_c.zig | 330 | ||||
| -rw-r--r-- | test/translate_c.zig | 6 |
3 files changed, 268 insertions, 156 deletions
diff --git a/src-self-hosted/c_tokenizer.zig b/src-self-hosted/c_tokenizer.zig index cc862a0e67..ba44467565 100644 --- a/src-self-hosted/c_tokenizer.zig +++ b/src-self-hosted/c_tokenizer.zig @@ -1,5 +1,8 @@ const std = @import("std"); const expect = std.testing.expect; +const ZigClangSourceLocation = @import("clang.zig").ZigClangSourceLocation; +const Context = @import("translate_c.zig").Context; +const failDecl = @import("translate_c.zig").failDecl; pub const TokenList = std.SegmentedList(CToken, 32); @@ -31,6 +34,8 @@ pub const CToken = struct { LBrace, RBrace, Pipe, + QuestionMark, + Colon, }; pub const NumLitSuffix = enum { @@ -44,13 +49,13 @@ pub const CToken = struct { }; }; -pub fn tokenizeCMacro(tl: *TokenList, chars: [*:0]const u8) !void { +pub fn tokenizeCMacro(ctx: *Context, loc: ZigClangSourceLocation, name: []const u8, tl: *TokenList, chars: [*:0]const u8) !void { var index: usize = 0; var first = true; while (true) { - const tok = try next(chars, &index); + const tok = try next(ctx, loc, name, chars, &index); if (tok.id == .StrLit or tok.id == .CharLit) - try tl.push(try zigifyEscapeSequences(tl.allocator, tok)) + try tl.push(try zigifyEscapeSequences(ctx, loc, name, tl.allocator, tok)) else try tl.push(tok); if (tok.id == .Eof) @@ -68,7 +73,7 @@ pub fn tokenizeCMacro(tl: *TokenList, chars: [*:0]const u8) !void { } } -fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken { +fn zigifyEscapeSequences(ctx: *Context, loc: ZigClangSourceLocation, name: []const u8, allocator: *std.mem.Allocator, tok: CToken) !CToken { for (tok.bytes) |c| { if (c == '\\') { break; @@ -134,11 +139,11 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken { bytes[i] = '?'; }, 'u', 'U' => { - // TODO unicode escape sequences + try failDecl(ctx, loc, name, "macro tokenizing failed: TODO unicode escape sequences", .{}); return error.TokenizingFailed; }, else => { - // unknown escape sequence + try failDecl(ctx, loc, name, "macro tokenizing failed: unknown escape sequence", .{}); return error.TokenizingFailed; }, } @@ -156,19 +161,28 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken { .Hex => { switch (c) { '0'...'9' => { - num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed; + num = std.math.mul(u8, num, 16) catch { + try failDecl(ctx, loc, name, "macro tokenizing failed: hex literal overflowed", .{}); + return error.TokenizingFailed; + }; num += c - '0'; }, 'a'...'f' => { - num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed; + num = std.math.mul(u8, num, 16) catch { + try failDecl(ctx, loc, name, "macro tokenizing failed: hex literal overflowed", .{}); + return error.TokenizingFailed; + }; num += c - 'a' + 10; }, 'A'...'F' => { - num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed; + num = std.math.mul(u8, num, 16) catch { + try failDecl(ctx, loc, name, "macro tokenizing failed: hex literal overflowed", .{}); + return error.TokenizingFailed; + }; num += c - 'A' + 10; }, else => { - i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2}); + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); num = 0; if (c == '\\') state = .Escape @@ -183,14 +197,17 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken { switch (c) { '0'...'7' => { count += 1; - num = std.math.mul(u8, num, 8) catch return error.TokenizingFailed; + num = std.math.mul(u8, num, 8) catch { + try failDecl(ctx, loc, name, "macro tokenizing failed: octal literal overflowed", .{}); + return error.TokenizingFailed; + }; num += c - '0'; if (count < 3) continue; }, else => {}, } - i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2}); + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); state = .Start; count = 0; num = 0; @@ -198,14 +215,14 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken { } } if (state == .Hex or state == .Octal) - i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2}); + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); return CToken{ .id = tok.id, .bytes = bytes[0..i], }; } -fn next(chars: [*:0]const u8, i: *usize) !CToken { +fn next(ctx: *Context, loc: ZigClangSourceLocation, name: []const u8, chars: [*:0]const u8, i: *usize) !CToken { var state: enum { Start, GotLt, @@ -276,7 +293,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { .String, .ExpSign, .FloatExpFirst, - => return error.TokenizingFailed, + => { + try failDecl(ctx, loc, name, "macro tokenizing failed: unexpected EOF", .{}); + return error.TokenizingFailed; + }, } } switch (state) { @@ -365,7 +385,18 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { result.id = .Pipe; state = .Done; }, - else => return error.TokenizingFailed, + '?' => { + result.id = .QuestionMark; + state = .Done; + }, + ':' => { + result.id = .Colon; + state = .Done; + }, + else => { + try failDecl(ctx, loc, name, "macro tokenizing failed: unexpected character '{c}'", .{c}); + return error.TokenizingFailed; + }, } }, .Done => return result, @@ -423,7 +454,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { '0'...'9' => { state = .FloatExp; }, - else => return error.TokenizingFailed, + else => { + try failDecl(ctx, loc, name, "macro tokenizing failed: expected a digit or '+' or '-'", .{}); + return error.TokenizingFailed; + }, } }, .FloatExpFirst => { @@ -431,7 +465,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { '0'...'9' => { state = .FloatExp; }, - else => return error.TokenizingFailed, + else => { + try failDecl(ctx, loc, name, "macro tokenizing failed: expected a digit", .{}); + return error.TokenizingFailed; + }, } }, .FloatExp => { @@ -508,7 +545,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { .Octal => { switch (c) { '0'...'7' => {}, - '8', '9' => return error.TokenizingFailed, + '8', '9' => { + try failDecl(ctx, loc, name, "macro tokenizing failed: invalid digit '{c}' in octal number", .{c}); + return error.TokenizingFailed; + }, else => { result.bytes = chars[begin_index..i.*]; return result; @@ -539,7 +579,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { .Bin => { switch (c) { '0'...'1' => {}, - '2'...'9' => return error.TokenizingFailed, + '2'...'9' => { + try failDecl(ctx, loc, name, "macro tokenizing failed: invalid digit '{c}' in binary number", .{c}); + return error.TokenizingFailed; + }, 'u', 'U' => { // marks the number literal as unsigned state = .NumLitIntSuffixU; @@ -671,7 +714,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { '\n', '\r' => { state = .Start; }, - else => return error.TokenizingFailed, + else => { + try failDecl(ctx, loc, name, "macro tokenizing failed: expected whitespace", .{}); + return error.TokenizingFailed; + }, } }, } diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index e1d6c9a814..4fc70f3af8 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -182,7 +182,7 @@ const Scope = struct { } }; -const Context = struct { +pub const Context = struct { tree: *ast.Tree, source_buffer: *std.Buffer, err: Error, @@ -3921,7 +3921,7 @@ fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []cons _ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, args_prefix ++ args); } -fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: var) !void { +pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: var) !void { // const name = @compileError(msg); const const_tok = try appendToken(c, .Keyword_const, "const"); const name_tok = try appendIdentifier(c, name); @@ -4091,10 +4091,9 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { continue; } const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc); - ctok.tokenizeCMacro(&tok_list, begin_c) catch |err| switch (err) { + ctok.tokenizeCMacro(c, begin_loc, checked_name, &tok_list, begin_c) catch |err| switch (err) { error.OutOfMemory => |e| return e, else => { - try failDecl(c, begin_loc, checked_name, "unable to tokenize macro definition", .{}); continue; }, }; @@ -4127,9 +4126,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { transMacroFnDefine(c, &tok_it, checked_name, begin_loc) else transMacroDefine(c, &tok_it, checked_name, begin_loc)) catch |err| switch (err) { - error.UnsupportedTranslation, - error.ParseError, - => try failDecl(c, begin_loc, checked_name, "unable to translate macro", .{}), + error.ParseError => continue, error.OutOfMemory => |e| return e, }; }, @@ -4139,20 +4136,19 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { } fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void { - const rp = makeRestorePoint(c); const scope = &c.global_scope.base; const node = try transCreateNodeVarDecl(c, true, true, name); node.eq_token = try appendToken(c, .Equal, "="); - node.init_node = try parseCExpr(rp, it, source_loc, scope); + node.init_node = try parseCExpr(c, it, source_loc, scope); const last = it.next().?; if (last.id != .Eof) - return revertAndWarn( - rp, - error.UnsupportedTranslation, + return failDecl( + c, source_loc, - "unable to translate C expr, unexpected token: {}", + name, + "unable to translate C expr: unexpected token {}", .{last.id}, ); @@ -4161,7 +4157,6 @@ fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, } fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void { - const rp = makeRestorePoint(c); const block_scope = try Scope.Block.init(c, &c.global_scope.base, null); const scope = &block_scope.base; @@ -4172,13 +4167,26 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u _ = try appendToken(c, .LParen, "("); if (it.next().?.id != .LParen) { - return error.ParseError; + return failDecl( + c, + source_loc, + name, + "unable to translate C expr: expected '('", + .{}, + ); } var fn_params = ast.Node.FnProto.ParamList.init(c.a()); while (true) { const param_tok = it.next().?; - if (param_tok.id != .Identifier) - return error.ParseError; + if (param_tok.id != .Identifier) { + return failDecl( + c, + source_loc, + name, + "unable to translate C expr: expected identifier", + .{}, + ); + } const checked_name = if (try scope.createAlias(c, param_tok.bytes)) |alias| blk: { try block_scope.variables.push(.{ .name = param_tok.bytes, .alias = alias }); @@ -4213,7 +4221,13 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u } if (it.next().?.id != .RParen) { - return error.ParseError; + return failDecl( + c, + source_loc, + name, + "unable to translate C expr: expected ')'", + .{}, + ); } _ = try appendToken(c, .RParen, ")"); @@ -4241,14 +4255,14 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u const block = try transCreateNodeBlock(c, null); const return_expr = try transCreateNodeReturnExpr(c); - const expr = try parseCExpr(rp, it, source_loc, scope); + const expr = try parseCExpr(c, it, source_loc, scope); const last = it.next().?; if (last.id != .Eof) - return revertAndWarn( - rp, - error.UnsupportedTranslation, + return failDecl( + c, source_loc, - "unable to translate C expr, unexpected token: {}", + name, + "unable to translate C expr: unexpected token {}", .{last.id}, ); _ = try appendToken(c, .Semicolon, ";"); @@ -4261,31 +4275,28 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u _ = try c.global_scope.macro_table.put(name, &fn_proto.base); } -const ParseError = Error || error{ - ParseError, - UnsupportedTranslation, -}; +const ParseError = Error || error{ParseError}; -fn parseCExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { - return parseCPrefixOpExpr(rp, it, source_loc, scope); +fn parseCExpr(c: *Context, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { + return parseCPrefixOpExpr(c, it, source_loc, scope); } -fn parseCNumLit(rp: RestorePoint, tok: *CToken, source_loc: ZigClangSourceLocation) ParseError!*ast.Node { +fn parseCNumLit(c: *Context, tok: *CToken, source_loc: ZigClangSourceLocation) ParseError!*ast.Node { if (tok.id == .NumLitInt) { if (tok.num_lit_suffix == .None) { if (tok.bytes.len > 2 and tok.bytes[0] == '0') { switch (tok.bytes[1]) { '0'...'7' => { // octal - return transCreateNodeInt(rp.c, try std.fmt.allocPrint(rp.c.a(), "0o{}", .{tok.bytes})); + return transCreateNodeInt(c, try std.fmt.allocPrint(c.a(), "0o{}", .{tok.bytes})); }, else => {}, } } - return transCreateNodeInt(rp.c, tok.bytes); + return transCreateNodeInt(c, tok.bytes); } - const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); - try cast_node.params.push(try transCreateNodeIdentifier(rp.c, switch (tok.num_lit_suffix) { + const cast_node = try transCreateNodeBuiltinFnCall(c, "@as"); + try cast_node.params.push(try transCreateNodeIdentifier(c, switch (tok.num_lit_suffix) { .U => "c_uint", .L => "c_long", .LU => "c_ulong", @@ -4293,62 +4304,55 @@ fn parseCNumLit(rp: RestorePoint, tok: *CToken, source_loc: ZigClangSourceLocati .LLU => "c_ulonglong", else => unreachable, })); - _ = try appendToken(rp.c, .Comma, ","); - try cast_node.params.push(try transCreateNodeInt(rp.c, tok.bytes)); - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + _ = try appendToken(c, .Comma, ","); + try cast_node.params.push(try transCreateNodeInt(c, tok.bytes)); + cast_node.rparen_token = try appendToken(c, .RParen, ")"); return &cast_node.base; } else if (tok.id == .NumLitFloat) { if (tok.num_lit_suffix == .None) { - return transCreateNodeFloat(rp.c, tok.bytes); + return transCreateNodeFloat(c, tok.bytes); } - const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); - try cast_node.params.push(try transCreateNodeIdentifier(rp.c, switch (tok.num_lit_suffix) { + const cast_node = try transCreateNodeBuiltinFnCall(c, "@as"); + try cast_node.params.push(try transCreateNodeIdentifier(c, switch (tok.num_lit_suffix) { .F => "f32", .L => "f64", else => unreachable, })); - _ = try appendToken(rp.c, .Comma, ","); - try cast_node.params.push(try transCreateNodeFloat(rp.c, tok.bytes)); - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + _ = try appendToken(c, .Comma, ","); + try cast_node.params.push(try transCreateNodeFloat(c, tok.bytes)); + cast_node.rparen_token = try appendToken(c, .RParen, ")"); return &cast_node.base; - } else - return revertAndWarn( - rp, - error.ParseError, - source_loc, - "expected number literal", - .{}, - ); + } else unreachable; } -fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { +fn parseCPrimaryExpr(c: *Context, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { const tok = it.next().?; switch (tok.id) { .CharLit => { - const token = try appendToken(rp.c, .CharLiteral, tok.bytes); - const node = try rp.c.a().create(ast.Node.CharLiteral); + const token = try appendToken(c, .CharLiteral, tok.bytes); + const node = try c.a().create(ast.Node.CharLiteral); node.* = ast.Node.CharLiteral{ .token = token, }; return &node.base; }, .StrLit => { - const token = try appendToken(rp.c, .StringLiteral, tok.bytes); - const node = try rp.c.a().create(ast.Node.StringLiteral); + const token = try appendToken(c, .StringLiteral, tok.bytes); + const node = try c.a().create(ast.Node.StringLiteral); node.* = ast.Node.StringLiteral{ .token = token, }; return &node.base; }, .NumLitInt, .NumLitFloat => { - return parseCNumLit(rp, tok, source_loc); + return parseCNumLit(c, tok, source_loc); }, .Identifier => { const name = if (scope.getAlias(tok.bytes)) |a| a else tok.bytes; - return transCreateNodeIdentifier(rp.c, name); + return transCreateNodeIdentifier(c, name); }, .LParen => { - const inner_node = try parseCExpr(rp, it, source_loc, scope); + const inner_node = try parseCExpr(c, it, source_loc, scope); if (it.peek().?.id == .RParen) { _ = it.next(); @@ -4359,18 +4363,19 @@ fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: } // hack to get zig fmt to render a comma in builtin calls - _ = try appendToken(rp.c, .Comma, ","); + _ = try appendToken(c, .Comma, ","); - const node_to_cast = try parseCExpr(rp, it, source_loc, scope); + const node_to_cast = try parseCExpr(c, it, source_loc, scope); if (it.next().?.id != .RParen) { - return revertAndWarn( - rp, - error.ParseError, + try failDecl( + c, source_loc, - "unable to translate C expr", + it.list.at(0).*.bytes, + "unable to translate C expr: expected ')''", .{}, ); + return error.ParseError; } //if (@typeId(@TypeOf(x)) == .Pointer) @@ -4380,114 +4385,133 @@ fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: //else // @as(dest, x) - const if_1 = try transCreateNodeIf(rp.c); - const type_id_1 = try transCreateNodeBuiltinFnCall(rp.c, "@typeId"); - const type_of_1 = try transCreateNodeBuiltinFnCall(rp.c, "@TypeOf"); + const if_1 = try transCreateNodeIf(c); + const type_id_1 = try transCreateNodeBuiltinFnCall(c, "@typeId"); + const type_of_1 = try transCreateNodeBuiltinFnCall(c, "@TypeOf"); try type_id_1.params.push(&type_of_1.base); try type_of_1.params.push(node_to_cast); - type_of_1.rparen_token = try appendToken(rp.c, .LParen, ")"); - type_id_1.rparen_token = try appendToken(rp.c, .LParen, ")"); + type_of_1.rparen_token = try appendToken(c, .LParen, ")"); + type_id_1.rparen_token = try appendToken(c, .LParen, ")"); - const cmp_1 = try rp.c.a().create(ast.Node.InfixOp); + const cmp_1 = try c.a().create(ast.Node.InfixOp); cmp_1.* = .{ - .op_token = try appendToken(rp.c, .EqualEqual, "=="), + .op_token = try appendToken(c, .EqualEqual, "=="), .lhs = &type_id_1.base, .op = .EqualEqual, - .rhs = try transCreateNodeEnumLiteral(rp.c, "Pointer"), + .rhs = try transCreateNodeEnumLiteral(c, "Pointer"), }; if_1.condition = &cmp_1.base; - _ = try appendToken(rp.c, .LParen, ")"); + _ = try appendToken(c, .LParen, ")"); - const ptr_cast = try transCreateNodeBuiltinFnCall(rp.c, "@ptrCast"); + const ptr_cast = try transCreateNodeBuiltinFnCall(c, "@ptrCast"); try ptr_cast.params.push(inner_node); try ptr_cast.params.push(node_to_cast); - ptr_cast.rparen_token = try appendToken(rp.c, .LParen, ")"); + ptr_cast.rparen_token = try appendToken(c, .LParen, ")"); if_1.body = &ptr_cast.base; - const else_1 = try transCreateNodeElse(rp.c); + const else_1 = try transCreateNodeElse(c); if_1.@"else" = else_1; - const if_2 = try transCreateNodeIf(rp.c); - const type_id_2 = try transCreateNodeBuiltinFnCall(rp.c, "@typeId"); - const type_of_2 = try transCreateNodeBuiltinFnCall(rp.c, "@TypeOf"); + const if_2 = try transCreateNodeIf(c); + const type_id_2 = try transCreateNodeBuiltinFnCall(c, "@typeId"); + const type_of_2 = try transCreateNodeBuiltinFnCall(c, "@TypeOf"); try type_id_2.params.push(&type_of_2.base); try type_of_2.params.push(node_to_cast); - type_of_2.rparen_token = try appendToken(rp.c, .LParen, ")"); - type_id_2.rparen_token = try appendToken(rp.c, .LParen, ")"); + type_of_2.rparen_token = try appendToken(c, .LParen, ")"); + type_id_2.rparen_token = try appendToken(c, .LParen, ")"); - const cmp_2 = try rp.c.a().create(ast.Node.InfixOp); + const cmp_2 = try c.a().create(ast.Node.InfixOp); cmp_2.* = .{ - .op_token = try appendToken(rp.c, .EqualEqual, "=="), + .op_token = try appendToken(c, .EqualEqual, "=="), .lhs = &type_id_2.base, .op = .EqualEqual, - .rhs = try transCreateNodeEnumLiteral(rp.c, "Int"), + .rhs = try transCreateNodeEnumLiteral(c, "Int"), }; if_2.condition = &cmp_2.base; else_1.body = &if_2.base; - _ = try appendToken(rp.c, .LParen, ")"); + _ = try appendToken(c, .LParen, ")"); - const int_to_ptr = try transCreateNodeBuiltinFnCall(rp.c, "@intToPtr"); + const int_to_ptr = try transCreateNodeBuiltinFnCall(c, "@intToPtr"); try int_to_ptr.params.push(inner_node); try int_to_ptr.params.push(node_to_cast); - int_to_ptr.rparen_token = try appendToken(rp.c, .LParen, ")"); + int_to_ptr.rparen_token = try appendToken(c, .LParen, ")"); if_2.body = &int_to_ptr.base; - const else_2 = try transCreateNodeElse(rp.c); + const else_2 = try transCreateNodeElse(c); if_2.@"else" = else_2; - const as = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + const as = try transCreateNodeBuiltinFnCall(c, "@as"); try as.params.push(inner_node); try as.params.push(node_to_cast); - as.rparen_token = try appendToken(rp.c, .LParen, ")"); + as.rparen_token = try appendToken(c, .LParen, ")"); else_2.body = &as.base; return &if_1.base; }, - else => return revertAndWarn( - rp, - error.UnsupportedTranslation, - source_loc, - "unable to translate C expr, unexpected token: {}", - .{tok.id}, - ), + else => { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: unexpected token {}", + .{tok.id}, + ); + return error.ParseError; + }, } } -fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { - var node = try parseCPrimaryExpr(rp, it, source_loc, scope); +fn parseCSuffixOpExpr(c: *Context, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { + var node = try parseCPrimaryExpr(c, it, source_loc, scope); while (true) { const tok = it.next().?; switch (tok.id) { .Dot => { const name_tok = it.next().?; - if (name_tok.id != .Identifier) + if (name_tok.id != .Identifier) { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: expected identifier", + .{}, + ); return error.ParseError; + } - node = try transCreateNodeFieldAccess(rp.c, node, name_tok.bytes); + node = try transCreateNodeFieldAccess(c, node, name_tok.bytes); }, .Arrow => { const name_tok = it.next().?; - if (name_tok.id != .Identifier) + if (name_tok.id != .Identifier) { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: expected identifier", + .{}, + ); return error.ParseError; + } - const deref = try transCreateNodePtrDeref(rp.c, node); - node = try transCreateNodeFieldAccess(rp.c, deref, name_tok.bytes); + const deref = try transCreateNodePtrDeref(c, node); + node = try transCreateNodeFieldAccess(c, deref, name_tok.bytes); }, .Asterisk => { if (it.peek().?.id == .RParen) { // type *) // hack to get zig fmt to render a comma in builtin calls - _ = try appendToken(rp.c, .Comma, ","); + _ = try appendToken(c, .Comma, ","); - const ptr = try transCreateNodePtrType(rp.c, false, false, .Identifier); + const ptr = try transCreateNodePtrType(c, false, false, .Identifier); ptr.rhs = node; return &ptr.base; } else { // expr * expr - const op_token = try appendToken(rp.c, .Asterisk, "*"); - const rhs = try parseCPrimaryExpr(rp, it, source_loc, scope); - const mul_node = try rp.c.a().create(ast.Node.InfixOp); + const op_token = try appendToken(c, .Asterisk, "*"); + const rhs = try parseCPrimaryExpr(c, it, source_loc, scope); + const mul_node = try c.a().create(ast.Node.InfixOp); mul_node.* = .{ .op_token = op_token, .lhs = node, @@ -4498,9 +4522,9 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc } }, .Shl => { - const op_token = try appendToken(rp.c, .AngleBracketAngleBracketLeft, "<<"); - const rhs = try parseCExpr(rp, it, source_loc, scope); - const bitshift_node = try rp.c.a().create(ast.Node.InfixOp); + const op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<"); + const rhs = try parseCExpr(c, it, source_loc, scope); + const bitshift_node = try c.a().create(ast.Node.InfixOp); bitshift_node.* = .{ .op_token = op_token, .lhs = node, @@ -4510,9 +4534,9 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc node = &bitshift_node.base; }, .Pipe => { - const op_token = try appendToken(rp.c, .Pipe, "|"); - const rhs = try parseCExpr(rp, it, source_loc, scope); - const or_node = try rp.c.a().create(ast.Node.InfixOp); + const op_token = try appendToken(c, .Pipe, "|"); + const rhs = try parseCExpr(c, it, source_loc, scope); + const or_node = try c.a().create(ast.Node.InfixOp); or_node.* = .{ .op_token = op_token, .lhs = node, @@ -4522,29 +4546,65 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc node = &or_node.base; }, .LBrace => { - const arr_node = try transCreateNodeArrayAccess(rp.c, node); - arr_node.op.ArrayAccess = try parseCExpr(rp, it, source_loc, scope); - arr_node.rtoken = try appendToken(rp.c, .RBrace, "]"); + const arr_node = try transCreateNodeArrayAccess(c, node); + arr_node.op.ArrayAccess = try parseCExpr(c, it, source_loc, scope); + arr_node.rtoken = try appendToken(c, .RBrace, "]"); node = &arr_node.base; - if (it.next().?.id != .RBrace) + if (it.next().?.id != .RBrace) { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: expected ']'", + .{}, + ); return error.ParseError; + } }, .LParen => { - const call_node = try transCreateNodeFnCall(rp.c, node); + const call_node = try transCreateNodeFnCall(c, node); while (true) { - const arg = try parseCExpr(rp, it, source_loc, scope); + const arg = try parseCExpr(c, it, source_loc, scope); try call_node.op.Call.params.push(arg); const next = it.next().?; if (next.id == .Comma) - _ = try appendToken(rp.c, .Comma, ",") + _ = try appendToken(c, .Comma, ",") else if (next.id == .RParen) break - else + else { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: expected ',' or ')'", + .{}, + ); return error.ParseError; + } } - call_node.rtoken = try appendToken(rp.c, .RParen, ")"); + call_node.rtoken = try appendToken(c, .RParen, ")"); node = &call_node.base; }, + .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, it, source_loc, scope); + if (it.next().?.id != .Colon) { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: expected ':'", + .{}, + ); + return error.ParseError; + } + if_node.@"else" = try transCreateNodeElse(c); + if_node.@"else".?.body = try parseCPrimaryExpr(c, it, source_loc, scope); + node = &if_node.base; + }, else => { _ = it.prev(); return node; @@ -4553,32 +4613,32 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc } } -fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { +fn parseCPrefixOpExpr(c: *Context, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { const op_tok = it.next().?; switch (op_tok.id) { .Bang => { - const node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); - node.rhs = try parseCPrefixOpExpr(rp, it, source_loc, scope); + const node = try transCreateNodePrefixOp(c, .BoolNot, .Bang, "!"); + node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); return &node.base; }, .Minus => { - const node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-"); - node.rhs = try parseCPrefixOpExpr(rp, it, source_loc, scope); + const node = try transCreateNodePrefixOp(c, .Negation, .Minus, "-"); + node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); return &node.base; }, .Tilde => { - const node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~"); - node.rhs = try parseCPrefixOpExpr(rp, it, source_loc, scope); + const node = try transCreateNodePrefixOp(c, .BitNot, .Tilde, "~"); + node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); return &node.base; }, .Asterisk => { - const prefix_op_expr = try parseCPrefixOpExpr(rp, it, source_loc, scope); - return try transCreateNodePtrDeref(rp.c, prefix_op_expr); + const prefix_op_expr = try parseCPrefixOpExpr(c, it, source_loc, scope); + return try transCreateNodePtrDeref(c, prefix_op_expr); }, else => { _ = it.prev(); - return try parseCSuffixOpExpr(rp, it, source_loc, scope); + return try parseCSuffixOpExpr(c, it, source_loc, scope); }, } } diff --git a/test/translate_c.zig b/test/translate_c.zig index 452898b1be..9994e23d4c 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -2192,6 +2192,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("macro conditional operator", + \\#define FOO a ? b : c + , &[_][]const u8{ + \\pub const FOO = if (a) b else c; + }); + /////////////// Cases for only stage1 because stage2 behavior is better //////////////// cases.addC("Parameterless function prototypes", \\void foo() {} |
