lib/chant/src/lexer/number.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const chant = @import("../root.zig");
3 const cursor = @import("cursor.zig");
4
5 const Token = chant.token.Token;
6
7 pub fn lex(self: anytype, start: usize, column: u32) ?Token {
8 const first = self.source[self.index];
9 if (!std.ascii.isDigit(first) and !(first == '.' and cursor.peekIsDigit(self, 1))) return null;
10 var is_float = false;
11 const base = numberBase(self);
12 if (base != .decimal) {
13 self.index += 2;
14 }
15 while (self.index < self.source.len) {
16 const c = self.source[self.index];
17 if (isDigitForBase(c, base) or c == '\'') {
18 self.index += 1;
19 continue;
20 }
21 if (c == '.') {
22 is_float = true;
23 self.index += 1;
24 continue;
25 }
26 if (base == .decimal and (c == 'e' or c == 'E')) {
27 is_float = true;
28 self.index += 1;
29 takeExponentSign(self);
30 continue;
31 }
32 if (base == .hex and (c == 'p' or c == 'P')) {
33 is_float = true;
34 self.index += 1;
35 takeExponentSign(self);
36 continue;
37 }
38 break;
39 }
40 while (self.index < self.source.len) {
41 if (takeDecimalFloatSuffix(self)) {
42 is_float = true;
43 continue;
44 }
45 if (takeBitPreciseSuffix(self)) continue;
46 const c = self.source[self.index];
47 if (c == 'u' or c == 'U' or c == 'l' or c == 'L') {
48 self.index += 1;
49 continue;
50 }
51 if (c == 'f' or c == 'F') {
52 is_float = true;
53 self.index += 1;
54 continue;
55 }
56 break;
57 }
58 return cursor.make(self, if (is_float) .floating else .integer, start, column);
59 }
60
61 fn takeDecimalFloatSuffix(self: anytype) bool {
62 const remaining = self.source[self.index..];
63 if (remaining.len < 2) return false;
64 if ((remaining[0] == 'd' and (remaining[1] == 'f' or remaining[1] == 'd' or remaining[1] == 'l')) or
65 (remaining[0] == 'D' and (remaining[1] == 'F' or remaining[1] == 'D' or remaining[1] == 'L')))
66 {
67 self.index += 2;
68 return true;
69 }
70 return false;
71 }
72
73 fn takeBitPreciseSuffix(self: anytype) bool {
74 const remaining = self.source[self.index..];
75 if (remaining.len >= 3 and (remaining[0] == 'u' or remaining[0] == 'U') and startsBitPreciseSuffix(remaining[1..])) {
76 self.index += 3;
77 return true;
78 }
79 if (startsBitPreciseSuffix(remaining)) {
80 self.index += 2;
81 return true;
82 }
83 return false;
84 }
85
86 fn startsBitPreciseSuffix(text: []const u8) bool {
87 if (text.len < 2) return false;
88 return std.mem.eql(u8, text[0..2], "wb") or std.mem.eql(u8, text[0..2], "WB");
89 }
90
91 const Base = enum {
92 decimal,
93 binary,
94 hex,
95 };
96
97 fn numberBase(self: anytype) Base {
98 if (self.index + 1 >= self.source.len or self.source[self.index] != '0') return .decimal;
99 return switch (self.source[self.index + 1]) {
100 'b', 'B' => .binary,
101 'x', 'X' => .hex,
102 else => .decimal,
103 };
104 }
105
106 fn isDigitForBase(c: u8, base: Base) bool {
107 return switch (base) {
108 .decimal => std.ascii.isDigit(c),
109 .binary => c == '0' or c == '1',
110 .hex => std.ascii.isHex(c),
111 };
112 }
113
114 fn takeExponentSign(self: anytype) void {
115 if (self.index < self.source.len and (self.source[self.index] == '+' or self.source[self.index] == '-')) {
116 self.index += 1;
117 }
118 }