/// <summary> /// Get the function or operator name of this function call /// </summary> public static string GetFunction(this ParseTreeNode input) { if (input.IsIntersection()) { return(GrammarNames.TokenIntersect); } if (input.IsUnion()) { return(GrammarNames.TokenUnionOperator); } if (input.IsBinaryOperation() || input.IsUnaryPostfixOperation()) { return(input.ChildNodes[1].Print()); } if (input.IsUnaryPrefixOperation()) { return(input.ChildNodes[0].Print()); } if (input.IsNamedFunction()) { return(RemoveFinalSymbol(input.ChildNodes[0].Print()).ToUpper()); } if (input.IsExternalUDFunction()) { return($"{input.ChildNodes[0].Print()}{GetFunction(input.ChildNodes[1])}"); } throw new ArgumentException("Not a function call", nameof(input)); }
/// <summary> /// Change a C# parse tree to a F# AST /// </summary> public static FSharpTransform.Formula CreateFSharpTree(this ParseTreeNode input) { if (input.IsParentheses()) { return(FSharpTransform.Formula.NewFunction("", ListModule.OfSeq(new [] { CreateFSharpTree(input.ChildNodes[0]) }))); } input = input.SkipToRelevant(); switch (input.Type()) { case GrammarNames.FunctionCall: case GrammarNames.ReferenceFunctionCall: case GrammarNames.UDFunctionCall: var fname = input.GetFunction() + (input.IsNamedFunction()?"(":""); var args = ListModule.OfSeq(input.GetFunctionArguments().Select(CreateFSharpTree)); // Check for range if (fname == ":") { return(makeFSharpRange(input)); } return(FSharpTransform.makeFormula(fname, args)); case GrammarNames.Reference: // ignore prefix return(CreateFSharpTree(input.ChildNodes.Count == 1 ? input.ChildNodes[0] : input.ChildNodes[1])); case GrammarNames.Cell: var L = new Location(input.Print()); return(FSharpTransform.makeSuperCell(FSharpTransform.makeCell(L.Column, L.Row))); case GrammarNames.NamedRange: return(FSharpTransform.makeNamedRange(input.Print())); case TransformationRuleGrammar.Names.DynamicCell: //get variables from dynamic cell return(FSharpTransform.makeSuperCell(GetDynamicCell(input))); case TransformationRuleGrammar.Names.DynamicRange: var letter = input // DynamicRange .ChildNodes[0] // LowLetter .Token.ValueString[0]; return(FSharpTransform.makeDRange(letter)); case GrammarNames.Constant: case GrammarNames.Number: case GrammarNames.Text: case GrammarNames.Bool: case GrammarNames.Error: case GrammarNames.RefError: return(FSharpTransform.makeConstant(input.Print())); case TransformationRuleGrammar.Names.DynamicConstant: return(FSharpTransform.makeDArgument(input.ChildNodes[0].Token.ValueString[1])); default: throw new ArgumentException($"Can't convert node type {input.Type()}", nameof(input)); } }
private static bool IsTargetFunction(ParseTreeNode node) { return // Not interested in not-functions (node.IsNamedFunction() // Or functions without arguments && node.ChildNodes[1].ChildNodes.Any() && (varargsFunctions.Contains(node.GetFunction()) // Functions have an arrayasargument parameter || node.GetFunctionArguments().Any(n => n.SkipToRelevant().IsUnion()) ) ); }
/// <summary> /// Get all the arguments of a function or operation /// </summary> public static IEnumerable <ParseTreeNode> GetFunctionArguments(this ParseTreeNode input) { if (input.IsNamedFunction()) { return(input .ChildNodes[1] // "Arguments" non-terminal .ChildNodes // "Argument" non-terminals .Select(node => node.ChildNodes[0]) ); } if (input.IsBinaryOperation()) { return(new[] { input.ChildNodes[0], input.ChildNodes[2] }); } if (input.IsUnaryPrefixOperation()) { return(new[] { input.ChildNodes[1] }); } if (input.IsUnaryPostfixOperation()) { return(new[] { input.ChildNodes[0] }); } if (input.IsUnion()) { return(input.ChildNodes[0].ChildNodes); } if (input.IsExternalUDFunction()) { return(input // Reference .ChildNodes[1] // UDFunctionCall .ChildNodes[1] // Arguments .ChildNodes // Argument non-terminals .Select(node => node.ChildNodes[0]) ); } throw new ArgumentException("Not a function call", nameof(input)); }
/// <summary> /// Pretty-print a parse tree to a string /// </summary> public static string Print(this ParseTreeNode input) { // For terminals, just print the token text if (input.Term is Terminal) { return(input.Token.Text); } // (Lazy) enumerable for printed children var children = input.ChildNodes.Select(Print); // Concrete list when needed List <string> childrenList; // Switch on non-terminals switch (input.Term.Name) { case GrammarNames.Formula: // Check if these are brackets, otherwise print first child return(IsParentheses(input) ? $"({children.First()})" : children.First()); case GrammarNames.FunctionCall: case GrammarNames.ReferenceFunctionCall: case GrammarNames.UDFunctionCall: childrenList = children.ToList(); if (input.IsNamedFunction()) { return(string.Join("", childrenList) + ")"); } if (input.IsBinaryOperation()) { // format string for "normal" binary operation string format = "{0} {1} {2}"; if (input.IsIntersection()) { format = "{0} {2}"; } else if (input.IsBinaryReferenceOperation()) { format = "{0}{1}{2}"; } return(string.Format(format, childrenList[0], childrenList[1], childrenList[2])); } if (input.IsUnion()) { return($"({string.Join(",", childrenList)})"); } if (input.IsUnaryOperation()) { return(string.Join("", childrenList)); } throw new ArgumentException("Unknown function type."); case GrammarNames.Reference: return(IsParentheses(input) ? $"({children.First()})" : string.Concat(children)); case GrammarNames.Prefix: var ret = string.Join("", children); // The exclamation mark token is not included in the parse tree, so we have to add that if it's a single file if (input.ChildNodes.Count == 1 && input.ChildNodes[0].Is(GrammarNames.File)) { ret += "!"; } return(ret); case GrammarNames.ArrayFormula: return("{=" + children.ElementAt(1) + "}"); case GrammarNames.StructuredReference: var sb = new StringBuilder(); var hashtable = input.ChildNodes.Count >= 1 && input.ChildNodes[0].Is(GrammarNames.StructuredReferenceTable); var contentsNode = hashtable ? 1 : 0; childrenList = children.ToList(); if (hashtable) { sb.Append(childrenList[0]); } if (hashtable && input.ChildNodes.Count == 1) { // Full table reference sb.Append("[]"); } else if (input.ChildNodes[contentsNode].Is(GrammarNames.StructuredReferenceElement)) { sb.Append(childrenList[contentsNode]); } else { sb.Append($"[{childrenList[contentsNode]}]"); } return(sb.ToString()); // Terms for which to print all child nodes concatenated case GrammarNames.ArrayConstant: case GrammarNames.DynamicDataExchange: case GrammarNames.FormulaWithEq: case GrammarNames.File: case GrammarNames.StructuredReferenceExpression: return(string.Join("", children)); // Terms for which we print the children comma-separated case GrammarNames.Arguments: case GrammarNames.ArrayRows: case GrammarNames.Union: return(string.Join(",", children)); case GrammarNames.ArrayColumns: return(string.Join(";", children)); case GrammarNames.ConstantArray: return($"{{{children.First()}}}"); default: // If it is not defined above and the number of children is exactly one, we want to just print the first child if (input.ChildNodes.Count == 1) { return(children.First()); } throw new ArgumentException($"Could not print node of type '{input.Term.Name}'." + Environment.NewLine + "This probably means the Excel grammar was modified without the print function being modified"); } }
/// <summary> /// Pretty-print a parse tree to a string /// </summary> public static string Print(this ParseTreeNode input) { // For terminals, just print the token text if (input.Term is Terminal) { return(input.Token.Text); } // (Lazy) enumerable for printed childs var childs = input.ChildNodes.Select(Print); // Concrete list when needed List <String> childsL; // Switch on nonterminals switch (input.Term.Name) { case GrammarNames.Formula: // Check if these are brackets, otherwise print first child return(IsParentheses(input) ? String.Format("({0})", childs.First()) : childs.First()); case GrammarNames.FunctionCall: case GrammarNames.ReferenceFunctionCall: case GrammarNames.UDFunctionCall: childsL = childs.ToList(); if (input.IsNamedFunction()) { return(String.Join("", childsL) + ")"); } if (input.IsBinaryOperation()) { // format string for "normal" binary operation string format = "{0} {1} {2}"; if (input.IsIntersection()) { format = "{0} {2}"; } else if (input.IsBinaryReferenceOperation()) { format = "{0}{1}{2}"; } return(String.Format(format, childsL[0], childsL[1], childsL[2])); } if (input.IsUnion()) { return(String.Format("({0})", String.Join(",", childsL))); } if (input.IsUnaryOperation()) { return(String.Join("", childsL)); } throw new ArgumentException("Unknown function type."); case GrammarNames.Reference: /*if (IsParentheses(input) || IsUnion(input)) * { * return String.Format("({0})", childs.First()); * } * * childsL = childs.ToList(); * if (IsIntersection(input)) * { * return String.Format("{0} {1}", childsL[0], childsL[2]); * } * * if (IsBinaryOperation(input)) * { * return String.Format("{0}{1}{2}", childsL[0], childsL[1], childsL[2]); * }*/ if (IsParentheses(input)) { return(String.Format("({0})", childs.First())); } return(String.Join("", childs)); case GrammarNames.File: return(String.Format("[{0}]", childs.First())); case GrammarNames.Prefix: var ret = String.Join("", childs); // The exclamation mark token is not included in the parse tree, so we have to add that if it's a single file if (input.ChildNodes.Count == 1 && input.ChildNodes[0].Is(GrammarNames.File)) { ret += "!"; } return(ret); case GrammarNames.ArrayFormula: return("{=" + childs.ElementAt(1) + "}"); case GrammarNames.DynamicDataExchange: childsL = childs.ToList(); return(String.Format("{0}!{1}", childsL[0], childsL[1])); // Terms for which to print all child nodes concatenated case GrammarNames.ArrayConstant: case GrammarNames.FormulaWithEq: return(String.Join("", childs)); // Terms for which we print the childs comma-separated case GrammarNames.Arguments: case GrammarNames.ArrayRows: case GrammarNames.Union: return(String.Join(",", childs)); case GrammarNames.ArrayColumns: return(String.Join(";", childs)); case GrammarNames.ConstantArray: return(String.Format("{{{0}}}", childs.First())); default: // If it is not defined above and the number of childs is exactly one, we want to just print the first child if (input.ChildNodes.Count == 1) { return(childs.First()); } throw new ArgumentException(String.Format("Could not print node of type '{0}'.\nThis probably means the excel grammar was modified without the print function being modified", input.Term.Name)); } }