Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Interpolated String Nesting #273

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 54 additions & 21 deletions src/Viasfora.Languages/BraceScanners/CSharpBraceScanner.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Winterdom.Viasfora.Rainbow;
using Winterdom.Viasfora.Util;
Expand All @@ -11,11 +12,34 @@ public class CSharpBraceScanner : IBraceScanner, IResumeControl {
const int stMultiLineComment = 4;
const int stIString = 5;

private int status = stText;
private int nestingLevel = 0;
private int istringNestLevel = 0;
private bool parsingExpression = false;
private bool multiLine = false;
private int status { get => this.nestings[this._nestingIndex].status; set => this.nestings[this._nestingIndex].status = value; }
private int nestingLevel { get => this.nestings[this._nestingIndex].nestingLevel; set => this.nestings[this._nestingIndex].nestingLevel = value; }
private bool parsingExpression { get => this.nestings[this._nestingIndex].parsingExpression; set => this.nestings[this._nestingIndex].parsingExpression = value; }
private bool multiLine { get => this.nestings[this._nestingIndex].multiLine; set => this.nestings[this._nestingIndex].multiLine = value; }


private int nestingIndex {
get {
return this._nestingIndex;
}
set {
if ( value > this._nestingIndex ) {
this.nestings.Add(new NestingOption() {status = this.status, nestingLevel = this.nestingLevel, parsingExpression = this.parsingExpression, multiLine=this.multiLine});
} else if(value < this._nestingIndex) {
this.nestings.RemoveAt(this.nestings.Count-1);
}
this._nestingIndex = value;
}
}
private int _nestingIndex = 0;
private List<NestingOption> nestings = new List<NestingOption>() { new NestingOption() };

private class NestingOption {
public int nestingLevel = 0;
public int status = stText;
public bool parsingExpression = false;
public bool multiLine = false;
}

public String BraceList => "(){}[]";

Expand All @@ -27,7 +51,7 @@ public void Reset(int state) {
this.parsingExpression = (state & 0x08000000) != 0;
this.nestingLevel = (state & 0xFF0000) >> 24;
this.multiLine = (state & 0x04000000) != 0;
this.istringNestLevel = (state & 0xFF00) >> 16;
this.nestingIndex = (state & 0xFF00) >> 16;
}

public bool CanResume(CharPos brace) {
Expand Down Expand Up @@ -170,17 +194,18 @@ private bool ParseInterpolatedString(ITextChars tc, ref CharPos pos) {
//
// we're inside an interpolated section
//
if ( tc.Char() == '$' && tc.NChar() == '"' ) {
if ( tc.Char() == '$' && (tc.NChar() == '"') || (tc.NNChar() == '"' && tc.NChar() == '@') ) {
// opening nested interpolated string
tc.Skip(2);
tc.Skip(tc.NChar() == '"' ? 2 : 3);
this.nestingIndex++;
this.parsingExpression = false;
this.istringNestLevel++;
this.nestingLevel = 0;
if ( this.ParseInterpolatedString(tc, ref pos) )
return true;
this.istringNestLevel--;
this.parsingExpression = true;
this.status = stIString;
//if ( this.ParseInterpolatedString(tc, ref pos) )
// return true;
//this.nestingIndex--;
//this.parsingExpression = true;
//this.status = stIString;
return false;
} else if ( tc.Char() == '@' && tc.NChar() == '"' ) {
// opening nested verbatim string
tc.Skip(2);
Expand Down Expand Up @@ -234,15 +259,23 @@ private bool ParseInterpolatedString(ITextChars tc, ref CharPos pos) {
tc.Skip(2);
} else if ( tc.Char() == '"' ) {
// done parsing the interpolated string
this.multiLine = false;
this.istringNestLevel--;
if (this.istringNestLevel <= 0) {
this.istringNestLevel = 0;
//this.multiLine = false;
//if ( this.nestingIndex - 1 <= 0 ) {
// this.nestingIndex = 0;
// this.status = stText;
//} else {
// this.status = stIString;
// this.parsingExpression = true;
//}
var indx = this.nestingIndex;
if(indx -1 < 0) {
this.multiLine = false;
this.nestingIndex = 0;
this.status = stText;
Copy link
Author

@numv numv Jul 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This in combination with the start in line 177.
Nesting got lost and stText was returned but it was still inside the nested string

} else {
this.status = stIString;
this.parsingExpression = true;
this.nestingIndex--;
}

tc.Next();
break;
} else {
Expand All @@ -260,7 +293,7 @@ private int EncodedState() {
if ( this.multiLine )
encoded |= 0x04000000;
encoded |= (this.nestingLevel & 0xFF) << 24;
encoded |= (this.istringNestLevel & 0xFF) << 16;
encoded |= (this.nestingIndex & 0xFF) << 16;
return encoded;
}
}
Expand Down
39 changes: 33 additions & 6 deletions tests/Viasfora.Tests/BraceScanners/CSharpBraceScannerTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Winterdom.Viasfora.Languages.BraceScanners;
using Xunit;

Expand Down Expand Up @@ -153,14 +155,39 @@ public void Bug128_InterpolatedStringWithNestedCurlyBraces() {
var chars = Extract(extractor, input.Trim(), 0, 0);
Assert.Equal(1+1+2+1+1+1+1, chars.Count);
}
[Fact]
public void Bug259_InterpolatedStringEmbedded() {
String input = "$\"{(string.IsNullOrWhiteSpace(a) ? $\"{b}\" : $\"{c}\")}\"";
var extractor = new CSharpBraceScanner();
var chars = Extract(extractor, input.Trim(), 0, 0);
Assert.Equal(2+1+1+1+1+1+1+2, chars.Count);
[Fact]
public void Bug259_InterpolatedStringEmbedded() {
String input = "$\"{(string.IsNullOrWhiteSpace(a) ? $\"{b}\" : $\"{c}\")}\"";
var extractor = new CSharpBraceScanner();
var chars = Extract(extractor, input.Trim(), 0, 0);
Assert.Equal(2+1+1+1+1+1+1+2, chars.Count);
}

public List<string> testList = new List<string>();

[Fact]
public void Bug263_InterpolatedStringWithInterpolatedString1() {

// bug is the missing ) in following section of the teststring:
// n => $\"pre_{n}_suf\").Aggregate

String input = "string.IsNullOrEmpty($\"{test} {(string.IsNullOrEmpty(test) ? \"\" : testList.Select(n => $\"pre_{n}_suf\").Aggregate((n,m) => n + \", \" + m))}\")";
var extractor = new CSharpBraceScanner();
var chars = Extract(extractor, input.Trim(), 0, 0);
Assert.Equal(1+1+1+2+1+1+1+1+1+1+2+1+3+1, chars.Count);
}

[Fact]
public void Bug263_InterpolatedStringWithInterpolatedString2() {

// bug is the missing ) in following section of the teststring:
// n => $\"pre_{n}_suf\").Aggregate

String input = "string.IsNullOrEmpty($\"{test} {(string.IsNullOrEmpty(test) ? \"\" : testList.Select(n => $@\"pre_{n}_suf\").Aggregate((n,m) => n + \", \" + m))}\")";
var extractor = new CSharpBraceScanner();
var chars = Extract(extractor, input.Trim(), 0, 0);
Assert.Equal(1 + 1 + 1 + 2 + 1 + 1 + 1 + 1 + 1 + 1 + 2 + 1 + 3 + 1, chars.Count);
}

[Fact]
public void InterpolatedAtString1() {
Expand Down