diff --git a/benchmark/biquad_f64.lyte b/benchmark/biquad_f64.lyte index 1bc5c76..202e519 100644 --- a/benchmark/biquad_f64.lyte +++ b/benchmark/biquad_f64.lyte @@ -1,6 +1,6 @@ // Biquad filter DSP benchmark // Process 10 million samples through a lowpass filter -// Double-precision version using f32 literals cast to f64. +// Double-precision version struct Biquad { b0: f64, b1: f64, b2: f64, @@ -11,28 +11,28 @@ struct Biquad { // Design a 2nd-order lowpass (RBJ cookbook) lpf(fc: f64, fs: f64, q: f64) -> Biquad { - var w0 = (2.0 as f64) * (3.14159265 as f64) * fc / fs - var alpha = sin(w0) / ((2.0 as f64) * q) + var w0 = 2.0f64 * 3.141592653589793f64 * fc / fs + var alpha = sin(w0) / (2.0f64 * q) var cs = cos(w0) - var a0 = (1.0 as f64) + alpha - var inv = (1.0 as f64) / a0 + var a0 = 1.0f64 + alpha + var inv = 1.0f64 / a0 var bq: Biquad - bq.b1 = ((1.0 as f64) - cs) * inv - bq.b0 = bq.b1 / (2.0 as f64) + bq.b1 = (1.0f64 - cs) * inv + bq.b0 = bq.b1 / 2.0f64 bq.b2 = bq.b0 - bq.a1 = ((0.0 as f64) - (2.0 as f64) * cs) * inv - bq.a2 = ((1.0 as f64) - alpha) * inv - bq.x1 = 0.0 as f64 - bq.x2 = 0.0 as f64 - bq.y1 = 0.0 as f64 - bq.y2 = 0.0 as f64 + bq.a1 = (0.0f64 - 2.0f64 * cs) * inv + bq.a2 = (1.0f64 - alpha) * inv + bq.x1 = 0.0f64 + bq.x2 = 0.0f64 + bq.y1 = 0.0f64 + bq.y2 = 0.0f64 return bq } main { - var bq = lpf(1000.0 as f64, 44100.0 as f64, 0.707 as f64) + var bq = lpf(1000.0f64, 44100.0f64, 0.707f64) var b0 = bq.b0 var b1 = bq.b1 var b2 = bq.b2 @@ -44,16 +44,16 @@ main { var y2 = bq.y2 var n = 10000000 - var phase = 0.0 as f64 - var freq = (440.0 as f64) / (44100.0 as f64) - var last_y = 0.0 as f64 - var two_pi = (2.0 as f64) * (3.14159265 as f64) + var phase = 0.0f64 + var freq = 440.0f64 / 44100.0f64 + var last_y = 0.0f64 + var two_pi = 2.0f64 * 3.141592653589793f64 // Process a 440 Hz sine wave through the filter for i in 0 .. n { var x = sin(phase * two_pi) phase = phase + freq - if phase > (1.0 as f64) { phase = phase - (1.0 as f64) } + if phase > 1.0f64 { phase = phase - 1.0f64 } var y = b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2 x2 = x1 diff --git a/docs/GRAMMAR.md b/docs/GRAMMAR.md index 4fbd313..9552993 100644 --- a/docs/GRAMMAR.md +++ b/docs/GRAMMAR.md @@ -16,8 +16,9 @@ digit = "0".."9" ; id = ( alpha ) { alpha | digit } ; integer = digit { digit } ; -uinteger = integer "u" ; +int_lit = integer [ "i32" | "u32" | "u" ] ; real = digit { digit } "." digit { digit } ; +real_lit = real [ "f32" | "f64" ] ; char_lit = "'" ( char | "\\" ( "\\" | "n" ) ) "'" ; string_lit = '"' { any - '"' } '"' ; @@ -90,7 +91,7 @@ postfix = atom { "(" [ exprlist ] ")" (* function call *) } ; atom = id - | integer | uinteger | real + | int_lit | real_lit | char_lit | string_lit | "true" | "false" | "." id (* enum variant *) diff --git a/docs/tutorial.md b/docs/tutorial.md index 031af84..4a35686 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -72,12 +72,12 @@ Note for Lua users: Lua lets one variable hold different kinds of values over ti | Type | What it is | Example values | |------|------------|----------------| -| `i32` | A whole number (integer) | `42`, `-7`, `0` | -| `f32` | A decimal number (float) | `3.14`, `-0.5`, `1.0` | +| `i32` | A whole number (integer) | `42`, `-7`, `0`, `42i32` | +| `f32` | A decimal number (float) | `3.14`, `-0.5`, `1.0`, `1.0f32` | | `bool` | True or false | `true`, `false` | | `str` | Text (a string in quotes) | `"hello"` | | `i8` | A very small whole number, used for individual characters | `65` (the letter `A`) | -| `u32` | A whole number that cannot be negative (unsigned) | `42`, `0` | +| `u32` | A whole number that cannot be negative (unsigned) | `42u32`, `42u`, `0u32` | Single characters can also be written with single quotes: `'a'`, `'Z'`, `'\n'` (newline). This is called a character literal. @@ -103,6 +103,24 @@ In audio and DSP work, you will use `f32` constantly. Audio signals, frequencies **A note on `f64`:** The language grammar also defines an `f64` type, which is a higher-precision decimal number. In Audulus DSP work, `f32` is the type you'll usually use. +By default, whole-number literals are `i32`, and decimal literals are `f32`. + +### Numeric literal suffixes + +Numeric literals can include a type suffix when you need the literal itself to have a specific type: + +```lyte +main { + var count = 1i32 + var mask = 1u32 + var also_unsigned = 1u + var gain = 1.0f32 + var precise = 1.0f64 +} +``` + +Use `as` when converting an existing value or expression from one type to another, such as `x as i32` or `(len - 2) as f32`. + ### Why does static typing matter? In audio DSP, you are doing a lot of math very quickly. If the language has to keep figuring out what kind of value something is while it runs, that adds overhead. By knowing the types up front, Lyte can build faster code. Here, **compile** just means “turn your code into something the computer can run.” diff --git a/src/compiler.rs b/src/compiler.rs index 225023b..6cbaf2e 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -2159,15 +2159,15 @@ mod tests { // f32 if-expression in void context. ("f32 if in void ctx", r#" main { - var x = 0.0 as f32 + var x = 0.0f32 if true { x = 1.0 } else { x = 2.0 } } "#), // f32 assignment RHS. ("f32 assign chain", r#" main { - var x = 0.0 as f32 - var y = 0.0 as f32 + var x = 0.0f32 + var y = 0.0f32 x = 1.5 y = x + x } diff --git a/src/stack_optimize.rs b/src/stack_optimize.rs index a605128..9bc168c 100644 --- a/src/stack_optimize.rs +++ b/src/stack_optimize.rs @@ -727,9 +727,9 @@ fn fuse(func: &mut StackFunction) { // before the 3-op one so the longest match wins at each position. // Constant-fold fw.f32.const v + dw.convert.f32_to_f64 → dw.f64.const. - // Widening f32→f64 is exact, so this is always valid; it both folds - // the many ` as f64` conversions and exposes the constant to - // the d-window compare-jump fusion below. + // Widening f32→f64 is exact, so this is always valid; it folds explicit + // f32-to-f64 conversions and exposes the constant to the d-window + // compare-jump fusion below. if i + 1 < len && !spans_target(i, 2) { if let (StackOp::F32ConstF(v), StackOp::F32ToF64D) = (&ops[i], &ops[i + 1]) { let v = *v as f64; diff --git a/stdlib.lyte b/stdlib.lyte index c3ee8e2..442718b 100644 --- a/stdlib.lyte +++ b/stdlib.lyte @@ -17,8 +17,8 @@ step(edge: f32, x: f32) -> f32 { } step(edge: f64, x: f64) -> f64 { - if x < edge { return 0.0 as f64 } - return 1.0 as f64 + if x < edge { return 0.0f64 } + return 1.0f64 } smoothstep(edge0: f32, edge1: f32, x: f32) -> f32 { @@ -27,8 +27,8 @@ smoothstep(edge0: f32, edge1: f32, x: f32) -> f32 { } smoothstep(edge0: f64, edge1: f64, x: f64) -> f64 { - var t = clamp((x - edge0) / (edge1 - edge0), 0.0 as f64, 1.0 as f64) - t * t * ((3.0 as f64) - (2.0 as f64) * t) + var t = clamp((x - edge0) / (edge1 - edge0), 0.0f64, 1.0f64) + t * t * (3.0f64 - 2.0f64 * t) } mix(a: f32, b: f32, t: f32) -> f32 { a + t * (b - a) } @@ -242,4 +242,4 @@ strcat(dst: [i8], src: [i8]) { } } } -} \ No newline at end of file +} diff --git a/tests/cases/f64_window.lyte b/tests/cases/f64_window.lyte index 7485b27..ce00cc0 100644 --- a/tests/cases/f64_window.lyte +++ b/tests/cases/f64_window.lyte @@ -26,8 +26,8 @@ struct Acc { a: f64, b: f64, c: f64 } approx(x: f64, y: f64) -> bool { var d = x - y - if d < 0.0 as f64 { d = 0.0 as f64 - d } - return d < 0.0001 as f64 + if d < 0.0f64 { d = 0.0f64 - d } + return d < 0.0001f64 } // f64 argument and return value bridging across a call. @@ -36,44 +36,44 @@ fma(a: f64, b: f64, c: f64) -> f64 { } main { - var x = 3.0 as f64 - var y = 2.0 as f64 + var x = 3.0f64 + var y = 2.0f64 // Arithmetic. - assert(approx(x + y, 5.0 as f64)) - assert(approx(x - y, 1.0 as f64)) - assert(approx(x * y, 6.0 as f64)) - assert(approx(x / y, 1.5 as f64)) - assert(approx(0.0 as f64 - x, -3.0 as f64)) + assert(approx(x + y, 5.0f64)) + assert(approx(x - y, 1.0f64)) + assert(approx(x * y, 6.0f64)) + assert(approx(x / y, 1.5f64)) + assert(approx(0.0f64 - x, -3.0f64)) // All six comparisons. assert(x > y) assert(y < x) - assert(x >= 3.0 as f64) - assert(y <= 2.0 as f64) - assert(x == 3.0 as f64) + assert(x >= 3.0f64) + assert(y <= 2.0f64) + assert(x == 3.0f64) assert(x != y) // Fused multiply-add sum chain: b0*x + b1*x1 - b2*x2. - var b0 = 1.0 as f64 - var b1 = 2.0 as f64 - var b2 = 0.5 as f64 - var x1 = 4.0 as f64 - var x2 = 6.0 as f64 + var b0 = 1.0f64 + var b1 = 2.0f64 + var b2 = 0.5f64 + var x1 = 4.0f64 + var x2 = 6.0f64 var sum = b0 * x + b1 * x1 - b2 * x2 // 1*3 + 2*4 - 0.5*6 = 3 + 8 - 3 = 8 - assert(approx(sum, 8.0 as f64)) + assert(approx(sum, 8.0f64)) // f64 across a function call (argument + return bridging). - assert(approx(fma(x, y, 1.0 as f64), 7.0 as f64)) + assert(approx(fma(x, y, 1.0f64), 7.0f64)) // Math intrinsic + struct store/load through the d-window. var acc: Acc - acc.a = sqrt(16.0 as f64) - acc.b = acc.a * 2.0 as f64 + acc.a = sqrt(16.0f64) + acc.b = acc.a * 2.0f64 acc.c = acc.a + acc.b - assert(approx(acc.c, 12.0 as f64)) + assert(approx(acc.c, 12.0f64)) // Conversion round trip f64 -> i32. - print((x * y * 7.0 as f64) as i32) + print((x * y * 7.0f64) as i32) } diff --git a/tests/cases/int_types.lyte b/tests/cases/int_types.lyte index 64c9e98..4da8587 100644 --- a/tests/cases/int_types.lyte +++ b/tests/cases/int_types.lyte @@ -15,9 +15,9 @@ main { // u32 arithmetic var x: u32 - x = 100 as u32 + x = 100u32 var y: u32 - y = 50 as u32 + y = 50u32 assert(x as i32 - y as i32 == 50) // u32 comparison diff --git a/tests/cases/int_types_f64.lyte b/tests/cases/int_types_f64.lyte index 2f94395..7103b5a 100644 --- a/tests/cases/int_types_f64.lyte +++ b/tests/cases/int_types_f64.lyte @@ -8,20 +8,20 @@ approx(a: f64, b: f64) -> bool { var d = a - b - if d < 0.0 as f64 { d = 0.0 as f64 - d } - return d < 0.0001 as f64 + if d < 0.0f64 { d = 0.0f64 - d } + return d < 0.0001f64 } main { var x: f64 - x = 3.0 as f64 + x = 3.0f64 var y: f64 - y = 2.0 as f64 + y = 2.0f64 // f64 arithmetic with helper function - assert(approx(x + y, 5.0 as f64)) - assert(approx(x - y, 1.0 as f64)) - assert(approx(x * y, 6.0 as f64)) + assert(approx(x + y, 5.0f64)) + assert(approx(x - y, 1.0f64)) + assert(approx(x * y, 6.0f64)) // f64 comparisons assert(x > y) diff --git a/tests/cases/int_types_u32.lyte b/tests/cases/int_types_u32.lyte index a01e938..b4ef6a6 100644 --- a/tests/cases/int_types_u32.lyte +++ b/tests/cases/int_types_u32.lyte @@ -9,9 +9,9 @@ main { // u32 arithmetic var a: u32 - a = 100 as u32 + a = 100u32 var b: u32 - b = 40 as u32 + b = 40u32 assert((a + b) as i32 == 140) assert((a - b) as i32 == 60) assert((a * b) as i32 == 4000) diff --git a/tests/cases/type_cast_f64.lyte b/tests/cases/type_cast_f64.lyte index 10e1fa9..d7c62cd 100644 --- a/tests/cases/type_cast_f64.lyte +++ b/tests/cases/type_cast_f64.lyte @@ -7,7 +7,7 @@ main { // i32 to f64 - assert(100 as f64 == 100.0 as f64) + assert(100 as f64 == 100.0f64) // f64 to i32 (truncates) assert((3.9 as f64) as i32 == 3) @@ -15,10 +15,10 @@ main { // f32 to f64 promotion var x: f32 x = 2.5 - assert(x as f64 == 2.5 as f64) + assert(x as f64 == 2.5f64) // f64 to f32 demotion var y: f64 - y = 1.25 as f64 + y = 1.25f64 assert(y as f32 == 1.25) }