@@ -17,15 +17,33 @@ namespace StringToExpression.GrammerDefinitions
17
17
/// <seealso cref="StringToExpression.GrammerDefinitions.BracketOpenDefinition" />
18
18
public class FunctionCallDefinition : BracketOpenDefinition
19
19
{
20
- /// <summary>
21
- /// Argument types that the function accepts.
22
- /// </summary>
23
- public readonly IReadOnlyList < Type > ArgumentTypes ;
20
+
21
+ public class Overload
22
+ {
23
+ /// <summary>
24
+ /// Argument types that the function accepts.
25
+ /// </summary>
26
+ public readonly IReadOnlyList < Type > ArgumentTypes ;
27
+
28
+ /// <summary>
29
+ /// A function given the arguments, outputs a new operand.
30
+ /// </summary>
31
+ public readonly Func < Expression [ ] , Expression > ExpressionBuilder ;
32
+
33
+ public Overload (
34
+ IEnumerable < Type > argumentTypes ,
35
+ Func < Expression [ ] , Expression > expressionBuilder )
36
+ {
37
+ this . ArgumentTypes = argumentTypes ? . ToList ( ) ;
38
+ this . ExpressionBuilder = expressionBuilder ;
39
+ }
40
+ }
41
+
24
42
25
43
/// <summary>
26
- /// A function given the arguments, outputs a new operand.
44
+ /// Function overlaods
27
45
/// </summary>
28
- public readonly Func < Expression [ ] , Expression > ExpressionBuilder ;
46
+ public readonly IReadOnlyList < Overload > Overloads ;
29
47
30
48
/// <summary>
31
49
/// Initializes a new instance of the <see cref="FunctionCallDefinition"/> class.
@@ -39,10 +57,29 @@ public FunctionCallDefinition(
39
57
string regex ,
40
58
IEnumerable < Type > argumentTypes ,
41
59
Func < Expression [ ] , Expression > expressionBuilder )
60
+ : this ( name , regex , new [ ] { new Overload ( argumentTypes , expressionBuilder ) } )
61
+ {
62
+
63
+ }
64
+
65
+ /// <summary>
66
+ /// Initializes a new instance of the <see cref="FunctionCallDefinition"/> class.
67
+ /// </summary>
68
+ /// <param name="name">The name of the definition.</param>
69
+ /// <param name="regex">The regex to match tokens.</param>
70
+ /// <param name="overloads">list of overloads avilable for function</param>
71
+ public FunctionCallDefinition (
72
+ string name ,
73
+ string regex ,
74
+ IEnumerable < Overload > overloads )
42
75
: base ( name , regex )
43
76
{
44
- this . ArgumentTypes = argumentTypes ? . ToList ( ) ;
45
- this . ExpressionBuilder = expressionBuilder ;
77
+ var overloadList = overloads ? . ToList ( ) ;
78
+ if ( overloadList . Count == 0 )
79
+ {
80
+ throw new ArgumentException ( "Must specify at least one overlaod" , nameof ( overloads ) ) ;
81
+ }
82
+ this . Overloads = overloadList ;
46
83
}
47
84
48
85
/// <summary>
@@ -56,6 +93,58 @@ public FunctionCallDefinition(string name, string regex,Func<Expression[], Expre
56
93
{
57
94
}
58
95
96
+
97
+ public Overload MatchOverload ( Stack < Operand > bracketOperands , out IEnumerable < Expression > typedArguments )
98
+ {
99
+ var possibleOverloads = Overloads
100
+ . Where ( x => x . ArgumentTypes == null || x . ArgumentTypes . Count == bracketOperands . Count )
101
+ . OrderBy ( x=> x . ArgumentTypes == null ) ;
102
+
103
+ // No viable overloads, user has probably inputted wrong number of arguments
104
+ if ( ! possibleOverloads . Any ( ) )
105
+ {
106
+ throw new FunctionArgumentCountException (
107
+ StringSegment . Encompass ( bracketOperands . Select ( x => x . SourceMap ) ) ,
108
+ Overloads . First ( ) . ArgumentTypes . Count ,
109
+ bracketOperands . Count ) ;
110
+ }
111
+
112
+ foreach ( var possibleOverload in possibleOverloads )
113
+ {
114
+ //null argument types is treated as a I can accept anything
115
+ if ( possibleOverload . ArgumentTypes == null )
116
+ {
117
+ typedArguments = bracketOperands . Select ( x => x . Expression ) ;
118
+ return possibleOverload ;
119
+ }
120
+
121
+ var argumentMatch = bracketOperands . Zip ( possibleOverload . ArgumentTypes , ( o , t ) => {
122
+ var canConvert = ExpressionConversions . TryConvert ( o . Expression , t , out var result ) ;
123
+ return new { CanConvert = canConvert , Operand = o , ArgumentType = t , ConvertedOperand = result } ;
124
+ } ) ;
125
+
126
+
127
+ if ( argumentMatch . All ( x => x . CanConvert ) )
128
+ {
129
+ typedArguments = argumentMatch . Select ( x => x . ConvertedOperand ) ;
130
+ return possibleOverload ;
131
+ }
132
+
133
+ // If we have only a single possible overlaod but we arguement types dont align
134
+ // we will throw an error as though they had the wrong types
135
+ if ( possibleOverloads . Count ( ) == 1 )
136
+ {
137
+ var badConvert = argumentMatch . First ( x => ! x . CanConvert ) ;
138
+ throw new FunctionArgumentTypeException ( badConvert . Operand . SourceMap , badConvert . ArgumentType , badConvert . Operand . Expression . Type ) ;
139
+ }
140
+ }
141
+
142
+ //We had multiple overloads, but none of them matched
143
+ throw new FunctionOverlaodNotFoundException ( StringSegment . Encompass ( bracketOperands . Select ( x => x . SourceMap ) ) ) ;
144
+
145
+
146
+ }
147
+
59
148
/// <summary>
60
149
/// Applies the bracket operands. Executes the expressionBuilder with all the operands in the brackets.
61
150
/// </summary>
@@ -68,39 +157,19 @@ public FunctionCallDefinition(string name, string regex,Func<Expression[], Expre
68
157
/// <exception cref="OperationInvalidException">When an error occured while executing the expressionBuilder</exception>
69
158
public override void ApplyBracketOperands ( Operator bracketOpen , Stack < Operand > bracketOperands , Operator bracketClose , ParseState state )
70
159
{
71
- var operandSource = StringSegment . Encompass ( bracketOperands . Select ( x => x . SourceMap ) ) ;
72
- var functionArguments = bracketOperands . Select ( x => x . Expression ) ;
73
- //if we have been given specific argument types validate them
74
- if ( ArgumentTypes != null )
75
- {
76
- var expectedArgumentCount = ArgumentTypes . Count ;
77
- if ( expectedArgumentCount != bracketOperands . Count )
78
- throw new FunctionArgumentCountException (
79
- operandSource ,
80
- expectedArgumentCount ,
81
- bracketOperands . Count ) ;
82
-
83
- functionArguments = bracketOperands . Zip ( ArgumentTypes , ( o , t ) => {
84
- try
85
- {
86
- return ExpressionConversions . Convert ( o . Expression , t ) ;
87
- }
88
- catch ( InvalidOperationException )
89
- {
90
- //if we cant convert to the argument type then something is wrong with the argument
91
- //so we will throw it up
92
- throw new FunctionArgumentTypeException ( o . SourceMap , t , o . Expression . Type ) ;
93
- }
94
- } ) ;
160
+ var functionSourceMap = StringSegment . Encompass (
161
+ bracketOpen . SourceMap ,
162
+ StringSegment . Encompass ( bracketOperands . Select ( x => x . SourceMap ) ) ,
163
+ bracketClose . SourceMap ) ;
95
164
96
- }
165
+ var overload = MatchOverload ( bracketOperands , out var functionArguments ) ;
97
166
98
- var functionSourceMap = StringSegment . Encompass ( bracketOpen . SourceMap , operandSource ) ;
167
+
99
168
var functionArgumentsArray = functionArguments . ToArray ( ) ;
100
169
Expression output ;
101
170
try
102
171
{
103
- output = ExpressionBuilder ( functionArgumentsArray ) ;
172
+ output = overload . ExpressionBuilder ( functionArgumentsArray ) ;
104
173
}
105
174
catch ( Exception ex )
106
175
{
0 commit comments