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 }