<?php
require_once 'PHPUnit/Framework.php';
require_once 'Lisphp/Runtime.php';
require_once 'Lisphp/Scope.php';
require_once 'Lisphp/Environment.php';
require_once 'Lisphp/List.php';
require_once 'Lisphp/Symbol.php';
require_once 'Lisphp/Literal.php';
require_once 'Lisphp/Parser.php';
final class Lisphp_Test_SampleClass {
const PI = 3.14;
static function a() {
return 'a';
}
static function b() {
return 'b';
}
}
class Lisphp_Test_RuntimeTest extends PHPUnit_Framework_TestCase {
static function lst($code) {
return Lisphp_Parser::parseForm("[$code]", $_);
}
function testEval() {
$eval = new Lisphp_Runtime_Eval;
$form = Lisphp_Parser::parseForm(':(+ 1 2 [- 4 3])', $_);
$scope = Lisphp_Environment::sandbox();
$args = new Lisphp_List(array($form));
$this->assertEquals(4, $eval->apply($scope, $args));
$args = new Lisphp_List(array($form, Lisphp_Symbol::get('scope')));
$names = new Lisphp_Scope;
$names['scope'] = $scope;
$this->assertEquals(4, $eval->apply($names, $args));
}
function testDefine() {
$define = new Lisphp_Runtime_Define;
$scope = new Lisphp_Scope(Lisphp_Environment::sandbox());
$result = $define->apply($scope, new Lisphp_List(array(
Lisphp_Symbol::get('*pi*'),
new Lisphp_Literal(pi())
)));
$this->assertEquals(pi(), $result);
$this->assertEquals(pi(), $scope['*pi*']);
$result = $define->apply($scope, new Lisphp_List(array(
Lisphp_Symbol::get('pi2'),
Lisphp_Symbol::get('*pi*')
)));
$this->assertEquals(pi(), $result);
$this->assertEquals(pi(), $scope['pi2']);
$result = $define->apply($scope, self::lst('[add a b] {+ a b}', $_));
$this->assertSame($result, $scope['add']);
$this->assertType('Lisphp_Runtime_Function', $result);
$this->assertFunction(3, $result, 1, 2);
}
function testLet() {
$let = new Lisphp_Runtime_Let;
$scope = Lisphp_Environment::sandbox();
$scope['a'] = 1;
$scope['c'] = 1;
$retval = $let->apply(
$scope,
self::lst('[(a 2) (b 1)] (define c 2) (+ a b)')
);
$this->assertEquals(2, $scope['c']);
$this->assertEquals(3, $retval);
$this->assertEquals(1, $scope['a']);
$this->assertNull($scope['b']);
}
function testQuote() {
$quote = new Lisphp_Runtime_Quote;
$this->assertEquals(Lisphp_Symbol::get('abc'),
$quote->apply(new Lisphp_Scope, new Lisphp_List(
array(Lisphp_Symbol::get('abc'))
)));
}
function logTest($log) {
$this->logged = $log;
}
function testUserMacro() {
$scope = Lisphp_Environment::sandbox();
$scope['log'] = new Lisphp_Runtime_PHPFunction(array($this, 'logTest'));
$body = self::lst('(log "testUserMacro") (list #scope #arguments)', $_);
$macro = new Lisphp_Runtime_UserMacro($scope, $body);
$this->assertSame($scope, $macro->scope);
$this->assertEquals($body, $macro->body);
$context = new Lisphp_Scope;
$args = self::lst('a (+ a b)');
$retval = $macro->apply($context, $args);
$this->assertType('Lisphp_List', $retval);
$this->assertSame($context, $retval[0]);
$this->assertEquals($args, $retval[1]);
}
function testMacro() {
$macro = new Lisphp_Runtime_Macro;
$args = self::lst('(+ 1 2)');
$scope = new Lisphp_Scope;
$retval = $macro->apply($scope, $args);
$this->assertType('Lisphp_Runtime_UserMacro', $retval);
$this->assertSame($scope, $retval->scope);
$this->assertEquals($args, $retval->body);
}
function testLambda() {
$lambda = new Lisphp_Runtime_Lambda;
$scope = new Lisphp_Scope;
$args = self::lst('[a b] (define x 2) (+ a b)');
$func = $lambda->apply($scope, $args);
$this->assertType('Lisphp_Runtime_Function', $func);
$this->assertSame($scope, $func->scope);
$this->assertEquals($args->car(), $func->parameters);
$this->assertEquals($args->cdr(), $func->body);
}
function testDo() {
$do = new Lisphp_Runtime_Do;
$scope = new Lisphp_Scope(Lisphp_Environment::sandbox());
$scope['a'] = new Lisphp_List;
$args = self::lst('(set-at! a "first")
(set-at! a "second")
(set-at! a "third")');
$retval = $do->apply($scope, $args);
$this->assertEquals('third', $retval);
$this->assertEquals(new Lisphp_List(array('first', 'second', 'third')),
$scope['a']);
}
function testIf() {
$if = new Lisphp_Runtime_Logical_If;
$scope = new Lisphp_Scope;
$scope['define'] = new Lisphp_Runtime_Define;
$args = array(
Lisphp_Symbol::get('condition'),
new Lisphp_List(array(Lisphp_Symbol::get('define'),
Lisphp_Symbol::get('a'),
new Lisphp_Literal(1))),
new Lisphp_List(array(Lisphp_Symbol::get('define'),
Lisphp_Symbol::get('b'),
new Lisphp_Literal(2)))
);
$scope['condition'] = true;
$scope['a'] = $scope['b'] = 0;
$retval = $if->apply($scope, new Lisphp_List($args));
$this->assertEquals(1, $retval);
$this->assertEquals(1, $scope['a']);
$this->assertEquals(0, $scope['b']);
$scope['condition'] = false;
$scope['a'] = $scope['b'] = 0;
$retval = $if->apply($scope, new Lisphp_List($args));
$this->assertEquals(2, $retval);
$this->assertEquals(0, $scope['a']);
$this->assertEquals(2, $scope['b']);
}
function applyFunction(Lisphp_Applicable $function) {
$args = func_get_args();
array_shift($args);
$scope = new Lisphp_Scope;
$symbol = 0;
foreach ($args as &$value) {
if ($value instanceof ArrayObject || is_array($value)) {
$value = new Lisphp_Quote(new Lisphp_List($value));
} else if (is_object($value) || is_bool($value) || is_null($value)){
$scope["tmp-$symbol"] = $value;
$value = Lisphp_Symbol::get('tmp-' . $symbol++);
} else {
$value = new Lisphp_Literal($value);
}
}
return $function->apply($scope, new Lisphp_List($args));
}
function assertFunction($expected, Lisphp_Applicable $function) {
$args = func_get_args();
array_shift($args);
$this->assertEquals(
$expected,
call_user_func_array(array($this, 'applyFunction'), $args)
);
}
function testFunction() {
$global = new Lisphp_Scope(Lisphp_Environment::sandbox());
$global['x'] = 1;
$params = self::lst('a b');
$body = self::lst('(define x 2) (+ a b)');
$func = new Lisphp_Runtime_Function($global, $params, $body);
$this->assertSame($global, $func->scope);
$this->assertEquals($params, $func->parameters);
$this->assertEquals($body, $func->body);
$this->assertFunction(3, $func, 1, 2);
$this->assertEquals(2, $global['x']);
try {
$this->applyFunction($func, 1);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
$body = self::lst('#arguments');
$func = new Lisphp_Runtime_Function($global, new Lisphp_List, $body);
$this->assertFunction(new Lisphp_List, $func);
$this->assertFunction(new Lisphp_List(range(1, 3)), $func, 1, 2, 3);
}
function testGenericCall() {
$val = Lisphp_Runtime_Function::call(
new Lisphp_Runtime_Arithmetic_Addition,
array(1, 2)
);
$this->assertEquals(3, $val);
try {
Lisphp_Runtime_Function::call('trim', array('a'));
$this->fail();
} catch (InvalidArgumentException $e) {
# pass
}
try {
Lisphp_Runtime_Function::call(1, array());
$this->fail();
} catch (InvalidArgumentException $e) {
# pass
}
}
function testGenericCall530() {
if (version_compare(phpversion(), '5.3.0', '<')) {
$this->markTestSkipped('PHP version is less than 5.3.0.');
}
eval('$f = function($a, $b) { return $a + $b; };');
$val = Lisphp_Runtime_Function::call($f, array(1, 2));
$this->assertEquals(3, $val);
}
function testApply() {
$apply = new Lisphp_Runtime_Apply;
$add = new Lisphp_Runtime_Arithmetic_Addition;
$this->assertFunction(9, $apply, $add, new Lisphp_List(array(2, 3, 4)));
}
function testAdd() {
$add = new Lisphp_Runtime_Arithmetic_Addition;
$this->assertFunction(5, $add, 5);
$this->assertFunction(10, $add, 5, 5);
$this->assertFunction(6, $add, 1, 2, 3);
}
function testSubtract() {
$sub = new Lisphp_Runtime_Arithmetic_Subtraction;
$this->assertFunction(-5, $sub, 5);
$this->assertFunction(2, $sub, 5, 3);
$this->assertFunction(1, $sub, 5, 3, 1);
}
function testMultiply() {
$mul = new Lisphp_Runtime_Arithmetic_Multiplication;
$this->assertFunction(1, $mul);
$this->assertFunction(5, $mul, 5);
$this->assertFunction(25, $mul, 5, 5);
$this->assertFunction(50, $mul, 5, 5, 2);
}
function testDivide() {
$div = new Lisphp_Runtime_Arithmetic_Division;
$this->assertFunction(5, $div, 25, 5);
$this->assertFunction(5, $div, 50, 2, 5);
}
function testMod() {
$mod = new Lisphp_Runtime_Arithmetic_Modulus;
$this->assertFunction(0, $mod, 25, 5);
$this->assertFunction(1, $mod, 25, 4);
}
function testNot() {
$not = new Lisphp_Runtime_Logical_Not;
$this->assertFunction(false, $not, true);
$this->assertFunction(true, $not, false);
$this->assertFunction(false, $not, 1);
$this->assertFunction(false, $not, 2);
$this->assertFunction(true, $not, 0);
$this->assertFunction(false, $not, 'abc');
$this->assertFunction(true, $not, '');
}
function testAnd() {
$and = new Lisphp_Runtime_Logical_And;
$this->assertFunction(false, $and, false);
$this->assertFunction(true, $and, true);
$this->assertFunction(false, $and, false, false);
$this->assertFunction(false, $and, false, true);
$this->assertFunction(false, $and, true, false);
$this->assertFunction(true, $and, true, true);
$this->assertFunction(false, $and, false, false, false);
$this->assertFunction(false, $and, false, true, false);
$this->assertFunction(false, $and, false, false, true);
$this->assertFunction(false, $and, false, true, true);
$this->assertFunction(true, $and, true, true, true);
$this->assertFunction('', $and, 'a', '');
$this->assertFunction(null, $and, 'a', null);
$this->assertFunction('b', $and, 'a', 'b');
$this->assertFunction('', $and, 'a', 'b', '');
$this->assertFunction(null, $and, 'a', 'b', null);
$this->assertFunction('c', $and, 'a', 'b', 'c');
$env = Lisphp_Environment::sandbox();
$scope = new Lisphp_Scope($env);
$scope['a'] = 1;
$retval = $and->apply($scope, self::lst('(define a 2)
(define b 0)
(define a 3)'));
$this->assertEquals(0, $retval);
$this->assertEquals(2, $scope['a']);
}
function testOr() {
$or = new Lisphp_Runtime_Logical_Or;
$this->assertFunction(false, $or, false);
$this->assertFunction(true, $or, true);
$this->assertFunction(false, $or, false, false);
$this->assertFunction(true, $or, true, false);
$this->assertFunction(true, $or, false, true);
$this->assertFunction(true, $or, true, true);
$this->assertFunction(false, $or, false, false, false);
$this->assertFunction(true, $or, false, false, true);
$this->assertFunction(true, $or, false, true, false);
$this->assertFunction(true, $or, true, false, false);
$this->assertFunction(true, $or, true, true, false);
$this->assertFunction(true, $or, false, true, true);
$this->assertFunction(true, $or, true, false, true);
$this->assertFunction(true, $or, true, true, true);
$this->assertFunction('a', $or, 'a', '');
$this->assertFunction('', $or, null, '');
$this->assertFunction('b', $or, '', 'b');
$this->assertFunction('a', $or, 'a', 'b', 'c');
$this->assertFunction('c', $or, false, null, 'c');
$env = Lisphp_Environment::sandbox();
$scope = new Lisphp_Scope($env);
$scope['a'] = 1;
$retval = $or->apply($scope, self::lst('(define b 0)
(define a 2)
(define a 3)'));
$this->assertEquals(2, $retval);
$this->assertEquals(2, $scope['a']);
$this->assertEquals(0, $scope['b']);
}
function testEq() {
$eq = new Lisphp_Runtime_Predicate_Eq;
$this->assertFunction(false, $eq, new stdClass, new stdClass);
$o = new stdClass;
$this->assertFunction(true, $eq, $o, $o);
$this->assertFunction(true, $eq, 3, 3);
$this->assertFunction(false, $eq, 3, 3.0);
$this->assertFunction(true, $eq, 3.0, 3.0);
$this->assertFunction(true, $eq, 'foo', 'foo');
$this->assertFunction(false, $eq, 'foo', 'bar');
$this->assertFunction(true, $eq, 3.0, 3.0, 3.0);
$this->assertFunction(false, $eq, 3, 3.0, 3.0);
$this->assertFunction(true, $eq, 'foo', 'foo', 'foo');
$this->assertFunction(false, $eq, 'foo', 'foo', 'bar');
try {
$this->applyFunction($eq);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
try {
$this->applyFunction($eq, 1);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
}
function testEqual() {
$eq = new Lisphp_Runtime_Predicate_Equal;
$this->assertFunction(true, $eq, new stdClass, new stdClass);
$this->assertFunction(true, $eq, 3, 3);
$this->assertFunction(true, $eq, 3, 3.0);
$this->assertFunction(true, $eq, 3.0, 3.0);
$this->assertFunction(true, $eq, 'foo', 'foo');
$this->assertFunction(false, $eq, 'foo', 'bar');
$this->assertFunction(true, $eq, 3.0, 3.0, 3.0);
$this->assertFunction(true, $eq, 3, 3.0, 3.0);
$this->assertFunction(true, $eq, 'foo', 'foo', 'foo');
$this->assertFunction(false, $eq, 'foo', 'foo', 'bar');
try {
$this->applyFunction($eq);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
try {
$this->applyFunction($eq, 1);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
}
function testNotEq() {
$ne = new Lisphp_Runtime_Predicate_NotEq;
$this->assertFunction(true, $ne, new stdClass, new stdClass);
$o = new stdClass;
$this->assertFunction(false, $ne, $o, $o);
$this->assertFunction(false, $ne, 3, 3);
$this->assertFunction(true, $ne, 3, 3.0);
$this->assertFunction(false, $ne, 3.0, 3.0);
$this->assertFunction(false, $ne, 'foo', 'foo');
$this->assertFunction(true, $ne, 'foo', 'bar');
$this->assertFunction(false, $ne, 3.0, 3.0, 3.0);
$this->assertFunction(true, $ne, 3, 3.0, 3.0);
$this->assertFunction(false, $ne, 'foo', 'foo', 'foo');
$this->assertFunction(true, $ne, 'foo', 'foo', 'bar');
try {
$this->applyFunction($ne);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
try {
$this->applyFunction($ne, 1);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
}
function testNotEqual() {
$ne = new Lisphp_Runtime_Predicate_NotEqual;
$this->assertFunction(false, $ne, new stdClass, new stdClass);
$o = new stdClass;
$this->assertFunction(false, $ne, $o, $o);
$this->assertFunction(false, $ne, 3, 3);
$this->assertFunction(false, $ne, 3, 3.0);
$this->assertFunction(false, $ne, 3.0, 3.0);
$this->assertFunction(false, $ne, 'foo', 'foo');
$this->assertFunction(true, $ne, 'foo', 'bar');
$this->assertFunction(false, $ne, 3.0, 3.0, 3.0);
$this->assertFunction(false, $ne, 3, 3.0, 3.0);
$this->assertFunction(false, $ne, 'foo', 'foo', 'foo');
$this->assertFunction(true, $ne, 'foo', 'foo', 'bar');
try {
$this->applyFunction($ne);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
try {
$this->applyFunction($ne, 1);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
}
function testTypePredicate() {
$int = new Lisphp_Runtime_Predicate_Type('int');
$this->assertEquals('int', $int->type);
$this->assertFunction(true, $int, 123);
$this->assertFunction(false, $int, 3.14);
$this->assertFunction(false, $int, 'abc');
$string = new Lisphp_Runtime_Predicate_Type('string');
$this->assertEquals('string', $string->type);
$this->assertFunction(true, $string, 'hello');
$this->assertFunction(false, $string, 123);
$env = Lisphp_Runtime_Predicate_Type::getFunctions();
foreach (Lisphp_Runtime_Predicate_Type::$types as $type) {
$this->assertType('Lisphp_Runtime_Predicate_Type', $env["$type?"]);
$this->assertEquals($type, $env["$type?"]->type);
}
$this->assertFunction(true, $env['nil?'], null);
$this->assertFunction(false, $env['nil?'], 123);
}
function testIsA() {
$isa = new Lisphp_Runtime_Predicate_IsA;
$this->assertFunction(true, $isa,
new stdClass,
new Lisphp_Runtime_PHPClass('stdClass'));
$this->assertFunction(false, $isa,
new stdClass,
new Lisphp_Runtime_PHPClass('ArrayObject'));
$this->assertFunction(false, $isa,
1, new Lisphp_Runtime_PHPClass('stdClass'));
$this->assertFunction(true, $isa,
new stdClass,
new Lisphp_Runtime_PHPClass('ArrayObject'),
new Lisphp_Runtime_PHPClass('stdClass'));
$this->assertFunction(false, $isa,
new stdClass,
new Lisphp_Runtime_PHPClass('ArrayObject'),
new Lisphp_Runtime_PHPClass('ArrayIterator'));
try {
$this->applyFunction($isa);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
try {
$this->applyFunction($isa, 1);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
}
function testList() {
$list = new Lisphp_Runtime_List;
$this->assertFunction(new Lisphp_List, $list);
$this->assertFunction(new Lisphp_List(array(1, 2, 3, 4)),
$list, 1, 2, 3, 4);
}
function testCar() {
$car = new Lisphp_Runtime_List_Car;
$this->assertFunction(1, $car, array(1, 2, 3));
try {
$this->applyFunction($car, new Lisphp_List);
$this->fails();
} catch (UnexpectedValueException $e) {
# pass.
}
}
function testCdr() {
$cdr = new Lisphp_Runtime_List_Cdr;
$this->assertFunction(new Lisphp_List(array(2, 3)),
$cdr, array(1, 2, 3));
$this->assertFunction(null, $cdr, array());
$this->assertFunction(new Lisphp_List, $cdr, array(1));
}
function testAt() {
$at = new Lisphp_Runtime_List_At;
$this->assertFunction(1, $at, new Lisphp_List(array(1, 2, 3)), 0);
$this->assertFunction(3, $at, new Lisphp_List(array(1, 2, 3)), 2);
try {
$this->applyFunction($at, new Lisphp_List(array(1, 2, 3)), 3);
$this->fail();
} catch (OutOfRangeException $e) {
# pass.
}
}
function testSetAt() {
$setAt = new Lisphp_Runtime_List_SetAt;
$array = new ArrayObject(array('a', 'b'));
$this->assertFunction('c', $setAt, $array, 'c');
$this->assertEquals(new ArrayObject(array('a', 'b', 'c')), $array);
$this->assertFunction('C', $setAt, $array, 2, 'C');
$this->assertEquals(new ArrayObject(array('a', 'b', 'C')), $array);
$this->assertFunction('d', $setAt, $array, 3, 'd');
$this->assertEquals(new ArrayObject(array('a', 'b', 'C', 'd')), $array);
}
function testUnsetAt() {
$unsetAt = new Lisphp_Runtime_List_UnsetAt;
$array = new ArrayObject(array('a', 'b'));
$this->assertFunction('b', $unsetAt, $array, 1);
$this->assertEquals(new ArrayObject(array('a')), $array);
$array = new ArrayObject(array('a', 'b'));
$this->assertFunction('a', $unsetAt, $array, 0);
$this->assertEquals(new ArrayObject(array(1 => 'b')), $array);
try {
$this->applyFunction($unsetAt, array('a', 'b'), 3);
$this->fail();
} catch (OutOfRangeException $e) {
# pass.
}
}
function testExistsAt() {
$existsAt = new Lisphp_Runtime_List_ExistsAt;
$this->assertFunction(true, $existsAt, array(1, 2, 3), 0);
$this->assertFunction(false, $existsAt, array(1, 2, 3), 5);
}
function testCount() {
$count = new Lisphp_Runtime_List_Count;
$this->assertFunction(0, $count, array());
$this->assertFunction(3, $count, array(1, 2, 3));
$this->assertFunction(0, $count, new Lisphp_List);
$this->assertFunction(3, $count, new Lisphp_List(array(1, 2, 3)));
$this->assertFunction(0, $count, '');
$this->assertFunction(3, $count, 'abc');
}
function testArray() {
$array = new Lisphp_Runtime_Array;
$this->assertFunction(array(), $array);
$this->assertFunction(array(1, 2, 3), $array, 1, 2, 3);
}
function testDict() {
$dict = new Lisphp_Runtime_Dict;
$scope = new Lisphp_Scope;
$scope['a'] = 'key';
$retval = $dict->apply($scope, self::lst('(a 1) ("key2" 2) (3) 4'));
$this->assertEquals(array('key' => 1, 'key2' => 2, 3, 4), $retval);
}
function testMap() {
$map = new Lisphp_Runtime_List_Map;
$func = new Lisphp_Runtime_Function(
Lisphp_Environment::sandbox(),
self::lst('a'),
self::lst('(+ a 1)')
);
$this->assertFunction(new Lisphp_List, $map, $func, array());
$this->assertFunction(new Lisphp_List(array(2, 3)),
$map, $func, array(1, 2));
$func = new Lisphp_Runtime_Function(new Lisphp_Scope,
self::lst(''),
self::lst('#arguments'));
$this->assertFunction(
new Lisphp_List(array(new Lisphp_List(array(1, 4)),
new Lisphp_List(array(2, 5)),
new Lisphp_List(array(3, 6)))),
$map, $func, array(1, 2, 3), array(4, 5, 6)
);
try {
$this->applyFunction($map);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
try {
$this->applyFunction($map, $func);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
try {
$this->applyFunction($map, 1, array(1));
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
}
function testFilter() {
$filter = new Lisphp_Runtime_List_Filter;
$this->assertFunction(new Lisphp_List(array(1, 3, 5)),
$filter,
new Lisphp_Runtime_Predicate_Type('int'),
array(1, '2', 3, array(4), 5));
}
function testFoldl() {
$fold = new Lisphp_Runtime_List_Fold;
$agg = new Lisphp_Runtime_Arithmetic_Subtraction;
$this->assertFunction(3, $fold, $agg, array(25, 9, 5, 7, 1));
$this->assertFunction(3, $fold, $agg, array(9, 5, 7, 1), 25);
$this->assertFunction(25, $fold, $agg, array(), 25);
try {
$this->applyFunction($fold, $agg, array());
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
}
function testConcat() {
$concat = new Lisphp_Runtime_String_Concat;
$this->assertFunction('ab', $concat, 'a', 'b');
$this->assertFunction('hello world!', $concat, 'hello', ' world', '!');
try {
$this->applyFunction($concat);
$this->fail();
} catch (InvalidArgumentException $e) {
# pass.
}
}
function testStringJoin() {
$join = new Lisphp_Runtime_String_StringJoin;
$this->assertFunction('one two three', $join,
array('one', 'two', 'three'), ' ');
}
function methodTest($a) {
return array($this, $a);
}
function testPHPFunction() {
$substr = new Lisphp_Runtime_PHPFunction('substr');
$this->assertFunction('world', $substr, 'hello world', 6);
$method = new Lisphp_Runtime_PHPFunction(array($this, 'methodTest'));
$this->assertFunction(array($this, 123), $method, 123);
try {
new Lisphp_Runtime_PHPFunction('undefined_function_name');
$this->fail();
} catch (UnexpectedValueException $e) {
# pass
}
}
function testPHPClass() {
$class = new Lisphp_Runtime_PHPClass('ArrayObject');
$obj = $this->applyFunction($class, array(1, 2, 3));
$this->assertType('ArrayObject', $obj);
$this->assertEquals(array(1, 2, 3), $obj->getArrayCopy());
try {
new Lisphp_Runtime_PHPClass('UndefinedClassName');
$this->fail();
} catch (UnexpectedValueException $e) {
# pass
}
$class = new Lisphp_Runtime_PHPClass('Lisphp_Test_SampleClass');
$methods = $class->getStaticMethods();
$this->assertEquals(2, count($methods));
$this->assertType('Lisphp_Runtime_PHPFunction', $methods['a']);
$this->assertEquals(array('Lisphp_Test_SampleClass', 'a'),
$methods['a']->callback);
$this->assertType('Lisphp_Runtime_PHPFunction', $methods['b']);
$this->assertEquals(array('Lisphp_Test_SampleClass', 'b'),
$methods['b']->callback);
$this->assertTrue($class->isClassOf(new Lisphp_Test_SampleClass));
$this->assertFalse($class->isClassOf(new stdClass));
$this->assertFalse($class->isClassOf(213));
}
function testGetAttribute() {
$attr = new Lisphp_Runtime_Object_GetAttribute;
$object = (object) array('abc' => 'value');
$object->ptr = $object;
$object->lst = new Lisphp_List;
$scope = new Lisphp_Scope;
$scope->let('object', $object);
$val = $attr->apply($scope,
self::lst('object abc'));
$this->assertEquals($object->abc, $val);
$val = $attr->apply($scope, self::lst('object ptr ptr abc'));
$this->assertEquals($object->ptr->ptr->abc, $val);
$val = $attr->apply($scope, self::lst('object lst car'));
$this->assertType('Lisphp_Runtime_PHPFunction', $val);
$this->assertSame($object->lst, $val->callback[0]);
$this->assertEquals('car', $val->callback[1]);
try {
$attr->apply($scope, self::lst('object lst a'));
$this->fail();
} catch (RuntimeException $e) {
# pass
}
}
function testUse() {
$use = new Lisphp_Runtime_Use;
$env = Lisphp_Environment::sandbox();
$scope = new Lisphp_Scope($env);
$values = $use->apply($scope, self::lst('array_merge
array-slice
[implode array->string]
<ArrayObject>
<Lisphp_Symbol>
Lisphp/<Program>
[<Lisphp-Scope> scope]
+PHP_VERSION+
PHP/+OS+'));
$this->assertType('Lisphp_Runtime_PHPFunction', $values[0]);
$this->assertEquals('array_merge', $values[0]->callback);
$this->assertSame($values[0], $scope['array_merge']);
$this->assertNull($env['array_merge']);
$this->assertType('Lisphp_Runtime_PHPFunction', $values[1]);
$this->assertEquals('array_slice', $values[1]->callback);
$this->assertSame($values[1], $scope['array-slice']);
$this->assertNull($env['array-slice']);
$this->assertType('Lisphp_Runtime_PHPFunction', $values[2]);
$this->assertEquals('implode', $values[2]->callback);
$this->assertSame($values[2], $scope['array->string']);
$this->assertNull($env['implode']);
$this->assertType('Lisphp_Runtime_PHPClass', $values[3]);
$this->assertEquals('ArrayObject', $values[3]->class->getName());
$this->assertSame($values[3], $scope['<ArrayObject>']);
$this->assertNull($env['<ArrayObject>']);
$this->assertType('Lisphp_Runtime_PHPClass', $values[4]);
$this->assertEquals('Lisphp_Symbol', $values[4]->class->getName());
$this->assertSame($values[4], $scope['<Lisphp_Symbol>']);
$this->assertNull($env['<Lisphp_Symbol>']);
$this->assertType('Lisphp_Runtime_PHPClass', $values[5]);
$this->assertEquals('Lisphp_Program', $values[5]->class->getName());
$this->assertSame($values[5], $scope['Lisphp/<Program>']);
$this->assertType('Lisphp_Runtime_PHPFunction',
$scope['Lisphp/<Program>/load']);
$this->assertEquals(array('Lisphp_Program', 'load'),
$scope['Lisphp/<Program>/load']->callback);
$this->assertNull($env['Lisphp/<Program>']);
$this->assertType('Lisphp_Runtime_PHPClass', $values[6]);
$this->assertEquals('Lisphp_Scope', $values[6]->class->getName());
$this->assertSame($values[6], $scope['scope']);
$this->assertNull($env['scope']);
$this->assertEquals(PHP_VERSION, $values[7]);
$this->assertEquals(PHP_VERSION, $scope['+PHP_VERSION+']);
$this->assertNull($env['+PHP_VERSION+']);
$this->assertEquals(PHP_OS, $values[8]);
$this->assertEquals(PHP_OS, $scope['PHP/+OS+']);
$this->assertNull($env['PHP/+OS+']);
try {
$use->apply($scope, self::lst('undefined-function-name'));
$this->fail();
} catch (InvalidArgumentException $e) {
# pass
}
try {
$use->apply($scope, self::lst('<UndefinedClassName>'));
$this->fail();
} catch (InvalidArgumentException $e) {
# pass
}
}
function testFrom() {
$from = new Lisphp_Runtime_From;
$env = Lisphp_Environment::sandbox();
$scope = new Lisphp_Scope($env);
$values = $from->apply($scope,
self::lst('Lisphp (<Symbol> <Program>)'));
$this->assertEquals('Lisphp_Symbol', $values[0]->class->getName());
$this->assertEquals('Lisphp_Symbol',
$scope['<Symbol>']->class->getName());
$this->assertEquals('Lisphp_Program', $values[1]->class->getName());
$this->assertEquals('Lisphp_Program',
$scope['<Program>']->class->getName());
}
}