1use std::fmt;
6
7use html5ever::{LocalName, Namespace, Prefix, QualName, local_name, namespace_prefix, ns};
8
9use super::parser::{
10 AdditiveOp, Axis, EqualityOp, Expr, FilterExpr, KindTest, Literal, MultiplicativeOp, NodeTest,
11 NumericLiteral, PathExpr, PredicateExpr, PredicateListExpr, PrimaryExpr,
12 QName as ParserQualName, RelationalOp, StepExpr, UnaryOp,
13};
14use super::{EvaluationCtx, Value};
15use crate::dom::attr::Attr;
16use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
17use crate::dom::bindings::domname::namespace_from_domstring;
18use crate::dom::bindings::inheritance::{Castable, CharacterDataTypeId, NodeTypeId};
19use crate::dom::bindings::root::DomRoot;
20use crate::dom::bindings::str::DOMString;
21use crate::dom::bindings::xmlname;
22use crate::dom::element::Element;
23use crate::dom::node::{Node, ShadowIncluding};
24use crate::dom::processinginstruction::ProcessingInstruction;
25use crate::xpath::context::PredicateCtx;
26
27#[derive(Clone, Debug, PartialEq)]
28pub(crate) enum Error {
29 NotANodeset,
30 InvalidPath,
31 UnknownFunction { name: QualName },
32 UnknownVariable { name: QualName },
33 UnknownNamespace { prefix: String },
34 InvalidQName { qname: ParserQualName },
35 FunctionEvaluation { fname: String },
36 Internal { msg: String },
37}
38
39impl std::fmt::Display for Error {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 match self {
42 Error::NotANodeset => write!(f, "expression did not evaluate to a nodeset"),
43 Error::InvalidPath => write!(f, "invalid path expression"),
44 Error::UnknownFunction { name } => write!(f, "unknown function {:?}", name),
45 Error::UnknownVariable { name } => write!(f, "unknown variable {:?}", name),
46 Error::UnknownNamespace { prefix } => {
47 write!(f, "unknown namespace prefix {:?}", prefix)
48 },
49 Error::InvalidQName { qname } => {
50 write!(f, "invalid QName {:?}", qname)
51 },
52 Error::FunctionEvaluation { fname } => {
53 write!(f, "error while evaluating function: {}", fname)
54 },
55 Error::Internal { msg } => {
56 write!(f, "internal error: {}", msg)
57 },
58 }
59 }
60}
61
62impl std::error::Error for Error {}
63
64pub(crate) fn try_extract_nodeset(v: Value) -> Result<Vec<DomRoot<Node>>, Error> {
65 match v {
66 Value::Nodeset(ns) => Ok(ns),
67 _ => Err(Error::NotANodeset),
68 }
69}
70
71pub(crate) trait Evaluatable: fmt::Debug {
72 fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error>;
73 fn is_primitive(&self) -> bool;
75}
76
77impl<T: ?Sized> Evaluatable for Box<T>
78where
79 T: Evaluatable,
80{
81 fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
82 (**self).evaluate(context)
83 }
84
85 fn is_primitive(&self) -> bool {
86 (**self).is_primitive()
87 }
88}
89
90impl<T> Evaluatable for Option<T>
91where
92 T: Evaluatable,
93{
94 fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
95 match self {
96 Some(expr) => expr.evaluate(context),
97 None => Ok(Value::Nodeset(vec![])),
98 }
99 }
100
101 fn is_primitive(&self) -> bool {
102 self.as_ref().is_some_and(|t| T::is_primitive(t))
103 }
104}
105
106impl Evaluatable for Expr {
107 fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
108 match self {
109 Expr::And(left, right) => {
110 let left_bool = left.evaluate(context)?.boolean();
111 let v = left_bool && right.evaluate(context)?.boolean();
112 Ok(Value::Boolean(v))
113 },
114 Expr::Or(left, right) => {
115 let left_bool = left.evaluate(context)?.boolean();
116 let v = left_bool || right.evaluate(context)?.boolean();
117 Ok(Value::Boolean(v))
118 },
119 Expr::Equality(left, equality_op, right) => {
120 let left_val = left.evaluate(context)?;
121 let right_val = right.evaluate(context)?;
122
123 let v = match equality_op {
124 EqualityOp::Eq => left_val == right_val,
125 EqualityOp::NotEq => left_val != right_val,
126 };
127
128 Ok(Value::Boolean(v))
129 },
130 Expr::Relational(left, relational_op, right) => {
131 let left_val = left.evaluate(context)?.number();
132 let right_val = right.evaluate(context)?.number();
133
134 let v = match relational_op {
135 RelationalOp::Lt => left_val < right_val,
136 RelationalOp::Gt => left_val > right_val,
137 RelationalOp::LtEq => left_val <= right_val,
138 RelationalOp::GtEq => left_val >= right_val,
139 };
140 Ok(Value::Boolean(v))
141 },
142 Expr::Additive(left, additive_op, right) => {
143 let left_val = left.evaluate(context)?.number();
144 let right_val = right.evaluate(context)?.number();
145
146 let v = match additive_op {
147 AdditiveOp::Add => left_val + right_val,
148 AdditiveOp::Sub => left_val - right_val,
149 };
150 Ok(Value::Number(v))
151 },
152 Expr::Multiplicative(left, multiplicative_op, right) => {
153 let left_val = left.evaluate(context)?.number();
154 let right_val = right.evaluate(context)?.number();
155
156 let v = match multiplicative_op {
157 MultiplicativeOp::Mul => left_val * right_val,
158 MultiplicativeOp::Div => left_val / right_val,
159 MultiplicativeOp::Mod => left_val % right_val,
160 };
161 Ok(Value::Number(v))
162 },
163 Expr::Unary(unary_op, expr) => {
164 let v = expr.evaluate(context)?.number();
165
166 match unary_op {
167 UnaryOp::Minus => Ok(Value::Number(-v)),
168 }
169 },
170 Expr::Union(left, right) => {
171 let as_nodes = |e: &Expr| e.evaluate(context).and_then(try_extract_nodeset);
172
173 let mut left_nodes = as_nodes(left)?;
174 let right_nodes = as_nodes(right)?;
175
176 left_nodes.extend(right_nodes);
177 Ok(Value::Nodeset(left_nodes))
178 },
179 Expr::Path(path_expr) => path_expr.evaluate(context),
180 }
181 }
182
183 fn is_primitive(&self) -> bool {
184 match self {
185 Expr::Or(left, right) => left.is_primitive() && right.is_primitive(),
186 Expr::And(left, right) => left.is_primitive() && right.is_primitive(),
187 Expr::Equality(left, _, right) => left.is_primitive() && right.is_primitive(),
188 Expr::Relational(left, _, right) => left.is_primitive() && right.is_primitive(),
189 Expr::Additive(left, _, right) => left.is_primitive() && right.is_primitive(),
190 Expr::Multiplicative(left, _, right) => left.is_primitive() && right.is_primitive(),
191 Expr::Unary(_, expr) => expr.is_primitive(),
192 Expr::Union(_, _) => false,
193 Expr::Path(path_expr) => path_expr.is_primitive(),
194 }
195 }
196}
197
198impl Evaluatable for PathExpr {
199 fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
200 let mut current_nodes = if self.is_absolute || self.is_descendant {
202 vec![context.starting_node.clone()]
203 } else {
204 vec![context.context_node.clone()]
205 };
206
207 if self.is_descendant {
209 current_nodes = current_nodes
210 .iter()
211 .flat_map(|n| n.traverse_preorder(ShadowIncluding::No))
212 .collect();
213 }
214
215 trace!("[PathExpr] Evaluating path expr: {:?}", self);
216
217 let have_multiple_steps = self.steps.len() > 1;
218
219 for step in &self.steps {
220 let mut next_nodes = Vec::new();
221 for node in current_nodes {
222 let step_context = context.subcontext_for_node(&node);
223 let step_result = step.evaluate(&step_context)?;
224 match (have_multiple_steps, step_result) {
225 (_, Value::Nodeset(mut nodes)) => {
226 next_nodes.append(&mut nodes);
228 },
229 (false, value) => {
230 trace!("[PathExpr] Got single primitive value: {:?}", value);
231 return Ok(value);
232 },
233 (true, value) => {
234 error!(
235 "Expected nodeset from step evaluation, got: {:?} node: {:?}, step: {:?}",
236 value, node, step
237 );
238 return Ok(value);
239 },
240 }
241 }
242 current_nodes = next_nodes;
243 }
244
245 trace!("[PathExpr] Got nodes: {:?}", current_nodes);
246
247 Ok(Value::Nodeset(current_nodes))
248 }
249
250 fn is_primitive(&self) -> bool {
251 !self.is_absolute &&
252 !self.is_descendant &&
253 self.steps.len() == 1 &&
254 self.steps[0].is_primitive()
255 }
256}
257
258#[derive(Debug)]
261enum ValidationError {
262 InvalidCharacter,
263 Namespace,
264}
265
266fn validate_and_extract_qualified_name(
270 qualified_name: &str,
271) -> Result<(Option<&str>, &str), ValidationError> {
272 if qualified_name.is_empty() {
273 return Err(ValidationError::InvalidCharacter);
275 }
276 let mut colon_offset = None;
277 let mut at_start_of_name = true;
278
279 for (byte_position, c) in qualified_name.char_indices() {
280 if c == ':' {
281 if colon_offset.is_some() {
282 return Err(ValidationError::InvalidCharacter);
284 }
285 colon_offset = Some(byte_position);
286 at_start_of_name = true;
287 continue;
288 }
289
290 if at_start_of_name {
291 if !xmlname::is_valid_start(c) {
292 return Err(ValidationError::InvalidCharacter);
294 }
295 at_start_of_name = false;
296 } else if !xmlname::is_valid_continuation(c) {
297 return Err(ValidationError::InvalidCharacter);
299 }
300 }
301
302 let Some(colon_offset) = colon_offset else {
303 return Ok((None, qualified_name));
305 };
306
307 let (prefix, local_name) = qualified_name.split_at(colon_offset);
308 let local_name = &local_name[1..]; if prefix.is_empty() || local_name.is_empty() {
311 return Err(ValidationError::InvalidCharacter);
313 }
314
315 Ok((Some(prefix), local_name))
316}
317
318fn validate_and_extract(
321 namespace: Option<DOMString>,
322 qualified_name: &str,
323) -> Result<(Namespace, Option<Prefix>, LocalName), ValidationError> {
324 let namespace = namespace_from_domstring(namespace);
326
327 let (prefix, local_name) = validate_and_extract_qualified_name(qualified_name)?;
334 debug_assert!(!local_name.contains(':'));
335
336 match (namespace, prefix) {
337 (ns!(), Some(_)) => {
338 Err(ValidationError::Namespace)
340 },
341 (ref ns, Some("xml")) if ns != &ns!(xml) => {
342 Err(ValidationError::Namespace)
345 },
346 (ref ns, p) if ns != &ns!(xmlns) && (qualified_name == "xmlns" || p == Some("xmlns")) => {
347 Err(ValidationError::Namespace)
350 },
351 (ns!(xmlns), p) if qualified_name != "xmlns" && p != Some("xmlns") => {
352 Err(ValidationError::Namespace)
355 },
356 (ns, p) => {
357 Ok((ns, p.map(Prefix::from), LocalName::from(local_name)))
359 },
360 }
361}
362
363pub(crate) struct QualNameConverter<'a> {
364 qname: &'a ParserQualName,
365 context: &'a EvaluationCtx,
366}
367
368impl<'a> TryFrom<QualNameConverter<'a>> for QualName {
369 type Error = Error;
370
371 fn try_from(converter: QualNameConverter<'a>) -> Result<Self, Self::Error> {
372 let qname_as_str = converter.qname.to_string();
373 let namespace = converter
374 .context
375 .resolve_namespace(converter.qname.prefix.as_deref());
376
377 if let Ok((ns, prefix, local)) = validate_and_extract(namespace, &qname_as_str) {
378 Ok(QualName { prefix, ns, local })
379 } else {
380 Err(Error::InvalidQName {
381 qname: converter.qname.clone(),
382 })
383 }
384 }
385}
386
387#[derive(Debug)]
388pub(crate) enum NameTestComparisonMode {
389 XHtml,
391 Html,
393}
394
395pub(crate) fn element_name_test(
396 expected_name: QualName,
397 element_qualname: QualName,
398 comparison_mode: NameTestComparisonMode,
399) -> bool {
400 let is_wildcard = expected_name.local == local_name!("*");
401
402 let test_prefix = expected_name
403 .prefix
404 .clone()
405 .unwrap_or(namespace_prefix!(""));
406 let test_ns_uri = match test_prefix {
407 namespace_prefix!("*") => ns!(*),
408 namespace_prefix!("html") => ns!(html),
409 namespace_prefix!("xml") => ns!(xml),
410 namespace_prefix!("xlink") => ns!(xlink),
411 namespace_prefix!("svg") => ns!(svg),
412 namespace_prefix!("mathml") => ns!(mathml),
413 namespace_prefix!("") => {
414 if matches!(comparison_mode, NameTestComparisonMode::XHtml) {
415 ns!()
416 } else {
417 ns!(html)
418 }
419 },
420 _ => {
421 if matches!(comparison_mode, NameTestComparisonMode::XHtml) {
423 panic!("Unrecognized namespace prefix: {}", test_prefix)
424 } else {
425 ns!(html)
426 }
427 },
428 };
429
430 if is_wildcard {
431 test_ns_uri == element_qualname.ns
432 } else {
433 test_ns_uri == element_qualname.ns && expected_name.local == element_qualname.local
434 }
435}
436
437fn apply_node_test(context: &EvaluationCtx, test: &NodeTest, node: &Node) -> Result<bool, Error> {
438 let result = match test {
439 NodeTest::Name(qname) => {
440 let wanted_name: QualName = QualNameConverter { qname, context }.try_into()?;
442 match node.type_id() {
443 NodeTypeId::Element(_) => {
444 let element = node.downcast::<Element>().unwrap();
445 let comparison_mode = if node.owner_doc().is_html_document() {
446 NameTestComparisonMode::Html
447 } else {
448 NameTestComparisonMode::XHtml
449 };
450 let element_qualname = QualName::new(
451 element.prefix().as_ref().cloned(),
452 element.namespace().clone(),
453 element.local_name().clone(),
454 );
455 element_name_test(wanted_name, element_qualname, comparison_mode)
456 },
457 NodeTypeId::Attr => {
458 let attr = node.downcast::<Attr>().unwrap();
459 let attr_qualname = QualName::new(
460 attr.prefix().cloned(),
461 attr.namespace().clone(),
462 attr.local_name().clone(),
463 );
464 let comparison_mode = NameTestComparisonMode::XHtml;
466 element_name_test(wanted_name, attr_qualname, comparison_mode)
467 },
468 _ => false,
469 }
470 },
471 NodeTest::Wildcard => matches!(node.type_id(), NodeTypeId::Element(_)),
472 NodeTest::Kind(kind) => match kind {
473 KindTest::PI(target) => {
474 if NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) ==
475 node.type_id()
476 {
477 let pi = node.downcast::<ProcessingInstruction>().unwrap();
478 match (target, pi.target()) {
479 (Some(target_name), node_target_name)
480 if target_name == &node_target_name.to_string() =>
481 {
482 true
483 },
484 (Some(_), _) => false,
485 (None, _) => true,
486 }
487 } else {
488 false
489 }
490 },
491 KindTest::Comment => matches!(
492 node.type_id(),
493 NodeTypeId::CharacterData(CharacterDataTypeId::Comment)
494 ),
495 KindTest::Text => matches!(
496 node.type_id(),
497 NodeTypeId::CharacterData(CharacterDataTypeId::Text(_))
498 ),
499 KindTest::Node => true,
500 },
501 };
502 Ok(result)
503}
504
505impl Evaluatable for StepExpr {
506 fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
507 match self {
508 StepExpr::Filter(filter_expr) => filter_expr.evaluate(context),
509 StepExpr::Axis(axis_step) => {
510 let nodes: Vec<DomRoot<Node>> = match axis_step.axis {
511 Axis::Child => context.context_node.children().collect(),
512 Axis::Descendant => context
513 .context_node
514 .traverse_preorder(ShadowIncluding::No)
515 .skip(1)
516 .collect(),
517 Axis::Parent => vec![context.context_node.GetParentNode()]
518 .into_iter()
519 .flatten()
520 .collect(),
521 Axis::Ancestor => context.context_node.ancestors().collect(),
522 Axis::Following => context
523 .context_node
524 .following_nodes(&context.context_node)
525 .skip(1)
526 .collect(),
527 Axis::Preceding => context
528 .context_node
529 .preceding_nodes(&context.context_node)
530 .skip(1)
531 .collect(),
532 Axis::FollowingSibling => context.context_node.following_siblings().collect(),
533 Axis::PrecedingSibling => context.context_node.preceding_siblings().collect(),
534 Axis::Attribute => {
535 if matches!(Node::type_id(&context.context_node), NodeTypeId::Element(_)) {
536 let element = context.context_node.downcast::<Element>().unwrap();
537 element
538 .attrs()
539 .iter()
540 .map(|attr| attr.upcast::<Node>())
541 .map(DomRoot::from_ref)
542 .collect()
543 } else {
544 vec![]
545 }
546 },
547 Axis::Self_ => vec![context.context_node.clone()],
548 Axis::DescendantOrSelf => context
549 .context_node
550 .traverse_preorder(ShadowIncluding::No)
551 .collect(),
552 Axis::AncestorOrSelf => context
553 .context_node
554 .inclusive_ancestors(ShadowIncluding::No)
555 .collect(),
556 Axis::Namespace => Vec::new(), };
558
559 trace!("[StepExpr] Axis {:?} got nodes {:?}", axis_step.axis, nodes);
560
561 let filtered_nodes: Vec<DomRoot<Node>> = nodes
564 .into_iter()
565 .map(|node| {
566 apply_node_test(context, &axis_step.node_test, &node)
567 .map(|matches| matches.then_some(node))
568 })
569 .collect::<Result<Vec<_>, _>>()?
570 .into_iter()
571 .flatten()
572 .collect();
573
574 trace!("[StepExpr] Filtering got nodes {:?}", filtered_nodes);
575
576 if axis_step.predicates.predicates.is_empty() {
577 trace!(
578 "[StepExpr] No predicates, returning nodes {:?}",
579 filtered_nodes
580 );
581 Ok(Value::Nodeset(filtered_nodes))
582 } else {
583 let predicate_list_subcontext = context
585 .update_predicate_nodes(filtered_nodes.iter().map(|n| &**n).collect());
586 axis_step.predicates.evaluate(&predicate_list_subcontext)
587 }
588 },
589 }
590 }
591
592 fn is_primitive(&self) -> bool {
593 match self {
594 StepExpr::Filter(filter_expr) => filter_expr.is_primitive(),
595 StepExpr::Axis(_) => false,
596 }
597 }
598}
599
600impl Evaluatable for PredicateListExpr {
601 fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
602 if let Some(ref predicate_nodes) = context.predicate_nodes {
603 let mut matched_nodes: Vec<DomRoot<Node>> = predicate_nodes.clone();
604
605 for predicate_expr in &self.predicates {
606 let size = matched_nodes.len();
607 let mut new_matched = Vec::new();
608
609 for (i, node) in matched_nodes.iter().enumerate() {
610 let predicate_ctx = EvaluationCtx {
612 starting_node: context.starting_node.clone(),
613 context_node: node.clone(),
614 predicate_nodes: context.predicate_nodes.clone(),
615 predicate_ctx: Some(PredicateCtx { index: i + 1, size }),
616 };
617
618 let eval_result = predicate_expr.expr.evaluate(&predicate_ctx);
619
620 let keep = match eval_result {
621 Ok(Value::Number(n)) => (i + 1) as f64 == n,
622 Ok(Value::Boolean(b)) => b,
623 Ok(v) => v.boolean(),
624 Err(_) => false,
625 };
626
627 if keep {
628 new_matched.push(node.clone());
629 }
630 }
631
632 matched_nodes = new_matched;
633 trace!(
634 "[PredicateListExpr] Predicate {:?} matched nodes {:?}",
635 predicate_expr, matched_nodes
636 );
637 }
638 Ok(Value::Nodeset(matched_nodes))
639 } else {
640 Err(Error::Internal {
641 msg: "[PredicateListExpr] No nodes on stack for predicate to operate on"
642 .to_string(),
643 })
644 }
645 }
646
647 fn is_primitive(&self) -> bool {
648 self.predicates.len() == 1 && self.predicates[0].is_primitive()
649 }
650}
651
652impl Evaluatable for PredicateExpr {
653 fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
654 let narrowed_nodes: Result<Vec<DomRoot<Node>>, Error> = context
655 .subcontext_iter_for_nodes()
656 .filter_map(|ctx| {
657 if let Some(predicate_ctx) = ctx.predicate_ctx {
658 let eval_result = self.expr.evaluate(&ctx);
659
660 let v = match eval_result {
661 Ok(Value::Number(v)) => Ok(predicate_ctx.index == v as usize),
662 Ok(Value::Boolean(v)) => Ok(v),
663 Ok(v) => Ok(v.boolean()),
664 Err(e) => Err(e),
665 };
666
667 match v {
668 Ok(true) => Some(Ok(ctx.context_node)),
669 Ok(false) => None,
670 Err(e) => Some(Err(e)),
671 }
672 } else {
673 Some(Err(Error::Internal {
674 msg: "[PredicateExpr] No predicate context set".to_string(),
675 }))
676 }
677 })
678 .collect();
679
680 Ok(Value::Nodeset(narrowed_nodes?))
681 }
682
683 fn is_primitive(&self) -> bool {
684 self.expr.is_primitive()
685 }
686}
687
688impl Evaluatable for FilterExpr {
689 fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
690 let primary_result = self.primary.evaluate(context)?;
691 let have_predicates = !self.predicates.predicates.is_empty();
692
693 match (have_predicates, &primary_result) {
694 (false, _) => {
695 trace!(
696 "[FilterExpr] No predicates, returning primary result: {:?}",
697 primary_result
698 );
699 Ok(primary_result)
700 },
701 (true, Value::Nodeset(vec)) => {
702 let predicate_list_subcontext =
703 context.update_predicate_nodes(vec.iter().map(|n| &**n).collect());
704 let result_filtered_by_predicates =
705 self.predicates.evaluate(&predicate_list_subcontext);
706 trace!(
707 "[FilterExpr] Result filtered by predicates: {:?}",
708 result_filtered_by_predicates
709 );
710 result_filtered_by_predicates
711 },
712 (true, _) => Err(Error::NotANodeset),
714 }
715 }
716
717 fn is_primitive(&self) -> bool {
718 self.predicates.predicates.is_empty() && self.primary.is_primitive()
719 }
720}
721
722impl Evaluatable for PrimaryExpr {
723 fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
724 match self {
725 PrimaryExpr::Literal(literal) => literal.evaluate(context),
726 PrimaryExpr::Variable(_qname) => todo!(),
727 PrimaryExpr::Parenthesized(expr) => expr.evaluate(context),
728 PrimaryExpr::ContextItem => Ok(Value::Nodeset(vec![context.context_node.clone()])),
729 PrimaryExpr::Function(core_function) => core_function.evaluate(context),
730 }
731 }
732
733 fn is_primitive(&self) -> bool {
734 match self {
735 PrimaryExpr::Literal(_) => true,
736 PrimaryExpr::Variable(_qname) => false,
737 PrimaryExpr::Parenthesized(expr) => expr.is_primitive(),
738 PrimaryExpr::ContextItem => false,
739 PrimaryExpr::Function(_) => false,
740 }
741 }
742}
743
744impl Evaluatable for Literal {
745 fn evaluate(&self, _context: &EvaluationCtx) -> Result<Value, Error> {
746 match self {
747 Literal::Numeric(numeric_literal) => match numeric_literal {
748 NumericLiteral::Integer(v) => Ok(Value::Number(*v as f64)),
750 NumericLiteral::Decimal(v) => Ok(Value::Number(*v)),
751 },
752 Literal::String(s) => Ok(Value::String(s.into())),
753 }
754 }
755
756 fn is_primitive(&self) -> bool {
757 true
758 }
759}