| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const expect = std.testing.expect; | 2 | const mem = std.mem; |
| 3 | | 3 | |
| 4 | pub const Source = struct { | 4 | pub const Source = struct { |
| 5 | buffer: []const u8, | 5 | buffer: []const u8, |
| ... | @@ -7,11 +7,19 @@ pub const Source = struct { | ... | @@ -7,11 +7,19 @@ pub const Source = struct { |
| 7 | }; | 7 | }; |
| 8 | | 8 | |
| 9 | pub const Token = struct { | 9 | pub const Token = struct { |
| 10 | id: union(enum) { | 10 | id: Id, |
| | 11 | start: usize, |
| | 12 | end: usize, |
| | 13 | source: *Source, |
| | 14 | |
| | 15 | pub const Id = union(enum) { |
| 11 | Invalid, | 16 | Invalid, |
| 12 | Eof, | 17 | Eof, |
| 13 | Nl, | 18 | Nl, |
| 14 | Identifier, | 19 | Identifier, |
| | 20 | |
| | 21 | /// special case for #include <...> |
| | 22 | MacroString, |
| 15 | StringLiteral: StrKind, | 23 | StringLiteral: StrKind, |
| 16 | CharLiteral: StrKind, | 24 | CharLiteral: StrKind, |
| 17 | IntegerLiteral: NumSuffix, | 25 | IntegerLiteral: NumSuffix, |
| ... | @@ -68,10 +76,160 @@ pub const Token = struct { | ... | @@ -68,10 +76,160 @@ pub const Token = struct { |
| 68 | MultiLineComment, | 76 | MultiLineComment, |
| 69 | Hash, | 77 | Hash, |
| 70 | HashHash, | 78 | HashHash, |
| 71 | }, | 79 | |
| 72 | start: usize, | 80 | Keyword_auto, |
| 73 | end: usize, | 81 | Keyword_break, |
| 74 | source: *Source, | 82 | Keyword_case, |
| | 83 | Keyword_char, |
| | 84 | Keyword_const, |
| | 85 | Keyword_continue, |
| | 86 | Keyword_default, |
| | 87 | Keyword_do, |
| | 88 | Keyword_double, |
| | 89 | Keyword_else, |
| | 90 | Keyword_enum, |
| | 91 | Keyword_extern, |
| | 92 | Keyword_float, |
| | 93 | Keyword_for, |
| | 94 | Keyword_goto, |
| | 95 | Keyword_if, |
| | 96 | Keyword_int, |
| | 97 | Keyword_long, |
| | 98 | Keyword_register, |
| | 99 | Keyword_return, |
| | 100 | Keyword_short, |
| | 101 | Keyword_signed, |
| | 102 | Keyword_sizeof, |
| | 103 | Keyword_static, |
| | 104 | Keyword_struct, |
| | 105 | Keyword_switch, |
| | 106 | Keyword_typedef, |
| | 107 | Keyword_union, |
| | 108 | Keyword_unsigned, |
| | 109 | Keyword_void, |
| | 110 | Keyword_volatile, |
| | 111 | Keyword_while, |
| | 112 | |
| | 113 | // ISO C99 |
| | 114 | Keyword_bool, |
| | 115 | Keyword_complex, |
| | 116 | Keyword_imaginary, |
| | 117 | Keyword_inline, |
| | 118 | Keyword_restrict, |
| | 119 | |
| | 120 | // ISO C11 |
| | 121 | Keyword_alignas, |
| | 122 | Keyword_alignof, |
| | 123 | Keyword_atomic, |
| | 124 | Keyword_generic, |
| | 125 | Keyword_noreturn, |
| | 126 | Keyword_static_assert, |
| | 127 | Keyword_thread_local, |
| | 128 | |
| | 129 | // Preprocessor |
| | 130 | Keyword_include, |
| | 131 | Keyword_define, |
| | 132 | Keyword_ifdef, |
| | 133 | Keyword_ifndef, |
| | 134 | Keyword_error, |
| | 135 | Keyword_pragma, |
| | 136 | }; |
| | 137 | |
| | 138 | pub const Keyword = struct { |
| | 139 | bytes: []const u8, |
| | 140 | id: Id, |
| | 141 | hash: u32, |
| | 142 | |
| | 143 | fn init(bytes: []const u8, id: Id) Keyword { |
| | 144 | @setEvalBranchQuota(2000); |
| | 145 | return .{ |
| | 146 | .bytes = bytes, |
| | 147 | .id = id, |
| | 148 | .hash = std.hash_map.hashString(bytes), |
| | 149 | }; |
| | 150 | } |
| | 151 | }; |
| | 152 | |
| | 153 | // TODO extensions |
| | 154 | pub const keywords = [_]Keyword{ |
| | 155 | Keyword.init("auto", .Keyword_auto), |
| | 156 | Keyword.init("break", .Keyword_break), |
| | 157 | Keyword.init("case", .Keyword_case), |
| | 158 | Keyword.init("char", .Keyword_char), |
| | 159 | Keyword.init("const", .Keyword_const), |
| | 160 | Keyword.init("continue", .Keyword_continue), |
| | 161 | Keyword.init("default", .Keyword_default), |
| | 162 | Keyword.init("do", .Keyword_do), |
| | 163 | Keyword.init("double", .Keyword_double), |
| | 164 | Keyword.init("else", .Keyword_else), |
| | 165 | Keyword.init("enum", .Keyword_enum), |
| | 166 | Keyword.init("extern", .Keyword_extern), |
| | 167 | Keyword.init("float", .Keyword_float), |
| | 168 | Keyword.init("for", .Keyword_for), |
| | 169 | Keyword.init("goto", .Keyword_goto), |
| | 170 | Keyword.init("if", .Keyword_if), |
| | 171 | Keyword.init("int", .Keyword_int), |
| | 172 | Keyword.init("long", .Keyword_long), |
| | 173 | Keyword.init("register", .Keyword_register), |
| | 174 | Keyword.init("return", .Keyword_return), |
| | 175 | Keyword.init("short", .Keyword_short), |
| | 176 | Keyword.init("signed", .Keyword_signed), |
| | 177 | Keyword.init("sizeof", .Keyword_sizeof), |
| | 178 | Keyword.init("static", .Keyword_static), |
| | 179 | Keyword.init("struct", .Keyword_struct), |
| | 180 | Keyword.init("switch", .Keyword_switch), |
| | 181 | Keyword.init("typedef", .Keyword_typedef), |
| | 182 | Keyword.init("union", .Keyword_union), |
| | 183 | Keyword.init("unsigned", .Keyword_unsigned), |
| | 184 | Keyword.init("void", .Keyword_void), |
| | 185 | Keyword.init("volatile", .Keyword_volatile), |
| | 186 | Keyword.init("while", .Keyword_while), |
| | 187 | |
| | 188 | // ISO C99 |
| | 189 | Keyword.init("_Bool", .Keyword_bool), |
| | 190 | Keyword.init("_Complex", .Keyword_complex), |
| | 191 | Keyword.init("_Imaginary", .Keyword_imaginary), |
| | 192 | Keyword.init("inline", .Keyword_inline), |
| | 193 | Keyword.init("restrict", .Keyword_restrict), |
| | 194 | |
| | 195 | // ISO C11 |
| | 196 | Keyword.init("_Alignas", .Keyword_alignas), |
| | 197 | Keyword.init("_Alignof", .Keyword_alignof), |
| | 198 | Keyword.init("_Atomic", .Keyword_atomic), |
| | 199 | Keyword.init("_Generic", .Keyword_generic), |
| | 200 | Keyword.init("_Noreturn", .Keyword_noreturn), |
| | 201 | Keyword.init("_Static_assert", .Keyword_static_assert), |
| | 202 | Keyword.init("_Thread_local", .Keyword_thread_local), |
| | 203 | |
| | 204 | // Preprocessor |
| | 205 | Keyword.init("include", .Keyword_include), |
| | 206 | Keyword.init("define", .Keyword_define), |
| | 207 | Keyword.init("ifdef", .Keyword_ifdef), |
| | 208 | Keyword.init("ifndef", .Keyword_ifndef), |
| | 209 | Keyword.init("error", .Keyword_error), |
| | 210 | Keyword.init("pragma", .Keyword_pragma), |
| | 211 | }; |
| | 212 | |
| | 213 | // TODO perfect hash at comptime |
| | 214 | pub fn getKeyword(bytes: []const u8, macro: bool) ?Id { |
| | 215 | var hash = std.hash_map.hashString(bytes); |
| | 216 | for (keywords) |kw| { |
| | 217 | if (kw.hash == hash and mem.eql(u8, kw.bytes, bytes)) { |
| | 218 | switch (kw.id) { |
| | 219 | .Keyword_include, |
| | 220 | .Keyword_define, |
| | 221 | .Keyword_ifdef, |
| | 222 | .Keyword_ifndef, |
| | 223 | .Keyword_error, |
| | 224 | .Keyword_pragma, |
| | 225 | => if (!macro) return null, |
| | 226 | else => {}, |
| | 227 | } |
| | 228 | return kw.id; |
| | 229 | } |
| | 230 | } |
| | 231 | return null; |
| | 232 | } |
| 75 | | 233 | |
| 76 | pub const NumSuffix = enum { | 234 | pub const NumSuffix = enum { |
| 77 | None, | 235 | None, |
| ... | @@ -95,6 +253,7 @@ pub const Token = struct { | ... | @@ -95,6 +253,7 @@ pub const Token = struct { |
| 95 | pub const Tokenizer = struct { | 253 | pub const Tokenizer = struct { |
| 96 | source: *Source, | 254 | source: *Source, |
| 97 | index: usize = 0, | 255 | index: usize = 0, |
| | 256 | prev_tok_id: @TagType(Token.Id), |
| 98 | | 257 | |
| 99 | pub fn next(self: *Tokenizer) Token { | 258 | pub fn next(self: *Tokenizer) Token { |
| 100 | const start_index = self.index; | 259 | const start_index = self.index; |
| ... | @@ -124,6 +283,9 @@ pub const Tokenizer = struct { | ... | @@ -124,6 +283,9 @@ pub const Tokenizer = struct { |
| 124 | Percent, | 283 | Percent, |
| 125 | Asterisk, | 284 | Asterisk, |
| 126 | Plus, | 285 | Plus, |
| | 286 | |
| | 287 | /// special case for #include <...> |
| | 288 | MacroString, |
| 127 | AngleBracketLeft, | 289 | AngleBracketLeft, |
| 128 | AngleBracketAngleBracketLeft, | 290 | AngleBracketAngleBracketLeft, |
| 129 | AngleBracketRight, | 291 | AngleBracketRight, |
| ... | @@ -189,7 +351,6 @@ pub const Tokenizer = struct { | ... | @@ -189,7 +351,6 @@ pub const Tokenizer = struct { |
| 189 | }, | 351 | }, |
| 190 | 'a'...'t', 'v'...'z', 'A'...'K', 'M'...'T', 'V'...'Z', '_' => { | 352 | 'a'...'t', 'v'...'z', 'A'...'K', 'M'...'T', 'V'...'Z', '_' => { |
| 191 | state = .Identifier; | 353 | state = .Identifier; |
| 192 | result.id = .Identifier; | | |
| 193 | }, | 354 | }, |
| 194 | '=' => { | 355 | '=' => { |
| 195 | state = .Equal; | 356 | state = .Equal; |
| ... | @@ -250,7 +411,10 @@ pub const Tokenizer = struct { | ... | @@ -250,7 +411,10 @@ pub const Tokenizer = struct { |
| 250 | state = .Plus; | 411 | state = .Plus; |
| 251 | }, | 412 | }, |
| 252 | '<' => { | 413 | '<' => { |
| 253 | state = .AngleBracketLeft; | 414 | if (self.prev_tok_id == .Keyword_include) |
| | 415 | state = .MacroString |
| | 416 | else |
| | 417 | state = .AngleBracketLeft; |
| 254 | }, | 418 | }, |
| 255 | '>' => { | 419 | '>' => { |
| 256 | state = .AngleBracketRight; | 420 | state = .AngleBracketRight; |
| ... | @@ -442,7 +606,7 @@ pub const Tokenizer = struct { | ... | @@ -442,7 +606,7 @@ pub const Tokenizer = struct { |
| 442 | .Identifier => switch (c) { | 606 | .Identifier => switch (c) { |
| 443 | 'a'...'z', 'A'...'Z', '_', '0'...'9' => {}, | 607 | 'a'...'z', 'A'...'Z', '_', '0'...'9' => {}, |
| 444 | else => { | 608 | else => { |
| 445 | result.id = .Identifier; | 609 | result.id = Token.getKeyword(self.source.buffer[result.start..self.index], self.prev_tok_id == .Hash) orelse .Identifier; |
| 446 | break; | 610 | break; |
| 447 | }, | 611 | }, |
| 448 | }, | 612 | }, |
| ... | @@ -522,6 +686,14 @@ pub const Tokenizer = struct { | ... | @@ -522,6 +686,14 @@ pub const Tokenizer = struct { |
| 522 | break; | 686 | break; |
| 523 | }, | 687 | }, |
| 524 | }, | 688 | }, |
| | 689 | .MacroString => switch (c) { |
| | 690 | '>' => { |
| | 691 | result.id = .MacroString; |
| | 692 | self.index += 1; |
| | 693 | break; |
| | 694 | }, |
| | 695 | else => {}, |
| | 696 | }, |
| 525 | .AngleBracketLeft => switch (c) { | 697 | .AngleBracketLeft => switch (c) { |
| 526 | '<' => { | 698 | '<' => { |
| 527 | state = .AngleBracketAngleBracketLeft; | 699 | state = .AngleBracketAngleBracketLeft; |
| ... | @@ -859,7 +1031,7 @@ pub const Tokenizer = struct { | ... | @@ -859,7 +1031,7 @@ pub const Tokenizer = struct { |
| 859 | switch (state) { | 1031 | switch (state) { |
| 860 | .Start => {}, | 1032 | .Start => {}, |
| 861 | .u, .u8, .U, .L, .Identifier => { | 1033 | .u, .u8, .U, .L, .Identifier => { |
| 862 | result.id = .Identifier; | 1034 | result.id = Token.getKeyword(self.source.buffer[result.start..self.index], self.prev_tok_id == .Hash) orelse .Identifier; |
| 863 | }, | 1035 | }, |
| 864 | | 1036 | |
| 865 | .Cr, | 1037 | .Cr, |
| ... | @@ -876,6 +1048,7 @@ pub const Tokenizer = struct { | ... | @@ -876,6 +1048,7 @@ pub const Tokenizer = struct { |
| 876 | .FloatFractionHex, | 1048 | .FloatFractionHex, |
| 877 | .FloatExponent, | 1049 | .FloatExponent, |
| 878 | .FloatExponentDigits, | 1050 | .FloatExponentDigits, |
| | 1051 | .MacroString, |
| 879 | => result.id = .Invalid, | 1052 | => result.id = .Invalid, |
| 880 | | 1053 | |
| 881 | .IntegerLiteralOct, | 1054 | .IntegerLiteralOct, |
| ... | @@ -910,6 +1083,7 @@ pub const Tokenizer = struct { | ... | @@ -910,6 +1083,7 @@ pub const Tokenizer = struct { |
| 910 | } | 1083 | } |
| 911 | } | 1084 | } |
| 912 | | 1085 | |
| | 1086 | self.prev_tok_id = result.id; |
| 913 | result.end = self.index; | 1087 | result.end = self.index; |
| 914 | return result; | 1088 | return result; |
| 915 | } | 1089 | } |