Blog

Lisphp at Ning: Custom functions

In my previous post, I mentioned that we are experimenting with Lisphp at Ning, and we have some custom functions that seem to be helpful:

  • import: imports a lisp file: (import 'foo/bar/baz.php')
  • php: runs a PHP function without having to import it: (php :htmlentities 'foo')
  • cons: prepends an item to an array: (cons 'strawberry' flavors)
  • hash: creates an array of key-value pairs: (hash 'key1' 'value1' 'key2' 'value2')
  • array-set: sets an item on a multidimensional array: $flavors['foo']['bar'] = 'baz' is (array-set flavors 'foo' 'bar' 'baz')
  • array-get: gets an item from a multidimensional array: $flavors['foo']['bar'] is (array-get flavors 'foo' 'bar')
  • arr: array_replace_recursive()
  • environment: this is the Lisp environment itself added as a variable, to allow you to check if a function exists: (exists-at? environment 'my-function')

Here is the code that we use to call Lisphp. Note that you have a choice of runFile() or runCode(); also note the custom functions. You may need to make some modifications to get this to run in your own environment:

<?php

/**
 * Parses Lisphp files.
 */
class XG_Lisp {

    /**
     * Executes a Lisp file using Lisphp.
     *
     * @param string $path  the path to the lisp file
     * @param array $env  names and values to add to the environment;
     *                             see Lisphp_Environment
     * @return  the result
     * @see http://jona.ca/blog/lisphp-at-ning-introduction
     */
    public function runFile($path, $env = []) {
        return $this->run(Lisphp_Program::load(NF_APP_BASE . '/' . $path), $env);
    }

    /**
     * Executes Lisp code using Lisphp.
     *
     * @param string $code  the Lisp code
     * @param array $env  names and values to add to the environment;
     *                             see Lisphp_Environment
     * @return  the result
     */
    public function runCode($code, $env = []) {
        return $this->run(new Lisphp_Program($code), $env);
    }

    /**
     * Executes a Lisp program using Lisphp.
     *
     * @param Lisphp_Program $program  a Lisp program object
     * @param array $env  names and values to add to the environment;
     *                             see Lisphp_Environment
     * @return  the result
     */
    protected function run($program, $env = []) {
        $environment = Lisphp_Environment::full();
        foreach ($env as $name => $value) {
            $environment[$name] = $value;
        }
        $environment['B'] = B(); // XG_BaseService
        // Import a PHP file: (import 'lib/components/activity/lib/foo.lisp')
        $environment['import'] = new Lisphp_Runtime_PHPFunction(function ($path) use ($environment) {
            $program = Lisphp_Program::load(NF_APP_BASE . '/' . $path);
            return $program->execute($environment);
        });
        // Prepends an element to a list
        $environment['cons'] = new Lisphp_Runtime_PHPFunction([$this, 'cons']);
        $environment['->$'] = new XG_Lisp_GetField;
        $environment['array-get'] = new Lisphp_Runtime_PHPFunction([$this, 'arrayGet']);
        $environment['array-set'] = new Lisphp_Runtime_PHPFunction([$this, 'arraySet']);
        $environment['hash'] = new Lisphp_Runtime_PHPFunction([$this, 'hash']);
        $environment['var_dump'] = new Lisphp_Runtime_PHPFunction('var_dump');
        $environment['arr'] = new Lisphp_Runtime_PHPFunction('array_replace_recursive');
        $environment['environment'] = $environment;
        return $program->execute($environment);
    }

    /**
     * Prepends an element to a list.
     *
     * @param mixed $item  the item to prepend
     * @param array|Lisphp_List $list  the list to prepend the item to
     * @return array|Lisphp_List  a new list
     */
    public function cons($item, $list) {
        if (is_array($list)) {
            return array_merge([$item], $list);
        }
        return new Lisphp_List(array_merge([$item], $list->getArrayCopy()));
    }

    /**
     * Retrieves an item from a multidimensional array
     *
     * @param array $array  the array to read from
     * @param string $key1  the first key
     * @param string $key2  the second key, etc.
     * @return mixed  the value at the given keys
     */
    public function arrayGet() {
        $args = func_get_args();
        $result = array_shift($args);
        foreach ($args as $key) {
            if (!is_array($result)) {
                return null;
            }
            if (!array_key_exists($key, $result)) {
                return null;
            }
            $result = $result[$key];
        }
        return $result;
    }

    /**
     * Sets an item on a multidimensional array
     *
     * @param array $array  the array to read from
     * @param string $key1  the first key
     * @param string $key2  the second key, etc.
     * @param string $value  the value to set
     * @return mixed  the new multidimensional array
     */
    public function arraySet() {
        $args = func_get_args();
        $array = array_shift($args);
        $subtree = &$array;
        $value = array_pop($args);
        $lastKey = array_pop($args);
        foreach ($args as $key) {
            if (!array_key_exists($key, $subtree)) {
                $subtree[$key] = [];
            }
            $subtree = &$subtree[$key];
        }
        $subtree[$lastKey] = $value;
        return $array;
    }

    /**
     * Converts a list into an array of key-value pairs.
     *
     * @param string $key1  the first key
     * @param string $value1  the first value
     * @param string $key2  the second key
     * @param string $value2  the second value, etc.
     * @return array  the key-value pairs
     */
    public function hash() {
        $args = func_get_args();
        $hash = [];
        for ($i = 0; $i < count($args); $i += 2) {
            $hash[$args[$i]] = $args[$i+1];
        }
        return $hash;
    }

}

comments powered by Disqus

Did you know?

I'm a software engineering consultant. This means I can help your company with your software engineering needs:

  • providing temporary manpower for short-staffed software projects

  • helping new software projects get off to a good architectural start

  • improving the performance and reliability of old, legacy software systems

  • doing an important investigation or small project that you've always wanted to do but haven't had time for

Since 1999, I have done software engineering projects for the Canadian government, for Silicon Valley startups, and for established Bay Area companies, for small companies and medium-sized companies, for successful commercial projects and open-source projects. 

Currently accepting small projects. If you have one, email or call me.