feature. See also
. The project being documented here (as the example) is the Zig library itself.
MacroTranslator.parseCNumLit
fn parseCNumLit(mt: *MacroTranslator) ParseError!ZigNode
File
Code
fn parseCNumLit(mt: *MacroTranslator) ParseError!ZigNode {
const arena = mt.t.arena;
const lit_bytes = mt.tokSlice();
mt.i += 1;
var bytes = try std.ArrayList(u8).initCapacity(arena, lit_bytes.len + 3 + 2);
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..];
},
'e', 'E' => {
if (prefix != .hex) break after_prefix[i..];
bytes.appendAssumeCapacity(c);
},
'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 mt.fail("invalid number suffix: '{s}'", .{suffix_str});
return error.ParseError;
};
if (suffix.isImaginary()) {
try mt.fail("TODO: imaginary literals", .{});
return error.ParseError;
}
if (suffix.isBitInt()) {
try mt.fail("TODO: _BitInt literals", .{});
return error.ParseError;
}
if (is_float) {
const type_node = try ZigTag.type.create(arena, switch (suffix) {
.F16 => "f16",
.F, .F32 => "f32",
.None, .F32x, .F64 => "f64",
.L, .F64x => "c_longdouble",
.W => "f80",
.Q, .F128 => "f128",
else => {
try mt.fail("TODO: float literal suffix: '{s}'", .{suffix_str});
return error.ParseError;
},
});
if (bytes.getLast().? == '.') {
bytes.appendAssumeCapacity('0');
} else if (mem.findAny(u8, bytes.items, ".eEpP") == null) {
bytes.appendSliceAssumeCapacity(".0");
}
const rhs = try ZigTag.float_literal.create(arena, bytes.items);
return ZigTag.as.create(arena, .{ .lhs = type_node, .rhs = rhs });
} else {
const type_node = try ZigTag.type.create(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);
// 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 ZigTag.integer_literal.create(arena, bytes.items);
if (guaranteed_to_fit) {
return ZigTag.as.create(arena, .{ .lhs = type_node, .rhs = literal_node });
} else {
return mt.t.createHelperCallNode(.promoteIntLiteral, &.{ type_node, literal_node, try ZigTag.enum_literal.create(arena, @tagName(prefix)) });
}
}
}