s-sharp /SSharp.Core/DataTypes/List.cs

Language C# Lines 134
MD5 Hash c9b1b8d005335af7d91e8e7726121b83 Estimated Cost $1,855 (why?)
Repository https://code.google.com/p/s-sharp/ View Raw File
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SSharp.Core.Evaluator;

namespace SSharp.Core.DataTypes {
	/// <summary>
	/// Scheme lists are built from pairs and the empty list instance.
	/// </summary>
	public static class List {
		private static readonly Symbol dotSymbol = new Symbol(".");

		/// <summary>
		/// Creates a scheme list from an array.
		/// </summary>
		public static object Create(params object[] values) {
			return ParseDots(values);
		}

		/// <summary>
		/// Creates a scheme list from an IList.
		/// </summary>
		public static object Create(IEnumerable<object> values) {
			return ParseDots(values.ToArray());
		}

		/// <summary>
		/// Parses dots in the list, and returns the scheme list.
		/// </summary>
		private static object ParseDots(object[] values) {
			for (int i = 0; i < values.Length; i++) {
				if (dotSymbol.Equals(values[i])) {
					if (i > 0
						&& values.Length == i + 2
						&& !dotSymbol.Equals(values[i + 1])) {
						// if the dot is not the first element
						//		and it is the one-before-last element
						//		and the last element is not a dot
						// then this is the standard dotted syntax - list is something like (a b c d . e)
						//		e is then used instead of the empty-list to end (a b c d)
						return BuildList(values.Take(i), values[i + 1]);
					}

					if (i > 0
						&& values.Length > i + 3
						&& !dotSymbol.Equals(values[i + 1])
						&& dotSymbol.Equals(values[i + 2])
						&& !values.Skip(i + 3).Contains(dotSymbol)) {
						// if the dot is not the first element
						//		and it has at least three elements after it
						//		and the element 2 after it is a dot
						//		and no other element is a dot
						// then this is the infix dotted syntax - list is something like (a b . c . d e)
						//		c is then moved to the front of the list to produce (c a b d e)
							return BuildList(
								new[] { values[i + 1] }
								.Concat(values.Take(i))
								.Concat(values.Skip(i + 3)),
								EmptyList.Instance);
					}

					throw new SyntaxError("List: incorrect use of .");
				}
			}

			return BuildList(values, EmptyList.Instance);
		}

		/// <summary>
		/// Builds a list, with the given last element (normally EmptyList.Instace)
		/// </summary>
		private static object BuildList(IEnumerable<object> values, object lastElement) {
			object result = lastElement;
			// loop backwards to generate a forward-pointing list.
			foreach (object value in values.Reverse()) {
				result = new Cons(value, result);
			}

			return result;
		}

		/// <summary>
		/// Creates an IList from a scheme list.
		/// If the list is not a valid list (e.g. it has a non-list cdr in some location), null is returned.
		/// </summary>
		public static IList<object> Unpack(object list) {
			if (list == null) {
				return null;
			}

			List<object> result = new List<object>();

			// go through all the consecutive car's and add them to the result list - until the cdr is not a cons.
			object cdr = list;
			Cons cons = cdr as Cons;
			while (cons != null) {
				result.Add(cons.car);
				cdr = cons.cdr;
				cons = cdr as Cons;
			}

			if (cdr is EmptyList) {
				// valid list
				return result;
			} else {
				// the last element is not () (and not a Cons) - it's not a valid list.
				return null;
			}
		}

		/// <summary>
		/// A singleton that represents the empty list, ()
		/// </summary>
		public class EmptyList {
			public static readonly EmptyList Instance = new EmptyList();

			private EmptyList() { }

			public override int GetHashCode() {
				return 0;
			}

			public override bool Equals(object obj) {
				return obj is EmptyList;
			}

			public override string ToString() {
				return "()";
			}
		}
	}
}
Back to Top