diff --git a/lib/src/org/model.dart b/lib/src/org/model.dart index bae8eac..7719607 100644 --- a/lib/src/org/model.dart +++ b/lib/src/org/model.dart @@ -1861,40 +1861,90 @@ class OrgEntity extends OrgLeafNode { } } -class OrgSubscript extends OrgLeafNode { - OrgSubscript(this.leading, this.body); +class OrgSubscript extends OrgParentNode { + OrgSubscript(this.leading, this.body, this.trailing); final String leading; - final String body; + final OrgContent body; + final String trailing; + + @override + List get children => [body]; @override bool contains(Pattern pattern) => - leading.contains(pattern) || body.contains(pattern); + leading.contains(pattern) || + body.contains(pattern) || + trailing.contains(pattern); + + @override + String toString() => 'OrgSubscript'; @override _toMarkupImpl(OrgSerializer buf) { buf ..write(leading) - ..write(body); + ..visit(body) + ..write(trailing); } + + @override + OrgSubscript fromChildren(List children) => + copyWith(body: children.single as OrgContent); + + OrgSubscript copyWith({ + String? leading, + OrgContent? body, + String? trailing, + }) => + OrgSubscript( + leading ?? this.leading, + body ?? this.body, + trailing ?? this.trailing, + ); } -class OrgSuperscript extends OrgLeafNode { - OrgSuperscript(this.leading, this.body); +class OrgSuperscript extends OrgParentNode { + OrgSuperscript(this.leading, this.body, this.trailing); final String leading; - final String body; + final OrgContent body; + final String trailing; + + @override + List get children => [body]; @override bool contains(Pattern pattern) => - leading.contains(pattern) || body.contains(pattern); + leading.contains(pattern) || + body.contains(pattern) || + trailing.contains(pattern); + + @override + String toString() => 'OrgSuperscript'; @override _toMarkupImpl(OrgSerializer buf) { buf ..write(leading) - ..write(body); + ..visit(body) + ..write(trailing); } + + @override + OrgSuperscript fromChildren(List children) => + copyWith(body: children.single as OrgContent); + + OrgSuperscript copyWith({ + String? leading, + OrgContent? body, + String? trailing, + }) => + OrgSuperscript( + leading ?? this.leading, + body ?? this.body, + trailing ?? this.trailing, + ); } class OrgLocalVariables extends OrgLeafNode { diff --git a/lib/src/org/parser.dart b/lib/src/org/parser.dart index ee9244c..3982d71 100644 --- a/lib/src/org/parser.dart +++ b/lib/src/org/parser.dart @@ -186,18 +186,43 @@ class OrgContentParserDefinition extends OrgContentGrammarDefinition { @override Parser subscript() => super.subscript().map((values) { - final leading = values[0] as String; - final body = values[1] as String; - return OrgSubscript(leading, body); + var leading = values[0] as String; + var trailing = ''; + var body = values[1] as String; + if (body.startsWith('{') && body.endsWith('}')) { + leading += '{'; + trailing += '}'; + body = body.substring(1, body.length - 1); + } + final content = buildFrom(_subSuperscriptRichBody()) + .castList() + .end() + .parse(body) + .value; + return OrgSubscript(leading, OrgContent(content), trailing); }); @override Parser superscript() => super.superscript().map((values) { - final leading = values[0] as String; - final body = values[1] as String; - return OrgSuperscript(leading, body); + var leading = values[0] as String; + var trailing = ''; + var body = values[1] as String; + if (body.startsWith('{') && body.endsWith('}')) { + leading += '{'; + trailing += '}'; + body = body.substring(1, body.length - 1); + } + final content = buildFrom(_subSuperscriptRichBody()) + .castList() + .end() + .parse(body) + .value; + return OrgSuperscript(leading, OrgContent(content), trailing); }); + /// Only used on this layer to parse subscript and superscript content + Parser _subSuperscriptRichBody() => (ref0(entity) | ref0(plainText)).star(); + @override Parser macroReference() => super .macroReference() diff --git a/test/org/model/subscript_test.dart b/test/org/model/subscript_test.dart index 740adf4..2852dfa 100644 --- a/test/org/model/subscript_test.dart +++ b/test/org/model/subscript_test.dart @@ -7,6 +7,13 @@ void main() { final definition = OrgContentParserDefinition(); final parser = seq2(letter(), definition.buildFrom(definition.subscript())).end(); + test('with entity', () { + final result = parser.parse(r'a_\alpha'); + final (_, OrgSubscript sup) = result.value; + expect(sup.contains('alpha'), isTrue); + expect(sup.contains(r'\alpha'), isFalse); + expect(sup.toMarkup(), r'_\alpha'); + }); test('nested bracketed expression', () { final result = parser.parse('a_{a1 {b2}}'); final (_, OrgSubscript sup) = result.value; diff --git a/test/org/parser/subscript_test.dart b/test/org/parser/subscript_test.dart index 11bec76..68bbdf9 100644 --- a/test/org/parser/subscript_test.dart +++ b/test/org/parser/subscript_test.dart @@ -7,11 +7,39 @@ void main() { final definition = OrgContentParserDefinition(); final parser = seq2(letter(), definition.buildFrom(definition.subscript())).end(); + test('with entity', () { + final result = parser.parse(r'a_\alpha'); + final (_, OrgSubscript sup) = result.value; + expect(sup.leading, '_'); + final body = sup.body.children.single as OrgEntity; + expect(body.name, 'alpha'); + expect(sup.trailing, ''); + }); + test('with text and entity', () { + final result = parser.parse(r'a_{1 + \alpha}'); + final (_, OrgSubscript sup) = result.value; + expect(sup.leading, '_{'); + final body1 = sup.body.children[0] as OrgPlainText; + expect(body1.content, '1 + '); + final body2 = sup.body.children[1] as OrgEntity; + expect(body2.name, 'alpha'); + expect(sup.trailing, '}'); + }); test('nested bracketed expression', () { final result = parser.parse('a_{a1 {b2}}'); final (_, OrgSubscript sup) = result.value; + expect(sup.leading, '_{'); + final body = sup.body.children.single as OrgPlainText; + expect(body.content, 'a1 {b2}'); + expect(sup.trailing, '}'); + }); + test('nested sexp', () { + final result = parser.parse('a_(a1 (b2))'); + final (_, OrgSubscript sup) = result.value; expect(sup.leading, '_'); - expect(sup.body, '{a1 {b2}}'); + final body = sup.body.children.single as OrgPlainText; + expect(body.content, '(a1 (b2))'); + expect(sup.trailing, ''); }); }); } diff --git a/test/org/parser/superscript_test.dart b/test/org/parser/superscript_test.dart index ccf8aa1..3b2cb10 100644 --- a/test/org/parser/superscript_test.dart +++ b/test/org/parser/superscript_test.dart @@ -7,11 +7,39 @@ void main() { final definition = OrgContentParserDefinition(); final parser = seq2(letter(), definition.buildFrom(definition.superscript())).end(); + test('with entity', () { + final result = parser.parse(r'a^\alpha'); + final (_, OrgSuperscript sup) = result.value; + expect(sup.leading, '^'); + final body = sup.body.children.single as OrgEntity; + expect(body.name, 'alpha'); + expect(sup.trailing, ''); + }); + test('with text and entity', () { + final result = parser.parse(r'a^{1 + \alpha}'); + final (_, OrgSuperscript sup) = result.value; + expect(sup.leading, '^{'); + final body1 = sup.body.children[0] as OrgPlainText; + expect(body1.content, '1 + '); + final body2 = sup.body.children[1] as OrgEntity; + expect(body2.name, 'alpha'); + expect(sup.trailing, '}'); + }); test('nested bracketed expression', () { final result = parser.parse('a^{a1 {b2}}'); final (_, OrgSuperscript sup) = result.value; + expect(sup.leading, '^{'); + final body = sup.body.children.single as OrgPlainText; + expect(body.content, 'a1 {b2}'); + expect(sup.trailing, '}'); + }); + test('nested sexp', () { + final result = parser.parse('a^(a1 (b2))'); + final (_, OrgSuperscript sup) = result.value; expect(sup.leading, '^'); - expect(sup.body, '{a1 {b2}}'); + final body = sup.body.children.single as OrgPlainText; + expect(body.content, '(a1 (b2))'); + expect(sup.trailing, ''); }); }); }