diff --git a/csharp/Platform.Protocols.Lino.Tests/SingleLineParserTests.cs b/csharp/Platform.Protocols.Lino.Tests/SingleLineParserTests.cs
index 015fc1f..f57b612 100644
--- a/csharp/Platform.Protocols.Lino.Tests/SingleLineParserTests.cs
+++ b/csharp/Platform.Protocols.Lino.Tests/SingleLineParserTests.cs
@@ -312,5 +312,51 @@ public static void TestValueLinkParserTest()
Assert.Null(result[0].Id);
Assert.Equal(3, result[0].Values?.Count);
}
+
+ [Fact]
+ public static void TestRightArrowSyntaxTest()
+ {
+ var input = "(1 → 2)";
+ var parser = new Parser();
+ var links = parser.Parse(input);
+ var target = links.Format();
+ Assert.Equal("(1 2)", target);
+ }
+
+ [Fact]
+ public static void TestLeftArrowSyntaxTest()
+ {
+ var input = "(2 ← 1)";
+ var parser = new Parser();
+ var links = parser.Parse(input);
+ var target = links.Format();
+ Assert.Equal("(1 2)", target);
+ }
+
+ [Fact]
+ public static void TestArrowSyntaxEquivalenceTest()
+ {
+ var parser = new Parser();
+ var normal = parser.Parse("(1 2)");
+ var rightArrow = parser.Parse("(1 → 2)");
+ var leftArrow = parser.Parse("(2 ← 1)");
+
+ var normalFormatted = normal.Format();
+ var rightArrowFormatted = rightArrow.Format();
+ var leftArrowFormatted = leftArrow.Format();
+
+ Assert.Equal(normalFormatted, rightArrowFormatted);
+ Assert.Equal(normalFormatted, leftArrowFormatted);
+ }
+
+ [Fact]
+ public static void TestArrowSyntaxWithNamesTest()
+ {
+ var input = "(papa → mama)";
+ var parser = new Parser();
+ var links = parser.Parse(input);
+ var target = links.Format();
+ Assert.Equal("(papa mama)", target);
+ }
}
}
\ No newline at end of file
diff --git a/csharp/Platform.Protocols.Lino/Parser.peg b/csharp/Platform.Protocols.Lino/Parser.peg
index 6a79128..7ed08bc 100644
--- a/csharp/Platform.Protocols.Lino/Parser.peg
+++ b/csharp/Platform.Protocols.Lino/Parser.peg
@@ -11,9 +11,9 @@ anyLink > = ml:multiLineAnyLink eol { ml } / sl:singleLineAnyLink {
multiLineAnyLink > = multiLineValueLink / multiLineLink
singleLineAnyLink > = fl:singleLineLink eol { fl } / vl:singleLineValueLink eol { vl }
multiLineValueAndWhitespace > = value:referenceOrLink _ { value }
-multiLineValues >> = _ list:multiLineValueAndWhitespace* { list }
+multiLineValues >> = _ list:arrowValues { list } / _ list:multiLineValueAndWhitespace* { list }
singleLineValueAndWhitespace > = __ value:referenceOrLink { value }
-singleLineValues >> = list:singleLineValueAndWhitespace+ { list }
+singleLineValues >> = arrowValues / list:singleLineValueAndWhitespace+ { list }
singleLineLink > = __ id:(reference) __ ":" v:singleLineValues { new Link(id, v) }
multiLineLink > = "(" _ id:(reference) _ ":" v:multiLineValues _ ")" { new Link(id, v) }
singleLineValueLink > = v:singleLineValues { new Link(v) }
@@ -32,3 +32,7 @@ __ = [ \t]*
_ = whiteSpaceSymbol*
whiteSpaceSymbol = [ \t\n\r]
referenceSymbol = [^ \t\n\r(:)]
+
+arrowValues >> = rightArrowValues / leftArrowValues
+rightArrowValues >> = left:referenceOrLink _ "→" _ right:referenceOrLink { new List> { left, right } }
+leftArrowValues >> = right:referenceOrLink _ "←" _ left:referenceOrLink { new List> { left, right } }
diff --git a/csharp/Platform.Protocols.Lino/Platform.Protocols.Lino.csproj b/csharp/Platform.Protocols.Lino/Platform.Protocols.Lino.csproj
index 3dd410d..aec75cb 100644
--- a/csharp/Platform.Protocols.Lino/Platform.Protocols.Lino.csproj
+++ b/csharp/Platform.Protocols.Lino/Platform.Protocols.Lino.csproj
@@ -4,7 +4,7 @@
LinksPlatform's Platform.Protocols.Lino Class Library
Konstantin Diachenko
Platform.Protocols.Lino
- 0.6.0
+ 0.7.0
Konstantin Diachenko
net8
Platform.Protocols.Lino
@@ -22,7 +22,9 @@
true
snupkg
latest
- Now empty list of links is supported.
+ Added support for optional arrow syntax: (2 ← 1) = (1 → 2) = (1 2).
+Arrow syntax provides intuitive directional notation for links.
+Now empty list of links is supported.
(:) and : syntax for empty links is now forbidden, to reduce confusion.
Singlet links are supported, no more point link terminology.
enable
diff --git a/examples/arrow_syntax_test.js b/examples/arrow_syntax_test.js
new file mode 100644
index 0000000..91ff12c
--- /dev/null
+++ b/examples/arrow_syntax_test.js
@@ -0,0 +1,35 @@
+#!/usr/bin/env bun
+
+import { Parser } from '../js/src/Parser.js';
+import { formatLinks } from '../js/src/Link.js';
+
+const parser = new Parser();
+
+console.log("Testing Arrow Syntax Implementation");
+console.log("===================================");
+
+// Test the examples from the issue
+const tests = [
+ "(2 ← 1)",
+ "(1 → 2)",
+ "(1 2)",
+ "(papa → mama)",
+ "(daughter ← papa)"
+];
+
+console.log("Testing equivalence: (2 ← 1) = (1 → 2) = (1 2)");
+console.log("");
+
+for (const test of tests) {
+ try {
+ const result = parser.parse(test);
+ const formatted = formatLinks(result);
+ console.log(`Input: ${test}`);
+ console.log(`Output: ${formatted}`);
+ console.log("");
+ } catch (error) {
+ console.log(`Error parsing "${test}": ${error.message}`);
+ }
+}
+
+console.log("All tests completed!");
\ No newline at end of file
diff --git a/js/package.json b/js/package.json
index 99494d7..6704ed8 100644
--- a/js/package.json
+++ b/js/package.json
@@ -1,6 +1,6 @@
{
"name": "@linksplatform/protocols-lino",
- "version": "0.6.0",
+ "version": "0.7.0",
"description": "Lino protocol parser for JavaScript",
"main": "dist/index.js",
"type": "module",
diff --git a/js/src/grammar.pegjs b/js/src/grammar.pegjs
index b313eeb..219b29d 100644
--- a/js/src/grammar.pegjs
+++ b/js/src/grammar.pegjs
@@ -45,11 +45,11 @@ singleLineAnyLink = fl:singleLineLink eol { return fl; }
multiLineValueAndWhitespace = value:referenceOrLink _ { return value; }
-multiLineValues = _ list:multiLineValueAndWhitespace* { return list; }
+multiLineValues = _ list:arrowValues { return list; } / _ list:multiLineValueAndWhitespace* { return list; }
singleLineValueAndWhitespace = __ value:referenceOrLink { return value; }
-singleLineValues = list:singleLineValueAndWhitespace+ { return list; }
+singleLineValues = arrowValues / list:singleLineValueAndWhitespace+ { return list; }
singleLineLink = __ id:reference __ ":" v:singleLineValues { return { id: id, values: v }; }
@@ -81,4 +81,10 @@ _ = whiteSpaceSymbol*
whiteSpaceSymbol = [ \t\n\r]
-referenceSymbol = [^ \t\n\r(:)]
\ No newline at end of file
+referenceSymbol = [^ \t\n\r(:)]
+
+arrowValues = rightArrowValues / leftArrowValues
+
+rightArrowValues = left:referenceOrLink _ "→" _ right:referenceOrLink { return [left, right]; }
+
+leftArrowValues = right:referenceOrLink _ "←" _ left:referenceOrLink { return [left, right]; }
\ No newline at end of file
diff --git a/js/src/parser-generated.js b/js/src/parser-generated.js
index 40ea078..fd28202 100644
--- a/js/src/parser-generated.js
+++ b/js/src/parser-generated.js
@@ -170,6 +170,8 @@ function peg$parse(input, options) {
const peg$c3 = "\"";
const peg$c4 = "'";
const peg$c5 = " ";
+ const peg$c6 = "\u2192";
+ const peg$c7 = "\u2190";
const peg$r0 = /^[^"]/;
const peg$r1 = /^[^']/;
@@ -191,6 +193,8 @@ function peg$parse(input, options) {
const peg$e10 = peg$classExpectation([" ", "\t"], false, false, false);
const peg$e11 = peg$classExpectation([" ", "\t", "\n", "\r"], false, false, false);
const peg$e12 = peg$classExpectation([" ", "\t", "\n", "\r", "(", ":", ")"], true, false, false);
+ const peg$e13 = peg$literalExpectation("\u2192", false);
+ const peg$e14 = peg$literalExpectation("\u2190", false);
function peg$f0(links) { return links; }
function peg$f1() { return []; }
@@ -209,18 +213,21 @@ function peg$parse(input, options) {
function peg$f12(vl) { return vl; }
function peg$f13(value) { return value; }
function peg$f14(list) { return list; }
- function peg$f15(value) { return value; }
- function peg$f16(list) { return list; }
- function peg$f17(id, v) { return { id: id, values: v }; }
+ function peg$f15(list) { return list; }
+ function peg$f16(value) { return value; }
+ function peg$f17(list) { return list; }
function peg$f18(id, v) { return { id: id, values: v }; }
- function peg$f19(v) { return { values: v }; }
+ function peg$f19(id, v) { return { id: id, values: v }; }
function peg$f20(v) { return { values: v }; }
- function peg$f21(chars) { return chars.join(''); }
- function peg$f22(r) { return r.join(''); }
+ function peg$f21(v) { return { values: v }; }
+ function peg$f22(chars) { return chars.join(''); }
function peg$f23(r) { return r.join(''); }
- function peg$f24(spaces) { return spaces.length > getCurrentIndentation(); }
- function peg$f25(spaces) { pushIndentation(spaces); }
- function peg$f26(spaces) { return checkIndentation(spaces); }
+ function peg$f24(r) { return r.join(''); }
+ function peg$f25(spaces) { return spaces.length > getCurrentIndentation(); }
+ function peg$f26(spaces) { pushIndentation(spaces); }
+ function peg$f27(spaces) { return checkIndentation(spaces); }
+ function peg$f28(left, right) { return [left, right]; }
+ function peg$f29(right, left) { return [left, right]; }
let peg$currPos = options.peg$currPos | 0;
let peg$savedPos = peg$currPos;
const peg$posDetailsCache = [{ line: 1, column: 1 }];
@@ -647,14 +654,26 @@ function peg$parse(input, options) {
s0 = peg$currPos;
s1 = peg$parse_();
- s2 = [];
- s3 = peg$parsemultiLineValueAndWhitespace();
- while (s3 !== peg$FAILED) {
- s2.push(s3);
+ s2 = peg$parsearrowValues();
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f14(s2);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ s1 = peg$parse_();
+ s2 = [];
s3 = peg$parsemultiLineValueAndWhitespace();
+ while (s3 !== peg$FAILED) {
+ s2.push(s3);
+ s3 = peg$parsemultiLineValueAndWhitespace();
+ }
+ peg$savedPos = s0;
+ s0 = peg$f15(s2);
}
- peg$savedPos = s0;
- s0 = peg$f14(s2);
return s0;
}
@@ -667,7 +686,7 @@ function peg$parse(input, options) {
s2 = peg$parsereferenceOrLink();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
- s0 = peg$f15(s2);
+ s0 = peg$f16(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -679,22 +698,25 @@ function peg$parse(input, options) {
function peg$parsesingleLineValues() {
let s0, s1, s2;
- s0 = peg$currPos;
- s1 = [];
- s2 = peg$parsesingleLineValueAndWhitespace();
- if (s2 !== peg$FAILED) {
- while (s2 !== peg$FAILED) {
- s1.push(s2);
- s2 = peg$parsesingleLineValueAndWhitespace();
+ s0 = peg$parsearrowValues();
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ s1 = [];
+ s2 = peg$parsesingleLineValueAndWhitespace();
+ if (s2 !== peg$FAILED) {
+ while (s2 !== peg$FAILED) {
+ s1.push(s2);
+ s2 = peg$parsesingleLineValueAndWhitespace();
+ }
+ } else {
+ s1 = peg$FAILED;
}
- } else {
- s1 = peg$FAILED;
- }
- if (s1 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$f16(s1);
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$f17(s1);
+ }
+ s0 = s1;
}
- s0 = s1;
return s0;
}
@@ -718,7 +740,7 @@ function peg$parse(input, options) {
s5 = peg$parsesingleLineValues();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
- s0 = peg$f17(s2, s5);
+ s0 = peg$f18(s2, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -760,17 +782,22 @@ function peg$parse(input, options) {
}
if (s5 !== peg$FAILED) {
s6 = peg$parsemultiLineValues();
- s7 = peg$parse_();
- if (input.charCodeAt(peg$currPos) === 41) {
- s8 = peg$c2;
- peg$currPos++;
- } else {
- s8 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e2); }
- }
- if (s8 !== peg$FAILED) {
- peg$savedPos = s0;
- s0 = peg$f18(s3, s6);
+ if (s6 !== peg$FAILED) {
+ s7 = peg$parse_();
+ if (input.charCodeAt(peg$currPos) === 41) {
+ s8 = peg$c2;
+ peg$currPos++;
+ } else {
+ s8 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e2); }
+ }
+ if (s8 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f19(s3, s6);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -798,7 +825,7 @@ function peg$parse(input, options) {
s1 = peg$parsesingleLineValues();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
- s1 = peg$f19(s1);
+ s1 = peg$f20(s1);
}
s0 = s1;
@@ -818,17 +845,22 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
s2 = peg$parsemultiLineValues();
- s3 = peg$parse_();
- if (input.charCodeAt(peg$currPos) === 41) {
- s4 = peg$c2;
- peg$currPos++;
- } else {
- s4 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e2); }
- }
- if (s4 !== peg$FAILED) {
- peg$savedPos = s0;
- s0 = peg$f20(s2);
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parse_();
+ if (input.charCodeAt(peg$currPos) === 41) {
+ s4 = peg$c2;
+ peg$currPos++;
+ } else {
+ s4 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e2); }
+ }
+ if (s4 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f21(s2);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -871,7 +903,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
- s1 = peg$f21(s1);
+ s1 = peg$f22(s1);
}
s0 = s1;
@@ -922,7 +954,7 @@ function peg$parse(input, options) {
}
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
- s0 = peg$f22(s2);
+ s0 = peg$f23(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -983,7 +1015,7 @@ function peg$parse(input, options) {
}
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
- s0 = peg$f23(s2);
+ s0 = peg$f24(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1023,7 +1055,7 @@ function peg$parse(input, options) {
}
}
peg$savedPos = peg$currPos;
- s2 = peg$f24(s1);
+ s2 = peg$f25(s1);
if (s2) {
s2 = undefined;
} else {
@@ -1031,7 +1063,7 @@ function peg$parse(input, options) {
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
- s0 = peg$f25(s1);
+ s0 = peg$f26(s1);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1063,7 +1095,7 @@ function peg$parse(input, options) {
}
}
peg$savedPos = peg$currPos;
- s2 = peg$f26(s1);
+ s2 = peg$f27(s1);
if (s2) {
s2 = undefined;
} else {
@@ -1210,6 +1242,89 @@ function peg$parse(input, options) {
return s0;
}
+ function peg$parsearrowValues() {
+ let s0;
+
+ s0 = peg$parserightArrowValues();
+ if (s0 === peg$FAILED) {
+ s0 = peg$parseleftArrowValues();
+ }
+
+ return s0;
+ }
+
+ function peg$parserightArrowValues() {
+ let s0, s1, s2, s3, s4, s5;
+
+ s0 = peg$currPos;
+ s1 = peg$parsereferenceOrLink();
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parse_();
+ if (input.charCodeAt(peg$currPos) === 8594) {
+ s3 = peg$c6;
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e13); }
+ }
+ if (s3 !== peg$FAILED) {
+ s4 = peg$parse_();
+ s5 = peg$parsereferenceOrLink();
+ if (s5 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f28(s1, s5);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parseleftArrowValues() {
+ let s0, s1, s2, s3, s4, s5;
+
+ s0 = peg$currPos;
+ s1 = peg$parsereferenceOrLink();
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parse_();
+ if (input.charCodeAt(peg$currPos) === 8592) {
+ s3 = peg$c7;
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e14); }
+ }
+ if (s3 !== peg$FAILED) {
+ s4 = peg$parse_();
+ s5 = peg$parsereferenceOrLink();
+ if (s5 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s0 = peg$f29(s1, s5);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
let indentationStack = [0];
diff --git a/js/tests/SingleLineParser.test.js b/js/tests/SingleLineParser.test.js
index 70c1438..fbba4b2 100644
--- a/js/tests/SingleLineParser.test.js
+++ b/js/tests/SingleLineParser.test.js
@@ -226,4 +226,38 @@ test('Test value link (parser)', () => {
expect(result.length).toBe(1);
expect(result[0].id).toBe(null);
expect(result[0].values.length).toBe(3);
+});
+
+test('Test right arrow syntax', () => {
+ const input = '(1 → 2)';
+ const result = parser.parse(input);
+ const target = formatLinks(result);
+ expect(target).toBe('(1 2)');
+});
+
+test('Test left arrow syntax', () => {
+ const input = '(2 ← 1)';
+ const result = parser.parse(input);
+ const target = formatLinks(result);
+ expect(target).toBe('(1 2)');
+});
+
+test('Test arrow syntax equivalence', () => {
+ const normal = parser.parse('(1 2)');
+ const rightArrow = parser.parse('(1 → 2)');
+ const leftArrow = parser.parse('(2 ← 1)');
+
+ const normalFormatted = formatLinks(normal);
+ const rightArrowFormatted = formatLinks(rightArrow);
+ const leftArrowFormatted = formatLinks(leftArrow);
+
+ expect(rightArrowFormatted).toBe(normalFormatted);
+ expect(leftArrowFormatted).toBe(normalFormatted);
+});
+
+test('Test arrow syntax with names', () => {
+ const input = '(papa → mama)';
+ const result = parser.parse(input);
+ const target = formatLinks(result);
+ expect(target).toBe('(papa mama)');
});
\ No newline at end of file
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 6ccebfe..bf7484a 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "platform-lino"
-version = "0.6.0"
+version = "0.7.0"
edition = "2021"
description = "Rust implementation of the Lino protocol parser"
license = "Unlicense"
diff --git a/rust/src/parser.rs b/rust/src/parser.rs
index cffc71c..f10114a 100644
--- a/rust/src/parser.rs
+++ b/rust/src/parser.rs
@@ -91,6 +91,35 @@ fn is_reference_char(c: char) -> bool {
!is_whitespace_char(c) && c != '(' && c != ':' && c != ')'
}
+fn arrow_values<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec> {
+ alt((
+ |i| right_arrow_values(i, state),
+ |i| left_arrow_values(i, state)
+ )).parse(input)
+}
+
+fn right_arrow_values<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec> {
+ (
+ |i| reference_or_link(i, state),
+ whitespace,
+ char('→'),
+ whitespace,
+ |i| reference_or_link(i, state)
+ ).map(|(left, _, _, _, right)| vec![left, right])
+ .parse(input)
+}
+
+fn left_arrow_values<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec> {
+ (
+ |i| reference_or_link(i, state),
+ whitespace,
+ char('←'),
+ whitespace,
+ |i| reference_or_link(i, state)
+ ).map(|(right, _, _, _, left)| vec![left, right]) // Note: order is swapped for left arrow
+ .parse(input)
+}
+
fn horizontal_whitespace(input: &str) -> IResult<&str, &str> {
take_while(is_horizontal_whitespace)(input)
}
@@ -159,7 +188,10 @@ fn multi_line_value_and_whitespace<'a>(input: &'a str, state: &ParserState) -> I
fn multi_line_values<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec> {
preceded(
whitespace,
- many0(|i| multi_line_value_and_whitespace(i, state))
+ alt((
+ |i| arrow_values(i, state),
+ many0(|i| multi_line_value_and_whitespace(i, state))
+ ))
).parse(input)
}
@@ -171,7 +203,10 @@ fn single_line_value_and_whitespace<'a>(input: &'a str, state: &ParserState) ->
}
fn single_line_values<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec> {
- many1(|i| single_line_value_and_whitespace(i, state)).parse(input)
+ alt((
+ |i| arrow_values(i, state),
+ many1(|i| single_line_value_and_whitespace(i, state))
+ )).parse(input)
}
fn single_line_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
diff --git a/rust/tests/single_line_parser_tests.rs b/rust/tests/single_line_parser_tests.rs
index fd44f35..3a51ca2 100644
--- a/rust/tests/single_line_parser_tests.rs
+++ b/rust/tests/single_line_parser_tests.rs
@@ -321,4 +321,42 @@ fn test_single_line_link() {
assert_eq!(result.1.len(), 1);
assert_eq!(result.1[0].id, Some("id".to_string()));
assert_eq!(result.1[0].values.len(), 2);
+}
+
+#[test]
+fn test_right_arrow_syntax() {
+ let input = "(1 → 2)";
+ let result = parse_lino(input).unwrap();
+ let formatted = format_links_multiline(&result);
+ assert_eq!(formatted, "(1 2)");
+}
+
+#[test]
+fn test_left_arrow_syntax() {
+ let input = "(2 ← 1)";
+ let result = parse_lino(input).unwrap();
+ let formatted = format_links_multiline(&result);
+ assert_eq!(formatted, "(1 2)");
+}
+
+#[test]
+fn test_arrow_syntax_equivalence() {
+ let normal = parse_lino("(1 2)").unwrap();
+ let right_arrow = parse_lino("(1 → 2)").unwrap();
+ let left_arrow = parse_lino("(2 ← 1)").unwrap();
+
+ let normal_formatted = format_links_multiline(&normal);
+ let right_arrow_formatted = format_links_multiline(&right_arrow);
+ let left_arrow_formatted = format_links_multiline(&left_arrow);
+
+ assert_eq!(normal_formatted, right_arrow_formatted);
+ assert_eq!(normal_formatted, left_arrow_formatted);
+}
+
+#[test]
+fn test_arrow_syntax_with_names() {
+ let input = "(papa → mama)";
+ let result = parse_lino(input).unwrap();
+ let formatted = format_links_multiline(&result);
+ assert_eq!(formatted, "(papa mama)");
}
\ No newline at end of file