1use crate::args::{ArgSchema, CoercionPolicy, ShapeKind};
2use crate::function::{FnCaps, Function};
3use crate::traits::{ArgumentHandle, FunctionContext};
4use formualizer_common::{ArgKind, ExcelError, ExcelErrorKind, LiteralValue};
5use formualizer_parse::parser::ReferenceType;
6
7fn number_strict_scalar() -> ArgSchema {
8 ArgSchema {
9 kinds: smallvec::smallvec![ArgKind::Number],
10 required: true,
11 by_ref: false,
12 shape: ShapeKind::Scalar,
13 coercion: CoercionPolicy::NumberStrict,
14 max: None,
15 repeating: None,
16 default: None,
17 }
18}
19
20fn arg_byref_array() -> Vec<ArgSchema> {
21 vec![
22 ArgSchema {
24 kinds: smallvec::smallvec![ArgKind::Any],
25 required: true,
26 by_ref: false,
27 shape: ShapeKind::Range,
28 coercion: CoercionPolicy::None,
29 max: None,
30 repeating: None,
31 default: None,
32 },
33 number_strict_scalar(),
34 ArgSchema {
36 kinds: smallvec::smallvec![ArgKind::Number],
37 required: false,
38 by_ref: false,
39 shape: ShapeKind::Scalar,
40 coercion: CoercionPolicy::NumberStrict,
41 max: None,
42 repeating: None,
43 default: None,
44 },
45 ]
46}
47
48fn arg_byref_reference() -> Vec<ArgSchema> {
49 vec![
50 ArgSchema {
51 kinds: smallvec::smallvec![ArgKind::Range],
52 required: true,
53 by_ref: true,
54 shape: ShapeKind::Range,
55 coercion: CoercionPolicy::None,
56 max: None,
57 repeating: None,
58 default: None,
59 },
60 number_strict_scalar(),
61 number_strict_scalar(),
62 ArgSchema {
63 kinds: smallvec::smallvec![ArgKind::Number],
65 required: false,
66 by_ref: false,
67 shape: ShapeKind::Scalar,
68 coercion: CoercionPolicy::NumberStrict,
69 max: None,
70 repeating: None,
71 default: None,
72 },
73 ArgSchema {
74 kinds: smallvec::smallvec![ArgKind::Number],
76 required: false,
77 by_ref: false,
78 shape: ShapeKind::Scalar,
79 coercion: CoercionPolicy::NumberStrict,
80 max: None,
81 repeating: None,
82 default: None,
83 },
84 ]
85}
86
87#[derive(Debug)]
88pub struct IndexFn;
89impl Function for IndexFn {
90 fn caps(&self) -> FnCaps {
91 FnCaps::PURE | FnCaps::RETURNS_REFERENCE
92 }
93 fn name(&self) -> &'static str {
94 "INDEX"
95 }
96 fn min_args(&self) -> usize {
97 2
98 }
99 fn arg_schema(&self) -> &'static [ArgSchema] {
100 use once_cell::sync::Lazy;
101 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(arg_byref_array);
102 &SCHEMA
103 }
104
105 fn eval_reference<'a, 'b, 'c>(
106 &self,
107 args: &'c [ArgumentHandle<'a, 'b>],
108 _ctx: &dyn FunctionContext<'b>,
109 ) -> Option<Result<ReferenceType, ExcelError>> {
110 if args.len() < 2 {
112 return Some(Err(ExcelError::new(ExcelErrorKind::Value)));
113 }
114 let base = match args[0].as_reference_or_eval() {
116 Ok(r) => r,
117 Err(_) => return None,
118 };
119 let row = match args[1].value() {
120 Ok(cv) => match cv.into_literal() {
121 LiteralValue::Number(n) => n as i64,
122 LiteralValue::Int(i) => i,
123 _ => return Some(Err(ExcelError::new(ExcelErrorKind::Value))),
124 },
125 Err(e) => return Some(Err(e)),
126 };
127 let col = if args.len() >= 3 {
128 match args[2].value() {
129 Ok(cv) => match cv.into_literal() {
130 LiteralValue::Number(n) => n as i64,
131 LiteralValue::Int(i) => i,
132 _ => return Some(Err(ExcelError::new(ExcelErrorKind::Value))),
133 },
134 Err(e) => return Some(Err(e)),
135 }
136 } else {
137 1
139 };
140
141 let (sheet, sr, sc, er, ec) = match base {
143 ReferenceType::Range {
144 sheet,
145 start_row,
146 start_col,
147 end_row,
148 end_col,
149 ..
150 } => match (start_row, start_col, end_row, end_col) {
151 (Some(sr), Some(sc), Some(er), Some(ec)) => (sheet, sr, sc, er, ec),
152 _ => return Some(Err(ExcelError::new(ExcelErrorKind::Ref))),
153 },
154 ReferenceType::Cell {
155 sheet, row, col, ..
156 } => (sheet, row, col, row, col),
157 _ => return Some(Err(ExcelError::new(ExcelErrorKind::Ref))),
158 };
159
160 if row <= 0 || col <= 0 {
162 return Some(Err(ExcelError::new(ExcelErrorKind::Ref)));
163 }
164 let r = sr + (row as u32) - 1;
165 let c = sc + (col as u32) - 1;
166 if r > er || c > ec {
167 return Some(Err(ExcelError::new(ExcelErrorKind::Ref)));
168 }
169
170 Some(Ok(ReferenceType::cell(sheet, r, c)))
171 }
172
173 fn eval<'a, 'b, 'c>(
174 &self,
175 args: &'c [ArgumentHandle<'a, 'b>],
176 ctx: &dyn FunctionContext<'b>,
177 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
178 if let Some(result) = self.eval_reference(args, ctx) {
180 match result {
181 Ok(r) => {
182 let current_sheet = ctx.current_sheet();
184 match ctx.resolve_range_view(&r, current_sheet) {
185 Ok(rv) => {
186 let (rows, cols) = rv.dims();
187 if rows == 1 && cols == 1 {
188 Ok(crate::traits::CalcValue::Scalar(
189 rv.as_1x1().unwrap_or(LiteralValue::Empty),
190 ))
191 } else {
192 Ok(crate::traits::CalcValue::Range(rv))
193 }
194 }
195 Err(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
196 }
197 }
198 Err(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
199 }
200 } else {
201 if args.len() < 2 {
203 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
204 ExcelError::new(ExcelErrorKind::Value),
205 )));
206 }
207 let v = args[0].value()?.into_literal();
208 let table: Vec<Vec<LiteralValue>> = match v {
209 LiteralValue::Array(rows) => rows,
210 other => vec![vec![other]],
211 };
212 let index = match args[1].value()?.into_literal() {
213 LiteralValue::Number(n) => n as i64,
214 LiteralValue::Int(i) => i,
215 _ => {
216 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
217 ExcelError::new(ExcelErrorKind::Value),
218 )));
219 }
220 };
221
222 let is_single_row = table.len() == 1;
224 let is_single_col = table.iter().all(|r| r.len() == 1);
225
226 if args.len() == 2 && (is_single_row || is_single_col) {
228 if index <= 0 {
230 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
231 ExcelError::new(ExcelErrorKind::Ref),
232 )));
233 }
234 let idx = (index - 1) as usize;
235 let val = if is_single_row {
236 table[0].get(idx).cloned()
237 } else {
238 table.get(idx).and_then(|r| r.first()).cloned()
239 };
240 return Ok(crate::traits::CalcValue::Scalar(val.unwrap_or_else(|| {
241 LiteralValue::Error(ExcelError::new(ExcelErrorKind::Ref))
242 })));
243 }
244
245 let row = index as usize;
247 let col = if args.len() >= 3 {
248 match args[2].value()?.into_literal() {
249 LiteralValue::Number(n) => n as usize,
250 LiteralValue::Int(i) => i as usize,
251 _ => {
252 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
253 ExcelError::new(ExcelErrorKind::Value),
254 )));
255 }
256 }
257 } else {
258 1
259 };
260
261 if row == 0 || col == 0 {
263 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
264 ExcelError::new(ExcelErrorKind::Ref),
265 )));
266 }
267 let val = table
268 .get(row - 1)
269 .and_then(|r| r.get(col - 1))
270 .cloned()
271 .unwrap_or_else(|| LiteralValue::Error(ExcelError::new(ExcelErrorKind::Ref)));
272 Ok(crate::traits::CalcValue::Scalar(val))
273 }
274 }
275}
276
277#[derive(Debug)]
278pub struct OffsetFn;
279impl Function for OffsetFn {
280 fn caps(&self) -> FnCaps {
281 FnCaps::PURE | FnCaps::RETURNS_REFERENCE | FnCaps::VOLATILE | FnCaps::DYNAMIC_DEPENDENCY
283 }
284 fn name(&self) -> &'static str {
285 "OFFSET"
286 }
287 fn min_args(&self) -> usize {
288 3
289 }
290 fn arg_schema(&self) -> &'static [ArgSchema] {
291 use once_cell::sync::Lazy;
292 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(arg_byref_reference);
293 &SCHEMA
294 }
295
296 fn eval_reference<'a, 'b, 'c>(
297 &self,
298 args: &'c [ArgumentHandle<'a, 'b>],
299 _ctx: &dyn FunctionContext<'b>,
300 ) -> Option<Result<ReferenceType, ExcelError>> {
301 if args.len() < 3 {
302 return Some(Err(ExcelError::new(ExcelErrorKind::Value)));
303 }
304 let base = match args[0].as_reference_or_eval() {
305 Ok(r) => r,
306 Err(e) => return Some(Err(e)),
307 };
308 let dr = match args[1].value() {
309 Ok(cv) => match cv.into_literal() {
310 LiteralValue::Number(n) => n as i64,
311 LiteralValue::Int(i) => i,
312 _ => return Some(Err(ExcelError::new(ExcelErrorKind::Value))),
313 },
314 Err(e) => return Some(Err(e)),
315 };
316 let dc = match args[2].value() {
317 Ok(cv) => match cv.into_literal() {
318 LiteralValue::Number(n) => n as i64,
319 LiteralValue::Int(i) => i,
320 _ => return Some(Err(ExcelError::new(ExcelErrorKind::Value))),
321 },
322 Err(e) => return Some(Err(e)),
323 };
324
325 let (sheet, sr, sc, er, ec) = match base {
326 ReferenceType::Range {
327 sheet,
328 start_row,
329 start_col,
330 end_row,
331 end_col,
332 ..
333 } => match (start_row, start_col, end_row, end_col) {
334 (Some(sr), Some(sc), Some(er), Some(ec)) => (sheet, sr, sc, er, ec),
335 _ => return Some(Err(ExcelError::new(ExcelErrorKind::Ref))),
336 },
337 ReferenceType::Cell {
338 sheet, row, col, ..
339 } => (sheet, row, col, row, col),
340 _ => return Some(Err(ExcelError::new(ExcelErrorKind::Ref))),
341 };
342
343 let nsr = (sr as i64) + dr;
344 let nsc = (sc as i64) + dc;
345 let height = if args.len() >= 4 {
346 match args[3].value() {
347 Ok(cv) => match cv.into_literal() {
348 LiteralValue::Number(n) => n as i64,
349 LiteralValue::Int(i) => i,
350 _ => return Some(Err(ExcelError::new(ExcelErrorKind::Value))),
351 },
352 Err(e) => return Some(Err(e)),
353 }
354 } else {
355 (er as i64) - (sr as i64) + 1
356 };
357 let width = if args.len() >= 5 {
358 match args[4].value() {
359 Ok(cv) => match cv.into_literal() {
360 LiteralValue::Number(n) => n as i64,
361 LiteralValue::Int(i) => i,
362 _ => return Some(Err(ExcelError::new(ExcelErrorKind::Value))),
363 },
364 Err(e) => return Some(Err(e)),
365 }
366 } else {
367 (ec as i64) - (sc as i64) + 1
368 };
369
370 if nsr <= 0 || nsc <= 0 || height <= 0 || width <= 0 {
371 return Some(Err(ExcelError::new(ExcelErrorKind::Ref)));
372 }
373 let ner = nsr + height - 1;
374 let nec = nsc + width - 1;
375
376 if height == 1 && width == 1 {
377 Some(Ok(ReferenceType::cell(sheet, nsr as u32, nsc as u32)))
378 } else {
379 Some(Ok(ReferenceType::range(
380 sheet,
381 Some(nsr as u32),
382 Some(nsc as u32),
383 Some(ner as u32),
384 Some(nec as u32),
385 )))
386 }
387 }
388
389 fn eval<'a, 'b, 'c>(
390 &self,
391 args: &'c [ArgumentHandle<'a, 'b>],
392 ctx: &dyn FunctionContext<'b>,
393 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
394 if let Some(Ok(r)) = self.eval_reference(args, ctx) {
395 let current_sheet = ctx.current_sheet();
396 match ctx.resolve_range_view(&r, current_sheet) {
397 Ok(rv) => {
398 let (rows, cols) = rv.dims();
399 if rows == 1 && cols == 1 {
400 Ok(crate::traits::CalcValue::Scalar(
401 rv.as_1x1().unwrap_or(LiteralValue::Empty),
402 ))
403 } else {
404 Ok(crate::traits::CalcValue::Range(rv))
405 }
406 }
407 Err(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
408 }
409 } else {
410 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
411 ExcelError::new(ExcelErrorKind::Ref),
412 )))
413 }
414 }
415}
416
417fn arg_indirect() -> Vec<ArgSchema> {
418 vec![
419 ArgSchema {
420 kinds: smallvec::smallvec![ArgKind::Text],
421 required: true,
422 by_ref: false,
423 shape: ShapeKind::Scalar,
424 coercion: CoercionPolicy::None,
425 max: None,
426 repeating: None,
427 default: None,
428 },
429 ArgSchema {
430 kinds: smallvec::smallvec![ArgKind::Logical, ArgKind::Number],
431 required: false,
432 by_ref: false,
433 shape: ShapeKind::Scalar,
434 coercion: CoercionPolicy::Logical,
435 max: None,
436 repeating: None,
437 default: Some(LiteralValue::Boolean(true)),
438 },
439 ]
440}
441
442#[derive(Debug)]
443pub struct IndirectFn;
444impl Function for IndirectFn {
445 fn caps(&self) -> FnCaps {
446 FnCaps::PURE | FnCaps::RETURNS_REFERENCE | FnCaps::VOLATILE | FnCaps::DYNAMIC_DEPENDENCY
447 }
448 fn name(&self) -> &'static str {
449 "INDIRECT"
450 }
451 fn min_args(&self) -> usize {
452 1
453 }
454 fn arg_schema(&self) -> &'static [ArgSchema] {
455 use once_cell::sync::Lazy;
456 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(arg_indirect);
457 &SCHEMA
458 }
459
460 fn eval_reference<'a, 'b, 'c>(
461 &self,
462 args: &'c [ArgumentHandle<'a, 'b>],
463 _ctx: &dyn FunctionContext<'b>,
464 ) -> Option<Result<ReferenceType, ExcelError>> {
465 if args.is_empty() {
466 return Some(Err(ExcelError::new(ExcelErrorKind::Value)));
467 }
468
469 let ref_text = match args[0].value() {
470 Ok(cv) => match cv.into_literal() {
471 LiteralValue::Text(s) => s.to_string(),
472 _ => return Some(Err(ExcelError::new(ExcelErrorKind::Value))),
473 },
474 Err(e) => return Some(Err(e)),
475 };
476
477 let a1_style = if args.len() >= 2 {
478 match args[1].value() {
479 Ok(cv) => match cv.into_literal() {
480 LiteralValue::Boolean(b) => b,
481 LiteralValue::Int(i) => i != 0,
482 LiteralValue::Number(n) => n != 0.0,
483 _ => return Some(Err(ExcelError::new(ExcelErrorKind::Value))),
484 },
485 Err(e) => return Some(Err(e)),
486 }
487 } else {
488 true
489 };
490
491 if !a1_style {
492 return Some(Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
493 "INDIRECT with R1C1 style (second argument FALSE) is not yet supported",
494 )));
495 }
496
497 let parsed = formualizer_parse::parser::ReferenceType::parse_sheet_ref(&ref_text);
498
499 match parsed {
500 Ok(formualizer_common::SheetRef::Cell(cell)) => {
501 let sheet = match cell.sheet {
502 formualizer_common::SheetLocator::Current => None,
503 formualizer_common::SheetLocator::Name(name) => Some(name.to_string()),
504 formualizer_common::SheetLocator::Id(_) => None,
505 };
506 Some(Ok(ReferenceType::Cell {
507 sheet,
508 row: cell.coord.row() + 1,
509 col: cell.coord.col() + 1,
510 row_abs: cell.coord.row_abs(),
511 col_abs: cell.coord.col_abs(),
512 }))
513 }
514 Ok(formualizer_common::SheetRef::Range(range)) => {
515 let sheet = match range.sheet {
516 formualizer_common::SheetLocator::Current => None,
517 formualizer_common::SheetLocator::Name(name) => Some(name.to_string()),
518 formualizer_common::SheetLocator::Id(_) => None,
519 };
520 Some(Ok(ReferenceType::Range {
521 sheet,
522 start_row: range.start_row.map(|b| b.index + 1),
523 start_col: range.start_col.map(|b| b.index + 1),
524 end_row: range.end_row.map(|b| b.index + 1),
525 end_col: range.end_col.map(|b| b.index + 1),
526 start_row_abs: range.start_row.map(|b| b.abs).unwrap_or(false),
527 start_col_abs: range.start_col.map(|b| b.abs).unwrap_or(false),
528 end_row_abs: range.end_row.map(|b| b.abs).unwrap_or(false),
529 end_col_abs: range.end_col.map(|b| b.abs).unwrap_or(false),
530 }))
531 }
532 Err(_) => match formualizer_parse::parser::ReferenceType::from_string(&ref_text) {
533 Ok(ReferenceType::NamedRange(name)) => Some(Ok(ReferenceType::NamedRange(name))),
534 Ok(ReferenceType::Table(tref)) => Some(Ok(ReferenceType::Table(tref))),
535 _ => Some(Err(ExcelError::new(ExcelErrorKind::Ref))),
536 },
537 }
538 }
539
540 fn eval<'a, 'b, 'c>(
541 &self,
542 args: &'c [ArgumentHandle<'a, 'b>],
543 ctx: &dyn FunctionContext<'b>,
544 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
545 match self.eval_reference(args, ctx) {
546 Some(Ok(r)) => {
547 let current_sheet = ctx.current_sheet();
548 match ctx.resolve_range_view(&r, current_sheet) {
549 Ok(rv) => {
550 let (rows, cols) = rv.dims();
551 if rows == 1 && cols == 1 {
552 Ok(crate::traits::CalcValue::Scalar(
553 rv.as_1x1().unwrap_or(LiteralValue::Empty),
554 ))
555 } else {
556 Ok(crate::traits::CalcValue::Range(rv))
557 }
558 }
559 Err(e) => {
560 let mapped = if e.kind == ExcelErrorKind::Name {
561 ExcelError::new(ExcelErrorKind::Ref)
562 } else {
563 e
564 };
565 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
566 mapped,
567 )))
568 }
569 }
570 }
571 Some(Err(e)) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
572 None => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
573 ExcelError::new(ExcelErrorKind::Ref),
574 ))),
575 }
576 }
577}
578
579pub fn register_builtins() {
580 crate::function_registry::register_function(std::sync::Arc::new(IndexFn));
581 crate::function_registry::register_function(std::sync::Arc::new(OffsetFn));
582 crate::function_registry::register_function(std::sync::Arc::new(IndirectFn));
583}
584
585#[cfg(test)]
586mod tests {
587 use super::*;
588 use crate::test_workbook::TestWorkbook;
589 use crate::traits::ArgumentHandle;
590 use formualizer_parse::parser::{ASTNode, ASTNodeType};
591
592 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
593 wb.interpreter()
594 }
595
596 #[test]
597 fn index_returns_reference_and_materializes_in_value_context() {
598 let wb = TestWorkbook::new()
599 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(42))
600 .with_function(std::sync::Arc::new(IndexFn));
601 let ctx = interp(&wb);
602
603 let array_ref = ASTNode::new(
605 ASTNodeType::Reference {
606 original: "A1:C3".into(),
607 reference: ReferenceType::Range {
608 sheet: None,
609 start_row: Some(1),
610 start_col: Some(1),
611 end_row: Some(3),
612 end_col: Some(3),
613 start_row_abs: false,
614 start_col_abs: false,
615 end_row_abs: false,
616 end_col_abs: false,
617 },
618 },
619 None,
620 );
621 let row = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(2)), None);
622 let col = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(2)), None);
623 let call = ASTNode::new(
624 ASTNodeType::Function {
625 name: "INDEX".into(),
626 args: vec![array_ref.clone(), row.clone(), col.clone()],
627 },
628 None,
629 );
630
631 let r = ctx.evaluate_ast_as_reference(&call).expect("ref ok");
633 match r {
634 ReferenceType::Cell { row, col, .. } => {
635 assert_eq!((row, col), (2, 2));
636 }
637 _ => panic!(),
638 }
639
640 let args = vec![
642 ArgumentHandle::new(&array_ref, &ctx),
643 ArgumentHandle::new(&row, &ctx),
644 ArgumentHandle::new(&col, &ctx),
645 ];
646 let f = ctx.context.get_function("", "INDEX").unwrap();
647 let v = f
648 .dispatch(&args, &ctx.function_context(None))
649 .unwrap()
650 .into_literal();
651 assert_eq!(v, LiteralValue::Number(42.0));
652 }
653
654 #[test]
655 fn offset_returns_reference_and_materializes() {
656 let wb = TestWorkbook::new()
657 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(1))
658 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(5))
659 .with_function(std::sync::Arc::new(OffsetFn));
660 let ctx = interp(&wb);
661
662 let base = ASTNode::new(
663 ASTNodeType::Reference {
664 original: "A1".into(),
665 reference: ReferenceType::Cell {
666 sheet: None,
667 row: 1,
668 col: 1,
669 row_abs: false,
670 col_abs: false,
671 },
672 },
673 None,
674 );
675 let dr = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(1)), None);
676 let dc = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(1)), None);
677 let call = ASTNode::new(
678 ASTNodeType::Function {
679 name: "OFFSET".into(),
680 args: vec![base.clone(), dr.clone(), dc.clone()],
681 },
682 None,
683 );
684
685 let r = ctx.evaluate_ast_as_reference(&call).expect("ref ok");
686 match r {
687 ReferenceType::Cell { row, col, .. } => assert_eq!((row, col), (2, 2)),
688 _ => panic!(),
689 }
690
691 let args = vec![
692 ArgumentHandle::new(&base, &ctx),
693 ArgumentHandle::new(&dr, &ctx),
694 ArgumentHandle::new(&dc, &ctx),
695 ];
696 let f = ctx.context.get_function("", "OFFSET").unwrap();
697 let v = f
698 .dispatch(&args, &ctx.function_context(None))
699 .unwrap()
700 .into_literal();
701 assert_eq!(v, LiteralValue::Number(5.0));
702 }
703}