phoenix_lang/native/
native_functions.rs

1use lazy_static::lazy_static;
2use std::sync::Mutex;
3use std::time::{SystemTime, UNIX_EPOCH};
4
5use crate::chunk::ModuleChunk;
6use crate::value::Value;
7use crate::value::Value::*;
8use crate::vm::{VMState, VM};
9use rand::Rng;
10use std::collections::HashMap;
11
12// todo: create methods for eg lists, classes or strings and replace the hard coded versions in the vm (push, pop, len, etc)
13// todo: maybe create a struct for the args
14pub type NativeFn = fn(Vec<Value>, &VM, &mut VMState, &[ModuleChunk]) -> Result<Value, String>;
15
16lazy_static! {
17    // format: name, (arity, function) if arity is None, the function is variadic
18    pub static ref NATIVE_FUNCTIONS: Mutex<HashMap<&'static str, (Option<usize>, NativeFn)>> =
19        Mutex::new(HashMap::from([
20            ("clock", (Some(0), clock as NativeFn)),
21            (
22                "to_string",
23                (Some(1), |args,vm,state,modules| {
24                    Ok(PhoenixString(args[0].to_string(vm, state, &modules.to_vec())))
25                })
26            ),
27            (
28                "type",
29                (Some(1), |args,_,_,_| {
30                    Ok(PhoenixString(args[0].get_type().to_string()))
31                })
32            ),
33            ("printf", (None, printf as NativeFn)),
34            ("int", (Some(1), int as NativeFn)),
35            ("float", (Some(1), float as NativeFn)),
36            ("rand", (Some(0), |_,_,_,_| {
37                Ok(Float(rand::random::<f32>()))
38            })),
39            ("rand_int", (Some(2), |args,_,_,_| {
40                let min = match args[0] {
41                    Float(f) => f as i32,
42                    Long(l) => l as i32,
43                    _ => {
44                        return Err(format!("Expected int or float as first argument, got {}", args[0].get_type()));
45                    }
46                } as i64;
47                let max = match args[1] {
48                    Float(f) => f as i32,
49                    Long(l) => l as i32,
50                    _ => {
51                        return Err(format!("Expected int or float as second argument, got {}", args[1].get_type()));
52                    }
53                } as i64;
54                if min - max == 0 {
55                    return Err("min and max cannot be the same!".to_string());
56                }
57                Ok(Long(rand::thread_rng().gen_range(min..max)))
58            })),
59            ("rand_float", (Some(2), |args,_,_,_| {
60                let min = match args[0] {
61                    Float(f) => f,
62                    Long(l) => l as f32,
63                    _ => {
64                        return Err(format!("Expected int or float as first argument, got {}", args[0].get_type()));
65                    }
66                };
67                let max = match args[1] {
68                    Float(f) => f,
69                    Long(l) => l as f32,
70                    _ => {
71                        return Err(format!("Expected int or float as second argument, got {}", args[1].get_type()));
72                    }
73                };
74                Ok(Float(rand::thread_rng().gen_range(min..max)))
75            })),
76        ]));
77}
78
79pub fn clock(
80    _args: Vec<Value>,
81    _: &VM,
82    _: &mut VMState,
83    _: &[ModuleChunk],
84) -> Result<Value, String> {
85    let start = SystemTime::now();
86    let since_the_epoch = match start.duration_since(UNIX_EPOCH) {
87        Ok(n) => n,
88        Err(_) => {
89            return Err("Could not get time!".to_string());
90        }
91    };
92    // println!("debug: time: {}", since_the_epoch.as_millis());
93    Ok(Long(since_the_epoch.as_millis() as i64))
94}
95#[allow(dead_code)]
96pub fn value_to_string(v: &Value) -> String {
97    match v {
98        Float(x) => format!("{}", x),
99        Long(x) => format!("{}", x),
100        Bool(x) => format!("{}", x),
101        Nil => "NIL".to_string(),
102        _ => {
103            todo!("to_string() not implemented for this type")
104        }
105    }
106}
107
108/// This formats and prints messages to the console like print!("{} {}", "Hello", "World"); in rust
109fn printf(
110    args: Vec<Value>,
111    vm: &VM,
112    state: &mut VMState,
113    modules: &[ModuleChunk],
114) -> Result<Value, String> {
115    match &args[0] {
116        PhoenixPointer(p) => {
117            let s = &state.deref_string(*p).value;
118            // get all {} in the string and replace it with the corresponding argument
119            let mut args = args.clone();
120            args.remove(0);
121            let mut i = 0;
122            let mut text = s.clone();
123            while i < text.len() {
124                if text.chars().nth(i).unwrap() == '{' && text.chars().nth(i + 1).unwrap() == '}' {
125                    text.remove(i);
126                    text.remove(i);
127                    if args.is_empty() {
128                        return Err(format!(
129                            "[printf] To few arguments! Expected {} got {}!",
130                            i + 1,
131                            args.len()
132                        ));
133                    }
134                    // text.insert_str(i, &value_to_string(args.get(0).unwrap()));
135                    text.insert_str(
136                        i,
137                        &args.get(0).unwrap().to_string(vm, state, &modules.to_vec()),
138                    );
139                    args.remove(0);
140                }
141                i += 1;
142            }
143            println!("{}", text);
144        }
145        _ => {
146            return Err("First argument must be a string!".to_string());
147        }
148    }
149
150    Ok(Nil)
151}
152
153pub fn int(args: Vec<Value>, _: &VM, _: &mut VMState, _: &[ModuleChunk]) -> Result<Value, String> {
154    Ok(match &args[0] {
155        Float(f) => Long(*f as i64),
156        Long(l) => Long(*l),
157        Bool(b) => Long(*b as i64),
158        PhoenixString(s) => Long(match s.parse::<i64>() {
159            Ok(i) => i,
160            Err(_) => {
161                return Err(format!("Could not convert \"{s}\" to int!"));
162            }
163        }),
164        _ => {
165            return Err(format!("Could not convert {} to int!", args[0].get_type()));
166        }
167    })
168}
169
170pub fn float(
171    args: Vec<Value>,
172    _: &VM,
173    _: &mut VMState,
174    _: &[ModuleChunk],
175) -> Result<Value, String> {
176    Ok(match &args[0] {
177        Float(f) => Float(*f),
178        Long(l) => Float(*l as f32),
179        Bool(b) => Float(*b as i32 as f32),
180        PhoenixString(s) => Float(
181            match if s.ends_with('f') {
182                &s[0..s.len() - 1]
183            } else {
184                s
185            }
186            .parse::<f32>()
187            {
188                Ok(i) => i,
189                Err(_) => {
190                    return Err(format!("Could not convert {} to float!", s));
191                }
192            },
193        ),
194        _ => {
195            return Err(format!(
196                "Could not convert {} to float!",
197                args[0].get_type()
198            ));
199        }
200    })
201}