1 
  
    2 
  
    3 
  
    4 
  
    5 
  
    6 using System;
  
    7 using System.Collections;
  
    8 
  
    9 namespace System.XPath
  
   10 {
  
   11     public enum FunctionKind
  
   12     {
  
   13         boolean, number, string, last, position, count, max
  
   14     }
  
   15 
  
   16     public string FunctionName(FunctionKind kind)
  
   17     {
  
   18         switch (kind)
  
   19         {
  
   20             case FunctionKind.boolean: return "boolean";
  
   21             case FunctionKind.number: return "number";
  
   22             case FunctionKind.string: return "string";
  
   23             case FunctionKind.last: return "last";
  
   24             case FunctionKind.position: return "position";
  
   25             case FunctionKind.count: return "count";
  
   26         }
  
   27         return "<unknown function kind>";
  
   28     }
  
   29 
  
   30     public abstract class Function
  
   31     {
  
   32         public Function(FunctionKind kind_) : kind(kind_), name(FunctionName(kind)), minArity(1), maxArity(1)
  
   33         {
  
   34         }
  
   35         public Function(FunctionKind kind_, int minArity_, int maxArity_) : kind(kind_), name(FunctionName(kind)), minArity(minArity_), maxArity(maxArity_)
  
   36         {
  
   37         }
  
   38         public virtual default ~Function();
  
   39         public inline FunctionKind Kind() const
  
   40         {
  
   41             return kind;
  
   42         }
  
   43         public inline const string& Name() const
  
   44         {
  
   45             return name;
  
   46         }
  
   47         public inline int MinArity() const
  
   48         {
  
   49             return minArity;
  
   50         }
  
   51         public inline int MaxArity() const
  
   52         {
  
   53             return maxArity;
  
   54         }
  
   55         public string ArityStr() const
  
   56         {
  
   57             if (minArity == maxArity)
  
   58             {
  
   59                 return ToString(minArity);
  
   60             }
  
   61             else if (minArity == 0)
  
   62             {
  
   63                 return "at most " + ToString(maxArity);
  
   64             }
  
   65             else
  
   66             {
  
   67                 return "at least " + ToString(minArity) + " and at most " + ToString(maxArity);
  
   68             }
  
   69         }
  
   70         [nodiscard]
  
   71         public Result<UniquePtr<Object>> Evaluate(Context& context, const List<Object*>& arguments)
  
   72         {
  
   73             int n = cast<int>(arguments.Count());
  
   74             if (n < minArity || n > maxArity)
  
   75             {
  
   76                 int errorId = AllocateError("wrong number of arguments for \'" + name + "()\' function: " + ArityStr() + " arguments expected, " + ToString(n) + 
  
   77                     " arguments provided");
  
   78                 return Result<UniquePtr<Object>>(ErrorId(errorId));
  
   79             }
  
   80             return DoEvaluate(context, arguments);
  
   81         }
  
   82         protected abstract Result<UniquePtr<Object>> DoEvaluate(Context& context, const List<Object*>& arguments);
  
   83         private FunctionKind kind;
  
   84         private string name;
  
   85         private int minArity;
  
   86         private int maxArity;
  
   87     }
  
   88 
  
   89     public class BooleanFunction : Function
  
   90     {
  
   91         public BooleanFunction() : base(FunctionKind.boolean)
  
   92         {
  
   93         }
  
   94         [nodiscard]
  
   95         public override Result<UniquePtr<Object>> DoEvaluate(Context& context, const List<Object*>& arguments)
  
   96         {
  
   97             Object* arg = arguments.Front();
  
   98             switch (arg->Kind())
  
   99             {
  
  100                 case ObjectKind.nodeSet:
  
  101                 {
  
  102                     NodeSet* nodeSet = cast<NodeSet*>(arg);
  
  103                     return EvaluateToBoolean(nodeSet);
  
  104                 }
  
  105                 case ObjectKind.boolean:
  
  106                 {
  
  107                     Boolean* boolean = cast<Boolean*>(arg);
  
  108                     return EvaluateToBoolean(boolean);
  
  109                 }
  
  110                 case ObjectKind.number:
  
  111                 {
  
  112                     Number* number = cast<Number*>(arg);
  
  113                     return EvaluateToBoolean(number);
  
  114                 }
  
  115                 case ObjectKind.string:
  
  116                 {
  
  117                     Str* string = cast<Str*>(arg);
  
  118                     return EvaluateToBoolean(string);
  
  119                 }
  
  120             }
  
  121             return Result<UniquePtr<Object>>(UniquePtr<Object>(new Boolean(false)));
  
  122         }
  
  123     }
  
  124 
  
  125     public class NumberFunction : Function
  
  126     {
  
  127         public NumberFunction() : base(FunctionKind.number, 0, 1)
  
  128         {
  
  129         }
  
  130         [nodiscard]
  
  131         public override Result<UniquePtr<Object>> DoEvaluate(Context& context, const List<Object*>& arguments)
  
  132         {
  
  133             Object* arg = null;
  
  134             NodeSet nodeSet;
  
  135             if (arguments.IsEmpty())
  
  136             {
  
  137                 nodeSet.Add(context.Node());
  
  138                 arg = &nodeSet;
  
  139             }
  
  140             else
  
  141             {
  
  142                 arg = arguments.Front();
  
  143             }
  
  144             switch (arg->Kind())
  
  145             {
  
  146                 case ObjectKind.nodeSet:
  
  147                 {
  
  148                     NodeSet* nodeSet = cast<NodeSet*>(arg);
  
  149                     return EvaluateToNumber(nodeSet);
  
  150                 }
  
  151                 case ObjectKind.boolean:
  
  152                 {
  
  153                     Boolean* boolean = cast<Boolean*>(arg);
  
  154                     return EvaluateToNumber(boolean);
  
  155                 }
  
  156                 case ObjectKind.number:
  
  157                 {
  
  158                     Number* number = cast<Number*>(arg);
  
  159                     return EvaluateToNumber(number);
  
  160                 }
  
  161                 case ObjectKind.string:
  
  162                 {
  
  163                     Str* string = cast<Str*>(arg);
  
  164                     return EvaluateToNumber(string);
  
  165                 }
  
  166             }
  
  167             int errorId = AllocateError("invalid argument to \'" + Name() + "()\' function");
  
  168             return Result<UniquePtr<Object>>(ErrorId(errorId));
  
  169         }
  
  170     }
  
  171 
  
  172     public class StringFunction : Function
  
  173     {
  
  174         public StringFunction() : base(FunctionKind.string, 0, 1)
  
  175         {
  
  176         }
  
  177         [nodiscard]
  
  178         public override Result<UniquePtr<Object>> DoEvaluate(Context& context, const List<Object*>& arguments)
  
  179         {
  
  180             Object* arg = null;
  
  181             NodeSet nodeSet;
  
  182             if (arguments.IsEmpty())
  
  183             {
  
  184                 nodeSet.Add(context.Node());
  
  185                 arg = &nodeSet;
  
  186             }
  
  187             else
  
  188             {
  
  189                 arg = arguments.Front();
  
  190             }
  
  191             switch (arg->Kind())
  
  192             {
  
  193                 case ObjectKind.nodeSet:
  
  194                 {
  
  195                     NodeSet* nodeSet = cast<NodeSet*>(arg);
  
  196                     return EvaluateToString(nodeSet);
  
  197                 }
  
  198                 case ObjectKind.boolean:
  
  199                 {
  
  200                     Boolean* boolean = cast<Boolean*>(arg);
  
  201                     return EvaluateToString(boolean);
  
  202                 }
  
  203                 case ObjectKind.number:
  
  204                 {
  
  205                     Number* number = cast<Number*>(arg);
  
  206                     return EvaluateToString(number);
  
  207                 }
  
  208                 case ObjectKind.string:
  
  209                 {
  
  210                     Str* string = cast<Str*>(arg);
  
  211                     return EvaluateToString(string);
  
  212                 }
  
  213             }
  
  214             int errorId = AllocateError("invalid argument to \'" + Name() + "()\' function");
  
  215             return Result<UniquePtr<Object>>(ErrorId(errorId));
  
  216         }
  
  217     }
  
  218 
  
  219     public class LastFunction : Function
  
  220     {
  
  221         public LastFunction() : base(FunctionKind.last, 0, 0)
  
  222         {
  
  223         }
  
  224         [nodiscard]
  
  225         public override Result<UniquePtr<Object>> DoEvaluate(Context& context, const List<Object*>& arguments)
  
  226         {
  
  227             return Result<UniquePtr<Object>>(UniquePtr<Object>(new Number(context.Size())));
  
  228         }
  
  229     }
  
  230 
  
  231     public class PositionFunction : Function
  
  232     {
  
  233         public PositionFunction() : base(FunctionKind.position, 0, 0)
  
  234         {
  
  235         }
  
  236         [nodiscard]
  
  237         public override Result<UniquePtr<Object>> DoEvaluate(Context& context, const List<Object*>& arguments)
  
  238         {
  
  239             return Result<UniquePtr<Object>>(UniquePtr<Object>(new Number(context.Pos())));
  
  240         }
  
  241     }
  
  242 
  
  243     public class CountFunction : Function
  
  244     {
  
  245         public CountFunction() : base(FunctionKind.count)
  
  246         {
  
  247         }
  
  248         [nodiscard]
  
  249         public override Result<UniquePtr<Object>> DoEvaluate(Context& context, const List<Object*>& arguments)
  
  250         {
  
  251             Result<NodeSet*> nodeSetResult = NodeSetCast(arguments.Front(), this);
  
  252             if (nodeSetResult.Error())
  
  253             {
  
  254                 return Result<UniquePtr<Object>>(ErrorId(nodeSetResult.GetErrorId()));
  
  255             }
  
  256             NodeSet* nodeSet = nodeSetResult.Value();
  
  257             return Result<UniquePtr<Object>>(UniquePtr<Object>(new Number(nodeSet->Count())));
  
  258         }
  
  259     }
  
  260 
  
  261     public class FunctionLibrary
  
  262     {
  
  263         static FunctionLibrary() : instance(new FunctionLibrary())
  
  264         {
  
  265         }
  
  266         public static FunctionLibrary& Instance()
  
  267         {
  
  268             return *instance;
  
  269         }
  
  270         private FunctionLibrary()
  
  271         {
  
  272             functions.Resize(cast<int>(FunctionKind.max));
  
  273             Install(new BooleanFunction());
  
  274             Install(new NumberFunction());
  
  275             Install(new StringFunction());
  
  276             Install(new LastFunction());
  
  277             Install(new PositionFunction());
  
  278             Install(new CountFunction());
  
  279         }
  
  280         [nodiscard]
  
  281         public Result<Function*> GetFunction(FunctionKind kind) const
  
  282         {
  
  283             int index = cast<int>(kind);
  
  284             #assert(index >= 0 && index < functions.Count());
  
  285             Function* function = functions[index].Get();
  
  286             if (function == null)
  
  287             {
  
  288                 int errorId = AllocateError("System.XPath.FunctionLibrary: function \'" + FunctionName(kind) + "()\' not installed");
  
  289                 return Result<Function*>(ErrorId(errorId));
  
  290             }
  
  291             return Result<Function*>(function);
  
  292         }
  
  293         [nodiscard]
  
  294         public Result<Function*> GetFunction(const string& name) const
  
  295         {
  
  296             auto it = functionMap.Find(name);
  
  297             if (it != functionMap.End())
  
  298             {
  
  299                 return Result<Function*>(it->second);
  
  300             }
  
  301             else
  
  302             {
  
  303                 int errorId = AllocateError("System.XPath.FunctionLibrary: function \'" + name + "()\' not found");
  
  304                 return Result<Function*>(ErrorId(errorId));
  
  305             }
  
  306         }
  
  307         public void Install(Function* function)
  
  308         {
  
  309             functions[cast<int>(function->Kind())].Reset(function);
  
  310             functionMap[function->Name()] = function;
  
  311         }
  
  312         private static UniquePtr<FunctionLibrary> instance;
  
  313         private List<UniquePtr<Function>> functions;
  
  314         private Map<string, Function*> functionMap;
  
  315     }
  
  316 
  
  317     [nodiscard]
  
  318     public Result<Function*> GetFunction(FunctionKind kind)
  
  319     {
  
  320         return FunctionLibrary.Instance().GetFunction(kind);
  
  321     }
  
  322 
  
  323     [nodiscard]
  
  324     public Result<Function*> GetFunction(const string& name)
  
  325     {
  
  326         return FunctionLibrary.Instance().GetFunction(name);
  
  327     }