Thursday, December 04, 2014

December 4, 2014

In a move that's bound to offend Lisp purists, I have added support for C-style arrays in pLisp; it is now possible to get and set array elements by syntax like a[10 2]. I initially thought of doing this the canonical way, i.e. a[10][2], but this complicates the lexical analysis (I have already introduced shift/reduce conflicts with these changes), so a single pair of square brackets will have to do.

You can thus do things like these now:

USER> (define a (array (2 2) 1))

[[1 1] [1 1]]

USER> a[0 0]

USER> (aset a[0 0] 100)

[[100 1] [1 1]]

array and aset are macros that internally call make-array and array-get/array-set respectively:

(defmacro array (dims default-value)
  (if (eq (length dims) 1)
      `(make-array ,(car dims) ,default-value)
    `(make-array ,(car dims) (array ,(cdr dims) ,default-value))))

(defmacro aset (ref val)
  (let ((a (second ref))
    (last-index (last ref))
    (indexes (butlast (cddr ref) 1)))
    `(array-set (build-ref ,a ,indexes) ,last-index ,val)))

During lexical analysis, the form a[x y] is converted into a list (aref x y) so that the sanctum sanctorum of the interpreter and other bits of pLisp are not polluted by the impurity of the square brackets.

build-ref is a macro that is used internally to convert a reference of the form (aref array-name i1 i2 ...) to one that uses array-get.

(defmacro build-ref (a indexes)
  (let ((rev-indexes (reverse indexes)))
    (if (eq (length rev-indexes) 1)
    `(array-get ,a ,(car rev-indexes))
      `(array-get (build-ref ,a ,(cdr rev-indexes)) ,(car rev-indexes)))))