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 }