@k-l-lambda/lilylet 0.1.80 → 0.1.81

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.
@@ -163,7 +163,11 @@ const renderRepeatSpan = (infos, lo, hi) => {
163
163
  return rangeCode(lo, hi);
164
164
  }
165
165
  const endInfo = repeatEnds[0];
166
- const startIdx = span.find(i => i.repeatStart)?.index ?? lo;
166
+ // The repeat-start for THIS end is the latest repeat-start at or before it; a
167
+ // repeat-start sitting AFTER the end (e.g. the second half of a ":|:"/"::" that
168
+ // opens the next section) belongs to a following span, not this one. Falling
169
+ // back to `lo` models the implicit "repeat from the start of the span".
170
+ const startIdx = span.filter(i => i.repeatStart && i.index <= endInfo.index).map(i => i.index).sort((a, b) => b - a)[0] ?? lo;
167
171
  const times = endInfo.repeatTimes ?? 2;
168
172
  const endingStarts = span.filter(i => i.endingStart !== undefined).map(i => i.index).sort((a, b) => a - b);
169
173
  if (endingStarts.length === 0) {
@@ -301,9 +305,14 @@ const buildDaCapoABA = (infos, dcIdx) => {
301
305
  const restCode = renderRepeatSections(infos, restLo, dcIdx);
302
306
  if (mainCode === null || restCode === null)
303
307
  return null;
304
- // main wraps in [..] only when it is a comma-separated sequence (so the ABA
305
- // parser reads it as one block); a single token (e.g. "2*[..]") needs no bracket.
306
- const main = /,/.test(mainCode) ? `[${mainCode}]` : mainCode;
308
+ // `main` is parsed by the ABA grammar as ONE item (parseItem). It needs wrapping
309
+ // in [..] when it is a comma sequence ("1, 2*[..]") or a BARE range ("1..37",
310
+ // which parseItem stops at the first number). A lone number, or a single
311
+ // already-structured token ("2*[..]" / "2*[..]{..}" / "[..]"), parses as one item
312
+ // and needs no extra bracket. `rest` is parsed as a sequence, so it only needs
313
+ // bracketing to group a comma sequence — a range there is fine.
314
+ const isSingleToken = /^\d+$/.test(mainCode) || /^\d+\*\[/.test(mainCode) || /^\[/.test(mainCode);
315
+ const main = isSingleToken ? mainCode : `[${mainCode}]`;
307
316
  const rest = /,/.test(restCode) ? `[${restCode}]` : restCode;
308
317
  return `<${main}, ${rest}>`;
309
318
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@k-l-lambda/lilylet",
3
- "version": "0.1.80",
3
+ "version": "0.1.81",
4
4
  "description": "Lilylet is a lilyopnd-like sheet music language designed for Markdown rendering and symbolic music representation in AIGC applications.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -174,7 +174,11 @@ const renderRepeatSpan = (infos: MeasureRepeatInfo[], lo: number, hi: number): s
174
174
  }
175
175
 
176
176
  const endInfo = repeatEnds[0];
177
- const startIdx = span.find(i => i.repeatStart)?.index ?? lo;
177
+ // The repeat-start for THIS end is the latest repeat-start at or before it; a
178
+ // repeat-start sitting AFTER the end (e.g. the second half of a ":|:"/"::" that
179
+ // opens the next section) belongs to a following span, not this one. Falling
180
+ // back to `lo` models the implicit "repeat from the start of the span".
181
+ const startIdx = span.filter(i => i.repeatStart && i.index <= endInfo.index).map(i => i.index).sort((a, b) => b - a)[0] ?? lo;
178
182
  const times = endInfo.repeatTimes ?? 2;
179
183
  const endingStarts = span.filter(i => i.endingStart !== undefined).map(i => i.index).sort((a, b) => a - b);
180
184
 
@@ -312,9 +316,14 @@ const buildDaCapoABA = (infos: MeasureRepeatInfo[], dcIdx: number): string | nul
312
316
  const restCode = renderRepeatSections(infos, restLo, dcIdx);
313
317
  if (mainCode === null || restCode === null) return null;
314
318
 
315
- // main wraps in [..] only when it is a comma-separated sequence (so the ABA
316
- // parser reads it as one block); a single token (e.g. "2*[..]") needs no bracket.
317
- const main = /,/.test(mainCode) ? `[${mainCode}]` : mainCode;
319
+ // `main` is parsed by the ABA grammar as ONE item (parseItem). It needs wrapping
320
+ // in [..] when it is a comma sequence ("1, 2*[..]") or a BARE range ("1..37",
321
+ // which parseItem stops at the first number). A lone number, or a single
322
+ // already-structured token ("2*[..]" / "2*[..]{..}" / "[..]"), parses as one item
323
+ // and needs no extra bracket. `rest` is parsed as a sequence, so it only needs
324
+ // bracketing to group a comma sequence — a range there is fine.
325
+ const isSingleToken = /^\d+$/.test(mainCode) || /^\d+\*\[/.test(mainCode) || /^\[/.test(mainCode);
326
+ const main = isSingleToken ? mainCode : `[${mainCode}]`;
318
327
  const rest = /,/.test(restCode) ? `[${restCode}]` : restCode;
319
328
  return `<${main}, ${rest}>`;
320
329
  };