@@ -17,15 +17,33 @@ namespace StringToExpression.GrammerDefinitions
1717 /// <seealso cref="StringToExpression.GrammerDefinitions.BracketOpenDefinition" />
1818 public class FunctionCallDefinition : BracketOpenDefinition
1919 {
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+
2442
2543 /// <summary>
26- /// A function given the arguments, outputs a new operand.
44+ /// Function overlaods
2745 /// </summary>
28- public readonly Func < Expression [ ] , Expression > ExpressionBuilder ;
46+ public readonly IReadOnlyList < Overload > Overloads ;
2947
3048 /// <summary>
3149 /// Initializes a new instance of the <see cref="FunctionCallDefinition"/> class.
@@ -39,10 +57,29 @@ public FunctionCallDefinition(
3957 string regex ,
4058 IEnumerable < Type > argumentTypes ,
4159 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 )
4275 : base ( name , regex )
4376 {
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 ;
4683 }
4784
4885 /// <summary>
@@ -56,6 +93,58 @@ public FunctionCallDefinition(string name, string regex,Func<Expression[], Expre
5693 {
5794 }
5895
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+
59148 /// <summary>
60149 /// Applies the bracket operands. Executes the expressionBuilder with all the operands in the brackets.
61150 /// </summary>
@@ -68,39 +157,19 @@ public FunctionCallDefinition(string name, string regex,Func<Expression[], Expre
68157 /// <exception cref="OperationInvalidException">When an error occured while executing the expressionBuilder</exception>
69158 public override void ApplyBracketOperands ( Operator bracketOpen , Stack < Operand > bracketOperands , Operator bracketClose , ParseState state )
70159 {
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 ) ;
95164
96- }
165+ var overload = MatchOverload ( bracketOperands , out var functionArguments ) ;
97166
98- var functionSourceMap = StringSegment . Encompass ( bracketOpen . SourceMap , operandSource ) ;
167+
99168 var functionArgumentsArray = functionArguments . ToArray ( ) ;
100169 Expression output ;
101170 try
102171 {
103- output = ExpressionBuilder ( functionArgumentsArray ) ;
172+ output = overload . ExpressionBuilder ( functionArgumentsArray ) ;
104173 }
105174 catch ( Exception ex )
106175 {
0 commit comments