Numeric Literals
Numeric literals produce integer (int, u8, i128, …) and floating-point
(f32, f64, f128) values.
In Silk, the sign is an operator: -1 is a unary - expression applied to the
integer literal token 1, not a distinct “negative literal” token.
Notes#
What works end-to-end today (lexer → parser → checker → lowering → codegen):
- Decimal integer literals:
0,42,255. - Digit separators (
_) within numeric literal digits:1_000_000,0b0000_1111_0000,0xFFFF_FFFF,1_000.25,1_000ms. - Integer base prefixes:
- binary:
0b1010/0B1010, - octal:
0o17/0O17, - hex:
0xFF/0Xff, - legacy octal:
017(value 15). - Decimal float literals with a fractional part:
0.0,1.5,10.25. - Unary
-over numeric literals:-1,-1.5. - Contextual typing:
- integer literals default to
int, but adopt an expected integer type (u8,i128, …) or time type (Duration,Instant) when a context provides one, - float literals default to
f64, but adoptf32/f64/f128from context. - Duration literal tokens of the form
<number><unit>(no whitespace) such as500msand1.5s(specified in literals duration). - Lowering note (current IR backend subset): unannotated local
letbindings participate only in the integer subset. Prefer explicit type annotations forbooland float locals when you intend to build an executable/library.
Limitations:
- Exponent notation (
1e6,1.0e-3). - Numeric type suffixes (
42u8,1.5f32). - Numeric suffixes for 128-bit types (
1u128,1.0f128) are not implemented; use annotations orascasts.
Quick Reference#
fn main () -> int {
let a = 42; // int
let b: u8 = 42; // u8 (typed by context)
let x: f64 = 1.5; // f64
let y: f32 = 1.5; // f32 (typed by context)
let d: Duration = 5ms;
let t0: Instant = 0;
return 0;
}
Surface Syntax#
Numeric literal tokens are recognized as:
- Integer literal:
- decimal digits (
[0-9]+), - binary prefix:
0b/0Bfollowed by binary digits ([01]+), - octal prefix:
0o/0Ofollowed by octal digits ([0-7]+), - hex prefix:
0x/0Xfollowed by hex digits ([0-9a-fA-F]+), - legacy octal:
0[0-7]+(for example017). - Float literal: digits,
., digits ([0-9]+ '.' [0-9]+).
Notes:
- Integer and float literals may use
_as a digit separator. Separators are ignored when parsing the numeric value, but must appear between digits. For example: - valid:
1_000,0xFFFF_FFFF,0b0000_1111_0000,1_000.2_5, - invalid:
_1,1_,1__0,0x_FF. - A float literal must have digits on both sides of the
.: 1.0is a float literal.1.is not a float literal in the current lexer..5is not a float literal; write0.5.- Numeric literals must start with a digit in the current lexer.
- The
-sign is not part of the literal token: -1parses as unary-applied to the integer literal1.-1.5parses as unary-applied to the float literal1.5.- A numeric token immediately followed by a duration unit suffix (e.g.
1s,500ms,1.5s) is a singleDurationliteral token, not a number token followed by an identifier. - A numeric literal token may not be immediately followed by an identifier start character or an ASCII digit (unless the identifier characters are part of a duration unit suffix). For example:
3inis a lexical error (write3 inor3 * inas intended),0b102is a lexical error (invalid binary digit),08is a lexical error in Silk because multi-digit literals starting with0are legacy octal (use0o10for octal 8, or write8for decimal).
Type Rules#
See types for the primitive type names used below.
Integer literals#
- Without an expected type, an integer literal has type
int. - When a context provides an expected type that is:
- an integer type (
u8,i64,int, …), or - a time type (
Duration,Instant), then the integer literal adopts that expected type.
Example: parameter context and “adopt the expected type”
fn id_u8 (x: u8) -> u8 {
return x;
}
fn main () -> int {
// `255` is contextually typed as `u8` because `id_u8` expects `u8`.
let v: u8 = id_u8(255);
if v != 255 {
return 1;
}
return 0;
}
Example: time types share an i64-based representation in the Supported forms
(duration instant), so integer literals can be used as
Instant/Duration values via context:
fn main () -> int {
let t0: Instant = 0;
let d: Duration = 1s;
let t1: Instant = t0 + d;
let diff: Duration = t1 - t0;
if diff != d {
return 1;
}
return 0;
}
Float literals#
- Without an expected type, a float literal has type
f64. - When a context provides an expected float type (
f32orf64), the literal adopts that expected type.
fn id_f32 (x: f32) -> f32 {
return x;
}
fn main () -> int {
// `1.5` is contextually typed as `f32` because `id_f32` expects `f32`.
let v: f32 = id_f32(1.5);
if v != 1.5 {
return 1;
}
return 0;
}
Common Pitfalls#
- Trying to use suffixes:
42u8/1.5f32are not supported. Use type annotations (let x: u8 = 42;) or casts (42 as u8). - Using exponent notation:
1e6is not supported yet. - Writing incomplete floats: write
1.0(not1.) and0.5(not.5). - Mixing integers and floats implicitly: use
ascasts (operators) to convert explicitly when you need to combine integer and float values.
Related Documents#
- literals duration (duration literals like
5ms,1.5s) - duration instant (time types and operators)
- operators (unary
-, arithmetic, andascasts) - types (primitive numeric type names)
Source repository · Edit this page · View Markdown