Scope: Fix serialising symbols with a cycle in them.

Now it does not recur once it finds a cycle.

Thanks to inchl for the report.
2 files changed, 37 insertions(+), 3 deletions(-)

M src/main/java/nl/grauw/glass/Scope.java
M src/test/java/nl/grauw/glass/SymbolsTest.java
M src/main/java/nl/grauw/glass/Scope.java +17 -3
@@ 124,10 124,10 @@ public class Scope implements Context {
 	}
 
 	public String serializeSymbols() {
-		return serializeSymbols("");
+		return serializeSymbols("", new SingleLinkedList<Scope>(this, null));
 	}
 
-	public String serializeSymbols(String namePrefix) {
+	public String serializeSymbols(String namePrefix, SingleLinkedList<Scope> breadcrumbs) {
 		StringBuilder builder = new StringBuilder();
 		TreeMap<String, Expression> sortedMap = new TreeMap<>(symbols);
 		for (Map.Entry<String, Expression> entry : sortedMap.entrySet()) {

          
@@ 143,7 143,9 @@ public class Scope implements Context {
 			if (value.is(Type.CONTEXT)) {
 				try {
 					Scope context = (Scope)value.getContext();
-					builder.append(context.serializeSymbols(name + "."));
+					if (!breadcrumbs.contains(context)) {
+						builder.append(context.serializeSymbols(name + ".", new SingleLinkedList<Scope>(context, breadcrumbs)));
+					}
 				} catch (EvaluationException e) {
 					// ignore
 				}

          
@@ 156,4 158,16 @@ public class Scope implements Context {
 		return serializeSymbols();
 	}
 
+	private static class SingleLinkedList<T> {
+		private T head;
+		private SingleLinkedList<T> tail;
+		public SingleLinkedList(T head, SingleLinkedList<T> tail) {
+			this.head = head;
+			this.tail = tail;
+		}
+		public boolean contains(T value) {
+			return value == head || (tail != null && tail.contains(value));
+		}
+	}
+
 }

          
M src/test/java/nl/grauw/glass/SymbolsTest.java +20 -0
@@ 210,6 210,26 @@ public class SymbolsTest extends TestBas
 		);
 	}
 
+	@Test
+	public void testCycle() {
+		assertIterableEquals(
+			s(
+				"Test: equ 1H",
+				"Test.x: equ 7H",
+				"Test.y: equ 1H",
+				"Test.z: equ 2H"
+			),
+			symbols(
+				" nop",
+				"Test: PROC",
+				"x: equ 7",
+				"y: equ Test",
+				"   nop",
+				"z: ENDP"
+			)
+		);
+	}
+
 	@TempDir
 	static Path temporaryDirectory;