feat: expand standard library with Math module and new functions
- Add Math module: abs, min, max, sqrt, pow, floor, ceil, round - Add List functions: isEmpty, find, any, all, take, drop - Add String functions: startsWith, endsWith, toUpper, toLower, substring - Add 12 tests for new standard library functions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -58,6 +58,31 @@ pub enum BuiltinFn {
|
||||
Versioned, // Create versioned value: versioned("TypeName", 1, value)
|
||||
Migrate, // Migrate to version: migrate(versionedValue, targetVersion)
|
||||
GetVersion, // Get version number: getVersion(versionedValue)
|
||||
|
||||
// Math operations
|
||||
MathAbs,
|
||||
MathMin,
|
||||
MathMax,
|
||||
MathSqrt,
|
||||
MathPow,
|
||||
MathFloor,
|
||||
MathCeil,
|
||||
MathRound,
|
||||
|
||||
// Additional List operations
|
||||
ListIsEmpty,
|
||||
ListFind,
|
||||
ListAny,
|
||||
ListAll,
|
||||
ListTake,
|
||||
ListDrop,
|
||||
|
||||
// Additional String operations
|
||||
StringStartsWith,
|
||||
StringEndsWith,
|
||||
StringToUpper,
|
||||
StringToLower,
|
||||
StringSubstring,
|
||||
}
|
||||
|
||||
/// Runtime value
|
||||
@@ -708,6 +733,15 @@ impl Interpreter {
|
||||
("length".to_string(), Value::Builtin(BuiltinFn::ListLength)),
|
||||
("get".to_string(), Value::Builtin(BuiltinFn::ListGet)),
|
||||
("range".to_string(), Value::Builtin(BuiltinFn::ListRange)),
|
||||
(
|
||||
"isEmpty".to_string(),
|
||||
Value::Builtin(BuiltinFn::ListIsEmpty),
|
||||
),
|
||||
("find".to_string(), Value::Builtin(BuiltinFn::ListFind)),
|
||||
("any".to_string(), Value::Builtin(BuiltinFn::ListAny)),
|
||||
("all".to_string(), Value::Builtin(BuiltinFn::ListAll)),
|
||||
("take".to_string(), Value::Builtin(BuiltinFn::ListTake)),
|
||||
("drop".to_string(), Value::Builtin(BuiltinFn::ListDrop)),
|
||||
]));
|
||||
env.define("List", list_module);
|
||||
|
||||
@@ -730,6 +764,26 @@ impl Interpreter {
|
||||
),
|
||||
("chars".to_string(), Value::Builtin(BuiltinFn::StringChars)),
|
||||
("lines".to_string(), Value::Builtin(BuiltinFn::StringLines)),
|
||||
(
|
||||
"startsWith".to_string(),
|
||||
Value::Builtin(BuiltinFn::StringStartsWith),
|
||||
),
|
||||
(
|
||||
"endsWith".to_string(),
|
||||
Value::Builtin(BuiltinFn::StringEndsWith),
|
||||
),
|
||||
(
|
||||
"toUpper".to_string(),
|
||||
Value::Builtin(BuiltinFn::StringToUpper),
|
||||
),
|
||||
(
|
||||
"toLower".to_string(),
|
||||
Value::Builtin(BuiltinFn::StringToLower),
|
||||
),
|
||||
(
|
||||
"substring".to_string(),
|
||||
Value::Builtin(BuiltinFn::StringSubstring),
|
||||
),
|
||||
]));
|
||||
env.define("String", string_module);
|
||||
|
||||
@@ -789,6 +843,19 @@ impl Interpreter {
|
||||
),
|
||||
]));
|
||||
env.define("Schema", schema_module);
|
||||
|
||||
// Math module
|
||||
let math_module = Value::Record(HashMap::from([
|
||||
("abs".to_string(), Value::Builtin(BuiltinFn::MathAbs)),
|
||||
("min".to_string(), Value::Builtin(BuiltinFn::MathMin)),
|
||||
("max".to_string(), Value::Builtin(BuiltinFn::MathMax)),
|
||||
("sqrt".to_string(), Value::Builtin(BuiltinFn::MathSqrt)),
|
||||
("pow".to_string(), Value::Builtin(BuiltinFn::MathPow)),
|
||||
("floor".to_string(), Value::Builtin(BuiltinFn::MathFloor)),
|
||||
("ceil".to_string(), Value::Builtin(BuiltinFn::MathCeil)),
|
||||
("round".to_string(), Value::Builtin(BuiltinFn::MathRound)),
|
||||
]));
|
||||
env.define("Math", math_module);
|
||||
}
|
||||
|
||||
/// Execute a program
|
||||
@@ -1945,6 +2012,237 @@ impl Interpreter {
|
||||
_ => Err(err("Schema.getVersion: argument must be a Versioned value")),
|
||||
}
|
||||
}
|
||||
|
||||
// Math operations
|
||||
BuiltinFn::MathAbs => {
|
||||
if args.len() != 1 {
|
||||
return Err(err("Math.abs requires 1 argument"));
|
||||
}
|
||||
match &args[0] {
|
||||
Value::Int(n) => Ok(EvalResult::Value(Value::Int(n.abs()))),
|
||||
Value::Float(n) => Ok(EvalResult::Value(Value::Float(n.abs()))),
|
||||
v => Err(err(&format!("Math.abs expects number, got {}", v.type_name()))),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::MathMin => {
|
||||
let (a, b) = Self::expect_args_2::<i64, i64>(&args, "Math.min", span)
|
||||
.or_else(|_| {
|
||||
// Try floats
|
||||
if args.len() == 2 {
|
||||
match (&args[0], &args[1]) {
|
||||
(Value::Float(a), Value::Float(b)) => {
|
||||
return Ok((0i64, 0i64)); // Placeholder - we'll handle below
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Err(err("Math.min requires 2 number arguments"))
|
||||
})?;
|
||||
// Check if they were floats
|
||||
match (&args[0], &args[1]) {
|
||||
(Value::Float(a), Value::Float(b)) => {
|
||||
Ok(EvalResult::Value(Value::Float(a.min(*b))))
|
||||
}
|
||||
(Value::Int(a), Value::Int(b)) => {
|
||||
Ok(EvalResult::Value(Value::Int((*a).min(*b))))
|
||||
}
|
||||
_ => Err(err("Math.min requires 2 number arguments")),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::MathMax => {
|
||||
match (&args[0], &args[1]) {
|
||||
(Value::Float(a), Value::Float(b)) => {
|
||||
Ok(EvalResult::Value(Value::Float(a.max(*b))))
|
||||
}
|
||||
(Value::Int(a), Value::Int(b)) => {
|
||||
Ok(EvalResult::Value(Value::Int((*a).max(*b))))
|
||||
}
|
||||
_ => Err(err("Math.max requires 2 number arguments")),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::MathSqrt => {
|
||||
if args.len() != 1 {
|
||||
return Err(err("Math.sqrt requires 1 argument"));
|
||||
}
|
||||
match &args[0] {
|
||||
Value::Int(n) => Ok(EvalResult::Value(Value::Float((*n as f64).sqrt()))),
|
||||
Value::Float(n) => Ok(EvalResult::Value(Value::Float(n.sqrt()))),
|
||||
v => Err(err(&format!("Math.sqrt expects number, got {}", v.type_name()))),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::MathPow => {
|
||||
if args.len() != 2 {
|
||||
return Err(err("Math.pow requires 2 arguments: base, exponent"));
|
||||
}
|
||||
match (&args[0], &args[1]) {
|
||||
(Value::Int(base), Value::Int(exp)) => {
|
||||
if *exp >= 0 {
|
||||
Ok(EvalResult::Value(Value::Int(base.pow(*exp as u32))))
|
||||
} else {
|
||||
Ok(EvalResult::Value(Value::Float((*base as f64).powi(*exp as i32))))
|
||||
}
|
||||
}
|
||||
(Value::Float(base), Value::Int(exp)) => {
|
||||
Ok(EvalResult::Value(Value::Float(base.powi(*exp as i32))))
|
||||
}
|
||||
(Value::Float(base), Value::Float(exp)) => {
|
||||
Ok(EvalResult::Value(Value::Float(base.powf(*exp))))
|
||||
}
|
||||
(Value::Int(base), Value::Float(exp)) => {
|
||||
Ok(EvalResult::Value(Value::Float((*base as f64).powf(*exp))))
|
||||
}
|
||||
_ => Err(err("Math.pow requires number arguments")),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::MathFloor => {
|
||||
if args.len() != 1 {
|
||||
return Err(err("Math.floor requires 1 argument"));
|
||||
}
|
||||
match &args[0] {
|
||||
Value::Float(n) => Ok(EvalResult::Value(Value::Int(n.floor() as i64))),
|
||||
Value::Int(n) => Ok(EvalResult::Value(Value::Int(*n))),
|
||||
v => Err(err(&format!("Math.floor expects number, got {}", v.type_name()))),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::MathCeil => {
|
||||
if args.len() != 1 {
|
||||
return Err(err("Math.ceil requires 1 argument"));
|
||||
}
|
||||
match &args[0] {
|
||||
Value::Float(n) => Ok(EvalResult::Value(Value::Int(n.ceil() as i64))),
|
||||
Value::Int(n) => Ok(EvalResult::Value(Value::Int(*n))),
|
||||
v => Err(err(&format!("Math.ceil expects number, got {}", v.type_name()))),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::MathRound => {
|
||||
if args.len() != 1 {
|
||||
return Err(err("Math.round requires 1 argument"));
|
||||
}
|
||||
match &args[0] {
|
||||
Value::Float(n) => Ok(EvalResult::Value(Value::Int(n.round() as i64))),
|
||||
Value::Int(n) => Ok(EvalResult::Value(Value::Int(*n))),
|
||||
v => Err(err(&format!("Math.round expects number, got {}", v.type_name()))),
|
||||
}
|
||||
}
|
||||
|
||||
// Additional List operations
|
||||
BuiltinFn::ListIsEmpty => {
|
||||
let list = Self::expect_arg_1::<Vec<Value>>(&args, "List.isEmpty", span)?;
|
||||
Ok(EvalResult::Value(Value::Bool(list.is_empty())))
|
||||
}
|
||||
|
||||
BuiltinFn::ListFind => {
|
||||
let (list, func) = Self::expect_args_2::<Vec<Value>, Value>(&args, "List.find", span)?;
|
||||
for item in list {
|
||||
let v = self.eval_call_to_value(func.clone(), vec![item.clone()], span)?;
|
||||
match v {
|
||||
Value::Bool(true) => {
|
||||
return Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "Some".to_string(),
|
||||
fields: vec![item],
|
||||
}));
|
||||
}
|
||||
Value::Bool(false) => {}
|
||||
_ => return Err(err("List.find predicate must return Bool")),
|
||||
}
|
||||
}
|
||||
Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "None".to_string(),
|
||||
fields: vec![],
|
||||
}))
|
||||
}
|
||||
|
||||
BuiltinFn::ListAny => {
|
||||
let (list, func) = Self::expect_args_2::<Vec<Value>, Value>(&args, "List.any", span)?;
|
||||
for item in list {
|
||||
let v = self.eval_call_to_value(func.clone(), vec![item], span)?;
|
||||
match v {
|
||||
Value::Bool(true) => return Ok(EvalResult::Value(Value::Bool(true))),
|
||||
Value::Bool(false) => {}
|
||||
_ => return Err(err("List.any predicate must return Bool")),
|
||||
}
|
||||
}
|
||||
Ok(EvalResult::Value(Value::Bool(false)))
|
||||
}
|
||||
|
||||
BuiltinFn::ListAll => {
|
||||
let (list, func) = Self::expect_args_2::<Vec<Value>, Value>(&args, "List.all", span)?;
|
||||
for item in list {
|
||||
let v = self.eval_call_to_value(func.clone(), vec![item], span)?;
|
||||
match v {
|
||||
Value::Bool(false) => return Ok(EvalResult::Value(Value::Bool(false))),
|
||||
Value::Bool(true) => {}
|
||||
_ => return Err(err("List.all predicate must return Bool")),
|
||||
}
|
||||
}
|
||||
Ok(EvalResult::Value(Value::Bool(true)))
|
||||
}
|
||||
|
||||
BuiltinFn::ListTake => {
|
||||
let (list, n) = Self::expect_args_2::<Vec<Value>, i64>(&args, "List.take", span)?;
|
||||
let n = n.max(0) as usize;
|
||||
let result: Vec<Value> = list.into_iter().take(n).collect();
|
||||
Ok(EvalResult::Value(Value::List(result)))
|
||||
}
|
||||
|
||||
BuiltinFn::ListDrop => {
|
||||
let (list, n) = Self::expect_args_2::<Vec<Value>, i64>(&args, "List.drop", span)?;
|
||||
let n = n.max(0) as usize;
|
||||
let result: Vec<Value> = list.into_iter().skip(n).collect();
|
||||
Ok(EvalResult::Value(Value::List(result)))
|
||||
}
|
||||
|
||||
// Additional String operations
|
||||
BuiltinFn::StringStartsWith => {
|
||||
let (s, prefix) = Self::expect_args_2::<String, String>(&args, "String.startsWith", span)?;
|
||||
Ok(EvalResult::Value(Value::Bool(s.starts_with(&prefix))))
|
||||
}
|
||||
|
||||
BuiltinFn::StringEndsWith => {
|
||||
let (s, suffix) = Self::expect_args_2::<String, String>(&args, "String.endsWith", span)?;
|
||||
Ok(EvalResult::Value(Value::Bool(s.ends_with(&suffix))))
|
||||
}
|
||||
|
||||
BuiltinFn::StringToUpper => {
|
||||
let s = Self::expect_arg_1::<String>(&args, "String.toUpper", span)?;
|
||||
Ok(EvalResult::Value(Value::String(s.to_uppercase())))
|
||||
}
|
||||
|
||||
BuiltinFn::StringToLower => {
|
||||
let s = Self::expect_arg_1::<String>(&args, "String.toLower", span)?;
|
||||
Ok(EvalResult::Value(Value::String(s.to_lowercase())))
|
||||
}
|
||||
|
||||
BuiltinFn::StringSubstring => {
|
||||
// String.substring(s, start, end) - end is exclusive
|
||||
if args.len() != 3 {
|
||||
return Err(err("String.substring requires 3 arguments: string, start, end"));
|
||||
}
|
||||
let s = match &args[0] {
|
||||
Value::String(s) => s.clone(),
|
||||
v => return Err(err(&format!("String.substring expects String, got {}", v.type_name()))),
|
||||
};
|
||||
let start = match &args[1] {
|
||||
Value::Int(n) => (*n).max(0) as usize,
|
||||
v => return Err(err(&format!("String.substring expects Int for start, got {}", v.type_name()))),
|
||||
};
|
||||
let end = match &args[2] {
|
||||
Value::Int(n) => (*n).max(0) as usize,
|
||||
v => return Err(err(&format!("String.substring expects Int for end, got {}", v.type_name()))),
|
||||
};
|
||||
let chars: Vec<char> = s.chars().collect();
|
||||
let end = end.min(chars.len());
|
||||
let start = start.min(end);
|
||||
let result: String = chars[start..end].iter().collect();
|
||||
Ok(EvalResult::Value(Value::String(result)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
104
src/main.rs
104
src/main.rs
@@ -1377,6 +1377,110 @@ c")"#;
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("downgrade"));
|
||||
}
|
||||
|
||||
// Math module tests
|
||||
#[test]
|
||||
fn test_math_abs() {
|
||||
let (result, _) = run_with_effects("let x = Math.abs(-42)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "42");
|
||||
let (result, _) = run_with_effects("let x = Math.abs(42)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "42");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_math_min_max() {
|
||||
let (result, _) = run_with_effects("let x = Math.min(3, 7)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "3");
|
||||
let (result, _) = run_with_effects("let x = Math.max(3, 7)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_math_sqrt() {
|
||||
let (result, _) = run_with_effects("let x = Math.sqrt(16)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_math_pow() {
|
||||
let (result, _) = run_with_effects("let x = Math.pow(2, 10)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "1024");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_math_floor_ceil_round() {
|
||||
let (result, _) = run_with_effects("let x = Math.floor(3.7)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "3");
|
||||
let (result, _) = run_with_effects("let x = Math.ceil(3.2)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "4");
|
||||
let (result, _) = run_with_effects("let x = Math.round(3.5)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "4");
|
||||
}
|
||||
|
||||
// List module additional functions
|
||||
#[test]
|
||||
fn test_list_is_empty() {
|
||||
let (result, _) = run_with_effects("let x = List.isEmpty([])", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "true");
|
||||
let (result, _) = run_with_effects("let x = List.isEmpty([1, 2])", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_find() {
|
||||
let source = "let x = List.find([1, 2, 3, 4, 5], fn(x: Int): Bool => x > 3)";
|
||||
let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "Some(4)");
|
||||
let source = "let x = List.find([1, 2, 3], fn(x: Int): Bool => x > 10)";
|
||||
let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "None");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_any_all() {
|
||||
let source = "let x = List.any([1, 2, 3], fn(x: Int): Bool => x > 2)";
|
||||
let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "true");
|
||||
let source = "let x = List.all([1, 2, 3], fn(x: Int): Bool => x > 0)";
|
||||
let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "true");
|
||||
let source = "let x = List.all([1, 2, 3], fn(x: Int): Bool => x > 2)";
|
||||
let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_take_drop() {
|
||||
let (result, _) = run_with_effects("let x = List.take([1, 2, 3, 4, 5], 3)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "[1, 2, 3]");
|
||||
let (result, _) = run_with_effects("let x = List.drop([1, 2, 3, 4, 5], 2)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "[3, 4, 5]");
|
||||
}
|
||||
|
||||
// String module additional functions
|
||||
#[test]
|
||||
fn test_string_starts_ends_with() {
|
||||
let (result, _) = run_with_effects("let x = String.startsWith(\"hello\", \"he\")", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "true");
|
||||
let (result, _) = run_with_effects("let x = String.endsWith(\"hello\", \"lo\")", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_case_conversion() {
|
||||
let (result, _) = run_with_effects("let x = String.toUpper(\"hello\")", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "\"HELLO\"");
|
||||
let (result, _) = run_with_effects("let x = String.toLower(\"HELLO\")", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "\"hello\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_substring() {
|
||||
let (result, _) = run_with_effects("let x = String.substring(\"hello world\", 0, 5)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "\"hello\"");
|
||||
let (result, _) = run_with_effects("let x = String.substring(\"hello\", 2, 4)", Value::Unit, Value::Unit).unwrap();
|
||||
assert_eq!(result, "\"ll\"");
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnostic rendering tests
|
||||
|
||||
105
src/types.rs
105
src/types.rs
@@ -974,6 +974,54 @@ impl TypeEnv {
|
||||
"range".to_string(),
|
||||
Type::function(vec![Type::Int, Type::Int], Type::List(Box::new(Type::Int))),
|
||||
),
|
||||
(
|
||||
"isEmpty".to_string(),
|
||||
Type::function(vec![Type::List(Box::new(Type::var()))], Type::Bool),
|
||||
),
|
||||
(
|
||||
"find".to_string(),
|
||||
Type::function(
|
||||
vec![
|
||||
Type::List(Box::new(Type::var())),
|
||||
Type::function(vec![Type::var()], Type::Bool),
|
||||
],
|
||||
Type::Option(Box::new(Type::var())),
|
||||
),
|
||||
),
|
||||
(
|
||||
"any".to_string(),
|
||||
Type::function(
|
||||
vec![
|
||||
Type::List(Box::new(Type::var())),
|
||||
Type::function(vec![Type::var()], Type::Bool),
|
||||
],
|
||||
Type::Bool,
|
||||
),
|
||||
),
|
||||
(
|
||||
"all".to_string(),
|
||||
Type::function(
|
||||
vec![
|
||||
Type::List(Box::new(Type::var())),
|
||||
Type::function(vec![Type::var()], Type::Bool),
|
||||
],
|
||||
Type::Bool,
|
||||
),
|
||||
),
|
||||
(
|
||||
"take".to_string(),
|
||||
Type::function(
|
||||
vec![Type::List(Box::new(Type::var())), Type::Int],
|
||||
Type::List(Box::new(Type::var())),
|
||||
),
|
||||
),
|
||||
(
|
||||
"drop".to_string(),
|
||||
Type::function(
|
||||
vec![Type::List(Box::new(Type::var())), Type::Int],
|
||||
Type::List(Box::new(Type::var())),
|
||||
),
|
||||
),
|
||||
]);
|
||||
env.bind("List", TypeScheme::mono(list_module_type));
|
||||
|
||||
@@ -1017,6 +1065,26 @@ impl TypeEnv {
|
||||
"lines".to_string(),
|
||||
Type::function(vec![Type::String], Type::List(Box::new(Type::String))),
|
||||
),
|
||||
(
|
||||
"startsWith".to_string(),
|
||||
Type::function(vec![Type::String, Type::String], Type::Bool),
|
||||
),
|
||||
(
|
||||
"endsWith".to_string(),
|
||||
Type::function(vec![Type::String, Type::String], Type::Bool),
|
||||
),
|
||||
(
|
||||
"toUpper".to_string(),
|
||||
Type::function(vec![Type::String], Type::String),
|
||||
),
|
||||
(
|
||||
"toLower".to_string(),
|
||||
Type::function(vec![Type::String], Type::String),
|
||||
),
|
||||
(
|
||||
"substring".to_string(),
|
||||
Type::function(vec![Type::String, Type::Int, Type::Int], Type::String),
|
||||
),
|
||||
]);
|
||||
env.bind("String", TypeScheme::mono(string_module_type));
|
||||
|
||||
@@ -1138,6 +1206,43 @@ impl TypeEnv {
|
||||
]);
|
||||
env.bind("Schema", TypeScheme::mono(schema_module_type));
|
||||
|
||||
// Math module
|
||||
let math_module_type = Type::Record(vec![
|
||||
(
|
||||
"abs".to_string(),
|
||||
Type::function(vec![Type::var()], Type::var()), // Works on Int or Float
|
||||
),
|
||||
(
|
||||
"min".to_string(),
|
||||
Type::function(vec![Type::var(), Type::var()], Type::var()),
|
||||
),
|
||||
(
|
||||
"max".to_string(),
|
||||
Type::function(vec![Type::var(), Type::var()], Type::var()),
|
||||
),
|
||||
(
|
||||
"sqrt".to_string(),
|
||||
Type::function(vec![Type::var()], Type::Float),
|
||||
),
|
||||
(
|
||||
"pow".to_string(),
|
||||
Type::function(vec![Type::var(), Type::var()], Type::var()),
|
||||
),
|
||||
(
|
||||
"floor".to_string(),
|
||||
Type::function(vec![Type::var()], Type::Int),
|
||||
),
|
||||
(
|
||||
"ceil".to_string(),
|
||||
Type::function(vec![Type::var()], Type::Int),
|
||||
),
|
||||
(
|
||||
"round".to_string(),
|
||||
Type::function(vec![Type::var()], Type::Int),
|
||||
),
|
||||
]);
|
||||
env.bind("Math", TypeScheme::mono(math_module_type));
|
||||
|
||||
env
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user