docs: function listFromArray(arr) {
  
    // note the below inadvertantly connected the input forms to the
    // output generated code forms and caused a problem if anonymous
    // "reactor functions" were used.  But without it - compile times
    // went thru the roof!!
    if(isList(arr)) { return arr; }
  
    var thelist = [];
  
    function _wrap(form) {
      var wrapped;
      if(!Array.isArray(form)) {
        wrapped = atom(form);
      }
      else {
  //      wrapped = (isList(form) ? arr : listFromArray(form));
        wrapped = listFromArray(form);
      }
      if(!thelist.noparenting) {
        wrapped.parent = thelist;
      }
      return wrapped;
    }
  
    /**
    * Add the given form to the end of the list.
    * note: this also wraps the form and sets the list as the parent of the item
    */
    thelist.push = function(form) {
      Array.prototype.push.call(this, _wrap(form));
      return this;
    };
  
    /**
    * Add each element of the array to the end of the list.
    * note: this also wraps the forms and sets the list as the parent of the item
    */
    thelist.pushFromArray = function(arr) {
      arr.forEach(function(elem) {
        thelist.push(elem);
      });
      return this;
    };
  
    /**
    * Add the given form to the beginning of the list.
    * note: this also wraps the form and sets the list as the parent of the item
    */
    thelist.unshift = function(form) {
      Array.prototype.unshift.call(this, _wrap(form));
      return this;
    };
  
    /**
    * Add each element of the array to the beginning of the list.
    * note: this also wraps the forms and sets the list as the parent of the item
    */
    thelist.unshiftArray = function(arr) {
      for(var i = arr.length-1; i >= 0; i--) {
        thelist.unshift(arr[i]);
      };
      return this;
    };
  
    /**
    * Disable the auto-setting of parents as forms are added to the list.
    * This is useful for macros because even though they rearrange forms
    * we don't want the macros themselves winding up in the parent/child
    * relationships that are used for finding locally scoped dialects.
    */
    thelist.disable_parenting = function() {
      thelist.noparenting = true;
    };
  
    /**
    * Remove the given item from its parent list.
    */
    thelist.remove = function(form) {
      var parent = parentOf(form);
      if(!parent) {
        parent = thelist;
      }
      else {
        if(parent !== thelist) {
          throw Error("Attempt to remove form from a list that was not it's parent");
        }
      }
  
      var pos = parent.indexOf(form);
      if(pos !== -1) {
        form.parent = undefined;
        delete parent[pos];
      }
      else {
        throw Error("Attempt to remove form from a list which didn't contain it");
      }
  
      return this;
    };
  
    thelist.error = function(msg, locator) {
      lexerOf(thelist).error(msg, locator || thelist);
    };
  
    /**
    * Get the simplified JSON representation of the atoms in this list.
    */
    thelist.toJSON = function() {
      return toJSON(this);
    };
  
    /**
    * For "object" or "attr" lists, this returns the js object with properties and
    * values equivalent to the lispy (object..) or (attr..) object.  For all other
    * lists it returns the same as toJSON (above).
    */
    thelist.toObject = function() {
      var obj;
      var jsonsexpr = toJSON(this);
      if(Array.isArray(jsonsexpr) && jsonsexpr.length > 0 &&
        (jsonsexpr[0] === 'object' || jsonsexpr[0] === 'attr'))
      {
        obj = {};
        for (var i = 1; i < jsonsexpr.length; i = i + 2) {
          // toJSON has already made keys into strings and values into primitives
          obj[jsonsexpr[i]] = jsonsexpr[i+1];
        }
      }
      else {
        // it wasn't an object/attr s-expression just give back our list json
        obj = jsonsexpr;
      }
  
      return obj;
    };
  
    /**
    * Set parents all the way down this form tree.
    */
    thelist.setParents = function(parent) {
  
      if(parent) {
        if(this.parent && this.parent !== parent) {
          console.log('warning:  setParents is *changing* an existing parent value??!!');
        }
  
        this.parent = parent;
  
      }
  
      this.forEach(function(form) {
        if(Array.isArray(form) && form.setParents) {
          form.setParents(thelist);
        }
      });
    };
  
    /**
    * Set the opening line/col for a list from the opening token (e.g. "(")
    * or current position in the Source
    */
    thelist.setOpening = function(tokenOrLexer) {
      this.line = tokenOrLexer.line;
      this.col = tokenOrLexer.col;
  
      // whitespace or comments that preceded the opening token
      // (e.g. on a () list whitespace or comments that preceded "(")
      this.prelude = tokenOrLexer.prelude;
    }
  
    /**
    * Set the closing line/col for a list from the closing token (e.g. ")")
    */
    thelist.setClosing = function(tokenOrLexer) {
      this.endLine = tokenOrLexer.line;
      this.endCol = tokenOrLexer.col;
  
      // whitespace or comments that preceded the end token
      // (e.g. on a () list whitespace or comments that preceded ")")
      this.endPrelude = tokenOrLexer.prelude;
    }
  
    // did they give an initial arr?
    if(arr && Array.isArray(arr) && arr.length > 0) {
      // wrap the elements and assign parents, etc.
      thelist = fromJSON(arr, thelist);
    }
  
  /*
  THIS APPEARS TO BE TRYING TO HELP SET LINE/COL POSITIONS FOR SOURCE
  MAPS WHEN A LIST MIGHT HAVE SOME PROGRAM-CREATED TOKENS?  COMMENTED
  FOR NOW SINCE WE'RE MOVING FROM THERE EVEN BEING ATOM.LEXER
    var atomwithsource = finddeep(thelist, function(atom) { return atom.lexer; });
    if(atomwithsource) {
      thelist.setOpening(lexerOf(atomwithsource));
    }
  */
  
    thelist.__islist = true; // is this is a sugarlisp list or just a js array?
  
    return thelist;
  }