public static ResolvedType GetFunctionType(FunctionDefinition func) { if (!funcTypesByRef.ContainsKey(func)) { ResolvedType type = GetFunctionType(func.ResolvedReturnType, func.ResolvedArgTypes, FunctionCall.CountOptionalArgs(func.DefaultValues)); funcTypesByRef[func] = type; } return(funcTypesByRef[func]); }
/// <summary> /// Takes an arithmetical or logical expression and returns the corresponding variable /// <para/>Examples: /// <para/>* "5 + 6" : returns Integer (11) /// <para/>* "$l[5 * (1 - $i)]" : returns the elements at index 5*(1-i) in the list "l" /// <para/>* "$l" : returns the list variable l /// </summary> /// <param name="expr_string"> expression to parse</param> /// <returns> Variable object containing the value of the evaluated expression value (at time t)</returns> public static Variable parse(string expr_string) { /* Order of operations: * checking expression string integrity * raw dynamic list * clean redundant symbols * raw integer value * raw boolean value (not done yet) * raw float value (not done yet) * mathematical or logical operation * function call * variable access (e.g. $name or in list by index) */ // clean expression expr_string = StringUtils.normalizeWhiteSpaces(expr_string); Exception invalid_expr_exception = new AquilaExceptions.SyntaxExceptions.SyntaxError($"The sentence \"{expr_string}\" is not understood"); Debugging.print("input expression: " + expr_string); // matching parentheses & brackets Debugging.assert(StringUtils.checkMatchingDelimiters(expr_string, '(', ')'), new AquilaExceptions.SyntaxExceptions.UnclosedTagError("Unclosed parenthesis")); Debugging.assert(StringUtils.checkMatchingDelimiters(expr_string, '[', ']'), new AquilaExceptions.SyntaxExceptions.UnclosedTagError("Unclosed bracket")); expr_string = StringUtils.removeRedundantMatchingDelimiters(expr_string, '(', ')'); Debugging.print("dynamic list ?"); // dynamic list { DynamicList list = StringUtils.parseListExpression(expr_string); if (list != null) { return(list); } } // now that lists are over, check for redundant brackets expr_string = StringUtils.removeRedundantMatchingDelimiters(expr_string, '[', ']'); if (expr_string == null) { throw new AquilaExceptions.SyntaxExceptions.SyntaxError("Null Expression"); } Debugging.assert(expr_string != ""); //! NullValue here, instead of Exception Debugging.print("int ?"); // try evaluating expression as an integer if (int.TryParse(expr_string, out int int_value)) { return(new Integer(int_value, true)); } Debugging.print("bool ?"); // try evaluating expression as a boolean if (expr_string == "true") { return(new BooleanVar(true, true)); } if (expr_string == "false") { return(new BooleanVar(false, true)); } Debugging.print("float ?"); // try evaluating expression as float if (!expr_string.Contains(' ')) { if (float.TryParse(expr_string, out float float_value)) { Debugging.print("french/classic float"); return(new FloatVar(float_value, true)); } if (float.TryParse(expr_string.Replace('.', ','), out float_value)) { Debugging.print("normalized float"); return(new FloatVar(float_value, true)); } if (expr_string.EndsWith("f") && float.TryParse(expr_string.Substring(0, expr_string.Length - 1), out float_value)) { Debugging.print("f-float"); return(new FloatVar(float_value, true)); } if (expr_string.EndsWith("f") && float.TryParse(expr_string.Replace('.', ',').Substring(0, expr_string.Length - 1), out float_value)) { Debugging.print("f-float"); return(new FloatVar(float_value, true)); } } Debugging.print("checking for negative expression"); // special step: check for -(expr) if (expr_string.StartsWith("-")) { Debugging.print("evaluating expression without \"-\" sign"); string opposite_sign_expr = expr_string.Substring(1); // take away the "-" Variable opposite_sign_var = parse(opposite_sign_expr); Debugging.print("evaluated expression without the \"-\" symbol is of type ", opposite_sign_var.getTypeString(), " and value ", opposite_sign_var.getValue()); // ReSharper disable once ConvertIfStatementToSwitchExpression if (opposite_sign_var is Integer) { return(new Integer(-opposite_sign_var.getValue())); } if (opposite_sign_var is FloatVar) { return(new FloatVar(-opposite_sign_var.getValue())); } throw new AquilaExceptions.InvalidTypeError($"Cannot cast \"-\" on a {opposite_sign_var.getTypeString()} variable"); } Debugging.print("AL operations ?"); // mathematical and logical operations foreach (char op in Global.al_operations) { // ReSharper disable once PossibleNullReferenceException if (expr_string.Contains(op.ToString())) { string simplified = StringUtils.simplifyExpr(expr_string, new [] { op }); // only look for specific delimiter // more than one simplified expression ? if (simplified.Split(op).Length > 1) { Debugging.print("operation ", expr_string, " and op: ", op); List <string> splitted_str = StringUtils.splitStringKeepingStructureIntegrity(expr_string, op, Global.base_delimiters); // custom: logic operations laziness here (tmp) //! Variable variable = parse(splitted_str[0]); if (Global.getSetting("lazy logic") && variable is BooleanVar) { Debugging.print("lazy logic evaluation"); bool first = (variable as BooleanVar).getValue(); switch (op) { case '|': // when first: if (first) { return(new BooleanVar(true, true)); } break; case '&': // when !first: if (!first) { return(new BooleanVar(false, true)); } break; } } var splitted_var = new List <Variable> { variable }; splitted_var.AddRange(splitted_str.GetRange(1, splitted_str.Count - 1).Select(parse)); // reduce the list to a list of one element // e.g. expr1 + expr2 + expr3 => final_expr while (splitted_var.Count > 1) { // merge the two first expressions Variable expr1_var = splitted_var[0]; Variable expr2_var = splitted_var[1]; Variable result = applyOperator(expr1_var, expr2_var, op); // merge result of 0 and 1 splitted_var[0] = result; // remove 1 (part of it found in 0 now) splitted_var.RemoveAt(1); } return(splitted_var[0]); } } } Debugging.print("not (!) operator ?"); // '!' operator (only one to take one variable) if (expr_string.StartsWith("!")) { Debugging.assert(expr_string[1] == '('); Debugging.assert(expr_string[expr_string.Length - 1] == ')'); Variable expr = parse(expr_string.Substring(2, expr_string.Length - 3)); Debugging.assert(expr is BooleanVar); Debugging.print("base val b4 not operator is ", expr.getValue()); return(((BooleanVar)expr).not()); } Debugging.print("value function call ?"); // value function call if (expr_string.Contains("(")) { string function_name = expr_string.Split('(')[0]; // extract function name int func_call_length = function_name.Length; function_name = StringUtils.normalizeWhiteSpaces(function_name); Debugging.print("function name: ", function_name); Functions.assertFunctionExists(function_name); expr_string = expr_string.Substring(func_call_length); // remove function name expr_string = expr_string.Substring(1, expr_string.Length - 2); // remove parenthesis Debugging.print("expr_string for function call ", expr_string); var arg_list = new List <Expression>(); foreach (string arg_string in StringUtils.splitStringKeepingStructureIntegrity(expr_string, ',', Global.base_delimiters)) { string purged_arg_string = StringUtils.normalizeWhiteSpaces(arg_string); Expression arg_expr = new Expression(purged_arg_string); arg_list.Add(arg_expr); } if (arg_list.Count == 1 && arg_list[0].expr == "") { arg_list = new List <Expression>(); } Debugging.print("creating value function call with ", arg_list.Count, " parameters"); FunctionCall func_call = new FunctionCall(function_name, arg_list); return(func_call.callFunction()); } // function call without parenthesis -> no parameters either if (!expr_string.StartsWith(StringConstants.Other.VARIABLE_PREFIX) && !expr_string.Contains(' ')) { Debugging.print($"Call the function \"{expr_string}\" with no parameters"); var func_call = new FunctionCall(expr_string, new List <Expression>()); return(func_call.callFunction()); } Debugging.print("variable ?"); // variable access // since it is the last possibility for the parse call to return something, assert it is a variable Debugging.assert(expr_string.StartsWith(StringConstants.Other.VARIABLE_PREFIX), invalid_expr_exception); Debugging.print("list access ?"); // ReSharper disable once PossibleNullReferenceException if (expr_string.Contains("[")) { // brackets Debugging.assert(expr_string.EndsWith("]"), invalid_expr_exception); // cannot be "$l[0] + 5" bc AL_operations have already been processed int bracket_start_index = expr_string.IndexOf('['); Debugging.assert(bracket_start_index > 1, invalid_expr_exception); // "$[$i - 4]" is not valid // variable Expression var_name_expr = new Expression(expr_string.Substring(0, bracket_start_index)); Debugging.print("list name: " + var_name_expr.expr); // index list IEnumerable <string> index_list = StringUtils.getBracketsContent(expr_string.Substring(bracket_start_index)); string index_list_expr_string = index_list.Aggregate("", (current, s) => current + s + ", "); index_list_expr_string = "[" + index_list_expr_string.Substring(0, index_list_expr_string.Length - 2) + "]"; var index_list_expr = new Expression(index_list_expr_string); Debugging.print("index: " + index_list_expr.expr); // create a value function call (list_at call) object[] args = { var_name_expr, index_list_expr }; return(Functions.callFunctionByName("list_at", args)); } // only variable name, no brackets Debugging.print("var by name: ", expr_string); return(variableFromName(expr_string)); }
private Expression ParseEntity(TokenStream tokens, Node owner) { Expression root; Token firstToken = tokens.Peek(); AType castPrefix = firstToken.Value == "(" ? this.MaybeParseCastPrefix(tokens) : null; if (castPrefix != null) { root = this.ParseEntity(tokens, owner); return(new Cast(firstToken, castPrefix, root, owner, true)); } Token maybeOpenParen = tokens.Peek(); if (tokens.PopIfPresent("(")) { if (tokens.PopIfPresent(")")) { root = this.ParseLambda(tokens, firstToken, new AType[0], new Token[0], owner); } else { if (this.parser.CurrentScope.IsStaticallyTyped) { TokenStream.StreamState state = tokens.RecordState(); AType lambdaArgType = this.parser.TypeParser.TryParse(tokens); Token lambdaArg = null; if (lambdaArgType != null) { lambdaArg = tokens.PopIfWord(); } if (lambdaArg != null) { List <AType> lambdaArgTypes = new List <AType>() { lambdaArgType }; List <Token> lambdaArgs = new List <Token>() { lambdaArg }; while (tokens.PopIfPresent(",")) { lambdaArgTypes.Add(this.parser.TypeParser.Parse(tokens)); lambdaArgs.Add(tokens.PopWord()); } tokens.PopExpected(")"); return(this.ParseLambda(tokens, maybeOpenParen, lambdaArgTypes, lambdaArgs, owner)); } else { tokens.RestoreState(state); } } root = this.Parse(tokens, owner); if (root is Variable) { if (tokens.PopIfPresent(")")) { if (tokens.IsNext("=>")) { root = this.ParseLambda(tokens, firstToken, new AType[] { AType.Any(root.FirstToken) }, new Token[] { root.FirstToken }, owner); } } else if (tokens.IsNext(",")) { List <Token> lambdaArgs = new List <Token>() { root.FirstToken }; List <AType> lambdaArgTypes = new List <AType>() { AType.Any(root.FirstToken) }; Token comma = tokens.Peek(); while (tokens.PopIfPresent(",")) { Token nextArg = tokens.Pop(); if (nextArg.Type != TokenType.WORD) { throw new ParserException(comma, "Unexpected comma."); } lambdaArgTypes.Add(AType.Any(nextArg)); lambdaArgs.Add(nextArg); comma = tokens.Peek(); } tokens.PopExpected(")"); root = this.ParseLambda(tokens, firstToken, lambdaArgTypes, lambdaArgs, owner); } else { // This will purposely cause an unexpected token error // since it's none of the above conditions. tokens.PopExpected(")"); } } else { tokens.PopExpected(")"); } } } else { root = ParseEntityWithoutSuffixChain(tokens, owner); } bool anySuffixes = true; bool isPreviousADot = false; while (anySuffixes) { if (tokens.IsNext(".")) { isPreviousADot = true; Token dotToken = tokens.Pop(); Token fieldToken = tokens.Pop(); // HACK alert: "class" is a valid field on a class. // ParserVerifyIdentifier is invoked downstream for non-resolved fields. if (fieldToken.Value != this.parser.Keywords.CLASS) { this.parser.VerifyIdentifier(fieldToken); } root = new DotField(root, dotToken, fieldToken, owner); } else if (tokens.IsNext("[")) { Token openBracket = tokens.Pop(); List <Expression> sliceComponents = new List <Expression>(); if (tokens.IsNext(":")) { sliceComponents.Add(null); } else { sliceComponents.Add(Parse(tokens, owner)); } for (int i = 0; i < 2; ++i) { if (tokens.PopIfPresent(":")) { if (tokens.IsNext(":") || tokens.IsNext("]")) { sliceComponents.Add(null); } else { sliceComponents.Add(Parse(tokens, owner)); } } } tokens.PopExpected("]"); if (sliceComponents.Count == 1) { Expression index = sliceComponents[0]; root = new BracketIndex(root, openBracket, index, owner); } else { root = new ListSlice(root, sliceComponents, openBracket, owner); } } else if (tokens.IsNext("(")) { Token openParen = tokens.Pop(); List <Expression> args = new List <Expression>(); while (!tokens.PopIfPresent(")")) { if (args.Count > 0) { tokens.PopExpected(","); } args.Add(Parse(tokens, owner)); } root = new FunctionCall(root, openParen, args, owner); } else if (tokens.IsNext(this.parser.Keywords.IS)) { Token isToken = tokens.Pop(); Token classToken = tokens.Pop(); string className = this.parser.PopClassNameWithFirstTokenAlreadyPopped(tokens, classToken); root = new IsComparison(root, isToken, classToken, className, owner); } else if (isPreviousADot && this.parser.IsCSharpCompat && tokens.IsNext("<")) { TokenStream.StreamState s = tokens.RecordState(); Token openBracket = tokens.Pop(); AType funcType = this.parser.TypeParser.TryParse(tokens); List <AType> types = new List <AType>() { funcType }; if (funcType != null) { while (tokens.PopIfPresent(",")) { types.Add(this.parser.TypeParser.Parse(tokens)); } } if (funcType == null) { anySuffixes = false; tokens.RestoreState(s); } else if (!tokens.PopIfPresent(">") || !tokens.IsNext("(")) { anySuffixes = false; tokens.RestoreState(s); } else { // TODO(acrylic-conversion): do something with this types list. } } else { anySuffixes = false; } } return(root); }
private Expression ParseEntity(TokenStream tokens, TopLevelConstruct owner) { Expression root; if (tokens.PopIfPresent("(")) { root = this.Parse(tokens, owner); tokens.PopExpected(")"); } else { root = ParseEntityWithoutSuffixChain(tokens, owner); } bool anySuffixes = true; while (anySuffixes) { if (tokens.IsNext(".")) { Token dotToken = tokens.Pop(); Token stepToken = tokens.Pop(); // HACK alert: "class" is a valid field on a class. // ParserVerifyIdentifier is invoked downstream for non-resolved fields. if (stepToken.Value != this.parser.Keywords.CLASS) { this.parser.VerifyIdentifier(stepToken); } root = new DotStep(root, dotToken, stepToken, owner); } else if (tokens.IsNext("[")) { Token openBracket = tokens.Pop(); List <Expression> sliceComponents = new List <Expression>(); if (tokens.IsNext(":")) { sliceComponents.Add(null); } else { sliceComponents.Add(Parse(tokens, owner)); } for (int i = 0; i < 2; ++i) { if (tokens.PopIfPresent(":")) { if (tokens.IsNext(":") || tokens.IsNext("]")) { sliceComponents.Add(null); } else { sliceComponents.Add(Parse(tokens, owner)); } } } tokens.PopExpected("]"); if (sliceComponents.Count == 1) { Expression index = sliceComponents[0]; root = new BracketIndex(root, openBracket, index, owner); } else { root = new ListSlice(root, sliceComponents, openBracket, owner); } } else if (tokens.IsNext("(")) { Token openParen = tokens.Pop(); List <Expression> args = new List <Expression>(); while (!tokens.PopIfPresent(")")) { if (args.Count > 0) { tokens.PopExpected(","); } args.Add(Parse(tokens, owner)); } root = new FunctionCall(root, openParen, args, owner); } else if (tokens.IsNext(this.parser.Keywords.IS)) { Token isToken = tokens.Pop(); Token classToken = tokens.Pop(); string className = this.parser.PopClassNameWithFirstTokenAlreadyPopped(tokens, classToken); root = new IsComparison(root, isToken, classToken, className, owner); } else { anySuffixes = false; } } return(root); }