Author Topic: Linq2Lisp test invitation  (Read 1911 times)

0 Members and 1 Guest are viewing this topic.

TheMaster

  • Guest
Linq2Lisp test invitation
« on: November 12, 2012, 03:56:15 PM »
Linq2Lisp is an AutoCAD-specific framework that supports VisualLisp/Managed .NET interoperability, focusing on the following functional areas:

1.  LispFunction argument parsing, processing and validation (as shown in another thread)

2.  Lisp/CLR data interoperability, including support for LISP list representations in managed code using Linq and IEnumerable<T>, with APIs that vastly-simplify handling of lists passed to managed code, as well as constructing and manipulating list data to be returned to LISP from managed code. This functionality is provided by the ListBuilder class, and a library of extension methods. In other words, no more having to deal with the nonsense/absurdity of bracketed list delimiters in resultbuffers (e.g., LispDataType.BeginList/EndList).

I'm looking for someone making extensive use of LispFunctions in a real-world scenario, and more notably, is marshaling LISP lists back and forth between LISP and managed code.

The library will not be publicly available because it is going to be included with a publication I'm working on, but any tester(s) will get the finished product for internal-use-only.

Here's one of the test files as a 'teaser' :-D

Code - C#: [Select]
  1.  
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Collections;
  7. using Autodesk.AutoCAD.Runtime;
  8. using Autodesk.AutoCAD.DatabaseServices;
  9. using Autodesk.AutoCAD.ApplicationServices;
  10. using Autodesk.AutoCAD.EditorInput;
  11. using LinqToLisp;
  12. using Autodesk.AutoCAD.Geometry;
  13.  
  14. namespace LinqToLispExamples
  15. {
  16.       /// <summary>
  17.       ///
  18.       /// The Implode() extension method:
  19.       ///
  20.       /// Since an array of TypedValue[] received from LISP
  21.       /// representing a complex list (e.g., a list with nested
  22.       /// sub-lists) is essentially 'exploded' or 'flattened',
  23.       /// the Implode() extension method performs the inverse,
  24.       /// converting nested lists into IEnumerables that will
  25.       /// allow a complex list to be dealt with in a recusive
  26.       /// manner, similar to how they are dealt with in LISP.
  27.       ///
  28.       /// The IEnumerable returned by this method enumerates
  29.       /// objects that are either TypedValues (e.g. 'atoms'),
  30.       /// or objects that implement IEnumerable (nested lists)
  31.       /// that themselves, enumerate a sequence of TypedValues
  32.       /// or IEnumerables representing more-deeply nested lists.
  33.       ///
  34.       /// Elements from the source TypedValue[] array that have
  35.       /// the follwing 'list-delimiter' TypeCodes never appear
  36.       /// in the sequence enumerated by this type, as all nested
  37.       /// lists are 'imploded' into IEnumerables:
  38.       ///
  39.       ///   LispDataType.BeginList
  40.       ///   LispDataType.EndList
  41.       ///   LispDataType.DottedPair
  42.       ///  
  43.       /// Nil and empty lists:
  44.       ///
  45.       /// This class does not convert 'NIL' (e.g., an empty
  46.       /// list) into empty IEnumerables, so one should be
  47.       /// wary of that when expecting a sublist to appear
  48.       /// at a certain point in the sequence. If a list is
  49.       /// either empty, or the element is explicitly nil, a
  50.       /// TypedValue with the TypeCode LispDataType.Nil will
  51.       /// appear in the sequence.
  52.       ///
  53.       /// Entity data association lists:
  54.       ///
  55.       /// AutoCAD does not 'explode' or flatten association lists
  56.       /// like those returned by the LISP (entget) function, and
  57.       /// instead returns a simple array of TypedValues, where the
  58.       /// TypeCode of each element is the numeric value of each
  59.       /// sublist's first element in the LISP representation, and
  60.       /// the Value of each TypedValue is the ('cdr') of each
  61.       /// sublist.
  62.       ///
  63.       /// For that reason, 'imploding' an entity data list yields
  64.       /// an IEnumerable that will enumerate the original source
  65.       /// TypedValue[] array elements.
  66.       ///
  67.       /// The Dump() function in the included sample code serves
  68.       /// as an exmple of how complex, 'exploded' lists passed
  69.       /// in a managed LispFunction's ResultBuffer argument can
  70.       /// be dealt with in a recursive, or more 'lisp-ish' way.
  71.       ///
  72.       /// The Implode() method and related helper classes perform
  73.       /// this task without copying or duplicating the source data,  
  74.       /// making them efficient with larger amounts of data.
  75.  
  76.  
  77.       /// Explode(): The functional inverse of Implode() (almost)
  78.       ///
  79.       /// This method can take the IEnumerable returned by a call to
  80.       /// Implode(), and return an IEnumerable<TypedValue> containing
  81.       /// the original sequence that was passed to Implode(), making
  82.       /// it the functional inverse (almost, see below).
  83.       ///
  84.       /// This method can be used to build a ResultBuffer that can be
  85.       /// returned back to LISP by a called LispFunction, containing an
  86.       /// arbitrarily-complex list. The method effectively 'flattens'
  87.       /// all IEnumerbles contained in the argument, into a simple array
  88.       /// of TypedValues that produce the equivalent LISP list.
  89.       ///
  90.       /// This method will flatten any list composed of IEnumerables
  91.       /// regardless of complexity or level of nesting, simplifying
  92.       /// construction of list results that are to be passed back to
  93.       /// calling LISP code.
  94.       ///
  95.       /// Caveats: This method is not the exact functional inverse of
  96.       /// Implode() in one regards, which is that dotted lists are not
  97.       /// reconstituted as such. In other words, given the result of
  98.       /// passing the array of TypedValues produced by the following
  99.       /// LISP list to Implode():
  100.       ///  
  101.       ///   ((1 . "one") (2 . "two") (3 . "three"))
  102.       ///  
  103.       /// And then passing that result to Explode() will, when passed
  104.       /// back to a LISP caller, yield this:
  105.       ///
  106.       ///   ((1 "one") (2 "two") (3 "three"))
  107.       ///  
  108.       /// Following the same convention used by Implode(), this method
  109.       /// will convert an empty IEnumerable (e.g., one with no elements)
  110.       /// to NIL (the empty list) in the result returned back to LISP.
  111.       ///
  112.       /// The IEnumerable argument to Explode() must consist entirely of
  113.       /// TypedValue instances or instances of IEnumerable whose elements
  114.       /// are themselves TypedValue instances, recursively.
  115.       ///  
  116.       /// A simple example:
  117.       ///
  118.       ///    ArrayList list1 = new ArrayList();
  119.       ///    list1.Add( new TypedValue( (short) LispDataType.Text, "a" ));
  120.       ///    list1.Add( new TypedValue( (short) LispDataType.Text, "b" ));
  121.       ///    list1.Add( new TypedValue( (short) LispDataType.Text, "c" ));
  122.       ///    
  123.       ///    ArrayList list2 = new ArrayList();
  124.       ///    list2.Add( new TypedValue( (short) LispDataType.Int16, 3 ));
  125.       ///    list2.Add( new TypedValue( (short) LispDataType.Int16, 4 ));
  126.       ///    list2.Add( list1 );
  127.       ///    list2.Add( new TypedValue( (short) LispDataType.Int16, 5 ));
  128.       ///    list2.Add( new TypedValue( (short) LispDataType.Int16, 6 ));
  129.       ///    
  130.       ///    ArrayList list3 = new ArrayList();
  131.       ///    list3.Add( new TypedValue( (short) LispDataType.Int16, 1 ));
  132.       ///    list3.Add( new TypedValue( (short) LispDataType.Int16, 2 ));
  133.       ///    list3.Add( list2 );
  134.       ///    list3.Add( new TypedValue( (short) LispDataType.Int16, 7 ));
  135.       ///    list3.Add( new TypedValue( (short) LispDataType.Int16, 8 ));
  136.       ///    
  137.       ///    TypedValue[] array = list3.Explode().ToArray();
  138.       ///    
  139.       ///    ResultBuffer result = new ResultBuffer( array );
  140.       ///
  141.       /// When the above ResultBuffer 'result' is returned by a LispFunction
  142.       /// to calling LISP code, it yields the following LISP list:
  143.       ///    
  144.       ///    (1 2 (3 4 ("a" "b" "c") 5 6) 7 8)
  145.       ///    
  146.       /// Notice that the complex list that is returned to LISP was
  147.       /// constructed without having to add TypedValue elements with
  148.       /// the ListDataType.BeginList and ListDataType.EndList list-
  149.       /// delimiting TypeCodes.
  150.  
  151.    /// test code
  152.    
  153.    public static class LinqToLispTest
  154.    {
  155.       /////////////////////////////////////////////////////////////////////
  156.       ///
  157.       /// The (implode-test) lisp function:
  158.       ///
  159.       /// Accepts 0 or more arguments of any type,
  160.       /// including lists, and dumps the 'imploded'
  161.       /// arguments to the console.
  162.       ///
  163.       /// A few examples:
  164.       ///
  165.       ///   Command: (implode-test 100 200 '("how" "now" (2) "brown" (3 4 nil 5 6) "cow") 300 400)
  166.       ///  
  167.       ///   [0]: Int16: 100
  168.       ///   [1]: Int16: 200
  169.       ///   [2]: (IEnumerable):
  170.       ///      [0]: Text: "how"
  171.       ///      [1]: Text: "now"
  172.       ///      [2]: (IEnumerable):
  173.       ///         [0]: Int16: 2
  174.       ///      [3]: Text: "brown"
  175.       ///      [4]: (IEnumerable):
  176.       ///         [0]: Int16: 3
  177.       ///         [1]: Int16: 4
  178.       ///         [2]: Nil: (null)
  179.       ///         [3]: Int16: 5
  180.       ///         [4]: Int16: 6
  181.       ///      [5]: Text: "cow"
  182.       ///   [3]: Int16: 300
  183.       ///   [4]: Int16: 400
  184.       ///  
  185.       ///   Command:
  186.       ///
  187.       ///   /// NOTE: Support for round-tripping 'dotted pairs' is a major "TODO"
  188.       ///   ///       that has yet to be done.
  189.       ///  
  190.       ///   Command: (implode-test '(("oranges" . 25) ("apples" . 40) ("pears". 32)))
  191.       ///  
  192.       ///   [0]: (IEnumerable):
  193.       ///      [0]: (IEnumerable):
  194.       ///         [0]: Text: "oranges"
  195.       ///         [1]: Int16: 25
  196.       ///      [1]: (IEnumerable):
  197.       ///         [0]: Text: "apples"
  198.       ///         [1]: Int16: 40
  199.       ///      [2]: (IEnumerable):
  200.       ///         [0]: Text: "pears"
  201.       ///         [1]: Int16: 32
  202.       ///
  203.       ///   Command: (setq somelist '("a" "b" "c" (100 200 300 400) "d" "e" "f"))
  204.       ///   ("a" "b" "c" (100 200 300 400) "d" "e" "f")
  205.       ///  
  206.       ///   Command: (apply 'implode-test somelist)
  207.       ///  
  208.       ///   [0]: Text: "a"
  209.       ///   [1]: Text: "b"
  210.       ///   [2]: Text: "c"
  211.       ///   [3]: (IEnumerable):
  212.       ///      [0]: Int16: 100
  213.       ///      [1]: Int16: 200
  214.       ///      [2]: Int16: 300
  215.       ///      [3]: Int16: 400
  216.       ///   [4]: Text: "d"
  217.       ///   [5]: Text: "e"
  218.       ///   [6]: Text: "f"
  219.       ///
  220.       ///
  221.       ///   Command: (implode-test (entget (entnext)) (entget (entlast)))
  222.       ///  
  223.       ///   [0]: (IEnumerable):
  224.       ///      [0]: -1: (8796082559792)
  225.       ///      [1]: 0: "CIRCLE"
  226.       ///      [2]: 330: (8796082551280)
  227.       ///      [3]: 5: "1AB"
  228.       ///      [4]: 100: "AcDbEntity"
  229.       ///      [5]: 67: 0
  230.       ///      [6]: 410: "Model"
  231.       ///      [7]: 8: "0"
  232.       ///      [8]: 100: "AcDbCircle"
  233.       ///      [9]: 10: (0,0,0)
  234.       ///      [10]: 40: 1
  235.       ///      [11]: 210: (0,0,1)
  236.       ///   [1]: (IEnumerable):
  237.       ///      [0]: -1: (8796082559808)
  238.       ///      [1]: 0: "LINE"
  239.       ///      [2]: 330: (8796082551280)
  240.       ///      [3]: 5: "1AC"
  241.       ///      [4]: 100: "AcDbEntity"
  242.       ///      [5]: 67: 0
  243.       ///      [6]: 410: "Model"
  244.       ///      [7]: 8: "0"
  245.       ///      [8]: 100: "AcDbLine"
  246.       ///      [9]: 10: (0,0,0)
  247.       ///      [10]: 11: (1,0,0)
  248.       ///      [11]: 210: (0,0,1)
  249.       ///      
  250.       ///   Command:
  251.       ///  
  252.  
  253.       [LispFunction( "implode-test" )]
  254.       public static void ImplodeTest( ResultBuffer args )
  255.       {
  256.          if( args != null )
  257.             args.Implode().Dump();
  258.          Utils.WriteLine( "\n\b\n" );
  259.       }
  260.  
  261.       /// Round-tripping test
  262.       // Round-trip a list argument ( Explode( Implode(...) )
  263.       [LispFunction( "implode-explode-test" )]
  264.       public static ResultBuffer ImplodeExplodeTest( ResultBuffer args )
  265.       {
  266.          if( args != null )
  267.          {
  268.             return new ResultBuffer( args.Implode().Explode().ToArray() );
  269.          }
  270.          return null;
  271.       }
  272.  
  273.       /// Round-trip a list argument ( Explode( Implode(...) )
  274.       ///
  275.       /// With the exception of lists containing dotted lists,
  276.       /// this will return its argument (after transformation
  277.       /// to the managed LIST representation and then back to
  278.       /// the ADS/RESBUF representation).
  279.  
  280.       [LispFunction( "implode-explode-roundtrip-test" )]
  281.       public static ResultBuffer ImplodeExplodeRoundTrip( ResultBuffer args )
  282.       {
  283.          if( args != null )
  284.          {
  285.             var list = args.Implode();
  286.             list.Dump();
  287.             Utils.WriteLine( "\n\b\n" );
  288.             return new ResultBuffer( list.Explode().ToArray() );
  289.          }
  290.          return null;
  291.       }
  292.  
  293.       /// Returns the follwing back to LISP:
  294.       ///
  295.       ///    (1 nil 2 ("foo" "bar" (0.0 0.0 0.0) "baz") 3 T 4 5)
  296.       ///    
  297.       /// Note that the two bool arguments (true and false)
  298.       /// are projected to T and NIL respectively in the
  299.       /// resulting list:
  300.       ///
  301.       [LispFunction( "listbuilder-return-list" )]
  302.       public static object ListBuilderTestReturnList( ResultBuffer args )
  303.       {
  304.          return ListBuilder.List(
  305.             1,
  306.             false,                // false => NIL
  307.             2,
  308.             ListBuilder.List(     // nested sublist
  309.                "foo",
  310.                "bar",
  311.                Point3d.Origin,
  312.                "baz"
  313.             ),
  314.             3,
  315.             true,                 // true => 'T
  316.             4,
  317.             5
  318.          );
  319.       }
  320.  
  321.       [LispFunction( "listbuilder-return-atom" )]
  322.       public static object ListBuilderTestReturnAtom( ResultBuffer args )
  323.       {
  324.          // ListBuilder creates TypedValues for any
  325.          // supported type without all the gibberish:
  326.  
  327.          TypedValue tv = ListBuilder.ToTypedValue( 99 );
  328.  
  329.          // returns an atom back to LISP:
  330.          return ListBuilder.RetVal( tv );
  331.       }
  332.  
  333.       /// Construct and return an association list:
  334.       ///
  335.       /// Command: (alist-test)
  336.       /// returns -> ((1 . "one") (2 . "two") (3 . "three"))
  337.  
  338.       [LispFunction( "alist-test" )]
  339.       public static object AListTest(ResultBuffer args)
  340.       {
  341.          return ListBuilder.List(
  342.             ListBuilder.Cons( 1, "one" ),
  343.             ListBuilder.Cons( 2, "two" ),
  344.             ListBuilder.Cons( 3, "three" ) );
  345.       }
  346.  
  347.       /// <summary>
  348.       /// Same as (alist-test), except the source
  349.       /// is a Dictionary<int,string>:
  350.  
  351.       [LispFunction( "alist-test2" )]
  352.       public static object AListTest2( ResultBuffer args )
  353.       {
  354.          Dictionary<int, string> dict = new Dictionary<int, string>();
  355.          dict[1] = "one";
  356.          dict[2] = "two";
  357.          dict[3] = "three";
  358.  
  359.          // construct and return association list manually using Linq
  360.          // (see below for the more-automated, 'easy button' approach):
  361.          
  362.          return ListBuilder.List( dict.Select( p => ListBuilder.Cons( p.Key, p.Value ) ) );
  363.       }
  364.  
  365.       /// <summary>
  366.       /// Returns the same result as (alist-test2), but using
  367.       /// the ToResultBuffer() extension method:
  368.      
  369.       [LispFunction( "alist-test3" )]
  370.       public static object AListTest3( ResultBuffer args )
  371.       {
  372.          Dictionary<int, string> dict = new Dictionary<int, string>();
  373.          dict[1] = "one";
  374.          dict[2] = "two";
  375.          dict[3] = "three";
  376.  
  377.          /// Returning the above dictionary as an association list
  378.          /// of the form ((<key> . <value>)...) is as simple as this:
  379.          
  380.          return dict.ToResultBuffer();
  381.       }
  382.  
  383.       /// Construct and return a list containing a nested association list:
  384.       ///
  385.       /// Command: (nested-alist-test)
  386.       /// returns  -> ("foo" ((1 . "one") (2 . "two") (3 . "three")) "bar" "baz")
  387.  
  388.       [LispFunction( "nested-alist-test" )]
  389.       public static object NestedAListTest( ResultBuffer args )
  390.       {
  391.          return ListBuilder.List(
  392.             "foo",
  393.             ListBuilder.List(
  394.                ListBuilder.Cons( 1, "one" ),
  395.                ListBuilder.Cons( 2, "two" ),
  396.                ListBuilder.Cons( 3, "three" )
  397.             ),
  398.             "bar",
  399.             "baz"
  400.          );
  401.       }
  402.      
  403.  
  404.    }
  405. }
  406.  
  407.