links-notation 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -105,7 +105,7 @@ test('Parse nested structure with indentation', () => {
105
105
  expect(result[2].values.length).toBe(2);
106
106
  });
107
107
 
108
- test('Test indentation consistency', () => {
108
+ test('Indentation consistency', () => {
109
109
  // Test that indentation must be consistent
110
110
  const input = `parent
111
111
  child1
@@ -135,7 +135,7 @@ test('Complex indentation', () => {
135
135
  expect(result.length).toBe(6);
136
136
  });
137
137
 
138
- test('Test nested links', () => {
138
+ test('Nested links', () => {
139
139
  const input = '(1: (2: (3: 3)))';
140
140
  const parsed = parser.parse(input);
141
141
  expect(parsed.length).toBeGreaterThan(0);
@@ -148,7 +148,7 @@ test('Test nested links', () => {
148
148
  expect(parsed.length).toBe(1);
149
149
  });
150
150
 
151
- test('Test indentation (parser)', () => {
151
+ test('Indentation parser', () => {
152
152
  const input = 'parent\n child1\n child2';
153
153
  const result = parser.parse(input);
154
154
  expect(result.length).toBeGreaterThan(0);
@@ -157,10 +157,37 @@ test('Test indentation (parser)', () => {
157
157
  expect(hasParentLink).toBe(true);
158
158
  });
159
159
 
160
- test('Test nested indentation (parser)', () => {
160
+ test('Nested indentation parser', () => {
161
161
  const input = 'parent\n child\n grandchild';
162
162
  const result = parser.parse(input);
163
163
  expect(result.length).toBeGreaterThan(0);
164
164
  // Should create nested structure with proper hierarchy
165
165
  expect(result.length).toBeGreaterThanOrEqual(1);
166
+ });
167
+
168
+ test('Three level nesting roundtrip', () => {
169
+ const input = '(1: (2: (3: 3)))';
170
+ const parsed = parser.parse(input);
171
+
172
+ // Validate round-trip
173
+ const output = formatLinks(parsed);
174
+ expect(output).toBe(input);
175
+ });
176
+
177
+ test('Deep nested structure roundtrip', () => {
178
+ const input = '(a: (b: (c: (d: d))))';
179
+ const parsed = parser.parse(input);
180
+
181
+ // Validate round-trip
182
+ const output = formatLinks(parsed);
183
+ expect(output).toBe(input);
184
+ });
185
+
186
+ test('Multiple nested links roundtrip', () => {
187
+ const input = '(parent: (child1: value1) (child2: value2))';
188
+ const parsed = parser.parse(input);
189
+
190
+ // Validate round-trip
191
+ const output = formatLinks(parsed);
192
+ expect(output).toBe(input);
166
193
  });
@@ -0,0 +1,102 @@
1
+ import { test, expect } from 'bun:test';
2
+ import { Parser } from '../src/Parser.js';
3
+
4
+ test('Nested self-referenced object in pair value', () => {
5
+ // Test case from PARSER_BUG.md
6
+ // This should parse a dict with two pairs, where the second pair's value
7
+ // is itself a self-referenced dict definition (obj_1: dict ...)
8
+ const notation = '(obj_0: dict ((str bmFtZQ==) (str ZGljdDE=)) ((str b3RoZXI=) (obj_1: dict ((str bmFtZQ==) (str ZGljdDI=)) ((str b3RoZXI=) obj_0))))';
9
+
10
+ const parser = new Parser();
11
+ const links = parser.parse(notation);
12
+
13
+ // Should parse exactly one top-level link
14
+ expect(links.length).toBe(1);
15
+
16
+ const link = links[0];
17
+
18
+ // Top-level link should have ID "obj_0"
19
+ expect(link.id).toBe('obj_0');
20
+
21
+ // Should have: type marker + 2 pairs = 3 values
22
+ expect(link.values.length).toBe(3);
23
+
24
+ // First value is the type marker "dict"
25
+ expect(link.values[0].id).toBe('dict');
26
+
27
+ // Second and third values are the two pairs
28
+ const pair1 = link.values[1];
29
+ const pair2 = link.values[2];
30
+
31
+ // Pair 1: ((str bmFtZQ==) (str ZGljdDE=))
32
+ // This is a parenthesized expression containing two sub-expressions
33
+ expect(pair1.id).toBe(null);
34
+ expect(pair1.values.length).toBe(2);
35
+
36
+ // First element of pair1: (str bmFtZQ==)
37
+ expect(pair1.values[0].id).toBe(null);
38
+ expect(pair1.values[0].values.length).toBe(2); // "str" and "bmFtZQ=="
39
+ expect(pair1.values[0].values[0].id).toBe('str');
40
+ expect(pair1.values[0].values[1].id).toBe('bmFtZQ==');
41
+
42
+ // Second element of pair1: (str ZGljdDE=)
43
+ expect(pair1.values[1].id).toBe(null);
44
+ expect(pair1.values[1].values.length).toBe(2); // "str" and "ZGljdDE="
45
+ expect(pair1.values[1].values[0].id).toBe('str');
46
+ expect(pair1.values[1].values[1].id).toBe('ZGljdDE=');
47
+
48
+ // Pair 2: ((str b3RoZXI=) (obj_1: dict ...))
49
+ // This is the critical test - the second element should be a self-referenced dict
50
+ expect(pair2.id).toBe(null);
51
+ expect(pair2.values.length).toBe(2);
52
+
53
+ // First element of pair2: (str b3RoZXI=)
54
+ expect(pair2.values[0].id).toBe(null);
55
+ expect(pair2.values[0].values.length).toBe(2);
56
+ expect(pair2.values[0].values[0].id).toBe('str');
57
+ expect(pair2.values[0].values[1].id).toBe('b3RoZXI=');
58
+
59
+ // Second element of pair2: (obj_1: dict ((str bmFtZQ==) (str ZGljdDI=)) ((str b3RoZXI=) obj_0))
60
+ // THIS IS THE KEY TEST - obj_1 should have its ID preserved
61
+ const obj1 = pair2.values[1];
62
+ expect(obj1.id).toBe('obj_1'); // This is the self-reference ID
63
+ expect(obj1.values).toBeTruthy(); // Should have nested values (dict definition)
64
+ expect(obj1.values.length).toBe(3); // type marker + 2 pairs
65
+
66
+ // obj_1's type marker
67
+ expect(obj1.values[0].id).toBe('dict');
68
+
69
+ // obj_1's first pair: ((str bmFtZQ==) (str ZGljdDI=))
70
+ const obj1_pair1 = obj1.values[1];
71
+ expect(obj1_pair1.values.length).toBe(2);
72
+ expect(obj1_pair1.values[0].values[0].id).toBe('str');
73
+ expect(obj1_pair1.values[0].values[1].id).toBe('bmFtZQ==');
74
+ expect(obj1_pair1.values[1].values[0].id).toBe('str');
75
+ expect(obj1_pair1.values[1].values[1].id).toBe('ZGljdDI=');
76
+
77
+ // obj_1's second pair: ((str b3RoZXI=) obj_0) - reference back to obj_0
78
+ const obj1_pair2 = obj1.values[2];
79
+ expect(obj1_pair2.values.length).toBe(2);
80
+ expect(obj1_pair2.values[0].values[0].id).toBe('str');
81
+ expect(obj1_pair2.values[0].values[1].id).toBe('b3RoZXI=');
82
+ expect(obj1_pair2.values[1].id).toBe('obj_0'); // Reference to obj_0
83
+ expect(obj1_pair2.values[1].values).toEqual([]); // Just a reference, no nested values
84
+ });
85
+
86
+ test('Self-reference as direct child works correctly', () => {
87
+ // This pattern should work (and did work before)
88
+ const notation = '(obj_0: list (int 1) (int 2) (obj_1: list (int 3) (int 4) obj_0))';
89
+
90
+ const parser = new Parser();
91
+ const links = parser.parse(notation);
92
+
93
+ expect(links.length).toBe(1);
94
+ expect(links[0].id).toBe('obj_0');
95
+ expect(links[0].values.length).toBe(4); // list + 1 + 2 + obj_1
96
+
97
+ // The fourth value should be obj_1 with a self-reference
98
+ const obj1 = links[0].values[3];
99
+ expect(obj1.id).toBe('obj_1');
100
+ expect(obj1.values.length).toBe(4); // list + 3 + 4 + obj_0
101
+ expect(obj1.values[3].id).toBe('obj_0'); // Reference back to obj_0
102
+ });
@@ -4,28 +4,28 @@ import { formatLinks } from '../src/Link.js';
4
4
 
5
5
  const parser = new Parser();
6
6
 
7
- test('SingleLinkTest', () => {
7
+ test('Single link', () => {
8
8
  const source = '(address: source target)';
9
9
  const links = parser.parse(source);
10
10
  const target = formatLinks(links);
11
11
  expect(target).toBe(source);
12
12
  });
13
13
 
14
- test('TripletSingleLinkTest', () => {
14
+ test('Triplet single link', () => {
15
15
  const source = '(papa has car)';
16
16
  const links = parser.parse(source);
17
17
  const target = formatLinks(links);
18
18
  expect(target).toBe(source);
19
19
  });
20
20
 
21
- test('BugTest1', () => {
21
+ test('Bug test 1', () => {
22
22
  const source = '(ignore conan-center-index repository)';
23
23
  const links = parser.parse(source);
24
24
  const target = formatLinks(links);
25
25
  expect(target).toBe(source);
26
26
  });
27
27
 
28
- test('QuotedReferencesTest', () => {
28
+ test('Quoted references', () => {
29
29
  const source = `(a: 'b' "c")`;
30
30
  const target = `(a: b c)`;
31
31
  const links = parser.parse(source);
@@ -33,7 +33,7 @@ test('QuotedReferencesTest', () => {
33
33
  expect(formattedLinks).toBe(target);
34
34
  });
35
35
 
36
- test('QuotedReferencesWithSpacesTest', () => {
36
+ test('Quoted references with spaces', () => {
37
37
  const source = `('a a': 'b b' "c c")`;
38
38
  const target = `('a a': 'b b' 'c c')`;
39
39
  const links = parser.parse(source);
@@ -127,7 +127,7 @@ test('Test value link', () => {
127
127
  expect(result.length).toBeGreaterThan(0);
128
128
  });
129
129
 
130
- test('ParseQuotedReferencesValuesOnly', () => {
130
+ test('Parse quoted references values only', () => {
131
131
  const source = `"has space" 'has:colon'`;
132
132
  const parser = new Parser();
133
133
  const links = parser.parse(source);
@@ -226,4 +226,84 @@ test('Test value link (parser)', () => {
226
226
  expect(result.length).toBe(1);
227
227
  expect(result[0].id).toBe(null);
228
228
  expect(result[0].values.length).toBe(3);
229
+ });
230
+
231
+ test('Multiline without id', () => {
232
+ // Test that '(:)' syntax is forbidden and should throw an error
233
+ const input = '(: value1 value2)';
234
+ // JS parser forbids this syntax
235
+ expect(() => parser.parse(input)).toThrow();
236
+ });
237
+
238
+ test('Parse values only standalone colon', () => {
239
+ // Test that standalone ':' is forbidden and should throw an error
240
+ const input = ': value1 value2';
241
+ // JS parser forbids this syntax
242
+ expect(() => parser.parse(input)).toThrow();
243
+ });
244
+
245
+ test('Quoted references with spaces in link', () => {
246
+ const input = '(id: "value with spaces")';
247
+ const result = parser.parse(input);
248
+ expect(result.length).toBe(1);
249
+ expect(result[0].id).toBe('id');
250
+ expect(result[0].values.length).toBe(1);
251
+ expect(result[0].values[0].id).toBe('value with spaces');
252
+ });
253
+
254
+ test('Single line without id', () => {
255
+ // Test parsing a single line without ID (just values)
256
+ const input = '(value1 value2)';
257
+ const result = parser.parse(input);
258
+ expect(result.length).toBe(1);
259
+ expect(result[0].id).toBe(null);
260
+ expect(result[0].values.length).toBe(2);
261
+ });
262
+
263
+ test('Link with id', () => {
264
+ const input = '(id: a b c)';
265
+ const result = parser.parse(input);
266
+ expect(result.length).toBe(1);
267
+ expect(result[0].id).toBe('id');
268
+ expect(result[0].values.length).toBe(3);
269
+ });
270
+
271
+ test('Quoted reference', () => {
272
+ const input = '"quoted value"';
273
+ const result = parser.parse(input);
274
+ expect(result.length).toBe(1);
275
+ expect(result[0].id).toBe(null);
276
+ expect(result[0].values.length).toBe(1);
277
+ expect(result[0].values[0].id).toBe('quoted value');
278
+ });
279
+
280
+ test('Quoted references with special chars', () => {
281
+ const input = '("special:char" "another@char")';
282
+ const result = parser.parse(input);
283
+ expect(result.length).toBe(1);
284
+ expect(result[0].id).toBe(null);
285
+ expect(result[0].values.length).toBe(2);
286
+ expect(result[0].values[0].id).toBe('special:char');
287
+ expect(result[0].values[1].id).toBe('another@char');
288
+ });
289
+
290
+ test('Simple reference', () => {
291
+ const input = 'simplereference';
292
+ const result = parser.parse(input);
293
+ expect(result.length).toBeGreaterThan(0);
294
+ });
295
+
296
+ test('Single line link', () => {
297
+ const input = 'id: value1 value2';
298
+ const result = parser.parse(input);
299
+ expect(result.length).toBe(1);
300
+ expect(result[0].id).toBe('id');
301
+ expect(result[0].values.length).toBe(2);
302
+ });
303
+
304
+ test('Single line with id', () => {
305
+ const input = 'myid: val1 val2';
306
+ const result = parser.parse(input);
307
+ expect(result.length).toBeGreaterThan(0);
308
+ expect(result[0].id).toBe('myid');
229
309
  });