| ... | @@ -260,6 +260,7 @@ pub const Tokenizer = struct { | ... | @@ -260,6 +260,7 @@ pub const Tokenizer = struct { |
| 260 | Slash, | 260 | Slash, |
| 261 | LineCommentStart, | 261 | LineCommentStart, |
| 262 | LineComment, | 262 | LineComment, |
| | 263 | DocComment, |
| 263 | Zero, | 264 | Zero, |
| 264 | IntegerLiteral, | 265 | IntegerLiteral, |
| 265 | IntegerLiteralWithRadix, | 266 | IntegerLiteralWithRadix, |
| ... | @@ -825,6 +826,7 @@ pub const Tokenizer = struct { | ... | @@ -825,6 +826,7 @@ pub const Tokenizer = struct { |
| 825 | State.Slash => switch (c) { | 826 | State.Slash => switch (c) { |
| 826 | '/' => { | 827 | '/' => { |
| 827 | state = State.LineCommentStart; | 828 | state = State.LineCommentStart; |
| | 829 | result.id = Token.Id.LineComment; |
| 828 | }, | 830 | }, |
| 829 | '=' => { | 831 | '=' => { |
| 830 | result.id = Token.Id.SlashEqual; | 832 | result.id = Token.Id.SlashEqual; |
| ... | @@ -839,15 +841,15 @@ pub const Tokenizer = struct { | ... | @@ -839,15 +841,15 @@ pub const Tokenizer = struct { |
| 839 | State.LineCommentStart => switch (c) { | 841 | State.LineCommentStart => switch (c) { |
| 840 | '/' => { | 842 | '/' => { |
| 841 | result.id = Token.Id.DocComment; | 843 | result.id = Token.Id.DocComment; |
| 842 | state = State.LineComment; | 844 | state = State.DocComment; |
| 843 | }, | 845 | }, |
| 844 | '\n' => { | 846 | '\n' => break, |
| 845 | result.id = Token.Id.LineComment; | 847 | else => { |
| 846 | break; | 848 | state = State.LineComment; |
| | 849 | self.checkLiteralCharacter(); |
| 847 | }, | 850 | }, |
| 848 | else => self.checkLiteralCharacter(), | | |
| 849 | }, | 851 | }, |
| 850 | State.LineComment => switch (c) { | 852 | State.LineComment, State.DocComment => switch (c) { |
| 851 | '\n' => break, | 853 | '\n' => break, |
| 852 | else => self.checkLiteralCharacter(), | 854 | else => self.checkLiteralCharacter(), |
| 853 | }, | 855 | }, |
| ... | @@ -934,7 +936,10 @@ pub const Tokenizer = struct { | ... | @@ -934,7 +936,10 @@ pub const Tokenizer = struct { |
| 934 | }, | 936 | }, |
| 935 | State.LineCommentStart, | 937 | State.LineCommentStart, |
| 936 | State.LineComment => { | 938 | State.LineComment => { |
| 937 | result.id = Token.Id.Eof; | 939 | result.id = Token.Id.LineComment; |
| | 940 | }, |
| | 941 | State.DocComment => { |
| | 942 | result.id = Token.Id.DocComment; |
| 938 | }, | 943 | }, |
| 939 | | 944 | |
| 940 | State.NumberDot, | 945 | State.NumberDot, |
| ... | @@ -1105,41 +1110,77 @@ test "tokenizer - invalid literal/comment characters" { | ... | @@ -1105,41 +1110,77 @@ test "tokenizer - invalid literal/comment characters" { |
| 1105 | Token.Id.Invalid, | 1110 | Token.Id.Invalid, |
| 1106 | }); | 1111 | }); |
| 1107 | testTokenize("//\x00", []Token.Id { | 1112 | testTokenize("//\x00", []Token.Id { |
| | 1113 | Token.Id.LineComment, |
| 1108 | Token.Id.Invalid, | 1114 | Token.Id.Invalid, |
| 1109 | }); | 1115 | }); |
| 1110 | testTokenize("//\x1f", []Token.Id { | 1116 | testTokenize("//\x1f", []Token.Id { |
| | 1117 | Token.Id.LineComment, |
| 1111 | Token.Id.Invalid, | 1118 | Token.Id.Invalid, |
| 1112 | }); | 1119 | }); |
| 1113 | testTokenize("//\x7f", []Token.Id { | 1120 | testTokenize("//\x7f", []Token.Id { |
| | 1121 | Token.Id.LineComment, |
| 1114 | Token.Id.Invalid, | 1122 | Token.Id.Invalid, |
| 1115 | }); | 1123 | }); |
| 1116 | } | 1124 | } |
| 1117 | | 1125 | |
| 1118 | test "tokenizer - utf8" { | 1126 | test "tokenizer - utf8" { |
| 1119 | testTokenize("//\xc2\x80", []Token.Id{}); | 1127 | testTokenize("//\xc2\x80", []Token.Id{Token.Id.LineComment}); |
| 1120 | testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{}); | 1128 | testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{Token.Id.LineComment}); |
| 1121 | } | 1129 | } |
| 1122 | | 1130 | |
| 1123 | test "tokenizer - invalid utf8" { | 1131 | test "tokenizer - invalid utf8" { |
| 1124 | testTokenize("//\x80", []Token.Id{Token.Id.Invalid}); | 1132 | testTokenize("//\x80", []Token.Id{ |
| 1125 | testTokenize("//\xbf", []Token.Id{Token.Id.Invalid}); | 1133 | Token.Id.LineComment, |
| 1126 | testTokenize("//\xf8", []Token.Id{Token.Id.Invalid}); | 1134 | Token.Id.Invalid, |
| 1127 | testTokenize("//\xff", []Token.Id{Token.Id.Invalid}); | 1135 | }); |
| 1128 | testTokenize("//\xc2\xc0", []Token.Id{Token.Id.Invalid}); | 1136 | testTokenize("//\xbf", []Token.Id{ |
| 1129 | testTokenize("//\xe0", []Token.Id{Token.Id.Invalid}); | 1137 | Token.Id.LineComment, |
| 1130 | testTokenize("//\xf0", []Token.Id{Token.Id.Invalid}); | 1138 | Token.Id.Invalid, |
| 1131 | testTokenize("//\xf0\x90\x80\xc0", []Token.Id{Token.Id.Invalid}); | 1139 | }); |
| | 1140 | testTokenize("//\xf8", []Token.Id{ |
| | 1141 | Token.Id.LineComment, |
| | 1142 | Token.Id.Invalid, |
| | 1143 | }); |
| | 1144 | testTokenize("//\xff", []Token.Id{ |
| | 1145 | Token.Id.LineComment, |
| | 1146 | Token.Id.Invalid, |
| | 1147 | }); |
| | 1148 | testTokenize("//\xc2\xc0", []Token.Id{ |
| | 1149 | Token.Id.LineComment, |
| | 1150 | Token.Id.Invalid, |
| | 1151 | }); |
| | 1152 | testTokenize("//\xe0", []Token.Id{ |
| | 1153 | Token.Id.LineComment, |
| | 1154 | Token.Id.Invalid, |
| | 1155 | }); |
| | 1156 | testTokenize("//\xf0", []Token.Id{ |
| | 1157 | Token.Id.LineComment, |
| | 1158 | Token.Id.Invalid, |
| | 1159 | }); |
| | 1160 | testTokenize("//\xf0\x90\x80\xc0", []Token.Id{ |
| | 1161 | Token.Id.LineComment, |
| | 1162 | Token.Id.Invalid, |
| | 1163 | }); |
| 1132 | } | 1164 | } |
| 1133 | | 1165 | |
| 1134 | test "tokenizer - illegal unicode codepoints" { | 1166 | test "tokenizer - illegal unicode codepoints" { |
| 1135 | // unicode newline characters.U+0085, U+2028, U+2029 | 1167 | // unicode newline characters.U+0085, U+2028, U+2029 |
| 1136 | testTokenize("//\xc2\x84", []Token.Id{}); | 1168 | testTokenize("//\xc2\x84", []Token.Id{Token.Id.LineComment}); |
| 1137 | testTokenize("//\xc2\x85", []Token.Id{Token.Id.Invalid}); | 1169 | testTokenize("//\xc2\x85", []Token.Id{ |
| 1138 | testTokenize("//\xc2\x86", []Token.Id{}); | 1170 | Token.Id.LineComment, |
| 1139 | testTokenize("//\xe2\x80\xa7", []Token.Id{}); | 1171 | Token.Id.Invalid, |
| 1140 | testTokenize("//\xe2\x80\xa8", []Token.Id{Token.Id.Invalid}); | 1172 | }); |
| 1141 | testTokenize("//\xe2\x80\xa9", []Token.Id{Token.Id.Invalid}); | 1173 | testTokenize("//\xc2\x86", []Token.Id{Token.Id.LineComment}); |
| 1142 | testTokenize("//\xe2\x80\xaa", []Token.Id{}); | 1174 | testTokenize("//\xe2\x80\xa7", []Token.Id{Token.Id.LineComment}); |
| | 1175 | testTokenize("//\xe2\x80\xa8", []Token.Id{ |
| | 1176 | Token.Id.LineComment, |
| | 1177 | Token.Id.Invalid, |
| | 1178 | }); |
| | 1179 | testTokenize("//\xe2\x80\xa9", []Token.Id{ |
| | 1180 | Token.Id.LineComment, |
| | 1181 | Token.Id.Invalid, |
| | 1182 | }); |
| | 1183 | testTokenize("//\xe2\x80\xaa", []Token.Id{Token.Id.LineComment}); |
| 1143 | } | 1184 | } |
| 1144 | | 1185 | |
| 1145 | test "tokenizer - string identifier and builtin fns" { | 1186 | test "tokenizer - string identifier and builtin fns" { |
| ... | @@ -1166,11 +1207,35 @@ test "tokenizer - pipe and then invalid" { | ... | @@ -1166,11 +1207,35 @@ test "tokenizer - pipe and then invalid" { |
| 1166 | }); | 1207 | }); |
| 1167 | } | 1208 | } |
| 1168 | | 1209 | |
| | 1210 | test "tokenizer - line comment and doc comment" { |
| | 1211 | testTokenize("//", []Token.Id{Token.Id.LineComment}); |
| | 1212 | testTokenize("// a / b", []Token.Id{Token.Id.LineComment}); |
| | 1213 | testTokenize("// /", []Token.Id{Token.Id.LineComment}); |
| | 1214 | testTokenize("/// a", []Token.Id{Token.Id.DocComment}); |
| | 1215 | testTokenize("///", []Token.Id{Token.Id.DocComment}); |
| | 1216 | } |
| | 1217 | |
| | 1218 | test "tokenizer - line comment followed by identifier" { |
| | 1219 | testTokenize( |
| | 1220 | \\ Unexpected, |
| | 1221 | \\ // another |
| | 1222 | \\ Another, |
| | 1223 | , []Token.Id{ |
| | 1224 | Token.Id.Identifier, |
| | 1225 | Token.Id.Comma, |
| | 1226 | Token.Id.LineComment, |
| | 1227 | Token.Id.Identifier, |
| | 1228 | Token.Id.Comma, |
| | 1229 | }); |
| | 1230 | } |
| | 1231 | |
| 1169 | fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { | 1232 | fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { |
| 1170 | var tokenizer = Tokenizer.init(source); | 1233 | var tokenizer = Tokenizer.init(source); |
| 1171 | for (expected_tokens) |expected_token_id| { | 1234 | for (expected_tokens) |expected_token_id| { |
| 1172 | const token = tokenizer.next(); | 1235 | const token = tokenizer.next(); |
| 1173 | std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id)); | 1236 | if (@TagType(Token.Id)(token.id) != @TagType(Token.Id)(expected_token_id)) { |
| | 1237 | std.debug.panic("expected {}, found {}\n", @tagName(@TagType(Token.Id)(expected_token_id)), @tagName(@TagType(Token.Id)(token.id))); |
| | 1238 | } |
| 1174 | switch (expected_token_id) { | 1239 | switch (expected_token_id) { |
| 1175 | Token.Id.StringLiteral => |expected_kind| { | 1240 | Token.Id.StringLiteral => |expected_kind| { |
| 1176 | std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable }); | 1241 | std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable }); |