mdld-parse 0.2.10 → 0.3.1
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/LICENCE +1 -1
- package/README.md +10 -11
- package/index.js +57 -21
- package/package.json +1 -1
package/LICENCE
CHANGED
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# MD-LD Parse v0.
|
|
1
|
+
# MD-LD Parse v0.3
|
|
2
2
|
|
|
3
3
|
**Markdown-Linked Data (MD-LD)** — a deterministic, streaming-friendly RDF authoring format that extends Markdown with explicit `{...}` annotations.
|
|
4
4
|
|
|
@@ -14,10 +14,10 @@ MD-LD allows you to author RDF graphs directly in Markdown using explicit `{...}
|
|
|
14
14
|
# Apollo 11 {=ex:apollo11 .SpaceMission}
|
|
15
15
|
|
|
16
16
|
Launch: [1969-07-16] {startDate ^^xsd:date}
|
|
17
|
-
Crew: [Neil Armstrong] {
|
|
17
|
+
Crew: [Neil Armstrong] {+ex:armstrong ?crewMember name}
|
|
18
18
|
Description: [First crewed Moon landing] {description}
|
|
19
19
|
|
|
20
|
-
[Section] {
|
|
20
|
+
[Section] {+#overview ?hasPart}
|
|
21
21
|
Overview: [Mission summary] {description}
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -35,8 +35,8 @@ ex:armstrong schema:name "Neil Armstrong" .
|
|
|
35
35
|
## Core Features
|
|
36
36
|
|
|
37
37
|
- **Subject declarations**: `{=IRI}` and `{=#fragment}` for context setting
|
|
38
|
-
- **Object IRIs**: `{
|
|
39
|
-
- **Four predicate forms**: `p` (S→L), `?p` (S→O),
|
|
38
|
+
- **Object IRIs**: `{+IRI}` and `{+#fragment}` for temporary object declarations
|
|
39
|
+
- **Four predicate forms**: `p` (S→L), `?p` (S→O), `!p` (O→S)
|
|
40
40
|
- **Type declarations**: `.Class` for rdf:type triples
|
|
41
41
|
- **Datatypes & language**: `^^xsd:date` and `@en` support
|
|
42
42
|
- **Lists**: Explicit subject declarations for structured data
|
|
@@ -92,8 +92,7 @@ Each predicate form determines the graph edge:
|
|
|
92
92
|
|-------|---------|------------------------------|------------------|
|
|
93
93
|
| `p` | S → L | `[Alice] {name}` | literal property |
|
|
94
94
|
| `?p` | S → O | `[NASA](ex:nasa) {?org}` | object property |
|
|
95
|
-
|
|
|
96
|
-
| `^?p` | O → S | `[Parent](ex:p) {^?hasPart}` | reverse object |
|
|
95
|
+
| `!p` | O → S | `[Parent](ex:p) {!hasPart}` | reverse object |
|
|
97
96
|
|
|
98
97
|
## Syntax Reference
|
|
99
98
|
|
|
@@ -237,7 +236,7 @@ Reverse the relationship direction:
|
|
|
237
236
|
```markdown
|
|
238
237
|
# Part {=ex:part}
|
|
239
238
|
|
|
240
|
-
Part of: {
|
|
239
|
+
Part of: {!hasPart}
|
|
241
240
|
|
|
242
241
|
- Book {=ex:book}
|
|
243
242
|
```
|
|
@@ -416,8 +415,8 @@ Each predicate is partitioned by **direction** and **node kind**:
|
|
|
416
415
|
| -------------- | -------------- |
|
|
417
416
|
| `p` | `S ─p→ L` |
|
|
418
417
|
| `?p` | `S ─p→ O` |
|
|
419
|
-
|
|
|
420
|
-
|
|
|
418
|
+
| not allowed | `L ─p→ S` |
|
|
419
|
+
| `!p` | `O ─p→ S` |
|
|
421
420
|
|
|
422
421
|
This spans all **2 × 2** combinations of:
|
|
423
422
|
|
|
@@ -480,7 +479,7 @@ npm test
|
|
|
480
479
|
|
|
481
480
|
Tests validate:
|
|
482
481
|
- Subject declaration and context
|
|
483
|
-
- All predicate forms (p, ?p,
|
|
482
|
+
- All predicate forms (p, ?p, !p)
|
|
484
483
|
- Datatypes and language tags
|
|
485
484
|
- List processing
|
|
486
485
|
- Code blocks and blockquotes
|
package/index.js
CHANGED
|
@@ -75,15 +75,15 @@ export function parseSemanticBlock(raw) {
|
|
|
75
75
|
continue;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
if (token.startsWith('
|
|
79
|
-
const fragment = token.substring(
|
|
78
|
+
if (token.startsWith('+#')) {
|
|
79
|
+
const fragment = token.substring(2);
|
|
80
80
|
result.object = `#${fragment}`;
|
|
81
81
|
result.entries.push({ kind: 'softFragment', fragment, relRange: { start: relStart, end: relEnd }, raw: token });
|
|
82
82
|
continue;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
if (token.startsWith('
|
|
86
|
-
const iri = token.substring(
|
|
85
|
+
if (token.startsWith('+')) {
|
|
86
|
+
const iri = token.substring(1);
|
|
87
87
|
result.object = iri;
|
|
88
88
|
result.entries.push({ kind: 'object', iri, relRange: { start: relStart, end: relEnd }, raw: token });
|
|
89
89
|
continue;
|
|
@@ -118,10 +118,10 @@ export function parseSemanticBlock(raw) {
|
|
|
118
118
|
continue;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
if (token.startsWith('
|
|
122
|
-
const iri = token.substring(
|
|
123
|
-
result.predicates.push({ iri, form: '
|
|
124
|
-
result.entries.push({ kind: 'property', iri, form: '
|
|
121
|
+
if (token.startsWith('!')) {
|
|
122
|
+
const iri = token.substring(1);
|
|
123
|
+
result.predicates.push({ iri, form: '!', entryIndex });
|
|
124
|
+
result.entries.push({ kind: 'property', iri, form: '!', relRange: { start: relStart, end: relEnd }, raw: token });
|
|
125
125
|
continue;
|
|
126
126
|
}
|
|
127
127
|
|
|
@@ -224,7 +224,7 @@ function scanTokens(text) {
|
|
|
224
224
|
continue;
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
const listMatch = line.match(/^(\s*)([-*+]|\d+\.)\s+(.+?)(?:\s*(\{[^}]+\}))
|
|
227
|
+
const listMatch = line.match(/^(\s*)([-*+]|\d+\.)\s+(.+?)(?:\s*(\{[^}]+\}))?\s*$/);
|
|
228
228
|
if (listMatch) {
|
|
229
229
|
const attrs = listMatch[4] || null;
|
|
230
230
|
const attrsStartInLine = attrs ? line.lastIndexOf(attrs) : -1;
|
|
@@ -593,7 +593,7 @@ function processAnnotation(carrier, sem, state) {
|
|
|
593
593
|
// L —p→ S (use soft IRI object as subject if available, otherwise current subject)
|
|
594
594
|
const subjectIRI = localObject || S;
|
|
595
595
|
emitQuad(state.quads, state.origin.quadIndex, block.id, L, P, subjectIRI, state.df, { kind: 'pred', token, form: pred.form, expandedPredicate: P.value, entryIndex: pred.entryIndex });
|
|
596
|
-
} else if (pred.form === '
|
|
596
|
+
} else if (pred.form === '!') {
|
|
597
597
|
// O —p→ S (use previous subject as object, newSubject as subject)
|
|
598
598
|
const objectIRI = newSubject ? previousSubject : S;
|
|
599
599
|
const subjectIRI = localObject || newSubject || carrierO;
|
|
@@ -661,9 +661,9 @@ function processListContext(contextSem, listTokens, state, contextSubject = null
|
|
|
661
661
|
contextSem.predicates.forEach(pred => {
|
|
662
662
|
const P = state.df.namedNode(expandIRI(pred.iri, state.ctx));
|
|
663
663
|
|
|
664
|
-
// According to MD-LD spec: list predicates that connect to item subjects MUST use object predicate forms (?p or
|
|
665
|
-
// Literal predicate forms (p
|
|
666
|
-
if (pred.form === '
|
|
664
|
+
// According to MD-LD spec: list predicates that connect to item subjects MUST use object predicate forms (?p or !p)
|
|
665
|
+
// Literal predicate forms (p) in list scope emit no quads
|
|
666
|
+
if (pred.form === '!') {
|
|
667
667
|
// Reverse object property: O —p→ S
|
|
668
668
|
emitQuad(state.quads, state.origin.quadIndex, 'list-context', itemSubject, P, contextSubject, state.df);
|
|
669
669
|
} else if (pred.form === '?') {
|
|
@@ -676,8 +676,44 @@ function processListContext(contextSem, listTokens, state, contextSubject = null
|
|
|
676
676
|
const prevSubject = state.currentSubject;
|
|
677
677
|
state.currentSubject = itemSubject;
|
|
678
678
|
|
|
679
|
+
// Check if item has its own predicates
|
|
680
|
+
let hasOwnPredicates = false;
|
|
681
|
+
let itemSem = null;
|
|
682
|
+
|
|
679
683
|
if (listToken.attrs) {
|
|
680
|
-
|
|
684
|
+
itemSem = parseSemanticBlock(listToken.attrs);
|
|
685
|
+
if (itemSem.predicates.length > 0) {
|
|
686
|
+
hasOwnPredicates = true;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (!hasOwnPredicates) {
|
|
691
|
+
// Check inline carriers for predicates
|
|
692
|
+
for (const carrier of carriers) {
|
|
693
|
+
if (carrier.attrs) {
|
|
694
|
+
const carrierSem = parseSemanticBlock(carrier.attrs);
|
|
695
|
+
if (carrierSem.predicates.length > 0) {
|
|
696
|
+
hasOwnPredicates = true;
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// If item has no predicates, inherit literal predicates from context
|
|
704
|
+
if (!hasOwnPredicates) {
|
|
705
|
+
const inheritedPredicates = contextSem.predicates.filter(p => p.form === '');
|
|
706
|
+
if (inheritedPredicates.length > 0 && listToken.text) {
|
|
707
|
+
// Create inherited annotation block
|
|
708
|
+
const inheritedTokens = inheritedPredicates.map(p => p.iri).join(' ');
|
|
709
|
+
const inheritedSem = parseSemanticBlock(`{${inheritedTokens}}`);
|
|
710
|
+
const carrier = { type: 'list', text: listToken.text, range: listToken.range, attrsRange: listToken.attrsRange || null, valueRange: listToken.valueRange || null };
|
|
711
|
+
processAnnotation(carrier, inheritedSem, state);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (listToken.attrs) {
|
|
716
|
+
if (!itemSem) itemSem = parseSemanticBlock(listToken.attrs);
|
|
681
717
|
const carrier = { type: 'list', text: listToken.text, range: listToken.range, attrsRange: listToken.attrsRange || null, valueRange: listToken.valueRange || null };
|
|
682
718
|
processAnnotation(carrier, itemSem, state);
|
|
683
719
|
}
|
|
@@ -797,22 +833,22 @@ function removeOneToken(tokens, matchFn) {
|
|
|
797
833
|
}
|
|
798
834
|
|
|
799
835
|
function addObjectToken(tokens, iri) {
|
|
800
|
-
const objectToken =
|
|
836
|
+
const objectToken = `+${iri}`;
|
|
801
837
|
return tokens.includes(objectToken) ? tokens : [...tokens, objectToken];
|
|
802
838
|
}
|
|
803
839
|
|
|
804
840
|
function removeObjectToken(tokens, iri) {
|
|
805
|
-
const objectToken =
|
|
841
|
+
const objectToken = `+${iri}`;
|
|
806
842
|
return removeOneToken(tokens, t => t === objectToken);
|
|
807
843
|
}
|
|
808
844
|
|
|
809
845
|
function addSoftFragmentToken(tokens, fragment) {
|
|
810
|
-
const fragmentToken =
|
|
846
|
+
const fragmentToken = `+#${fragment}`;
|
|
811
847
|
return tokens.includes(fragmentToken) ? tokens : [...tokens, fragmentToken];
|
|
812
848
|
}
|
|
813
849
|
|
|
814
850
|
function removeSoftFragmentToken(tokens, fragment) {
|
|
815
|
-
const fragmentToken =
|
|
851
|
+
const fragmentToken = `+#${fragment}`;
|
|
816
852
|
return removeOneToken(tokens, t => t === fragmentToken);
|
|
817
853
|
}
|
|
818
854
|
|
|
@@ -1203,7 +1239,7 @@ export function serialize({ text, diff, origin, options = {} }) {
|
|
|
1203
1239
|
const full = quad.object.value;
|
|
1204
1240
|
const label = shortenIRI(full, ctx);
|
|
1205
1241
|
const objectShort = shortenIRI(full, ctx);
|
|
1206
|
-
edits.push({ start: result.length, end: result.length, text: `\n[${label}] {
|
|
1242
|
+
edits.push({ start: result.length, end: result.length, text: `\n[${label}] {+${objectShort} ?${predShort}}` });
|
|
1207
1243
|
}
|
|
1208
1244
|
return;
|
|
1209
1245
|
}
|
|
@@ -1246,9 +1282,9 @@ export function serialize({ text, diff, origin, options = {} }) {
|
|
|
1246
1282
|
// Create new annotation with object token
|
|
1247
1283
|
if (isSoftFragment) {
|
|
1248
1284
|
const fragment = full.split('#')[1];
|
|
1249
|
-
edits.push({ start: result.length, end: result.length, text: `\n[${objectShort}] {
|
|
1285
|
+
edits.push({ start: result.length, end: result.length, text: `\n[${objectShort}] {+#${fragment} ?${predShort}}` });
|
|
1250
1286
|
} else {
|
|
1251
|
-
edits.push({ start: result.length, end: result.length, text: `\n[${objectShort}] {
|
|
1287
|
+
edits.push({ start: result.length, end: result.length, text: `\n[${objectShort}] {+${objectShort} ?${predShort}}` });
|
|
1252
1288
|
}
|
|
1253
1289
|
}
|
|
1254
1290
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mdld-parse",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "A standards-compliant parser for **MD-LD (Markdown-Linked Data)** — a human-friendly RDF authoring format that extends Markdown with semantic annotations.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|