diff --git a/src/compile.rs b/src/compile.rs index 8d82e5e98..edfecdfc7 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -149,12 +149,12 @@ test!(passing_ints_from_global_memory => r#" // This one will replace the hello_world test above once the syntax is updated test!(print_function => r#" - from @std/app import start, print, exit - on start { + export fn main(): ExitCode { print('Hello, World'); - emit exit 0; + return toExitCode(0); }"#; stdout "Hello, World\n"; + status 0; ); test!(stdout_event => r#" from @std/app import start, stdout, exit @@ -169,50 +169,40 @@ test!(stdout_event => r#" // Basic Math Tests test!(int8_add => r#" - from @std/app import start, exit - on start { emit exit add(toInt8(1), toInt8(2)).getOrExit(); }"#; + export fn main(): ExitCode = toExitCode(getOrExit(add(toI8(1), toI8(2))));"#; status 3; ); test!(int8_sub => r#" - from @std/app import start, exit - on start { emit exit sub(toInt8(2), toInt8(1)).getOrExit(); }"#; + export fn main(): ExitCode = toExitCode(getOrExit(sub(toI8(2), toI8(1))));"#; status 1; ); test!(int8_mul => r#" - from @std/app import start, exit - on start { emit exit mul(toInt8(2), toInt8(1)).getOrExit(); }"#; + export fn main(): ExitCode = toExitCode(getOrExit(mul(toI8(2), toI8(1))));"#; status 2; ); test!(int8_div => r#" - from @std/app import start, exit - on start { emit exit div(toInt8(6), toInt8(2)).getOrExit(); }"#; + export fn main(): ExitCode = toExitCode(getOrExit(div(toI8(6), toI8(2))));"#; status 3; ); test!(int8_mod => r#" - from @std/app import start, exit - on start { emit exit mod(toInt8(6), toInt8(4)); }"#; + export fn main(): ExitCode = toExitCode(getOrExit(mod(toI8(6), toI8(4))));"#; status 2; ); test!(int8_pow => r#" - from @std/app import start, exit - on start { emit exit pow(toInt8(6), toInt8(2)).getOrExit(); }"#; + export fn main(): ExitCode = toExitCode(getOrExit(pow(toI8(6), toI8(2))));"#; status 36; ); test!(int8_min => r#" - from @std/app import start, print, exit - on start { - min(3.toInt8(), 5.toInt8()).print(); - emit exit 0; + export fn main() { + print(min(toI8(3), toI8(5))); }"#; - status 3; + stdout "3\n"; ); test!(int8_max => r#" - from @std/app import start, print, exit - on start { - max(3.toInt8(), 5.toInt8()).print(); - emit exit 0; + export fn main() { + print(max(toI8(3), toI8(5))); }"#; - status 5; + stdout "5\n"; ); test!(int16_add => r#" diff --git a/src/std/root.ln b/src/std/root.ln index c5a6e6b37..aa0489c1f 100644 --- a/src/std/root.ln +++ b/src/std/root.ln @@ -1,3 +1,26 @@ +/** + * The Alan root scope. The functions and types it binds from Rust are either part of the standard + * library, or are defined in the sibling root.rs file + **/ + +// Integer-related bindings +export fn toI8(i: i64): i8 binds i64toi8; +export fn add(a: i8, b: i8): Result binds addi8; +export fn sub(a: i8, b: i8): Result binds subi8; +export fn mul(a: i8, b: i8): Result binds muli8; +export fn div(a: i8, b: i8): Result binds divi8; +export fn mod(a: i8, b: i8): Result binds modi8; +export fn pow(a: i8, b: i8): Result binds powi8; +export fn min(a: i8, b: i8): i8 binds mini8; +export fn max(a: i8, b: i8): i8 binds maxi8; + +// Process exit-related bindings export type ExitCode binds std::process::ExitCode; -export fn toExitCode(e: i64): ExitCode binds to_exit_code; -export fn print(str: String) binds println!; \ No newline at end of file +export fn toExitCode(e: i64): ExitCode binds to_exit_code_i64; +export fn toExitCode(e: i8): ExitCode binds to_exit_code_i8; +export fn getOrExit(a: Result): i8 binds get_or_exit; // TODO: Support real generics + +// Stdout/stderr-related bindings +export fn print(str: String) binds println!; +export fn print(i: i8) binds print; +export fn print(i: i64) binds print; \ No newline at end of file diff --git a/src/std/root.rs b/src/std/root.rs index c4ad2cc65..137e785e3 100644 --- a/src/std/root.rs +++ b/src/std/root.rs @@ -1,3 +1,87 @@ -fn to_exit_code(i: i64) -> std::process::ExitCode { +/// Rust functions that the root scope binds. + +/// `to_exit_code_i64` converts a 64-bit integer into an exit code, for convenience since `i64` is the +/// default integer type in Alan. +fn to_exit_code_i64(i: i64) -> std::process::ExitCode { (i as u8).into() +} + +/// `to_exit_code_i8` converts a 64-bit integer into an exit code, for convenience since `i64` is the +/// default integer type in Alan. +fn to_exit_code_i8(i: i8) -> std::process::ExitCode { + (i as u8).into() +} + +/// `i64toi8` casts an i64 to an i8. +fn i64toi8(i: i64) -> i8 { + i as i8 +} + +/// `addi8` safely adds two i8s together, returning a Result-wrapped i8 (or an error on overflow) +fn addi8(a: i8, b: i8) -> Result> { + match a.checked_add(b) { + Some(c) => Ok(c), + None => Err("Overflow".into()), + } +} + +/// `subi8` safely subtracts two i8s, returning a Result-wrapped i8 (or an error on underflow) +fn subi8(a: i8, b: i8) -> Result> { + match a.checked_sub(b) { + Some(c) => Ok(c), + None => Err("Underflow".into()), + } +} + +/// `muli8` safely multiplies two i8s, returning a Result-wrapped i8 (or an error on under/overflow) +fn muli8(a: i8, b: i8) -> Result> { + match a.checked_mul(b) { + Some(c) => Ok(c), + None => Err("Underflow or Overflow".into()), + } +} + +/// `divi8` safely divides two i8s, returning a Result-wrapped i8 (or an error on divide-by-zero) +fn divi8(a: i8, b: i8) -> Result> { + match a.checked_div(b) { + Some(c) => Ok(c), + None => Err("Divide-by-zero".into()), + } +} + +/// `modi8` safely divides two i8s, returning a Result-wrapped remainder in i8 (or an error on divide-by-zero) +fn modi8(a: i8, b: i8) -> Result> { + match a.checked_rem(b) { + Some(c) => Ok(c), + None => Err("Divide-by-zero".into()), + } +} + +/// `powi8` safely raises the first i8 to the second i8, returning a Result-wrapped i8 (or an error on under/overflow) +fn powi8(a: i8, b: i8) -> Result> { + // TODO: Support b being negative correctly + match a.checked_pow(b as u32) { + Some(c) => Ok(c), + None => Err("Underflow or Overflow".into()), + } +} + +/// `mini8` returns the smaller of the two i8 values +fn mini8(a: i8, b: i8) -> i8 { + if a < b { a } else { b } +} + +/// `maxi8` returns the larger of the two i8 values +fn maxi8(a: i8, b: i8) -> i8 { + if a > b { a } else { b } +} + +/// `get_or_exit` is basically an alias to `unwrap`, but as a function instead of a method +fn get_or_exit(a: Result>) -> A { + a.unwrap() +} + +/// `print` is a simple function that prints basically anything other than String +fn print(a: A) { + println!("{}", a); } \ No newline at end of file