memio / PHP-Printer

A Pretty Printer for PHP-Parser, powered by a template engine
https://memio.github.io/PHP-Printer/
MIT License
1 stars 1 forks source link

Simpla Templating Language #1

Open gnugat opened 7 years ago

gnugat commented 7 years ago

Simpla Templating Language

We need a simple templating language that allows us to describe how the code generated from the AST should look like.

Why not Twig?

PHP-Printer will define an interface for the Templating Engine, allowing us to implement an adapter for any existing third party Templating Engine. In Memio, we've done the same thing and provided by default a Twig implementation, since it is the most popular Templating Engine. However this choice made us miss the opportunity to work with Puli as they were reluctant to pull Twig dependency in all projects that'd use it, and it made phpspec core template incompatible, which is a shame since spec-gen is the main use case for Memio.

Also as it happens, Twig isn't as fantastic with generating PHP code as it is with generating HTML...

Requirements

  1. Our default mplementation shouldn't rely on any third party library
  2. It should be compatible with phpspec templates (replace ℅parameter% with value associated to parameter key)
  3. It should have a succinct syntax, so we can best see how the generated code should look like

Of course the tricky part is on how to make the syntax "succinct" especially in regards to the following challenges:

Here's an example of generated code we'd like to be able to handle:

<?php

namespace Vendor\Project;

use Vendor\Project\Service\SayHello;

class HelloWorld
{
    private $sayHello;
    private static $count = 0;

    public function __construct(SayHello $sayHello)
    {
        $this->sayHello = $sayHello;
    }

    public function say($name = self::WORLD)
    {
        $this->sayHello->to($name);
        $this->count++;
    }
}

Notes:

  • a class can be abstract or final. It can extend 0 or 1 parent and it can inherit 0 to many interfaces. they should all be separated by a space, and if they aren't here there shouldn't be any trailing spaces
  • a class can contain 0 to many constants, they shouldn't have an empty line between them but they should have an empty line to separate them from properties or methods. If there aren't any contstants, then there shouldn't be any empty line. Constants are indented with 4 spaces, they can have a visibility (PHP 7.1) and a value, they should be separated by a space and if there aren't any then there shouldn't be any trailing whitespace, except for the indentation.
  • same rules for properties, except their value is optional and they can also be static
  • a class can contain 0 to many methods, they should be all separated by an empty line. Method signature shouldn't be longer than 80 characters, if it is then it should be split on many lines. Their opening curly brace should be on their own line if the method signature is inlined, or on the same line as the closing parenthesis if it is multiline.
gnugat commented 7 years ago

Here's a first draft:

So for example:

<?php

namespace_statement?

use_statements?

abstractness? finalness? class :name extend? inherit?
{
    constant_statements?
    property_statements?
    method_statements?
}

In the above example, we have some whitespace issues:

gnugat commented 7 years ago

Second draft without alternative syntax, and optional placeholders use a similar syntax to the required ones:

PhpSpec uses templates without optional placeholders, so it should work perfectly:

    public static function %methodName%(%arguments%)
    {
        %returnVar% = new %className%(%constructorArguments%);

        // TODO: write logic here

        return %returnVar%;
    }

Pretty Printer should rely on templates that mirror PHP Parser's nodes:

<?php

namesapce %name%;

?Stmt_Uses?

?Stmt_Interfaces?

?Stmt_Classes?

?Stmt_Traits?

With deeper templates:

interface %name% ?extends?
{
    ?Stmt_Constants?

    ?Stmt_Functions?
}

But once again optional placeholders are problematic regarding surrounding whitespaces when they shouldn't print anything:

gnugat commented 7 years ago

Third draft adding up a collection placeholder:

Collection placeholders should received an array:

So for example the Stmt_Interface template would look like:

interface %name% ?extends?
{
    +Stmt_Constant+

    +Stmt_Function+
}

And Stmt_Constant:

const %name% = %value%;

If there are no Stmt_Constant, interface should look like:

interface FindLatestLocation
{
    public function find() : array;
} 

If there's one constant:

interface FindLatestLocation
{
    const MY_CONSTANT = 42;

    public function find() : array;
} 

If there's more than one constants:

interface FindLatestLocation
{
    const MY_CONSTANT = 42;

    public function find() : array;
}