function parse_SEXP($buf, $offset, $attr = NULL)
{
    $r = $buf;
    $i = $offset;
    // some simple parsing - just skip attributes and assume short responses
    $ra = int8($r, $i);
    $rl = int24($r, $i + 1);
    $i += 4;
    $offset = $eoa = $i + $rl;
    // echo "[data type ".($ra & 63).", length ".$rl." with payload from ".$i." to ".$eoa."]<br/>\n";
    if (($ra & 64) == 64) {
        echo "sorry, long packets are not supported (yet).";
        return FALSE;
    }
    if ($ra > 127) {
        $ra &= 127;
        $al = int24($r, $i + 1);
        $attr = parse_SEXP($buf, $i);
        $i += $al + 4;
    }
    if ($ra == 0) {
        return NULL;
    }
    if ($ra == 16) {
        // generic vector
        $a = array();
        while ($i < $eoa) {
            $a[] = parse_SEXP($buf, &$i);
        }
        // if the 'names' attribute is set, convert the plain array into a map
        if (isset($attr['names'])) {
            $names = $attr['names'];
            $na = array();
            $n = count($a);
            for ($k = 0; $k < $n; $k++) {
                $na[$names[$k]] = $a[$k];
            }
            return $na;
        }
        return $a;
    }
    if ($ra == 19) {
        // symbol
        $oi = $i;
        while ($i < $eoa && ord($r[$i]) != 0) {
            $i++;
        }
        return substr($buf, $oi, $i - $oi);
    }
    if ($ra == 20 || $ra == 22) {
        // pairlist w/o tags
        $a = array();
        while ($i < $eoa) {
            $a[] = parse_SEXP($buf, &$i);
        }
        return $a;
    }
    if ($ra == 21 || $ra == 23) {
        // pairlist with tags
        $a = array();
        while ($i < $eoa) {
            $val = parse_SEXP($buf, &$i);
            $tag = parse_SEXP($buf, &$i);
            $a[$tag] = $val;
        }
        return $a;
    }
    if ($ra == 32) {
        // integer array
        $a = array();
        while ($i < $eoa) {
            $a[] = int32($r, $i);
            $i += 4;
        }
        if (count($a) == 1) {
            return $a[0];
        }
        return $a;
    }
    if ($ra == 33) {
        // double array
        $a = array();
        while ($i < $eoa) {
            $a[] = flt64($r, $i);
            $i += 8;
        }
        if (count($a) == 1) {
            return $a[0];
        }
        return $a;
    }
    if ($ra == 34) {
        // string array
        $a = array();
        $oi = $i;
        while ($i < $eoa) {
            if (ord($r[$i]) == 0) {
                $a[] = substr($r, $oi, $i - $oi);
                $oi = $i + 1;
            }
            $i++;
        }
        if (count($a) == 1) {
            return $a[0];
        }
        return $a;
    }
    if ($ra == 36) {
        // boolean vector
        $n = int32($r, $i);
        $i += 4;
        $k = 0;
        $a = array();
        while ($k < $n) {
            $v = int8($r, $i++);
            $a[$k++] = $v == 1 ? TRUE : ($v == 0 ? FALSE : NULL);
        }
        if ($n == 1) {
            return $a[0];
        }
        return $a;
    }
    if ($ra == 37) {
        // raw vector
        $len = int32($r, $i);
        $i += 4;
        return substr($r, $i, $len);
    }
    if ($ra == 48) {
        // unimplemented type in Rserve
        $uit = int32($r, $i);
        // echo "Note: result contains type #$uit unsupported by Rserve.<br/>";
        return NULL;
    }
    echo "Warning: type " . $ra . " is currently not implemented in the PHP client.";
    return FALSE;
}
function Rserve_eval($socket, $command, $attr = NULL)
{
    $pkt = mkp_str(3, $command);
    socket_send($socket, $pkt, strlen($pkt), 0);
    $r = get_rsp($socket);
    $res = int32($r);
    $sc = $res >> 24 & 127;
    $rr = $res & 255;
    if ($rr != 1) {
        echo "eval failed with error code " . $sc;
        return FALSE;
    }
    if (int8($r, 16) != 10) {
        echo "invalid response (expecting SEXP)";
        return FALSE;
    }
    $i = 20;
    return parse_SEXP($r, $i, &$attr);
}