Skip to content

Commit

Permalink
use parser combinators
Browse files Browse the repository at this point in the history
short arrow functions
implement typeName-parser + enum class generator
extract builder function for enum classes
add manyArray & manyArray1 parsers, refactor namespace type object
update kahlan
parse enum constructors
implement enum parser
namespace parsing, hack into spaces() function
parse ns with sub ns
add fpp.php entry point
make use of configuration
namespace parsing is configurable, gen default config file
  • Loading branch information
prolic committed Apr 12, 2020
1 parent 042e443 commit 6a7a9ab
Show file tree
Hide file tree
Showing 148 changed files with 1,535 additions and 15,308 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2018, Sascha-Oliver Prolic
Copyright (c) 2018-2020, Sascha-Oliver Prolic
All rights reserved.

Redistribution and use in source and binary forms, with or without
Expand Down
190 changes: 4 additions & 186 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,194 +1,12 @@
# FPP

## Functional PHP Preprocessor - Immutable data type generator
## Functional PHP Preprocessor - Immutable data type generator

### What it this?

This library can generate immutable data types based on fpp definitions. The syntax is inspired by Haskell.

### YouTube Video Tutorial
## Credits

[![YouTube Video Tutorial](https://i.ytimg.com/vi/MYh1_sydQ5U/hqdefault.jpg?sqp=-oaymwEXCNACELwBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCtO68XORuK-gEGeTJSXdSHqY3PBQ)](https://youtu.be/MYh1_sydQ5U)

### Docs

[See the docs here](https://github.com/prolic/fpp/tree/master/docs/Home.md)

### Sponsor

If you want to support my work, become a patron at [patreon.com/prolic](https://www.patreon.com/notifications).

### Install

```console
composer require prolic/fpp
```

### So what is it really?

Create a file and put this in it:

```fpp
namespace Model\Foo;
data Person = Person { string $name, ?int $age };
```

This will generate the following php code:

```php
namespace Model\Foo;
final class Person
{
private $name;
private $age;

public function __construct(string $name, ?int $age)
{
$this->name = $name;
$this->age = $age;
}

public function name(): string
{
return $this->name;
}

public function age(): ?int
{
return $this->age;
}

public function withName(string $name): Person
{
return new self($name, $this->age);
}

public function withAge(?int $age): Person
{
return new self($this->name, $age);
}
}
```

### Enums?

No problem

```fpp
namespace MyEnum;
data Color = Red | Blue | Green | Yellow deriving (Enum);
```

```php
$blue = Color::blue();
var_dump($blue->equals(Color::blue())); // true
var_dump($blue->equals(Color::red())); // false

function (MyEnum\Color $color): string
{
return $color->value();
}
```

Enums with value mappings

```fpp
namespace MyEnum;
data Color = Red | Blue deriving (Enum) with (Red:'someThing', Blue: 13);
```

```php
var_dump(Color::red()->value()); // 'someThing'
var_dump(Color::blue()->value()); // 13
```

### Derivings

Derivings are kind of PHP's extends keyword, the following rules apply:

- It's possible to derive multiple times
- Some derivings are not compatible to each other (ie, Command and ToArray cannot be mixed)

There are 14 deriving types for now:

- `AggregateChanged`
- `Command`
- `DomainEvent`
- `Enum(useName)` (default) / `Enum(useValue)`
- `Equals`
- `FromArray`
- `FromScalar`
- `FromString`
- `Query`
- `MicroAggregateChanged` (not extending from prooph/eventsourcing, f.e. for prooph/micro)
- `ToArray`
- `ToScalar`
- `ToString`
- `Uuid`

Deriving Equals + ToArray

```fpp
namespace Model\Foo;
data Person = Person { string $name, ?int $age } deriving (ToArray, Equals);
```

Now you can do this:

```php
$p = new Model\Foo\Person(['name' => 'sasa', 'age' => 36]);
var_dump($p->toArray()); // ['name' => 'sasa', 'age' => 36]
$p->equals($p) // true
```

### Conditions

You can add additional conditions to your code:

```
namespace Foo;
data FirstName = String deriving (FromString, ToString);
data LastName = String deriving (FromString, ToString);
data Age = Int deriving (FromScalar, ToScalar);
data Person = Person { FirstName $firstName, LastName $lastName, Age $age } | Boss { FirstName $firstName, LastName $lastName } where
Person:
| strlen($lastName->toString()) === 0 => 'last name too short'
| $age->toScalar() < 18 => "you\'re too young, sorry"
Boss:
| strlen($lastName->toString()) < 5 => 'last name too short'
_:
| strlen($firstName->toString()) === 0 => 'first name too short';
```

### Usage

```php
php bin/fpp.php <source dir or file>
# or
php vendor/prolic/fpp/bin/fpp.php <source dir or file>
```

It will try to find your composer autoload and fetch psr-4 and psr-0 prefixes from it.
You'll get an exception, if you want to dump a class, where you have no composer autoload definition.

### Features

- [x] Create immutable data types with ease
- [x] Strict types always
- [x] Generate prooph commands
- [x] Generate prooph events
- [x] Generate prooph queries
- [x] Generate prooph aggregate changed events
- [x] Ability to switch dumper implementation for custom output
- [x] Allow composite data objects
- [x] Allow composite prooph objects
- [x] Constructor validation
- [x] Allow creating of custom constructors
- [x] Dump files according to psr-4 and psr-0 autoloading rules
- [x] Array notation for objects and scalar types
- [x] Enum value mappings
- [x] Support for conditions
[Marcello Duarte](https://github.com/MarcelloDuarte/) created the [ParserCombinators](https://github.com/MarcelloDuarte/ParserCombinators/) project in 2017.
The rewrite of this library is heavily inspired by it and reuses some of its base functions.
110 changes: 89 additions & 21 deletions bin/fpp.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/**
* This file is part of prolic/fpp.
* (c) 2018-2019 Sascha-Oliver Prolic <[email protected]>
* (c) 2018-2020 Sascha-Oliver Prolic <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
Expand All @@ -12,41 +12,109 @@

namespace Fpp;

use Nette\PhpGenerator\PsrPrinter;
use function Pair;

if (! isset($argv[1])) {
echo 'Missing input directory or file argument';
exit(1);
}

$path = $argv[1];

$autoloader = require __DIR__ . '/../src/bootstrap.php';
$pwd = \realpath(\getcwd());
$vendorName = 'vendor';

$prefixesPsr4 = $autoloader->getPrefixesPsr4();
$prefixesPsr0 = $autoloader->getPrefixes();
if (\file_exists($composerPath = "{$pwd}/composer.json")) {
$composerJson = \json_decode(\file_get_contents($composerPath), true);
$vendorName = isset($composerJson['config']['vendor-dir']) ? $composerJson['config']['vendor-dir'] : $vendorName;
}

$locatePsrPath = function (Definition $definition, ?Constructor $constructor) use ($prefixesPsr4, $prefixesPsr0): string {
return locatePsrPath($prefixesPsr4, $prefixesPsr0, $definition, $constructor);
};
if (! \file_exists("{$pwd}/{$vendorName}/autoload.php")) {
echo "\033[1;31mYou need to set up the project dependencies using the following commands: \033[0m" . PHP_EOL;
echo 'curl -s http://getcomposer.org/installer | php' . PHP_EOL;
echo 'php composer.phar install' . PHP_EOL;
exit(1);
}

$derivingMap = defaultDerivingMap();
require "{$pwd}/{$vendorName}/autoload.php";

$collection = new DefinitionCollection();
$config = [
'use_strict_types' => true,
'one_class_per_file' => true, // @todo this configuration isn't working right now
'printer' => PsrPrinter::class,
'namespace_parser' => Pair(namespaceName, 'buildNamespace'),
'types' => [
Type\Enum\Enum::class => Pair(enum, buildEnum),
],
];

try {
foreach (scan($path) as $file) {
$collection = $collection->merge(parse($file, $derivingMap));
}
} catch (ParseError $e) {
echo 'Parse Error: ' . $e->getMessage();
exit(1);
if ($path === '--gen-config') {
$file = <<<CODE
<?php
declare(strict_types=1);
namespace Fpp;
use Nette\PhpGenerator\PsrPrinter;
use function Pair;
return [
'use_strict_types' => true,
'one_class_per_file' => true, // @todo this configuration isn't working right now
'printer' => PsrPrinter::class,
'namespace_parser' => Pair(namespaceName, 'buildNamespace'),
'types' => [
Type\Enum\Enum::class => Pair(enum, buildEnum),
],
];
CODE;

\file_put_contents("{$pwd}/fpp-config.php", $file);

echo "Default configuration written to {$pwd}/fpp-config.php\n";
exit(0);
}

if (\file_exists("{$pwd}/fpp-config.php")) {
$config = \array_merge_recursive($config, require "{$pwd}/fpp-config.php");
}

try {
dump($collection, $locatePsrPath, loadTemplate, replace);
} catch (\Exception $e) {
echo 'Exception: ' . $e->getMessage();
if (empty($config['types'])) {
echo "\033[1;31mNo parser found, check your fpp-config.php file\033[0m" . PHP_EOL;
exit(1);
}

echo "Successfully generated and written to disk\n";
// bootstrapping done - @todo: make this bottom part more FP stylish
$parser = zero();

foreach ($config['types'] as $type => $pair) {
$parser = $parser->or(($pair->_1)());
}

$namespaceParser = $config['namespace_parser']->_1;

$toDump = \Nil();

scan($path)->map(
fn ($f) => Pair(manyList($namespaceParser($parser))->run(\file_get_contents($f)), $f)
)->map(function ($p) use ($config, &$toDump) {
$parsed = $p->_1->head();
$filename = $p->_2;

if (\strlen($parsed->_2) !== 0) {
echo "\033[1;31mSyntax error at file $filename at:\033[0m" . PHP_EOL . PHP_EOL;
echo \substr($parsed->_2, 0, 40) . PHP_EOL;
exit(1);
}

$toDump = $toDump->combine($parsed->_1);
});

$toDump->map(function ($ns) use ($config) {
\var_dump(dump($ns, $config));
});

exit(0);
21 changes: 14 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,30 @@
}
],
"require": {
"php": "^7.1"
"php": "^7.4",
"phunkie/phunkie": "^0.11.1",
"nette/php-generator": "^3.3.4"
},
"require-dev": {
"mikey179/vfsstream": "^1.6.5",
"phpunit/phpunit": "^8.2",
"kahlan/kahlan": "^4.7.4",
"prooph/php-cs-fixer-config": "^0.3",
"satooshi/php-coveralls": "^2.0",
"phpstan/phpstan": "^0.12.2"
"satooshi/php-coveralls": "^2.0"
},
"autoload": {
"psr-4": {
"Fpp\\": "src/"
}
},
"files": [
"src/Functions/builder.php",
"src/Functions/basic_parser.php",
"src/Functions/fpp_parser.php",
"src/Functions/fpp.php",
"src/Functions/scan.php"
]
},
"autoload-dev": {
"psr-4": {
"FppTest\\": "tests/"
"FppSpec\\": "spec/"
}
},
"bin": [
Expand Down
8 changes: 8 additions & 0 deletions demo/enum.fpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Foo\Bar {
enum Color = Red | Green | Blue
enum Human = Man | Woman
}

namespace Foo {
enum Ok = Right | Wrong
}
Loading

0 comments on commit 6a7a9ab

Please sign in to comment.