basic/mach/
function.rs

1extern crate chrono;
2use super::{Opcode, Stack, Val};
3use crate::error;
4use crate::lang::Error;
5use std::convert::TryFrom;
6use std::rc::Rc;
7
8type Result<T> = std::result::Result<T, Error>;
9
10pub struct Function {}
11
12impl Function {
13    pub fn opcode_and_arity(func_name: &str) -> Option<(Opcode, std::ops::RangeInclusive<usize>)> {
14        match func_name {
15            "ABS" => Some((Opcode::Abs, 1..=1)),
16            "ASC" => Some((Opcode::Asc, 1..=1)),
17            "ATN" => Some((Opcode::Atn, 1..=1)),
18            "CDBL" => Some((Opcode::Cdbl, 1..=1)),
19            "CHR$" => Some((Opcode::Chr, 1..=1)),
20            "CINT" => Some((Opcode::Cint, 1..=1)),
21            "COS" => Some((Opcode::Cos, 1..=1)),
22            "CSNG" => Some((Opcode::Csng, 1..=1)),
23            "DATE$" => Some((Opcode::Date, 0..=0)),
24            "EXP" => Some((Opcode::Exp, 1..=1)),
25            "FIX" => Some((Opcode::Fix, 1..=1)),
26            "HEX$" => Some((Opcode::Hex, 1..=1)),
27            "INKEY$" => Some((Opcode::Inkey, 0..=0)),
28            "INSTR" => Some((Opcode::Instr, 2..=3)),
29            "INT" => Some((Opcode::Int, 1..=1)),
30            "LEFT$" => Some((Opcode::Left, 2..=2)),
31            "LEN" => Some((Opcode::Len, 1..=1)),
32            "LOG" => Some((Opcode::Log, 1..=1)),
33            "MID$" => Some((Opcode::Mid, 2..=3)),
34            "OCT$" => Some((Opcode::Oct, 1..=1)),
35            "POS" => Some((Opcode::Pos, 0..=1)),
36            "RIGHT$" => Some((Opcode::Right, 2..=2)),
37            "RND" => Some((Opcode::Rnd, 0..=1)),
38            "SGN" => Some((Opcode::Sgn, 1..=1)),
39            "SIN" => Some((Opcode::Sin, 1..=1)),
40            "SPC" => Some((Opcode::Spc, 1..=1)),
41            "SQR" => Some((Opcode::Sqr, 1..=1)),
42            "STR$" => Some((Opcode::Str, 1..=1)),
43            "STRING$" => Some((Opcode::String, 2..=2)),
44            "TAB" => Some((Opcode::Tab, 1..=1)),
45            "TAN" => Some((Opcode::Tan, 1..=1)),
46            "TIME$" => Some((Opcode::Time, 0..=0)),
47            "VAL" => Some((Opcode::Val, 1..=1)),
48            _ => None,
49        }
50    }
51
52    pub fn abs(val: Val) -> Result<Val> {
53        use Val::*;
54        match val {
55            Integer(n) => Ok(Integer(n.abs())),
56            Single(n) => Ok(Single(n.abs())),
57            Double(n) => Ok(Double(n.abs())),
58            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
59        }
60    }
61
62    pub fn asc(string: Val) -> Result<Val> {
63        let string = Rc::<str>::try_from(string)?;
64        match string.chars().next() {
65            Some(ch) => {
66                let num = u32::from(ch);
67                if num <= i16::max_value() as u32 {
68                    Ok(Val::Integer(num as i16))
69                } else if num <= 16_777_216 {
70                    Ok(Val::Single(num as f32))
71                } else {
72                    Ok(Val::Double(num as f64))
73                }
74            }
75            None => Err(error!(IllegalFunctionCall)),
76        }
77    }
78
79    pub fn atn(val: Val) -> Result<Val> {
80        use Val::*;
81        match val {
82            Integer(n) => Ok(Single((n as f32).atan())),
83            Single(n) => Ok(Single(n.atan())),
84            Double(n) => Ok(Double(n.atan())),
85            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
86        }
87    }
88
89    pub fn cdbl(val: Val) -> Result<Val> {
90        use Val::*;
91        match val {
92            Integer(n) => Ok(Double(n as f64)),
93            Single(n) => Ok(Double(n as f64)),
94            Double(n) => Ok(Double(n)),
95            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
96        }
97    }
98
99    pub fn chr(val: Val) -> Result<Val> {
100        match char::try_from(u32::try_from(val)?) {
101            Ok(ch) => Ok(Val::String(ch.to_string().into())),
102            Err(_) => Err(error!(Overflow)),
103        }
104    }
105
106    pub fn cint(val: Val) -> Result<Val> {
107        Ok(Val::Integer(i16::try_from(val)?))
108    }
109
110    pub fn cos(val: Val) -> Result<Val> {
111        use Val::*;
112        match val {
113            Integer(n) => Ok(Single((n as f32).cos())),
114            Single(n) => Ok(Single(n.cos())),
115            Double(n) => Ok(Double(n.cos())),
116            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
117        }
118    }
119
120    pub fn csng(val: Val) -> Result<Val> {
121        use Val::*;
122        match val {
123            Integer(n) => Ok(Single(n as f32)),
124            Single(n) => Ok(Single(n)),
125            Double(n) => Ok(Single(n as f32)),
126            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
127        }
128    }
129
130    pub fn date() -> Result<Val> {
131        Ok(Val::String(
132            chrono::Local::now().format("%m-%d-%Y").to_string().into(),
133        ))
134    }
135
136    pub fn exp(val: Val) -> Result<Val> {
137        use Val::*;
138        match val {
139            Integer(n) => Ok(Single((n as f32).exp())),
140            Single(n) => Ok(Single(n.exp())),
141            Double(n) => Ok(Double(n.exp())),
142            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
143        }
144    }
145
146    pub fn fix(val: Val) -> Result<Val> {
147        use Val::*;
148        match val {
149            Integer(n) => Ok(Integer(n)),
150            Single(n) => Ok(Single(n.trunc())),
151            Double(n) => Ok(Double(n.trunc())),
152            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
153        }
154    }
155
156    pub fn hex(val: Val) -> Result<Val> {
157        let num = i16::try_from(val)?;
158        Ok(Val::String(format!("{:X}", num).into()))
159    }
160
161    pub fn instr(mut vec_val: Stack<Val>) -> Result<Val> {
162        let pattern = Rc::<str>::try_from(vec_val.pop()?)?;
163        let string = Rc::<str>::try_from(vec_val.pop()?)?;
164        let start = match vec_val.pop() {
165            Ok(n) => i16::try_from(n)?,
166            Err(_) => 1,
167        } as usize;
168        if start == 0 {
169            return Err(error!(IllegalFunctionCall; "START IS 0"));
170        }
171        let ch_idx = match string.char_indices().nth(start - 1) {
172            Some((pos, _ch)) => pos,
173            None => return Ok(Val::Integer(0)),
174        };
175        let string: Rc<str> = string[ch_idx..].into(); //??
176        let index = match string.find(pattern.as_ref()) {
177            Some(n) => n,
178            None => 0,
179        };
180        let str_index = string
181            .char_indices()
182            .enumerate()
183            .find_map(|(str_idx, (ch_idx, _ch))| {
184                if ch_idx == index {
185                    Some(str_idx + start)
186                } else {
187                    None
188                }
189            });
190        match str_index {
191            Some(n) => Ok(Val::try_from(n)?),
192            None => Ok(Val::Integer(0)),
193        }
194    }
195
196    pub fn int(val: Val) -> Result<Val> {
197        use Val::*;
198        match val {
199            Integer(n) => Ok(Integer(n)),
200            Single(n) => Ok(Single(n.floor())),
201            Double(n) => Ok(Double(n.floor())),
202            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
203        }
204    }
205
206    pub fn left(string: Val, len: Val) -> Result<Val> {
207        let len = usize::try_from(len)?;
208        let string = Rc::<str>::try_from(string)?;
209        match string.char_indices().nth(len) {
210            Some((pos, _ch)) => Ok(Val::String(string[..pos].into())),
211            None => Ok(Val::String(string)),
212        }
213    }
214
215    pub fn len(string: Val) -> Result<Val> {
216        let string = Rc::<str>::try_from(string)?;
217        Ok(Val::try_from(string.chars().count())?)
218    }
219
220    pub fn log(val: Val) -> Result<Val> {
221        use Val::*;
222        match val {
223            Integer(n) => Ok(Single((n as f32).ln())),
224            Single(n) => Ok(Single(n.ln())),
225            Double(n) => Ok(Double(n.ln())),
226            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
227        }
228    }
229
230    pub fn mid(mut args: Stack<Val>) -> Result<Val> {
231        let len = match args.len() {
232            3 => Some(u16::try_from(args.pop()?)?),
233            _ => None,
234        };
235        let pos = usize::try_from(args.pop()?)?;
236        if pos == 0 {
237            return Err(error!(Overflow));
238        }
239        let string = Rc::<str>::try_from(args.pop()?)?;
240        match string.char_indices().nth(pos - 1) {
241            Some((pos, _ch)) => match len {
242                None => Ok(Val::String(string[pos..].into())),
243                Some(len) => {
244                    let string: Rc<str> = string[pos..].into();
245                    match string.char_indices().nth(len as usize) {
246                        Some((pos, _ch)) => Ok(Val::String(string[..pos].into())),
247                        None => Ok(Val::String(string)),
248                    }
249                }
250            },
251            None => Ok(Val::String(string)),
252        }
253    }
254
255    pub fn oct(val: Val) -> Result<Val> {
256        let num = i16::try_from(val)?;
257        Ok(Val::String(format!("{:o}", num).into()))
258    }
259
260    pub fn pos(print_col: usize) -> Result<Val> {
261        match i16::try_from(print_col) {
262            Ok(pos) => Ok(Val::Integer(pos)),
263            Err(_) => Err(error!(Overflow)),
264        }
265    }
266
267    pub fn right(string: Val, len: Val) -> Result<Val> {
268        let len = usize::try_from(len)?;
269        if len == 0 {
270            return Ok(Val::String("".into()));
271        }
272        let string = Rc::<str>::try_from(string)?;
273        match string.char_indices().rev().nth(len - 1) {
274            Some((pos, _ch)) => Ok(Val::String(string[pos..].into())),
275            None => Ok(Val::String(string)),
276        }
277    }
278
279    pub fn rnd(st: &mut (u32, u32, u32), mut vec_val: Stack<Val>) -> Result<Val> {
280        let val = match vec_val.pop() {
281            Ok(s) => f32::try_from(s)?,
282            Err(_) => 1.0,
283        };
284        if val < 0.0 {
285            let seed = u32::from_le_bytes(val.to_be_bytes()) & 0x_00FF_FFFF;
286            st.0 = seed;
287            st.1 = seed;
288            st.2 = seed;
289        }
290        if val != 0.0 {
291            st.0 = (171 * st.0) % 30269;
292            st.1 = (172 * st.1) % 30307;
293            st.2 = (170 * st.2) % 30323;
294        }
295        Ok(Val::Single(
296            (st.0 as f32 / 30269.0 + st.1 as f32 / 30307.0 + st.2 as f32 / 30323.0) % 1.0,
297        ))
298    }
299
300    pub fn sgn(val: Val) -> Result<Val> {
301        use Val::*;
302        match val {
303            Integer(n) => Ok(Integer(if n == 0 {
304                0
305            } else if n.is_negative() {
306                -1
307            } else {
308                1
309            })),
310            Single(n) => Ok(Integer(if n == 0.0 {
311                0
312            } else if n.is_sign_negative() {
313                -1
314            } else {
315                1
316            })),
317            Double(n) => Ok(Integer(if n == 0.0 {
318                0
319            } else if n.is_sign_negative() {
320                -1
321            } else {
322                1
323            })),
324            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
325        }
326    }
327
328    pub fn sin(val: Val) -> Result<Val> {
329        use Val::*;
330        match val {
331            Integer(n) => Ok(Single((n as f32).sin())),
332            Single(n) => Ok(Single(n.sin())),
333            Double(n) => Ok(Double(n.sin())),
334            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
335        }
336    }
337
338    pub fn spc(val: Val) -> Result<Val> {
339        let len = usize::try_from(val)?;
340        if len > 255 {
341            return Err(error!(Overflow));
342        }
343        Ok(Val::String(" ".repeat(len).into()))
344    }
345
346    pub fn sqr(val: Val) -> Result<Val> {
347        use Val::*;
348        match val {
349            Integer(n) => Ok(Single((n as f32).sqrt())),
350            Single(n) => Ok(Single(n.sqrt())),
351            Double(n) => Ok(Double(n.sqrt())),
352            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
353        }
354    }
355
356    pub fn str(val: Val) -> Result<Val> {
357        use Val::*;
358        match val {
359            Integer(_) | Single(_) | Double(_) => Ok(String(format!("{}", val).into())),
360            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
361        }
362    }
363
364    pub fn string(num: Val, ch: Val) -> Result<Val> {
365        let num = usize::try_from(num)?;
366        if num > 255 {
367            return Err(error!(Overflow));
368        }
369        let ch = match ch {
370            Val::String(s) => match s.chars().next() {
371                Some(ch) => ch,
372                None => return Err(error!(IllegalFunctionCall)),
373            },
374            _ => {
375                let num = u32::try_from(ch)?;
376                match char::try_from(num) {
377                    Ok(ch) => ch,
378                    _ => return Err(error!(Overflow)),
379                }
380            }
381        };
382        Ok(Val::String(ch.to_string().repeat(num).into()))
383    }
384
385    pub fn tab(print_col: usize, val: Val) -> Result<Val> {
386        let tab = i16::try_from(val)?;
387        if tab < -255 || tab > 255 {
388            return Err(error!(Overflow));
389        }
390        let len = if tab < 0 {
391            let tab = -tab as usize;
392            tab - (print_col % tab)
393        } else if tab as usize > print_col {
394            tab as usize - print_col
395        } else {
396            0
397        };
398        Ok(Val::String(" ".repeat(len).into()))
399    }
400
401    pub fn tan(val: Val) -> Result<Val> {
402        use Val::*;
403        match val {
404            Integer(n) => Ok(Single((n as f32).tan())),
405            Single(n) => Ok(Single(n.tan())),
406            Double(n) => Ok(Double(n.tan())),
407            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
408        }
409    }
410
411    pub fn time() -> Result<Val> {
412        Ok(Val::String(
413            chrono::Local::now().format("%H:%M:%S").to_string().into(),
414        ))
415    }
416
417    pub fn val(val: Val) -> Result<Val> {
418        if let Val::String(s) = val {
419            let mut s = s.trim();
420            while let Some((idx, _)) = s.char_indices().last() {
421                let v = Val::from(s);
422                if !matches!(v, Val::String(_)) {
423                    return Ok(v);
424                }
425                s = &s[0..idx];
426            }
427            Ok(Val::Integer(0))
428        } else {
429            Err(error!(TypeMismatch))
430        }
431    }
432}