Skip to content

Commit 7cabaa2

Browse files
committed
Improve multi-line handler
1 parent 0cd79d8 commit 7cabaa2

File tree

9 files changed

+197
-32
lines changed

9 files changed

+197
-32
lines changed

src/JsPhpize/Compiler/Compiler.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,28 @@ public function __construct(JsPhpize $engine)
2121
protected function outputNode($node, $indent)
2222
{
2323
if ($node instanceof Block) {
24-
return $indent . $node->getHead() . '{' . "\n" .
24+
return $indent . $node->getHead() . "{\n" .
2525
$this->compile($node, ' ' . $indent) .
26-
$indent . '}';
26+
$indent . "}\n";
2727
}
2828
if ($node instanceof Comment) {
29-
return $indent . $node;
29+
return $indent . $node . "\n";
30+
}
31+
$node = rtrim($node, ';');
32+
if (empty($node)) {
33+
return '';
3034
}
3135

32-
return $indent . rtrim($node, ';') . ';';
36+
return $indent . $node . ";\n";
3337
}
3438

3539
public function compile(Block $block, $indent = '')
3640
{
3741
$output = '';
42+
$line = array();
3843

3944
foreach ($block->getNodes() as $node) {
40-
$output .= $this->outputNode($node, $indent) . "\n";
45+
$output .= $this->outputNode($node, $indent);
4146
}
4247

4348
return $output;

src/JsPhpize/Lexer/Lexer.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ public function next()
9797
}
9898

9999
$patterns = array(
100-
'\/\/.*?\n|\/\*[\\s\\S]*?\*\/' => 'comment',
100+
'\n' => 'newline',
101+
'\/\/.*?\n|\/\*[\s\S]*?\*\/' => 'comment',
101102
'"(?:\\\\.|[^"\\\\])*"|\'(?:\\\\.|[^\'\\\\])*\'' => 'string',
102103
'0[bB][01]+|0[oO][0-7]+|0[xX][0-9a-fA-F]+|(\d+(\.\d*)?|\.\d+)([eE]-?\d+)?' => 'number',
103104
'=>' => 'lambda',

src/JsPhpize/Lexer/Scanner.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ public function scanComment($matches)
1111
return $this->valueToken('comment', $matches);
1212
}
1313

14+
public function scanNewline($matches)
15+
{
16+
return $this->valueToken('newline', $matches);
17+
}
18+
1419
public function scanConstant($matches)
1520
{
1621
$constant = trim($matches[0]);

src/JsPhpize/Lexer/Token.php

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ public function __construct($type, array $data)
1616
), $data);
1717
}
1818

19+
public function isValue()
20+
{
21+
return in_array($this->type, array('variable', 'constant', 'string', 'number'));
22+
}
23+
1924
public function isComparison()
2025
{
2126
return in_array($this->type, array('===', '!==', '>=', '<=', '<>', '!=', '==', '>', '<'));
@@ -36,16 +41,76 @@ public function isArithmetic()
3641
return in_array($this->type, array('+', '-', '/', '*', '%', '**', '--', '++'));
3742
}
3843

39-
public function expectRightMember()
44+
public function isVarOperator()
4045
{
41-
return $this->isArithmetic() || $this->isLogical() || $this->isBinary() || $this->isComparison();
46+
return in_array($this->type, array('delete', 'void', 'typeof'));
47+
}
48+
49+
public function isOpener()
50+
{
51+
return in_array($this->type, array('{', '(', '['));
52+
}
53+
54+
public function isCloser()
55+
{
56+
return in_array($this->type, array('}', ')', ']'));
4257
}
4358

4459
public function isAssignation()
4560
{
4661
return substr($this->type, -1) === '=' && !$this->isComparison();
4762
}
4863

64+
public function isOperator()
65+
{
66+
return $this->isAssignation() || $this->isComparison() || $this->isArithmetic() || $this->isBinary() || $this->isLogical() || $this->isVarOperator();
67+
}
68+
69+
public function expectNoLeftMember()
70+
{
71+
return in_array($this->type, array('!', '~')) || $this->isVarOperator();
72+
}
73+
74+
public function expectRightMember()
75+
{
76+
return $this->isOperator() || $this->isOpener();
77+
}
78+
79+
public function canBeFollowedBy($token)
80+
{
81+
if (
82+
!$token ||
83+
in_array($token->type, array('comment', 'newline')) ||
84+
in_array($this->type, array('comment', 'newline')) ||
85+
($token->type === 'lambda' && !$this->isOperator()) ||
86+
(!$token->isOperator() && !$this->type === 'lambda')
87+
) {
88+
return true;
89+
}
90+
91+
if (in_array(';', array($token->type, $this->type))) {
92+
return false;
93+
}
94+
95+
if ($this->isOpener()) {
96+
return $token->isValue() || $token->isOpener();
97+
}
98+
99+
if ($this->type === 'variable' && in_array($token->type, array('(', '['))) {
100+
return true;
101+
}
102+
103+
if ($this->isValue()) {
104+
return !$token->expectNoLeftMember() && ($token->type === '.' || $token->isOperator() || $token->isCloser());
105+
}
106+
107+
if ($this->isOperator()) {
108+
return $token->isOpener() || $token->isValue() || in_array($token->type, array('+', '-'));
109+
}
110+
111+
return false;
112+
}
113+
49114
public function __get($key)
50115
{
51116
return isset($this->data[$key]) ? $this->data[$key] : null;

src/JsPhpize/Nodes/Block.php

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ public function __construct($type, $parentheses = null)
3333

3434
public function let($variable, $prefix)
3535
{
36-
$this->addNodes(
36+
$this->addInstructions(
3737
'$name = ' . var_export(ltrim($variable, '$'), true),
3838
'$names = array()'
3939
);
4040
$while = new self('while', '($prev = $name) && ($name = "' . $prefix . 'l_" . $name) && isset($$prev)');
41-
$while->addNode('$names[] = array($name, $prev)');
41+
$while->addInstruction('$names[] = array($name, $prev)');
4242
$this->addNode($while);
4343
$while = new self('while', '$data = array_pop($names)');
44-
$while->addNodes(
44+
$while->addInstructions(
4545
'list($name, $prev) = $data',
4646
'$$name = $$prev'
4747
);
@@ -51,15 +51,35 @@ public function let($variable, $prefix)
5151

5252
public function addNodes($nodes)
5353
{
54-
$nodes = array_filter(is_array($nodes) ? $nodes : func_get_args());
55-
$this->nodes = array_merge($this->nodes, $nodes);
54+
foreach (array_filter(is_array($nodes) ? $nodes : func_get_args()) as $node) {
55+
if (is_string($node) && is_string(end($this->nodes))) {
56+
$this->nodes[count($this->nodes) - 1] .= ' ' . $node;
57+
} else {
58+
$this->nodes[] = $node;
59+
}
60+
}
5661
}
5762

5863
public function addNode()
5964
{
6065
$this->addNodes(func_get_args());
6166
}
6267

68+
public function addInstructions($instructions)
69+
{
70+
foreach (is_array($instructions) ? $instructions : func_get_args() as $instruction) {
71+
$this->addNode(
72+
$instruction,
73+
new NodeEnd()
74+
);
75+
}
76+
}
77+
78+
public function addInstruction()
79+
{
80+
$this->addInstructions(func_get_args());
81+
}
82+
6383
public function setParentheses($parentheses)
6484
{
6585
$this->parentheses = $parentheses;
@@ -73,7 +93,7 @@ public function getNodes()
7393
}, $this->localVariables)) . ')';
7494
$foreach = new self('foreach', $localVariables . ' as $data');
7595
$while = new self('while', '($prev = $name) && ($name = $prefix . "l_" . $name) && isset($$prev)');
76-
$while->addNodes(
96+
$while->addInstructions(
7797
'$$prev = $$name',
7898
'unset($$name)'
7999
);

src/JsPhpize/Nodes/NodeEnd.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace JsPhpize\Nodes;
4+
5+
class NodeEnd
6+
{
7+
protected $token;
8+
9+
public function __construct($token = null)
10+
{
11+
$this->token = $token;
12+
}
13+
14+
public function __toString()
15+
{
16+
return '';
17+
}
18+
}

src/JsPhpize/Parser/ExpressionParser.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ protected function handlePlus(&$expression, $token, $handlePlus)
138138
$element = ',';
139139
}
140140
}
141-
$token = 'call_user_func(' . $this->getHelper('plus') . ', ' . $before . ' ' . implode(' ', $after) . ')';
141+
$token = $this->helperWrap('plus', $before . ' ' . implode(' ', $after));
142142
}
143143
}
144144

@@ -175,4 +175,27 @@ protected function getExpression($handlePlus = true)
175175

176176
return implode(' ', $expression);
177177
}
178+
179+
protected function dyiade(&$variable, $next)
180+
{
181+
while ($next && $next->expectRightMember()) {
182+
$this->skip();
183+
$variable = $next->type === '+'
184+
? $this->helperWrap('plus', $variable, $this->getExpression(false))
185+
: $variable . ' ' . $next . ' ' . implode(' ', $this->visitToken($this->next()));
186+
$next = $this->current();
187+
}
188+
}
189+
190+
protected function appendToVariableParts(&$variableParts, $afterNext, $bracket, $parenthesis)
191+
{
192+
if ($parenthesis) {
193+
$variableParts = array('call_user_func(' . $this->helperWrap('dot', $variableParts) . ', ' . $this->getExpression() . ')');
194+
195+
return;
196+
}
197+
$variableParts[] = $bracket
198+
? $this->getExpression()
199+
: var_export($afterNext->value, true);
200+
}
178201
}

src/JsPhpize/Parser/Parser.php

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use JsPhpize\JsPhpize;
66
use JsPhpize\Lexer\Lexer;
77
use JsPhpize\Nodes\Main;
8+
use JsPhpize\Nodes\NodeEnd;
89

910
class Parser extends Visitor
1011
{
@@ -33,6 +34,11 @@ class Parser extends Visitor
3334
*/
3435
protected $stack;
3536

37+
/**
38+
* @var Token
39+
*/
40+
protected $previousToken;
41+
3642
public function __construct(JsPhpize $engine, $input, $filename)
3743
{
3844
$input = str_replace(array("\r\n", "\r"), array("\n", ''), $input);
@@ -68,6 +74,15 @@ protected function getHelper($helper)
6874
return '$GLOBALS["' . $this->engine->getOption('varPrefix', JsPhpize::VAR_PREFIX) . 'h_' . $helper . '"]';
6975
}
7076

77+
protected function helperWrap($helper, $argumments)
78+
{
79+
if (!is_array($argumments)) {
80+
$argumments = array_slice(func_get_args(), 1);
81+
}
82+
83+
return 'call_user_func(' . $this->getHelper($helper) . ', ' . implode(', ', $argumments) . ')';
84+
}
85+
7186
protected function exceptionInfos()
7287
{
7388
return $this->lexer->exceptionInfos();
@@ -78,9 +93,14 @@ protected function prepend($token)
7893
return array_unshift($this->tokens, $token);
7994
}
8095

96+
protected function getNextUnread()
97+
{
98+
return $this->lexer->next();
99+
}
100+
81101
protected function next()
82102
{
83-
return array_shift($this->tokens) ?: $this->lexer->next();
103+
return array_shift($this->tokens) ?: $this->getNextUnread();
84104
}
85105

86106
protected function skip($index = 1)
@@ -95,7 +115,7 @@ protected function advance($index)
95115
$token = null;
96116

97117
while ($index--) {
98-
$token = $this->lexer->next();
118+
$token = $this->getNextUnread();
99119
$this->tokens[] = $token;
100120
}
101121

@@ -104,6 +124,10 @@ protected function advance($index)
104124

105125
protected function get($index)
106126
{
127+
if ($index === -1) {
128+
return $this->previous();
129+
}
130+
107131
return isset($this->tokens[$index])
108132
? $this->tokens[$index]
109133
: $this->advance($index + 1 - count($this->tokens));
@@ -114,6 +138,11 @@ protected function current()
114138
return $this->get(0);
115139
}
116140

141+
protected function previous()
142+
{
143+
return $this->previousToken;
144+
}
145+
117146
protected function unexpected($token)
118147
{
119148
throw new Exception('Unexpected ' . $token->type . rtrim(' ' . ($token->value ?: '')) . $this->exceptionInfos(), 8);
@@ -136,7 +165,6 @@ protected function getCurrentBlock()
136165

137166
protected function visitToken($token)
138167
{
139-
$block = $this->getCurrentBlock();
140168
$method = 'visit' . ucfirst($token->type);
141169
$token = method_exists($this, $method)
142170
? $this->$method($token)
@@ -151,15 +179,16 @@ protected function visitToken($token)
151179
public function parseBlock($block)
152180
{
153181
$this->stack[] = $block;
154-
$prev = null;
182+
$this->previousToken = null;
155183
while ($token = $this->next()) {
156-
if ($token === $prev) {
184+
if ($token === $this->previousToken) {
157185
$this->unexpected($token);
158186
}
159-
$prev = $token;
160187
if ($token->type === ';') {
188+
$block->addNode(new NodeEnd($token));
161189
continue;
162190
}
191+
$this->previousToken = $token;
163192
if ($token->type === '}') {
164193
return;
165194
}

0 commit comments

Comments
 (0)