diff --git a/composer.json b/composer.json index bcf1c67..7e96954 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ ], "require": { "php": "^8.2 || ^8.3 || ^8.4 || ^8.5", - "phunkie/phunkie": "^1.3", + "phunkie/phunkie": "^1.4", "phunkie/effect": "^1.1", "nikic/php-parser": "^5.6" }, diff --git a/composer.lock b/composer.lock index 9dbf55f..586e311 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0a5eec3b6893b5edf3ed95c5a1abd02e", + "content-hash": "75caf47d62bbeccb5eca18b2757ab361", "packages": [ { "name": "nikic/php-parser", @@ -148,16 +148,16 @@ }, { "name": "phunkie/phunkie", - "version": "1.3.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/phunkie/phunkie.git", - "reference": "43a800e55e8c91a0f104cdff261039453db605f3" + "reference": "9b7795477c794e6c23fabcab4a1dcf0dae6ccac2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phunkie/phunkie/zipball/43a800e55e8c91a0f104cdff261039453db605f3", - "reference": "43a800e55e8c91a0f104cdff261039453db605f3", + "url": "https://api.github.com/repos/phunkie/phunkie/zipball/9b7795477c794e6c23fabcab4a1dcf0dae6ccac2", + "reference": "9b7795477c794e6c23fabcab4a1dcf0dae6ccac2", "shasum": "" }, "require": { @@ -195,9 +195,9 @@ "description": "Functional structures library for PHP", "support": { "issues": "https://github.com/phunkie/phunkie/issues", - "source": "https://github.com/phunkie/phunkie/tree/1.3.0" + "source": "https://github.com/phunkie/phunkie/tree/1.4.0" }, - "time": "2026-08-02T15:07:02+00:00" + "time": "2026-08-03T15:42:52+00:00" } ], "packages-dev": [ diff --git a/features/repl/basic_php_expressions.feature b/features/repl/basic_php_expressions.feature index 8e979d4..0e754bc 100644 --- a/features/repl/basic_php_expressions.feature +++ b/features/repl/basic_php_expressions.feature @@ -16,12 +16,12 @@ Feature: Evaluating Basic PHP Expressions Scenario: Boolean AND operation Given I start the REPL When I enter "true && false" - Then I should see output containing "$var0: Bool = false" + Then I should see output containing "$var0: Boolean = false" Scenario: Boolean NOT operation Given I start the REPL When I enter "!true" - Then I should see output containing "$var0: Bool = false" + Then I should see output containing "$var0: Boolean = false" Scenario: Simple variable assignment Given I start the REPL @@ -68,17 +68,17 @@ Feature: Evaluating Basic PHP Expressions Scenario: Boolean OR operation Given I start the REPL When I enter "true || false" - Then I should see output containing "$var0: Bool = true" + Then I should see output containing "$var0: Boolean = true" Scenario: Comparison greater than Given I start the REPL When I enter "5 > 3" - Then I should see output containing "$var0: Bool = true" + Then I should see output containing "$var0: Boolean = true" Scenario: Comparison equal Given I start the REPL When I enter "5 == 5" - Then I should see output containing "$var0: Bool = true" + Then I should see output containing "$var0: Boolean = true" Scenario: Unary minus Given I start the REPL diff --git a/features/repl/casts.feature b/features/repl/casts.feature index 04325c5..d6c3e46 100644 --- a/features/repl/casts.feature +++ b/features/repl/casts.feature @@ -26,12 +26,12 @@ Feature: Type casts Scenario: Casting zero to bool Given I start the REPL When I enter "(bool) 0" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" Scenario: Casting a non-empty string to bool Given I start the REPL When I enter "(bool) \"phunkie\"" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: Casting a scalar to array Given I start the REPL diff --git a/features/repl/clone_expressions.feature b/features/repl/clone_expressions.feature index 17c121f..e9c9f2c 100644 --- a/features/repl/clone_expressions.feature +++ b/features/repl/clone_expressions.feature @@ -22,7 +22,7 @@ Feature: Clone expressions Then I should see "$var0: Int = 42" When I type "$original === $copy" And I press enter - Then I should see "$var1: Bool = false" + Then I should see "$var1: Boolean = false" Scenario: Clone with __clone magic method Given I am running the repl diff --git a/features/repl/error_suppression.feature b/features/repl/error_suppression.feature index 0ba37d0..624cb87 100644 --- a/features/repl/error_suppression.feature +++ b/features/repl/error_suppression.feature @@ -12,7 +12,7 @@ Feature: Error Suppression Operator (@) Scenario: Suppress warning from file operation Given I start the REPL When I enter "@file_get_contents('/nonexistent/file.txt')" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" Scenario: Error suppression in variable assignment Given I start the REPL diff --git a/features/repl/first_class_callable.feature b/features/repl/first_class_callable.feature index 013910e..1ddbd07 100644 --- a/features/repl/first_class_callable.feature +++ b/features/repl/first_class_callable.feature @@ -82,4 +82,4 @@ Feature: First-Class Callable Syntax When I enter "$f1 = strlen(...)" And I enter "$f2 = strlen(...)" And I enter "$f1 === $f2" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" diff --git a/features/repl/import_command.feature b/features/repl/import_command.feature index 91806f5..54ed5f7 100644 --- a/features/repl/import_command.feature +++ b/features/repl/import_command.feature @@ -69,14 +69,14 @@ Feature: Import Command When I enter ":import option/isDefined" Then I should see output containing "imported function \Phunkie\Functions\option\isDefined()" When I enter "isDefined(Some(42))" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: Import from option module - isNone Given I start the REPL When I enter ":import option/isNone" Then I should see output containing "imported function \Phunkie\Functions\option\isNone()" When I enter "isNone(None())" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: Error when importing non-existent module Given I start the REPL diff --git a/features/repl/instanceof_checks.feature b/features/repl/instanceof_checks.feature index 040b7cd..e6d97c3 100644 --- a/features/repl/instanceof_checks.feature +++ b/features/repl/instanceof_checks.feature @@ -8,7 +8,7 @@ Feature: Instanceof Operator Support When I enter "class Dog {}" And I enter "$dog = new Dog()" And I enter "$dog instanceof Dog" - Then I should see output containing "$var0: Bool = true" + Then I should see output containing "$var0: Boolean = true" Scenario: instanceof with false result for different class Given I start the REPL @@ -16,7 +16,7 @@ Feature: Instanceof Operator Support And I enter "class Cat {}" And I enter "$dog = new Dog()" And I enter "$dog instanceof Cat" - Then I should see output containing "$var0: Bool = false" + Then I should see output containing "$var0: Boolean = false" Scenario: instanceof with interface Given I start the REPL @@ -24,7 +24,7 @@ Feature: Instanceof Operator Support And I enter "class Bird implements Flyable {}" And I enter "$bird = new Bird()" And I enter "$bird instanceof Flyable" - Then I should see output containing "$var0: Bool = true" + Then I should see output containing "$var0: Boolean = true" Scenario: instanceof with parent class Given I start the REPL @@ -32,7 +32,7 @@ Feature: Instanceof Operator Support And I enter "class Dog extends Animal {}" And I enter "$dog = new Dog()" And I enter "$dog instanceof Animal" - Then I should see output containing "$var0: Bool = true" + Then I should see output containing "$var0: Boolean = true" Scenario: instanceof with variable class name Given I start the REPL @@ -40,26 +40,26 @@ Feature: Instanceof Operator Support And I enter "$dog = new Dog()" And I enter "$className = 'Dog'" And I enter "$dog instanceof $className" - Then I should see output containing "$var0: Bool = true" + Then I should see output containing "$var0: Boolean = true" Scenario: instanceof with null value returns false Given I start the REPL When I enter "class Dog {}" And I enter "$dog = null" And I enter "$dog instanceof Dog" - Then I should see output containing "$var0: Bool = false" + Then I should see output containing "$var0: Boolean = false" Scenario: instanceof with standard PHP class Given I start the REPL When I enter "$date = new DateTime()" And I enter "$date instanceof DateTime" - Then I should see output containing "$var0: Bool = true" + Then I should see output containing "$var0: Boolean = true" Scenario: instanceof with Phunkie Some monad Given I start the REPL When I enter "$x = Some(42)" And I enter "$x instanceof Phunkie\Types\Some" - Then I should see output containing "$var0: Bool = true" + Then I should see output containing "$var0: Boolean = true" Scenario: instanceof checks are chainable in expressions Given I start the REPL @@ -67,18 +67,18 @@ Feature: Instanceof Operator Support And I enter "class Cat {}" And I enter "$dog = new Dog()" And I enter "$dog instanceof Dog && !($dog instanceof Cat)" - Then I should see output containing "$var0: Bool = true" + Then I should see output containing "$var0: Boolean = true" Scenario: instanceof with scalar value returns false Given I start the REPL When I enter "class Dog {}" And I enter "$value = 42" And I enter "$value instanceof Dog" - Then I should see output containing "$var0: Bool = false" + Then I should see output containing "$var0: Boolean = false" Scenario: instanceof with anonymous class Given I start the REPL When I enter "interface Runnable { public function run(); }" And I enter "$obj = new class implements Runnable { public function run() { return 'running'; } }" And I enter "$obj instanceof Runnable" - Then I should see output containing "$var0: Bool = true" + Then I should see output containing "$var0: Boolean = true" diff --git a/features/repl/php8.2/false_type.feature b/features/repl/php8.2/false_type.feature index 3a023b2..457b12c 100644 --- a/features/repl/php8.2/false_type.feature +++ b/features/repl/php8.2/false_type.feature @@ -22,7 +22,7 @@ Feature: False Type (PHP 8.2) } """ And I enter "alwaysFalse()" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" # Note: Literal true/false/null types in parameters have a known limitation in REPL # due to how arguments are evaluated before function calls. @@ -41,13 +41,13 @@ Feature: False Type (PHP 8.2) """ And I enter "$checker = new Checker()" And I enter "$checker->check()" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" Scenario: Arrow function with false return type Given I start the REPL When I enter "$failure = fn(): false => false" And I enter "$failure()" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" Scenario: False type in union with other types Given I start the REPL @@ -58,4 +58,4 @@ Feature: False Type (PHP 8.2) } """ And I enter "tryOperation()" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" diff --git a/features/repl/php8.2/true_type.feature b/features/repl/php8.2/true_type.feature index 6100fbf..d944b9a 100644 --- a/features/repl/php8.2/true_type.feature +++ b/features/repl/php8.2/true_type.feature @@ -22,7 +22,7 @@ Feature: True Type (PHP 8.2) } """ And I enter "alwaysTrue()" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" # Note: Literal true/false/null types in parameters have a known limitation in REPL # due to how arguments are evaluated before function calls. @@ -41,13 +41,13 @@ Feature: True Type (PHP 8.2) """ And I enter "$validator = new Validator()" And I enter "$validator->validate()" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: Arrow function with true return type Given I start the REPL When I enter "$success = fn(): true => true" And I enter "$success()" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: True type in union with other types Given I start the REPL @@ -58,4 +58,4 @@ Feature: True Type (PHP 8.2) } """ And I enter "checkStatus()" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" diff --git a/features/repl/php8.3/builtin_functions.feature b/features/repl/php8.3/builtin_functions.feature index 6f92fa2..3598fcd 100644 --- a/features/repl/php8.3/builtin_functions.feature +++ b/features/repl/php8.3/builtin_functions.feature @@ -6,22 +6,22 @@ Feature: PHP 8.3 Built-in Functions Scenario: json_validate() with valid JSON Given I start the REPL When I enter "json_validate('{\"name\": \"test\", \"value\": 123}')" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: json_validate() with invalid JSON Given I start the REPL When I enter "json_validate('{invalid json}')" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" Scenario: json_validate() with empty string Given I start the REPL When I enter "json_validate('')" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" Scenario: json_validate() with valid array JSON Given I start the REPL When I enter "json_validate('[1, 2, 3, 4]')" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: mb_str_pad() basic padding Given I start the REPL @@ -49,11 +49,11 @@ Feature: PHP 8.3 Built-in Functions When I enter "$r = new \Random\Randomizer()" And I enter "$f = $r->getFloat(0, 1)" And I enter "$f >= 0 && $f < 1" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: Randomizer::nextFloat() Given I start the REPL When I enter "$r = new \Random\Randomizer()" And I enter "$f = $r->nextFloat()" And I enter "$f >= 0 && $f < 1" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" diff --git a/features/repl/php8.3/readonly_amendments.feature b/features/repl/php8.3/readonly_amendments.feature index b1a87ae..e1c72f0 100644 --- a/features/repl/php8.3/readonly_amendments.feature +++ b/features/repl/php8.3/readonly_amendments.feature @@ -107,7 +107,7 @@ Feature: Readonly Amendments (PHP 8.3) When I enter "$c2->timeout" Then I should see output containing "Int = 60" When I enter "$c2->debug" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" Scenario: Clone with conditional readonly modification Given I start the REPL diff --git a/features/repl/php8.3/typed_class_constants.feature b/features/repl/php8.3/typed_class_constants.feature index d000c6c..f525d84 100644 --- a/features/repl/php8.3/typed_class_constants.feature +++ b/features/repl/php8.3/typed_class_constants.feature @@ -100,7 +100,7 @@ Feature: Typed Class Constants (PHP 8.3) When I enter "Database::PORT" Then I should see output containing "Int = 3306" When I enter "Database::SSL" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: Private typed constant Given I start the REPL diff --git a/features/repl/php8.4/array_functions.feature b/features/repl/php8.4/array_functions.feature index 820caed..acc9d7b 100644 --- a/features/repl/php8.4/array_functions.feature +++ b/features/repl/php8.4/array_functions.feature @@ -51,35 +51,35 @@ Feature: New Array Functions (PHP 8.4) Given I start the REPL When I enter "$numbers = [1, 2, 3, 4]" And I enter "array_any($numbers, fn($n) => $n > 3)" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: array_any() with no matches Given I start the REPL When I enter "$numbers = [1, 2, 3]" And I enter "array_any($numbers, fn($n) => $n > 10)" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" Scenario: array_any() with empty array Given I start the REPL When I enter "array_any([], fn($n) => true)" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" Scenario: array_all() with all matching Given I start the REPL When I enter "$numbers = [2, 4, 6, 8]" And I enter "array_all($numbers, fn($n) => $n % 2 === 0)" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: array_all() with partial match Given I start the REPL When I enter "$numbers = [2, 3, 4]" And I enter "array_all($numbers, fn($n) => $n % 2 === 0)" - Then I should see output containing "Bool = false" + Then I should see output containing "Boolean = false" Scenario: array_all() with empty array Given I start the REPL When I enter "array_all([], fn($n) => false)" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: array_all() with associative array Given I start the REPL @@ -92,13 +92,13 @@ Feature: New Array Functions (PHP 8.4) ]; """ And I enter "array_all($products, fn($p) => $p['stock'] > 5)" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: Chaining array functions Given I start the REPL When I enter "$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" And I enter "array_any($numbers, fn($n) => $n > 5 && $n % 2 === 0)" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: array_find() with object array Given I start the REPL diff --git a/features/repl/php8.4/asymmetric_visibility.feature b/features/repl/php8.4/asymmetric_visibility.feature index 6caa793..79770f7 100644 --- a/features/repl/php8.4/asymmetric_visibility.feature +++ b/features/repl/php8.4/asymmetric_visibility.feature @@ -199,4 +199,4 @@ Feature: Asymmetric Visibility (PHP 8.4) """ And I enter "$doc = new Document()" And I enter "$doc->createdAt > 0" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" diff --git a/features/repl/php8.4/lazy_objects.feature b/features/repl/php8.4/lazy_objects.feature index d231629..d509409 100644 --- a/features/repl/php8.4/lazy_objects.feature +++ b/features/repl/php8.4/lazy_objects.feature @@ -99,7 +99,7 @@ Feature: Lazy Objects (PHP 8.4) }); """ And I enter "(new ReflectionClass($lazy))->isUninitializedLazyObject($lazy)" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: Lazy object becomes initialized after access Given I start the REPL @@ -120,7 +120,7 @@ Feature: Lazy Objects (PHP 8.4) And I enter "$lazy->name" And I enter "$after = (new ReflectionClass($lazy))->isUninitializedLazyObject($lazy)" And I enter "$before && !$after" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: Initialize lazy object explicitly Given I start the REPL @@ -171,7 +171,7 @@ Feature: Lazy Objects (PHP 8.4) And I enter "$lazy->increment()" And I enter "(new ReflectionClass($lazy))->resetAsLazyGhost($lazy, function ($object) { $object->__construct(); })" And I enter "(new ReflectionClass($lazy))->isUninitializedLazyObject($lazy)" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: Lazy object with dependencies Given I start the REPL diff --git a/features/repl/php8.5/error_handler_introspection.feature b/features/repl/php8.5/error_handler_introspection.feature index 214c0c4..8a1aeba 100644 --- a/features/repl/php8.5/error_handler_introspection.feature +++ b/features/repl/php8.5/error_handler_introspection.feature @@ -12,7 +12,7 @@ Feature: Error Handler Introspection (PHP 8.5) Given I start the REPL When I enter "set_exception_handler(function ($e) { return null; })" And I enter "is_callable(get_exception_handler())" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: get_exception_handler() is null once the handler is cleared Given I start the REPL @@ -24,4 +24,4 @@ Feature: Error Handler Introspection (PHP 8.5) Scenario: PHP_BUILD_DATE is available Given I start the REPL When I enter "is_string(PHP_BUILD_DATE)" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" diff --git a/features/repl/php8.5/pipe_operator.feature b/features/repl/php8.5/pipe_operator.feature index 4b95e0f..65184b5 100644 --- a/features/repl/php8.5/pipe_operator.feature +++ b/features/repl/php8.5/pipe_operator.feature @@ -43,7 +43,7 @@ Feature: Pipe Operator (PHP 8.5) Scenario: Pipe binds tighter than comparison Given I start the REPL When I enter "\"beep\" |> strlen(...) == 4" - Then I should see output containing "Bool = true" + Then I should see output containing "Boolean = true" Scenario: Piping into a value that is not callable Given I start the REPL diff --git a/features/repl/phunkie_structures.feature b/features/repl/phunkie_structures.feature index 5c38248..1f0fc93 100644 --- a/features/repl/phunkie_structures.feature +++ b/features/repl/phunkie_structures.feature @@ -42,4 +42,4 @@ Feature: Evaluating Phunkie Data Structures Given I start the REPL When I enter "None" And I enter "$var0->isEmpty()" - Then I should see output containing "$var1: Bool = true" + Then I should see output containing "$var1: Boolean = true" diff --git a/features/repl/scalar_evaluation.feature b/features/repl/scalar_evaluation.feature index c859d46..4af2b3c 100644 --- a/features/repl/scalar_evaluation.feature +++ b/features/repl/scalar_evaluation.feature @@ -16,9 +16,9 @@ Feature: Evaluating Scalar Values Scenario: Evaluating booleans Given I start the REPL When I enter "true" - Then I should see output containing "$var0: Bool = true" + Then I should see output containing "$var0: Boolean = true" When I enter "false" - Then I should see output containing "$var1: Bool = false" + Then I should see output containing "$var1: Boolean = false" Scenario: Evaluating null Given I start the REPL diff --git a/src/Functions/evaluation.php b/src/Functions/evaluation.php index 8432908..5b7f886 100644 --- a/src/Functions/evaluation.php +++ b/src/Functions/evaluation.php @@ -23,6 +23,7 @@ use function Success; use function Failure; +use function Phunkie\Functions\type\normaliseType; /** * Pure function to evaluate a PHP expression. @@ -297,25 +298,25 @@ function evaluateNode(Node $node, ReplSession $session): Validation { return match (true) { $node instanceof Scalar\Int_ - => Success(EvaluationResult::of($node->value, 'Int')), + => Success(EvaluationResult::of($node->value, getType($node->value))), $node instanceof Scalar\Float_ - => Success(EvaluationResult::of($node->value, 'Float')), + => Success(EvaluationResult::of($node->value, getType($node->value))), $node instanceof Scalar\String_ - => Success(EvaluationResult::of($node->value, 'String')), + => Success(EvaluationResult::of($node->value, getType($node->value))), $node instanceof Scalar\InterpolatedString => evaluateInterpolatedString($node, $session), $node instanceof Expr\ConstFetch && $node->name->toString() === 'true' - => Success(EvaluationResult::of(true, 'Bool')), + => Success(EvaluationResult::of(true, getType(true))), $node instanceof Expr\ConstFetch && $node->name->toString() === 'false' - => Success(EvaluationResult::of(false, 'Bool')), + => Success(EvaluationResult::of(false, getType(false))), $node instanceof Expr\ConstFetch && $node->name->toString() === 'null' - => Success(EvaluationResult::of(null, 'Null')), + => Success(EvaluationResult::of(null, getType(null))), $node instanceof Expr\ConstFetch && $node->name->toString() === 'None' => Success(EvaluationResult::of(\None(), getType(\None()))), @@ -1330,22 +1331,22 @@ function evaluateFunctionCall(Expr\FuncCall $node, ReplSession $session): Valida /** * Gets the type name of a value. * + * Only the names the REPL invents for itself live here. Every native type is named + * by Phunkie's normaliseType(), so the console cannot drift away from the library. + * get_debug_type() is what we hand it: it spells native types the way normaliseType() + * expects, where gettype() would say "double" and "NULL". + * * @param mixed $value * @return string */ function getType(mixed $value): string { return match (true) { - is_null($value) => 'Null', - is_bool($value) => 'Bool', - is_int($value) => 'Int', - is_float($value) => 'Float', - is_string($value) => 'String', - is_array($value) => 'Array', $value instanceof \Closure => 'Callable', $value instanceof \Generator => 'Generator', is_object($value) => getObjectType($value), - default => 'Unknown' + is_resource($value) => normaliseType('resource'), + default => normaliseType(get_debug_type($value)), }; } @@ -2242,7 +2243,7 @@ function evaluateInstanceof(Expr\Instanceof_ $node, ReplSession $session): Valid // Perform the instanceof check $result = $object instanceof $className; - return Success(EvaluationResult::of($result, 'Bool')); + return Success(EvaluationResult::of($result, getType($result))); } catch (\Throwable $e) { return Failure(new EvaluationError( 'Instanceof', diff --git a/tests/Unit/Functions/EvaluationTest.php b/tests/Unit/Functions/EvaluationTest.php index ed11619..e6a5231 100644 --- a/tests/Unit/Functions/EvaluationTest.php +++ b/tests/Unit/Functions/EvaluationTest.php @@ -11,10 +11,12 @@ namespace Tests\Phunkie\Console\Functions; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Phunkie\Console\Types\ReplSession; -use function Phunkie\Console\Functions\{evaluateExpression, cleanErrorMessage}; +use function Phunkie\Console\Functions\{evaluateExpression, cleanErrorMessage, getType}; +use function Phunkie\Functions\type\normaliseType; class EvaluationTest extends TestCase { @@ -121,6 +123,63 @@ public function testEvaluateExpressionHandlesExpressionWithVariables(): void $this->assertEquals(15, $evalResult->value); } + /** + * @return list + */ + public static function scalarValues(): array + { + return [ + [null, 'null'], + [true, 'bool'], + [false, 'bool'], + [42, 'int'], + [3.14, 'float'], + ['hello', 'string'], + [[1, 2, 3], 'array'], + ]; + } + + #[DataProvider('scalarValues')] + public function testGetTypeTakesItsScalarNamesFromPhunkie(mixed $value, string $nativeType): void + { + $this->assertSame(normaliseType($nativeType), getType($value)); + } + + public function testGetTypeNamesBooleansBoolean(): void + { + $this->assertSame('Boolean', getType(true)); + $this->assertSame('Boolean', getType(false)); + } + + public function testGetTypeNamesClosuresCallable(): void + { + $this->assertSame('Callable', getType(fn(int $x): int => $x)); + } + + public function testGetTypeNamesGenerators(): void + { + $generator = (function () { + yield 1; + })(); + + $this->assertSame('Generator', getType($generator)); + } + + public function testGetTypeDelegatesObjectsToTheirShowType(): void + { + $this->assertSame('Option', getType(Some(42))); + $this->assertSame('List', getType(ImmList(1, 2, 3))); + } + + public function testEvaluatingBooleanLiteralsYieldsBoolean(): void + { + $session = ReplSession::empty(); + + $this->assertSame('Boolean', evaluateExpression('true', $session)->getOrElse(null)->type); + $this->assertSame('Boolean', evaluateExpression('false', $session)->getOrElse(null)->type); + $this->assertSame('Boolean', evaluateExpression('1 < 2', $session)->getOrElse(null)->type); + } + public function testCleanErrorMessageIsAvailable(): void { $message = 'TypeError: Too few arguments to function bind(), 0 passed in /Users/md/code/phunkie/console/src/Repl/ReplLoop.php(470) : eval()\'d code on line 4 and exactly 1 expected';