Skip to content

Commit

Permalink
Merge pull request #28 from pug-php/feature/support-back-ticks
Browse files Browse the repository at this point in the history
Support back ticks and interpolation
  • Loading branch information
kylekatarnls authored Dec 12, 2019
2 parents 8bd3a87 + 718bc40 commit 88e1a89
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 7 deletions.
3 changes: 3 additions & 0 deletions examples/interpolation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name = 'Bob';

return `Hello ${name}, \${not} can you ${(function (verb) { return verb; })('tell')}?`;
1 change: 1 addition & 0 deletions examples/interpolation.return
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello Bob, ${not} can you tell?
1 change: 1 addition & 0 deletions examples/typeof-return.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return typeof 'foobar'
1 change: 1 addition & 0 deletions examples/typeof-return.return
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
string
32 changes: 27 additions & 5 deletions src/JsPhpize/Compiler/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
class Compiler
{
use DyiadeTrait;
use InterpolationTrait;

/**
* @const string
Expand All @@ -42,9 +43,15 @@ class Compiler
*/
protected $arrayShortSyntax;

public function __construct(JsPhpize $engine)
/**
* @var string
*/
protected $filename;

public function __construct(JsPhpize $engine, $filename = null)
{
$this->engine = $engine;
$this->filename = $filename;
$this->setPrefixes($engine->getVarPrefix(), $engine->getConstPrefix());
$this->arrayShortSyntax = $engine->getOption('arrayShortSyntax', false);
}
Expand Down Expand Up @@ -138,12 +145,25 @@ function ($pair) use ($visitNode, $indent) {
protected function visitConstant(Constant $constant)
{
$value = $constant->value;
if ($constant->type === 'string' && mb_substr($constant->value, 0, 1) === '"') {
$value = str_replace('$', '\\$', $value);

if ($constant->type === 'string') {
if (substr($value, 0, 1) === '`') {
return implode(
' . ',
iterator_to_array($this->readInterpolation(substr($value, 1, -1)))
);
}

if (mb_substr($constant->value, 0, 1) === '"') {
return str_replace('$', '\\$', $value);
}
}

if ($constant->type === 'regexp') {
$regExp = $this->engine->getHelperName('regExp');
$value = $this->helperWrap($regExp, [var_export($value, true)]);
return $this->helperWrap(
$this->engine->getHelperName('regExp'),
[var_export($value, true)]
);
}

return $value;
Expand Down Expand Up @@ -217,6 +237,7 @@ protected function visitFunctionCall(FunctionCall $functionCall, $indent)
$staticCall = $name . '(' . $arguments . ')';

$functions = str_replace(["\n", "\t", "\r", ' '], '', static::STATIC_CALL_FUNCTIONS);

if ($applicant === 'new' || in_array($name, explode(',', $functions))) {
return $staticCall;
}
Expand Down Expand Up @@ -270,6 +291,7 @@ public function visitNode(Node $node, $indent)
get_class($node)
);
$php = method_exists($this, $method) ? $this->$method($node, $indent) : '';

if ($node instanceof Value) {
$php = $node->getBefore() . $php . $node->getAfter();
}
Expand Down
37 changes: 37 additions & 0 deletions src/JsPhpize/Compiler/InterpolationTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace JsPhpize\Compiler;

use JsPhpize\Parser\Parser;

trait InterpolationTrait
{
protected function readInterpolation($value)
{
while (strlen($value)) {
preg_match('/^(.*)(?:(\\\\|\\${).*)?$/U', $value, $match);

yield var_export($match[1], true);

$value = mb_substr($value, mb_strlen($match[1]));

if (isset($match[2])) {
if ($match[2] === '\\') {
yield var_export(mb_substr($value, 1, 1), true);

$value = mb_substr($value, 2);

continue;
}

$value = mb_substr($value, 2);

$parser = new Parser($this->engine, $value, $this->filename);

yield rtrim($this->visitInstruction($parser->parse()->instructions[0], ''), ";\t\n\r\0\x0B ");

$value = $parser->rest();
}
}
}
}
4 changes: 3 additions & 1 deletion src/JsPhpize/JsPhpize.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,13 @@ public function compile($input, $filename = null)

$start = '';
$end = '';

if (preg_match('/^([)}\]\s]*)(.*?)([({\[\s]*)$/', trim($input), $match)) {
list(, $start, $input, $end) = $match;
}

$parser = new Parser($this, $input, $filename);
$compiler = new Compiler($this);
$compiler = new Compiler($this, $filename);
$block = $parser->parse();
$php = $compiler->compile($block);

Expand All @@ -70,6 +71,7 @@ public function compile($input, $filename = null)
}

$dependencies = $compiler->getDependencies();

if ($this->getOption('catchDependencies')) {
$this->dependencies = array_unique(array_merge($this->dependencies, $dependencies));
$dependencies = [];
Expand Down
2 changes: 1 addition & 1 deletion src/JsPhpize/JsPhpizeOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function __construct($options = [])
$this->options['patterns'] = [
new Pattern(10, 'newline', '\n'),
new Pattern(20, 'comment', '\/\/.*?\n|\/\*[\s\S]*?\*\/'),
new Pattern(30, 'string', '"(?:\\\\.|[^"\\\\])*"|\'(?:\\\\.|[^\'\\\\])*\''),
new Pattern(30, 'string', '"(?:\\\\.|[^"\\\\])*"|\'(?:\\\\.|[^\'\\\\])*\'|`(?:\\\\.|[^`\\\\])*`'),
new Pattern(40, 'number', '0[bB][01]+|0[oO][0-7]+|0[xX][0-9a-fA-F]+|(\d+(\.\d*)?|\.\d+)([eE]-?\d+)?'),
new Pattern(50, 'lambda', '=>'),
new Pattern(60, 'operator', ['delete', 'typeof', 'void'], true),
Expand Down
5 changes: 5 additions & 0 deletions src/JsPhpize/Lexer/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ public function exceptionInfos()
' near from ' . trim($this->consumed);
}

public function rest()
{
return $this->input;
}

protected function consume($consumed)
{
$consumed = is_int($consumed) ? mb_substr($this->input, 0, $consumed) : $consumed;
Expand Down
3 changes: 3 additions & 0 deletions src/JsPhpize/Nodes/Constant.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public function __construct($type, $value)
if (!in_array($type, ['constant', 'number', 'string', 'regexp'])) {
throw new Exception("The given type [$type] is not a valid constant type.", 23);
}

$this->type = $type;
$this->value = $value;
}
Expand All @@ -44,9 +45,11 @@ public function getNonAssignableReason()
if ($this->type !== 'constant') {
return "{$this->type} is not assignable.";
}

if (in_array($this->value, ['NAN', 'INF'])) {
return "{$this->value} is not assignable.";
}

if (mb_substr($this->value, 0, 2) === 'M_') {
return "'M_' prefix is reserved to mathematical constants.";
}
Expand Down
30 changes: 30 additions & 0 deletions src/JsPhpize/Parser/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,25 @@ public function __construct(JsPhpize $engine, $input, $filename)
$this->lexer = new Lexer($engine, $input, $filename);
}

public function rest()
{
return $this->lexer->rest();
}

protected function parseLambda(Value $parameters)
{
$lambda = new Block('function');
$lambda->setValue($parameters);
$next = $this->next();

if ($next) {
if ($next->is('{')) {
$this->parseBlock($lambda);
$this->skip();

return $lambda;
}

$return = new Block('return');
$return->setValue($this->expectValue($next));
$lambda->addInstruction($return);
Expand All @@ -71,6 +78,7 @@ protected function parseParentheses($allowedSeparators = [',', ';'])
while ($token = $this->next()) {
if ($token->is(')')) {
$next = $this->get(0);

if ($next && $next->type === 'lambda') {
$this->skip();

Expand Down Expand Up @@ -204,6 +212,7 @@ protected function parseFunctionCallChildren($function, $applicant = null)
$children[] = $value;

$next = $this->get(0);

if ($next && $next->is('(')) {
$this->skip();

Expand All @@ -225,6 +234,7 @@ protected function parseVariable($name)
{
$children = [];
$variable = null;

while ($next = $this->get(0)) {
if ($next->type === 'lambda') {
$this->skip();
Expand All @@ -238,6 +248,7 @@ protected function parseVariable($name)
$children[] = $value;

$next = $this->get(0);

if ($next && $next->is('(')) {
$this->skip();

Expand All @@ -257,6 +268,7 @@ protected function parseVariable($name)

for ($i = count($this->stack) - 1; $i >= 0; $i--) {
$block = $this->stack[$i];

if ($block->isLet($name)) {
$variable->setScope($block);

Expand All @@ -272,6 +284,7 @@ protected function parseTernary(Value $condition)
{
$trueValue = $this->expectValue($this->next());
$next = $this->next();

if (!$next) {
throw new Exception("Ternary expression not properly closed after '?' " . $this->exceptionInfos(), 14);
}
Expand All @@ -281,6 +294,7 @@ protected function parseTernary(Value $condition)
}

$next = $this->next();

if (!$next) {
throw new Exception("Ternary expression not properly closed after ':' " . $this->exceptionInfos(), 16);
}
Expand All @@ -294,6 +308,7 @@ protected function parseTernary(Value $condition)
protected function jsonMethodToPhpFunction($method)
{
$function = null;

switch ($method) {
case 'stringify':
$function = 'json_encode';
Expand All @@ -310,6 +325,7 @@ protected function parseJsonMethod($method)
{
if ($method->type === 'variable' && ($function = $this->jsonMethodToPhpFunction($method->value))) {
$this->skip(2);

if (($next = $this->get(0)) && $next->is('(')) {
$this->skip();

Expand Down Expand Up @@ -344,6 +360,7 @@ protected function parseFunction()
$function = new Block('function');
$function->enableMultipleInstructions();
$token = $this->get(0);

if ($token && $token->type === 'variable') {
$this->skip();
$token = $this->get(0);
Expand All @@ -356,6 +373,7 @@ protected function parseFunction()
$this->skip();
$function->setValue($this->parseParentheses());
$token = $this->get(0);

if ($token && !$token->is('{')) {
throw $this->unexpected($token);
}
Expand All @@ -370,9 +388,12 @@ protected function parseKeywordStatement($token)
{
$name = $token->value;
$keyword = new Block($name);

switch ($name) {
case 'typeof':
// @codeCoverageIgnoreStart
throw new Exception('typeof keyword not supported', 26);
// @codeCoverageIgnoreEnd
break;
case 'new':
case 'clone':
Expand All @@ -384,9 +405,11 @@ protected function parseKeywordStatement($token)
'clone' => 'Object',
];
$value = $this->get(0);

if (isset($expects[$name]) && !$value) {
throw new Exception($expects[$name] . " expected after '" . $name . "'", 25);
}

$this->handleOptionalValue($keyword, $value, $name);
break;
case 'case':
Expand All @@ -407,6 +430,7 @@ protected function parseKeywordStatement($token)
protected function parseKeyword($token)
{
$keyword = $this->parseKeywordStatement($token);

if ($keyword->handleInstructions()) {
$this->parseBlock($keyword);
}
Expand All @@ -417,6 +441,7 @@ protected function parseKeyword($token)
protected function parseLet()
{
$letVariable = $this->get(0);

if ($letVariable->type !== 'variable') {
throw $this->unexpected($letVariable);
}
Expand Down Expand Up @@ -445,6 +470,7 @@ protected function parseInstruction($block, $token, &$initNext)
if ($initNext && $instruction instanceof Variable) {
$instruction = new Assignation('=', $instruction, new Constant('constant', 'null'));
}

$initNext = false;
$block->addInstruction($instruction);

Expand Down Expand Up @@ -486,15 +512,19 @@ protected function parseInstructions($block)
public function parseBlock($block)
{
$this->stack[] = $block;

if (!$block->multipleInstructions) {
$next = $this->get(0);

if ($next && $next->is('{')) {
$block->enableMultipleInstructions();
}

if ($block->multipleInstructions) {
$this->skip();
}
}

$this->parseInstructions($block);
array_pop($this->stack);
}
Expand Down
1 change: 1 addition & 0 deletions src/JsPhpize/Parser/TokenExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ protected function getBracketsArrayItemKeyFromToken($token)
if ($typeAndValue->isValid()) {
list($type, $value) = $typeAndValue->get();
$token = $this->next();

if (!$token) {
throw new Exception('Missing value after ' . $value . $this->exceptionInfos(), 12);
}
Expand Down
2 changes: 2 additions & 0 deletions tests/render.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public function caseProvider()
$cases = [];

$examples = __DIR__ . '/../examples';

foreach (scandir($examples) as $file) {
if (substr($file, -7) === '.return') {
$cases[] = [$file, substr($file, 0, -7) . '.js'];
Expand Down Expand Up @@ -39,6 +40,7 @@ public function testJsPhpizeGeneration($returnFile, $jsFile)
$contents = $jsPhpize->compile($examples . '/' . $jsFile);
$message = "\n" . get_class($error) . ' in ' . $jsFile . ' line ' . $error->getLine() .
"\n" . $error->getMessage() . "\n";

foreach (explode("\n", $contents) as $index => $line) {
$number = $index + 1;
$message .= ($number === $error->getLine() ? '>' : ' ') .
Expand Down
1 change: 1 addition & 0 deletions tests/stream.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ public function testStreamEmulator()
$this->assertTrue(is_array($stream->url_stat('foo', 0)));
$this->assertSame('r', $stream->stream_read(2));
$this->assertTrue($stream->stream_eof());
$this->assertTrue($stream->stream_set_option(1, 2, 3));
}
}

0 comments on commit 88e1a89

Please sign in to comment.