@k-l-lambda/lilylet 0.1.71 → 0.1.73
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.
- package/lib/highlight.d.ts +1 -0
- package/lib/highlight.js +1 -0
- package/lib/lilylet/highlight.d.ts +29 -0
- package/lib/lilylet/highlight.js +145 -0
- package/package.json +8 -2
- package/source/lilylet/highlight.ts +192 -0
- package/lib/source/abc/abc.d.ts +0 -102
- package/lib/source/abc/abc.js +0 -25
- package/lib/source/abc/parser.d.ts +0 -3
- package/lib/source/abc/parser.js +0 -6
- package/lib/source/lilylet/abcDecoder.d.ts +0 -25
- package/lib/source/lilylet/abcDecoder.js +0 -1035
- package/lib/source/lilylet/index.d.ts +0 -10
- package/lib/source/lilylet/index.js +0 -10
- package/lib/source/lilylet/lilypondDecoder.d.ts +0 -29
- package/lib/source/lilylet/lilypondDecoder.js +0 -1223
- package/lib/source/lilylet/lilypondEncoder.d.ts +0 -34
- package/lib/source/lilylet/lilypondEncoder.js +0 -893
- package/lib/source/lilylet/meiEncoder.d.ts +0 -8
- package/lib/source/lilylet/meiEncoder.js +0 -1985
- package/lib/source/lilylet/musicXmlDecoder.d.ts +0 -20
- package/lib/source/lilylet/musicXmlDecoder.js +0 -1195
- package/lib/source/lilylet/musicXmlEncoder.d.ts +0 -15
- package/lib/source/lilylet/musicXmlEncoder.js +0 -701
- package/lib/source/lilylet/musicXmlTypes.d.ts +0 -199
- package/lib/source/lilylet/musicXmlTypes.js +0 -7
- package/lib/source/lilylet/musicXmlUtils.d.ts +0 -92
- package/lib/source/lilylet/musicXmlUtils.js +0 -469
- package/lib/source/lilylet/parser.d.ts +0 -14
- package/lib/source/lilylet/parser.js +0 -161
- package/lib/source/lilylet/serializer.d.ts +0 -11
- package/lib/source/lilylet/serializer.js +0 -791
- package/lib/source/lilylet/types.d.ts +0 -253
- package/lib/source/lilylet/types.js +0 -100
- package/lib/tests/abc-abcjs-parse.d.ts +0 -8
- package/lib/tests/abc-abcjs-parse.js +0 -90
- package/lib/tests/abc-abcjs-svg.d.ts +0 -1
- package/lib/tests/abc-abcjs-svg.js +0 -143
- package/lib/tests/abc-decoder.d.ts +0 -1
- package/lib/tests/abc-decoder.js +0 -67
- package/lib/tests/abc-mei-compare.d.ts +0 -1
- package/lib/tests/abc-mei-compare.js +0 -525
- package/lib/tests/auto-beam.d.ts +0 -9
- package/lib/tests/auto-beam.js +0 -151
- package/lib/tests/computeMeiHashes.d.ts +0 -1
- package/lib/tests/computeMeiHashes.js +0 -87
- package/lib/tests/encoder-mutation.d.ts +0 -9
- package/lib/tests/encoder-mutation.js +0 -110
- package/lib/tests/gpt-review-issues.d.ts +0 -5
- package/lib/tests/gpt-review-issues.js +0 -255
- package/lib/tests/json-to-lyl.d.ts +0 -1
- package/lib/tests/json-to-lyl.js +0 -18
- package/lib/tests/lilypond-roundtrip.d.ts +0 -7
- package/lib/tests/lilypond-roundtrip.js +0 -558
- package/lib/tests/lilypondDecoder.d.ts +0 -6
- package/lib/tests/lilypondDecoder.js +0 -95
- package/lib/tests/ly-to-lyl.d.ts +0 -1
- package/lib/tests/ly-to-lyl.js +0 -12
- package/lib/tests/mei.d.ts +0 -1
- package/lib/tests/mei.js +0 -278
- package/lib/tests/musicxml-decoder.d.ts +0 -4
- package/lib/tests/musicxml-decoder.js +0 -61
- package/lib/tests/musicxml-detail.d.ts +0 -4
- package/lib/tests/musicxml-detail.js +0 -85
- package/lib/tests/musicxml-fprod.d.ts +0 -9
- package/lib/tests/musicxml-fprod.js +0 -153
- package/lib/tests/musicxml-roundtrip.d.ts +0 -7
- package/lib/tests/musicxml-roundtrip.js +0 -296
- package/lib/tests/musicxml-to-mei.d.ts +0 -6
- package/lib/tests/musicxml-to-mei.js +0 -115
- package/lib/tests/parser.d.ts +0 -1
- package/lib/tests/parser.js +0 -17
- package/lib/tests/render-k283.d.ts +0 -1
- package/lib/tests/render-k283.js +0 -33
- package/lib/tests/render-lyl.d.ts +0 -1
- package/lib/tests/render-lyl.js +0 -35
- package/lib/tests/unit/afterGraceInsideTuplet.test.d.ts +0 -23
- package/lib/tests/unit/afterGraceInsideTuplet.test.js +0 -186
- package/lib/tests/unit/changeStaffBeforeTuplet.test.d.ts +0 -21
- package/lib/tests/unit/changeStaffBeforeTuplet.test.js +0 -356
- package/lib/tests/unit/crossStaffDecoder.test.d.ts +0 -15
- package/lib/tests/unit/crossStaffDecoder.test.js +0 -147
- package/lib/tests/unit/crossStaffEdgeCases.test.d.ts +0 -1
- package/lib/tests/unit/crossStaffEdgeCases.test.js +0 -209
- package/lib/tests/unit/crossStaffMultiMeasure.test.d.ts +0 -15
- package/lib/tests/unit/crossStaffMultiMeasure.test.js +0 -231
- package/lib/tests/unit/fullMeasureRestDecoder.test.d.ts +0 -11
- package/lib/tests/unit/fullMeasureRestDecoder.test.js +0 -154
- package/lib/tests/unit/gptReviewIssues.test.d.ts +0 -8
- package/lib/tests/unit/gptReviewIssues.test.js +0 -240
- package/lib/tests/unit/parallelMusicDecoder.test.d.ts +0 -13
- package/lib/tests/unit/parallelMusicDecoder.test.js +0 -261
- package/lib/tests/unit/partialWarning.test.d.ts +0 -4
- package/lib/tests/unit/partialWarning.test.js +0 -65
- package/lib/tests/unit/serializerRoundTrip.test.d.ts +0 -8
- package/lib/tests/unit/serializerRoundTrip.test.js +0 -263
- package/lib/tests/unit/staffInsideTuplet.test.d.ts +0 -25
- package/lib/tests/unit/staffInsideTuplet.test.js +0 -133
- package/lib/tests/unit/timesFirstNoteEscape.test.d.ts +0 -16
- package/lib/tests/unit/timesFirstNoteEscape.test.js +0 -152
- package/lib/tests/unit/tupletWithBaseDuration.test.d.ts +0 -17
- package/lib/tests/unit/tupletWithBaseDuration.test.js +0 -139
- package/lib/tests/unit/voiceStaffParsing.test.d.ts +0 -13
- package/lib/tests/unit/voiceStaffParsing.test.js +0 -118
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./lilylet/highlight.js";
|
package/lib/highlight.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./lilylet/highlight.js";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Generic highlight scopes Lilylet tokens can carry. */
|
|
2
|
+
export type HighlightScope = "articulation" | "bar" | "brace" | "chordBracket" | "comment" | "dynamic" | "grace" | "hairpin" | "header" | "keyword" | "markup" | "mode" | "navigation" | "number" | "octave" | "operator" | "ornament" | "paren" | "pedal" | "pitch" | "punctuation" | "rest" | "separator" | "squareBracket" | "stem" | "string" | "tie" | "tuplet";
|
|
3
|
+
export interface HighlightRule {
|
|
4
|
+
/** Sticky, case-insensitive regex anchored at the scan position. */
|
|
5
|
+
re: RegExp;
|
|
6
|
+
scope: HighlightScope;
|
|
7
|
+
}
|
|
8
|
+
export interface HighlightToken {
|
|
9
|
+
scope: HighlightScope;
|
|
10
|
+
/** Start offset within the line (inclusive). */
|
|
11
|
+
start: number;
|
|
12
|
+
/** End offset within the line (exclusive). */
|
|
13
|
+
end: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Ordered highlight rules. Order mirrors the grammar's lexer; the tokenizer
|
|
17
|
+
* applies LONGEST-match (flex semantics), using order only as a tie-breaker.
|
|
18
|
+
*/
|
|
19
|
+
export declare const HIGHLIGHT_RULES: HighlightRule[];
|
|
20
|
+
/**
|
|
21
|
+
* Match a single token at `pos` in `line` using longest-match. Returns the
|
|
22
|
+
* winning token, or null if no rule matches (caller should advance one char).
|
|
23
|
+
*/
|
|
24
|
+
export declare const matchAt: (line: string, pos: number) => HighlightToken | null;
|
|
25
|
+
/**
|
|
26
|
+
* Tokenize one line into a list of scoped spans. Characters that match no rule
|
|
27
|
+
* are skipped (no token emitted), mirroring the lexer's catch-all.
|
|
28
|
+
*/
|
|
29
|
+
export declare const tokenizeLine: (line: string) => HighlightToken[];
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// AUTO-GENERATED by tools/buildHighlight.ts from source/lilylet/grammar.jison.js.
|
|
2
|
+
// Do NOT edit by hand. Run `npm run build:highlight` to regenerate.
|
|
3
|
+
//
|
|
4
|
+
// Framework-agnostic syntax-highlighting definition for Lilylet, derived from
|
|
5
|
+
// the grammar's lexer so it never drifts from the language. No editor
|
|
6
|
+
// dependency: it exposes generic SCOPE names and a longest-match tokenizer.
|
|
7
|
+
// Downstream editors (CodeMirror, Monaco, Prism, ...) map SCOPE -> their theme.
|
|
8
|
+
/**
|
|
9
|
+
* Ordered highlight rules. Order mirrors the grammar's lexer; the tokenizer
|
|
10
|
+
* applies LONGEST-match (flex semantics), using order only as a tie-breaker.
|
|
11
|
+
*/
|
|
12
|
+
export const HIGHLIGHT_RULES = [
|
|
13
|
+
{ re: /(?:%.*)/iy, scope: "comment" },
|
|
14
|
+
{ re: /(?:\[title\b)/iy, scope: "header" },
|
|
15
|
+
{ re: /(?:\[subtitle\b)/iy, scope: "header" },
|
|
16
|
+
{ re: /(?:\[composer\b)/iy, scope: "header" },
|
|
17
|
+
{ re: /(?:\[arranger\b)/iy, scope: "header" },
|
|
18
|
+
{ re: /(?:\[lyricist\b)/iy, scope: "header" },
|
|
19
|
+
{ re: /(?:\[opus\b)/iy, scope: "header" },
|
|
20
|
+
{ re: /(?:\[instrument-[A-Za-z0-9_]+(?:-[A-Za-z0-9_]+)*)/iy, scope: "header" },
|
|
21
|
+
{ re: /(?:\[instrument\b)/iy, scope: "header" },
|
|
22
|
+
{ re: /(?:\[genre\b)/iy, scope: "header" },
|
|
23
|
+
{ re: /(?:\[staves\b)/iy, scope: "header" },
|
|
24
|
+
{ re: /(?:\[auto-beam\b)/iy, scope: "header" },
|
|
25
|
+
{ re: /(?:\])/iy, scope: "squareBracket" },
|
|
26
|
+
{ re: /(?:"[^"]*")/iy, scope: "string" },
|
|
27
|
+
{ re: /(?:\\clef\b)/iy, scope: "keyword" },
|
|
28
|
+
{ re: /(?:\\key\b)/iy, scope: "keyword" },
|
|
29
|
+
{ re: /(?:\\time\b)/iy, scope: "keyword" },
|
|
30
|
+
{ re: /(?:\\partial\b)/iy, scope: "keyword" },
|
|
31
|
+
{ re: /(?:\\numericTimeSignature\b)/iy, scope: "keyword" },
|
|
32
|
+
{ re: /(?:\\defaultTimeSignature\b)/iy, scope: "keyword" },
|
|
33
|
+
{ re: /(?:\\tempo\b)/iy, scope: "keyword" },
|
|
34
|
+
{ re: /(?:\\staff\b)/iy, scope: "keyword" },
|
|
35
|
+
{ re: /(?:\\grace\b)/iy, scope: "grace" },
|
|
36
|
+
{ re: /(?:\\times\b)/iy, scope: "tuplet" },
|
|
37
|
+
{ re: /(?:\\tuplet\b)/iy, scope: "tuplet" },
|
|
38
|
+
{ re: /(?:\\repeat\b)/iy, scope: "keyword" },
|
|
39
|
+
{ re: /(?:\\ottava\b)/iy, scope: "keyword" },
|
|
40
|
+
{ re: /(?:\\stemUp\b)/iy, scope: "stem" },
|
|
41
|
+
{ re: /(?:\\stemDown\b)/iy, scope: "stem" },
|
|
42
|
+
{ re: /(?:\\stemNeutral\b)/iy, scope: "stem" },
|
|
43
|
+
{ re: /(?:\\major\b)/iy, scope: "mode" },
|
|
44
|
+
{ re: /(?:\\minor\b)/iy, scope: "mode" },
|
|
45
|
+
{ re: /(?:\\sustainOn\b)/iy, scope: "pedal" },
|
|
46
|
+
{ re: /(?:\\sustainOff\b)/iy, scope: "pedal" },
|
|
47
|
+
{ re: /(?:\\bar\b)/iy, scope: "keyword" },
|
|
48
|
+
{ re: /(?:\\coda\b)/iy, scope: "navigation" },
|
|
49
|
+
{ re: /(?:\\segno\b)/iy, scope: "navigation" },
|
|
50
|
+
{ re: /(?:\\chords\b)/iy, scope: "keyword" },
|
|
51
|
+
{ re: /(?:\\markup\b)/iy, scope: "markup" },
|
|
52
|
+
{ re: /(?:\\<)/iy, scope: "hairpin" },
|
|
53
|
+
{ re: /(?:\\>)/iy, scope: "hairpin" },
|
|
54
|
+
{ re: /(?:\\!)/iy, scope: "hairpin" },
|
|
55
|
+
{ re: /(?:\\staccato\b)/iy, scope: "articulation" },
|
|
56
|
+
{ re: /(?:\\staccatissimo\b)/iy, scope: "articulation" },
|
|
57
|
+
{ re: /(?:\\tenuto\b)/iy, scope: "articulation" },
|
|
58
|
+
{ re: /(?:\\marcato\b)/iy, scope: "articulation" },
|
|
59
|
+
{ re: /(?:\\accent\b)/iy, scope: "articulation" },
|
|
60
|
+
{ re: /(?:\\portato\b)/iy, scope: "articulation" },
|
|
61
|
+
{ re: /(?:\\trill\b)/iy, scope: "ornament" },
|
|
62
|
+
{ re: /(?:\\turn\b)/iy, scope: "ornament" },
|
|
63
|
+
{ re: /(?:\\mordent\b)/iy, scope: "ornament" },
|
|
64
|
+
{ re: /(?:\\prall\b)/iy, scope: "ornament" },
|
|
65
|
+
{ re: /(?:\\fermata\b)/iy, scope: "ornament" },
|
|
66
|
+
{ re: /(?:\\shortfermata\b)/iy, scope: "ornament" },
|
|
67
|
+
{ re: /(?:\\arpeggio\b)/iy, scope: "ornament" },
|
|
68
|
+
{ re: /(?:\\ppp\b)/iy, scope: "dynamic" },
|
|
69
|
+
{ re: /(?:\\pp\b)/iy, scope: "dynamic" },
|
|
70
|
+
{ re: /(?:\\mp\b)/iy, scope: "dynamic" },
|
|
71
|
+
{ re: /(?:\\mf\b)/iy, scope: "dynamic" },
|
|
72
|
+
{ re: /(?:\\fff\b)/iy, scope: "dynamic" },
|
|
73
|
+
{ re: /(?:\\ff\b)/iy, scope: "dynamic" },
|
|
74
|
+
{ re: /(?:\\sfz\b)/iy, scope: "dynamic" },
|
|
75
|
+
{ re: /(?:\\rfz\b)/iy, scope: "dynamic" },
|
|
76
|
+
{ re: /(?:\\sf\b)/iy, scope: "dynamic" },
|
|
77
|
+
{ re: /(?:\\fp\b)/iy, scope: "dynamic" },
|
|
78
|
+
{ re: /(?:\\p\b)/iy, scope: "dynamic" },
|
|
79
|
+
{ re: /(?:\\f)/iy, scope: "dynamic" },
|
|
80
|
+
{ re: /(?:\\rest\b)/iy, scope: "rest" },
|
|
81
|
+
{ re: /(?:\\\\\\)/iy, scope: "separator" },
|
|
82
|
+
{ re: /(?:\\\\)/iy, scope: "separator" },
|
|
83
|
+
{ re: /(?:tremolo\b)/iy, scope: "keyword" },
|
|
84
|
+
{ re: /(?:[a-g](ss|ff|s|f)?)/iy, scope: "pitch" },
|
|
85
|
+
{ re: /(?:')/iy, scope: "octave" },
|
|
86
|
+
{ re: /(?:,)/iy, scope: "octave" },
|
|
87
|
+
{ re: /(?:[0-9]+)/iy, scope: "number" },
|
|
88
|
+
{ re: /(?:\/)/iy, scope: "operator" },
|
|
89
|
+
{ re: /(?:#)/iy, scope: "punctuation" },
|
|
90
|
+
{ re: /(?:\{)/iy, scope: "brace" },
|
|
91
|
+
{ re: /(?:\})/iy, scope: "brace" },
|
|
92
|
+
{ re: /(?:<)/iy, scope: "chordBracket" },
|
|
93
|
+
{ re: /(?:>)/iy, scope: "chordBracket" },
|
|
94
|
+
{ re: /(?:\|)/iy, scope: "bar" },
|
|
95
|
+
{ re: /(?:\[)/iy, scope: "squareBracket" },
|
|
96
|
+
{ re: /(?:\])/iy, scope: "squareBracket" },
|
|
97
|
+
{ re: /(?:\()/iy, scope: "paren" },
|
|
98
|
+
{ re: /(?:\))/iy, scope: "paren" },
|
|
99
|
+
{ re: /(?:~)/iy, scope: "tie" },
|
|
100
|
+
{ re: /(?:\.)/iy, scope: "punctuation" },
|
|
101
|
+
{ re: /(?:-)/iy, scope: "punctuation" },
|
|
102
|
+
{ re: /(?:[_])/iy, scope: "punctuation" },
|
|
103
|
+
{ re: /(?:\^)/iy, scope: "punctuation" },
|
|
104
|
+
{ re: /(?:!)/iy, scope: "punctuation" },
|
|
105
|
+
{ re: /(?::)/iy, scope: "operator" },
|
|
106
|
+
{ re: /(?:=)/iy, scope: "operator" },
|
|
107
|
+
{ re: /(?:[rR])/iy, scope: "rest" },
|
|
108
|
+
{ re: /(?:[sS])/iy, scope: "rest" },
|
|
109
|
+
];
|
|
110
|
+
/**
|
|
111
|
+
* Match a single token at `pos` in `line` using longest-match. Returns the
|
|
112
|
+
* winning token, or null if no rule matches (caller should advance one char).
|
|
113
|
+
*/
|
|
114
|
+
export const matchAt = (line, pos) => {
|
|
115
|
+
let best = null;
|
|
116
|
+
for (const rule of HIGHLIGHT_RULES) {
|
|
117
|
+
rule.re.lastIndex = pos;
|
|
118
|
+
const m = rule.re.exec(line);
|
|
119
|
+
if (m && m.index === pos && m[0].length > 0) {
|
|
120
|
+
const end = pos + m[0].length;
|
|
121
|
+
if (!best || end > best.end)
|
|
122
|
+
best = { scope: rule.scope, start: pos, end };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return best;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Tokenize one line into a list of scoped spans. Characters that match no rule
|
|
129
|
+
* are skipped (no token emitted), mirroring the lexer's catch-all.
|
|
130
|
+
*/
|
|
131
|
+
export const tokenizeLine = (line) => {
|
|
132
|
+
const tokens = [];
|
|
133
|
+
let pos = 0;
|
|
134
|
+
while (pos < line.length) {
|
|
135
|
+
const tok = matchAt(line, pos);
|
|
136
|
+
if (tok) {
|
|
137
|
+
tokens.push(tok);
|
|
138
|
+
pos = tok.end;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
pos++;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return tokens;
|
|
145
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@k-l-lambda/lilylet",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.73",
|
|
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",
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
"types": "./lib/browser.d.ts",
|
|
15
15
|
"default": "./lib/browser.js"
|
|
16
16
|
},
|
|
17
|
+
"./highlight": {
|
|
18
|
+
"types": "./lib/lilylet/highlight.d.ts",
|
|
19
|
+
"default": "./lib/lilylet/highlight.js"
|
|
20
|
+
},
|
|
17
21
|
"./lib/lilypondDecoder.js": {
|
|
18
22
|
"types": "./lib/lilypondDecoder.d.ts",
|
|
19
23
|
"default": "./lib/lilypondDecoder.js"
|
|
@@ -25,7 +29,8 @@
|
|
|
25
29
|
],
|
|
26
30
|
"scripts": {
|
|
27
31
|
"build": "tsc -p tsconfig.build.json && node tools/convertGrammarToESM.cjs && node tools/fixEsmExtensions.cjs",
|
|
28
|
-
"build:grammar": "npx tsx ./tools/buildJisonParser.ts && node tools/convertGrammarToESM.cjs",
|
|
32
|
+
"build:grammar": "npx tsx ./tools/buildJisonParser.ts && node tools/convertGrammarToESM.cjs && npm run build:highlight",
|
|
33
|
+
"build:highlight": "npx tsx ./tools/buildHighlight.ts",
|
|
29
34
|
"prepublishOnly": "npm run build:grammar && npm run build",
|
|
30
35
|
"test": "tsx ./tests/parser.ts",
|
|
31
36
|
"test:mei": "tsx ./tests/mei.ts",
|
|
@@ -37,6 +42,7 @@
|
|
|
37
42
|
"test:abc-svg": "tsx ./tests/abc-abcjs-svg.ts",
|
|
38
43
|
"train:bpe": "tsx ./tools/trainBpeTokenizer.ts",
|
|
39
44
|
"build:manual-tokenizer": "tsx ./tools/buildManualTokenizer.ts",
|
|
45
|
+
"build:browser-bundle": "esbuild tools/browserBundle.ts --bundle --format=iife --platform=browser --minify --outfile=dist/lilylet.bundle.js",
|
|
40
46
|
"test:bpe": "tsx ./tests/unit/bpeTokenizer.test.ts",
|
|
41
47
|
"build:tests": "tsc -p tsconfig.tests.json; cp source/lilylet/grammar.jison.js lib-tests/source/lilylet/ && cp source/abc/grammar.jison.js lib-tests/source/abc/ && node tools/fixEsmExtensions.cjs lib-tests && ln -sfn ../../tests/assets lib-tests/tests/assets",
|
|
42
48
|
"test:roundtrip:compiled": "node lib-tests/tests/lilypond-roundtrip.js",
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// AUTO-GENERATED by tools/buildHighlight.ts from source/lilylet/grammar.jison.js.
|
|
2
|
+
// Do NOT edit by hand. Run `npm run build:highlight` to regenerate.
|
|
3
|
+
//
|
|
4
|
+
// Framework-agnostic syntax-highlighting definition for Lilylet, derived from
|
|
5
|
+
// the grammar's lexer so it never drifts from the language. No editor
|
|
6
|
+
// dependency: it exposes generic SCOPE names and a longest-match tokenizer.
|
|
7
|
+
// Downstream editors (CodeMirror, Monaco, Prism, ...) map SCOPE -> their theme.
|
|
8
|
+
|
|
9
|
+
/** Generic highlight scopes Lilylet tokens can carry. */
|
|
10
|
+
export type HighlightScope =
|
|
11
|
+
| "articulation"
|
|
12
|
+
| "bar"
|
|
13
|
+
| "brace"
|
|
14
|
+
| "chordBracket"
|
|
15
|
+
| "comment"
|
|
16
|
+
| "dynamic"
|
|
17
|
+
| "grace"
|
|
18
|
+
| "hairpin"
|
|
19
|
+
| "header"
|
|
20
|
+
| "keyword"
|
|
21
|
+
| "markup"
|
|
22
|
+
| "mode"
|
|
23
|
+
| "navigation"
|
|
24
|
+
| "number"
|
|
25
|
+
| "octave"
|
|
26
|
+
| "operator"
|
|
27
|
+
| "ornament"
|
|
28
|
+
| "paren"
|
|
29
|
+
| "pedal"
|
|
30
|
+
| "pitch"
|
|
31
|
+
| "punctuation"
|
|
32
|
+
| "rest"
|
|
33
|
+
| "separator"
|
|
34
|
+
| "squareBracket"
|
|
35
|
+
| "stem"
|
|
36
|
+
| "string"
|
|
37
|
+
| "tie"
|
|
38
|
+
| "tuplet";
|
|
39
|
+
|
|
40
|
+
export interface HighlightRule {
|
|
41
|
+
/** Sticky, case-insensitive regex anchored at the scan position. */
|
|
42
|
+
re: RegExp;
|
|
43
|
+
scope: HighlightScope;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface HighlightToken {
|
|
47
|
+
scope: HighlightScope;
|
|
48
|
+
/** Start offset within the line (inclusive). */
|
|
49
|
+
start: number;
|
|
50
|
+
/** End offset within the line (exclusive). */
|
|
51
|
+
end: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Ordered highlight rules. Order mirrors the grammar's lexer; the tokenizer
|
|
56
|
+
* applies LONGEST-match (flex semantics), using order only as a tie-breaker.
|
|
57
|
+
*/
|
|
58
|
+
export const HIGHLIGHT_RULES: HighlightRule[] = [
|
|
59
|
+
{ re: /(?:%.*)/iy, scope: "comment" },
|
|
60
|
+
{ re: /(?:\[title\b)/iy, scope: "header" },
|
|
61
|
+
{ re: /(?:\[subtitle\b)/iy, scope: "header" },
|
|
62
|
+
{ re: /(?:\[composer\b)/iy, scope: "header" },
|
|
63
|
+
{ re: /(?:\[arranger\b)/iy, scope: "header" },
|
|
64
|
+
{ re: /(?:\[lyricist\b)/iy, scope: "header" },
|
|
65
|
+
{ re: /(?:\[opus\b)/iy, scope: "header" },
|
|
66
|
+
{ re: /(?:\[instrument-[A-Za-z0-9_]+(?:-[A-Za-z0-9_]+)*)/iy, scope: "header" },
|
|
67
|
+
{ re: /(?:\[instrument\b)/iy, scope: "header" },
|
|
68
|
+
{ re: /(?:\[genre\b)/iy, scope: "header" },
|
|
69
|
+
{ re: /(?:\[staves\b)/iy, scope: "header" },
|
|
70
|
+
{ re: /(?:\[auto-beam\b)/iy, scope: "header" },
|
|
71
|
+
{ re: /(?:\])/iy, scope: "squareBracket" },
|
|
72
|
+
{ re: /(?:"[^"]*")/iy, scope: "string" },
|
|
73
|
+
{ re: /(?:\\clef\b)/iy, scope: "keyword" },
|
|
74
|
+
{ re: /(?:\\key\b)/iy, scope: "keyword" },
|
|
75
|
+
{ re: /(?:\\time\b)/iy, scope: "keyword" },
|
|
76
|
+
{ re: /(?:\\partial\b)/iy, scope: "keyword" },
|
|
77
|
+
{ re: /(?:\\numericTimeSignature\b)/iy, scope: "keyword" },
|
|
78
|
+
{ re: /(?:\\defaultTimeSignature\b)/iy, scope: "keyword" },
|
|
79
|
+
{ re: /(?:\\tempo\b)/iy, scope: "keyword" },
|
|
80
|
+
{ re: /(?:\\staff\b)/iy, scope: "keyword" },
|
|
81
|
+
{ re: /(?:\\grace\b)/iy, scope: "grace" },
|
|
82
|
+
{ re: /(?:\\times\b)/iy, scope: "tuplet" },
|
|
83
|
+
{ re: /(?:\\tuplet\b)/iy, scope: "tuplet" },
|
|
84
|
+
{ re: /(?:\\repeat\b)/iy, scope: "keyword" },
|
|
85
|
+
{ re: /(?:\\ottava\b)/iy, scope: "keyword" },
|
|
86
|
+
{ re: /(?:\\stemUp\b)/iy, scope: "stem" },
|
|
87
|
+
{ re: /(?:\\stemDown\b)/iy, scope: "stem" },
|
|
88
|
+
{ re: /(?:\\stemNeutral\b)/iy, scope: "stem" },
|
|
89
|
+
{ re: /(?:\\major\b)/iy, scope: "mode" },
|
|
90
|
+
{ re: /(?:\\minor\b)/iy, scope: "mode" },
|
|
91
|
+
{ re: /(?:\\sustainOn\b)/iy, scope: "pedal" },
|
|
92
|
+
{ re: /(?:\\sustainOff\b)/iy, scope: "pedal" },
|
|
93
|
+
{ re: /(?:\\bar\b)/iy, scope: "keyword" },
|
|
94
|
+
{ re: /(?:\\coda\b)/iy, scope: "navigation" },
|
|
95
|
+
{ re: /(?:\\segno\b)/iy, scope: "navigation" },
|
|
96
|
+
{ re: /(?:\\chords\b)/iy, scope: "keyword" },
|
|
97
|
+
{ re: /(?:\\markup\b)/iy, scope: "markup" },
|
|
98
|
+
{ re: /(?:\\<)/iy, scope: "hairpin" },
|
|
99
|
+
{ re: /(?:\\>)/iy, scope: "hairpin" },
|
|
100
|
+
{ re: /(?:\\!)/iy, scope: "hairpin" },
|
|
101
|
+
{ re: /(?:\\staccato\b)/iy, scope: "articulation" },
|
|
102
|
+
{ re: /(?:\\staccatissimo\b)/iy, scope: "articulation" },
|
|
103
|
+
{ re: /(?:\\tenuto\b)/iy, scope: "articulation" },
|
|
104
|
+
{ re: /(?:\\marcato\b)/iy, scope: "articulation" },
|
|
105
|
+
{ re: /(?:\\accent\b)/iy, scope: "articulation" },
|
|
106
|
+
{ re: /(?:\\portato\b)/iy, scope: "articulation" },
|
|
107
|
+
{ re: /(?:\\trill\b)/iy, scope: "ornament" },
|
|
108
|
+
{ re: /(?:\\turn\b)/iy, scope: "ornament" },
|
|
109
|
+
{ re: /(?:\\mordent\b)/iy, scope: "ornament" },
|
|
110
|
+
{ re: /(?:\\prall\b)/iy, scope: "ornament" },
|
|
111
|
+
{ re: /(?:\\fermata\b)/iy, scope: "ornament" },
|
|
112
|
+
{ re: /(?:\\shortfermata\b)/iy, scope: "ornament" },
|
|
113
|
+
{ re: /(?:\\arpeggio\b)/iy, scope: "ornament" },
|
|
114
|
+
{ re: /(?:\\ppp\b)/iy, scope: "dynamic" },
|
|
115
|
+
{ re: /(?:\\pp\b)/iy, scope: "dynamic" },
|
|
116
|
+
{ re: /(?:\\mp\b)/iy, scope: "dynamic" },
|
|
117
|
+
{ re: /(?:\\mf\b)/iy, scope: "dynamic" },
|
|
118
|
+
{ re: /(?:\\fff\b)/iy, scope: "dynamic" },
|
|
119
|
+
{ re: /(?:\\ff\b)/iy, scope: "dynamic" },
|
|
120
|
+
{ re: /(?:\\sfz\b)/iy, scope: "dynamic" },
|
|
121
|
+
{ re: /(?:\\rfz\b)/iy, scope: "dynamic" },
|
|
122
|
+
{ re: /(?:\\sf\b)/iy, scope: "dynamic" },
|
|
123
|
+
{ re: /(?:\\fp\b)/iy, scope: "dynamic" },
|
|
124
|
+
{ re: /(?:\\p\b)/iy, scope: "dynamic" },
|
|
125
|
+
{ re: /(?:\\f)/iy, scope: "dynamic" },
|
|
126
|
+
{ re: /(?:\\rest\b)/iy, scope: "rest" },
|
|
127
|
+
{ re: /(?:\\\\\\)/iy, scope: "separator" },
|
|
128
|
+
{ re: /(?:\\\\)/iy, scope: "separator" },
|
|
129
|
+
{ re: /(?:tremolo\b)/iy, scope: "keyword" },
|
|
130
|
+
{ re: /(?:[a-g](ss|ff|s|f)?)/iy, scope: "pitch" },
|
|
131
|
+
{ re: /(?:')/iy, scope: "octave" },
|
|
132
|
+
{ re: /(?:,)/iy, scope: "octave" },
|
|
133
|
+
{ re: /(?:[0-9]+)/iy, scope: "number" },
|
|
134
|
+
{ re: /(?:\/)/iy, scope: "operator" },
|
|
135
|
+
{ re: /(?:#)/iy, scope: "punctuation" },
|
|
136
|
+
{ re: /(?:\{)/iy, scope: "brace" },
|
|
137
|
+
{ re: /(?:\})/iy, scope: "brace" },
|
|
138
|
+
{ re: /(?:<)/iy, scope: "chordBracket" },
|
|
139
|
+
{ re: /(?:>)/iy, scope: "chordBracket" },
|
|
140
|
+
{ re: /(?:\|)/iy, scope: "bar" },
|
|
141
|
+
{ re: /(?:\[)/iy, scope: "squareBracket" },
|
|
142
|
+
{ re: /(?:\])/iy, scope: "squareBracket" },
|
|
143
|
+
{ re: /(?:\()/iy, scope: "paren" },
|
|
144
|
+
{ re: /(?:\))/iy, scope: "paren" },
|
|
145
|
+
{ re: /(?:~)/iy, scope: "tie" },
|
|
146
|
+
{ re: /(?:\.)/iy, scope: "punctuation" },
|
|
147
|
+
{ re: /(?:-)/iy, scope: "punctuation" },
|
|
148
|
+
{ re: /(?:[_])/iy, scope: "punctuation" },
|
|
149
|
+
{ re: /(?:\^)/iy, scope: "punctuation" },
|
|
150
|
+
{ re: /(?:!)/iy, scope: "punctuation" },
|
|
151
|
+
{ re: /(?::)/iy, scope: "operator" },
|
|
152
|
+
{ re: /(?:=)/iy, scope: "operator" },
|
|
153
|
+
{ re: /(?:[rR])/iy, scope: "rest" },
|
|
154
|
+
{ re: /(?:[sS])/iy, scope: "rest" },
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Match a single token at `pos` in `line` using longest-match. Returns the
|
|
159
|
+
* winning token, or null if no rule matches (caller should advance one char).
|
|
160
|
+
*/
|
|
161
|
+
export const matchAt = (line: string, pos: number): HighlightToken | null => {
|
|
162
|
+
let best: HighlightToken | null = null;
|
|
163
|
+
for (const rule of HIGHLIGHT_RULES) {
|
|
164
|
+
rule.re.lastIndex = pos;
|
|
165
|
+
const m = rule.re.exec(line);
|
|
166
|
+
if (m && m.index === pos && m[0].length > 0) {
|
|
167
|
+
const end = pos + m[0].length;
|
|
168
|
+
if (!best || end > best.end)
|
|
169
|
+
best = { scope: rule.scope, start: pos, end };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return best;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Tokenize one line into a list of scoped spans. Characters that match no rule
|
|
177
|
+
* are skipped (no token emitted), mirroring the lexer's catch-all.
|
|
178
|
+
*/
|
|
179
|
+
export const tokenizeLine = (line: string): HighlightToken[] => {
|
|
180
|
+
const tokens: HighlightToken[] = [];
|
|
181
|
+
let pos = 0;
|
|
182
|
+
while (pos < line.length) {
|
|
183
|
+
const tok = matchAt(line, pos);
|
|
184
|
+
if (tok) {
|
|
185
|
+
tokens.push(tok);
|
|
186
|
+
pos = tok.end;
|
|
187
|
+
} else {
|
|
188
|
+
pos++;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return tokens;
|
|
192
|
+
};
|
package/lib/source/abc/abc.d.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
interface Fraction {
|
|
2
|
-
numerator: number;
|
|
3
|
-
denominator: number;
|
|
4
|
-
}
|
|
5
|
-
declare namespace ABC {
|
|
6
|
-
type Token = string;
|
|
7
|
-
interface KeyValue {
|
|
8
|
-
name: string;
|
|
9
|
-
value: any;
|
|
10
|
-
}
|
|
11
|
-
export interface ControlTerm {
|
|
12
|
-
control: KeyValue;
|
|
13
|
-
}
|
|
14
|
-
export interface Triplet {
|
|
15
|
-
triplet: number;
|
|
16
|
-
multiplier?: number;
|
|
17
|
-
n?: number;
|
|
18
|
-
}
|
|
19
|
-
export interface OctaveShift {
|
|
20
|
-
octaveShift: number;
|
|
21
|
-
}
|
|
22
|
-
export interface Fingering {
|
|
23
|
-
fingering: string;
|
|
24
|
-
}
|
|
25
|
-
export interface Tremolo {
|
|
26
|
-
tremolo: number;
|
|
27
|
-
}
|
|
28
|
-
interface Grace {
|
|
29
|
-
grace: boolean;
|
|
30
|
-
acciaccatura: Token;
|
|
31
|
-
events: GraceMusicTerm[];
|
|
32
|
-
}
|
|
33
|
-
interface Comment {
|
|
34
|
-
comment: string;
|
|
35
|
-
}
|
|
36
|
-
export interface Articulation {
|
|
37
|
-
articulation: Token;
|
|
38
|
-
scope?: '(' | ')';
|
|
39
|
-
}
|
|
40
|
-
export type Expressive = Articulation | {
|
|
41
|
-
express: Token;
|
|
42
|
-
};
|
|
43
|
-
export interface TextTerm {
|
|
44
|
-
text: string;
|
|
45
|
-
}
|
|
46
|
-
export interface Pitch {
|
|
47
|
-
acc: number | null;
|
|
48
|
-
phonet: Token;
|
|
49
|
-
quotes: number;
|
|
50
|
-
tie?: boolean;
|
|
51
|
-
}
|
|
52
|
-
export interface Chord {
|
|
53
|
-
pitches: Pitch[];
|
|
54
|
-
tie?: any;
|
|
55
|
-
}
|
|
56
|
-
export interface EventData {
|
|
57
|
-
chord: Chord;
|
|
58
|
-
duration?: Fraction;
|
|
59
|
-
}
|
|
60
|
-
export interface EventTerm {
|
|
61
|
-
event: EventData;
|
|
62
|
-
broken?: number;
|
|
63
|
-
}
|
|
64
|
-
export type MusicTerm = Expressive | TextTerm | EventTerm | Grace | ControlTerm | Triplet | OctaveShift | Fingering | Tremolo;
|
|
65
|
-
export type GraceMusicTerm = Expressive | EventTerm | Fingering;
|
|
66
|
-
type Header = KeyValue | Comment;
|
|
67
|
-
export interface BarPatch {
|
|
68
|
-
control: {
|
|
69
|
-
[k: string]: any;
|
|
70
|
-
};
|
|
71
|
-
terms: MusicTerm[];
|
|
72
|
-
bar: Token;
|
|
73
|
-
}
|
|
74
|
-
export interface StaffGroup {
|
|
75
|
-
items: (StaffGroup | string)[];
|
|
76
|
-
bound?: 'arc' | 'square' | 'curly';
|
|
77
|
-
}
|
|
78
|
-
export interface StaffLayout {
|
|
79
|
-
staffLayout: StaffGroup[];
|
|
80
|
-
}
|
|
81
|
-
export interface KeySignature {
|
|
82
|
-
root: string;
|
|
83
|
-
mode?: string;
|
|
84
|
-
}
|
|
85
|
-
export interface ClefValue {
|
|
86
|
-
clef: string;
|
|
87
|
-
}
|
|
88
|
-
interface Measure {
|
|
89
|
-
index: number;
|
|
90
|
-
voices: BarPatch[];
|
|
91
|
-
}
|
|
92
|
-
interface Body {
|
|
93
|
-
measures: Measure[];
|
|
94
|
-
}
|
|
95
|
-
export interface Tune {
|
|
96
|
-
header: Header[];
|
|
97
|
-
body: Body;
|
|
98
|
-
}
|
|
99
|
-
export type Document = Tune[];
|
|
100
|
-
export {};
|
|
101
|
-
}
|
|
102
|
-
export { ABC, };
|
package/lib/source/abc/abc.js
DELETED
package/lib/source/abc/parser.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ABC Notation Decoder for Lilylet
|
|
3
|
-
*
|
|
4
|
-
* Converts ABC notation files to Lilylet's internal LilyletDoc format.
|
|
5
|
-
*/
|
|
6
|
-
import { LilyletDoc } from "./types";
|
|
7
|
-
/**
|
|
8
|
-
* Decode ABC notation string to LilyletDoc.
|
|
9
|
-
* If the ABC contains multiple tunes, only the first is decoded.
|
|
10
|
-
*/
|
|
11
|
-
export declare const decode: (abcString: string) => LilyletDoc;
|
|
12
|
-
/**
|
|
13
|
-
* Decode ABC notation string to multiple LilyletDocs (one per tune).
|
|
14
|
-
*/
|
|
15
|
-
export declare const decodeAll: (abcString: string) => LilyletDoc[];
|
|
16
|
-
/**
|
|
17
|
-
* Decode an ABC file to LilyletDoc
|
|
18
|
-
*/
|
|
19
|
-
export declare const decodeFile: (filePath: string) => Promise<LilyletDoc>;
|
|
20
|
-
declare const _default: {
|
|
21
|
-
decode: (abcString: string) => LilyletDoc;
|
|
22
|
-
decodeAll: (abcString: string) => LilyletDoc[];
|
|
23
|
-
decodeFile: (filePath: string) => Promise<LilyletDoc>;
|
|
24
|
-
};
|
|
25
|
-
export default _default;
|