Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simpla Templating Language #1

Open
gnugat opened this issue Oct 5, 2016 · 3 comments
Open

Simpla Templating Language #1

gnugat opened this issue Oct 5, 2016 · 3 comments
Assignees
Labels

Comments

@gnugat
Copy link
Member

gnugat commented Oct 5, 2016

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:

  • Placeholders, to be replaced by given value
  • Optional placeholders, to be replaced under condition (hint: if blocks can clutter the templates)
  • Whitespace management including indentation, word separation, line separation, etc

Usage examples

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 gnugat added the RFC label Oct 5, 2016
@gnugat gnugat self-assigned this Oct 6, 2016
@gnugat
Copy link
Member Author

gnugat commented Oct 6, 2016

Here's a first draft:

  • Placeholders using %placeholder% or alternatively :placeholder
  • Optional placeholders using placeholder?, if it is falsey then replace it with an empty string (and remove the next whitespace?)

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:

  • if there are no namespace statement, the use statements will have 2 empty lines above it, when we'd expect only one
  • if the class doesn't extend anything and if it doesn't inherit anything, then we have a trailing space after name
  • if the class doesn't have any constants but does have properties, the first property will be indented with 8 spaces (same issue with properties and methods)

@gnugat
Copy link
Member Author

gnugat commented Oct 16, 2016

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

  • Placeholders: %placeholder%
  • Optional placehodlers: ?optional placeholder?

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:

  • if there are no Stmt_Uses, then we expect it to be removed with its two new lines characters
  • if there are no extends, then we expect the previous space to be removed
  • if there are no Stmt_Constants, then we expect its indentation to be removed as well as its two new lines characters

@gnugat
Copy link
Member Author

gnugat commented Oct 19, 2016

Third draft adding up a collection placeholder:

  • Collection placeholder: +collection placeholder+

Collection placeholders should received an array:

  • if it's empty, then remove its line, as well as the surrounding empty lines
  • otherwise replace the placeholder with the concatenation of each elements, separated with the same whitespaces on the placeholder's line

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;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant