1use golem_wasm_ast::analysis::AnalysedType;
16use golem_wasm_rpc::{IntoValueAndType, Value, ValueAndType};
17use std::cmp::Ordering;
18use std::fmt::Display;
19use std::ops::{Add, Div, Mul, Sub};
20
21pub trait GetLiteralValue {
22 fn get_literal(&self) -> Option<LiteralValue>;
23}
24
25impl GetLiteralValue for ValueAndType {
26 fn get_literal(&self) -> Option<LiteralValue> {
27 match self {
28 ValueAndType {
29 value: Value::String(value),
30 ..
31 } => Some(LiteralValue::String(value.clone())),
32 ValueAndType {
33 value: Value::Char(code_point),
34 ..
35 } => char::from_u32(*code_point as u32)
36 .map(|c| c.to_string())
37 .map(LiteralValue::String),
38 ValueAndType {
39 value: Value::Bool(value),
40 ..
41 } => Some(LiteralValue::Bool(*value)),
42 ValueAndType {
43 value: Value::Enum(idx),
44 typ: AnalysedType::Enum(typ),
45 } => {
46 Some(LiteralValue::String(typ.cases[*idx as usize].clone()))
48 }
49 ValueAndType {
50 value:
51 Value::Variant {
52 case_idx,
53 case_value,
54 },
55 typ: AnalysedType::Variant(typ),
56 } => {
57 if case_value.is_none() {
59 Some(LiteralValue::String(
60 typ.cases[*case_idx as usize].name.clone(),
61 ))
62 } else {
63 None
64 }
65 }
66 other => internal::get_numeric_value(other).map(LiteralValue::Num),
67 }
68 }
69}
70
71#[derive(Clone, Debug, PartialEq, PartialOrd)]
72pub enum LiteralValue {
73 Num(CoercedNumericValue),
74 String(String),
75 Bool(bool),
76}
77
78impl LiteralValue {
79 pub fn get_bool(&self) -> Option<bool> {
80 match self {
81 LiteralValue::Bool(value) => Some(*value),
82 _ => None,
83 }
84 }
85
86 pub fn get_number(&self) -> Option<CoercedNumericValue> {
87 match self {
88 LiteralValue::Num(num) => Some(num.clone()),
89 _ => None,
90 }
91 }
92
93 pub fn as_string(&self) -> String {
94 match self {
95 LiteralValue::Num(number) => number.to_string(),
96 LiteralValue::String(value) => value.clone(),
97 LiteralValue::Bool(value) => value.to_string(),
98 }
99 }
100}
101
102impl From<String> for LiteralValue {
103 fn from(value: String) -> Self {
104 if let Ok(u64) = value.parse::<u64>() {
105 LiteralValue::Num(CoercedNumericValue::PosInt(u64))
106 } else if let Ok(i64_value) = value.parse::<i64>() {
107 LiteralValue::Num(CoercedNumericValue::NegInt(i64_value))
108 } else if let Ok(f64_value) = value.parse::<f64>() {
109 LiteralValue::Num(CoercedNumericValue::Float(f64_value))
110 } else if let Ok(bool) = value.parse::<bool>() {
111 LiteralValue::Bool(bool)
112 } else {
113 LiteralValue::String(value.to_string())
114 }
115 }
116}
117
118#[derive(Clone, Debug)]
120pub enum CoercedNumericValue {
121 PosInt(u64),
122 NegInt(i64),
123 Float(f64),
124}
125
126impl CoercedNumericValue {
127 pub fn cast_to(&self, analysed_type: &AnalysedType) -> Option<ValueAndType> {
128 match (self, analysed_type) {
129 (CoercedNumericValue::PosInt(val), AnalysedType::U8(_)) if *val <= u8::MAX as u64 => {
130 Some((*val as u8).into_value_and_type())
131 }
132 (CoercedNumericValue::PosInt(val), AnalysedType::U16(_)) if *val <= u16::MAX as u64 => {
133 Some((*val as u16).into_value_and_type())
134 }
135 (CoercedNumericValue::PosInt(val), AnalysedType::U32(_)) if *val <= u32::MAX as u64 => {
136 Some((*val as u32).into_value_and_type())
137 }
138 (CoercedNumericValue::PosInt(val), AnalysedType::U64(_)) => {
139 Some((*val).into_value_and_type())
140 }
141
142 (CoercedNumericValue::NegInt(val), AnalysedType::S8(_))
143 if *val >= i8::MIN as i64 && *val <= i8::MAX as i64 =>
144 {
145 Some((*val as i8).into_value_and_type())
146 }
147 (CoercedNumericValue::NegInt(val), AnalysedType::S16(_))
148 if *val >= i16::MIN as i64 && *val <= i16::MAX as i64 =>
149 {
150 Some((*val as i16).into_value_and_type())
151 }
152 (CoercedNumericValue::NegInt(val), AnalysedType::S32(_))
153 if *val >= i32::MIN as i64 && *val <= i32::MAX as i64 =>
154 {
155 Some((*val as i32).into_value_and_type())
156 }
157 (CoercedNumericValue::NegInt(val), AnalysedType::S64(_)) => {
158 Some((*val).into_value_and_type())
159 }
160
161 (CoercedNumericValue::Float(val), AnalysedType::F64(_)) => {
162 Some((*val).into_value_and_type())
163 }
164 (CoercedNumericValue::Float(val), AnalysedType::F32(_))
165 if *val >= f32::MIN as f64 && *val <= f32::MAX as f64 =>
166 {
167 Some((*val as f32).into_value_and_type())
168 }
169
170 _ => None,
171 }
172 }
173}
174
175macro_rules! impl_ops {
176 ($trait:ident, $method:ident) => {
177 impl $trait for CoercedNumericValue {
178 type Output = Self;
179
180 fn $method(self, rhs: Self) -> Self::Output {
181 match (self, rhs) {
182 (CoercedNumericValue::Float(a), CoercedNumericValue::Float(b)) => {
183 CoercedNumericValue::Float(a.$method(b))
184 }
185 (CoercedNumericValue::Float(a), CoercedNumericValue::PosInt(b)) => {
186 CoercedNumericValue::Float(a.$method(b as f64))
187 }
188 (CoercedNumericValue::Float(a), CoercedNumericValue::NegInt(b)) => {
189 CoercedNumericValue::Float(a.$method(b as f64))
190 }
191 (CoercedNumericValue::PosInt(a), CoercedNumericValue::Float(b)) => {
192 CoercedNumericValue::Float((a as f64).$method(b))
193 }
194 (CoercedNumericValue::NegInt(a), CoercedNumericValue::Float(b)) => {
195 CoercedNumericValue::Float((a as f64).$method(b))
196 }
197 (CoercedNumericValue::PosInt(a), CoercedNumericValue::PosInt(b)) => {
198 CoercedNumericValue::PosInt(a.$method(b))
199 }
200 (CoercedNumericValue::NegInt(a), CoercedNumericValue::NegInt(b)) => {
201 CoercedNumericValue::NegInt(a.$method(b))
202 }
203 (CoercedNumericValue::PosInt(a), CoercedNumericValue::NegInt(b)) => {
204 CoercedNumericValue::NegInt((a as i64).$method(b))
205 }
206 (CoercedNumericValue::NegInt(a), CoercedNumericValue::PosInt(b)) => {
207 CoercedNumericValue::NegInt(a.$method(b as i64))
208 }
209 }
210 }
211 }
212 };
213}
214
215impl_ops!(Add, add);
216impl_ops!(Sub, sub);
217impl_ops!(Mul, mul);
218impl_ops!(Div, div);
219
220impl PartialOrd for CoercedNumericValue {
223 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
224 use CoercedNumericValue::*;
225 match (self, other) {
226 (PosInt(a), PosInt(b)) => a.partial_cmp(b),
227 (NegInt(a), NegInt(b)) => a.partial_cmp(b),
228 (Float(a), Float(b)) => a.partial_cmp(b),
229
230 (PosInt(a), NegInt(b)) => {
231 if let Ok(b_as_u64) = u64::try_from(*b) {
232 a.partial_cmp(&b_as_u64)
233 } else {
234 Some(Ordering::Greater) }
236 }
237
238 (NegInt(a), PosInt(b)) => {
239 if let Ok(a_as_u64) = u64::try_from(*a) {
240 a_as_u64.partial_cmp(b)
241 } else {
242 Some(Ordering::Less) }
244 }
245
246 (PosInt(a), Float(b)) => (*a as f64).partial_cmp(b),
247
248 (Float(a), PosInt(b)) => a.partial_cmp(&(*b as f64)),
249
250 (NegInt(a), Float(b)) => (*a as f64).partial_cmp(b),
251
252 (Float(a), NegInt(b)) => a.partial_cmp(&(*b as f64)),
253 }
254 }
255}
256
257impl PartialEq for CoercedNumericValue {
261 fn eq(&self, other: &Self) -> bool {
262 use CoercedNumericValue::*;
263 match (self, other) {
264 (PosInt(a), PosInt(b)) => a == b,
265 (NegInt(a), NegInt(b)) => a == b,
266 (Float(a), Float(b)) => a == b,
267
268 (PosInt(a), NegInt(b)) => {
270 if let Ok(b_as_u64) = u64::try_from(*b) {
271 a == &b_as_u64
272 } else {
273 false
274 }
275 }
276
277 (NegInt(a), PosInt(b)) => {
279 if let Ok(a_as_u64) = u64::try_from(*a) {
280 &a_as_u64 == b
281 } else {
282 false
283 }
284 }
285
286 (PosInt(a), Float(b)) => (*a as f64) == *b,
288
289 (Float(a), PosInt(b)) => *a == (*b as f64),
291
292 (NegInt(a), Float(b)) => (*a as f64) == *b,
294
295 (Float(a), NegInt(b)) => *a == (*b as f64),
297 }
298 }
299}
300
301impl Display for CoercedNumericValue {
302 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
303 match self {
304 CoercedNumericValue::PosInt(value) => write!(f, "{}", value),
305 CoercedNumericValue::NegInt(value) => write!(f, "{}", value),
306 CoercedNumericValue::Float(value) => write!(f, "{}", value),
307 }
308 }
309}
310
311impl Display for LiteralValue {
312 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
313 match self {
314 LiteralValue::Num(number) => write!(f, "{}", number),
315 LiteralValue::String(value) => write!(f, "{}", value),
316 LiteralValue::Bool(value) => write!(f, "{}", value),
317 }
318 }
319}
320
321mod internal {
322 use crate::interpreter::literal::CoercedNumericValue;
323 use golem_wasm_rpc::{Value, ValueAndType};
324
325 pub(crate) fn get_numeric_value(value_and_type: &ValueAndType) -> Option<CoercedNumericValue> {
326 match &value_and_type.value {
327 Value::S8(value) => Some(CoercedNumericValue::NegInt(*value as i64)),
328 Value::S16(value) => Some(CoercedNumericValue::NegInt(*value as i64)),
329 Value::S32(value) => Some(CoercedNumericValue::NegInt(*value as i64)),
330 Value::S64(value) => Some(CoercedNumericValue::NegInt(*value)),
331 Value::U8(value) => Some(CoercedNumericValue::PosInt(*value as u64)),
332 Value::U16(value) => Some(CoercedNumericValue::PosInt(*value as u64)),
333 Value::U32(value) => Some(CoercedNumericValue::PosInt(*value as u64)),
334 Value::U64(value) => Some(CoercedNumericValue::PosInt(*value)),
335 Value::F32(value) => Some(CoercedNumericValue::Float(*value as f64)),
336 Value::F64(value) => Some(CoercedNumericValue::Float(*value)),
337 _ => None,
338 }
339 }
340}