Skip to content

Commit

Permalink
Support @(XYZ) raw strings (#2110)
Browse files Browse the repository at this point in the history
Co-authored-by: ike709 <[email protected]>
Co-authored-by: wixoa <[email protected]>
  • Loading branch information
3 people authored Jan 27, 2025
1 parent 4714111 commit d12f5ef
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 3 deletions.
7 changes: 7 additions & 0 deletions Content.Tests/DMProject/Tests/Expression/String/raw2.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

//# issue 380

/proc/RunTest()
var/a = @(ZZZ)
asdfZZZ
ASSERT(a == "asdf")
7 changes: 7 additions & 0 deletions Content.Tests/DMProject/Tests/Expression/String/raw3.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

//# issue 380

/proc/RunTest()
var/a = @(ZZQ)
asdfZZQ
ASSERT(a == "asdf")
72 changes: 69 additions & 3 deletions DMCompiler/Compiler/DMPreprocessor/DMPreprocessorLexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -301,20 +301,65 @@ public Token NextToken(bool ignoreWhitespace = false) {
}
case '@': { //Raw string
char delimiter = Advance();
var startLoc = CurrentLocation();

// @(XYZ) where XYZ is the delimiter
string complexDelimiter = string.Empty;
if (delimiter == '(') {
Advance();
while (GetCurrent() != ')') {
if (AtEndOfSource()) {
_compiler.Emit(WarningCode.BadExpression, startLoc,
"Unterminated string delimiter");
break;
}

complexDelimiter += GetCurrent();
Advance();
}
}

TokenTextBuilder.Clear();
TokenTextBuilder.Append('@');
TokenTextBuilder.Append(delimiter);

bool isComplex = complexDelimiter != string.Empty;
bool isLong = false;

c = Advance();
if (delimiter == '{') {
TokenTextBuilder.Append(c);

if (c == '"') isLong = true;
}

if (isLong) {
if (isComplex) {
TokenTextBuilder.Append(complexDelimiter);
TokenTextBuilder.Append(')');

// Ignore a newline immediately after @(complexDelimiter)
if (c == '\r') c = Advance();
if (c == '\n') c = Advance();

var delimIdx = 0;
do {
TokenTextBuilder.Append(c);

if (GetCurrent() == complexDelimiter[delimIdx]) delimIdx++;
else delimIdx = 0;

if (delimIdx == complexDelimiter.Length && c == complexDelimiter[^1]) { // latter check ensures a 1-char delimiter actually matches
break;
}

c = Advance();
} while (!AtEndOfSource());

if (AtEndOfSource()) {
_compiler.Emit(WarningCode.BadExpression, startLoc,
"Unterminated string delimiter");
}
} else if (isLong) {
bool nextCharCanTerm = false;

Advance();
Expand All @@ -335,26 +380,43 @@ public Token NextToken(bool ignoreWhitespace = false) {
if (c == '"')
nextCharCanTerm = true;
} while (!AtEndOfSource());

if (AtEndOfSource()) {
_compiler.Emit(WarningCode.BadExpression, startLoc,
"Unterminated string delimiter");
}
} else {
while (c != delimiter && !AtLineEnd() && !AtEndOfSource()) {
TokenTextBuilder.Append(c);
c = Advance();
}
}

TokenTextBuilder.Append(c);
if (!isComplex) TokenTextBuilder.Append(c);

if (!HandleLineEnd())
Advance();

string text = TokenTextBuilder.ToString();
string value;

if (isLong) {
if (isComplex) {
// Complex strings need to strip @(complexDelimiter) and a potential final newline. Newline after @(complexDelimiter) is already handled
var trimEnd = complexDelimiter.Length;
if (TokenTextBuilder[^(complexDelimiter.Length + 1)] == '\n') trimEnd += 1;
if (TokenTextBuilder[^(complexDelimiter.Length + 2)] == '\r') trimEnd += 1;
var trimStart = 3 + complexDelimiter.Length; // 3 is from these chars: @()
value = TokenTextBuilder.ToString(trimStart, TokenTextBuilder.Length - (trimStart + trimEnd));
} else if (isLong) {
// Long strings ignore a newline immediately after the @{" and before the "}
if (TokenTextBuilder[3] == '\r')
TokenTextBuilder.Remove(3, 1);
if (TokenTextBuilder[3] == '\n')
TokenTextBuilder.Remove(3, 1);
if (TokenTextBuilder[^3] == '\n')
TokenTextBuilder.Remove(TokenTextBuilder.Length - 3, 1);
if (TokenTextBuilder[^3] == '\r')
TokenTextBuilder.Remove(TokenTextBuilder.Length - 3, 1);

value = TokenTextBuilder.ToString(3, TokenTextBuilder.Length - 5);
} else {
Expand Down Expand Up @@ -639,6 +701,10 @@ private char GetCurrent() {
return _current;
}

private Location CurrentLocation() {
return new Location(File, _previousLine, _previousColumn, _isDMStandard);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private char Advance() {
int value = _source.Read();
Expand Down

0 comments on commit d12f5ef

Please sign in to comment.