105 lines
3.7 KiB
Rust
105 lines
3.7 KiB
Rust
//! JS's rules on values, as C: the conversions and tests that depend only on a value's member
|
|
|
|
use super::types::{CType, Member};
|
|
|
|
/// The literal of a member that has one value
|
|
pub fn unit_literal(member: Member) -> &'static str {
|
|
match member {
|
|
Member::Undefined | Member::Native(_) => "(undefined_t){0}",
|
|
Member::Null => "(null_t){0}",
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
/// The undefined value, and its C type
|
|
pub fn undefined() -> (String, CType) {
|
|
(
|
|
unit_literal(Member::Undefined).into(),
|
|
CType::One(Member::Undefined),
|
|
)
|
|
}
|
|
|
|
/// The C literal of an int
|
|
pub fn int(n: i64) -> String {
|
|
match n == i32::MIN as i64 {
|
|
true => "INT32_MIN".into(),
|
|
false => n.to_string(),
|
|
}
|
|
}
|
|
|
|
/// `expr`, a value of `member`, as a number (a `double`), as JS's ToNumber does. `None` for an
|
|
/// object: its conversion calls its methods, which is not supported
|
|
pub fn to_number(expr: &str, member: Member) -> Option<String> {
|
|
Some(match member {
|
|
Member::Undefined => "NAN".into(),
|
|
Member::Null => "0.0".into(),
|
|
Member::Bool | Member::Int => format!("(double){expr}"),
|
|
Member::Float => expr.into(),
|
|
Member::Str => format!("__rt_string_to_number({expr})"),
|
|
Member::Obj(_) | Member::Native(_) => return None,
|
|
})
|
|
}
|
|
|
|
/// `expr`, a value of `member`, as an int32, as JS's ToInt32 does. `None` for an object
|
|
pub fn to_int32(expr: &str, member: Member) -> Option<String> {
|
|
match member {
|
|
Member::Int => Some(expr.into()),
|
|
_ => Some(format!("__rt_to_int32({})", to_number(expr, member)?)),
|
|
}
|
|
}
|
|
|
|
/// Whether `expr`, a value of `member`, is truthy
|
|
pub fn truthy(expr: &str, member: Member) -> String {
|
|
match member {
|
|
Member::Undefined | Member::Null => "false".into(),
|
|
Member::Bool => expr.into(),
|
|
Member::Int => format!("({expr} != 0)"),
|
|
Member::Float => format!("({expr} == {expr} && {expr} != 0)"),
|
|
Member::Str => format!("(__rt_string_length({expr}) != 0)"),
|
|
Member::Obj(_) | Member::Native(_) => "true".into(),
|
|
}
|
|
}
|
|
|
|
/// A JS base type: the kinds of value that `===` tells apart
|
|
pub fn base(member: Member) -> u8 {
|
|
match member {
|
|
Member::Undefined => 0,
|
|
Member::Null => 1,
|
|
Member::Bool => 2,
|
|
Member::Int | Member::Float => 3,
|
|
Member::Str => 4,
|
|
Member::Obj(_) | Member::Native(_) => 5,
|
|
}
|
|
}
|
|
|
|
/// `a === b`, for one member of each. Different kinds are never equal. Numbers compare as
|
|
/// numbers, strings by their text, and objects by identity
|
|
pub fn strict_eq(l: &str, lm: Member, r: &str, rm: Member) -> String {
|
|
match (lm, rm) {
|
|
_ if base(lm) != base(rm) => "false".into(),
|
|
(Member::Undefined | Member::Null, _) => "true".into(),
|
|
(Member::Str, _) => format!("__rt_string_eq({l}, {r})"),
|
|
(Member::Obj(_), Member::Obj(_)) => format!("((void *){l} == (void *){r})"),
|
|
(Member::Native(a), Member::Native(b)) => (a == b).to_string(),
|
|
(Member::Obj(_) | Member::Native(_), _) => "false".into(),
|
|
_ => format!("({l} == {r})"),
|
|
}
|
|
}
|
|
|
|
/// An index of member `member` as an `int32_t`
|
|
pub fn as_index(index: &str, member: Member) -> String {
|
|
match member {
|
|
Member::Int => index.into(),
|
|
Member::Float => format!("(int32_t){index}"),
|
|
_ => unreachable!("an index is a number (see infer)"),
|
|
}
|
|
}
|
|
|
|
/// Whether an index is an array index below `length`. A float must be a whole number
|
|
pub fn in_range(index: &str, member: Member, length: &str) -> String {
|
|
match member {
|
|
Member::Int => format!("{index} >= 0 && {index} < {length}"),
|
|
_ => format!("{index} >= 0 && {index} < {length} && {index} == (double)(int32_t){index}"),
|
|
}
|
|
}
|