1 // =================================
  2 // Copyright (c) 2025 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.Collections;
  8 using System.Xml;
  9 
 10 namespace System.XPath
 11 {
 12     public enum Operator
 13     {
 14         or_and_equalnotEquallessgreaterlessOrEqualgreaterOrEqualplusminusmuldivmodunionslashslashSlashparens
 15     }
 16 
 17     public string OperatorStr(Operator op)
 18     {
 19         switch (op)
 20         {
 21             case Operator.or_: return "or";
 22             case Operator.and_: return "and";
 23             case Operator.equal: return "equal";
 24             case Operator.notEqual: return "not-equal";
 25             case Operator.less: return "less";
 26             case Operator.greater: return "greater";
 27             case Operator.lessOrEqual: return "less-or-equal";
 28             case Operator.greaterOrEqual: return "greater-or-equal";
 29             case Operator.plus: return "plus";
 30             case Operator.minus: return "minus";
 31             case Operator.mul: return "mul";
 32             case Operator.div: return "div";
 33             case Operator.mod: return "mod";
 34             case Operator.union: return "union";
 35             case Operator.slash: return "slash";
 36             case Operator.slashSlash: return "slash-slash";
 37             case Operator.parens: return "parens";
 38         }
 39         return "<unknown operator>";
 40     }
 41 
 42     public enum ExprKind
 43     {
 44         unaryExprbinaryExprrootfilterExprlocationStepExprvariableReferenceliteralnumberExprfunctionCall
 45     }
 46 
 47     public string ExprKindStr(ExprKind kind)
 48     {
 49         switch (kind)
 50         {
 51             case ExprKind.unaryExpr:  return "unary-expr";
 52             case ExprKind.binaryExpr:  return "binary-expr";
 53             case ExprKind.root:  return "root";
 54             case ExprKind.filterExpr:  return "filter";
 55             case ExprKind.locationStepExpr:  return "location-step";
 56             case ExprKind.variableReference:  return "variable-reference";
 57             case ExprKind.literal:  return "literal";
 58             case ExprKind.numberExpr:  return "number";
 59             case ExprKind.functionCall:  return "function-call";
 60         }
 61         return "<unknown expression>";
 62     }
 63 
 64     public abstract class Expr
 65     {
 66         public Expr(ExprKind kind_) : kind(kind_)
 67         {
 68         }
 69         public virtual default ~Expr();
 70         public inline ExprKind Kind() const
 71         {
 72             return kind;
 73         }
 74         public inline const string& GetString() const
 75         {
 76             return str;
 77         }
 78         public void SetString(const string& str_)
 79         {
 80             str = str_;
 81         }
 82         public abstract Result<UniquePtr<Object>> Evaluate(Context& context);
 83         public virtual System.Xml.Element* ToXmlElement() const
 84         {
 85             System.Xml.Element* element = System.Xml.MakeElement("expr");
 86             element->SetAttribute("kind"ExprKindStr(kind));
 87             return element;
 88         }
 89         private ExprKind kind;
 90         private string str;
 91     }
 92 
 93     public class UnaryExpr : Expr
 94     {
 95         public UnaryExpr(Operator op_Expr* operand_) : base(ExprKind.unaryExpr)op(op_)operand(operand_)
 96         {
 97         }
 98         public inline Operator Op() const
 99         {
100             return op;
101         }
102         public inline Expr* Operand() const
103         {
104             return operand.Get();
105         }
106         [nodiscard]
107         public override Result<UniquePtr<Object>> Evaluate(Context& context)
108         {
109             switch (op)
110             {
111                 case Operator.minus:
112                 {
113                     return EvaluateUnaryMinusExpr(operand.Get()context);
114                 }
115                 case Operator.parens:
116                 {
117                     return EvaluateParenExpr(operand.Get()context);
118                 }
119             }
120             int errorId = AllocateError("unary minus or parenthesis operator expected");
121             return Result<UniquePtr<Object>>(ErrorId(errorId));
122         }
123         public override System.Xml.Element* ToXmlElement() const
124         {
125             System.Xml.Element* element = base->ToXmlElement();
126             element->SetAttribute("operator"OperatorStr(op));
127             System.Xml.Element* child = operand->ToXmlElement();
128             element->AppendChild(child);
129             return element;
130         }
131         private Operator op;
132         private UniquePtr<Expr> operand;
133     }
134 
135     public class BinaryExpr : Expr
136     {
137         public BinaryExpr(Operator op_Expr* left_Expr* right_) : base(ExprKind.binaryExpr)op(op_)left(left_)right(right_)
138         {
139         }
140         public inline Operator Op() const
141         {
142             return op;
143         }
144         public inline Expr* Left() const
145         {
146             return left.Get();
147         }
148         public inline Expr* Right() const
149         {
150             return right.Get();
151         }
152         [nodiscard]
153         public override Result<UniquePtr<Object>> Evaluate(Context& context)
154         {
155             switch (op)
156             {
157                 case Operator.or_:
158                 {
159                     return EvaluateOrExpr(left.Get()right.Get()context);
160                 }
161                 case Operator.and_:
162                 {
163                     return EvaluateAndExpr(left.Get()right.Get()context);
164                 }
165                 case Operator.equal:
166                 case Operator.notEqual:
167                 case Operator.less:
168                 case Operator.greater:
169                 case Operator.lessOrEqual:
170                 case Operator.greaterOrEqual:
171                 {
172                     return Compare(left.Get()right.Get()opcontext);
173                 }
174                 case Operator.plus:
175                 case Operator.minus:
176                 case Operator.mul:
177                 case Operator.div:
178                 case Operator.mod:
179                 {
180                     return EvaluateArithmeticOp(left.Get()right.Get()opcontext);
181                 }
182                 case Operator.union:
183                 {
184                     return EvaluateUnionExpr(left.Get()right.Get()context);
185                 }
186                 case Operator.slash:
187                 {
188                     return EvaluateCombineStepExpr(left.Get()right.Get()context);
189                 }
190             }
191             int errorId = AllocateError("binary operator expected");
192             return Result<UniquePtr<Object>>(ErrorId(errorId));
193         }
194         public override System.Xml.Element* ToXmlElement() const
195         {
196             System.Xml.Element* element = base->ToXmlElement();
197             element->SetAttribute("operator"OperatorStr(op));
198             System.Xml.Element* leftElement = left->ToXmlElement();
199             element->AppendChild(leftElement);
200             System.Xml.Element* rightElement = right->ToXmlElement();
201             element->AppendChild(rightElement);
202             return element;
203         }
204         private Operator op;
205         private UniquePtr<Expr> left;
206         private UniquePtr<Expr> right;
207     }
208 
209     public class Root : Expr
210     {
211         public Root() : base(ExprKind.root)
212         {
213         }
214         [nodiscard]
215         public override Result<UniquePtr<Object>> Evaluate(Context& context) const
216         {
217             UniquePtr<NodeSet> nodeSet = new NodeSet();
218             if (context.Node()->IsDocumentNode())
219             {
220                 nodeSet->Add(context.Node());
221             }
222             else if (context.Node()->OwnerDocument() != null)
223             {
224                 nodeSet->Add(context.Node()->OwnerDocument());
225             }
226             return Result<UniquePtr<Object>>(UniquePtr<Object>(nodeSet.Release()));
227         }
228     }
229 
230     public class FilterExpr : Expr
231     {
232         public FilterExpr(Expr* subject_Expr* predicate_) : base(ExprKind.filterExpr)subject(subject_)predicate(predicate_)
233         {
234         }
235         public inline Expr* Subject() const
236         {
237             return subject.Get();
238         }
239         public inline Expr* Predicate() const
240         {
241             return predicate.Get();
242         }
243         [nodiscard]
244         public override Result<UniquePtr<Object>> Evaluate(Context& context) const
245         {
246             auto subjectResult = subject->Evaluate(context);
247             if (subjectResult.Error())
248             {
249                 return subjectResult;
250             }
251             UniquePtr<Object> subjectObject = Rvalue(subjectResult.Value());
252             if (subjectObject->IsNodeSet())
253             {
254                 UniquePtr<NodeSet> nodeSet = cast<NodeSet*>(subjectObject.Release());
255                 UniquePtr<NodeSet> filteredNodeSet = new NodeSet();
256                 int n = nodeSet->Count();
257                 for (int i = 0; i < n; ++i;)
258                 {
259                     System.Xml.Node* node = nodeSet->GetNode(i);
260                     Context filterContext(nodei + 1n);
261                     auto result = predicate->Evaluate(filterContext);
262                     if (result.Error())
263                     {
264                         return result;
265                     }
266                     Object* resultObject = result.Value().Get();
267                     bool include = false;
268                     if (resultObject->IsNumber())
269                     {
270                         Number* number = cast<Number*>(resultObject);
271                         if (number->Value() == filterContext.Pos())
272                         {
273                             include = true;
274                         }
275                     }
276                     else
277                     {
278                         auto booleanResult = ToBoolean(resultObjectfilterContext);
279                         if (booleanResult.Error())
280                         {
281                             return booleanResult;
282                         }
283                         auto booleanCastResult = BooleanCast(booleanResult.Value().Get());
284                         if (booleanCastResult.Error())
285                         {
286                             return Result<UniquePtr<Object>>(ErrorId(booleanCastResult.GetErrorId()));
287                         }
288                         Boolean* boolean = booleanCastResult.Value();
289                         include = boolean->Value();
290                     }
291                     if (include)
292                     {
293                         filteredNodeSet->Add(node);
294                     }
295                 }
296                 Swap(nodeSetfilteredNodeSet);
297                 return Result<UniquePtr<Object>>(UniquePtr<Object>(nodeSet.Release()));
298             }
299             else
300             {
301                 int errorId = AllocateError("node-set expected");
302                 return Result<UniquePtr<Object>>(ErrorId(errorId));
303             }
304         }
305         public override System.Xml.Element* ToXmlElement() const
306         {
307             System.Xml.Element* element = base->ToXmlElement();
308             System.Xml.Element* subjectElement = subject->ToXmlElement();
309             element->AppendChild(subjectElement);
310             System.Xml.Element* predicateElement = predicate->ToXmlElement();
311             element->AppendChild(predicateElement);
312             return element;
313         }
314         private UniquePtr<Expr> subject;
315         private UniquePtr<Expr> predicate;
316     }
317 
318     public class LocationStepExpr : Expr
319     {
320         public LocationStepExpr(System.Xml.Axis axis_NodeTest* nodeTest_) : base(ExprKind.locationStepExpr)axis(axis_)nodeTest(nodeTest_)
321         {
322         }
323         public inline NodeTest* GetNodeTest() const
324         {
325             return nodeTest.Get();
326         }
327         public void AddPredicate(Expr* predicate)
328         {
329             predicates.Add(UniquePtr<Expr>(predicate));
330         }
331         public inline const List<UniquePtr<Expr>>& Predicates() const
332         {
333             return predicates;
334         }
335         [nodiscard]
336         public override Result<UniquePtr<Object>> Evaluate(Context& context) const
337         {
338             UniquePtr<NodeSet> nodeSet(new NodeSet());
339             NodeSelectionOperation selectNodes(nodeTest.Get()*nodeSetaxis);
340             context.Node()->Walk(selectNodesaxis);
341             for (const auto& predicate : predicates)
342             {
343                 UniquePtr<NodeSet> filteredNodeSet(new NodeSet());
344                 int n = nodeSet->Count();
345                 for (int i = 0; i < n; ++i;)
346                 {
347                     System.Xml.Node* node = nodeSet->GetNode(i);
348                     Context filterContext(nodei + 1n);
349                     auto predicateEvaluationResult = predicate->Evaluate(filterContext);
350                     if (predicateEvaluationResult.Error())
351                     {
352                         return Result<UniquePtr<Object>>(ErrorId(predicateEvaluationResult.GetErrorId()));
353                     }
354                     bool include = false;
355                     Object* predicateResult = predicateEvaluationResult.Value().Get();
356                     if (predicateResult->IsNumber())
357                     {
358                         Number* number = cast<Number*>(predicateResult);
359                         if (number->Value() == filterContext.Pos())
360                         {
361                             include = true;
362                         }
363                     }
364                     else
365                     {
366                         auto booleanResult = ToBoolean(predicateResultfilterContext);
367                         if (booleanResult.Error())
368                         {
369                             return booleanResult;
370                         }
371                         auto booleanCastResult = BooleanCast(booleanResult.Value().Get());
372                         if (booleanCastResult.Error())
373                         {
374                             return Result<UniquePtr<Object>>(ErrorId(booleanCastResult.GetErrorId()));
375                         }
376                         Boolean* boolean = booleanCastResult.Value();
377                         include = boolean->Value();
378                     }
379                     if (include)
380                     {
381                         filteredNodeSet->Add(node);
382                     }
383                 }
384                 Swap(nodeSetfilteredNodeSet);
385             }
386             return Result<UniquePtr<Object>>(UniquePtr<Object>(nodeSet.Release()));
387         }
388         public override System.Xml.Element* ToXmlElement() const
389         {
390             System.Xml.Element* element = base->ToXmlElement();
391             element->SetAttribute("axis"System.Xml.AxisName(axis));
392             element->AppendChild(nodeTest->ToXmlElement());
393             System.Xml.Element* predicatesElement = System.Xml.MakeElement("predicates");
394             for (const auto& predicate : predicates)
395             {
396                 predicatesElement->AppendChild(predicate->ToXmlElement());
397             }
398             element->AppendChild(predicatesElement);
399             return element;
400         }
401         private System.Xml.Axis axis;
402         private UniquePtr<NodeTest> nodeTest;
403         private List<UniquePtr<Expr>> predicates;
404     }
405 
406     public class VariableReference : Expr
407     {
408         public VariableReference(const string& variableName_) : base(ExprKind.variableReference)variableName(variableName_)
409         {
410         }
411         public inline const string& VariableName() const
412         {
413             return variableName;
414         }
415         [nodiscard]
416         public override Result<UniquePtr<Object>> Evaluate(Context& context) const
417         {
418             #assert(false);
419             return Result<UniquePtr<Object>>();
420         }
421         public override System.Xml.Element* ToXmlElement() const
422         {
423             System.Xml.Element* element = base->ToXmlElement();
424             element->SetAttribute("variable-name"variableName);
425             return element;
426         }
427         private string variableName;
428     }
429 
430     public class Literal : Expr
431     {
432         public Literal(const string& value_) : base(ExprKind.literal)value(value_)
433         {
434         }
435         public inline const string& Value() const
436         {
437             return value;
438         }
439         [nodiscard]
440         public override Result<UniquePtr<Object>> Evaluate(Context& context) const
441         {
442             return Result<UniquePtr<Object>>(UniquePtr<Object>(new Str(value)));
443         }
444         public override System.Xml.Element* ToXmlElement() const
445         {
446             System.Xml.Element* element = base->ToXmlElement();
447             element->SetAttribute("value"value);
448             return element;
449         }
450         private string value;
451     }
452 
453     public class NumberExpr : Expr
454     {
455         public NumberExpr(double value_) : base(ExprKind.numberExpr)value(value_)
456         {
457         }
458         [nodiscard]
459         public override Result<UniquePtr<Object>> Evaluate(Context& context) const
460         {
461             return Result<UniquePtr<Object>>(UniquePtr<Object>(new Number(value)));
462         }
463         public override System.Xml.Element* ToXmlElement() const
464         {
465             System.Xml.Element* element = base->ToXmlElement();
466             element->SetAttribute("value"ToString(value));
467             return element;
468         }
469         private double value;
470     }
471 
472     public class FunctionCall : Expr
473     {
474         public FunctionCall(const string& functionName_) : base(ExprKind.functionCall)functionName(functionName_)
475         {
476         }
477         public inline const string& FunctionName() const
478         {
479             return functionName;
480         }
481         public void AddArgument(Expr* argument)
482         {
483             arguments.Add(UniquePtr<Expr>(argument));
484         }
485         [nodiscard]
486         public override Result<UniquePtr<Object>> Evaluate(Context& context) const
487         {
488             auto functionResult = GetFunction(functionName);
489             if (functionResult.Error())
490             {
491                 return Result<UniquePtr<Object>>(ErrorId(functionResult.GetErrorId()));
492             }
493             Function* function = functionResult.Value();
494             List<UniquePtr<Object>> ownedArgs;
495             List<Object*> args;
496             for (const auto& arg : arguments)
497             {
498                 auto argResult = arg->Evaluate(context);
499                 if (argResult.Error())
500                 {
501                     return Result<UniquePtr<Object>>(ErrorId(argResult.GetErrorId()));
502                 }
503                 UniquePtr<Object> ownedArg = Rvalue(argResult.Value());
504                 args.Add(ownedArg.Get());
505                 ownedArgs.Add(Rvalue(ownedArg));
506             }
507             return function->Evaluate(contextargs);
508         }
509         public override System.Xml.Element* ToXmlElement() const
510         {
511             System.Xml.Element* element = base->ToXmlElement();
512             element->SetAttribute("function-name"functionName);
513             System.Xml.Element* argumentsElement = System.Xml.MakeElement("arguments");
514             for (const auto& arg : arguments)
515             {
516                 argumentsElement->AppendChild(arg->ToXmlElement());
517             }
518             element->AppendChild(argumentsElement);
519             return element;
520         }
521         private string functionName;
522         private List<UniquePtr<Expr>> arguments;
523     }
524 
525     public Expr* MakeSlashSlashExpr(Expr* leftExpr* right)
526     {
527         return new BinaryExpr(Operator.slashleft
528             new BinaryExpr(Operator.slash
529                 new LocationStepExpr(System.Xml.Axis.descendantOrSelfnew NodeTest(NodeTestKind.anyNodeTest))
530                 right));
531     }