Commit 75218d4765

Vexu <git@vexu.eu>
2019-12-15 13:44:11
translate-c-2 macros
1 parent c3724a6
Changed files (4)
src-self-hosted/c_tokenizer.zig
@@ -1,4 +1,5 @@
 const std = @import("std");
+const expect = std.testing.expect;
 
 pub const TokenList = std.SegmentedList(CToken, 32);
 
@@ -28,6 +29,7 @@ pub const CToken = struct {
 
     pub const NumLitSuffix = enum {
         None,
+        F,
         L,
         U,
         LU,
@@ -39,19 +41,18 @@ pub const CToken = struct {
 pub fn tokenizeCMacro(tl: *TokenList, chars: [*]const u8) !void {
     var index: usize = 0;
     while (true) {
-        const tok = try next(chars[index..], &index);
-        tl.push(tok);
+        const tok = try next(chars, &index);
+        try tl.push(tok);
         if (tok.id == .Eof)
             return;
     }
 }
 
-fn next(chars: [*]const u8, index: *usize) !CToken {
+fn next(chars: [*]const u8, i: *usize) !CToken {
     var state: enum {
         Start,
         GotLt,
-        ExpectChar,
-        ExpectEndQuot,
+        CharLit,
         OpenComment,
         Comment,
         CommentStar,
@@ -62,6 +63,7 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
         Octal,
         GotZero,
         Hex,
+        Bin,
         Float,
         ExpSign,
         FloatExp,
@@ -70,7 +72,6 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
         NumLitIntSuffixL,
         NumLitIntSuffixLL,
         NumLitIntSuffixUL,
-        GotLt,
     } = .Start;
 
     var result = CToken{
@@ -79,9 +80,10 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
     };
     var begin_index: usize = 0;
     var digits: u8 = 0;
-    var pre_escape = .Start;
+    var pre_escape = state;
 
-    for (chars[begin_index..]) |c, i| {
+    while (true) {
+        const c = chars[i.*];
         if (c == 0) {
             switch (state) {
                 .Start => {
@@ -90,22 +92,25 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
                 .Identifier,
                 .Decimal,
                 .Hex,
+                .Bin,
                 .Octal,
                 .GotZero,
+                .Float,
+                .FloatExp,
+                => {
+                    result.bytes = chars[begin_index..i.*];
+                    return result;
+                },
                 .NumLitIntSuffixU,
                 .NumLitIntSuffixL,
                 .NumLitIntSuffixUL,
                 .NumLitIntSuffixLL,
-                .Float,
-                .FloatExp,
                 .GotLt,
                 => {
                     return result;
                 },
-                .ExpectChar,
-                .ExpectEndQuot,
+                .CharLit,
                 .OpenComment,
-                .LineComment,
                 .Comment,
                 .CommentStar,
                 .Backslash,
@@ -115,20 +120,20 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
                 => return error.TokenizingFailed,
             }
         }
-        index.* += 1;
+        i.* += 1;
         switch (state) {
             .Start => {
                 switch (c) {
                     ' ', '\t', '\x0B', '\x0C' => {},
                     '\'' => {
-                        state = .ExpectChar;
+                        state = .CharLit;
                         result.id = .CharLit;
-                        begin_index = i;
+                        begin_index = i.* - 1;
                     },
                     '\"' => {
                         state = .String;
                         result.id = .StrLit;
-                        begin_index = i;
+                        begin_index = i.* - 1;
                     },
                     '/' => {
                         state = .OpenComment;
@@ -142,17 +147,17 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
                     'a'...'z', 'A'...'Z', '_' => {
                         state = .Identifier;
                         result.id = .Identifier;
-                        begin_index = i;
+                        begin_index = i.* - 1;
                     },
                     '1'...'9' => {
                         state = .Decimal;
                         result.id = .NumLitInt;
-                        begin_index = i;
+                        begin_index = i.* - 1;
                     },
                     '0' => {
                         state = .GotZero;
                         result.id = .NumLitInt;
-                        begin_index = i;
+                        begin_index = i.* - 1;
                     },
                     '.' => {
                         result.id = .Dot;
@@ -206,12 +211,23 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
                     'e', 'E' => {
                         state = .ExpSign;
                     },
-                    'f', 'F', 'l', 'L' => {
-                        result.bytes = chars[begin_index..i];
+                    'f',
+                    'F',
+                    => {
+                        i.* -= 1;
+                        result.num_lit_suffix = .F;
+                        result.bytes = chars[begin_index..i.*];
+                        return result;
+                    },
+                    'l', 'L' => {
+                        i.* -= 1;
+                        result.num_lit_suffix = .L;
+                        result.bytes = chars[begin_index..i.*];
                         return result;
                     },
                     else => {
-                        result.bytes = chars[begin_index..i];
+                        i.* -= 1;
+                        result.bytes = chars[begin_index..i.*];
                         return result;
                     },
                 }
@@ -238,12 +254,19 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
             .FloatExp => {
                 switch (c) {
                     '0'...'9' => {},
-                    'f', 'F', 'l', 'L' => {
-                        result.bytes = chars[begin_index..i];
+                    'f', 'F' => {
+                        result.num_lit_suffix = .F;
+                        result.bytes = chars[begin_index .. i.* - 1];
+                        return result;
+                    },
+                    'l', 'L' => {
+                        result.num_lit_suffix = .L;
+                        result.bytes = chars[begin_index .. i.* - 1];
                         return result;
                     },
                     else => {
-                        result.bytes = chars[begin_index..i];
+                        i.* -= 1;
+                        result.bytes = chars[begin_index..i.*];
                         return result;
                     },
                 }
@@ -255,17 +278,20 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
                     'u', 'U' => {
                         state = .NumLitIntSuffixU;
                         result.num_lit_suffix = .U;
+                        result.bytes = chars[begin_index .. i.* - 1];
                     },
                     'l', 'L' => {
                         state = .NumLitIntSuffixL;
                         result.num_lit_suffix = .L;
+                        result.bytes = chars[begin_index .. i.* - 1];
                     },
                     '.' => {
                         result.id = .NumLitFloat;
                         state = .Float;
                     },
                     else => {
-                        result.bytes = chars[begin_index..i];
+                        i.* -= 1;
+                        result.bytes = chars[begin_index..i.*];
                         return result;
                     },
                 }
@@ -275,15 +301,25 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
                     'x', 'X' => {
                         state = .Hex;
                     },
+                    'b', 'B' => {
+                        state = .Bin;
+                    },
                     '.' => {
                         state = .Float;
                         result.id = .NumLitFloat;
                     },
-                    'l', 'L', 'u', 'U' => {
-                        c -= 1;
-                        state = .Decimal;
+                    'u', 'U' => {
+                        state = .NumLitIntSuffixU;
+                        result.num_lit_suffix = .U;
+                        result.bytes = chars[begin_index .. i.* - 1];
+                    },
+                    'l', 'L' => {
+                        state = .NumLitIntSuffixL;
+                        result.num_lit_suffix = .L;
+                        result.bytes = chars[begin_index .. i.* - 1];
                     },
                     else => {
+                        i.* -= 1;
                         state = .Octal;
                     },
                 }
@@ -293,7 +329,8 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
                     '0'...'7' => {},
                     '8', '9' => return error.TokenizingFailed,
                     else => {
-                        result.bytes = chars[begin_index..i];
+                        i.* -= 1;
+                        result.bytes = chars[begin_index..i.*];
                         return result;
                     },
                 }
@@ -301,23 +338,44 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
             .Hex => {
                 switch (c) {
                     '0'...'9', 'a'...'f', 'A'...'F' => {},
-
-                    'p', 'P' => {
-                        result.id = .NumLitFloat;
-                        state = .ExpSign;
+                    'u', 'U' => {
+                        // marks the number literal as unsigned
+                        state = .NumLitIntSuffixU;
+                        result.num_lit_suffix = .U;
+                        result.bytes = chars[begin_index .. i.* - 1];
                     },
+                    'l', 'L' => {
+                        // marks the number literal as long
+                        state = .NumLitIntSuffixL;
+                        result.num_lit_suffix = .L;
+                        result.bytes = chars[begin_index .. i.* - 1];
+                    },
+                    else => {
+                        i.* -= 1;
+                        result.bytes = chars[begin_index..i.*];
+                        return result;
+                    },
+                }
+            },
+            .Bin => {
+                switch (c) {
+                    '0'...'1' => {},
+                    '2'...'9' => return error.TokenizingFailed,
                     'u', 'U' => {
                         // marks the number literal as unsigned
                         state = .NumLitIntSuffixU;
                         result.num_lit_suffix = .U;
+                        result.bytes = chars[begin_index .. i.* - 1];
                     },
                     'l', 'L' => {
                         // marks the number literal as long
                         state = .NumLitIntSuffixL;
                         result.num_lit_suffix = .L;
+                        result.bytes = chars[begin_index .. i.* - 1];
                     },
                     else => {
-                        result.bytes = chars[begin_index..i];
+                        i.* -= 1;
+                        result.bytes = chars[begin_index..i.*];
                         return result;
                     },
                 }
@@ -329,7 +387,7 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
                         state = .NumLitIntSuffixUL;
                     },
                     else => {
-                        result.bytes = chars[begin_index..i - 1];
+                        i.* -= 1;
                         return result;
                     },
                 }
@@ -342,11 +400,10 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
                     },
                     'u', 'U' => {
                         result.num_lit_suffix = .LU;
-                        result.bytes = chars[begin_index..i - 2];
                         return result;
                     },
                     else => {
-                        result.bytes = chars[begin_index..i - 1];
+                        i.* -= 1;
                         return result;
                     },
                 }
@@ -355,11 +412,10 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
                 switch (c) {
                     'u', 'U' => {
                         result.num_lit_suffix = .LLU;
-                        result.bytes = chars[begin_index..i - 3];
                         return result;
                     },
                     else => {
-                        result.bytes = chars[begin_index..i - 2];
+                        i.* -= 1;
                         return result;
                     },
                 }
@@ -368,11 +424,10 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
                 switch (c) {
                     'l', 'L' => {
                         result.num_lit_suffix = .LLU;
-                        result.bytes = chars[begin_index..i - 3];
                         return result;
                     },
                     else => {
-                        result.bytes = chars[begin_index..i - 2];
+                        i.* -= 1;
                         return result;
                     },
                 }
@@ -381,35 +436,28 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
                 switch (c) {
                     '_', 'a'...'z', 'A'...'Z', '0'...'9' => {},
                     else => {
-                        result.bytes = chars[begin_index..i];
+                        i.* -= 1;
+                        result.bytes = chars[begin_index..i.*];
                         return result;
                     },
                 }
             },
-            .String => {
+            .String => { // TODO char escapes
                 switch (c) {
                     '\"' => {
-                        result.bytes = chars[begin_index + 1 .. i];
+                        result.bytes = chars[begin_index + 1 .. i.* - 1];
                         return result;
                     },
                     else => {},
                 }
             },
-            .ExpectChar => {
-                switch (c) {
-                    '\'' => return error.TokenizingFailed,
-                    else => {
-                        state = .ExpectEndQuot;
-                    },
-                }
-            },
-            .ExpectEndQuot => {
+            .CharLit => {
                 switch (c) {
                     '\'' => {
-                        result.bytes = chars[begin_index + 1 .. i];
+                        result.bytes = chars[begin_index + 1 .. i.* - 1];
                         return result;
                     },
-                    else => return error.TokenizingFailed,
+                    else => {},
                 }
             },
             .OpenComment => {
@@ -455,4 +503,56 @@ fn next(chars: [*]const u8, index: *usize) !CToken {
             },
         }
     }
+    unreachable;
+}
+
+test "tokenize macro" {
+    var tl = TokenList.init(std.heap.page_allocator);
+    defer tl.deinit();
+
+    const src = "TEST 0\n";
+    try tokenizeCMacro(&tl, src);
+    var it = tl.iterator(0);
+    expect(it.next().?.id == .Identifier);
+    expect(std.mem.eql(u8, it.next().?.bytes, "0"));
+    expect(it.next().?.id == .Eof);
+    expect(it.next() == null);
+    tl.shrink(0);
+
+    const src2 = "__FLT_MIN_10_EXP__ -37\n";
+    try tokenizeCMacro(&tl, src2);
+    it = tl.iterator(0);
+    expect(std.mem.eql(u8, it.next().?.bytes, "__FLT_MIN_10_EXP__"));
+    expect(it.next().?.id == .Minus);
+    expect(std.mem.eql(u8, it.next().?.bytes, "37"));
+    expect(it.next().?.id == .Eof);
+    expect(it.next() == null);
+    tl.shrink(0);
+
+    const src3 = "__llvm__ 1\n#define";
+    try tokenizeCMacro(&tl, src3);
+    it = tl.iterator(0);
+    expect(std.mem.eql(u8, it.next().?.bytes, "__llvm__"));
+    expect(std.mem.eql(u8, it.next().?.bytes, "1"));
+    expect(it.next().?.id == .Eof);
+    expect(it.next() == null);
+    tl.shrink(0);
+
+    const src4 = "TEST 2";
+    try tokenizeCMacro(&tl, src4);
+    it = tl.iterator(0);
+    expect(it.next().?.id == .Identifier);
+    expect(std.mem.eql(u8, it.next().?.bytes, "2"));
+    expect(it.next().?.id == .Eof);
+    expect(it.next() == null);
+    tl.shrink(0);
+
+    const src5 = "FOO 0l";
+    try tokenizeCMacro(&tl, src5);
+    it = tl.iterator(0);
+    expect(it.next().?.id == .Identifier);
+    expect(std.mem.eql(u8, it.next().?.bytes, "0"));
+    expect(it.next().?.id == .Eof);
+    expect(it.next() == null);
+    tl.shrink(0);
 }
src-self-hosted/clang.zig
@@ -75,6 +75,7 @@ pub const struct_ZigClangWhileStmt = @OpaqueType();
 pub const struct_ZigClangFunctionType = @OpaqueType();
 pub const struct_ZigClangPredefinedExpr = @OpaqueType();
 pub const struct_ZigClangInitListExpr = @OpaqueType();
+pub const ZigClangPreprocessingRecord = @OpaqueType();
 
 pub const ZigClangBO = extern enum {
     PtrMemD,
@@ -717,6 +718,18 @@ pub const ZigClangEnumDecl_enumerator_iterator = extern struct {
     opaque: *c_void,
 };
 
+pub const ZigClangPreprocessingRecord_iterator = extern struct {
+    I: c_int,
+    Self: *ZigClangPreprocessingRecord,
+};
+
+pub const ZigClangPreprocessedEntity_EntityKind = extern enum {
+    InvalidKind,
+    MacroExpansionKind,
+    MacroDefinitionKind,
+    InclusionDirectiveKind,
+};
+
 pub extern fn ZigClangSourceManager_getSpellingLoc(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) struct_ZigClangSourceLocation;
 pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*:0]const u8;
 pub extern fn ZigClangSourceManager_getSpellingLineNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint;
@@ -1014,3 +1027,12 @@ pub extern fn ZigClangFieldDecl_getLocation(*const struct_ZigClangFieldDecl) str
 
 pub extern fn ZigClangEnumConstantDecl_getInitExpr(*const ZigClangEnumConstantDecl) ?*const ZigClangExpr;
 pub extern fn ZigClangEnumConstantDecl_getInitVal(*const ZigClangEnumConstantDecl) *const ZigClangAPSInt;
+
+pub extern fn ZigClangASTUnit_getLocalPreprocessingEntities_begin(*ZigClangASTUnit) ZigClangPreprocessingRecord_iterator;
+pub extern fn ZigClangASTUnit_getLocalPreprocessingEntities_end(*ZigClangASTUnit) ZigClangPreprocessingRecord_iterator;
+pub extern fn ZigClangPreprocessingRecord_iterator_deref(ZigClangPreprocessingRecord_iterator) *ZigClangPreprocessedEntity;
+pub extern fn ZigClangPreprocessedEntity_getKind(*const ZigClangPreprocessedEntity) ZigClangPreprocessedEntity_EntityKind;
+
+pub extern fn ZigClangMacroDefinitionRecord_getName_getNameStart(*const ZigClangMacroDefinitionRecord) [*:0]const u8;
+pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getBegin(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation;
+pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getEnd(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation;
src-self-hosted/translate_c.zig
@@ -6,6 +6,8 @@ const assert = std.debug.assert;
 const ast = std.zig.ast;
 const Token = std.zig.Token;
 usingnamespace @import("clang.zig");
+const ctok = @import("c_tokenizer.zig");
+const CToken = ctok.CToken;
 
 const CallingConvention = std.builtin.TypeInfo.CallingConvention;
 
@@ -31,6 +33,7 @@ fn addrEql(a: usize, b: usize) bool {
     return a == b;
 }
 
+const MacroTable = std.StringHashMap(*ast.Node);
 const SymbolTable = std.StringHashMap(void);
 const AliasList = std.SegmentedList(struct {
     alias: []const u8,
@@ -106,6 +109,7 @@ const Context = struct {
     decl_table: DeclTable,
     alias_list: AliasList,
     sym_table: SymbolTable,
+    macro_table: MacroTable,
     global_scope: *Scope.Root,
     ptr_params: std.BufSet,
     clang_context: *ZigClangASTContext,
@@ -193,6 +197,7 @@ pub fn translate(
         .decl_table = DeclTable.init(arena),
         .alias_list = AliasList.init(arena),
         .sym_table = SymbolTable.init(arena),
+        .macro_table = MacroTable.init(arena),
         .global_scope = try arena.create(Scope.Root),
         .ptr_params = std.BufSet.init(arena),
         .clang_context = ZigClangASTUnit_getASTContext(ast_unit).?,
@@ -207,6 +212,14 @@ pub fn translate(
     if (!ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, &context, declVisitorC)) {
         return context.err;
     }
+
+    try transPreprocessorEntities(&context, ast_unit);
+
+    var macro_it = context.macro_table.iterator();
+    while (macro_it.next()) |kv| {
+        try addTopLevelDecl(&context, kv.key, kv.value);
+    }
+
     var it = context.alias_list.iterator(0);
     while (it.next()) |alias| {
         if (!context.sym_table.contains(alias.alias)) {
@@ -1931,18 +1944,18 @@ fn transCreateNodeInt(c: *Context, int: var) !*ast.Node {
     return &node.base;
 }
 
-fn transCreateNodeOpaqueType(c: *Context) !*ast.Node {
-    const builtin_tok = try appendToken(c, .Builtin, "@OpaqueType");
-    _ = try appendToken(c, .LParen, "(");
-    const rparen_tok = try appendToken(c, .RParen, ")");
-
-    const call_node = try c.a().create(ast.Node.BuiltinCall);
-    call_node.* = ast.Node.BuiltinCall{
-        .base = ast.Node{ .id = ast.Node.Id.BuiltinCall },
-        .builtin_token = builtin_tok,
-        .params = ast.Node.BuiltinCall.ParamList.init(c.a()),
-        .rparen_token = rparen_tok,
+fn transCreateNodeFloat(c: *Context, int: var) !*ast.Node {
+    const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int});
+    const node = try c.a().create(ast.Node.FloatLiteral);
+    node.* = .{
+        .token = token,
     };
+    return &node.base;
+}
+
+fn transCreateNodeOpaqueType(c: *Context) !*ast.Node {
+    const call_node = try transCreateNodeBuiltinFnCall(c, "@OpaqueType");
+    call_node.rparen_token = try appendToken(c, .RParen, ")");
     return &call_node.base;
 }
 
@@ -2441,3 +2454,273 @@ fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {
 pub fn freeErrors(errors: []ClangErrMsg) void {
     ZigClangErrorMsg_delete(errors.ptr, errors.len);
 }
+
+fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
+    // TODO if we see #undef, delete it from the table
+    var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(unit);
+    const it_end = ZigClangASTUnit_getLocalPreprocessingEntities_end(unit);
+    var tok_list = ctok.TokenList.init(c.a());
+
+    while (it.I != it_end.I) : (it.I += 1) {
+        const entity = ZigClangPreprocessingRecord_iterator_deref(it);
+        tok_list.shrink(0);
+
+        switch (ZigClangPreprocessedEntity_getKind(entity)) {
+            .MacroExpansionKind => {
+                // TODO
+            },
+            .MacroDefinitionKind => {
+                const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity);
+                const raw_name = ZigClangMacroDefinitionRecord_getName_getNameStart(macro);
+                const begin_loc = ZigClangMacroDefinitionRecord_getSourceRange_getBegin(macro);
+
+                const name = try c.str(raw_name);
+                // if (name_exists_global(c, name)) { // TODO
+                //     continue;
+                // }
+
+                const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc);
+                try transMacroDefine(c, &tok_list, name, begin_c, begin_loc);
+            },
+            else => {},
+        }
+    }
+}
+
+fn transMacroDefine(c: *Context, tok_list: *ctok.TokenList, name: []const u8, char_ptr: [*]const u8, source_loc: ZigClangSourceLocation) Error!void {
+    ctok.tokenizeCMacro(tok_list, char_ptr) catch |err| switch (err) {
+        error.OutOfMemory => |e| return e,
+        else => return failDecl(c, source_loc, name, "unable to tokenize macro definition", .{}),
+    };
+    const rp = makeRestorePoint(c);
+
+    var it = tok_list.iterator(0);
+    const first_tok = it.next().?;
+    assert(first_tok.id == .Identifier and std.mem.eql(u8, first_tok.bytes, name));
+    const next = it.peek().?;
+    switch (next.id) {
+        .Identifier => {
+            // if it equals itself, ignore. for example, from stdio.h:
+            // #define stdin stdin
+            if (std.mem.eql(u8, name, next.bytes)) {
+                return;
+            }
+        },
+        .Eof => {
+            // this means it is a macro without a value
+            // we don't care about such things
+            return;
+        },
+        else => {},
+    }
+
+    const visib_tok = try appendToken(c, .Keyword_pub, "pub");
+    const mut_tok = try appendToken(c, .Keyword_const, "const");
+    const name_tok = try appendIdentifier(c, name);
+
+    const eq_tok = try appendToken(c, .Equal, "=");
+
+    const init_node = parseCExpr(rp, &it, source_loc) catch |err| switch (err) {
+        error.UnsupportedTranslation,
+        error.ParseError,
+        => return failDecl(c, source_loc, name, "unable to translate macro", .{}),
+        error.OutOfMemory => |e| return e,
+    };
+
+    const node = try c.a().create(ast.Node.VarDecl);
+    node.* = ast.Node.VarDecl{
+        .doc_comments = null,
+        .visib_token = visib_tok,
+        .thread_local_token = null,
+        .name_token = name_tok,
+        .eq_token = eq_tok,
+        .mut_token = mut_tok,
+        .comptime_token = null,
+        .extern_export_token = null,
+        .lib_name = null,
+        .type_node = null,
+        .align_node = null,
+        .section_node = null,
+        .init_node = init_node,
+        .semicolon_token = try appendToken(c, .Semicolon, ";"),
+    };
+    _ = try c.macro_table.put(name, &node.base);
+}
+
+const ParseError = Error || error{
+    ParseError,
+    UnsupportedTranslation,
+};
+
+fn parseCExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {
+    return parseCPrefixOpExpr(rp, it, source_loc);
+}
+
+fn parseCNumLit(rp: RestorePoint, 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}));
+                    },
+                    else => {},
+                }
+            }
+            return transCreateNodeInt(rp.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) {
+            .U => "c_uint",
+            .L => "c_long",
+            .LU => "c_ulong",
+            .LL => "c_longlong",
+            .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, ")");
+        return &cast_node.base;
+    } else if (tok.id == .NumLitFloat) {
+        if (tok.num_lit_suffix == .None) {
+            return transCreateNodeFloat(rp.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) {
+            .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, ")");
+        return &cast_node.base;
+    } else
+        return revertAndWarn(
+        rp,
+        error.ParseError,
+        source_loc,
+        "expected number literal",
+        .{},
+    );
+}
+
+fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {
+    const tok = it.next().?;
+    switch (tok.id) {
+        .CharLit => {
+            const buf = try rp.c.a().alloc(u8, tok.bytes.len + "''".len);
+            buf[0] = '\'';
+            writeEscapedString(buf[1..], tok.bytes);
+            buf[buf.len - 1] = '\'';
+            const token = try appendToken(rp.c, .CharLiteral, buf);
+            const node = try rp.c.a().create(ast.Node.CharLiteral);
+            node.* = ast.Node.CharLiteral{
+                .token = token,
+            };
+            return &node.base;
+        },
+        .StrLit => {
+            const buf = try rp.c.a().alloc(u8, tok.bytes.len + "\"\"".len);
+            buf[0] = '"';
+            writeEscapedString(buf[1..], tok.bytes);
+            buf[buf.len - 1] = '"';
+            const token = try appendToken(rp.c, .StringLiteral, buf);
+            const node = try rp.c.a().create(ast.Node.StringLiteral);
+            node.* = ast.Node.StringLiteral{
+                .token = token,
+            };
+            return &node.base;
+        },
+        .Minus => {
+            const node = try transCreateNodePrefixOp(
+                rp.c,
+                .Negation,
+                .Minus,
+                "-",
+            );
+            node.rhs = try parseCNumLit(rp, it.next().?, source_loc);
+            return &node.base;
+        },
+        .NumLitInt, .NumLitFloat => {
+            return parseCNumLit(rp, tok, source_loc);
+        },
+        .Identifier => return transCreateNodeIdentifier(rp.c, tok.bytes),
+        .LParen => {
+            _ = try appendToken(rp.c, .LParen, "(");
+            const inner_node = try parseCExpr(rp, it, source_loc);
+            _ = try appendToken(rp.c, .RParen, ")");
+
+            return inner_node; // TODO
+        },
+        else => return revertAndWarn(
+            rp,
+            error.UnsupportedTranslation,
+            source_loc,
+            "unable to translate C expr",
+            .{},
+        ),
+    }
+}
+
+fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {
+    var node = try parseCPrimaryExpr(rp, it, source_loc);
+    while (true) {
+        const tok = it.next().?;
+        switch (tok.id) {
+            .Dot => {
+                const name_tok = it.next().?;
+                if (name_tok.id != .Identifier)
+                    return revertAndWarn(
+                        rp,
+                        error.ParseError,
+                        source_loc,
+                        "unable to translate C expr",
+                        .{},
+                    );
+
+                const op_token = try appendToken(rp.c, .Period, ".");
+                const rhs = try transCreateNodeIdentifier(rp.c, tok.bytes);
+                const access_node = try rp.c.a().create(ast.Node.InfixOp);
+                access_node.* = .{
+                    .op_token = op_token,
+                    .lhs = node,
+                    .op = .Period,
+                    .rhs = rhs,
+                };
+                node = &access_node.base;
+            },
+            .Shl => {
+                const rhs_node = try parseCPrimaryExpr(rp, it, source_loc);
+
+                const op_token = try appendToken(rp.c, .AngleBracketAngleBracketLeft, "<<");
+                const rhs = try parseCPrimaryExpr(rp, it, source_loc);
+                const bitshift_node = try rp.c.a().create(ast.Node.InfixOp);
+                bitshift_node.* = .{
+                    .op_token = op_token,
+                    .lhs = node,
+                    .op = .BitShiftLeft,
+                    .rhs = rhs,
+                };
+                node = &bitshift_node.base;
+            },
+            else => {
+                _ = it.prev();
+                return node;
+            },
+        }
+    }
+}
+
+fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {
+    const op_tok = it.next().?;
+
+    switch (op_tok.id) {
+        else => {
+            _ = it.prev();
+            return try parseCSuffixOpExpr(rp, it, source_loc);
+        },
+    }
+}
test/translate_c.zig
@@ -214,6 +214,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\        Clear: c_int,
         \\    },
         \\};
+    ,
         \\pub const OpenGLProcs = union_OpenGLProcs;
     });
 
@@ -280,9 +281,64 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    o,
         \\    p,
         \\};
+    ,
         \\pub const Baz = struct_Baz;
     });
 
+    cases.add_2("#define a char literal",
+        \\#define A_CHAR  'a'
+    , &[_][]const u8{
+        \\pub const A_CHAR = 'a';
+    });
+
+    cases.add_2("comment after integer literal",
+        \\#define SDL_INIT_VIDEO 0x00000020  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+    , &[_][]const u8{
+        \\pub const SDL_INIT_VIDEO = 0x00000020;
+    });
+
+    cases.add_2("u integer suffix after hex literal",
+        \\#define SDL_INIT_VIDEO 0x00000020u  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+    , &[_][]const u8{
+        \\pub const SDL_INIT_VIDEO = @as(c_uint, 0x00000020);
+    });
+
+    cases.add_2("l integer suffix after hex literal",
+        \\#define SDL_INIT_VIDEO 0x00000020l  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+    , &[_][]const u8{
+        \\pub const SDL_INIT_VIDEO = @as(c_long, 0x00000020);
+    });
+
+    cases.add_2("ul integer suffix after hex literal",
+        \\#define SDL_INIT_VIDEO 0x00000020ul  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+    , &[_][]const u8{
+        \\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020);
+    });
+
+    cases.add_2("lu integer suffix after hex literal",
+        \\#define SDL_INIT_VIDEO 0x00000020lu  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+    , &[_][]const u8{
+        \\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020);
+    });
+
+    cases.add_2("ll integer suffix after hex literal",
+        \\#define SDL_INIT_VIDEO 0x00000020ll  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+    , &[_][]const u8{
+        \\pub const SDL_INIT_VIDEO = @as(c_longlong, 0x00000020);
+    });
+
+    cases.add_2("ull integer suffix after hex literal",
+        \\#define SDL_INIT_VIDEO 0x00000020ull  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+    , &[_][]const u8{
+        \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020);
+    });
+
+    cases.add_2("llu integer suffix after hex literal",
+        \\#define SDL_INIT_VIDEO 0x00000020llu  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+    , &[_][]const u8{
+        \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020);
+    });
+
     /////////////// Cases for only stage1 which are TODO items for stage2 ////////////////
 
     cases.add_both("typedef of function in struct field",
@@ -314,7 +370,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\};
     });
 
-    cases.add("macro with left shift",
+    cases.add_both("macro with left shift",
         \\#define REDISMODULE_READ (1<<0)
     , &[_][]const u8{
         \\pub const REDISMODULE_READ = 1 << 0;
@@ -637,13 +693,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\pub const A_CHAR = 97;
     });
 
-    cases.add("#define an unsigned integer literal",
+    cases.add_both("#define an unsigned integer literal",
         \\#define CHANNEL_COUNT 24
     , &[_][]const u8{
         \\pub const CHANNEL_COUNT = 24;
     });
 
-    cases.add("#define referencing another #define",
+    cases.add_both("#define referencing another #define",
         \\#define THING2 THING1
         \\#define THING1 1234
     , &[_][]const u8{
@@ -692,7 +748,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\}
     });
 
-    cases.add("#define string",
+    cases.add_both("#define string",
         \\#define  foo  "a string"
     , &[_][]const u8{
         \\pub const foo = "a string";
@@ -788,7 +844,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\pub const FOO_CHAR = 63;
     });
 
-    cases.add("macro with parens around negative number",
+    cases.add_both("macro with parens around negative number",
         \\#define LUA_GLOBALSINDEX        (-10002)
     , &[_][]const u8{
         \\pub const LUA_GLOBALSINDEX = -10002;
@@ -1732,7 +1788,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\}
     });
 
-    cases.addC(
+    cases.add_both(
         "u integer suffix after 0 (zero) in macro definition",
         "#define ZERO 0U",
         &[_][]const u8{
@@ -1740,7 +1796,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         },
     );
 
-    cases.addC(
+    cases.add_both(
         "l integer suffix after 0 (zero) in macro definition",
         "#define ZERO 0L",
         &[_][]const u8{
@@ -1748,7 +1804,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         },
     );
 
-    cases.addC(
+    cases.add_both(
         "ul integer suffix after 0 (zero) in macro definition",
         "#define ZERO 0UL",
         &[_][]const u8{
@@ -1756,7 +1812,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         },
     );
 
-    cases.addC(
+    cases.add_both(
         "lu integer suffix after 0 (zero) in macro definition",
         "#define ZERO 0LU",
         &[_][]const u8{
@@ -1764,7 +1820,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         },
     );
 
-    cases.addC(
+    cases.add_both(
         "ll integer suffix after 0 (zero) in macro definition",
         "#define ZERO 0LL",
         &[_][]const u8{
@@ -1772,7 +1828,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         },
     );
 
-    cases.addC(
+    cases.add_both(
         "ull integer suffix after 0 (zero) in macro definition",
         "#define ZERO 0ULL",
         &[_][]const u8{
@@ -1780,7 +1836,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         },
     );
 
-    cases.addC(
+    cases.add_both(
         "llu integer suffix after 0 (zero) in macro definition",
         "#define ZERO 0LLU",
         &[_][]const u8{
@@ -1788,7 +1844,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         },
     );
 
-    cases.addC(
+    cases.addC(//todo
         "bitwise not on u-suffixed 0 (zero) in macro definition",
         "#define NOT_ZERO (~0U)",
         &[_][]const u8{