using System; using System.Collections.Generic; using System.Globalization; using System.Text; namespace io.github.hatayama.uLoopMCP { internal static class DynamicCodeLiteralHoister { private const string LiteralParameterPrefix = "__uloop_literal_"; public static HoistedLiteralRewriteResult Rewrite(string source) { StringBuilder rewrittenSource = new StringBuilder(source.Length); List bindings = new List(); int index = 0; while (index < source.Length) { if (TryCopyInterpolatedStringLiteral(source, rewrittenSource, ref index)) { continue; } if (TryCopyVerbatimStringLiteral(source, rewrittenSource, ref index)) { continue; } if (TryCopyCharLiteral(source, rewrittenSource, ref index)) { continue; } if (TryCopyLineComment(source, rewrittenSource, ref index)) { continue; } if (TryCopyBlockComment(source, rewrittenSource, ref index)) { continue; } if (TryHoistRegularStringLiteral(source, rewrittenSource, bindings, ref index)) { continue; } if (TryHoistIntegerLiteral(source, rewrittenSource, bindings, ref index)) { continue; } rewrittenSource.Append(source[index]); index++; } List declarationLines = new List(); foreach (HoistedLiteralBinding binding in bindings) { declarationLines.Add( $"{binding.TypeName} {binding.ParameterName} = ({binding.TypeName})parameters[\"{binding.ParameterName}\"];"); } return new HoistedLiteralRewriteResult( rewrittenSource.ToString(), bindings, declarationLines); } private static bool TryCopyInterpolatedStringLiteral( string source, StringBuilder rewrittenSource, ref int index) { int start = index; if (!TryAdvanceInterpolatedStringLiteral(source, ref index)) { return false; } rewrittenSource.Append(source, start, index - start); return true; } private static bool TryCopyLineComment( string source, StringBuilder rewrittenSource, ref int index) { if (index + 1 >= source.Length || source[index] != '/' || source[index + 1] != '/') { return false; } while (index < source.Length) { char current = source[index]; rewrittenSource.Append(current); index++; if (current == '\n') { break; } } return true; } private static bool TryCopyBlockComment( string source, StringBuilder rewrittenSource, ref int index) { if (index + 1 >= source.Length || source[index] != '/' || source[index + 1] != '*') { return false; } rewrittenSource.Append(source[index]); rewrittenSource.Append(source[index + 1]); index += 2; while (index < source.Length) { char current = source[index]; rewrittenSource.Append(current); index++; if (current == '*' && index < source.Length && source[index] == '/') { rewrittenSource.Append('/'); index++; break; } } return true; } private static bool TryCopyVerbatimStringLiteral( string source, StringBuilder rewrittenSource, ref int index) { if (source[index] != '@' || index + 1 >= source.Length || source[index + 1] != '"') { return false; } int start = index; index += 2; while (index < source.Length) { if (source[index] != '"') { index++; continue; } if (index + 1 < source.Length && source[index + 1] == '"') { index += 2; continue; } index++; rewrittenSource.Append(source, start, index - start); return true; } index = start; return false; } private static bool TryAdvanceInterpolatedStringLiteral(string source, ref int index) { int start = index; if (!TryMatchInterpolatedStringStart(source, ref index, out bool isVerbatim)) { return false; } int interpolationDepth = 0; while (index < source.Length) { if (interpolationDepth > 0) { if (TryAdvanceInterpolatedExpressionToken(source, ref index)) { continue; } char expressionCharacter = source[index]; if (expressionCharacter == '{') { interpolationDepth++; index++; continue; } if (expressionCharacter == '}') { interpolationDepth--; index++; continue; } index++; continue; } char current = source[index]; if (current == '{') { if (index + 1 < source.Length && source[index + 1] == '{') { index += 2; continue; } interpolationDepth = 1; index++; continue; } if (current == '}') { if (index + 1 < source.Length && source[index + 1] == '}') { index += 2; continue; } index++; continue; } if (!isVerbatim && current == '\\') { AdvanceEscapedLiteralSequence(source, ref index); continue; } if (current == '"') { if (isVerbatim && index + 1 < source.Length && source[index + 1] == '"') { index += 2; continue; } index++; return true; } index++; } index = start; return false; } private static bool TryMatchInterpolatedStringStart( string source, ref int index, out bool isVerbatim) { isVerbatim = false; if (source[index] == '$') { if (index + 1 < source.Length && source[index + 1] == '"') { index += 2; return true; } if (index + 2 < source.Length && source[index + 1] == '@' && source[index + 2] == '"') { isVerbatim = true; index += 3; return true; } return false; } if (source[index] != '@') { return false; } if (index + 2 < source.Length && source[index + 1] == '$' && source[index + 2] == '"') { isVerbatim = true; index += 3; return true; } return false; } private static bool TryAdvanceInterpolatedExpressionToken(string source, ref int index) { if (TryAdvanceInterpolatedStringLiteral(source, ref index)) { return true; } if (TryAdvanceVerbatimStringLiteral(source, ref index)) { return true; } if (TryAdvanceRegularStringLiteral(source, ref index)) { return true; } if (TryAdvanceCharLiteral(source, ref index)) { return true; } if (TryAdvanceLineComment(source, ref index)) { return true; } if (TryAdvanceBlockComment(source, ref index)) { return true; } return false; } private static bool TryAdvanceLineComment(string source, ref int index) { if (index + 1 >= source.Length || source[index] != '/' || source[index + 1] != '/') { return false; } index += 2; while (index < source.Length && source[index] != '\n') { index++; } if (index < source.Length) { index++; } return true; } private static bool TryAdvanceBlockComment(string source, ref int index) { if (index + 1 >= source.Length || source[index] != '/' || source[index + 1] != '*') { return false; } index += 2; while (index < source.Length) { if (source[index] == '*' && index + 1 < source.Length && source[index + 1] == '/') { index += 2; return true; } index++; } return true; } private static bool TryAdvanceVerbatimStringLiteral(string source, ref int index) { if (source[index] != '@' || index + 1 >= source.Length || source[index + 1] != '"') { return false; } int start = index; index += 2; while (index < source.Length) { if (source[index] != '"') { index++; continue; } if (index + 1 < source.Length && source[index + 1] == '"') { index += 2; continue; } index++; return true; } index = start; return false; } private static bool TryAdvanceRegularStringLiteral(string source, ref int index) { if (source[index] != '"') { return false; } if (index > 0 && (source[index - 1] == '@' || source[index - 1] == '$')) { return false; } int start = index; index++; while (index < source.Length) { char current = source[index]; if (current == '\\') { AdvanceEscapedLiteralSequence(source, ref index); continue; } if (current == '"') { index++; return true; } index++; } index = start; return false; } private static bool TryAdvanceCharLiteral(string source, ref int index) { if (source[index] != '\'') { return false; } int start = index; index++; while (index < source.Length) { char current = source[index]; if (current == '\\') { AdvanceEscapedLiteralSequence(source, ref index); continue; } if (current == '\'') { index++; return true; } index++; } index = start; return false; } private static bool TryCopyCharLiteral( string source, StringBuilder rewrittenSource, ref int index) { if (source[index] != '\'') { return false; } int start = index; index++; while (index < source.Length) { char current = source[index]; if (current == '\\') { AdvanceEscapedLiteralSequence(source, ref index); continue; } if (current == '\'') { index++; rewrittenSource.Append(source, start, index - start); return true; } index++; } index = start; return false; } private static bool TryHoistRegularStringLiteral( string source, StringBuilder rewrittenSource, List bindings, ref int index) { if (source[index] != '"') { return false; } if (index > 0 && (source[index - 1] == '@' || source[index - 1] == '$')) { return false; } int start = index; index++; while (index < source.Length) { char current = source[index]; if (current == '\\') { AdvanceEscapedLiteralSequence(source, ref index); continue; } if (current == '"') { index++; string literalToken = source.Substring(start, index - start); if (!TryUnescapeRegularStringLiteral(literalToken, out string value)) { rewrittenSource.Append(literalToken); return true; } string parameterName = CreateParameterName(bindings.Count); bindings.Add(new HoistedLiteralBinding(parameterName, "string", value)); rewrittenSource.Append(parameterName); return true; } index++; } index = start; return false; } private static bool TryHoistIntegerLiteral( string source, StringBuilder rewrittenSource, List bindings, ref int index) { char current = source[index]; if (!char.IsDigit(current)) { return false; } if (index > 0) { char previous = source[index - 1]; if (char.IsLetterOrDigit(previous) || previous == '_' || previous == '.') { return false; } } int start = index; index++; while (index < source.Length && char.IsDigit(source[index])) { index++; } if (index < source.Length && (source[index] == 'L' || source[index] == 'l')) { int suffixIndex = index; index++; if (!HasIntegerLiteralBoundary(source, index)) { index = start; return false; } string longToken = source.Substring(start, suffixIndex - start); if (!long.TryParse(longToken, NumberStyles.None, CultureInfo.InvariantCulture, out long longValue)) { index = start; return false; } string longParameterName = CreateParameterName(bindings.Count); bindings.Add(new HoistedLiteralBinding(longParameterName, "long", longValue)); rewrittenSource.Append(longParameterName); return true; } if (!HasIntegerLiteralBoundary(source, index)) { index = start; return false; } string token = source.Substring(start, index - start); if (!int.TryParse(token, NumberStyles.None, CultureInfo.InvariantCulture, out int intValue)) { index = start; return false; } string parameterName = CreateParameterName(bindings.Count); bindings.Add(new HoistedLiteralBinding(parameterName, "int", intValue)); rewrittenSource.Append(parameterName); return true; } private static bool HasIntegerLiteralBoundary(string source, int index) { if (index >= source.Length) { return true; } char next = source[index]; return !char.IsLetter(next) && !char.IsDigit(next) && next != '_' && next != '.'; } private static string CreateParameterName(int index) { return $"{LiteralParameterPrefix}{index}"; } private static void AdvanceEscapedLiteralSequence(string source, ref int index) { index++; if (index >= source.Length) { return; } char escape = source[index]; index++; switch (escape) { case 'u': index = Math.Min(source.Length, index + 4); break; case 'U': index = Math.Min(source.Length, index + 8); break; case 'x': index = AdvanceVariableLengthHexDigits(source, index, 4); break; } } private static int AdvanceVariableLengthHexDigits(string value, int index, int maxDigits) { int digitsConsumed = 0; while (index < value.Length && digitsConsumed < maxDigits && IsHexDigit(value[index])) { index++; digitsConsumed++; } return index; } private static bool TryUnescapeRegularStringLiteral(string token, out string value) { string inner = token.Substring(1, token.Length - 2); StringBuilder unescaped = new StringBuilder(inner.Length); for (int index = 0; index < inner.Length; index++) { char current = inner[index]; if (current != '\\') { unescaped.Append(current); continue; } if (index + 1 >= inner.Length) { value = null; return false; } index++; char escape = inner[index]; switch (escape) { case '\'': unescaped.Append('\''); break; case '"': unescaped.Append('"'); break; case '\\': unescaped.Append('\\'); break; case '0': unescaped.Append('\0'); break; case 'a': unescaped.Append('\a'); break; case 'b': unescaped.Append('\b'); break; case 'f': unescaped.Append('\f'); break; case 'n': unescaped.Append('\n'); break; case 'r': unescaped.Append('\r'); break; case 't': unescaped.Append('\t'); break; case 'v': unescaped.Append('\v'); break; case 'u': if (!TryParseHexDigits(inner, ref index, 4, out int unicodeValue)) { value = null; return false; } unescaped.Append((char)unicodeValue); break; case 'U': if (!TryParseHexDigits(inner, ref index, 8, out int codePoint) || !IsValidUtf32CodePoint(codePoint)) { value = null; return false; } unescaped.Append(char.ConvertFromUtf32(codePoint)); break; case 'x': if (!TryParseVariableLengthHexDigits(inner, ref index, out int variableLengthValue)) { value = null; return false; } unescaped.Append((char)variableLengthValue); break; default: value = null; return false; } } value = unescaped.ToString(); return true; } private static bool TryParseHexDigits(string value, ref int index, int digitCount, out int parsedValue) { int start = index + 1; if (start + digitCount > value.Length) { parsedValue = 0; return false; } string hex = value.Substring(start, digitCount); if (!int.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out parsedValue)) { return false; } index += digitCount; return true; } private static bool TryParseVariableLengthHexDigits(string value, ref int index, out int parsedValue) { int start = index + 1; int end = AdvanceVariableLengthHexDigits(value, start, 4); if (end == start) { parsedValue = 0; return false; } string hex = value.Substring(start, end - start); if (!int.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out parsedValue)) { return false; } index = end - 1; return true; } private static bool IsValidUtf32CodePoint(int value) { return value >= 0 && value <= 0x10FFFF && (value < 0xD800 || value > 0xDFFF); } private static bool IsHexDigit(char value) { return (value >= '0' && value <= '9') || (value >= 'a' && value <= 'f') || (value >= 'A' && value <= 'F'); } } internal sealed class HoistedLiteralRewriteResult { public string RewrittenSource { get; } public List Bindings { get; } public List DeclarationLines { get; } public HoistedLiteralRewriteResult( string rewrittenSource, List bindings, List declarationLines) { RewrittenSource = rewrittenSource; Bindings = bindings; DeclarationLines = declarationLines; } } }