// // Traverses expressions in "parts" and concats all contiguous literal strings/bytes. // Notes: // - We place the string/byte[] values that can be concatenated so far in to "concat" list thats keep track of their total length. // If we reach a non-literal expression and we have some literals ready in "concat" list we perform concat and clear the list. // - "result" contains argument expressions to be passed to a CreateMutableString* overload. // - "opName" contains the name of the operation. This method appends either "non-literal" suffix (for each expression) // or "encoding" suffix (for each concatenated literal). // - "anyBinary" keeps track of whether any iteral visited so far is binary (byte[]). // private static void ConcatLiteralsAndTransformRecursive(AstGenerator /*!*/ gen, List <Expression> /*!*/ parts, LiteralConcatenation /*!*/ concat, MSAst.ExpressionCollectionBuilder /*!*/ result, StringBuilder /*!*/ opName, ref bool anyBinary) { for (int i = 0; i < parts.Count; i++) { Expression part = parts[i]; StringLiteral literal; StringConstructor ctor; if ((literal = part as StringLiteral) != null) { concat.Add(literal); } else if ((ctor = part as StringConstructor) != null) { ConcatLiteralsAndTransformRecursive(gen, ctor.Parts, concat, result, opName, ref anyBinary); } else { if (concat.Count > 0) { result.Add(MakeConstant(concat.GetValue())); opName.Append(RubyOps.SuffixLiteral); anyBinary |= concat.IsBinary; concat.Clear(); } result.Add(MakeConversion(gen, part)); opName.Append(RubyOps.SuffixMutable); } } }
internal static MSA.Expression /*!*/ TransformConcatentation(AstGenerator /*!*/ gen, List <Expression> /*!*/ parts, IFactory /*!*/ factory) { // fast path for a single element: if (parts.Count == 1) { var literal = parts[0] as StringLiteral; if (literal != null) { var str = literal.Value as string; if (str != null) { return(factory.CreateExpression(gen, str)); } } else { return(factory.CreateExpression(gen, "M", MakeConversion(gen, parts[0]))); } } var opSuffix = new StringBuilder(Math.Min(parts.Count, 4)); bool anyBinary = false; var merged = new MSAst.ExpressionCollectionBuilder(); var concat = new LiteralConcatenation(gen.Encoding.Encoding); ConcatLiteralsAndTransformRecursive(gen, parts, concat, merged, opSuffix, ref anyBinary); // finish trailing literals: if (concat.Count > 0) { object value = concat.GetValue(); // TODO (opt): We don't to optimize for binary strings, we can if it is needed. if (!concat.IsBinary && merged.Count == 0) { return(factory.CreateExpression(gen, (string)value)); } merged.Add(MakeConstant(value)); opSuffix.Append(RubyOps.SuffixLiteral); anyBinary |= concat.IsBinary; } // TODO (opt): We don't to optimize for binary strings, we can if it is needed. if (!anyBinary && merged.Count <= RubyOps.MakeStringParamCount) { if (merged.Count == 0) { return(factory.CreateExpression(gen, String.Empty)); } return(factory.CreateExpression(gen, opSuffix.ToString(), merged)); } else { return(factory.CreateExpression(gen, "N", Ast.NewArrayInit(typeof(object), merged))); } }
internal static MSA.Expression /*!*/ TransformConcatentation(AstGenerator /*!*/ gen, List <Expression> /*!*/ parts, IFactory /*!*/ factory) { // fast path for a single element: if (parts.Count == 1) { var literal = parts[0] as StringLiteral; if (literal != null) { var str = literal.Value as string; if (str != null) { return(factory.CreateExpression(gen, str, literal.Encoding)); } else { return(factory.CreateExpression(gen, (byte[])literal.Value, literal.Encoding)); } } else { return(factory.CreateExpressionM(gen, new MSAst.ExpressionCollectionBuilder { MakeConversion(gen, parts[0]) })); } } var merged = new MSAst.ExpressionCollectionBuilder(); var concat = new LiteralConcatenation(gen.Encoding); if (!ConcatLiteralsAndTransformRecursive(gen, parts, concat, merged)) { // TODO: we should emit Append calls directly, and not create an array first // TODO: MRI reports a syntax error // we can't concatenate due to encoding incompatibilities, report error at runtime: return(factory.CreateExpressionN(gen, CollectionUtils.ConvertAll(parts, (e) => e.Transform(gen)))); } // finish trailing literals: if (concat.Count > 0) { merged.Add(StringLiteral.Transform(concat.GetValue(), concat.Encoding)); } if (merged.Count <= RubyOps.MakeStringParamCount) { if (merged.Count == 0) { return(factory.CreateExpression(gen, String.Empty, gen.Encoding)); } return(factory.CreateExpressionM(gen, merged)); } else { // TODO: we should emit Append calls directly, and not create an array first return(factory.CreateExpressionN(gen, merged)); } }
// // Traverses expressions in "parts" and concats all contiguous literal strings/bytes. // Notes: // - We place the string/byte[] values that can be concatenated so far in to "concat" list that keeps track of their total length. // If we reach a non-literal expression and we have some literals ready in "concat" list we perform concat and clear the list. // - "result" contains argument expressions to be passed to a CreateMutableString* overload. // - "opName" contains the name of the operation. This method appends either "non-literal" suffix (for each expression) // or "encoding" suffix (for each concatenated literal). // // Returns false if the parts can't be concatenated due to encoding incompatibilities. // private static bool ConcatLiteralsAndTransformRecursive(AstGenerator /*!*/ gen, List <Expression> /*!*/ parts, LiteralConcatenation /*!*/ concat, MSAst.ExpressionCollectionBuilder /*!*/ result) { for (int i = 0; i < parts.Count; i++) { Expression part = parts[i]; StringLiteral literal; StringConstructor ctor; if ((literal = part as StringLiteral) != null) { if (!concat.Add(literal)) { return(false); } } else if ((ctor = part as StringConstructor) != null) { if (!ConcatLiteralsAndTransformRecursive(gen, ctor.Parts, concat, result)) { return(false); } } else { if (concat.Count > 0) { result.Add(StringLiteral.Transform(concat.GetValue(), concat.Encoding)); concat.Clear(); } result.Add(MakeConversion(gen, part)); } } return(true); }