/SSharp.Core/DataTypes/List.cs
C# | 133 lines | 78 code | 18 blank | 37 comment | 17 complexity | c9b1b8d005335af7d91e8e7726121b83 MD5 | raw file
- 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 "()";
- }
- }
- }
- }