Skip to content

Commit 792ef62

Browse files
authored
Merge pull request #168 from link-foundation/issue-142-23f307922307
Add support for backtick quotes and multi-quote strings with escaping (fixes #142)
2 parents 59109f9 + b51f608 commit 792ef62

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+6963
-254
lines changed
Lines changed: 381 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
using System;
2+
using Xunit;
3+
4+
namespace Link.Foundation.Links.Notation.Tests
5+
{
6+
public static class MultiQuoteParserTests
7+
{
8+
// Helper to extract single reference ID
9+
private static string? GetSingleRefId(System.Collections.Generic.IList<Link<string>> result)
10+
{
11+
if (result.Count == 1 && result[0].Id == null && result[0].Values?.Count == 1)
12+
{
13+
return result[0].Values[0].Id;
14+
}
15+
return result.Count == 1 ? result[0].Id : null;
16+
}
17+
18+
// ============================================================================
19+
// Backtick Quote Tests (Single Backtick)
20+
// ============================================================================
21+
22+
[Fact]
23+
public static void TestBacktickQuotedReference()
24+
{
25+
var parser = new Parser();
26+
var result = parser.Parse("`backtick quoted`");
27+
Assert.Equal("backtick quoted", GetSingleRefId(result));
28+
}
29+
30+
[Fact]
31+
public static void TestBacktickQuotedWithSpaces()
32+
{
33+
var parser = new Parser();
34+
var result = parser.Parse("`text with spaces`");
35+
Assert.Equal("text with spaces", GetSingleRefId(result));
36+
}
37+
38+
[Fact]
39+
public static void TestBacktickQuotedMultiline()
40+
{
41+
var parser = new Parser();
42+
var result = parser.Parse("(`line1\nline2`)");
43+
Assert.Single(result);
44+
Assert.NotNull(result[0].Values);
45+
Assert.Single(result[0].Values);
46+
Assert.Equal("line1\nline2", result[0].Values![0].Id);
47+
}
48+
49+
[Fact]
50+
public static void TestBacktickQuotedWithEscapedBacktick()
51+
{
52+
var parser = new Parser();
53+
var result = parser.Parse("`text with `` escaped backtick`");
54+
Assert.Equal("text with ` escaped backtick", GetSingleRefId(result));
55+
}
56+
57+
// ============================================================================
58+
// Single Quote Tests (with escaping)
59+
// ============================================================================
60+
61+
[Fact]
62+
public static void TestSingleQuoteWithEscapedSingleQuote()
63+
{
64+
var parser = new Parser();
65+
var result = parser.Parse("'text with '' escaped quote'");
66+
Assert.Equal("text with ' escaped quote", GetSingleRefId(result));
67+
}
68+
69+
// ============================================================================
70+
// Double Quote Tests (with escaping)
71+
// ============================================================================
72+
73+
[Fact]
74+
public static void TestDoubleQuoteWithEscapedDoubleQuote()
75+
{
76+
var parser = new Parser();
77+
var result = parser.Parse("\"text with \"\" escaped quote\"");
78+
Assert.Equal("text with \" escaped quote", GetSingleRefId(result));
79+
}
80+
81+
// ============================================================================
82+
// Double Quotes (2 quote chars) Tests
83+
// ============================================================================
84+
85+
[Fact]
86+
public static void TestDoubleDoubleQuotes()
87+
{
88+
var parser = new Parser();
89+
var result = parser.Parse("\"\"double double quotes\"\"");
90+
Assert.Equal("double double quotes", GetSingleRefId(result));
91+
}
92+
93+
[Fact]
94+
public static void TestDoubleDoubleQuotesWithSingleQuoteInside()
95+
{
96+
var parser = new Parser();
97+
var result = parser.Parse("\"\"text with \" inside\"\"");
98+
Assert.Equal("text with \" inside", GetSingleRefId(result));
99+
}
100+
101+
[Fact]
102+
public static void TestDoubleDoubleQuotesWithEscape()
103+
{
104+
var parser = new Parser();
105+
var result = parser.Parse("\"\"text with \"\"\"\" escaped double\"\"");
106+
Assert.Equal("text with \"\" escaped double", GetSingleRefId(result));
107+
}
108+
109+
[Fact]
110+
public static void TestDoubleSingleQuotes()
111+
{
112+
var parser = new Parser();
113+
var result = parser.Parse("''double single quotes''");
114+
Assert.Equal("double single quotes", GetSingleRefId(result));
115+
}
116+
117+
[Fact]
118+
public static void TestDoubleSingleQuotesWithSingleQuoteInside()
119+
{
120+
var parser = new Parser();
121+
var result = parser.Parse("''text with ' inside''");
122+
Assert.Equal("text with ' inside", GetSingleRefId(result));
123+
}
124+
125+
[Fact]
126+
public static void TestDoubleSingleQuotesWithEscape()
127+
{
128+
var parser = new Parser();
129+
var result = parser.Parse("''text with '''' escaped single''");
130+
Assert.Equal("text with '' escaped single", GetSingleRefId(result));
131+
}
132+
133+
[Fact]
134+
public static void TestDoubleBacktickQuotes()
135+
{
136+
var parser = new Parser();
137+
var result = parser.Parse("``double backtick quotes``");
138+
Assert.Equal("double backtick quotes", GetSingleRefId(result));
139+
}
140+
141+
[Fact]
142+
public static void TestDoubleBacktickQuotesWithBacktickInside()
143+
{
144+
var parser = new Parser();
145+
var result = parser.Parse("``text with ` inside``");
146+
Assert.Equal("text with ` inside", GetSingleRefId(result));
147+
}
148+
149+
[Fact]
150+
public static void TestDoubleBacktickQuotesWithEscape()
151+
{
152+
var parser = new Parser();
153+
var result = parser.Parse("``text with ```` escaped backtick``");
154+
Assert.Equal("text with `` escaped backtick", GetSingleRefId(result));
155+
}
156+
157+
// ============================================================================
158+
// Triple Quotes (3 quote chars) Tests
159+
// ============================================================================
160+
161+
[Fact]
162+
public static void TestTripleDoubleQuotes()
163+
{
164+
var parser = new Parser();
165+
var result = parser.Parse("\"\"\"triple double quotes\"\"\"");
166+
Assert.Equal("triple double quotes", GetSingleRefId(result));
167+
}
168+
169+
[Fact]
170+
public static void TestTripleDoubleQuotesWithDoubleQuoteInside()
171+
{
172+
var parser = new Parser();
173+
var result = parser.Parse("\"\"\"text with \"\" inside\"\"\"");
174+
Assert.Equal("text with \"\" inside", GetSingleRefId(result));
175+
}
176+
177+
[Fact]
178+
public static void TestTripleDoubleQuotesWithEscape()
179+
{
180+
var parser = new Parser();
181+
var result = parser.Parse("\"\"\"text with \"\"\"\"\"\" escaped triple\"\"\"");
182+
Assert.Equal("text with \"\"\" escaped triple", GetSingleRefId(result));
183+
}
184+
185+
[Fact]
186+
public static void TestTripleSingleQuotes()
187+
{
188+
var parser = new Parser();
189+
var result = parser.Parse("'''triple single quotes'''");
190+
Assert.Equal("triple single quotes", GetSingleRefId(result));
191+
}
192+
193+
[Fact]
194+
public static void TestTripleBacktickQuotes()
195+
{
196+
var parser = new Parser();
197+
var result = parser.Parse("```triple backtick quotes```");
198+
Assert.Equal("triple backtick quotes", GetSingleRefId(result));
199+
}
200+
201+
// ============================================================================
202+
// Quadruple Quotes (4 quote chars) Tests
203+
// ============================================================================
204+
205+
[Fact]
206+
public static void TestQuadrupleDoubleQuotes()
207+
{
208+
var parser = new Parser();
209+
var result = parser.Parse("\"\"\"\"quadruple double quotes\"\"\"\"");
210+
Assert.Equal("quadruple double quotes", GetSingleRefId(result));
211+
}
212+
213+
[Fact]
214+
public static void TestQuadrupleSingleQuotes()
215+
{
216+
var parser = new Parser();
217+
var result = parser.Parse("''''quadruple single quotes''''");
218+
Assert.Equal("quadruple single quotes", GetSingleRefId(result));
219+
}
220+
221+
[Fact]
222+
public static void TestQuadrupleBacktickQuotes()
223+
{
224+
var parser = new Parser();
225+
var result = parser.Parse("````quadruple backtick quotes````");
226+
Assert.Equal("quadruple backtick quotes", GetSingleRefId(result));
227+
}
228+
229+
// ============================================================================
230+
// Quintuple Quotes (5 quote chars) Tests
231+
// ============================================================================
232+
233+
[Fact]
234+
public static void TestQuintupleDoubleQuotes()
235+
{
236+
var parser = new Parser();
237+
var result = parser.Parse("\"\"\"\"\"quintuple double quotes\"\"\"\"\"");
238+
Assert.Equal("quintuple double quotes", GetSingleRefId(result));
239+
}
240+
241+
[Fact]
242+
public static void TestQuintupleSingleQuotes()
243+
{
244+
var parser = new Parser();
245+
var result = parser.Parse("'''''quintuple single quotes'''''");
246+
Assert.Equal("quintuple single quotes", GetSingleRefId(result));
247+
}
248+
249+
[Fact]
250+
public static void TestQuintupleBacktickQuotes()
251+
{
252+
var parser = new Parser();
253+
var result = parser.Parse("`````quintuple backtick quotes`````");
254+
Assert.Equal("quintuple backtick quotes", GetSingleRefId(result));
255+
}
256+
257+
// ============================================================================
258+
// Complex Scenarios Tests
259+
// ============================================================================
260+
261+
[Fact]
262+
public static void TestMixedQuotesInLink()
263+
{
264+
var parser = new Parser();
265+
var result = parser.Parse("(\"double\" 'single' `backtick`)");
266+
Assert.Single(result);
267+
Assert.NotNull(result[0].Values);
268+
Assert.Equal(3, result[0].Values!.Count);
269+
Assert.Equal("double", result[0].Values[0].Id);
270+
Assert.Equal("single", result[0].Values[1].Id);
271+
Assert.Equal("backtick", result[0].Values[2].Id);
272+
}
273+
274+
[Fact]
275+
public static void TestBacktickAsIdInLink()
276+
{
277+
var parser = new Parser();
278+
var result = parser.Parse("(`myId`: value1 value2)");
279+
Assert.Single(result);
280+
Assert.Equal("myId", result[0].Id);
281+
Assert.NotNull(result[0].Values);
282+
Assert.Equal(2, result[0].Values!.Count);
283+
}
284+
285+
[Fact]
286+
public static void TestCodeBlockLikeContent()
287+
{
288+
var parser = new Parser();
289+
var result = parser.Parse("```const x = 1;```");
290+
Assert.Equal("const x = 1;", GetSingleRefId(result));
291+
}
292+
293+
[Fact]
294+
public static void TestNestedQuotesInMarkdown()
295+
{
296+
var parser = new Parser();
297+
var result = parser.Parse("``Use `code` in markdown``");
298+
Assert.Equal("Use `code` in markdown", GetSingleRefId(result));
299+
}
300+
301+
[Fact]
302+
public static void TestJsonStringWithQuotes()
303+
{
304+
var parser = new Parser();
305+
var result = parser.Parse("\"\"{ \"key\": \"value\"}\"\"");
306+
Assert.Equal("{ \"key\": \"value\"}", GetSingleRefId(result));
307+
}
308+
309+
// ============================================================================
310+
// Edge Cases
311+
// ============================================================================
312+
313+
[Fact]
314+
public static void TestWhitespacePreservedInQuotes()
315+
{
316+
var parser = new Parser();
317+
var result = parser.Parse("\" spaces \"");
318+
Assert.Equal(" spaces ", GetSingleRefId(result));
319+
}
320+
321+
[Fact]
322+
public static void TestMultilineInDoubleDoubleQuotes()
323+
{
324+
var parser = new Parser();
325+
var result = parser.Parse("(\"\"line1\nline2\"\")");
326+
Assert.Single(result);
327+
Assert.NotNull(result[0].Values);
328+
Assert.Single(result[0].Values);
329+
Assert.Equal("line1\nline2", result[0].Values![0].Id);
330+
}
331+
332+
// ============================================================================
333+
// Unlimited Quotes (6+ quote chars) Tests
334+
// ============================================================================
335+
336+
[Fact]
337+
public static void TestUnlimitedQuotes6()
338+
{
339+
// Test 6-quote strings
340+
var parser = new Parser();
341+
var result = parser.Parse("\"\"\"\"\"\"hello\"\"\"\"\"\"");
342+
Assert.Equal("hello", GetSingleRefId(result));
343+
}
344+
345+
[Fact]
346+
public static void TestUnlimitedQuotes10()
347+
{
348+
// Test 10-quote strings
349+
var parser = new Parser();
350+
var result = parser.Parse("\"\"\"\"\"\"\"\"\"\"very deeply quoted\"\"\"\"\"\"\"\"\"\"");
351+
Assert.Equal("very deeply quoted", GetSingleRefId(result));
352+
}
353+
354+
[Fact]
355+
public static void TestUnlimitedQuotes6WithInnerQuotes()
356+
{
357+
// Test 6-quote strings with inner 5-quote sequences
358+
var parser = new Parser();
359+
var result = parser.Parse("\"\"\"\"\"\"hello with \"\"\"\"\" five quotes inside\"\"\"\"\"\"");
360+
Assert.Equal("hello with \"\"\"\"\" five quotes inside", GetSingleRefId(result));
361+
}
362+
363+
[Fact]
364+
public static void TestUnlimitedSingleQuotes7()
365+
{
366+
// Test 7-quote single quote strings
367+
var parser = new Parser();
368+
var result = parser.Parse("'''''''seven single quotes'''''''");
369+
Assert.Equal("seven single quotes", GetSingleRefId(result));
370+
}
371+
372+
[Fact]
373+
public static void TestUnlimitedBackticks8()
374+
{
375+
// Test 8-quote backtick strings
376+
var parser = new Parser();
377+
var result = parser.Parse("````````eight backticks````````");
378+
Assert.Equal("eight backticks", GetSingleRefId(result));
379+
}
380+
}
381+
}

csharp/Link.Foundation.Links.Notation/Link.Foundation.Links.Notation.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<Description>Link.Foundation's Platform.Protocols.Lino Class Library</Description>
55
<Copyright>Konstantin Diachenko</Copyright>
66
<AssemblyTitle>Link.Foundation.Links.Notation</AssemblyTitle>
7-
<VersionPrefix>0.12.0</VersionPrefix>
7+
<VersionPrefix>0.13.0</VersionPrefix>
88
<Authors>Konstantin Diachenko</Authors>
99
<TargetFramework>net8</TargetFramework>
1010
<PackageId>Link.Foundation.Links.Notation</PackageId>

0 commit comments

Comments
 (0)