PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/Ruby/Ruby/Compiler/Ast/Expressions/StringConstructor.cs

http://github.com/IronLanguages/main
C# | 331 lines | 238 code | 57 blank | 36 comment | 43 complexity | be1ec86480f21e1de8577baebc4d3fa1 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if FEATURE_CORE_DLR
  16. using MSA = System.Linq.Expressions;
  17. #else
  18. using MSA = Microsoft.Scripting.Ast;
  19. #endif
  20. using System;
  21. using System.Collections.Generic;
  22. using System.Reflection;
  23. using System.Text;
  24. using IronRuby.Builtins;
  25. using IronRuby.Runtime;
  26. using IronRuby.Runtime.Calls;
  27. using Microsoft.Scripting;
  28. using Microsoft.Scripting.Utils;
  29. using System.Diagnostics;
  30. using IronRuby.Runtime.Conversions;
  31. namespace IronRuby.Compiler.Ast {
  32. using Ast = MSA.Expression;
  33. using AstUtils = Microsoft.Scripting.Ast.Utils;
  34. using MSAst = Microsoft.Scripting.Ast;
  35. public enum StringKind {
  36. Mutable,
  37. Symbol,
  38. Command
  39. }
  40. /// <summary>
  41. /// Sequence of string literals and/or string embedded expressions.
  42. /// </summary>
  43. public partial class StringConstructor : Expression {
  44. private readonly StringKind _kind;
  45. private readonly List<Expression>/*!*/ _parts;
  46. public StringKind Kind {
  47. get { return _kind; }
  48. }
  49. public List<Expression>/*!*/ Parts {
  50. get { return _parts; }
  51. }
  52. public StringConstructor(List<Expression>/*!*/ parts, StringKind kind, SourceSpan location)
  53. : base(location) {
  54. ContractUtils.RequiresNotNullItems(parts, "parts");
  55. _parts = parts;
  56. _kind = kind;
  57. }
  58. internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) {
  59. switch (_kind) {
  60. case StringKind.Mutable:
  61. return TransformConcatentation(gen, _parts, StringFactory.Instance);
  62. case StringKind.Symbol:
  63. return TransformConcatentation(gen, _parts, SymbolFactory.Instance);
  64. case StringKind.Command:
  65. return CallSiteBuilder.InvokeMethod(gen.Context, "`", new RubyCallSignature(1, RubyCallFlags.HasScope | RubyCallFlags.HasImplicitSelf),
  66. gen.CurrentScopeVariable,
  67. gen.CurrentSelfVariable,
  68. TransformConcatentation(gen, _parts, StringFactory.Instance)
  69. );
  70. }
  71. throw Assert.Unreachable;
  72. }
  73. internal static MSA.Expression/*!*/ MakeConversion(AstGenerator/*!*/ gen, Expression/*!*/ expression) {
  74. return AstUtils.LightDynamic(ConvertToSAction.Make(gen.Context), typeof(MutableString), expression.TransformRead(gen));
  75. }
  76. #region Factories
  77. // TODO:
  78. internal interface IFactory {
  79. MSA.Expression/*!*/ CreateExpression(AstGenerator/*!*/ gen, string/*!*/ literal, RubyEncoding/*!*/ encoding);
  80. MSA.Expression/*!*/ CreateExpression(AstGenerator/*!*/ gen, byte[]/*!*/ literal, RubyEncoding/*!*/ encoding);
  81. MSA.Expression/*!*/ CreateExpressionN(AstGenerator/*!*/ gen, IEnumerable<MSA.Expression>/*!*/ args);
  82. MSA.Expression/*!*/ CreateExpressionM(AstGenerator/*!*/ gen, MSAst.ExpressionCollectionBuilder/*!*/ args);
  83. }
  84. private sealed class StringFactory : IFactory {
  85. public static readonly StringFactory Instance = new StringFactory();
  86. public MSA.Expression/*!*/ CreateExpression(AstGenerator/*!*/ gen, string/*!*/ literal, RubyEncoding/*!*/ encoding) {
  87. return Methods.CreateMutableStringL.OpCall(Ast.Constant(literal), encoding.Expression);
  88. }
  89. public MSA.Expression/*!*/ CreateExpression(AstGenerator/*!*/ gen, byte[]/*!*/ literal, RubyEncoding/*!*/ encoding) {
  90. return Methods.CreateMutableStringB.OpCall(Ast.Constant(literal), encoding.Expression);
  91. }
  92. public MSA.Expression/*!*/ CreateExpressionN(AstGenerator/*!*/ gen, IEnumerable<MSA.Expression>/*!*/ args) {
  93. return Methods.CreateMutableString("N").OpCall(Ast.NewArrayInit(typeof(MutableString), args));
  94. }
  95. public MSA.Expression/*!*/ CreateExpressionM(AstGenerator/*!*/ gen, MSAst.ExpressionCollectionBuilder/*!*/ args) {
  96. string suffix = new String('M', args.Count);
  97. args.Add(gen.Encoding.Expression);
  98. return Methods.CreateMutableString(suffix).OpCall(args);
  99. }
  100. }
  101. internal sealed class SymbolFactory : IFactory {
  102. public static readonly SymbolFactory Instance = new SymbolFactory();
  103. public MSA.Expression/*!*/ CreateExpression(AstGenerator/*!*/ gen, string/*!*/ literal, RubyEncoding/*!*/ encoding) {
  104. return Ast.Constant(gen.Context.CreateSymbol(literal, encoding));
  105. }
  106. public MSA.Expression/*!*/ CreateExpression(AstGenerator/*!*/ gen, byte[]/*!*/ literal, RubyEncoding/*!*/ encoding) {
  107. return Ast.Constant(gen.Context.CreateSymbol(literal, encoding));
  108. }
  109. public MSA.Expression/*!*/ CreateExpressionN(AstGenerator/*!*/ gen, IEnumerable<MSA.Expression>/*!*/ args) {
  110. return Methods.CreateSymbol("N").OpCall(Ast.NewArrayInit(typeof(MutableString), args), gen.CurrentScopeVariable);
  111. }
  112. public MSA.Expression/*!*/ CreateExpressionM(AstGenerator/*!*/ gen, MSAst.ExpressionCollectionBuilder/*!*/ args) {
  113. string suffix = new String('M', args.Count);
  114. args.Add(gen.Encoding.Expression);
  115. args.Add(gen.CurrentScopeVariable);
  116. return Methods.CreateSymbol(suffix).OpCall(args);
  117. }
  118. }
  119. #endregion
  120. #region Literal Concatenation
  121. private sealed class LiteralConcatenation : List<object> {
  122. private RubyEncoding/*!*/ _encoding;
  123. private int _length;
  124. private bool _isBinary;
  125. private void ObjectInvariant() {
  126. ContractUtils.Invariant(_isBinary || CollectionUtils.TrueForAll(this, (item) => item is string));
  127. }
  128. public LiteralConcatenation(RubyEncoding/*!*/ sourceEncoding) {
  129. Assert.NotNull(sourceEncoding);
  130. _encoding = sourceEncoding;
  131. }
  132. public bool IsBinary {
  133. get { return _isBinary; }
  134. }
  135. public RubyEncoding/*!*/ Encoding {
  136. get { return _encoding; }
  137. }
  138. public new void Clear() {
  139. _length = 0;
  140. base.Clear();
  141. }
  142. // Returns false if the literal can't be concatenated due to incompatible encodings.
  143. public bool Add(StringLiteral/*!*/ literal) {
  144. var concatEncoding = MutableString.GetCompatibleEncoding(_encoding, literal.Encoding);
  145. if (concatEncoding == null) {
  146. return false;
  147. }
  148. var str = literal.Value as string;
  149. if (str != null) {
  150. if (_isBinary) {
  151. _length += literal.Encoding.Encoding.GetByteCount(str);
  152. } else {
  153. _length += str.Length;
  154. }
  155. } else {
  156. var bytes = (byte[])literal.Value;
  157. if (!_isBinary) {
  158. _length = 0;
  159. foreach (object item in this) {
  160. Debug.Assert(item is string);
  161. _length += _encoding.Encoding.GetByteCount((string)item);
  162. }
  163. _isBinary = true;
  164. }
  165. _length += bytes.Length;
  166. }
  167. _encoding = concatEncoding;
  168. base.Add(literal.Value);
  169. return true;
  170. }
  171. public object/*!*/ GetValue() {
  172. ContractUtils.Ensures(ContractUtils.Result<object>() is byte[] || ContractUtils.Result<object>() is string);
  173. if (Count == 1) {
  174. return this[0];
  175. }
  176. if (_isBinary) {
  177. int offset = 0;
  178. var result = new byte[_length];
  179. foreach (object item in this) {
  180. byte[] bytes = item as byte[];
  181. if (bytes != null) {
  182. Buffer.BlockCopy(bytes, 0, result, offset, bytes.Length);
  183. offset += bytes.Length;
  184. } else {
  185. string str = (string)item;
  186. offset += _encoding.Encoding.GetBytes(str, 0, str.Length, result, offset);
  187. }
  188. }
  189. Debug.Assert(offset == result.Length);
  190. return result;
  191. } else {
  192. var result = new StringBuilder(_length);
  193. foreach (string item in this) {
  194. result.Append(item);
  195. }
  196. return result.ToString();
  197. }
  198. }
  199. }
  200. internal static MSA.Expression/*!*/ TransformConcatentation(AstGenerator/*!*/ gen, List<Expression>/*!*/ parts, IFactory/*!*/ factory) {
  201. // fast path for a single element:
  202. if (parts.Count == 1) {
  203. var literal = parts[0] as StringLiteral;
  204. if (literal != null) {
  205. var str = literal.Value as string;
  206. if (str != null) {
  207. return factory.CreateExpression(gen, str, literal.Encoding);
  208. } else {
  209. return factory.CreateExpression(gen, (byte[])literal.Value, literal.Encoding);
  210. }
  211. } else {
  212. return factory.CreateExpressionM(gen, new MSAst.ExpressionCollectionBuilder { MakeConversion(gen, parts[0]) });
  213. }
  214. }
  215. var merged = new MSAst.ExpressionCollectionBuilder();
  216. var concat = new LiteralConcatenation(gen.Encoding);
  217. if (!ConcatLiteralsAndTransformRecursive(gen, parts, concat, merged)) {
  218. // TODO: we should emit Append calls directly, and not create an array first
  219. // TODO: MRI reports a syntax error
  220. // we can't concatenate due to encoding incompatibilities, report error at runtime:
  221. return factory.CreateExpressionN(gen, CollectionUtils.ConvertAll(parts, (e) => e.Transform(gen)));
  222. }
  223. // finish trailing literals:
  224. if (concat.Count > 0) {
  225. merged.Add(StringLiteral.Transform(concat.GetValue(), concat.Encoding));
  226. }
  227. if (merged.Count <= RubyOps.MakeStringParamCount) {
  228. if (merged.Count == 0) {
  229. return factory.CreateExpression(gen, String.Empty, gen.Encoding);
  230. }
  231. return factory.CreateExpressionM(gen, merged);
  232. } else {
  233. // TODO: we should emit Append calls directly, and not create an array first
  234. return factory.CreateExpressionN(gen, merged);
  235. }
  236. }
  237. //
  238. // Traverses expressions in "parts" and concats all contiguous literal strings/bytes.
  239. // Notes:
  240. // - We place the string/byte[] values that can be concatenated so far in to "concat" list that keeps track of their total length.
  241. // If we reach a non-literal expression and we have some literals ready in "concat" list we perform concat and clear the list.
  242. // - "result" contains argument expressions to be passed to a CreateMutableString* overload.
  243. // - "opName" contains the name of the operation. This method appends either "non-literal" suffix (for each expression)
  244. // or "encoding" suffix (for each concatenated literal).
  245. //
  246. // Returns false if the parts can't be concatenated due to encoding incompatibilities.
  247. //
  248. private static bool ConcatLiteralsAndTransformRecursive(AstGenerator/*!*/ gen, List<Expression>/*!*/ parts,
  249. LiteralConcatenation/*!*/ concat, MSAst.ExpressionCollectionBuilder/*!*/ result) {
  250. for (int i = 0; i < parts.Count; i++) {
  251. Expression part = parts[i];
  252. StringLiteral literal;
  253. StringConstructor ctor;
  254. if ((literal = part as StringLiteral) != null) {
  255. if (!concat.Add(literal)) {
  256. return false;
  257. }
  258. } else if ((ctor = part as StringConstructor) != null) {
  259. if (!ConcatLiteralsAndTransformRecursive(gen, ctor.Parts, concat, result)) {
  260. return false;
  261. }
  262. } else {
  263. if (concat.Count > 0) {
  264. result.Add(StringLiteral.Transform(concat.GetValue(), concat.Encoding));
  265. concat.Clear();
  266. }
  267. result.Add(MakeConversion(gen, part));
  268. }
  269. }
  270. return true;
  271. }
  272. #endregion
  273. }
  274. }