/// <summary> /// Initializes a new instance of the <see cref="CSharpFunctionCall" /> class by parsing a function call from /// <paramref name="callString" /> /// </summary> /// <param name="callString">The full call signature to parse.</param> /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="callString" /> is <c>null</c>.</exception> /// <exception cref="System.ArgumentException"> /// Thrown if <see cref="CSharpFunctionCallValidator.Validate" /> fails to /// successfully parse <paramref name="callString" />. /// </exception> public CSharpFunctionCall(string callString) { if (callString == null) { throw new ArgumentNullException("callString", "Inheritance list signature string cannot be null."); } _validatedSignature = CSharpFunctionCallValidator.Validate(callString); if (!_validatedSignature.Success) { throw new ArgumentException(_validatedSignature.ErrorDescription, "callString"); } _fullSignature = callString; }
/// <summary> /// Parses and validates a string as a CSharp method call. /// </summary> /// <param name="signature">The method call signature, without a semi-colon at the end.</param> /// <returns>A parse/validation results object. <see cref="CSharpFunctionCallValidationResult" /></returns> /// <exception cref="ArgumentNullException"><paramref name="signature"/> is <c>null</c>.</exception> public static CSharpFunctionCallValidationResult Validate(string signature) { if (signature == null) { throw new ArgumentNullException("signature"); } var result = new CSharpFunctionCallValidationResult { Success = true }; var explicitGenericParameters = new GenericArray <CSharpClassNameValidationResult>(); var parameters = new GenericArray <CSharpParameterSignature>(); var lastModifier = CSharpParameterModifier.None; var state = States.WaitingForFirstCharacter; var stateBeforeBrackets = new Stack <States>(); var stateBeforeGenericTypePart = new Stack <States>(); var stateBeforeParenthesis = new Stack <States>(); var stateBeforeCurlyBraces = new Stack <States>(); string accum = ""; int unmatchedGenericBrackets = 0; int unmatchedParenthesis = 0; int unmatchedCurlyBraces = 0; int unmatchedBrackets = 0; string methodName = ""; for (int index = 0; index < signature.Length; index++) { var c = signature[index]; accum += c; if (c == 'o' || c == 'r' || c == 'n') { int wordBoundry = index + 2; int ahead = wordBoundry + 1; if (wordBoundry >= signature.Length) { goto afterRefOutChecks; } var lookAhead = signature.Substring(index, 3); var lookAheadAssertion = lookAhead == "out" || lookAhead == "ref" || lookAhead == "new"; if (ahead < signature.Length && !char.IsWhiteSpace(signature[ahead])) { goto afterRefOutChecks; } if (lookAheadAssertion && state == States.WaitingForParameterName && lastModifier == CSharpParameterModifier.None) { lastModifier = lookAhead == "out" ? CSharpParameterModifier.Out : (lookAhead == "ref" ? CSharpParameterModifier.Ref : CSharpParameterModifier.New); accum = ""; index = wordBoundry; state = States.WaitingForParameterName; continue; } } afterRefOutChecks: if (c == '[') { if (state == States.AccumulatingParameter || state == States.WaitingForParameterName) { stateBeforeBrackets.Push(States.AccumulatingParameter); state = States.InBracketExpression; unmatchedBrackets++; continue; } if (state == States.InBracketExpression || state == States.InCurlyBraceExpression || state == States.InParenthesizedExpression) { unmatchedBrackets++; continue; } } if (c == ']') { if (state == States.InBracketExpression || state == States.InCurlyBraceExpression || state == States.InParenthesizedExpression) { unmatchedBrackets--; if (unmatchedBrackets < 0) { result.Success = false; result.ErrorDescription = "Unexpected closing bracket."; result.ErrorIndex = index; return(result); } if (unmatchedBrackets == 0) { if (stateBeforeBrackets.Count > 0) { state = stateBeforeBrackets.Pop(); } else if (state == States.InBracketExpression) { goto unexpectedBracket; } } continue; unexpectedBracket: result.Success = false; result.ErrorDescription = "Unexpected closing bracket."; result.ErrorIndex = index; return(result); } } if (c == '{') { if (state == States.AccumulatingParameter || state == States.WaitingForParameterName) { stateBeforeCurlyBraces.Push(States.AccumulatingParameter); state = States.InCurlyBraceExpression; unmatchedCurlyBraces++; continue; } if (state == States.InBracketExpression || state == States.InCurlyBraceExpression || state == States.InParenthesizedExpression) { unmatchedCurlyBraces++; continue; } } if (c == '}') { if (state == States.InBracketExpression || state == States.InCurlyBraceExpression || state == States.InParenthesizedExpression) { unmatchedCurlyBraces--; if (unmatchedCurlyBraces < 0) { result.Success = false; result.ErrorDescription = "Unexpected closing brace."; result.ErrorIndex = index; return(result); } if (unmatchedCurlyBraces == 0) { if (stateBeforeCurlyBraces.Count > 0) { state = stateBeforeCurlyBraces.Pop(); } else if (state == States.InCurlyBraceExpression) { goto unexpectedCurlyBrace; } } continue; } unexpectedCurlyBrace: result.Success = false; result.ErrorDescription = "Unexpected closing brace."; result.ErrorIndex = index; return(result); } if (c == ')') { if (state == States.WaitingForParameterName && parameters.Count == 0) { if (lastModifier == CSharpParameterModifier.None) { state = States.AfterSignature; accum = ""; } //otherwise the signature is incomplete continue; } if (state == States.InBracketExpression || state == States.InCurlyBraceExpression || state == States.InParenthesizedExpression) { unmatchedParenthesis--; if (unmatchedParenthesis < 0) { result.Success = false; result.ErrorDescription = "Unexpected closing parenthesis."; result.ErrorIndex = index; return(result); } if (unmatchedParenthesis == 0) { if (stateBeforeParenthesis.Count > 0) { state = stateBeforeParenthesis.Pop(); } else if (state == States.InParenthesizedExpression) { goto unexpectedParenth; } } continue; } if (state == States.AccumulatingParameter) { var param = accum.Substring(0, accum.Length - 1).Trim(); if (!CSharpIDValidator.IsValidIdentifier(param) && (lastModifier == CSharpParameterModifier.Ref || lastModifier == CSharpParameterModifier.Out)) { result.Success = false; result.ErrorDescription = "'ref' and 'out' can only be used with a direct variable reference."; result.ErrorIndex = index - (accum.Length - 1); return(result); } string err; int errIndex; if (lastModifier == CSharpParameterModifier.New && !IsValidNewParameter(param, index - (accum.Length - 1), out err, out errIndex)) { result.Success = false; result.ErrorDescription = err; result.ErrorIndex = errIndex; return(result); } if (lastModifier == CSharpParameterModifier.None && !IsValidPlainParameter(param, index - (accum.Length - 1), out err, out errIndex)) { result.Success = false; result.ErrorDescription = err; result.ErrorIndex = errIndex; return(result); } parameters.Add(new CSharpParameterSignature(param, lastModifier)); accum = ""; lastModifier = CSharpParameterModifier.None; state = States.AfterSignature; continue; } unexpectedParenth: result.Success = false; result.ErrorDescription = "Unexpected closing parenthesis."; result.ErrorIndex = index; return(result); } if (c == '(') { if (state == States.AccumulatingParameter || state == States.WaitingForParameterName) { stateBeforeParenthesis.Push(States.AccumulatingParameter); state = States.InParenthesizedExpression; unmatchedParenthesis++; continue; } if (state == States.AfterExplicitGenericMethodParameters) { accum = ""; state = States.WaitingForParameterName; continue; } if (state == States.AccumulatingMethodNamePart) { methodName = accum.TrimEnd('(').Trim(); accum = ""; state = States.WaitingForParameterName; continue; } if (state == States.InBracketExpression || state == States.InCurlyBraceExpression || state == States.InParenthesizedExpression) { unmatchedParenthesis++; continue; } result.Success = false; result.ErrorDescription = "Unexpected opening parenthesis."; result.ErrorIndex = index; return(result); } if (c == ',') { if (state == States.InBracketExpression || state == States.InCurlyBraceExpression || state == States.InParenthesizedExpression) { continue; } if (state == States.AccumulatingExplicitGenericMethodParameters) { var param = accum.TrimEnd(',').Trim(); var validate = CSharpClassNameValidator.ValidateInitialization(param, true); if (!validate.Success) { result.Success = false; result.ErrorDescription = validate.ErrorDescription; result.ErrorIndex = (index - (accum.Length - 1)) + validate.ErrorIndex; return(result); } explicitGenericParameters.Add(validate); accum = ""; continue; } if (state == States.AccumulatingParameter) { var param = accum.TrimEnd(',').Trim(); if (!CSharpIDValidator.IsValidIdentifier(param) && (lastModifier == CSharpParameterModifier.Ref || lastModifier == CSharpParameterModifier.Out)) { result.Success = false; result.ErrorDescription = "'ref' and 'out' can only be used with a direct variable reference."; result.ErrorIndex = index - (accum.Length - 1); return(result); } string err; int errIndex; if (lastModifier == CSharpParameterModifier.New && !IsValidNewParameter(param, index - (accum.Length - 1), out err, out errIndex)) { result.Success = false; result.ErrorDescription = err; result.ErrorIndex = errIndex; return(result); } if (lastModifier == CSharpParameterModifier.None && !IsValidPlainParameter(param, index - (accum.Length - 1), out err, out errIndex)) { result.Success = false; result.ErrorDescription = err; result.ErrorIndex = errIndex; return(result); } state = States.WaitingForParameterName; parameters.Add(new CSharpParameterSignature(param, lastModifier)); lastModifier = CSharpParameterModifier.None; accum = ""; continue; } result.Success = false; result.ErrorDescription = "Unexpected ',' character."; result.ErrorIndex = index; return(result); } if (c == '<') { if (state == States.AccumulatingParameter || state == States.InCurlyBraceExpression || state == States.InParenthesizedExpression || state == States.InBracketExpression) { continue; } if (state == States.AccumulatingMethodNamePart) { methodName = accum.TrimEnd('<').Trim(); accum = ""; state = States.AccumulatingExplicitGenericMethodParameters; continue; } if (state == States.AccumulatingExplicitGenericMethodParameters) { unmatchedGenericBrackets++; stateBeforeGenericTypePart.Push(state); state = States.AccumulatingGenericTypePart; continue; } if (state == States.AccumulatingGenericTypePart) { unmatchedGenericBrackets++; continue; } result.Success = false; result.ErrorDescription = "Unexpected '<' character."; result.ErrorIndex = index; return(result); } if (c == '>') { if (state == States.AccumulatingParameter || state == States.InCurlyBraceExpression || state == States.InParenthesizedExpression || state == States.InBracketExpression) { continue; } if (state == States.AccumulatingGenericTypePart) { unmatchedGenericBrackets--; if (unmatchedGenericBrackets == 0) { state = stateBeforeGenericTypePart.Pop(); } continue; } if (state == States.AccumulatingExplicitGenericMethodParameters) { var param = accum.Substring(0, accum.Length > 0 ? accum.Length - 1 : 0).Trim(); var validate = CSharpClassNameValidator.ValidateInitialization(param, true); if (!validate.Success) { result.Success = false; result.ErrorDescription = validate.ErrorDescription; result.ErrorIndex = (index - (accum.Length - 1)) + validate.ErrorIndex; return(result); } explicitGenericParameters.Add(validate); accum = ""; state = States.AfterExplicitGenericMethodParameters; continue; } result.Success = false; result.ErrorDescription = "Unexpected '>' character."; result.ErrorIndex = index; return(result); } if (!char.IsWhiteSpace(c)) { if (state == States.AccumulatingParameter || state == States.AccumulatingExplicitGenericMethodParameters || state == States.AccumulatingMethodNamePart || state == States.AccumulatingGenericTypePart || state == States.InCurlyBraceExpression || state == States.InBracketExpression || state == States.InParenthesizedExpression) { continue; } if (state == States.WaitingForParameterName) { accum = "" + c; state = States.AccumulatingParameter; continue; } if (state == States.WaitingForFirstCharacter) { accum = "" + c; state = States.AccumulatingMethodNamePart; continue; } result.Success = false; result.ErrorDescription = string.Format("Unexpected '{0}' character.", c); result.ErrorIndex = index; return(result); } } if (state != States.AfterSignature) { result.Success = false; result.ErrorDescription = "Call signature incomplete."; result.ErrorIndex = signature.Length - 1; return(result); } result.MethodName = methodName; result.Parameters = parameters; result.ExplicitGenericParameters = explicitGenericParameters; return(result); }