molt_forked/value.rs
1//! The Value Type
2//!
3//! The [`Value`] struct is the standard representation of a data value
4//! in the Molt language. It represents a single immutable data value; the
5//! data is reference-counted, so instances can be cloned efficiently. Its
6//! content may be any TCL data value: e.g., a number, a list, a string, or a value of
7//! an arbitrary type that meets certain requirements.
8//!
9//! In TCL, "everything is a string": every value is defined by its _string
10//! representation_, or _string rep_. For example, "one two three" is the string rep of a
11//! list with three items, the strings "one", "two", and "three". A string that is a
12//! valid string rep for multiple types can be interpreted as any of those types;
13//! for example, the string "5" can be used as a string, the integer 5, or a list of one
14//! element, the value "5".
15//!
16//! Internally, the `Value` can also have a `data representation`, or `data rep`, that
17//! reflects how the value has been most recently used. Once a `Value` has been used
18//! as a list, it will continue to be efficiently used as a list (until it is used something
19//! with a different data rep).
20//!
21//! # Value is not Sync!
22//!
23//! A `Value` is associated with a particular `Interp` and changes internally to optimize
24//! performance within that `Interp`. Consequently, `Values` are not `Sync`. `Values`
25//! may be used to pass values between `Interps` in the same thread (at the cost of
26//! potential shimmering), but between threads one should pass the value's string rep instead.
27//!
28//! # Comparisons
29//!
30//! If two `Value`'s are compared for equality in Rust, Rust compares their string reps;
31//! the client may also use the two `Values` as some other type before comparing them. In
32//! TCL expressions the `==` and `!=` operators compare numbers and the
33//! `eq` and `ne` operators compare string reps.
34//!
35//! # Internal Representation
36//!
37//! "Everything is a string"; thus, every `Value` has a string
38//! representation, or _string rep_. But for efficiency with numbers, lists,
39//! and user-defined binary data structures, the `Value` also caches a
40//! data representation, or _data rep_.
41//!
42//! A `Value` can have just a string rep, just a data rep, or both.
43//! Like the `Tcl_Obj` in standard TCL, the `Value` is like a two-legged stork: it
44//! can stand one leg, the other leg, or both legs.
45//!
46//! A client can ask the `Value` for its string, which is always available
47//! and will be computed from the data rep if it doesn't already exist. (Once
48//! computed, the string rep never changes.) A client can also ask
49//! the `Value` for any other type it desires. If the requested data rep
50//! is already available, it will be returned; otherwise, the `Value` will
51//! attempt to parse it from the string_rep, returning an error result on failure. The
52//! most recent data rep is cached for later.
53//!
54//! For example, consider the following sequence:
55//!
56//! * A computation yields a `Value` containing the integer 5. The data rep is
57//! a `MoltInt`, and the string rep is undefined.
58//!
59//! * The client asks for the string, and the string rep "5" is computed.
60//!
61//! * The client asks for the value's integer value. It is available and is returned.
62//!
63//! * The client asks for the value's value as a MoltList. This is possible, because
64//! the string "5" can be interpreted as a list of one element, the
65//! string "5". A new data rep is computed and saved, replacing the previous one.
66//!
67//! With this scheme, long series of computations can be carried
68//! out efficiently using only the the data rep, incurring the parsing cost at most
69//! once, while preserving TCL's "everything is a string" semantics.
70//!
71//! **Shimmering**: Converting from one data rep to another is expensive, as it involves parsing
72//! the string value. Performance can suffer if the user's code switches rapidly from one data
73//! rep to another, e.g., in a tight loop. The effect, which is known as "shimmering",
74//! can usually be avoided with a little care. Note that accessing the value's string rep
75//! doesn't cause shimmering; the string is always readily available.
76//!
77//! `Value` handles strings, integers, floating-point values, lists, and a few other things as
78//! special cases, since they are part of the language and are so frequently used.
79//! In addition, a `Value` can also contain _external types_: Rust types that implement
80//! certain traits.
81//!
82//! # External Types
83//!
84//! Any type that implements the `std::fmt::Display`, `std::fmt::Debug`,
85//! and `std::str::FromStr` traits can be saved in a `Value`. The struct's
86//! `Display` and `FromStr` trait implementations are used to convert between
87//! the string rep and data rep.
88//!
89//! * The `Display` implementation is responsible for producing the value's string rep.
90//!
91//! * The `FromStr` implementation is responsible for producing the value's data rep from
92//! a string, and so must be able to parse the `Display` implementation's
93//! output.
94//!
95//! * The string rep should be chosen so as to fit in well with TCL syntax, lest
96//! confusion, quoting hell, and comedy should ensue. (You'll know it when you
97//! see it.)
98//!
99//! ## Example
100//!
101//! For example, the following code shows how to define an external type implementing
102//! a simple enum.
103//!
104//! ```
105//! use molt::types::*;
106//! use std::fmt;
107//! use std::str::FromStr;
108//!
109//! #[derive(Debug, PartialEq, Copy, Clone)]
110//! pub enum Flavor {
111//! SALTY,
112//! SWEET,
113//! }
114//!
115//! impl fmt::Display for Flavor {
116//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117//! if *self == Flavor::SALTY {
118//! write!(f, "salty")
119//! } else {
120//! write!(f, "sweet")
121//! }
122//! }
123//! }
124//!
125//! impl FromStr for Flavor {
126//! type Err = String;
127//!
128//! fn from_str(value: &str) -> Result<Self, Self::Err> {
129//! let value = value.to_lowercase();
130//!
131//! if value == "salty" {
132//! Ok(Flavor::SALTY)
133//! } else if value == "sweet" {
134//! Ok(Flavor::SWEET)
135//! } else {
136//! // The error message doesn't matter to Molt
137//! Err("Not a flavor string".to_string())
138//! }
139//! }
140//! }
141//!
142//! impl Flavor {
143//! /// A convenience: retrieves the enumerated value, converting it from
144//! /// `Option<Flavor>` into `Result<Flavor,Exception>`.
145//! pub fn from_molt(value: &Value) -> Result<Self, Exception> {
146//! if let Some(x) = value.as_copy::<Flavor>() {
147//! Ok(x)
148//! } else {
149//! Err(Exception::molt_err(Value::from("Not a flavor string")))
150//! }
151//! }
152//! }
153//! ```
154//!
155//! # Special Implementation Types
156//!
157//! Values can also be interpreted as two special types, `Script` and `VarName`. The
158//! Interpreter uses the (non-public) `as_script` method to parse script bodies for
159//! evaluation; generally this means that a script will get parsed only once.
160//!
161//! Similarly, `as_var_name` interprets a variable name reference as a `VarName`, which
162//! contains the variable name and, optionally, an array index. This is usually hidden
163//! from the extension author by the `var` and `set_var` methods, but it is available if
164//! publically if needed.
165//!
166//! [`Value`]: struct.Value.html
167
168use crate::{
169 dict::{dict_to_string, list_to_dict},
170 expr::Datum,
171 list::{get_list, list_to_string},
172 parser::{self, Script},
173 types::{Exception, MoltDict, MoltFloat, MoltInt, MoltList, VarName},
174};
175use std::{
176 any::{Any, TypeId},
177 cell::{RefCell, UnsafeCell},
178 fmt::{Debug, Display},
179 hash::{Hash, Hasher},
180 rc::Rc,
181 str::FromStr,
182};
183
184//-----------------------------------------------------------------------------
185// Public Data Types
186
187/// The `Value` type. See [the module level documentation](index.html) for more.
188#[derive(Clone)]
189pub struct Value {
190 /// The actual data, to be shared among multiple instances of `Value`.
191 inner: Rc<InnerValue>,
192}
193
194impl Hash for Value {
195 // A Value is hashed according to its string rep; all Values with the same string rep
196 // are identical.
197 fn hash<H: Hasher>(&self, state: &mut H) {
198 self.as_str().hash(state);
199 }
200}
201
202/// The inner value of a `Value`, to be wrapped in an `Rc<T>` so that `Values` can be shared.
203#[derive(Debug)]
204struct InnerValue {
205 string_rep: UnsafeCell<Option<String>>,
206 data_rep: RefCell<DataRep>,
207}
208
209impl std::fmt::Debug for Value {
210 /// The Debug formatter for values.
211 ///
212 /// TODO: This should indicate something about the data rep as well, especially for
213 /// values in which the string rep isn't yet set.
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 write!(f, "Value[{}]", self.as_str())
216 }
217}
218
219impl Value {
220 /// Creates a value whose `InnerValue` is defined by its string rep.
221 fn inner_from_string(str: String) -> Self {
222 let inner = InnerValue {
223 string_rep: UnsafeCell::new(Some(str)),
224 data_rep: RefCell::new(DataRep::None),
225 };
226
227 Self { inner: Rc::new(inner) }
228 }
229
230 /// Creates a value whose `InnerValue` is defined by its data rep.
231 fn inner_from_data(data: DataRep) -> Self {
232 let inner = InnerValue {
233 string_rep: UnsafeCell::new(None),
234 data_rep: RefCell::new(data),
235 };
236
237 Self { inner: Rc::new(inner) }
238 }
239}
240
241impl Display for Value {
242 /// The `Display` formatter for `Value`. Outputs the value's string rep.
243 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
244 write!(f, "{}", self.as_str())
245 }
246}
247
248impl Eq for Value {}
249impl PartialEq for Value {
250 /// Two Values are equal if their string representations are equal. Application code will
251 /// often want to compare values numerically.
252 fn eq(&self, other: &Self) -> bool {
253 self.as_str() == other.as_str()
254 }
255}
256
257impl From<String> for Value {
258 /// Creates a new `Value` from the given String.
259 ///
260 /// # Example
261 ///
262 /// ```
263 /// use molt::types::Value;
264 /// let string = String::from("My New String");
265 /// let value = Value::from(string);
266 /// assert_eq!(value.as_str(), "My New String");
267 /// ```
268 fn from(str: String) -> Self {
269 Value::inner_from_string(str)
270 }
271}
272
273impl From<&str> for Value {
274 /// Creates a new `Value` from the given string slice.
275 ///
276 /// # Example
277 ///
278 /// ```
279 /// use molt::types::Value;
280 /// let value = Value::from("My String Slice");
281 /// assert_eq!(value.as_str(), "My String Slice");
282 /// ```
283 fn from(str: &str) -> Self {
284 Value::inner_from_string(str.to_string())
285 }
286}
287
288impl From<&String> for Value {
289 /// Creates a new `Value` from the given string reference.
290 ///
291 /// # Example
292 ///
293 /// ```
294 /// use molt::types::Value;
295 /// let value = Value::from("My String Slice");
296 /// assert_eq!(value.as_str(), "My String Slice");
297 /// ```
298 fn from(str: &String) -> Self {
299 Value::inner_from_string(str.to_string())
300 }
301}
302
303impl From<bool> for Value {
304 /// Creates a new `Value` whose data representation is a `bool`. The value's
305 /// string representation will be "1" or "0".
306 ///
307 /// # Example
308 ///
309 /// ```
310 /// use molt::types::Value;
311 /// let value = Value::from(true);
312 /// assert_eq!(value.as_str(), "1");
313 ///
314 /// let value = Value::from(false);
315 /// assert_eq!(value.as_str(), "0");
316 /// ```
317 fn from(flag: bool) -> Self {
318 Value::inner_from_data(DataRep::Bool(flag))
319 }
320}
321
322impl From<MoltDict> for Value {
323 /// Creates a new `Value` whose data representation is a `MoltDict`.
324 ///
325 /// # Example
326 ///
327 /// ```
328 /// use molt::types::Value;
329 /// use molt::types::MoltDict;
330 /// use molt::dict::dict_new;
331 ///
332 /// let mut dict: MoltDict = dict_new();
333 /// dict.insert(Value::from("abc"), Value::from("123"));
334 /// let value = Value::from(dict);
335 /// assert_eq!(value.as_str(), "abc 123");
336 /// ```
337 fn from(dict: MoltDict) -> Self {
338 Value::inner_from_data(DataRep::Dict(Rc::new(dict)))
339 }
340}
341
342impl From<MoltInt> for Value {
343 /// Creates a new `Value` whose data representation is a `MoltInt`.
344 ///
345 /// # Example
346 ///
347 /// ```
348 /// use molt::types::Value;
349 ///
350 /// let value = Value::from(123);
351 /// assert_eq!(value.as_str(), "123");
352 /// ```
353 fn from(int: MoltInt) -> Self {
354 Value::inner_from_data(DataRep::Int(int))
355 }
356}
357
358impl From<MoltFloat> for Value {
359 /// Creates a new `Value` whose data representation is a `MoltFloat`.
360 ///
361 /// # String Representation
362 ///
363 /// The string representation of the value will be however Rust's `format!` macro
364 /// formats floating point numbers by default. **Note**: this isn't quite what we
365 /// want; Standard TCL goes to great lengths to ensure that the formatted string
366 /// will yield exactly the same floating point number when it is parsed. Rust
367 /// will format the number `5.0` as `5`, turning it into a integer if parsed naively. So
368 /// there is more work to be done here.
369 ///
370 /// # Example
371 ///
372 /// ```
373 /// use molt::types::Value;
374 ///
375 /// let value = Value::from(12.34);
376 /// assert_eq!(value.as_str(), "12.34");
377 /// ```
378 fn from(flt: MoltFloat) -> Self {
379 Value::inner_from_data(DataRep::Flt(flt))
380 }
381}
382
383impl From<MoltList> for Value {
384 /// Creates a new `Value` whose data representation is a `MoltList`.
385 ///
386 /// # Example
387 ///
388 /// ```
389 /// use molt::types::Value;
390 ///
391 /// let list = vec![Value::from(1234), Value::from("abc")];
392 /// let value = Value::from(list);
393 /// assert_eq!(value.as_str(), "1234 abc");
394 /// ```
395 fn from(list: MoltList) -> Self {
396 Value::inner_from_data(DataRep::List(Rc::new(list)))
397 }
398}
399
400impl From<&[Value]> for Value {
401 /// Creates a new `Value` whose data representation is a `MoltList`.
402 ///
403 /// # Example
404 ///
405 /// ```
406 /// use molt::types::Value;
407 ///
408 /// let values = [Value::from(1234), Value::from("abc")];
409 /// let value = Value::from(&values[..]);
410 /// assert_eq!(value.as_str(), "1234 abc");
411 /// ```
412 fn from(list: &[Value]) -> Self {
413 Value::inner_from_data(DataRep::List(Rc::new(list.to_vec())))
414 }
415}
416
417impl Value {
418 /// Returns the empty `Value`, a value whose string representation is the empty
419 /// string.
420 ///
421 /// TODO: This should really be a constant, but there's way to build it as one
422 /// unless I use lazy_static.
423 pub fn empty() -> Value {
424 Value::inner_from_string("".into())
425 }
426
427 /// Returns the value's string representation as a reference-counted
428 /// string.
429 ///
430 /// **Note**: This is the standard way of retrieving a `Value`'s
431 /// string rep, as unlike `to_string` it doesn't create a new `String`.
432 ///
433 /// # Example
434 ///
435 /// ```
436 /// use molt::types::Value;
437 /// let value = Value::from(123);
438 /// assert_eq!(value.as_str(), "123");
439 /// ```
440 pub fn as_str(&self) -> &str {
441 // FIRST, get the string rep, computing it from the data_rep if necessary.
442 // self.inner.string_rep.get_or_init(|| (self.inner.data_rep.borrow()).to_string())
443
444 // NOTE: This method is the only place where the string_rep is queried.
445 let slot = unsafe { &*self.inner.string_rep.get() };
446
447 if let Some(inner) = slot {
448 return inner;
449 }
450
451 // NOTE: This is the only place where the string_rep is set.
452 // Because we returned it if it was Some, it is only ever set once.
453 // Thus, this is safe: as_str() is the only way to retrieve the string_rep,
454 // and it computes the string_rep lazily after which it is immutable.
455 let slot = unsafe { &mut *self.inner.string_rep.get() };
456 *slot = Some((self.inner.data_rep.borrow()).to_string());
457
458 slot.as_ref().expect("string rep")
459 }
460
461 /// Returns the value's string representation if it is already ready.
462 /// This is useful for implementing command line switches.
463 ///
464 /// # Example
465 ///
466 /// ```
467 /// use molt::types::Value;
468 /// let value = Value::from(123);
469 /// assert_eq!(value.try_as_str(), None);
470 /// assert_eq!(value.as_str(), "123");
471 /// assert_eq!(value.try_as_str(), Some("123"));
472 /// let value_str = Value::from("123");
473 /// assert_eq!(value_str.try_as_str(), Some("123"));
474 /// ```
475 pub fn try_as_str(&self) -> Option<&str> {
476 unsafe { &*self.inner.string_rep.get() }.as_ref().map(|x| x.as_ref())
477 }
478
479 /// Tries to return the `Value` as a `bool`, parsing the
480 /// value's string representation if necessary.
481 ///
482 /// # Boolean Strings
483 ///
484 /// The following are valid boolean strings, regardless of case: `true`,
485 /// `false`, `on`, `off`, `yes`, `no`, `1`, `0`. Note that other numeric strings are
486 /// _not_ valid boolean strings.
487 ///
488 /// # Numeric Values
489 ///
490 /// Non-negative numbers are interpreted as true; zero is interpreted as false.
491 ///
492 /// # Example
493 ///
494 /// ```
495 /// use molt::types::Value;
496 /// use molt::types::Exception;
497 /// # fn dummy() -> Result<bool,Exception> {
498 /// // All of the following can be interpreted as booleans.
499 /// let value = Value::from(true);
500 /// let flag = value.as_bool()?;
501 /// assert_eq!(flag, true);
502 ///
503 /// let value = Value::from("no");
504 /// let flag = value.as_bool()?;
505 /// assert_eq!(flag, false);
506 ///
507 /// let value = Value::from(5);
508 /// let flag = value.as_bool()?;
509 /// assert_eq!(flag, true);
510 ///
511 /// let value = Value::from(0);
512 /// let flag = value.as_bool()?;
513 /// assert_eq!(flag, false);
514 ///
515 /// let value = Value::from(1.1);
516 /// let flag = value.as_bool()?;
517 /// assert_eq!(flag, true);
518 ///
519 /// let value = Value::from(0.0);
520 /// let flag = value.as_bool()?;
521 /// assert_eq!(flag, false);
522 ///
523 /// // Numeric strings can not, unless evaluated as expressions.
524 /// let value = Value::from("123");
525 /// assert!(value.as_bool().is_err());
526 /// # Ok(true)
527 /// # }
528 /// ```
529 pub fn as_bool(&self) -> Result<bool, Exception> {
530 // Extra block, so that the dref is dropped before we borrow mutably.
531 {
532 let data_ref = self.inner.data_rep.borrow();
533
534 // FIRST, if we have a boolean then just return it.
535 if let DataRep::Bool(flag) = *data_ref {
536 return Ok(flag);
537 }
538
539 // NEXT, if we have a number return whether it's zero or not.
540 if let DataRep::Int(int) = *data_ref {
541 return Ok(int != 0);
542 }
543
544 if let DataRep::Flt(flt) = *data_ref {
545 return Ok(flt != 0.0);
546 }
547 }
548
549 // NEXT, Try to parse the string_rep as a boolean
550 let str = self.as_str();
551 let flag = Value::get_bool(str)?;
552 *(self.inner.data_rep.borrow_mut()) = DataRep::Bool(flag);
553 Ok(flag)
554 }
555
556 /// Converts a string argument into a boolean, returning an error on failure.
557 ///
558 /// Molt accepts the following strings as Boolean values:
559 ///
560 /// * **true**: `true`, `yes`, `on`, `1`
561 /// * **false**: `false`, `no`, `off`, `0`
562 ///
563 /// Parsing is case-insensitive, and leading and trailing whitespace are ignored.
564 ///
565 /// This method does not evaluate expressions; use `molt::expr` to evaluate boolean
566 /// expressions.
567 ///
568 /// # Example
569 ///
570 /// ```
571 /// # use molt::types::*;
572 /// # fn dummy() -> Result<bool,Exception> {
573 /// let arg = "yes";
574 /// let flag = Value::get_bool(arg)?;
575 /// assert!(flag);
576 /// # Ok(flag)
577 /// # }
578 /// ```
579 pub fn get_bool(arg: &str) -> Result<bool, Exception> {
580 let orig = arg;
581 let value: &str = &arg.trim().to_lowercase();
582 match value {
583 "1" | "true" | "yes" | "on" => Ok(true),
584 "0" | "false" | "no" | "off" => Ok(false),
585 _ => molt_err!("expected boolean but got \"{}\"", orig),
586 }
587 }
588
589 /// Tries to return the `Value` as an `Rc<MoltDict>`, parsing the
590 /// value's string representation if necessary.
591 ///
592 /// # Example
593 ///
594 /// ```
595 /// use std::rc::Rc;
596 /// use molt::types::Value;
597 /// use molt::types::MoltDict;
598 /// use molt::types::Exception;
599 /// # fn dummy() -> Result<(),Exception> {
600 ///
601 /// let value = Value::from("abc 1234");
602 /// let dict: Rc<MoltDict> = value.as_dict()?;
603 ///
604 /// assert_eq!(dict.len(), 1);
605 /// assert_eq!(dict.get(&Value::from("abc")), Some(&Value::from("1234")));
606 ///
607 /// # Ok(())
608 /// # }
609 /// ```
610 pub fn as_dict(&self) -> Result<Rc<MoltDict>, Exception> {
611 // FIRST, if we have the desired type, return it.
612 if let DataRep::Dict(dict) = &*self.inner.data_rep.borrow() {
613 return Ok(dict.clone());
614 }
615
616 // NEXT, try to parse the string_rep as a list; then turn it into a dict.
617 let str = self.as_str();
618 let list = get_list(str)?;
619
620 if list.len() % 2 != 0 {
621 return molt_err!("missing value to go with key");
622 }
623
624 let dict = Rc::new(list_to_dict(&list));
625
626 *self.inner.data_rep.borrow_mut() = DataRep::Dict(dict.clone());
627
628 Ok(dict)
629 }
630
631 /// Tries to return the `Value` as a `MoltDict`, parsing the
632 /// value's string representation if necessary.
633 ///
634 /// Use [`as_dict`](#method.as_dict) when simply referring to the dict's content;
635 /// use this method when constructing a new dict from the old one.
636 ///
637 /// # Example
638 ///
639 /// ```
640 /// use molt::types::Value;
641 /// use molt::types::MoltDict;
642 /// use molt::types::Exception;
643 /// # fn dummy() -> Result<String,Exception> {
644 ///
645 /// let value = Value::from("abc 1234");
646 /// let dict: MoltDict = value.to_dict()?;
647 /// assert_eq!(dict.len(), 2);
648 /// assert_eq!(dict.get(&Value::from("abc")), Some(&Value::from("1234")));
649 ///
650 /// # Ok("dummy".to_string())
651 /// # }
652 /// ```
653 pub fn to_dict(&self) -> Result<MoltDict, Exception> {
654 Ok((&*self.as_dict()?).to_owned())
655 }
656
657 /// Tries to return the `Value` as a `MoltInt`, parsing the
658 /// value's string representation if necessary.
659 ///
660 /// # Integer Syntax
661 ///
662 /// Molt accepts decimal integer strings, and hexadecimal integer strings
663 /// with a `0x` prefix. Strings may begin with a unary "+" or "-". Hex
664 /// digits may be in upper or lower case.
665 ///
666 /// # Example
667 ///
668 /// ```
669 /// use molt::types::Value;
670 /// use molt::types::MoltInt;
671 /// use molt::types::Exception;
672 /// # fn dummy() -> Result<MoltInt,Exception> {
673 ///
674 /// let value = Value::from(123);
675 /// let int = value.as_int()?;
676 /// assert_eq!(int, 123);
677 ///
678 /// let value = Value::from("OxFF");
679 /// let int = value.as_int()?;
680 /// assert_eq!(int, 255);
681 /// # Ok(1)
682 /// # }
683 /// ```
684 pub fn as_int(&self) -> Result<MoltInt, Exception> {
685 // FIRST, if we have an integer then just return it.
686 if let DataRep::Int(int) = *self.inner.data_rep.borrow() {
687 return Ok(int);
688 }
689
690 // NEXT, Try to parse the string_rep as an integer
691 let str = self.as_str();
692 let int = Value::get_int(str)?;
693 *self.inner.data_rep.borrow_mut() = DataRep::Int(int);
694 Ok(int)
695 }
696
697 /// Converts a string argument into a `MoltInt`, returning an error on failure.
698 ///
699 /// Molt accepts decimal integer strings, and hexadecimal integer strings
700 /// with a `0x` prefix. Strings may begin with a unary "+" or "-". Leading and
701 /// trailing whitespace is ignored.
702 ///
703 /// # Example
704 ///
705 /// ```
706 /// # use molt::types::*;
707 /// # fn dummy() -> Result<MoltInt,Exception> {
708 /// let arg = "1";
709 /// let int = Value::get_int(arg)?;
710 /// # Ok(int)
711 /// # }
712 /// ```
713 pub fn get_int(arg: &str) -> Result<MoltInt, Exception> {
714 let orig = arg;
715 let mut arg = arg.trim();
716 let mut minus = 1;
717
718 if arg.starts_with('+') {
719 arg = &arg[1..];
720 } else if arg.starts_with('-') {
721 minus = -1;
722 arg = &arg[1..];
723 }
724
725 let parse_result = if arg.starts_with("0x") {
726 MoltInt::from_str_radix(&arg[2..], 16)
727 } else {
728 arg.parse::<MoltInt>()
729 };
730
731 match parse_result {
732 Ok(int) => Ok(minus * int),
733 Err(_) => molt_err!("expected integer but got \"{}\"", orig),
734 }
735 }
736
737 /// Tries to return the `Value` as a `MoltFloat`, parsing the
738 /// value's string representation if necessary.
739 ///
740 /// # Floating-Point Syntax
741 ///
742 /// Molt accepts the same floating-point strings as Rust's standard numeric parser.
743 ///
744 /// # Example
745 ///
746 /// ```
747 /// use molt::types::Value;
748 /// use molt::types::MoltFloat;
749 /// use molt::types::Exception;
750 /// # fn dummy() -> Result<MoltFloat,Exception> {
751 ///
752 /// let value = Value::from(12.34);
753 /// let flt = value.as_float()?;
754 /// assert_eq!(flt, 12.34);
755 ///
756 /// let value = Value::from("23.45");
757 /// let flt = value.as_float()?;
758 /// assert_eq!(flt, 23.45);
759 /// # Ok(1.0)
760 /// # }
761 /// ```
762 pub fn as_float(&self) -> Result<MoltFloat, Exception> {
763 // FIRST, if we have a float then just return it.
764 if let DataRep::Flt(flt) = *self.inner.data_rep.borrow() {
765 return Ok(flt);
766 }
767
768 // NEXT, Try to parse the string_rep as a float
769 let str = self.as_str();
770 let flt = Value::get_float(str)?;
771 *self.inner.data_rep.borrow_mut() = DataRep::Flt(flt);
772 Ok(flt)
773 }
774
775 /// Converts an string argument into a `MoltFloat`, returning an error on failure.
776 ///
777 /// Molt accepts any string acceptable to `str::parse<f64>` as a valid floating
778 /// point string. Leading and trailing whitespace is ignored, and parsing is
779 /// case-insensitive.
780 ///
781 /// # Example
782 ///
783 /// ```
784 /// # use molt::types::*;
785 /// # fn dummy() -> Result<MoltFloat,Exception> {
786 /// let arg = "1e2";
787 /// let val = Value::get_float(arg)?;
788 /// # Ok(val)
789 /// # }
790 /// ```
791 pub fn get_float(arg: &str) -> Result<MoltFloat, Exception> {
792 let arg_trim = arg.trim().to_lowercase();
793
794 match arg_trim.parse::<MoltFloat>() {
795 Ok(flt) => Ok(flt),
796 Err(_) => molt_err!("expected floating-point number but got \"{}\"", arg),
797 }
798 }
799
800 /// Computes the string rep for a MoltFloat.
801 ///
802 /// TODO: This needs a lot of work, so that floating point outputs will parse back into
803 /// the same floating point numbers.
804 fn fmt_float(f: &mut std::fmt::Formatter, flt: MoltFloat) -> std::fmt::Result {
805 if flt == std::f64::INFINITY {
806 write!(f, "Inf")
807 } else if flt == std::f64::NEG_INFINITY {
808 write!(f, "-Inf")
809 } else if flt.is_nan() {
810 write!(f, "NaN")
811 } else {
812 // TODO: Needs improvement.
813 write!(f, "{}", flt)
814 }
815 }
816
817 /// Tries to return the `Value` as an `Rc<MoltList>`, parsing the
818 /// value's string representation if necessary.
819 ///
820 /// # Example
821 ///
822 /// ```
823 /// use std::rc::Rc;
824 /// use molt::types::Value;
825 /// use molt::types::MoltList;
826 /// use molt::types::Exception;
827 /// # fn dummy() -> Result<String,Exception> {
828 ///
829 /// let value = Value::from("1234 abc");
830 /// let list: Rc<MoltList> = value.as_list()?;
831 /// assert_eq!(list.len(), 2);
832 ///
833 /// assert_eq!(list[0], Value::from("1234"));
834 /// assert_eq!(list[1], Value::from("abc"));
835 ///
836 /// # Ok("dummy".to_string())
837 /// # }
838 /// ```
839 pub fn as_list(&self) -> Result<Rc<MoltList>, Exception> {
840 // FIRST, if we have the desired type, return it.
841 if let DataRep::List(list) = &*self.inner.data_rep.borrow() {
842 return Ok(list.clone());
843 }
844
845 // NEXT, try to parse the string_rep as a list.
846 let str = self.as_str();
847 let list = Rc::new(get_list(str)?);
848 *self.inner.data_rep.borrow_mut() = DataRep::List(list.clone());
849
850 Ok(list)
851 }
852
853 /// Tries to return the `Value` as a `MoltList`, parsing the
854 /// value's string representation if necessary.
855 ///
856 /// Use [`as_list`](#method.as_list) when simply referring to the list's content;
857 /// use this method when constructing a new list from the old one.
858 ///
859 /// # Example
860 ///
861 /// ```
862 /// use molt::types::Value;
863 /// use molt::types::MoltList;
864 /// use molt::types::Exception;
865 /// # fn dummy() -> Result<String,Exception> {
866 ///
867 /// let value = Value::from("1234 abc");
868 /// let list: MoltList = value.to_list()?;
869 /// assert_eq!(list.len(), 2);
870 ///
871 /// assert_eq!(list[0], Value::from("1234"));
872 /// assert_eq!(list[1], Value::from("abc"));
873 ///
874 /// # Ok("dummy".to_string())
875 /// # }
876 /// ```
877 pub fn to_list(&self) -> Result<MoltList, Exception> {
878 Ok((&*self.as_list()?).to_owned())
879 }
880
881 /// Tries to return the `Value` as an `Rc<Script>`, parsing the
882 /// value's string representation if necessary.
883 ///
884 /// For internal use only. Note: this is the normal way to convert a script string
885 /// into a Script object. Converting the Script back into a Tcl string is not
886 /// currently supported.
887 pub(crate) fn as_script(&self) -> Result<Rc<Script>, Exception> {
888 // FIRST, if we have the desired type, return it.
889 if let DataRep::Script(script) = &*self.inner.data_rep.borrow() {
890 return Ok(script.clone());
891 }
892
893 // NEXT, try to parse the string_rep as a script.
894 let str = self.as_str();
895 let script = Rc::new(parser::parse(str)?);
896 *self.inner.data_rep.borrow_mut() = DataRep::Script(script.clone());
897
898 Ok(script)
899 }
900
901 /// Returns the `Value` as an `Rc<VarName>`, parsing the
902 /// value's string representation if necessary. This type is usually hidden by the
903 /// `Interp`'s `var` and `set_var` methods, which use it implicitly; however it is
904 /// available to extension authors if need be.
905 ///
906 /// # Example
907 ///
908 /// ```
909 /// use molt::types::{Value, VarName};
910 ///
911 /// let value = Value::from("my_var");
912 /// let var_name = value.as_var_name();
913 /// assert_eq!(var_name.name(), "my_var");
914 /// assert_eq!(var_name.index(), None);
915 ///
916 /// let value = Value::from("my_array(1)");
917 /// let var_name = value.as_var_name();
918 /// assert_eq!(var_name.name(), "my_array");
919 /// assert_eq!(var_name.index(), Some("1"));
920 /// ```
921 pub fn as_var_name(&self) -> Rc<VarName> {
922 // FIRST, if we have the desired type, return it.
923 if let DataRep::VarName(var_name) = &*self.inner.data_rep.borrow() {
924 return var_name.clone();
925 }
926
927 // NEXT, try to parse the string_rep as a variable name.
928 let var_name = Rc::new(parser::parse_varname_literal(self.as_str()));
929
930 *self.inner.data_rep.borrow_mut() = DataRep::VarName(var_name.clone());
931 var_name
932 }
933
934 /// Creates a new `Value` containing the given value of some user type.
935 ///
936 /// The user type must meet certain constraints; see the
937 /// [module level documentation](index.html) for details on
938 /// how to define an external type for use with Molt.
939 ///
940 /// # Example
941 ///
942 /// Suppose we have a type `HexColor` that meets the constraints; we can create
943 /// a `Value` containing one as follows. Notice that the `Value` ownership of its input:
944 ///
945 /// ```ignore
946 /// let color: HexColor::new(0x11u8, 0x22u8, 0x33u8);
947 /// let value = Value::from_other(color);
948 ///
949 /// // Retrieve the value's string rep.
950 /// assert_eq!(value.as_str(), "#112233");
951 /// ```
952 ///
953 /// See [`Value::as_other`](#method.as_other) and
954 /// [`Value::as_copy`](#method.as_copy) for examples of how to
955 /// retrieve a `MyType` value from a `Value`.
956 pub fn from_other<T: 'static>(value: T) -> Value
957 where
958 T: Display + Debug,
959 {
960 Value::inner_from_data(DataRep::Other(Rc::new(value)))
961 }
962
963 /// Tries to interpret the `Value` as a value of external type `T`, parsing
964 /// the string representation if necessary.
965 ///
966 /// The user type must meet certain constraints; see the
967 /// [module level documentation](index.html) for details on
968 /// how to define an external type for use with Molt.
969 ///
970 /// # Return Value
971 ///
972 /// The value is returned as an `Rc<T>`, as this allows the client to
973 /// use the value freely and clone it efficiently if needed.
974 ///
975 /// This method returns `Option<Rc<T>>` rather than `Result<Rc<T>,Exception>`
976 /// because it is up to the caller to provide a meaningful error message.
977 /// It is normal for externally defined types to wrap this function in a function
978 /// that does so; see the [module level documentation](index.html) for an example.
979 ///
980 /// # Example
981 ///
982 /// Suppose we have a type `HexColor` that meets the constraints; we can create
983 /// a `Value` containing one and retrieve it as follows.
984 ///
985 /// ```ignore
986 /// // Just a normal Molt string
987 /// let value = Value::from("#112233");
988 ///
989 /// // Retrieve it as an Option<Rc<HexColor>>:
990 /// let color = value.as_other::<HexColor>()
991 ///
992 /// if color.is_some() {
993 /// let color = color.unwrap();
994 /// let r = *color.red();
995 /// let g = *color.green();
996 /// let b = *color.blue();
997 /// }
998 /// ```
999 pub fn as_other<T: 'static>(&self) -> Option<Rc<T>>
1000 where
1001 T: Display + Debug + FromStr,
1002 {
1003 // FIRST, if we have the desired type, return it.
1004 if let DataRep::Other(other) = &*self.inner.data_rep.borrow() {
1005 // other is an &Rc<MoltAny>
1006 if let Ok(out) = other.clone().downcast::<T>() {
1007 return Some(out);
1008 }
1009 }
1010
1011 // NEXT, can we parse it as a T? If so, save it back to
1012 // the data_rep, and return it.
1013 let str = self.as_str();
1014
1015 if let Ok(tval) = str.parse::<T>() {
1016 let tval = Rc::new(tval);
1017 let out = tval.clone();
1018 *self.inner.data_rep.borrow_mut() = DataRep::Other(Rc::new(tval));
1019 return Some(out);
1020 }
1021
1022 // NEXT, we couldn't do it.
1023 None
1024 }
1025
1026 /// Tries to interpret the `Value` as a value of type `T`, parsing the string
1027 /// representation if necessary, and returning a copy.
1028 ///
1029 /// The user type must meet certain constraints; and in particular it must
1030 /// implement `Copy`. See the [module level documentation](index.html) for details on
1031 /// how to define an external type for use with Molt.
1032 ///
1033 /// This method returns `Option` rather than `Result` because it is up
1034 /// to the caller to provide a meaningful error message. It is normal
1035 /// for externally defined types to wrap this function in a function
1036 /// that does so.
1037 ///
1038 /// # Example
1039 ///
1040 /// Suppose we have a type `HexColor` that meets the normal external type
1041 /// constraints and also supports copy; we can create a `Value` containing one and
1042 /// retrieve it as follows.
1043 ///
1044 /// ```ignore
1045 /// // Just a normal Molt string
1046 /// let value = Value::from("#112233");
1047 ///
1048 /// // Retrieve it as an Option<HexColor>:
1049 /// let color = value.as_copy::<HexColor>()
1050 ///
1051 /// if color.is_some() {
1052 /// let color = color.unwrap();
1053 /// let r = color.red();
1054 /// let g = color.green();
1055 /// let b = color.blue();
1056 /// }
1057 /// ```
1058 pub fn as_copy<T: 'static>(&self) -> Option<T>
1059 where
1060 T: Display + Debug + FromStr + Copy,
1061 {
1062 // FIRST, if we have the desired type, return it.
1063 if let DataRep::Other(other) = &*self.inner.data_rep.borrow() {
1064 // other is an &Rc<MoltAny>
1065 if let Ok(out) = other.clone().downcast::<T>() {
1066 return Some(*out);
1067 }
1068 }
1069
1070 // NEXT, can we parse it as a T? If so, save it back to
1071 // the data_rep, and return it.
1072 let str = self.as_str();
1073
1074 if let Ok(tval) = str.parse::<T>() {
1075 let tval = Rc::new(tval);
1076 let out = tval.clone();
1077 *self.inner.data_rep.borrow_mut() = DataRep::Other(Rc::new(tval));
1078 return Some(*out);
1079 }
1080
1081 // NEXT, we couldn't do it.
1082 None
1083 }
1084
1085 /// For use by `expr::expr` in parsing out `Values`.
1086 pub(crate) fn already_number(&self) -> Option<Datum> {
1087 let iref = self.inner.data_rep.borrow();
1088
1089 match *iref {
1090 DataRep::Flt(flt) => Some(Datum::float(flt)),
1091 DataRep::Int(int) => Some(Datum::int(int)),
1092 _ => None,
1093 }
1094 }
1095}
1096
1097//-----------------------------------------------------------------------------
1098// The MoltAny Trait: a tool for handling external types.
1099
1100/// This trait allows us to except "other" types, and still compute their
1101/// string rep on demand.
1102trait MoltAny: Any + Display + Debug {
1103 fn as_any(&self) -> &dyn Any;
1104 fn as_any_mut(&mut self) -> &mut dyn Any;
1105 fn into_any(self: Box<Self>) -> Box<dyn Any>;
1106}
1107
1108impl dyn MoltAny {
1109 /// Is this value a value of the desired type?
1110 pub fn is<T: 'static>(&self) -> bool {
1111 TypeId::of::<T>() == self.type_id()
1112 }
1113
1114 /// Downcast an `Rc<MoltAny>` to an `Rc<T>`
1115 fn downcast<T: 'static>(self: Rc<Self>) -> Result<Rc<T>, Rc<Self>> {
1116 if self.is::<T>() {
1117 unsafe { Ok(Rc::from_raw(Rc::into_raw(self) as _)) }
1118 } else {
1119 Err(self)
1120 }
1121 }
1122}
1123
1124impl<T: Any + Display + Debug> MoltAny for T {
1125 fn as_any(&self) -> &dyn Any {
1126 self
1127 }
1128 fn as_any_mut(&mut self) -> &mut dyn Any {
1129 self
1130 }
1131 fn into_any(self: Box<Self>) -> Box<dyn Any> {
1132 self
1133 }
1134}
1135
1136//-----------------------------------------------------------------------------
1137// DataRep enum: a sum type for the different kinds of data_reps.
1138
1139// The data representation for Values.
1140#[derive(Clone, Debug)]
1141enum DataRep {
1142 /// A Boolean
1143 Bool(bool),
1144
1145 /// A Molt Dictionary
1146 Dict(Rc<MoltDict>),
1147
1148 /// A Molt integer
1149 Int(MoltInt),
1150
1151 /// A Molt float
1152 Flt(MoltFloat),
1153
1154 /// A Molt List
1155 List(Rc<MoltList>),
1156
1157 /// A Script
1158 Script(Rc<Script>),
1159
1160 /// A Variable Name
1161 VarName(Rc<VarName>),
1162
1163 /// An external data type
1164 Other(Rc<dyn MoltAny>),
1165
1166 /// The Value has no data rep at present.
1167 None,
1168}
1169
1170impl Display for DataRep {
1171 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1172 match self {
1173 DataRep::Bool(flag) => write!(f, "{}", if *flag { 1 } else { 0 }),
1174 DataRep::Dict(dict) => write!(f, "{}", dict_to_string(&*dict)),
1175 DataRep::Int(int) => write!(f, "{}", int),
1176 DataRep::Flt(flt) => Value::fmt_float(f, *flt),
1177 DataRep::List(list) => write!(f, "{}", list_to_string(&*list)),
1178 DataRep::Script(script) => write!(f, "{:?}", script),
1179 DataRep::VarName(var_name) => write!(f, "{:?}", var_name),
1180 DataRep::Other(other) => write!(f, "{}", other),
1181 DataRep::None => write!(f, ""),
1182 }
1183 }
1184}
1185
1186#[cfg(test)]
1187mod tests {
1188 use super::*;
1189 use crate::dict::dict_new;
1190 use std::fmt;
1191 use std::str::FromStr;
1192
1193 #[test]
1194 fn from_string() {
1195 // Using From<String>
1196 let val = Value::from("xyz".to_string());
1197 assert_eq!(&val.to_string(), "xyz");
1198
1199 // Using Into
1200 let val: Value = String::from("Fred").into();
1201 assert_eq!(&val.to_string(), "Fred");
1202 }
1203
1204 #[test]
1205 fn from_str_ref() {
1206 // Using From<&str>
1207 let val = Value::from("xyz");
1208 assert_eq!(&val.to_string(), "xyz");
1209
1210 // Using Into
1211 let val: Value = "Fred".into();
1212 assert_eq!(&val.to_string(), "Fred");
1213 }
1214
1215 #[test]
1216 fn clone_string() {
1217 // Values with just string reps can be cloned and have equal string reps.
1218 let val = Value::from("abc");
1219 let val2 = val.clone();
1220 assert_eq!(*val.to_string(), *val2.to_string());
1221 }
1222
1223 #[test]
1224 fn as_str() {
1225 let val = Value::from("abc");
1226 assert_eq!(val.as_str(), "abc");
1227
1228 let val2 = val.clone();
1229 assert_eq!(val.as_str(), val2.as_str());
1230 }
1231
1232 #[test]
1233 fn compare() {
1234 let val = Value::from("123");
1235 let val2 = Value::from(123);
1236 let val3 = Value::from(456);
1237
1238 assert_eq!(val, val2);
1239 assert_ne!(val, val3);
1240 }
1241
1242 #[test]
1243 fn from_bool() {
1244 // Using From<bool>
1245 let val = Value::from(true);
1246 assert_eq!(&val.to_string(), "1");
1247
1248 let val = Value::from(false);
1249 assert_eq!(&val.to_string(), "0");
1250 }
1251
1252 #[test]
1253 fn as_bool() {
1254 // Can convert string to bool.
1255 let val = Value::from("true");
1256 assert_eq!(val.as_bool(), Ok(true));
1257
1258 // Non-zero numbers are true; zero is false.
1259 let val = Value::from(5);
1260 assert_eq!(val.as_bool(), Ok(true));
1261
1262 let val = Value::from(0);
1263 assert_eq!(val.as_bool(), Ok(false));
1264
1265 let val = Value::from(5.5);
1266 assert_eq!(val.as_bool(), Ok(true));
1267
1268 let val = Value::from(0.0);
1269 assert_eq!(val.as_bool(), Ok(false));
1270 }
1271
1272 #[test]
1273 fn get_bool() {
1274 // Test the underlying boolean value parser.
1275 assert_eq!(Ok(true), Value::get_bool("1"));
1276 assert_eq!(Ok(true), Value::get_bool("true"));
1277 assert_eq!(Ok(true), Value::get_bool("yes"));
1278 assert_eq!(Ok(true), Value::get_bool("on"));
1279 assert_eq!(Ok(true), Value::get_bool("TRUE"));
1280 assert_eq!(Ok(true), Value::get_bool("YES"));
1281 assert_eq!(Ok(true), Value::get_bool("ON"));
1282 assert_eq!(Ok(false), Value::get_bool("0"));
1283 assert_eq!(Ok(false), Value::get_bool("false"));
1284 assert_eq!(Ok(false), Value::get_bool("no"));
1285 assert_eq!(Ok(false), Value::get_bool("off"));
1286 assert_eq!(Ok(false), Value::get_bool("FALSE"));
1287 assert_eq!(Ok(false), Value::get_bool("NO"));
1288 assert_eq!(Ok(false), Value::get_bool("OFF"));
1289 assert_eq!(Ok(true), Value::get_bool(" true "));
1290 assert_eq!(
1291 Value::get_bool("nonesuch"),
1292 molt_err!("expected boolean but got \"nonesuch\"")
1293 );
1294 assert_eq!(
1295 Value::get_bool(" Nonesuch "),
1296 molt_err!("expected boolean but got \" Nonesuch \"")
1297 );
1298 }
1299
1300 #[test]
1301 fn from_as_dict() {
1302 // NOTE: we aren't testing dict formatting and parsing here.
1303 // We *are* testing that Value can convert dicts to and from strings.
1304 // and back again.
1305 let mut dict = dict_new();
1306 dict.insert(Value::from("abc"), Value::from("def"));
1307 let dictval = Value::from(dict);
1308 assert_eq!(dictval.as_str(), "abc def");
1309
1310 let dictval = Value::from("qrs xyz");
1311 let result = dictval.as_dict();
1312
1313 assert!(result.is_ok());
1314
1315 if let Ok(rcdict) = result {
1316 assert_eq!(rcdict.len(), 1);
1317 assert_eq!(rcdict.get(&Value::from("qrs")), Some(&Value::from("xyz")));
1318 }
1319 }
1320
1321 #[test]
1322 fn to_dict() {
1323 let dictval = Value::from("qrs xyz");
1324 let result = dictval.to_dict();
1325
1326 assert!(result.is_ok());
1327
1328 if let Ok(dict) = result {
1329 assert_eq!(dict.len(), 1);
1330 assert_eq!(dict.get(&Value::from("qrs")), Some(&Value::from("xyz")));
1331 }
1332 }
1333
1334 #[test]
1335 fn from_as_int() {
1336 let val = Value::from(5);
1337 assert_eq!(val.as_str(), "5");
1338 assert_eq!(val.as_int(), Ok(5));
1339 assert_eq!(val.as_float(), Ok(5.0));
1340
1341 let val = Value::from("7");
1342 assert_eq!(val.as_str(), "7");
1343 assert_eq!(val.as_int(), Ok(7));
1344 assert_eq!(val.as_float(), Ok(7.0));
1345
1346 // TODO: Note, 7.0 might not get converted to "7" long term.
1347 // In Standard TCL, its string_rep would be "7.0". Need to address
1348 // MoltFloat formatting/parsing.
1349 let val = Value::from(7.0);
1350 assert_eq!(val.as_str(), "7");
1351 assert_eq!(val.as_int(), Ok(7));
1352 assert_eq!(val.as_float(), Ok(7.0));
1353
1354 let val = Value::from("abc");
1355 assert_eq!(val.as_int(), molt_err!("expected integer but got \"abc\""));
1356 }
1357
1358 #[test]
1359 fn get_int() {
1360 // Test the internal integer parser
1361 assert_eq!(Value::get_int("1"), Ok(1));
1362 assert_eq!(Value::get_int("-1"), Ok(-1));
1363 assert_eq!(Value::get_int("+1"), Ok(1));
1364 assert_eq!(Value::get_int("0xFF"), Ok(255));
1365 assert_eq!(Value::get_int("+0xFF"), Ok(255));
1366 assert_eq!(Value::get_int("-0xFF"), Ok(-255));
1367 assert_eq!(Value::get_int(" 1 "), Ok(1));
1368
1369 assert_eq!(Value::get_int(""), molt_err!("expected integer but got \"\""));
1370 assert_eq!(Value::get_int("a"), molt_err!("expected integer but got \"a\""));
1371 assert_eq!(Value::get_int("0x"), molt_err!("expected integer but got \"0x\""));
1372 assert_eq!(
1373 Value::get_int("0xABGG"),
1374 molt_err!("expected integer but got \"0xABGG\"")
1375 );
1376 assert_eq!(
1377 Value::get_int(" abc "),
1378 molt_err!("expected integer but got \" abc \"")
1379 );
1380 }
1381
1382 #[test]
1383 fn from_as_float() {
1384 let val = Value::from(12.5);
1385 assert_eq!(val.as_str(), "12.5");
1386 assert_eq!(val.as_int(), molt_err!("expected integer but got \"12.5\""));
1387 assert_eq!(val.as_float(), Ok(12.5));
1388
1389 let val = Value::from("7.8");
1390 assert_eq!(val.as_str(), "7.8");
1391 assert_eq!(val.as_int(), molt_err!("expected integer but got \"7.8\""));
1392 assert_eq!(val.as_float(), Ok(7.8));
1393
1394 // TODO: Problem here: tries to mutably borrow the data_rep to convert from int to string
1395 // while the data_rep is already mutably borrowed to convert the string to float.
1396 let val = Value::from(5);
1397 assert_eq!(val.as_float(), Ok(5.0));
1398
1399 let val = Value::from("abc");
1400 assert_eq!(
1401 val.as_float(),
1402 molt_err!("expected floating-point number but got \"abc\"")
1403 );
1404 }
1405
1406 #[test]
1407 fn get_float() {
1408 // Test the internal float parser.
1409 // NOTE: At present, it relies on the standard Rust float parser, so only
1410 // check special case behavior.
1411 assert_eq!(Value::get_float("1"), Ok(1.0));
1412 assert_eq!(Value::get_float("2.3"), Ok(2.3));
1413 assert_eq!(Value::get_float(" 4.5 "), Ok(4.5));
1414 assert_eq!(Value::get_float("Inf"), Ok(std::f64::INFINITY));
1415
1416 assert_eq!(
1417 Value::get_float("abc"),
1418 molt_err!("expected floating-point number but got \"abc\"")
1419 );
1420 assert_eq!(
1421 Value::get_float(" abc "),
1422 molt_err!("expected floating-point number but got \" abc \"")
1423 );
1424 }
1425
1426 #[test]
1427 fn from_as_list() {
1428 // NOTE: we aren't testing list formatting and parsing here; that's done in list.rs.
1429 // We *are* testing that Value will use the list.rs code to convert strings to lists
1430 // and back again.
1431 let listval = Value::from(vec![Value::from("abc"), Value::from("def")]);
1432 assert_eq!(listval.as_str(), "abc def");
1433
1434 let listval = Value::from("qrs xyz");
1435 let result = listval.as_list();
1436
1437 assert!(result.is_ok());
1438
1439 if let Ok(rclist) = result {
1440 assert_eq!(rclist.len(), 2);
1441 assert_eq!(rclist[0].to_string(), "qrs".to_string());
1442 assert_eq!(rclist[1].to_string(), "xyz".to_string());
1443 }
1444 }
1445
1446 #[test]
1447 fn to_list() {
1448 let listval = Value::from(vec![Value::from("abc"), Value::from("def")]);
1449 let result = listval.to_list();
1450
1451 assert!(result.is_ok());
1452 let list: MoltList = result.expect("an owned list");
1453
1454 assert_eq!(list.len(), 2);
1455 assert_eq!(list[0].to_string(), "abc".to_string());
1456 assert_eq!(list[1].to_string(), "def".to_string());
1457 }
1458
1459 #[test]
1460 fn as_script() {
1461 let val = Value::from("a");
1462 assert!(val.as_script().is_ok());
1463
1464 let val = Value::from("a {b");
1465 assert_eq!(val.as_script(), molt_err_uncompleted!("missing close-brace"));
1466 }
1467
1468 #[test]
1469 fn as_var_name() {
1470 let val = Value::from("a");
1471 assert_eq!(val.as_var_name().name(), "a");
1472 assert_eq!(val.as_var_name().index(), None);
1473
1474 let val = Value::from("a(b)");
1475 assert_eq!(val.as_var_name().name(), "a");
1476 assert_eq!(val.as_var_name().index(), Some("b"));
1477 }
1478
1479 #[test]
1480 fn from_value_slice() {
1481 // NOTE: we aren't testing list formatting and parsing here; that's done in list.rs.
1482 // We *are* testing that Value will use the list.rs code to convert strings to lists
1483 // and back again.
1484 let array = [Value::from("abc"), Value::from("def")];
1485 let listval = Value::from(&array[..]);
1486 assert_eq!(listval.as_str(), "abc def");
1487 }
1488
1489 #[test]
1490 fn from_to_flavor() {
1491 // Give a Flavor, get an Rc<Flavor> back.
1492 let myval = Value::from_other(Flavor::SALTY);
1493 let result = myval.as_other::<Flavor>();
1494 assert!(result.is_some());
1495 let out = result.unwrap();
1496 assert_eq!(*out, Flavor::SALTY);
1497
1498 // Give a String, get an Rc<Flavor> back.
1499 let myval = Value::from("sweet");
1500 let result = myval.as_other::<Flavor>();
1501 assert!(result.is_some());
1502 let out = result.unwrap();
1503 assert_eq!(*out, Flavor::SWEET);
1504
1505 // Flavor is Copy, so get a Flavor back
1506 let myval = Value::from_other(Flavor::SALTY);
1507 let result = myval.as_copy::<Flavor>();
1508 assert!(result.is_some());
1509 let out = result.unwrap();
1510 assert_eq!(out, Flavor::SALTY);
1511 }
1512
1513 #[test]
1514 fn already_number() {
1515 // Can retrieve a DataRep::Int as a Datum::Int.
1516 let value = Value::from(123);
1517 let out = value.already_number();
1518 assert!(out.is_some());
1519 assert_eq!(out.unwrap(), Datum::int(123));
1520
1521 // Can retrieve a DataRep::Flt as a Datum::Flt.
1522 let value = Value::from(45.6);
1523 let out = value.already_number();
1524 assert!(out.is_some());
1525 assert_eq!(out.unwrap(), Datum::float(45.6));
1526
1527 // Other values, None.
1528 let value = Value::from("123");
1529 assert!(value.already_number().is_none());
1530 }
1531
1532 // Sample external type, used for testing.
1533
1534 #[derive(Debug, PartialEq, Copy, Clone)]
1535 pub enum Flavor {
1536 SALTY,
1537 SWEET,
1538 }
1539
1540 impl FromStr for Flavor {
1541 type Err = String;
1542
1543 fn from_str(value: &str) -> Result<Self, Self::Err> {
1544 let value = value.to_lowercase();
1545
1546 if value == "salty" {
1547 Ok(Flavor::SALTY)
1548 } else if value == "sweet" {
1549 Ok(Flavor::SWEET)
1550 } else {
1551 Err("Not a flavor string".to_string())
1552 }
1553 }
1554 }
1555
1556 impl fmt::Display for Flavor {
1557 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1558 if *self == Flavor::SALTY {
1559 write!(f, "salty")
1560 } else {
1561 write!(f, "sweet")
1562 }
1563 }
1564 }
1565}