private bool GetSingleParameter(FunctionDefinitionExpression function, InterpreterScope parameterScope, out ExpressionBase error) { var funcParameter = function.Parameters.First(); ExpressionBase value = Parameters.First(); if (value.IsConstant) { // already a basic type, just proceed to storing it error = null; } else { var assignedParameter = value as AssignmentExpression; if (assignedParameter == null) { assignedParameter = new AssignmentExpression(new VariableExpression(funcParameter.Name), value); } else if (funcParameter.Name != assignedParameter.Variable.Name) { error = new ParseErrorExpression(String.Format("'{0}' does not have a '{1}' parameter", function.Name.Name, assignedParameter.Variable.Name), value); return(true); } value = GetParameter(parameterScope, parameterScope, assignedParameter); error = value as ParseErrorExpression; if (error != null) { return(true); } } parameterScope.DefineVariable(new VariableDefinitionExpression(funcParameter.Name), value); return(true); }
/// <summary> /// Adds the function definition. /// </summary> public void AddFunction(FunctionDefinitionExpression function) { if (_functions == null) { _functions = new Dictionary <string, FunctionDefinitionExpression>(); } _functions[function.Name.Name] = function; }
/// <summary> /// Creates a new scope for calling a function and populates values for parameters passed to the function. /// </summary> /// <param name="function">The function defining the parameters to populate.</param> /// <param name="scope">The outer scope containing the function call.</param> /// <param name="error">[out] A <see cref="ParseErrorExpression"/> indicating why constructing the new scope failed.</param> /// <returns>The new scope, <c>null</c> if an error occurred - see <paramref name="error"/> for error details.</returns> public InterpreterScope GetParameters(FunctionDefinitionExpression function, InterpreterScope scope, out ExpressionBase error) { var parameterScope = function.CreateCaptureScope(scope); // optimization for no parameter function if (function.Parameters.Count == 0 && Parameters.Count == 0) { error = null; return(parameterScope); } // optimization for single parameter function if (function.Parameters.Count == 1 && Parameters.Count == 1) { if (GetSingleParameter(function, parameterScope, out error)) { return(parameterScope); } if (error != null) { return(null); } } var providedParameters = new List <string>(function.Parameters.Count); foreach (var parameter in function.Parameters) { providedParameters.Add(parameter.Name); } ArrayExpression varargs = null; if (providedParameters.Remove("...")) { varargs = new ArrayExpression(); parameterScope.DefineVariable(new VariableDefinitionExpression("varargs"), varargs); } var parameterCount = providedParameters.Count; int index = 0; bool namedParameters = false; foreach (var parameter in Parameters) { var assignedParameter = parameter as AssignmentExpression; if (assignedParameter != null) { if (!providedParameters.Remove(assignedParameter.Variable.Name)) { if (!function.Parameters.Any(p => p.Name == assignedParameter.Variable.Name)) { error = new ParseErrorExpression(String.Format("'{0}' does not have a '{1}' parameter", function.Name.Name, assignedParameter.Variable.Name), parameter); return(null); } error = new ParseErrorExpression(String.Format("'{0}' already has a value", assignedParameter.Variable.Name), assignedParameter.Variable); return(null); } var value = GetParameter(parameterScope, scope, assignedParameter); error = value as ParseErrorExpression; if (error != null) { return(null); } parameterScope.DefineVariable(new VariableDefinitionExpression(assignedParameter.Variable), value); namedParameters = true; } else { if (namedParameters) { error = new ParseErrorExpression("Non-named parameter following named parameter", parameter); return(null); } if (index >= parameterCount && varargs == null) { error = new ParseErrorExpression("Too many parameters passed to function", parameter); return(null); } var variableName = (index < parameterCount) ? function.Parameters.ElementAt(index).Name : "..."; assignedParameter = new AssignmentExpression(new VariableExpression(variableName), parameter); var value = GetParameter(parameterScope, scope, assignedParameter); error = value as ParseErrorExpression; if (error != null) { return(null); } if (index < parameterCount) { providedParameters.Remove(variableName); parameterScope.DefineVariable(new VariableDefinitionExpression(variableName), value); } else { varargs.Entries.Add(value); } } ++index; } foreach (var parameter in providedParameters) { ExpressionBase value; if (!function.DefaultParameters.TryGetValue(parameter, out value)) { error = new ParseErrorExpression(String.Format("Required parameter '{0}' not provided", parameter), FunctionName); return(null); } var assignmentScope = new InterpreterScope(scope) { Context = new AssignmentExpression(new VariableExpression(parameter), value) }; if (!value.ReplaceVariables(assignmentScope, out value)) { error = new ParseErrorExpression(value, this); return(null); } parameterScope.DefineVariable(new VariableDefinitionExpression(parameter), value); } error = null; return(parameterScope); }
/// <summary> /// Adds the function definition. /// </summary> public void AddFunction(FunctionDefinitionExpression function) { _functions[function.Name.Name] = function; }
/// <summary> /// Parses a function definition. /// </summary> /// <remarks> /// Assumes the 'function' keyword has already been consumed. /// </remarks> internal static ExpressionBase Parse(PositionalTokenizer tokenizer, int line = 0, int column = 0) { var function = new FunctionDefinitionExpression(); function._keyword = new KeywordExpression("function", line, column); ExpressionBase.SkipWhitespace(tokenizer); line = tokenizer.Line; column = tokenizer.Column; var functionName = tokenizer.ReadIdentifier(); function.Name = new VariableDefinitionExpression(functionName.ToString(), line, column); if (functionName.IsEmpty) { ExpressionBase.ParseError(tokenizer, "Invalid function name"); return(function); } ExpressionBase.SkipWhitespace(tokenizer); if (tokenizer.NextChar != '(') { ExpressionBase.ParseError(tokenizer, "Expected '(' after function name", function.Name); return(function); } tokenizer.Advance(); ExpressionBase.SkipWhitespace(tokenizer); if (tokenizer.NextChar != ')') { do { line = tokenizer.Line; column = tokenizer.Column; var parameter = tokenizer.ReadIdentifier(); if (parameter.IsEmpty) { ExpressionBase.ParseError(tokenizer, "Invalid parameter name", line, column); return(function); } function.Parameters.Add(new VariableDefinitionExpression(parameter.ToString(), line, column)); ExpressionBase.SkipWhitespace(tokenizer); if (tokenizer.NextChar == ')') { break; } if (tokenizer.NextChar != ',') { ExpressionBase.ParseError(tokenizer, "Expected ',' or ')' after parameter name, found: " + tokenizer.NextChar); return(function); } tokenizer.Advance(); ExpressionBase.SkipWhitespace(tokenizer); } while (true); } tokenizer.Advance(); // closing parenthesis ExpressionBase.SkipWhitespace(tokenizer); ExpressionBase expression; if (tokenizer.Match("=>")) { ExpressionBase.SkipWhitespace(tokenizer); expression = ExpressionBase.Parse(tokenizer); if (expression.Type == ExpressionType.ParseError) { return(expression); } if (expression.Type == ExpressionType.Return) { return(new ParseErrorExpression("Return statement is implied by =>", ((ReturnExpression)expression).Keyword)); } var returnExpression = new ReturnExpression(expression); function.Expressions.Add(returnExpression); return(function); } if (tokenizer.NextChar != '{') { ExpressionBase.ParseError(tokenizer, "Expected '{' after function declaration", function.Name); return(function); } line = tokenizer.Line; column = tokenizer.Column; tokenizer.Advance(); ExpressionBase.SkipWhitespace(tokenizer); bool seenReturn = false; while (tokenizer.NextChar != '}') { expression = ExpressionBase.Parse(tokenizer); if (expression.Type == ExpressionType.ParseError) { return(expression); } if (expression.Type == ExpressionType.Return) { seenReturn = true; } else if (seenReturn) { ExpressionBase.ParseError(tokenizer, "Expression after return statement", expression); } function.Expressions.Add(expression); ExpressionBase.SkipWhitespace(tokenizer); } tokenizer.Advance(); return(function); }
private static ExpressionBase ParseClauseCore(PositionalTokenizer tokenizer) { ExpressionBase clause; switch (tokenizer.NextChar) { case '!': tokenizer.Advance(); clause = ParseClause(tokenizer); if (clause.Type == ExpressionType.ParseError) { return(clause); } return(new ConditionalExpression(null, ConditionalOperation.Not, clause)); case '(': tokenizer.Advance(); clause = ExpressionBase.Parse(tokenizer); if (clause.Type == ExpressionType.ParseError) { return(clause); } if (tokenizer.NextChar != ')') { if (tokenizer.NextChar == '\0') { return(ParseError(tokenizer, "No closing parenthesis found")); } return(ParseError(tokenizer, "Expected closing parenthesis, found: " + tokenizer.NextChar)); } clause.IsLogicalUnit = true; tokenizer.Advance(); return(clause); case '"': try { var stringValue = tokenizer.ReadQuotedString().ToString(); return(new StringConstantExpression(stringValue)); } catch (InvalidOperationException ex) { return(ParseError(tokenizer, ex.Message)); } case '0': if (tokenizer.Match("0x")) { string hexNumber = ""; while (Char.IsDigit(tokenizer.NextChar) || (tokenizer.NextChar >= 'A' && tokenizer.NextChar <= 'F') || (tokenizer.NextChar >= 'a' && tokenizer.NextChar <= 'f')) { hexNumber += tokenizer.NextChar; tokenizer.Advance(); } int hexValue = 0; Int32.TryParse(hexNumber, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.CurrentCulture, out hexValue); return(new IntegerConstantExpression(hexValue)); } goto case '1'; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { var number = tokenizer.ReadNumber(); uint value; UInt32.TryParse(number.ToString(), out value); return(new IntegerConstantExpression((int)value)); } case '-': tokenizer.Advance(); if (tokenizer.NextChar >= '0' && tokenizer.NextChar <= '9') { if (tokenizer.Match("0x")) { string hexNumber = ""; while (Char.IsDigit(tokenizer.NextChar) || (tokenizer.NextChar >= 'A' && tokenizer.NextChar <= 'F') || (tokenizer.NextChar >= 'a' && tokenizer.NextChar <= 'f')) { hexNumber += tokenizer.NextChar; tokenizer.Advance(); } int hexValue = 0; Int32.TryParse(hexNumber, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.CurrentCulture, out hexValue); return(new IntegerConstantExpression(-hexValue)); } var number = tokenizer.ReadNumber(); int value; Int32.TryParse(number.ToString(), out value); return(new IntegerConstantExpression(-value)); } return(ParseError(tokenizer, "Minus without value")); case '{': tokenizer.Advance(); return(DictionaryExpression.Parse(tokenizer)); case '[': tokenizer.Advance(); return(ParseArray(tokenizer)); default: var line = tokenizer.Line; var column = tokenizer.Column; var identifier = tokenizer.ReadIdentifier(); if (identifier.IsEmpty) { var error = ParseError(tokenizer, "Unexpected character: " + tokenizer.NextChar); tokenizer.Advance(); return(error); } SkipWhitespace(tokenizer); if (identifier == "return") { clause = ExpressionBase.Parse(tokenizer); if (clause.Type == ExpressionType.ParseError) { return(clause); } return(new ReturnExpression(new KeywordExpression(identifier.ToString(), line, column), clause)); } if (identifier == "function") { return(FunctionDefinitionExpression.Parse(tokenizer, line, column)); } if (identifier == "for") { return(ForExpression.Parse(tokenizer, line, column)); } if (identifier == "if") { return(IfExpression.Parse(tokenizer, line, column)); } if (tokenizer.NextChar == '(') { tokenizer.Advance(); var parameters = new List <ExpressionBase>(); ParseParameters(tokenizer, parameters); var functionCall = new FunctionCallExpression(new VariableExpression(identifier.ToString(), line, column), parameters); functionCall.EndLine = tokenizer.Line; functionCall.EndColumn = tokenizer.Column - 1; return(functionCall); } if (tokenizer.NextChar == '[') { IndexedVariableExpression parent = null; do { tokenizer.Advance(); var index = ExpressionBase.Parse(tokenizer); if (index.Type == ExpressionType.ParseError) { return(index); } SkipWhitespace(tokenizer); if (tokenizer.NextChar != ']') { return(ParseError(tokenizer, "Expecting closing bracket after index")); } tokenizer.Advance(); SkipWhitespace(tokenizer); if (parent != null) { parent = new IndexedVariableExpression(parent, index); } else { parent = new IndexedVariableExpression(new VariableExpression(identifier.ToString(), line, column), index); } } while (tokenizer.NextChar == '['); return(parent); } return(new VariableExpression(identifier.ToString(), line, column)); } }