links-notation 0.11.2 → 0.13.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.
@@ -4,7 +4,7 @@ import { formatLinks } from '../src/Link.js';
4
4
 
5
5
  const parser = new Parser();
6
6
 
7
- test('TwoLinksTest', () => {
7
+ test('Two links', () => {
8
8
  const source = `(first: x y)
9
9
  (second: a b)`;
10
10
  const links = parser.parse(source);
@@ -12,7 +12,7 @@ test('TwoLinksTest', () => {
12
12
  expect(target).toBe(source);
13
13
  });
14
14
 
15
- test('ParseAndStringifyTest', () => {
15
+ test('Parse and stringify', () => {
16
16
  const source = `(papa (lovesMama: loves mama))
17
17
  (son lovesMama)
18
18
  (daughter lovesMama)
@@ -22,7 +22,7 @@ test('ParseAndStringifyTest', () => {
22
22
  expect(target).toBe(source);
23
23
  });
24
24
 
25
- test('ParseAndStringifyTest2', () => {
25
+ test('Parse and stringify test 2', () => {
26
26
  const source = `father (lovesMom: loves mom)
27
27
  son lovesMom
28
28
  daughter lovesMom
@@ -32,7 +32,7 @@ all (love mom)`;
32
32
  expect(target).toBe(source);
33
33
  });
34
34
 
35
- test('ParseAndStringifyWithLessParenthesesTest', () => {
35
+ test('Parse and stringify with less parentheses', () => {
36
36
  const source = `lovesMama: loves mama
37
37
  papa lovesMama
38
38
  son lovesMama
@@ -43,7 +43,7 @@ all (love mama)`;
43
43
  expect(target).toBe(source);
44
44
  });
45
45
 
46
- test('DuplicateIdentifiersTest', () => {
46
+ test('Duplicate identifiers', () => {
47
47
  const source = `(a: a b)
48
48
  (a: b c)`;
49
49
  const target = `(a: a b)
@@ -61,7 +61,7 @@ test('Test complex structure', () => {
61
61
  Value
62
62
  (property: name type)
63
63
  (method: name params return)`;
64
-
64
+
65
65
  const result = parser.parse(input);
66
66
  expect(result.length).toBeGreaterThan(0);
67
67
  });
@@ -75,44 +75,44 @@ simple_ref
75
75
  nested1
76
76
  nested2
77
77
  )`;
78
-
78
+
79
79
  const result = parser.parse(input);
80
80
  expect(result.length).toBeGreaterThan(0);
81
81
  });
82
82
 
83
83
  test('Test multiline with id', () => {
84
84
  // Test multi-line link with id
85
- const input = "(id: value1 value2)";
85
+ const input = '(id: value1 value2)';
86
86
  const result = parser.parse(input);
87
87
  expect(result.length).toBeGreaterThan(0);
88
88
  });
89
89
 
90
90
  test('Test multiple top level elements', () => {
91
91
  // Test multiple top-level elements
92
- const input = "(elem1: val1)\n(elem2: val2)";
92
+ const input = '(elem1: val1)\n(elem2: val2)';
93
93
  const result = parser.parse(input);
94
94
  expect(result.length).toBeGreaterThan(0);
95
95
  });
96
96
 
97
97
  test('Test multiline simple links', () => {
98
- const input = "(1: 1 1)\n(2: 2 2)";
98
+ const input = '(1: 1 1)\n(2: 2 2)';
99
99
  const parsed = parser.parse(input);
100
100
  expect(parsed.length).toBeGreaterThan(0);
101
-
101
+
102
102
  // Validate regular formatting
103
103
  const output = formatLinks(parsed);
104
- expect(output).toContain("(1: 1 1)");
105
- expect(output).toContain("(2: 2 2)");
106
-
104
+ expect(output).toContain('(1: 1 1)');
105
+ expect(output).toContain('(2: 2 2)');
106
+
107
107
  // Validate alternate formatting matches input
108
108
  const outputAlternate = formatLinks(parsed);
109
109
  expect(outputAlternate).toBe(input);
110
110
  });
111
111
 
112
112
  test('Test indented children', () => {
113
- const input = "parent\n child1\n child2";
113
+ const input = 'parent\n child1\n child2';
114
114
  const parsed = parser.parse(input);
115
-
115
+
116
116
  // The parsed structure should have parent with children
117
117
  expect(parsed.length).toBeGreaterThan(0);
118
- });
118
+ });
@@ -1,9 +1,9 @@
1
- import { test, expect } from "bun:test";
2
- import { Parser } from "../src/Parser.js";
1
+ import { test, expect } from 'bun:test';
2
+ import { Parser } from '../src/Parser.js';
3
3
 
4
4
  const parser = new Parser();
5
5
 
6
- test("TestMultilineDoubleQuotedReference", () => {
6
+ test('TestMultilineDoubleQuotedReference', () => {
7
7
  const input = `(
8
8
  "long
9
9
  string literal representing
@@ -32,7 +32,7 @@ long string literal
32
32
  as another reference`);
33
33
  });
34
34
 
35
- test("TestSimpleMultilineDoubleQuoted", () => {
35
+ test('TestSimpleMultilineDoubleQuoted', () => {
36
36
  const input = `("line1
37
37
  line2")`;
38
38
  const result = parser.parse(input);
@@ -44,10 +44,10 @@ line2")`;
44
44
  expect(link.id).toBe(null);
45
45
  expect(link.values).toBeTruthy();
46
46
  expect(link.values.length).toBe(1);
47
- expect(link.values[0].id).toBe("line1\nline2");
47
+ expect(link.values[0].id).toBe('line1\nline2');
48
48
  });
49
49
 
50
- test("TestSimpleMultilineSingleQuoted", () => {
50
+ test('TestSimpleMultilineSingleQuoted', () => {
51
51
  const input = `('line1
52
52
  line2')`;
53
53
  const result = parser.parse(input);
@@ -59,10 +59,10 @@ line2')`;
59
59
  expect(link.id).toBe(null);
60
60
  expect(link.values).toBeTruthy();
61
61
  expect(link.values.length).toBe(1);
62
- expect(link.values[0].id).toBe("line1\nline2");
62
+ expect(link.values[0].id).toBe('line1\nline2');
63
63
  });
64
64
 
65
- test("TestMultilineQuotedAsId", () => {
65
+ test('TestMultilineQuotedAsId', () => {
66
66
  const input = `("multi
67
67
  line
68
68
  id": value1 value2)`;
@@ -72,7 +72,7 @@ id": value1 value2)`;
72
72
  expect(result.length).toBe(1);
73
73
 
74
74
  const link = result[0];
75
- expect(link.id).toBe("multi\nline\nid");
75
+ expect(link.id).toBe('multi\nline\nid');
76
76
  expect(link.values).toBeTruthy();
77
77
  expect(link.values.length).toBe(2);
78
78
  });
@@ -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);
@@ -143,24 +143,53 @@ test('Test nested links', () => {
143
143
  // Validate regular formatting
144
144
  const output = formatLinks(parsed);
145
145
  expect(output).toBeTruthy();
146
-
146
+
147
147
  // Validate that the structure is properly nested
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);
155
155
  // Should have parent link
156
- const hasParentLink = result.some(l => l.values && l.values.some(v => v.id === 'parent'));
156
+ const hasParentLink = result.some(
157
+ (l) => l.values && l.values.some((v) => v.id === 'parent')
158
+ );
157
159
  expect(hasParentLink).toBe(true);
158
160
  });
159
161
 
160
- test('Test nested indentation (parser)', () => {
162
+ test('Nested indentation parser', () => {
161
163
  const input = 'parent\n child\n grandchild';
162
164
  const result = parser.parse(input);
163
165
  expect(result.length).toBeGreaterThan(0);
164
166
  // Should create nested structure with proper hierarchy
165
167
  expect(result.length).toBeGreaterThanOrEqual(1);
166
- });
168
+ });
169
+
170
+ test('Three level nesting roundtrip', () => {
171
+ const input = '(1: (2: (3: 3)))';
172
+ const parsed = parser.parse(input);
173
+
174
+ // Validate round-trip
175
+ const output = formatLinks(parsed);
176
+ expect(output).toBe(input);
177
+ });
178
+
179
+ test('Deep nested structure roundtrip', () => {
180
+ const input = '(a: (b: (c: (d: d))))';
181
+ const parsed = parser.parse(input);
182
+
183
+ // Validate round-trip
184
+ const output = formatLinks(parsed);
185
+ expect(output).toBe(input);
186
+ });
187
+
188
+ test('Multiple nested links roundtrip', () => {
189
+ const input = '(parent: (child1: value1) (child2: value2))';
190
+ const parsed = parser.parse(input);
191
+
192
+ // Validate round-trip
193
+ const output = formatLinks(parsed);
194
+ expect(output).toBe(input);
195
+ });
@@ -0,0 +1,104 @@
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 =
9
+ '(obj_0: dict ((str bmFtZQ==) (str ZGljdDE=)) ((str b3RoZXI=) (obj_1: dict ((str bmFtZQ==) (str ZGljdDI=)) ((str b3RoZXI=) obj_0))))';
10
+
11
+ const parser = new Parser();
12
+ const links = parser.parse(notation);
13
+
14
+ // Should parse exactly one top-level link
15
+ expect(links.length).toBe(1);
16
+
17
+ const link = links[0];
18
+
19
+ // Top-level link should have ID "obj_0"
20
+ expect(link.id).toBe('obj_0');
21
+
22
+ // Should have: type marker + 2 pairs = 3 values
23
+ expect(link.values.length).toBe(3);
24
+
25
+ // First value is the type marker "dict"
26
+ expect(link.values[0].id).toBe('dict');
27
+
28
+ // Second and third values are the two pairs
29
+ const pair1 = link.values[1];
30
+ const pair2 = link.values[2];
31
+
32
+ // Pair 1: ((str bmFtZQ==) (str ZGljdDE=))
33
+ // This is a parenthesized expression containing two sub-expressions
34
+ expect(pair1.id).toBe(null);
35
+ expect(pair1.values.length).toBe(2);
36
+
37
+ // First element of pair1: (str bmFtZQ==)
38
+ expect(pair1.values[0].id).toBe(null);
39
+ expect(pair1.values[0].values.length).toBe(2); // "str" and "bmFtZQ=="
40
+ expect(pair1.values[0].values[0].id).toBe('str');
41
+ expect(pair1.values[0].values[1].id).toBe('bmFtZQ==');
42
+
43
+ // Second element of pair1: (str ZGljdDE=)
44
+ expect(pair1.values[1].id).toBe(null);
45
+ expect(pair1.values[1].values.length).toBe(2); // "str" and "ZGljdDE="
46
+ expect(pair1.values[1].values[0].id).toBe('str');
47
+ expect(pair1.values[1].values[1].id).toBe('ZGljdDE=');
48
+
49
+ // Pair 2: ((str b3RoZXI=) (obj_1: dict ...))
50
+ // This is the critical test - the second element should be a self-referenced dict
51
+ expect(pair2.id).toBe(null);
52
+ expect(pair2.values.length).toBe(2);
53
+
54
+ // First element of pair2: (str b3RoZXI=)
55
+ expect(pair2.values[0].id).toBe(null);
56
+ expect(pair2.values[0].values.length).toBe(2);
57
+ expect(pair2.values[0].values[0].id).toBe('str');
58
+ expect(pair2.values[0].values[1].id).toBe('b3RoZXI=');
59
+
60
+ // Second element of pair2: (obj_1: dict ((str bmFtZQ==) (str ZGljdDI=)) ((str b3RoZXI=) obj_0))
61
+ // THIS IS THE KEY TEST - obj_1 should have its ID preserved
62
+ const obj1 = pair2.values[1];
63
+ expect(obj1.id).toBe('obj_1'); // This is the self-reference ID
64
+ expect(obj1.values).toBeTruthy(); // Should have nested values (dict definition)
65
+ expect(obj1.values.length).toBe(3); // type marker + 2 pairs
66
+
67
+ // obj_1's type marker
68
+ expect(obj1.values[0].id).toBe('dict');
69
+
70
+ // obj_1's first pair: ((str bmFtZQ==) (str ZGljdDI=))
71
+ const obj1_pair1 = obj1.values[1];
72
+ expect(obj1_pair1.values.length).toBe(2);
73
+ expect(obj1_pair1.values[0].values[0].id).toBe('str');
74
+ expect(obj1_pair1.values[0].values[1].id).toBe('bmFtZQ==');
75
+ expect(obj1_pair1.values[1].values[0].id).toBe('str');
76
+ expect(obj1_pair1.values[1].values[1].id).toBe('ZGljdDI=');
77
+
78
+ // obj_1's second pair: ((str b3RoZXI=) obj_0) - reference back to obj_0
79
+ const obj1_pair2 = obj1.values[2];
80
+ expect(obj1_pair2.values.length).toBe(2);
81
+ expect(obj1_pair2.values[0].values[0].id).toBe('str');
82
+ expect(obj1_pair2.values[0].values[1].id).toBe('b3RoZXI=');
83
+ expect(obj1_pair2.values[1].id).toBe('obj_0'); // Reference to obj_0
84
+ expect(obj1_pair2.values[1].values).toEqual([]); // Just a reference, no nested values
85
+ });
86
+
87
+ test('Self-reference as direct child works correctly', () => {
88
+ // This pattern should work (and did work before)
89
+ const notation =
90
+ '(obj_0: list (int 1) (int 2) (obj_1: list (int 3) (int 4) obj_0))';
91
+
92
+ const parser = new Parser();
93
+ const links = parser.parse(notation);
94
+
95
+ expect(links.length).toBe(1);
96
+ expect(links[0].id).toBe('obj_0');
97
+ expect(links[0].values.length).toBe(4); // list + 1 + 2 + obj_1
98
+
99
+ // The fourth value should be obj_1 with a self-reference
100
+ const obj1 = links[0].values[3];
101
+ expect(obj1.id).toBe('obj_1');
102
+ expect(obj1.values.length).toBe(4); // list + 3 + 4 + obj_0
103
+ expect(obj1.values[3].id).toBe('obj_0'); // Reference back to obj_0
104
+ });
@@ -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);
@@ -161,7 +161,7 @@ test('Test special characters in quotes', () => {
161
161
  let input = '("key:with:colons": "value(with)parens")';
162
162
  let result = parser.parse(input);
163
163
  expect(result.length).toBeGreaterThan(0);
164
-
164
+
165
165
  input = "('key with spaces': 'value: with special chars')";
166
166
  result = parser.parse(input);
167
167
  expect(result.length).toBeGreaterThan(0);
@@ -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
- });
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');
309
+ });