translate-c: use Aro's tokenizer

This commit is contained in:
Veikka Tuominen
2023-11-24 20:11:11 +02:00
parent d2a8660d04
commit 74010fecc7
9 changed files with 324 additions and 1872 deletions
-1
View File
@@ -218,7 +218,6 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/lib/std/builtin.zig"
"${CMAKE_SOURCE_DIR}/lib/std/c.zig"
"${CMAKE_SOURCE_DIR}/lib/std/c/linux.zig"
"${CMAKE_SOURCE_DIR}/lib/std/c/tokenizer.zig"
"${CMAKE_SOURCE_DIR}/lib/std/child_process.zig"
"${CMAKE_SOURCE_DIR}/lib/std/coff.zig"
"${CMAKE_SOURCE_DIR}/lib/std/comptime_string_map.zig"
-8
View File
@@ -5,14 +5,6 @@ const page_size = std.mem.page_size;
const iovec = std.os.iovec;
const iovec_const = std.os.iovec_const;
test {
_ = tokenizer;
}
pub const tokenizer = @import("c/tokenizer.zig");
pub const Token = tokenizer.Token;
pub const Tokenizer = tokenizer.Tokenizer;
/// The return type is `type` to force comptime function call execution.
/// TODO: https://github.com/ziglang/zig/issues/425
/// If not linking libc, returns struct{pub const ok = false;}
-1585
View File
@@ -1,1585 +0,0 @@
const std = @import("std");
pub const Token = struct {
id: Id,
start: usize,
end: usize,
pub const Id = union(enum) {
Invalid,
Eof,
Nl,
Identifier,
/// special case for #include <...>
MacroString,
StringLiteral: StrKind,
CharLiteral: StrKind,
IntegerLiteral: NumSuffix,
FloatLiteral: NumSuffix,
Bang,
BangEqual,
Pipe,
PipePipe,
PipeEqual,
Equal,
EqualEqual,
LParen,
RParen,
LBrace,
RBrace,
LBracket,
RBracket,
Period,
Ellipsis,
Caret,
CaretEqual,
Plus,
PlusPlus,
PlusEqual,
Minus,
MinusMinus,
MinusEqual,
Asterisk,
AsteriskEqual,
Percent,
PercentEqual,
Arrow,
Colon,
Semicolon,
Slash,
SlashEqual,
Comma,
Ampersand,
AmpersandAmpersand,
AmpersandEqual,
QuestionMark,
AngleBracketLeft,
AngleBracketLeftEqual,
AngleBracketAngleBracketLeft,
AngleBracketAngleBracketLeftEqual,
AngleBracketRight,
AngleBracketRightEqual,
AngleBracketAngleBracketRight,
AngleBracketAngleBracketRightEqual,
Tilde,
LineComment,
MultiLineComment,
Hash,
HashHash,
Keyword_auto,
Keyword_break,
Keyword_case,
Keyword_char,
Keyword_const,
Keyword_continue,
Keyword_default,
Keyword_do,
Keyword_double,
Keyword_else,
Keyword_enum,
Keyword_extern,
Keyword_float,
Keyword_for,
Keyword_goto,
Keyword_if,
Keyword_int,
Keyword_long,
Keyword_register,
Keyword_return,
Keyword_short,
Keyword_signed,
Keyword_sizeof,
Keyword_static,
Keyword_struct,
Keyword_switch,
Keyword_typedef,
Keyword_union,
Keyword_unsigned,
Keyword_void,
Keyword_volatile,
Keyword_while,
// ISO C99
Keyword_bool,
Keyword_complex,
Keyword_imaginary,
Keyword_inline,
Keyword_restrict,
// ISO C11
Keyword_alignas,
Keyword_alignof,
Keyword_atomic,
Keyword_generic,
Keyword_noreturn,
Keyword_static_assert,
Keyword_thread_local,
// Preprocessor directives
Keyword_include,
Keyword_define,
Keyword_ifdef,
Keyword_ifndef,
Keyword_error,
Keyword_pragma,
pub fn symbol(id: Id) []const u8 {
return symbolName(id);
}
pub fn symbolName(id: std.meta.Tag(Id)) []const u8 {
return switch (id) {
.Invalid => "Invalid",
.Eof => "Eof",
.Nl => "NewLine",
.Identifier => "Identifier",
.MacroString => "MacroString",
.StringLiteral => "StringLiteral",
.CharLiteral => "CharLiteral",
.IntegerLiteral => "IntegerLiteral",
.FloatLiteral => "FloatLiteral",
.LineComment => "LineComment",
.MultiLineComment => "MultiLineComment",
.Bang => "!",
.BangEqual => "!=",
.Pipe => "|",
.PipePipe => "||",
.PipeEqual => "|=",
.Equal => "=",
.EqualEqual => "==",
.LParen => "(",
.RParen => ")",
.LBrace => "{",
.RBrace => "}",
.LBracket => "[",
.RBracket => "]",
.Period => ".",
.Ellipsis => "...",
.Caret => "^",
.CaretEqual => "^=",
.Plus => "+",
.PlusPlus => "++",
.PlusEqual => "+=",
.Minus => "-",
.MinusMinus => "--",
.MinusEqual => "-=",
.Asterisk => "*",
.AsteriskEqual => "*=",
.Percent => "%",
.PercentEqual => "%=",
.Arrow => "->",
.Colon => ":",
.Semicolon => ";",
.Slash => "/",
.SlashEqual => "/=",
.Comma => ",",
.Ampersand => "&",
.AmpersandAmpersand => "&&",
.AmpersandEqual => "&=",
.QuestionMark => "?",
.AngleBracketLeft => "<",
.AngleBracketLeftEqual => "<=",
.AngleBracketAngleBracketLeft => "<<",
.AngleBracketAngleBracketLeftEqual => "<<=",
.AngleBracketRight => ">",
.AngleBracketRightEqual => ">=",
.AngleBracketAngleBracketRight => ">>",
.AngleBracketAngleBracketRightEqual => ">>=",
.Tilde => "~",
.Hash => "#",
.HashHash => "##",
.Keyword_auto => "auto",
.Keyword_break => "break",
.Keyword_case => "case",
.Keyword_char => "char",
.Keyword_const => "const",
.Keyword_continue => "continue",
.Keyword_default => "default",
.Keyword_do => "do",
.Keyword_double => "double",
.Keyword_else => "else",
.Keyword_enum => "enum",
.Keyword_extern => "extern",
.Keyword_float => "float",
.Keyword_for => "for",
.Keyword_goto => "goto",
.Keyword_if => "if",
.Keyword_int => "int",
.Keyword_long => "long",
.Keyword_register => "register",
.Keyword_return => "return",
.Keyword_short => "short",
.Keyword_signed => "signed",
.Keyword_sizeof => "sizeof",
.Keyword_static => "static",
.Keyword_struct => "struct",
.Keyword_switch => "switch",
.Keyword_typedef => "typedef",
.Keyword_union => "union",
.Keyword_unsigned => "unsigned",
.Keyword_void => "void",
.Keyword_volatile => "volatile",
.Keyword_while => "while",
.Keyword_bool => "_Bool",
.Keyword_complex => "_Complex",
.Keyword_imaginary => "_Imaginary",
.Keyword_inline => "inline",
.Keyword_restrict => "restrict",
.Keyword_alignas => "_Alignas",
.Keyword_alignof => "_Alignof",
.Keyword_atomic => "_Atomic",
.Keyword_generic => "_Generic",
.Keyword_noreturn => "_Noreturn",
.Keyword_static_assert => "_Static_assert",
.Keyword_thread_local => "_Thread_local",
.Keyword_include => "include",
.Keyword_define => "define",
.Keyword_ifdef => "ifdef",
.Keyword_ifndef => "ifndef",
.Keyword_error => "error",
.Keyword_pragma => "pragma",
};
}
};
// TODO extensions
pub const keywords = std.ComptimeStringMap(Id, .{
.{ "auto", .Keyword_auto },
.{ "break", .Keyword_break },
.{ "case", .Keyword_case },
.{ "char", .Keyword_char },
.{ "const", .Keyword_const },
.{ "continue", .Keyword_continue },
.{ "default", .Keyword_default },
.{ "do", .Keyword_do },
.{ "double", .Keyword_double },
.{ "else", .Keyword_else },
.{ "enum", .Keyword_enum },
.{ "extern", .Keyword_extern },
.{ "float", .Keyword_float },
.{ "for", .Keyword_for },
.{ "goto", .Keyword_goto },
.{ "if", .Keyword_if },
.{ "int", .Keyword_int },
.{ "long", .Keyword_long },
.{ "register", .Keyword_register },
.{ "return", .Keyword_return },
.{ "short", .Keyword_short },
.{ "signed", .Keyword_signed },
.{ "sizeof", .Keyword_sizeof },
.{ "static", .Keyword_static },
.{ "struct", .Keyword_struct },
.{ "switch", .Keyword_switch },
.{ "typedef", .Keyword_typedef },
.{ "union", .Keyword_union },
.{ "unsigned", .Keyword_unsigned },
.{ "void", .Keyword_void },
.{ "volatile", .Keyword_volatile },
.{ "while", .Keyword_while },
// ISO C99
.{ "_Bool", .Keyword_bool },
.{ "_Complex", .Keyword_complex },
.{ "_Imaginary", .Keyword_imaginary },
.{ "inline", .Keyword_inline },
.{ "restrict", .Keyword_restrict },
// ISO C11
.{ "_Alignas", .Keyword_alignas },
.{ "_Alignof", .Keyword_alignof },
.{ "_Atomic", .Keyword_atomic },
.{ "_Generic", .Keyword_generic },
.{ "_Noreturn", .Keyword_noreturn },
.{ "_Static_assert", .Keyword_static_assert },
.{ "_Thread_local", .Keyword_thread_local },
// Preprocessor directives
.{ "include", .Keyword_include },
.{ "define", .Keyword_define },
.{ "ifdef", .Keyword_ifdef },
.{ "ifndef", .Keyword_ifndef },
.{ "error", .Keyword_error },
.{ "pragma", .Keyword_pragma },
});
// TODO do this in the preprocessor
pub fn getKeyword(bytes: []const u8, pp_directive: bool) ?Id {
if (keywords.get(bytes)) |id| {
switch (id) {
.Keyword_include,
.Keyword_define,
.Keyword_ifdef,
.Keyword_ifndef,
.Keyword_error,
.Keyword_pragma,
=> if (!pp_directive) return null,
else => {},
}
return id;
}
return null;
}
pub const NumSuffix = enum {
none,
f,
l,
u,
lu,
ll,
llu,
};
pub const StrKind = enum {
none,
wide,
utf_8,
utf_16,
utf_32,
};
};
pub const Tokenizer = struct {
buffer: []const u8,
index: usize = 0,
prev_tok_id: std.meta.Tag(Token.Id) = .Invalid,
pp_directive: bool = false,
pub fn next(self: *Tokenizer) Token {
var result = Token{
.id = .Eof,
.start = self.index,
.end = undefined,
};
var state: enum {
Start,
Cr,
BackSlash,
BackSlashCr,
u,
u8,
U,
L,
StringLiteral,
CharLiteralStart,
CharLiteral,
EscapeSequence,
CrEscape,
OctalEscape,
HexEscape,
UnicodeEscape,
Identifier,
Equal,
Bang,
Pipe,
Percent,
Asterisk,
Plus,
/// special case for #include <...>
MacroString,
AngleBracketLeft,
AngleBracketAngleBracketLeft,
AngleBracketRight,
AngleBracketAngleBracketRight,
Caret,
Period,
Period2,
Minus,
Slash,
Ampersand,
Hash,
LineComment,
MultiLineComment,
MultiLineCommentAsterisk,
Zero,
IntegerLiteralOct,
IntegerLiteralBinary,
IntegerLiteralBinaryFirst,
IntegerLiteralHex,
IntegerLiteralHexFirst,
IntegerLiteral,
IntegerSuffix,
IntegerSuffixU,
IntegerSuffixL,
IntegerSuffixLL,
IntegerSuffixUL,
FloatFraction,
FloatFractionHex,
FloatExponent,
FloatExponentDigits,
FloatSuffix,
} = .Start;
var string = false;
var counter: u32 = 0;
while (self.index < self.buffer.len) : (self.index += 1) {
const c = self.buffer[self.index];
switch (state) {
.Start => switch (c) {
'\n' => {
self.pp_directive = false;
result.id = .Nl;
self.index += 1;
break;
},
'\r' => {
state = .Cr;
},
'"' => {
result.id = .{ .StringLiteral = .none };
state = .StringLiteral;
},
'\'' => {
result.id = .{ .CharLiteral = .none };
state = .CharLiteralStart;
},
'u' => {
state = .u;
},
'U' => {
state = .U;
},
'L' => {
state = .L;
},
'a'...'t', 'v'...'z', 'A'...'K', 'M'...'T', 'V'...'Z', '_', '$' => {
state = .Identifier;
},
'=' => {
state = .Equal;
},
'!' => {
state = .Bang;
},
'|' => {
state = .Pipe;
},
'(' => {
result.id = .LParen;
self.index += 1;
break;
},
')' => {
result.id = .RParen;
self.index += 1;
break;
},
'[' => {
result.id = .LBracket;
self.index += 1;
break;
},
']' => {
result.id = .RBracket;
self.index += 1;
break;
},
';' => {
result.id = .Semicolon;
self.index += 1;
break;
},
',' => {
result.id = .Comma;
self.index += 1;
break;
},
'?' => {
result.id = .QuestionMark;
self.index += 1;
break;
},
':' => {
result.id = .Colon;
self.index += 1;
break;
},
'%' => {
state = .Percent;
},
'*' => {
state = .Asterisk;
},
'+' => {
state = .Plus;
},
'<' => {
if (self.prev_tok_id == .Keyword_include)
state = .MacroString
else
state = .AngleBracketLeft;
},
'>' => {
state = .AngleBracketRight;
},
'^' => {
state = .Caret;
},
'{' => {
result.id = .LBrace;
self.index += 1;
break;
},
'}' => {
result.id = .RBrace;
self.index += 1;
break;
},
'~' => {
result.id = .Tilde;
self.index += 1;
break;
},
'.' => {
state = .Period;
},
'-' => {
state = .Minus;
},
'/' => {
state = .Slash;
},
'&' => {
state = .Ampersand;
},
'#' => {
state = .Hash;
},
'0' => {
state = .Zero;
},
'1'...'9' => {
state = .IntegerLiteral;
},
'\\' => {
state = .BackSlash;
},
'\t', '\x0B', '\x0C', ' ' => {
result.start = self.index + 1;
},
else => {
// TODO handle invalid bytes better
result.id = .Invalid;
self.index += 1;
break;
},
},
.Cr => switch (c) {
'\n' => {
self.pp_directive = false;
result.id = .Nl;
self.index += 1;
break;
},
else => {
result.id = .Invalid;
break;
},
},
.BackSlash => switch (c) {
'\n' => {
result.start = self.index + 1;
state = .Start;
},
'\r' => {
state = .BackSlashCr;
},
'\t', '\x0B', '\x0C', ' ' => {
// TODO warn
},
else => {
result.id = .Invalid;
break;
},
},
.BackSlashCr => switch (c) {
'\n' => {
result.start = self.index + 1;
state = .Start;
},
else => {
result.id = .Invalid;
break;
},
},
.u => switch (c) {
'8' => {
state = .u8;
},
'\'' => {
result.id = .{ .CharLiteral = .utf_16 };
state = .CharLiteralStart;
},
'\"' => {
result.id = .{ .StringLiteral = .utf_16 };
state = .StringLiteral;
},
else => {
self.index -= 1;
state = .Identifier;
},
},
.u8 => switch (c) {
'\"' => {
result.id = .{ .StringLiteral = .utf_8 };
state = .StringLiteral;
},
else => {
self.index -= 1;
state = .Identifier;
},
},
.U => switch (c) {
'\'' => {
result.id = .{ .CharLiteral = .utf_32 };
state = .CharLiteralStart;
},
'\"' => {
result.id = .{ .StringLiteral = .utf_32 };
state = .StringLiteral;
},
else => {
self.index -= 1;
state = .Identifier;
},
},
.L => switch (c) {
'\'' => {
result.id = .{ .CharLiteral = .wide };
state = .CharLiteralStart;
},
'\"' => {
result.id = .{ .StringLiteral = .wide };
state = .StringLiteral;
},
else => {
self.index -= 1;
state = .Identifier;
},
},
.StringLiteral => switch (c) {
'\\' => {
string = true;
state = .EscapeSequence;
},
'"' => {
self.index += 1;
break;
},
'\n', '\r' => {
result.id = .Invalid;
break;
},
else => {},
},
.CharLiteralStart => switch (c) {
'\\' => {
string = false;
state = .EscapeSequence;
},
'\'', '\n' => {
result.id = .Invalid;
break;
},
else => {
state = .CharLiteral;
},
},
.CharLiteral => switch (c) {
'\\' => {
string = false;
state = .EscapeSequence;
},
'\'' => {
self.index += 1;
break;
},
'\n' => {
result.id = .Invalid;
break;
},
else => {},
},
.EscapeSequence => switch (c) {
'\'', '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v', '\n' => {
state = if (string) .StringLiteral else .CharLiteral;
},
'\r' => {
state = .CrEscape;
},
'0'...'7' => {
counter = 1;
state = .OctalEscape;
},
'x' => {
state = .HexEscape;
},
'u' => {
counter = 4;
state = .OctalEscape;
},
'U' => {
counter = 8;
state = .OctalEscape;
},
else => {
result.id = .Invalid;
break;
},
},
.CrEscape => switch (c) {
'\n' => {
state = if (string) .StringLiteral else .CharLiteral;
},
else => {
result.id = .Invalid;
break;
},
},
.OctalEscape => switch (c) {
'0'...'7' => {
counter += 1;
if (counter == 3) {
state = if (string) .StringLiteral else .CharLiteral;
}
},
else => {
self.index -= 1;
state = if (string) .StringLiteral else .CharLiteral;
},
},
.HexEscape => switch (c) {
'0'...'9', 'a'...'f', 'A'...'F' => {},
else => {
self.index -= 1;
state = if (string) .StringLiteral else .CharLiteral;
},
},
.UnicodeEscape => switch (c) {
'0'...'9', 'a'...'f', 'A'...'F' => {
counter -= 1;
if (counter == 0) {
state = if (string) .StringLiteral else .CharLiteral;
}
},
else => {
if (counter != 0) {
result.id = .Invalid;
break;
}
self.index -= 1;
state = if (string) .StringLiteral else .CharLiteral;
},
},
.Identifier => switch (c) {
'a'...'z', 'A'...'Z', '_', '0'...'9', '$' => {},
else => {
result.id = Token.getKeyword(self.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier;
if (self.prev_tok_id == .Hash)
self.pp_directive = true;
break;
},
},
.Equal => switch (c) {
'=' => {
result.id = .EqualEqual;
self.index += 1;
break;
},
else => {
result.id = .Equal;
break;
},
},
.Bang => switch (c) {
'=' => {
result.id = .BangEqual;
self.index += 1;
break;
},
else => {
result.id = .Bang;
break;
},
},
.Pipe => switch (c) {
'=' => {
result.id = .PipeEqual;
self.index += 1;
break;
},
'|' => {
result.id = .PipePipe;
self.index += 1;
break;
},
else => {
result.id = .Pipe;
break;
},
},
.Percent => switch (c) {
'=' => {
result.id = .PercentEqual;
self.index += 1;
break;
},
else => {
result.id = .Percent;
break;
},
},
.Asterisk => switch (c) {
'=' => {
result.id = .AsteriskEqual;
self.index += 1;
break;
},
else => {
result.id = .Asterisk;
break;
},
},
.Plus => switch (c) {
'=' => {
result.id = .PlusEqual;
self.index += 1;
break;
},
'+' => {
result.id = .PlusPlus;
self.index += 1;
break;
},
else => {
result.id = .Plus;
break;
},
},
.MacroString => switch (c) {
'>' => {
result.id = .MacroString;
self.index += 1;
break;
},
else => {},
},
.AngleBracketLeft => switch (c) {
'<' => {
state = .AngleBracketAngleBracketLeft;
},
'=' => {
result.id = .AngleBracketLeftEqual;
self.index += 1;
break;
},
else => {
result.id = .AngleBracketLeft;
break;
},
},
.AngleBracketAngleBracketLeft => switch (c) {
'=' => {
result.id = .AngleBracketAngleBracketLeftEqual;
self.index += 1;
break;
},
else => {
result.id = .AngleBracketAngleBracketLeft;
break;
},
},
.AngleBracketRight => switch (c) {
'>' => {
state = .AngleBracketAngleBracketRight;
},
'=' => {
result.id = .AngleBracketRightEqual;
self.index += 1;
break;
},
else => {
result.id = .AngleBracketRight;
break;
},
},
.AngleBracketAngleBracketRight => switch (c) {
'=' => {
result.id = .AngleBracketAngleBracketRightEqual;
self.index += 1;
break;
},
else => {
result.id = .AngleBracketAngleBracketRight;
break;
},
},
.Caret => switch (c) {
'=' => {
result.id = .CaretEqual;
self.index += 1;
break;
},
else => {
result.id = .Caret;
break;
},
},
.Period => switch (c) {
'.' => {
state = .Period2;
},
'0'...'9' => {
state = .FloatFraction;
},
else => {
result.id = .Period;
break;
},
},
.Period2 => switch (c) {
'.' => {
result.id = .Ellipsis;
self.index += 1;
break;
},
else => {
result.id = .Period;
self.index -= 1;
break;
},
},
.Minus => switch (c) {
'>' => {
result.id = .Arrow;
self.index += 1;
break;
},
'=' => {
result.id = .MinusEqual;
self.index += 1;
break;
},
'-' => {
result.id = .MinusMinus;
self.index += 1;
break;
},
else => {
result.id = .Minus;
break;
},
},
.Slash => switch (c) {
'/' => {
state = .LineComment;
},
'*' => {
state = .MultiLineComment;
},
'=' => {
result.id = .SlashEqual;
self.index += 1;
break;
},
else => {
result.id = .Slash;
break;
},
},
.Ampersand => switch (c) {
'&' => {
result.id = .AmpersandAmpersand;
self.index += 1;
break;
},
'=' => {
result.id = .AmpersandEqual;
self.index += 1;
break;
},
else => {
result.id = .Ampersand;
break;
},
},
.Hash => switch (c) {
'#' => {
result.id = .HashHash;
self.index += 1;
break;
},
else => {
result.id = .Hash;
break;
},
},
.LineComment => switch (c) {
'\n' => {
result.id = .LineComment;
break;
},
else => {},
},
.MultiLineComment => switch (c) {
'*' => {
state = .MultiLineCommentAsterisk;
},
else => {},
},
.MultiLineCommentAsterisk => switch (c) {
'/' => {
result.id = .MultiLineComment;
self.index += 1;
break;
},
else => {
state = .MultiLineComment;
},
},
.Zero => switch (c) {
'0'...'9' => {
state = .IntegerLiteralOct;
},
'b', 'B' => {
state = .IntegerLiteralBinaryFirst;
},
'x', 'X' => {
state = .IntegerLiteralHexFirst;
},
'.' => {
state = .FloatFraction;
},
else => {
state = .IntegerSuffix;
self.index -= 1;
},
},
.IntegerLiteralOct => switch (c) {
'0'...'7' => {},
else => {
state = .IntegerSuffix;
self.index -= 1;
},
},
.IntegerLiteralBinaryFirst => switch (c) {
'0'...'7' => state = .IntegerLiteralBinary,
else => {
result.id = .Invalid;
break;
},
},
.IntegerLiteralBinary => switch (c) {
'0', '1' => {},
else => {
state = .IntegerSuffix;
self.index -= 1;
},
},
.IntegerLiteralHexFirst => switch (c) {
'0'...'9', 'a'...'f', 'A'...'F' => state = .IntegerLiteralHex,
'.' => {
state = .FloatFractionHex;
},
'p', 'P' => {
state = .FloatExponent;
},
else => {
result.id = .Invalid;
break;
},
},
.IntegerLiteralHex => switch (c) {
'0'...'9', 'a'...'f', 'A'...'F' => {},
'.' => {
state = .FloatFractionHex;
},
'p', 'P' => {
state = .FloatExponent;
},
else => {
state = .IntegerSuffix;
self.index -= 1;
},
},
.IntegerLiteral => switch (c) {
'0'...'9' => {},
'.' => {
state = .FloatFraction;
},
'e', 'E' => {
state = .FloatExponent;
},
else => {
state = .IntegerSuffix;
self.index -= 1;
},
},
.IntegerSuffix => switch (c) {
'u', 'U' => {
state = .IntegerSuffixU;
},
'l', 'L' => {
state = .IntegerSuffixL;
},
else => {
result.id = .{ .IntegerLiteral = .none };
break;
},
},
.IntegerSuffixU => switch (c) {
'l', 'L' => {
state = .IntegerSuffixUL;
},
else => {
result.id = .{ .IntegerLiteral = .u };
break;
},
},
.IntegerSuffixL => switch (c) {
'l', 'L' => {
state = .IntegerSuffixLL;
},
'u', 'U' => {
result.id = .{ .IntegerLiteral = .lu };
self.index += 1;
break;
},
else => {
result.id = .{ .IntegerLiteral = .l };
break;
},
},
.IntegerSuffixLL => switch (c) {
'u', 'U' => {
result.id = .{ .IntegerLiteral = .llu };
self.index += 1;
break;
},
else => {
result.id = .{ .IntegerLiteral = .ll };
break;
},
},
.IntegerSuffixUL => switch (c) {
'l', 'L' => {
result.id = .{ .IntegerLiteral = .llu };
self.index += 1;
break;
},
else => {
result.id = .{ .IntegerLiteral = .lu };
break;
},
},
.FloatFraction => switch (c) {
'0'...'9' => {},
'e', 'E' => {
state = .FloatExponent;
},
else => {
self.index -= 1;
state = .FloatSuffix;
},
},
.FloatFractionHex => switch (c) {
'0'...'9', 'a'...'f', 'A'...'F' => {},
'p', 'P' => {
state = .FloatExponent;
},
else => {
result.id = .Invalid;
break;
},
},
.FloatExponent => switch (c) {
'+', '-' => {
state = .FloatExponentDigits;
},
else => {
self.index -= 1;
state = .FloatExponentDigits;
},
},
.FloatExponentDigits => switch (c) {
'0'...'9' => {
counter += 1;
},
else => {
if (counter == 0) {
result.id = .Invalid;
break;
}
self.index -= 1;
state = .FloatSuffix;
},
},
.FloatSuffix => switch (c) {
'l', 'L' => {
result.id = .{ .FloatLiteral = .l };
self.index += 1;
break;
},
'f', 'F' => {
result.id = .{ .FloatLiteral = .f };
self.index += 1;
break;
},
else => {
result.id = .{ .FloatLiteral = .none };
break;
},
},
}
} else if (self.index == self.buffer.len) {
switch (state) {
.Start => {},
.u, .u8, .U, .L, .Identifier => {
result.id = Token.getKeyword(self.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier;
},
.Cr,
.BackSlash,
.BackSlashCr,
.Period2,
.StringLiteral,
.CharLiteralStart,
.CharLiteral,
.EscapeSequence,
.CrEscape,
.OctalEscape,
.HexEscape,
.UnicodeEscape,
.MultiLineComment,
.MultiLineCommentAsterisk,
.FloatExponent,
.MacroString,
.IntegerLiteralBinaryFirst,
.IntegerLiteralHexFirst,
=> result.id = .Invalid,
.FloatExponentDigits => result.id = if (counter == 0) .Invalid else .{ .FloatLiteral = .none },
.FloatFraction,
.FloatFractionHex,
=> result.id = .{ .FloatLiteral = .none },
.IntegerLiteralOct,
.IntegerLiteralBinary,
.IntegerLiteralHex,
.IntegerLiteral,
.IntegerSuffix,
.Zero,
=> result.id = .{ .IntegerLiteral = .none },
.IntegerSuffixU => result.id = .{ .IntegerLiteral = .u },
.IntegerSuffixL => result.id = .{ .IntegerLiteral = .l },
.IntegerSuffixLL => result.id = .{ .IntegerLiteral = .ll },
.IntegerSuffixUL => result.id = .{ .IntegerLiteral = .lu },
.FloatSuffix => result.id = .{ .FloatLiteral = .none },
.Equal => result.id = .Equal,
.Bang => result.id = .Bang,
.Minus => result.id = .Minus,
.Slash => result.id = .Slash,
.Ampersand => result.id = .Ampersand,
.Hash => result.id = .Hash,
.Period => result.id = .Period,
.Pipe => result.id = .Pipe,
.AngleBracketAngleBracketRight => result.id = .AngleBracketAngleBracketRight,
.AngleBracketRight => result.id = .AngleBracketRight,
.AngleBracketAngleBracketLeft => result.id = .AngleBracketAngleBracketLeft,
.AngleBracketLeft => result.id = .AngleBracketLeft,
.Plus => result.id = .Plus,
.Percent => result.id = .Percent,
.Caret => result.id = .Caret,
.Asterisk => result.id = .Asterisk,
.LineComment => result.id = .LineComment,
}
}
self.prev_tok_id = result.id;
result.end = self.index;
return result;
}
};
test "operators" {
try expectTokens(
\\ ! != | || |= = ==
\\ ( ) { } [ ] . .. ...
\\ ^ ^= + ++ += - -- -=
\\ * *= % %= -> : ; / /=
\\ , & && &= ? < <= <<
\\ <<= > >= >> >>= ~ # ##
\\
, &[_]Token.Id{
.Bang,
.BangEqual,
.Pipe,
.PipePipe,
.PipeEqual,
.Equal,
.EqualEqual,
.Nl,
.LParen,
.RParen,
.LBrace,
.RBrace,
.LBracket,
.RBracket,
.Period,
.Period,
.Period,
.Ellipsis,
.Nl,
.Caret,
.CaretEqual,
.Plus,
.PlusPlus,
.PlusEqual,
.Minus,
.MinusMinus,
.MinusEqual,
.Nl,
.Asterisk,
.AsteriskEqual,
.Percent,
.PercentEqual,
.Arrow,
.Colon,
.Semicolon,
.Slash,
.SlashEqual,
.Nl,
.Comma,
.Ampersand,
.AmpersandAmpersand,
.AmpersandEqual,
.QuestionMark,
.AngleBracketLeft,
.AngleBracketLeftEqual,
.AngleBracketAngleBracketLeft,
.Nl,
.AngleBracketAngleBracketLeftEqual,
.AngleBracketRight,
.AngleBracketRightEqual,
.AngleBracketAngleBracketRight,
.AngleBracketAngleBracketRightEqual,
.Tilde,
.Hash,
.HashHash,
.Nl,
});
}
test "keywords" {
try expectTokens(
\\auto break case char const continue default do
\\double else enum extern float for goto if int
\\long register return short signed sizeof static
\\struct switch typedef union unsigned void volatile
\\while _Bool _Complex _Imaginary inline restrict _Alignas
\\_Alignof _Atomic _Generic _Noreturn _Static_assert _Thread_local
\\
, &[_]Token.Id{
.Keyword_auto,
.Keyword_break,
.Keyword_case,
.Keyword_char,
.Keyword_const,
.Keyword_continue,
.Keyword_default,
.Keyword_do,
.Nl,
.Keyword_double,
.Keyword_else,
.Keyword_enum,
.Keyword_extern,
.Keyword_float,
.Keyword_for,
.Keyword_goto,
.Keyword_if,
.Keyword_int,
.Nl,
.Keyword_long,
.Keyword_register,
.Keyword_return,
.Keyword_short,
.Keyword_signed,
.Keyword_sizeof,
.Keyword_static,
.Nl,
.Keyword_struct,
.Keyword_switch,
.Keyword_typedef,
.Keyword_union,
.Keyword_unsigned,
.Keyword_void,
.Keyword_volatile,
.Nl,
.Keyword_while,
.Keyword_bool,
.Keyword_complex,
.Keyword_imaginary,
.Keyword_inline,
.Keyword_restrict,
.Keyword_alignas,
.Nl,
.Keyword_alignof,
.Keyword_atomic,
.Keyword_generic,
.Keyword_noreturn,
.Keyword_static_assert,
.Keyword_thread_local,
.Nl,
});
}
test "preprocessor keywords" {
try expectTokens(
\\#include <test>
\\#define #include <1
\\#ifdef
\\#ifndef
\\#error
\\#pragma
\\
, &[_]Token.Id{
.Hash,
.Keyword_include,
.MacroString,
.Nl,
.Hash,
.Keyword_define,
.Hash,
.Identifier,
.AngleBracketLeft,
.{ .IntegerLiteral = .none },
.Nl,
.Hash,
.Keyword_ifdef,
.Nl,
.Hash,
.Keyword_ifndef,
.Nl,
.Hash,
.Keyword_error,
.Nl,
.Hash,
.Keyword_pragma,
.Nl,
});
}
test "line continuation" {
try expectTokens(
\\#define foo \
\\ bar
\\"foo\
\\ bar"
\\#define "foo"
\\ "bar"
\\#define "foo" \
\\ "bar"
, &[_]Token.Id{
.Hash,
.Keyword_define,
.Identifier,
.Identifier,
.Nl,
.{ .StringLiteral = .none },
.Nl,
.Hash,
.Keyword_define,
.{ .StringLiteral = .none },
.Nl,
.{ .StringLiteral = .none },
.Nl,
.Hash,
.Keyword_define,
.{ .StringLiteral = .none },
.{ .StringLiteral = .none },
});
}
test "string prefix" {
try expectTokens(
\\"foo"
\\u"foo"
\\u8"foo"
\\U"foo"
\\L"foo"
\\'foo'
\\u'foo'
\\U'foo'
\\L'foo'
\\
, &[_]Token.Id{
.{ .StringLiteral = .none },
.Nl,
.{ .StringLiteral = .utf_16 },
.Nl,
.{ .StringLiteral = .utf_8 },
.Nl,
.{ .StringLiteral = .utf_32 },
.Nl,
.{ .StringLiteral = .wide },
.Nl,
.{ .CharLiteral = .none },
.Nl,
.{ .CharLiteral = .utf_16 },
.Nl,
.{ .CharLiteral = .utf_32 },
.Nl,
.{ .CharLiteral = .wide },
.Nl,
});
}
test "num suffixes" {
try expectTokens(
\\ 1.0f 1.0L 1.0 .0 1.
\\ 0l 0lu 0ll 0llu 0
\\ 1u 1ul 1ull 1
\\ 0x 0b
\\
, &[_]Token.Id{
.{ .FloatLiteral = .f },
.{ .FloatLiteral = .l },
.{ .FloatLiteral = .none },
.{ .FloatLiteral = .none },
.{ .FloatLiteral = .none },
.Nl,
.{ .IntegerLiteral = .l },
.{ .IntegerLiteral = .lu },
.{ .IntegerLiteral = .ll },
.{ .IntegerLiteral = .llu },
.{ .IntegerLiteral = .none },
.Nl,
.{ .IntegerLiteral = .u },
.{ .IntegerLiteral = .lu },
.{ .IntegerLiteral = .llu },
.{ .IntegerLiteral = .none },
.Nl,
.Invalid,
.Invalid,
.Nl,
});
}
fn expectTokens(source: []const u8, expected_tokens: []const Token.Id) !void {
var tokenizer = Tokenizer{
.buffer = source,
};
for (expected_tokens) |expected_token_id| {
const token = tokenizer.next();
if (!std.meta.eql(token.id, expected_token_id)) {
std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.id) });
}
}
const last_token = tokenizer.next();
try std.testing.expect(last_token.id == .Eof);
}
+3 -3
View File
@@ -252,7 +252,7 @@ test "sizeof" {
try testing.expect(sizeof(anyopaque) == 1);
}
pub const CIntLiteralBase = enum { decimal, octal, hexadecimal };
pub const CIntLiteralBase = enum { decimal, octal, hex };
/// Deprecated: use `CIntLiteralBase`
pub const CIntLiteralRadix = CIntLiteralBase;
@@ -289,13 +289,13 @@ pub fn promoteIntLiteral(
}
test "promoteIntLiteral" {
const signed_hex = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .hexadecimal);
const signed_hex = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .hex);
try testing.expectEqual(c_uint, @TypeOf(signed_hex));
if (math.maxInt(c_longlong) == math.maxInt(c_int)) return;
const signed_decimal = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .decimal);
const unsigned = promoteIntLiteral(c_uint, math.maxInt(c_uint) + 1, .hexadecimal);
const unsigned = promoteIntLiteral(c_uint, math.maxInt(c_uint) + 1, .hex);
if (math.maxInt(c_long) > math.maxInt(c_int)) {
try testing.expectEqual(c_long, @TypeOf(signed_decimal));
+1 -1
View File
@@ -4194,7 +4194,7 @@ pub const CImportResult = struct {
/// This API is currently coupled pretty tightly to stage1's needs; it will need to be reworked
/// a bit when we want to start using it from self-hosted.
pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
if (build_options.only_c) unreachable; // @cImport is not needed for bootstrapping
if (build_options.only_core_functionality) @panic("@cImport is not available in a zig2.c build");
const tracy_trace = trace(@src());
defer tracy_trace.end();
+1 -1
View File
@@ -4286,7 +4286,7 @@ fn updateModule(comp: *Compilation) !void {
}
fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilation.CImportResult) !void {
if (build_options.only_c) unreachable; // translate-c is not needed for bootstrapping
if (build_options.only_core_functionality) @panic("@translate-c is not available in a zig2.c build");
assert(comp.c_source_files.len == 1);
const c_source_file = comp.c_source_files[0];
+3 -1
View File
@@ -22,7 +22,9 @@ pub fn with(comptime Properties: type) type {
return .{};
}
pub fn tagFromName(name: []const u8) ?Tag {
return @enumFromInt(name.len);
var res: u16 = 0;
for (name) |c| res +%= c;
return @enumFromInt(res);
}
pub const NameBuf = struct {
pub fn span(_: *const NameBuf) []const u8 {
+309 -265
View File
@@ -1,13 +1,13 @@
const std = @import("std");
const testing = std.testing;
const assert = std.debug.assert;
const clang = @import("clang.zig");
const ctok = std.c.tokenizer;
const CToken = std.c.Token;
const mem = std.mem;
const math = std.math;
const meta = std.meta;
const CallingConvention = std.builtin.CallingConvention;
const clang = @import("clang.zig");
const aro = @import("aro");
const CToken = aro.Tokenizer.Token;
const ast = @import("translate_c/ast.zig");
const Node = ast.Node;
const Tag = Node.Tag;
@@ -190,19 +190,21 @@ pub fn translate(
/// Determines whether macro is of the form: `#define FOO FOO` (Possibly with trailing tokens)
/// Macros of this form will not be translated.
fn isSelfDefinedMacro(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) bool {
const source = getMacroText(unit, c, macro);
var tokenizer = std.c.Tokenizer{
.buffer = source,
fn isSelfDefinedMacro(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) !bool {
const source = try getMacroText(unit, c, macro);
var tokenizer: aro.Tokenizer = .{
.buf = source,
.source = .unused,
.langopts = .{},
};
const name_tok = tokenizer.next();
const name_tok = tokenizer.nextNoWS();
const name = source[name_tok.start..name_tok.end];
const first_tok = tokenizer.next();
const first_tok = tokenizer.nextNoWS();
// We do not just check for `.Identifier` below because keyword tokens are preferentially matched first by
// the tokenizer.
// In other words we would miss `#define inline inline` (`inline` is a valid c89 identifier)
if (first_tok.id == .Eof) return false;
if (first_tok.id == .eof) return false;
return mem.eql(u8, name, source[first_tok.start..first_tok.end]);
}
@@ -223,7 +225,7 @@ fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void {
const raw_name = macro.getName_getNameStart();
const name = try c.str(raw_name);
if (!isSelfDefinedMacro(ast_unit, c, macro)) {
if (!try isSelfDefinedMacro(ast_unit, c, macro)) {
try c.global_names.put(c.gpa, name, {});
}
},
@@ -5159,16 +5161,16 @@ pub const PatternList = struct {
/// Assumes that `ms` represents a tokenized function-like macro.
fn buildArgsHash(allocator: mem.Allocator, ms: MacroSlicer, hash: *ArgsPositionMap) MacroProcessingError!void {
assert(ms.tokens.len > 2);
assert(ms.tokens[0].id == .Identifier);
assert(ms.tokens[1].id == .LParen);
assert(ms.tokens[0].id == .identifier or ms.tokens[0].id == .extended_identifier);
assert(ms.tokens[1].id == .l_paren);
var i: usize = 2;
while (true) : (i += 1) {
const token = ms.tokens[i];
switch (token.id) {
.RParen => break,
.Comma => continue,
.Identifier => {
.r_paren => break,
.comma => continue,
.identifier, .extended_identifier => {
const identifier = ms.slice(token);
try hash.put(allocator, identifier, i);
},
@@ -5220,18 +5222,18 @@ pub const PatternList = struct {
if (args_hash.count() != self.args_hash.count()) return false;
var i: usize = 2;
while (self.tokens[i].id != .RParen) : (i += 1) {}
while (self.tokens[i].id != .r_paren) : (i += 1) {}
const pattern_slicer = MacroSlicer{ .source = self.source, .tokens = self.tokens };
while (i < self.tokens.len) : (i += 1) {
const pattern_token = self.tokens[i];
const macro_token = ms.tokens[i];
if (meta.activeTag(pattern_token.id) != meta.activeTag(macro_token.id)) return false;
if (pattern_token.id != macro_token.id) return false;
const pattern_bytes = pattern_slicer.slice(pattern_token);
const macro_bytes = ms.slice(macro_token);
switch (pattern_token.id) {
.Identifier => {
.identifier, .extended_identifier => {
const pattern_arg_index = self.args_hash.get(pattern_bytes);
const macro_arg_index = args_hash.get(macro_bytes);
@@ -5243,7 +5245,7 @@ pub const PatternList = struct {
return false;
}
},
.MacroString, .StringLiteral, .CharLiteral, .IntegerLiteral, .FloatLiteral => {
.string_literal, .char_literal, .pp_num => {
if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
},
else => {
@@ -5359,13 +5361,13 @@ const MacroCtx = struct {
return self.list[self.i].id;
}
fn skip(self: *MacroCtx, c: *Context, expected_id: std.meta.Tag(CToken.Id)) ParseError!void {
fn skip(self: *MacroCtx, c: *Context, expected_id: CToken.Id) ParseError!void {
const next_id = self.next().?;
if (next_id != expected_id) {
if (next_id != expected_id and !(expected_id == .identifier and next_id == .extended_identifier)) {
try self.fail(
c,
"unable to translate C expr: expected '{s}' instead got '{s}'",
.{ CToken.Id.symbolName(expected_id), next_id.symbol() },
.{ expected_id.symbol(), next_id.symbol() },
);
return error.ParseError;
}
@@ -5396,12 +5398,12 @@ const MacroCtx = struct {
while (i < self.list.len) : (i += 1) {
const token = self.list[i];
switch (token.id) {
.Period, .Arrow => i += 1, // skip next token since field identifiers can be unknown
.Keyword_struct, .Keyword_union, .Keyword_enum => if (!last_is_type_kw) {
.period, .arrow => i += 1, // skip next token since field identifiers can be unknown
.keyword_struct, .keyword_union, .keyword_enum => if (!last_is_type_kw) {
last_is_type_kw = true;
continue;
},
.Identifier => {
.identifier, .extended_identifier => {
const identifier = slicer.slice(token);
const is_param = for (params) |param| {
if (param.name != null and mem.eql(u8, identifier, param.name.?)) break true;
@@ -5422,31 +5424,38 @@ const MacroCtx = struct {
};
fn tokenizeMacro(source: []const u8, tok_list: *std.ArrayList(CToken)) Error!void {
var tokenizer = std.c.Tokenizer{
.buffer = source,
var tokenizer: aro.Tokenizer = .{
.buf = source,
.source = .unused,
.langopts = .{},
};
while (true) {
const tok = tokenizer.next();
switch (tok.id) {
.Nl, .Eof => {
.whitespace => continue,
.nl, .eof => {
try tok_list.append(tok);
break;
},
.LineComment, .MultiLineComment => continue,
else => {},
}
try tok_list.append(tok);
}
}
fn getMacroText(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) []const u8 {
fn getMacroText(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) ![]const u8 {
const begin_loc = macro.getSourceRange_getBegin();
const end_loc = clang.Lexer.getLocForEndOfToken(macro.getSourceRange_getEnd(), c.source_manager, unit);
const begin_c = c.source_manager.getCharacterData(begin_loc);
const end_c = c.source_manager.getCharacterData(end_loc);
const slice_len = @intFromPtr(end_c) - @intFromPtr(begin_c);
return begin_c[0..slice_len];
var comp = aro.Compilation.init(c.gpa);
defer comp.deinit();
const result = comp.addSourceFromBuffer("", begin_c[0..slice_len]) catch return error.OutOfMemory;
return c.arena.dupe(u8, result.buf);
}
fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
@@ -5471,7 +5480,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
continue;
}
const source = getMacroText(unit, c, macro);
const source = try getMacroText(unit, c, macro);
try tokenizeMacro(source, &tok_list);
@@ -5485,7 +5494,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
var macro_fn = false;
switch (macro_ctx.peek().?) {
.Identifier => {
.identifier, .extended_identifier => {
// if it equals itself, ignore. for example, from stdio.h:
// #define stdin stdin
const tok = macro_ctx.list[1];
@@ -5494,7 +5503,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
continue;
}
},
.Nl, .Eof => {
.nl, .eof => {
// this means it is a macro without a value
// We define it as an empty string so that it can still be used with ++
const str_node = try Tag.string_literal.create(c.arena, "\"\"");
@@ -5503,7 +5512,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
try c.global_scope.blank_macros.put(name, {});
continue;
},
.LParen => {
.l_paren => {
// if the name is immediately followed by a '(' then it is a function
macro_fn = macro_ctx.list[0].end == macro_ctx.list[1].start;
},
@@ -5534,7 +5543,7 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
// Check if the macro only uses other blank macros.
while (true) {
switch (m.peek().?) {
.Identifier => {
.identifier, .extended_identifier => {
const tok = m.list[m.i + 1];
const slice = m.source[tok.start..tok.end];
if (c.global_scope.blank_macros.contains(slice)) {
@@ -5542,7 +5551,7 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
continue;
}
},
.Eof, .Nl => {
.eof, .nl => {
try c.global_scope.blank_macros.put(m.name, {});
const init_node = try Tag.string_literal.create(c.arena, "\"\"");
const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node });
@@ -5556,7 +5565,7 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
const init_node = try parseCExpr(c, m, scope);
const last = m.next().?;
if (last != .Eof and last != .Nl)
if (last != .eof and last != .nl)
return m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node });
@@ -5578,14 +5587,16 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
defer block_scope.deinit();
const scope = &block_scope.base;
try m.skip(c, .LParen);
try m.skip(c, .l_paren);
var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);
defer fn_params.deinit();
while (true) {
if (m.peek().? != .Identifier) break;
_ = m.next();
switch (m.peek().?) {
.identifier, .extended_identifier => _ = m.next(),
else => break,
}
const mangled_name = try block_scope.makeMangledName(c, m.slice());
try fn_params.append(.{
@@ -5594,11 +5605,11 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
.type = Tag.@"anytype".init(),
});
try block_scope.discardVariable(c, mangled_name);
if (m.peek().? != .Comma) break;
if (m.peek().? != .comma) break;
_ = m.next();
}
try m.skip(c, .RParen);
try m.skip(c, .r_paren);
if (m.checkTranslatableMacro(scope, fn_params.items)) |err| switch (err) {
.undefined_identifier => |ident| return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident}),
@@ -5607,7 +5618,7 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
const expr = try parseCExpr(c, m, scope);
const last = m.next().?;
if (last != .Eof and last != .Nl)
if (last != .eof and last != .nl)
return m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
const typeof_arg = if (expr.castTag(.block)) |some| blk: {
@@ -5644,7 +5655,7 @@ fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
defer block_scope.deinit();
const node = try parseCCondExpr(c, m, &block_scope.base);
if (m.next().? != .Comma) {
if (m.next().? != .comma) {
m.i -= 1;
return node;
}
@@ -5656,7 +5667,7 @@ fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
try block_scope.statements.append(ignore);
last = try parseCCondExpr(c, m, &block_scope.base);
if (m.next().? != .Comma) {
if (m.next().? != .comma) {
m.i -= 1;
break;
}
@@ -5670,118 +5681,135 @@ fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
return try block_scope.complete(c);
}
fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
var lit_bytes = m.slice();
fn parseCNumLit(ctx: *Context, m: *MacroCtx) ParseError!Node {
const lit_bytes = m.slice();
var bytes = try std.ArrayListUnmanaged(u8).initCapacity(ctx.arena, lit_bytes.len + 3);
switch (m.list[m.i].id) {
.IntegerLiteral => |suffix| {
var base: []const u8 = "decimal";
if (lit_bytes.len >= 2 and lit_bytes[0] == '0') {
switch (lit_bytes[1]) {
'0'...'7' => {
// Octal
lit_bytes = try std.fmt.allocPrint(c.arena, "0o{s}", .{lit_bytes[1..]});
base = "octal";
},
'X' => {
// Hexadecimal with capital X, valid in C but not in Zig
lit_bytes = try std.fmt.allocPrint(c.arena, "0x{s}", .{lit_bytes[2..]});
base = "hexadecimal";
},
'x' => {
base = "hexadecimal";
},
else => {},
}
}
const type_node = try Tag.type.create(c.arena, switch (suffix) {
.none => "c_int",
.u => "c_uint",
.l => "c_long",
.lu => "c_ulong",
.ll => "c_longlong",
.llu => "c_ulonglong",
.f => unreachable,
});
lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (suffix) {
.none => @as(u8, 0),
.u, .l => 1,
.lu, .ll => 2,
.llu => 3,
.f => unreachable,
}];
const value = std.fmt.parseInt(i128, lit_bytes, 0) catch math.maxInt(i128);
// make the output less noisy by skipping promoteIntLiteral where
// it's guaranteed to not be required because of C standard type constraints
const guaranteed_to_fit = switch (suffix) {
.none => math.cast(i16, value) != null,
.u => math.cast(u16, value) != null,
.l => math.cast(i32, value) != null,
.lu => math.cast(u32, value) != null,
.ll => math.cast(i64, value) != null,
.llu => math.cast(u64, value) != null,
.f => unreachable,
};
const literal_node = try transCreateNodeNumber(c, lit_bytes, .int);
if (guaranteed_to_fit) {
return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = literal_node });
} else {
return Tag.helpers_promoteIntLiteral.create(c.arena, .{
.type = type_node,
.value = literal_node,
.base = try Tag.enum_literal.create(c.arena, base),
});
const prefix = aro.Tree.Token.NumberPrefix.fromString(lit_bytes);
switch (prefix) {
.binary => bytes.appendSliceAssumeCapacity("0b"),
.octal => bytes.appendSliceAssumeCapacity("0o"),
.hex => bytes.appendSliceAssumeCapacity("0x"),
.decimal => {},
}
const after_prefix = lit_bytes[prefix.stringLen()..];
const after_int = for (after_prefix, 0..) |c, i| switch (c) {
'.' => {
if (i == 0) {
bytes.appendAssumeCapacity('0');
}
break after_prefix[i..];
},
.FloatLiteral => |suffix| {
if (suffix != .none) lit_bytes = lit_bytes[0 .. lit_bytes.len - 1];
if (lit_bytes.len >= 2 and std.ascii.eqlIgnoreCase(lit_bytes[0..2], "0x")) {
if (mem.indexOfScalar(u8, lit_bytes, '.')) |dot_index| {
if (dot_index == 2) {
lit_bytes = try std.fmt.allocPrint(c.arena, "0x0{s}", .{lit_bytes[2..]});
} else if (dot_index + 1 == lit_bytes.len or !std.ascii.isHex(lit_bytes[dot_index + 1])) {
// If the literal lacks a digit after the `.`, we need to
// add one since `0x1.p10` would be invalid syntax in Zig.
lit_bytes = try std.fmt.allocPrint(c.arena, "0x{s}0{s}", .{
lit_bytes[2 .. dot_index + 1],
lit_bytes[dot_index + 1 ..],
});
}
}
if (lit_bytes[1] == 'X') {
// Hexadecimal with capital X, valid in C but not in Zig
lit_bytes = try std.fmt.allocPrint(c.arena, "0x{s}", .{lit_bytes[2..]});
}
} else if (mem.indexOfScalar(u8, lit_bytes, '.')) |dot_index| {
if (dot_index == 0) {
lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes});
} else if (dot_index + 1 == lit_bytes.len or !std.ascii.isDigit(lit_bytes[dot_index + 1])) {
// If the literal lacks a digit after the `.`, we need to
// add one since `1.` or `1.e10` would be invalid syntax in Zig.
lit_bytes = try std.fmt.allocPrint(c.arena, "{s}0{s}", .{
lit_bytes[0 .. dot_index + 1],
lit_bytes[dot_index + 1 ..],
});
}
}
const type_node = try Tag.type.create(c.arena, switch (suffix) {
.f => "f32",
.none => "f64",
.l => "c_longdouble",
else => unreachable,
});
const rhs = try transCreateNodeNumber(c, lit_bytes, .float);
return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs });
'e', 'E' => {
if (prefix != .hex) break after_prefix[i..];
bytes.appendAssumeCapacity(c);
},
else => unreachable,
'p', 'P' => break after_prefix[i..],
'0'...'9', 'a'...'d', 'A'...'D', 'f', 'F' => {
if (!prefix.digitAllowed(c)) break after_prefix[i..];
bytes.appendAssumeCapacity(c);
},
'\'' => {
bytes.appendAssumeCapacity('_');
},
else => break after_prefix[i..],
} else "";
const after_frac = frac: {
if (after_int.len == 0 or after_int[0] != '.') break :frac after_int;
bytes.appendAssumeCapacity('.');
for (after_int[1..], 1..) |c, i| {
if (c == '\'') {
bytes.appendAssumeCapacity('_');
continue;
}
if (!prefix.digitAllowed(c)) break :frac after_int[i..];
bytes.appendAssumeCapacity(c);
}
break :frac "";
};
const suffix_str = exponent: {
if (after_frac.len == 0) break :exponent after_frac;
switch (after_frac[0]) {
'e', 'E' => {},
'p', 'P' => if (prefix != .hex) break :exponent after_frac,
else => break :exponent after_frac,
}
bytes.appendAssumeCapacity(after_frac[0]);
for (after_frac[1..], 1..) |c, i| switch (c) {
'+', '-', '0'...'9' => {
bytes.appendAssumeCapacity(c);
},
'\'' => {
bytes.appendAssumeCapacity('_');
},
else => break :exponent after_frac[i..],
};
break :exponent "";
};
const is_float = after_int.len != suffix_str.len;
const suffix = aro.Tree.Token.NumberSuffix.fromString(suffix_str, if (is_float) .float else .int) orelse {
try m.fail(ctx, "invalid number suffix: '{s}'", .{suffix_str});
return error.ParseError;
};
if (suffix.isImaginary()) {
try m.fail(ctx, "TODO: imaginary literals", .{});
return error.ParseError;
}
if (suffix.isBitInt()) {
try m.fail(ctx, "TODO: _BitInt literals", .{});
return error.ParseError;
}
if (is_float) {
const type_node = try Tag.type.create(ctx.arena, switch (suffix) {
.F16 => "f16",
.F => "f32",
.None => "f64",
.L => "c_longdouble",
.W => "f80",
.Q, .F128 => "f128",
else => unreachable,
});
const rhs = try Tag.float_literal.create(ctx.arena, bytes.items);
return Tag.as.create(ctx.arena, .{ .lhs = type_node, .rhs = rhs });
} else {
const type_node = try Tag.type.create(ctx.arena, switch (suffix) {
.None => "c_int",
.U => "c_uint",
.L => "c_long",
.UL => "c_ulong",
.LL => "c_longlong",
.ULL => "c_ulonglong",
else => unreachable,
});
const value = std.fmt.parseInt(i128, bytes.items, 0) catch math.maxInt(i128);
// make the output less noisy by skipping promoteIntLiteral where
// it's guaranteed to not be required because of C standard type constraints
const guaranteed_to_fit = switch (suffix) {
.None => math.cast(i16, value) != null,
.U => math.cast(u16, value) != null,
.L => math.cast(i32, value) != null,
.UL => math.cast(u32, value) != null,
.LL => math.cast(i64, value) != null,
.ULL => math.cast(u64, value) != null,
else => unreachable,
};
const literal_node = try Tag.integer_literal.create(ctx.arena, bytes.items);
if (guaranteed_to_fit) {
return Tag.as.create(ctx.arena, .{ .lhs = type_node, .rhs = literal_node });
} else {
return Tag.helpers_promoteIntLiteral.create(ctx.arena, .{
.type = type_node,
.value = literal_node,
.base = try Tag.enum_literal.create(ctx.arena, @tagName(prefix)),
});
}
}
}
@@ -5800,17 +5828,17 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
} else return source;
var bytes = try ctx.arena.alloc(u8, source.len * 2);
var state: enum {
Start,
Escape,
Hex,
Octal,
} = .Start;
start,
escape,
hex,
octal,
} = .start;
var i: usize = 0;
var count: u8 = 0;
var num: u8 = 0;
for (source) |c| {
switch (state) {
.Escape => {
.escape => {
switch (c) {
'n', 'r', 't', '\\', '\'', '\"' => {
bytes[i] = c;
@@ -5818,11 +5846,11 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
'0'...'7' => {
count += 1;
num += c - '0';
state = .Octal;
state = .octal;
bytes[i] = 'x';
},
'x' => {
state = .Hex;
state = .hex;
bytes[i] = 'x';
},
'a' => {
@@ -5867,10 +5895,10 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
},
}
i += 1;
if (state == .Escape)
state = .Start;
if (state == .escape)
state = .start;
},
.Start => {
.start => {
if (c == '\t') {
bytes[i] = '\\';
i += 1;
@@ -5879,12 +5907,12 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
continue;
}
if (c == '\\') {
state = .Escape;
state = .escape;
}
bytes[i] = c;
i += 1;
},
.Hex => {
.hex => {
switch (c) {
'0'...'9' => {
num = std.math.mul(u8, num, 16) catch {
@@ -5911,15 +5939,15 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
i += std.fmt.formatIntBuf(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
num = 0;
if (c == '\\')
state = .Escape
state = .escape
else
state = .Start;
state = .start;
bytes[i] = c;
i += 1;
},
}
},
.Octal => {
.octal => {
const accept_digit = switch (c) {
// The maximum length of a octal literal is 3 digits
'0'...'7' => count < 3,
@@ -5938,16 +5966,16 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
num = 0;
count = 0;
if (c == '\\')
state = .Escape
state = .escape
else
state = .Start;
state = .start;
bytes[i] = c;
i += 1;
}
},
}
}
if (state == .Hex or state == .Octal)
if (state == .hex or state == .octal)
i += std.fmt.formatIntBuf(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
return bytes[0..i];
}
@@ -5972,7 +6000,12 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N
const tok = m.next().?;
const slice = m.slice();
switch (tok) {
.CharLiteral => {
.char_literal,
.char_literal_utf_8,
.char_literal_utf_16,
.char_literal_utf_32,
.char_literal_wide,
=> {
if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
return Tag.char_literal.create(c.arena, try escapeUnprintables(c, m));
} else {
@@ -5980,13 +6013,18 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N
return Tag.integer_literal.create(c.arena, str);
}
},
.StringLiteral => {
.string_literal,
.string_literal_utf_16,
.string_literal_utf_8,
.string_literal_utf_32,
.string_literal_wide,
=> {
return Tag.string_literal.create(c.arena, try escapeUnprintables(c, m));
},
.IntegerLiteral, .FloatLiteral => {
.pp_num => {
return parseCNumLit(c, m);
},
.Identifier => {
.identifier, .extended_identifier => {
if (c.global_scope.blank_macros.contains(slice)) {
return parseCPrimaryExprInner(c, m, scope);
}
@@ -5996,10 +6034,10 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N
scope.skipVariableDiscard(identifier.castTag(.identifier).?.data);
return identifier;
},
.LParen => {
.l_paren => {
const inner_node = try parseCExpr(c, m, scope);
try m.skip(c, .RParen);
try m.skip(c, .r_paren);
return inner_node;
},
else => {
@@ -6022,8 +6060,13 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
// after a primary expression.
while (true) {
switch (m.peek().?) {
.StringLiteral => {},
.Identifier => {
.string_literal,
.string_literal_utf_16,
.string_literal_utf_8,
.string_literal_utf_32,
.string_literal_wide,
=> {},
.identifier, .extended_identifier => {
const tok = m.list[m.i + 1];
const slice = m.source[tok.start..tok.end];
if (c.global_scope.blank_macros.contains(slice)) {
@@ -6057,20 +6100,20 @@ fn macroIntToBool(c: *Context, node: Node) !Node {
fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
const node = try parseCOrExpr(c, m, scope);
if (m.peek().? != .QuestionMark) {
if (m.peek().? != .question_mark) {
return node;
}
_ = m.next();
const then_body = try parseCOrExpr(c, m, scope);
try m.skip(c, .Colon);
try m.skip(c, .colon);
const else_body = try parseCCondExpr(c, m, scope);
return Tag.@"if".create(c.arena, .{ .cond = node, .then = then_body, .@"else" = else_body });
}
fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
var node = try parseCAndExpr(c, m, scope);
while (m.next().? == .PipePipe) {
while (m.next().? == .pipe_pipe) {
const lhs = try macroIntToBool(c, node);
const rhs = try macroIntToBool(c, try parseCAndExpr(c, m, scope));
node = try Tag.@"or".create(c.arena, .{ .lhs = lhs, .rhs = rhs });
@@ -6081,7 +6124,7 @@ fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
var node = try parseCBitOrExpr(c, m, scope);
while (m.next().? == .AmpersandAmpersand) {
while (m.next().? == .ampersand_ampersand) {
const lhs = try macroIntToBool(c, node);
const rhs = try macroIntToBool(c, try parseCBitOrExpr(c, m, scope));
node = try Tag.@"and".create(c.arena, .{ .lhs = lhs, .rhs = rhs });
@@ -6092,7 +6135,7 @@ fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
var node = try parseCBitXorExpr(c, m, scope);
while (m.next().? == .Pipe) {
while (m.next().? == .pipe) {
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCBitXorExpr(c, m, scope));
node = try Tag.bit_or.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
@@ -6103,7 +6146,7 @@ fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
var node = try parseCBitAndExpr(c, m, scope);
while (m.next().? == .Caret) {
while (m.next().? == .caret) {
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCBitAndExpr(c, m, scope));
node = try Tag.bit_xor.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
@@ -6114,7 +6157,7 @@ fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
var node = try parseCEqExpr(c, m, scope);
while (m.next().? == .Ampersand) {
while (m.next().? == .ampersand) {
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCEqExpr(c, m, scope));
node = try Tag.bit_and.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
@@ -6127,13 +6170,13 @@ fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
var node = try parseCRelExpr(c, m, scope);
while (true) {
switch (m.peek().?) {
.BangEqual => {
.bang_equal => {
_ = m.next();
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCRelExpr(c, m, scope));
node = try Tag.not_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
},
.EqualEqual => {
.equal_equal => {
_ = m.next();
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCRelExpr(c, m, scope));
@@ -6148,25 +6191,25 @@ fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
var node = try parseCShiftExpr(c, m, scope);
while (true) {
switch (m.peek().?) {
.AngleBracketRight => {
.angle_bracket_right => {
_ = m.next();
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCShiftExpr(c, m, scope));
node = try Tag.greater_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
},
.AngleBracketRightEqual => {
.angle_bracket_right_equal => {
_ = m.next();
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCShiftExpr(c, m, scope));
node = try Tag.greater_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
},
.AngleBracketLeft => {
.angle_bracket_left => {
_ = m.next();
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCShiftExpr(c, m, scope));
node = try Tag.less_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
},
.AngleBracketLeftEqual => {
.angle_bracket_left_equal => {
_ = m.next();
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCShiftExpr(c, m, scope));
@@ -6181,13 +6224,13 @@ fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
var node = try parseCAddSubExpr(c, m, scope);
while (true) {
switch (m.peek().?) {
.AngleBracketAngleBracketLeft => {
.angle_bracket_angle_bracket_left => {
_ = m.next();
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCAddSubExpr(c, m, scope));
node = try Tag.shl.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
},
.AngleBracketAngleBracketRight => {
.angle_bracket_angle_bracket_right => {
_ = m.next();
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCAddSubExpr(c, m, scope));
@@ -6202,13 +6245,13 @@ fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
var node = try parseCMulExpr(c, m, scope);
while (true) {
switch (m.peek().?) {
.Plus => {
.plus => {
_ = m.next();
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCMulExpr(c, m, scope));
node = try Tag.add.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
},
.Minus => {
.minus => {
_ = m.next();
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCMulExpr(c, m, scope));
@@ -6223,17 +6266,17 @@ fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
var node = try parseCCastExpr(c, m, scope);
while (true) {
switch (m.next().?) {
.Asterisk => {
.asterisk => {
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCCastExpr(c, m, scope));
node = try Tag.mul.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
},
.Slash => {
.slash => {
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCCastExpr(c, m, scope));
node = try Tag.macro_arithmetic.create(c.arena, .{ .op = .div, .lhs = lhs, .rhs = rhs });
},
.Percent => {
.percent => {
const lhs = try macroIntFromBool(c, node);
const rhs = try macroIntFromBool(c, try parseCCastExpr(c, m, scope));
node = try Tag.macro_arithmetic.create(c.arena, .{ .op = .rem, .lhs = lhs, .rhs = rhs });
@@ -6248,17 +6291,18 @@ fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
fn parseCCastExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
switch (m.next().?) {
.LParen => {
.l_paren => {
if (try parseCTypeName(c, m, scope, true)) |type_name| {
while (true) {
const next_token = m.next().?;
switch (next_token) {
.RParen => break,
.r_paren => break,
else => |next_tag| {
// Skip trailing blank defined before the RParen.
if (next_tag == .Identifier and c.global_scope.blank_macros.contains(m.slice())) {
if ((next_tag == .identifier or next_tag == .extended_identifier) and
c.global_scope.blank_macros.contains(m.slice()))
continue;
}
try m.fail(
c,
"unable to translate C expr: expected ')' instead got '{s}'",
@@ -6268,7 +6312,7 @@ fn parseCCastExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
},
}
}
if (m.peek().? == .LBrace) {
if (m.peek().? == .l_brace) {
// initializer list
return parseCPostfixExpr(c, m, scope, type_name);
}
@@ -6294,7 +6338,7 @@ fn parseCTypeName(c: *Context, m: *MacroCtx, scope: *Scope, allow_fail: bool) Pa
fn parseCSpecifierQualifierList(c: *Context, m: *MacroCtx, scope: *Scope, allow_fail: bool) ParseError!?Node {
const tok = m.next().?;
switch (tok) {
.Identifier => {
.identifier, .extended_identifier => {
if (c.global_scope.blank_macros.contains(m.slice())) {
return try parseCSpecifierQualifierList(c, m, scope, allow_fail);
}
@@ -6304,25 +6348,25 @@ fn parseCSpecifierQualifierList(c: *Context, m: *MacroCtx, scope: *Scope, allow_
return try Tag.identifier.create(c.arena, mangled_name);
}
},
.Keyword_void => return try Tag.type.create(c.arena, "anyopaque"),
.Keyword_bool => return try Tag.type.create(c.arena, "bool"),
.Keyword_char,
.Keyword_int,
.Keyword_short,
.Keyword_long,
.Keyword_float,
.Keyword_double,
.Keyword_signed,
.Keyword_unsigned,
.Keyword_complex,
.keyword_void => return try Tag.type.create(c.arena, "anyopaque"),
.keyword_bool => return try Tag.type.create(c.arena, "bool"),
.keyword_char,
.keyword_int,
.keyword_short,
.keyword_long,
.keyword_float,
.keyword_double,
.keyword_signed,
.keyword_unsigned,
.keyword_complex,
=> {
m.i -= 1;
return try parseCNumericType(c, m);
},
.Keyword_enum, .Keyword_struct, .Keyword_union => {
.keyword_enum, .keyword_struct, .keyword_union => {
// struct Foo will be declared as struct_Foo by transRecordDecl
const slice = m.slice();
try m.skip(c, .Identifier);
try m.skip(c, .identifier);
const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() });
return try Tag.identifier.create(c.arena, name);
@@ -6364,15 +6408,15 @@ fn parseCNumericType(c: *Context, m: *MacroCtx) ParseError!Node {
var i: u8 = 0;
while (i < math.maxInt(u8)) : (i += 1) {
switch (m.next().?) {
.Keyword_double => kw.double += 1,
.Keyword_long => kw.long += 1,
.Keyword_int => kw.int += 1,
.Keyword_float => kw.float += 1,
.Keyword_short => kw.short += 1,
.Keyword_char => kw.char += 1,
.Keyword_unsigned => kw.unsigned += 1,
.Keyword_signed => kw.signed += 1,
.Keyword_complex => kw.complex += 1,
.keyword_double => kw.double += 1,
.keyword_long => kw.long += 1,
.keyword_int => kw.int += 1,
.keyword_float => kw.float += 1,
.keyword_short => kw.short += 1,
.keyword_char => kw.char += 1,
.keyword_unsigned => kw.unsigned += 1,
.keyword_signed => kw.signed += 1,
.keyword_complex => kw.complex += 1,
else => {
m.i -= 1;
break;
@@ -6442,11 +6486,11 @@ fn parseCNumericType(c: *Context, m: *MacroCtx) ParseError!Node {
fn parseCAbstractDeclarator(c: *Context, m: *MacroCtx, node: Node) ParseError!Node {
switch (m.next().?) {
.Asterisk => {
.asterisk => {
// last token of `node`
const prev_id = m.list[m.i - 1].id;
if (prev_id == .Keyword_void) {
if (prev_id == .keyword_void) {
const ptr = try Tag.single_pointer.create(c.arena, .{
.is_const = false,
.is_volatile = false,
@@ -6472,28 +6516,28 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?Node)
var node = type_name orelse try parseCPrimaryExpr(c, m, scope);
while (true) {
switch (m.next().?) {
.Period => {
try m.skip(c, .Identifier);
.period => {
try m.skip(c, .identifier);
node = try Tag.field_access.create(c.arena, .{ .lhs = node, .field_name = m.slice() });
},
.Arrow => {
try m.skip(c, .Identifier);
.arrow => {
try m.skip(c, .identifier);
const deref = try Tag.deref.create(c.arena, node);
node = try Tag.field_access.create(c.arena, .{ .lhs = deref, .field_name = m.slice() });
},
.LBracket => {
.l_bracket => {
const index_val = try macroIntFromBool(c, try parseCExpr(c, m, scope));
const index = try Tag.as.create(c.arena, .{
.lhs = try Tag.type.create(c.arena, "usize"),
.rhs = try Tag.int_cast.create(c.arena, index_val),
});
node = try Tag.array_access.create(c.arena, .{ .lhs = node, .rhs = index });
try m.skip(c, .RBracket);
try m.skip(c, .r_bracket);
},
.LParen => {
if (m.peek().? == .RParen) {
.l_paren => {
if (m.peek().? == .r_paren) {
m.i += 1;
node = try Tag.call.create(c.arena, .{ .lhs = node, .args = &[0]Node{} });
} else {
@@ -6504,8 +6548,8 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?Node)
try args.append(arg);
const next_id = m.next().?;
switch (next_id) {
.Comma => {},
.RParen => break,
.comma => {},
.r_paren => break,
else => {
try m.fail(c, "unable to translate C expr: expected ',' or ')' instead got '{s}'", .{next_id.symbol()});
return error.ParseError;
@@ -6515,24 +6559,24 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?Node)
node = try Tag.call.create(c.arena, .{ .lhs = node, .args = try c.arena.dupe(Node, args.items) });
}
},
.LBrace => {
.l_brace => {
// Check for designated field initializers
if (m.peek().? == .Period) {
if (m.peek().? == .period) {
var init_vals = std.ArrayList(ast.Payload.ContainerInitDot.Initializer).init(c.gpa);
defer init_vals.deinit();
while (true) {
try m.skip(c, .Period);
try m.skip(c, .Identifier);
try m.skip(c, .period);
try m.skip(c, .identifier);
const name = m.slice();
try m.skip(c, .Equal);
try m.skip(c, .equal);
const val = try parseCCondExpr(c, m, scope);
try init_vals.append(.{ .name = name, .value = val });
const next_id = m.next().?;
switch (next_id) {
.Comma => {},
.RBrace => break,
.comma => {},
.r_brace => break,
else => {
try m.fail(c, "unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
return error.ParseError;
@@ -6552,8 +6596,8 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?Node)
try init_vals.append(val);
const next_id = m.next().?;
switch (next_id) {
.Comma => {},
.RBrace => break,
.comma => {},
.r_brace => break,
else => {
try m.fail(c, "unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
return error.ParseError;
@@ -6563,7 +6607,7 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?Node)
const tuple_node = try Tag.tuple.create(c.arena, try c.arena.dupe(Node, init_vals.items));
node = try Tag.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node });
},
.PlusPlus, .MinusMinus => {
.plus_plus, .minus_minus => {
try m.fail(c, "TODO postfix inc/dec expr", .{});
return error.ParseError;
},
@@ -6577,47 +6621,47 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?Node)
fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
switch (m.next().?) {
.Bang => {
.bang => {
const operand = try macroIntToBool(c, try parseCCastExpr(c, m, scope));
return Tag.not.create(c.arena, operand);
},
.Minus => {
.minus => {
const operand = try macroIntFromBool(c, try parseCCastExpr(c, m, scope));
return Tag.negate.create(c.arena, operand);
},
.Plus => return try parseCCastExpr(c, m, scope),
.Tilde => {
.plus => return try parseCCastExpr(c, m, scope),
.tilde => {
const operand = try macroIntFromBool(c, try parseCCastExpr(c, m, scope));
return Tag.bit_not.create(c.arena, operand);
},
.Asterisk => {
.asterisk => {
const operand = try parseCCastExpr(c, m, scope);
return Tag.deref.create(c.arena, operand);
},
.Ampersand => {
.ampersand => {
const operand = try parseCCastExpr(c, m, scope);
return Tag.address_of.create(c.arena, operand);
},
.Keyword_sizeof => {
const operand = if (m.peek().? == .LParen) blk: {
.keyword_sizeof => {
const operand = if (m.peek().? == .l_paren) blk: {
_ = m.next();
const inner = (try parseCTypeName(c, m, scope, false)).?;
try m.skip(c, .RParen);
try m.skip(c, .r_paren);
break :blk inner;
} else try parseCUnaryExpr(c, m, scope);
return Tag.helpers_sizeof.create(c.arena, operand);
},
.Keyword_alignof => {
.keyword_alignof => {
// TODO this won't work if using <stdalign.h>'s
// #define alignof _Alignof
try m.skip(c, .LParen);
try m.skip(c, .l_paren);
const operand = (try parseCTypeName(c, m, scope, false)).?;
try m.skip(c, .RParen);
try m.skip(c, .r_paren);
return Tag.alignof.create(c.arena, operand);
},
.PlusPlus, .MinusMinus => {
.plus_plus, .minus_minus => {
try m.fail(c, "TODO unary inc/dec expr", .{});
return error.ParseError;
},
+7 -7
View File
@@ -424,7 +424,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ });
\\}
,
\\pub const B = A(@as(f32, 0.0));
\\pub const B = A(@as(f32, 0));
});
cases.add("complex switch",
@@ -633,7 +633,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("#define hex literal with capital X",
\\#define VAL 0XF00D
, &[_][]const u8{
\\pub const VAL = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0xF00D, .hexadecimal);
\\pub const VAL = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0xF00D, .hex);
});
cases.add("anonymous struct & unions",
@@ -1243,12 +1243,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\extern const long double my_extended_precision_longdouble = 1.0000000000000003l;
, &([_][]const u8{
"pub const foo = @as(f32, 3.14);",
"pub const bar = @as(c_longdouble, 16.0e-2);",
"pub const bar = @as(c_longdouble, 16.e-2);",
"pub const FOO = @as(f64, 0.12345);",
"pub const BAR = @as(f64, 0.12345);",
"pub const baz = @as(f64, 1e1);",
"pub const BAZ = @as(f32, 42e-3);",
"pub const foobar = -@as(c_longdouble, 73.0);",
"pub const foobar = -@as(c_longdouble, 73);",
"pub export const my_float: f32 = 1.0;",
"pub export const my_double: f64 = 1.0;",
"pub export const my_longdouble: c_longdouble = 1.0;",
@@ -1272,7 +1272,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
"pub const BAR = -@as(f32, 0x8F.BP5);",
"pub const FOOBAR = @as(f64, 0x0P+0);",
"pub const BAZ = -@as(f64, 0x0.0a5dp+12);",
"pub const FOOBAZ = @as(c_longdouble, 0xfE.0P-1);",
"pub const FOOBAZ = @as(c_longdouble, 0xfE.P-1);",
});
cases.add("comments",
@@ -3730,7 +3730,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, &[_][]const u8{
\\pub const NULL = @import("std").zig.c_translation.cast(?*anyopaque, @as(c_int, 0));
,
\\pub const FOO = @import("std").zig.c_translation.cast(c_int, @import("std").zig.c_translation.promoteIntLiteral(c_int, 0x8000, .hexadecimal));
\\pub const FOO = @import("std").zig.c_translation.cast(c_int, @import("std").zig.c_translation.promoteIntLiteral(c_int, 0x8000, .hex));
});
if (builtin.abi == .msvc) {
@@ -3812,7 +3812,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const MAY_NEED_PROMOTION_1 = @import("std").zig.c_translation.promoteIntLiteral(c_int, 10241024, .decimal);
\\pub const MAY_NEED_PROMOTION_2 = @import("std").zig.c_translation.promoteIntLiteral(c_long, 307230723072, .decimal);
\\pub const MAY_NEED_PROMOTION_3 = @import("std").zig.c_translation.promoteIntLiteral(c_ulong, 819281928192, .decimal);
\\pub const MAY_NEED_PROMOTION_HEX = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0x80000000, .hexadecimal);
\\pub const MAY_NEED_PROMOTION_HEX = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0x80000000, .hex);
\\pub const MAY_NEED_PROMOTION_OCT = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0o20000000000, .octal);
});