csml_interpreter/data/primitive/
float.rs

1use crate::data::primitive::tools::check_division_by_zero_f64;
2use crate::data::{
3    ast::Interval,
4    error_info::ErrorInfo,
5    literal,
6    literal::ContentType,
7    message::Message,
8    position::Position,
9    primitive::{
10        Primitive, PrimitiveBoolean, PrimitiveInt, PrimitiveObject, PrimitiveString, PrimitiveType,
11        Right,
12    },
13    Data, Literal, MemoryType, MessageData, MSG,
14};
15use crate::error_format::*;
16use phf::phf_map;
17use serde::{Deserialize, Serialize};
18use std::cmp::Ordering;
19use std::{collections::HashMap, sync::mpsc};
20
21////////////////////////////////////////////////////////////////////////////////
22// DATA STRUCTURES
23////////////////////////////////////////////////////////////////////////////////
24
25type PrimitiveMethod = fn(
26    float: &mut PrimitiveFloat,
27    args: &HashMap<String, Literal>,
28    additional_info: &Option<HashMap<String, Literal>>,
29    data: &mut Data,
30    interval: Interval,
31) -> Result<Literal, ErrorInfo>;
32
33const FUNCTIONS: phf::Map<&'static str, (PrimitiveMethod, Right)> = phf_map! {
34    "is_number" => (PrimitiveFloat::is_number as PrimitiveMethod, Right::Read),
35    "is_int" => (PrimitiveFloat::is_int as PrimitiveMethod, Right::Read),
36    "is_float" => (PrimitiveFloat::is_float as PrimitiveMethod, Right::Read),
37    "type_of" => (PrimitiveFloat::type_of as PrimitiveMethod, Right::Read),
38    "is_error" => (PrimitiveFloat::is_error as PrimitiveMethod, Right::Read),
39    "get_info" => (PrimitiveFloat::get_info as PrimitiveMethod, Right::Read),
40    "to_string" => (PrimitiveFloat::to_string as PrimitiveMethod, Right::Read),
41
42    "precision" => (PrimitiveFloat::precision as PrimitiveMethod, Right::Read),
43    "abs" => (PrimitiveFloat::abs as PrimitiveMethod, Right::Read),
44    "cos" => (PrimitiveFloat::cos as PrimitiveMethod, Right::Read),
45    "ceil" => (PrimitiveFloat::ceil as PrimitiveMethod, Right::Read),
46    "floor" => (PrimitiveFloat::floor as PrimitiveMethod, Right::Read),
47    "pow" => (PrimitiveFloat::pow as PrimitiveMethod, Right::Read),
48    "round" => (PrimitiveFloat::round as PrimitiveMethod, Right::Read),
49    "sin" => (PrimitiveFloat::sin as PrimitiveMethod, Right::Read),
50    "sqrt" => (PrimitiveFloat::sqrt as PrimitiveMethod, Right::Read),
51    "tan" => (PrimitiveFloat::tan as PrimitiveMethod, Right::Read),
52    "to_int" => (PrimitiveFloat::to_int as PrimitiveMethod, Right::Read),
53    "to_float" => (PrimitiveFloat::to_float as PrimitiveMethod, Right::Read),
54};
55#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
56pub struct PrimitiveFloat {
57    pub value: f64,
58}
59
60////////////////////////////////////////////////////////////////////////////////
61// METHOD FUNCTIONS
62////////////////////////////////////////////////////////////////////////////////
63
64impl PrimitiveFloat {
65    fn is_number(
66        _float: &mut PrimitiveFloat,
67        args: &HashMap<String, Literal>,
68        _additional_info: &Option<HashMap<String, Literal>>,
69        data: &mut Data,
70        interval: Interval,
71    ) -> Result<Literal, ErrorInfo> {
72        let usage = "is_number() => boolean";
73
74        if !args.is_empty() {
75            return Err(gen_error_info(
76                Position::new(interval, &data.context.flow),
77                format!("usage: {}", usage),
78            ));
79        }
80
81        Ok(PrimitiveBoolean::get_literal(true, interval))
82    }
83
84    fn is_int(
85        _float: &mut PrimitiveFloat,
86        args: &HashMap<String, Literal>,
87        _additional_info: &Option<HashMap<String, Literal>>,
88        data: &mut Data,
89        interval: Interval,
90    ) -> Result<Literal, ErrorInfo> {
91        let usage = "is_int() => boolean";
92
93        if !args.is_empty() {
94            return Err(gen_error_info(
95                Position::new(interval, &data.context.flow),
96                format!("usage: {}", usage),
97            ));
98        }
99
100        Ok(PrimitiveBoolean::get_literal(false, interval))
101    }
102
103    fn is_float(
104        _float: &mut PrimitiveFloat,
105        args: &HashMap<String, Literal>,
106        _additional_info: &Option<HashMap<String, Literal>>,
107        data: &mut Data,
108        interval: Interval,
109    ) -> Result<Literal, ErrorInfo> {
110        let usage = "is_float() => boolean";
111
112        if !args.is_empty() {
113            return Err(gen_error_info(
114                Position::new(interval, &data.context.flow),
115                format!("usage: {}", usage),
116            ));
117        }
118
119        Ok(PrimitiveBoolean::get_literal(true, interval))
120    }
121
122    fn type_of(
123        _float: &mut PrimitiveFloat,
124        args: &HashMap<String, Literal>,
125        _additional_info: &Option<HashMap<String, Literal>>,
126        data: &mut Data,
127        interval: Interval,
128    ) -> Result<Literal, ErrorInfo> {
129        let usage = "type_of() => string";
130
131        if !args.is_empty() {
132            return Err(gen_error_info(
133                Position::new(interval, &data.context.flow),
134                format!("usage: {}", usage),
135            ));
136        }
137
138        Ok(PrimitiveString::get_literal("float", interval))
139    }
140
141    fn get_info(
142        _float: &mut PrimitiveFloat,
143        args: &HashMap<String, Literal>,
144        additional_info: &Option<HashMap<String, Literal>>,
145        data: &mut Data,
146        interval: Interval,
147    ) -> Result<Literal, ErrorInfo> {
148        literal::get_info(args, additional_info, interval, data)
149    }
150
151    fn is_error(
152        _float: &mut PrimitiveFloat,
153        _args: &HashMap<String, Literal>,
154        additional_info: &Option<HashMap<String, Literal>>,
155        _data: &mut Data,
156        interval: Interval,
157    ) -> Result<Literal, ErrorInfo> {
158        match additional_info {
159            Some(map) if map.contains_key("error") => {
160                Ok(PrimitiveBoolean::get_literal(true, interval))
161            }
162            _ => Ok(PrimitiveBoolean::get_literal(false, interval)),
163        }
164    }
165
166    fn to_string(
167        float: &mut PrimitiveFloat,
168        args: &HashMap<String, Literal>,
169        _additional_info: &Option<HashMap<String, Literal>>,
170        data: &mut Data,
171        interval: Interval,
172    ) -> Result<Literal, ErrorInfo> {
173        let usage = "to_string() => string";
174
175        if !args.is_empty() {
176            return Err(gen_error_info(
177                Position::new(interval, &data.context.flow),
178                format!("usage: {}", usage),
179            ));
180        }
181
182        Ok(PrimitiveString::get_literal(&float.to_string(), interval))
183    }
184}
185
186impl PrimitiveFloat {
187    fn abs(
188        float: &mut PrimitiveFloat,
189        args: &HashMap<String, Literal>,
190        _additional_info: &Option<HashMap<String, Literal>>,
191        data: &mut Data,
192        interval: Interval,
193    ) -> Result<Literal, ErrorInfo> {
194        let usage = "abs() => float";
195
196        if !args.is_empty() {
197            return Err(gen_error_info(
198                Position::new(interval, &data.context.flow),
199                format!("usage: {}", usage),
200            ));
201        }
202
203        let result = float.value.abs();
204
205        Ok(PrimitiveFloat::get_literal(result, interval))
206    }
207
208    fn cos(
209        float: &mut PrimitiveFloat,
210        args: &HashMap<String, Literal>,
211        _additional_info: &Option<HashMap<String, Literal>>,
212        data: &mut Data,
213        interval: Interval,
214    ) -> Result<Literal, ErrorInfo> {
215        let usage = "cos() => float";
216
217        if !args.is_empty() {
218            return Err(gen_error_info(
219                Position::new(interval, &data.context.flow),
220                format!("usage: {}", usage),
221            ));
222        }
223
224        let result = float.value.cos();
225
226        Ok(PrimitiveFloat::get_literal(result, interval))
227    }
228
229    fn ceil(
230        float: &mut PrimitiveFloat,
231        args: &HashMap<String, Literal>,
232        _additional_info: &Option<HashMap<String, Literal>>,
233        data: &mut Data,
234        interval: Interval,
235    ) -> Result<Literal, ErrorInfo> {
236        let usage = "ceil() => float";
237
238        if !args.is_empty() {
239            return Err(gen_error_info(
240                Position::new(interval, &data.context.flow),
241                format!("usage: {}", usage),
242            ));
243        }
244
245        let result = float.value.ceil();
246
247        Ok(PrimitiveFloat::get_literal(result, interval))
248    }
249
250    fn precision(
251        float: &mut PrimitiveFloat,
252        args: &HashMap<String, Literal>,
253        _additional_info: &Option<HashMap<String, Literal>>,
254        data: &mut Data,
255        interval: Interval,
256    ) -> Result<Literal, ErrorInfo> {
257        let usage = "precision(value) => float";
258
259        let precision = match args.get("arg0") {
260            Some(int) if int.primitive.get_type() == PrimitiveType::PrimitiveInt => {
261                Literal::get_value::<i64>(
262                    &int.primitive,
263                    &data.context.flow,
264                    int.interval,
265                    format!("usage: {}", usage),
266                )?
267            }
268            _ => {
269                return Err(gen_error_info(
270                    Position::new(interval, &data.context.flow),
271                    format!("usage: {}", usage),
272                ))
273            }
274        };
275
276        let result = format!("{:.*}", *precision as usize, float.value)
277            .parse::<f64>()
278            .unwrap_or(float.value);
279
280        Ok(PrimitiveFloat::get_literal(result, interval))
281    }
282
283    fn floor(
284        float: &mut PrimitiveFloat,
285        args: &HashMap<String, Literal>,
286        _additional_info: &Option<HashMap<String, Literal>>,
287        data: &mut Data,
288        interval: Interval,
289    ) -> Result<Literal, ErrorInfo> {
290        let usage = "floor() => float";
291
292        if !args.is_empty() {
293            return Err(gen_error_info(
294                Position::new(interval, &data.context.flow),
295                format!("usage: {}", usage),
296            ));
297        }
298
299        let result = float.value.floor();
300
301        Ok(PrimitiveFloat::get_literal(result, interval))
302    }
303
304    fn pow(
305        float: &mut PrimitiveFloat,
306        args: &HashMap<String, Literal>,
307        _additional_info: &Option<HashMap<String, Literal>>,
308        data: &mut Data,
309        interval: Interval,
310    ) -> Result<Literal, ErrorInfo> {
311        let usage = "pow(exponent: number) => float";
312
313        if args.len() != 1 {
314            return Err(gen_error_info(
315                Position::new(interval, &data.context.flow),
316                format!("usage: {}", usage),
317            ));
318        }
319
320        let exponent = match args.get("arg0") {
321            Some(exponent) if exponent.primitive.get_type() == PrimitiveType::PrimitiveInt => {
322                *Literal::get_value::<i64>(
323                    &exponent.primitive,
324                    &data.context.flow,
325                    interval,
326                    ERROR_NUMBER_POW.to_owned(),
327                )? as f64
328            }
329            Some(exponent) if exponent.primitive.get_type() == PrimitiveType::PrimitiveFloat => {
330                *Literal::get_value::<f64>(
331                    &exponent.primitive,
332                    &data.context.flow,
333                    interval,
334                    ERROR_NUMBER_POW.to_owned(),
335                )?
336            }
337            Some(exponent) if exponent.primitive.get_type() == PrimitiveType::PrimitiveString => {
338                let exponent = Literal::get_value::<String>(
339                    &exponent.primitive,
340                    &data.context.flow,
341                    interval,
342                    ERROR_NUMBER_POW.to_owned(),
343                )?;
344
345                match exponent.parse::<f64>() {
346                    Ok(res) => res,
347                    Err(_) => {
348                        return Err(gen_error_info(
349                            Position::new(interval, &data.context.flow),
350                            ERROR_NUMBER_POW.to_owned(),
351                        ));
352                    }
353                }
354            }
355            _ => {
356                return Err(gen_error_info(
357                    Position::new(interval, &data.context.flow),
358                    ERROR_NUMBER_POW.to_owned(),
359                ));
360            }
361        };
362
363        let result = float.value.powf(exponent);
364
365        Ok(PrimitiveFloat::get_literal(result, interval))
366    }
367
368    fn round(
369        float: &mut PrimitiveFloat,
370        args: &HashMap<String, Literal>,
371        _additional_info: &Option<HashMap<String, Literal>>,
372        data: &mut Data,
373        interval: Interval,
374    ) -> Result<Literal, ErrorInfo> {
375        let usage = "round() => float";
376
377        if !args.is_empty() {
378            return Err(gen_error_info(
379                Position::new(interval, &data.context.flow),
380                format!("usage: {}", usage),
381            ));
382        }
383
384        let result = float.value.round();
385
386        Ok(PrimitiveFloat::get_literal(result, interval))
387    }
388
389    fn sin(
390        float: &mut PrimitiveFloat,
391        args: &HashMap<String, Literal>,
392        _additional_info: &Option<HashMap<String, Literal>>,
393        data: &mut Data,
394        interval: Interval,
395    ) -> Result<Literal, ErrorInfo> {
396        let usage = "sin() => float";
397
398        if !args.is_empty() {
399            return Err(gen_error_info(
400                Position::new(interval, &data.context.flow),
401                format!("usage: {}", usage),
402            ));
403        }
404
405        let result = float.value.sin();
406
407        Ok(PrimitiveFloat::get_literal(result, interval))
408    }
409
410    fn sqrt(
411        float: &mut PrimitiveFloat,
412        args: &HashMap<String, Literal>,
413        _additional_info: &Option<HashMap<String, Literal>>,
414        data: &mut Data,
415        interval: Interval,
416    ) -> Result<Literal, ErrorInfo> {
417        let usage = "sqrt() => float";
418
419        if !args.is_empty() {
420            return Err(gen_error_info(
421                Position::new(interval, &data.context.flow),
422                format!("usage: {}", usage),
423            ));
424        }
425
426        let result = float.value.sqrt();
427
428        Ok(PrimitiveFloat::get_literal(result, interval))
429    }
430
431    fn tan(
432        float: &mut PrimitiveFloat,
433        args: &HashMap<String, Literal>,
434        _additional_info: &Option<HashMap<String, Literal>>,
435        data: &mut Data,
436        interval: Interval,
437    ) -> Result<Literal, ErrorInfo> {
438        let usage = "tan() => float";
439
440        if !args.is_empty() {
441            return Err(gen_error_info(
442                Position::new(interval, &data.context.flow),
443                format!("usage: {}", usage),
444            ));
445        }
446
447        let result = float.value.tan();
448
449        Ok(PrimitiveFloat::get_literal(result, interval))
450    }
451
452    fn to_int(
453        float: &mut PrimitiveFloat,
454        args: &HashMap<String, Literal>,
455        _additional_info: &Option<HashMap<String, Literal>>,
456        data: &mut Data,
457        interval: Interval,
458    ) -> Result<Literal, ErrorInfo> {
459        let usage = "to_int() => int";
460
461        if !args.is_empty() {
462            return Err(gen_error_info(
463                Position::new(interval, &data.context.flow),
464                format!("usage: {}", usage),
465            ));
466        }
467
468        Ok(PrimitiveInt::get_literal(float.value as i64, interval))
469    }
470
471    fn to_float(
472        float: &mut PrimitiveFloat,
473        args: &HashMap<String, Literal>,
474        _additional_info: &Option<HashMap<String, Literal>>,
475        data: &mut Data,
476        interval: Interval,
477    ) -> Result<Literal, ErrorInfo> {
478        let usage = "to_float() => float";
479
480        if !args.is_empty() {
481            return Err(gen_error_info(
482                Position::new(interval, &data.context.flow),
483                format!("usage: {}", usage),
484            ));
485        }
486
487        Ok(PrimitiveFloat::get_literal(float.value, interval))
488    }
489}
490
491////////////////////////////////////////////////////////////////////////////////
492// PUBLIC FUNCTIONS
493////////////////////////////////////////////////////////////////////////////////
494
495impl PrimitiveFloat {
496    pub fn new(value: f64) -> Self {
497        Self { value }
498    }
499
500    pub fn get_literal(float: f64, interval: Interval) -> Literal {
501        let primitive = Box::new(PrimitiveFloat::new(float));
502
503        Literal {
504            content_type: "float".to_owned(),
505            primitive,
506            additional_info: None,
507            secure_variable: false,
508            interval,
509        }
510    }
511}
512
513////////////////////////////////////////////////////////////////////////////////
514// TRAIT FUNCTIONS
515////////////////////////////////////////////////////////////////////////////////
516
517#[typetag::serde]
518impl Primitive for PrimitiveFloat {
519    fn is_eq(&self, other: &dyn Primitive) -> bool {
520        if let Some(other) = other.as_any().downcast_ref::<Self>() {
521            return self.value == other.value;
522        }
523
524        false
525    }
526
527    fn is_cmp(&self, other: &dyn Primitive) -> Option<Ordering> {
528        if let Some(other) = other.as_any().downcast_ref::<Self>() {
529            return self.value.partial_cmp(&other.value);
530        }
531
532        None
533    }
534
535    fn do_add(&self, other: &dyn Primitive) -> Result<Box<dyn Primitive>, String> {
536        let mut error_msg = ERROR_ILLEGAL_OPERATION;
537
538        if let Some(other) = other.as_any().downcast_ref::<Self>() {
539            let lhs = self.value as i64;
540            let rhs = other.value as i64;
541
542            if lhs.checked_add(rhs).is_some() {
543                return Ok(Box::new(PrimitiveFloat::new(self.value + other.value)));
544            }
545
546            error_msg = OVERFLOWING_OPERATION;
547        }
548
549        Err(format!(
550            "{} {:?} + {:?}",
551            error_msg,
552            self.get_type(),
553            other.get_type()
554        ))
555    }
556
557    fn do_sub(&self, other: &dyn Primitive) -> Result<Box<dyn Primitive>, String> {
558        let mut error_msg = ERROR_ILLEGAL_OPERATION;
559
560        if let Some(other) = other.as_any().downcast_ref::<Self>() {
561            let lhs = self.value as i64;
562            let rhs = other.value as i64;
563
564            if lhs.checked_sub(rhs).is_some() {
565                return Ok(Box::new(PrimitiveFloat::new(self.value - other.value)));
566            }
567
568            error_msg = OVERFLOWING_OPERATION;
569        }
570
571        Err(format!(
572            "{} {:?} - {:?}",
573            error_msg,
574            self.get_type(),
575            other.get_type()
576        ))
577    }
578
579    fn do_div(&self, other: &dyn Primitive) -> Result<Box<dyn Primitive>, String> {
580        let mut error_msg = ERROR_ILLEGAL_OPERATION;
581
582        if let Some(other) = other.as_any().downcast_ref::<Self>() {
583            check_division_by_zero_f64(self.value, other.value)?;
584
585            let lhs = self.value as i64;
586            let rhs = other.value as i64;
587
588            if lhs.checked_div(rhs).is_some() {
589                return Ok(Box::new(PrimitiveFloat::new(self.value / other.value)));
590            }
591
592            error_msg = OVERFLOWING_OPERATION;
593        }
594
595        Err(format!(
596            "{} {:?} / {:?}",
597            error_msg,
598            self.get_type(),
599            other.get_type()
600        ))
601    }
602
603    fn do_mul(&self, other: &dyn Primitive) -> Result<Box<dyn Primitive>, String> {
604        let mut error_msg = ERROR_ILLEGAL_OPERATION;
605
606        if let Some(other) = other.as_any().downcast_ref::<Self>() {
607            let lhs = self.value as i64;
608            let rhs = other.value as i64;
609
610            if lhs.checked_mul(rhs).is_some() {
611                return Ok(Box::new(PrimitiveFloat::new(self.value * other.value)));
612            }
613
614            error_msg = OVERFLOWING_OPERATION;
615        }
616
617        Err(format!(
618            "{} {:?} * {:?}",
619            error_msg,
620            self.get_type(),
621            other.get_type()
622        ))
623    }
624
625    fn do_rem(&self, other: &dyn Primitive) -> Result<Box<dyn Primitive>, String> {
626        let mut error_msg = ERROR_ILLEGAL_OPERATION;
627
628        if let Some(other) = other.as_any().downcast_ref::<Self>() {
629            let lhs = self.value as i64;
630            let rhs = other.value as i64;
631
632            if lhs.checked_rem(rhs).is_some() {
633                return Ok(Box::new(PrimitiveFloat::new(self.value % other.value)));
634            }
635
636            error_msg = OVERFLOWING_OPERATION;
637        }
638
639        Err(format!(
640            "{} {:?} % {:?}",
641            error_msg,
642            self.get_type(),
643            other.get_type()
644        ))
645    }
646
647    fn as_debug(&self) -> &dyn std::fmt::Debug {
648        self
649    }
650
651    fn as_any(&self) -> &dyn std::any::Any {
652        self
653    }
654
655    fn get_type(&self) -> PrimitiveType {
656        PrimitiveType::PrimitiveFloat
657    }
658
659    fn as_box_clone(&self) -> Box<dyn Primitive> {
660        Box::new((*self).clone())
661    }
662
663    fn to_json(&self) -> serde_json::Value {
664        serde_json::json!(self.value)
665    }
666
667    fn format_mem(&self, _content_type: &str, _first: bool) -> serde_json::Value {
668        serde_json::json!(self.value)
669    }
670
671    fn to_string(&self) -> String {
672        self.value.to_string()
673    }
674
675    fn as_bool(&self) -> bool {
676        self.value.is_normal()
677    }
678
679    fn get_value(&self) -> &dyn std::any::Any {
680        &self.value
681    }
682
683    fn get_mut_value(&mut self) -> &mut dyn std::any::Any {
684        &mut self.value
685    }
686
687    fn to_msg(&self, _content_type: String) -> Message {
688        let mut hashmap: HashMap<String, Literal> = HashMap::new();
689
690        hashmap.insert(
691            "text".to_owned(),
692            Literal {
693                content_type: "float".to_owned(),
694                primitive: Box::new(PrimitiveString::new(&self.to_string())),
695                additional_info: None,
696                secure_variable: false,
697                interval: Interval {
698                    start_column: 0,
699                    start_line: 0,
700                    offset: 0,
701                    end_line: None,
702                    end_column: None,
703                },
704            },
705        );
706
707        let mut result = PrimitiveObject::get_literal(
708            &hashmap,
709            Interval {
710                start_column: 0,
711                start_line: 0,
712                offset: 0,
713                end_line: None,
714                end_column: None,
715            },
716        );
717        result.set_content_type("text");
718
719        Message {
720            content_type: result.content_type,
721            content: result.primitive.to_json(),
722        }
723    }
724
725    fn do_exec(
726        &mut self,
727        name: &str,
728        args: &HashMap<String, Literal>,
729        mem_type: &MemoryType,
730        additional_info: &Option<HashMap<String, Literal>>,
731        interval: Interval,
732        _content_type: &ContentType,
733        data: &mut Data,
734        _msg_data: &mut MessageData,
735        _sender: &Option<mpsc::Sender<MSG>>,
736    ) -> Result<(Literal, Right), ErrorInfo> {
737        if let Some((f, right)) = FUNCTIONS.get(name) {
738            if *mem_type == MemoryType::Constant && *right == Right::Write {
739                return Err(gen_error_info(
740                    Position::new(interval, &data.context.flow),
741                    format!("{}" , ERROR_CONSTANT_MUTABLE_FUNCTION),
742                ));
743            } else {
744                let res = f(self, args, additional_info, data, interval)?;
745
746                return Ok((res, *right));
747            }
748        }
749
750        Err(gen_error_info(
751            Position::new(interval, &data.context.flow),
752            format!("[{}] {}", name, ERROR_FLOAT_UNKNOWN_METHOD),
753        ))
754    }
755}