Skip to content

Commit

Permalink
Implement simple i8 arithmetic. (#660)
Browse files Browse the repository at this point in the history
  • Loading branch information
dfellis authored Feb 27, 2024
1 parent e08037c commit d6535b4
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 28 deletions.
40 changes: 15 additions & 25 deletions src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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#"
Expand Down
27 changes: 25 additions & 2 deletions src/std/root.ln
Original file line number Diff line number Diff line change
@@ -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<i8> binds addi8;
export fn sub(a: i8, b: i8): Result<i8> binds subi8;
export fn mul(a: i8, b: i8): Result<i8> binds muli8;
export fn div(a: i8, b: i8): Result<i8> binds divi8;
export fn mod(a: i8, b: i8): Result<i8> binds modi8;
export fn pow(a: i8, b: i8): Result<i8> 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!;
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>): 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;
86 changes: 85 additions & 1 deletion src/std/root.rs
Original file line number Diff line number Diff line change
@@ -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<i8, Box<dyn std::error::Error>> {
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<i8, Box<dyn std::error::Error>> {
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<i8, Box<dyn std::error::Error>> {
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<i8, Box<dyn std::error::Error>> {
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<i8, Box<dyn std::error::Error>> {
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<i8, Box<dyn std::error::Error>> {
// 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>(a: Result<A, Box<dyn std::error::Error>>) -> A {
a.unwrap()
}

/// `print` is a simple function that prints basically anything other than String
fn print<A: std::fmt::Display>(a: A) {
println!("{}", a);
}

0 comments on commit d6535b4

Please sign in to comment.