Skip to content

Commit bf4a60a

Browse files
committed
Handle block and let
1 parent b9fcfd2 commit bf4a60a

File tree

10 files changed

+161
-21
lines changed

10 files changed

+161
-21
lines changed

examples/let.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
foo = 9;
2+
bar = 5;
3+
4+
if (foo == 9) {
5+
let bar = 2;
6+
foo += bar;
7+
}
8+
9+
return foo - bar;

examples/let.return

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
6

src/JsPhpize/Compiler/Compiler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ protected function outputNode($node, $indent)
2323
if ($node instanceof Block) {
2424
return $indent . $node->getHead() . '{' . "\n" .
2525
$this->compile($node, ' ' . $indent) .
26-
'}';
26+
$indent . '}';
2727
}
2828
if ($node instanceof Comment) {
2929
return $indent . $node;

src/JsPhpize/JsPhpize.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
class JsPhpize
1010
{
11+
const CONST_PREFIX = '__JP_';
12+
const VAR_PREFIX = '__jp_';
13+
1114
/**
1215
* @var string
1316
*/

src/JsPhpize/Lexer/Scanner.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace JsPhpize\Lexer;
44

5+
use JsPhpize\JsPhpize;
6+
57
class Scanner
68
{
79
public function scanComment($matches)
@@ -12,8 +14,9 @@ public function scanComment($matches)
1214
public function scanConstant($matches)
1315
{
1416
$constant = trim($matches[0]);
15-
if (substr($constant, 0, 5) === '__JP_') {
16-
throw new Exception('Constants cannot start with __JP_, this prefix is reserved for JsPhpize' . $this->exceptionInfos(), 1);
17+
$constPrefix = $this->engine->getOption('constPrefix', JsPhpize::CONST_PREFIX);
18+
if (substr($constant, 0, 5) === $constPrefix) {
19+
throw new Exception('Constants cannot start with ' . $constPrefix . ', this prefix is reserved for JsPhpize' . $this->exceptionInfos(), 1);
1720
}
1821
$translate = array(
1922
'Infinity' => 'INF',
@@ -37,7 +40,7 @@ public function scanFunction($matches)
3740

3841
public function scanKeyword($matches)
3942
{
40-
return $this->typeToken($matches);
43+
return $this->valueToken('keyword', $matches);
4144
}
4245

4346
public function scanLambda($matches)
@@ -62,8 +65,9 @@ public function scanOperator($matches)
6265

6366
public function scanVariable($matches)
6467
{
65-
if (substr($matches[0], 0, 5) === '__jp_') {
66-
throw new Exception('Variables cannot start with __jp_, this prefix is reserved for JsPhpize' . $this->exceptionInfos(), 1);
68+
$varPrefix = $this->engine->getOption('varPrefix', JsPhpize::VAR_PREFIX);
69+
if (substr($matches[0], 0, 5) === $varPrefix) {
70+
throw new Exception('Variables cannot start with ' . $varPrefix . ', this prefix is reserved for JsPhpize' . $this->exceptionInfos(), 1);
6771
}
6872

6973
return $this->valueToken('variable', $matches);

src/JsPhpize/Nodes/Block.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,36 @@ class Block
1919
*/
2020
protected $nodes;
2121

22+
/**
23+
* @var array
24+
*/
25+
protected $localVariables;
26+
2227
public function __construct($type, $parentheses = null)
2328
{
2429
$this->type = $type;
2530
$this->nodes = array();
2631
$this->parentheses = $parentheses;
2732
}
2833

34+
public function let($variable, $prefix)
35+
{
36+
$this->addNodes(
37+
'$name = ' . var_export(ltrim($variable, '$'), true),
38+
'$names = array()'
39+
);
40+
$while = new static('while', '($prev = $name) && ($name = "' . $prefix . 'l_" . $name) && isset($$prev)');
41+
$while->addNode('$names[] = array($name, $prev)');
42+
$this->addNode($while);
43+
$while = new static('while', '$data = array_pop($names)');
44+
$while->addNodes(
45+
'list($name, $prev) = $data',
46+
'$$name = $$prev'
47+
);
48+
$this->addNode($while);
49+
$this->localVariables[] = func_get_args();
50+
}
51+
2952
public function addNodes($nodes)
3053
{
3154
$nodes = array_filter(is_array($nodes) ? $nodes : func_get_args());
@@ -44,6 +67,23 @@ public function setParentheses($parentheses)
4467

4568
public function getNodes()
4669
{
70+
if (count($this->localVariables)) {
71+
$localVariables = 'array(' . implode(', ', array_map(function ($data) {
72+
return 'array(' . var_export($data[0], true) . ',' . var_export($data[1], true) . ')';
73+
}, $this->localVariables)) . ')';
74+
$foreach = new static('foreach', $localVariables . ' as $data');
75+
$while = new static('while', '($prev = $name) && ($name = $prefix . "l_" . $name) && isset($$prev)');
76+
$while->addNodes(
77+
'$$prev = $$name',
78+
'unset($$name)'
79+
);
80+
$foreach->addNodes(
81+
'list($name, $prefix) = $data',
82+
$while
83+
);
84+
$this->addNode($foreach);
85+
}
86+
4787
return $this->nodes;
4888
}
4989

src/JsPhpize/Nodes/Main.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@
44

55
class Main extends Block
66
{
7-
public function __construct($parentheses = null)
7+
protected $helperPrefix;
8+
9+
public function __construct($helperPrefix, $parentheses = null)
810
{
11+
$this->helperPrefix = $helperPrefix;
912
parent::__construct('main', $parentheses);
1013
}
1114

1215
public function addDependencies($dependencies)
1316
{
1417
foreach ($dependencies as $varname => $dependency) {
15-
array_unshift($this->nodes, '$GLOBALS["__jp_' . $varname . '"] = ' . $dependency);
18+
array_unshift($this->nodes, '$GLOBALS["' . $this->helperPrefix . $varname . '"] = ' . $dependency);
1619
}
1720
}
1821
}

src/JsPhpize/Nodes/Parenthesis.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,29 @@
22

33
namespace JsPhpize\Nodes;
44

5-
class Parenthesis extends Block
5+
class Parenthesis
66
{
7+
/**
8+
* @var array
9+
*/
10+
protected $nodes;
11+
12+
public function __construct()
13+
{
14+
$this->nodes = array();
15+
}
16+
17+
public function addNodes($nodes)
18+
{
19+
$nodes = array_filter(is_array($nodes) ? $nodes : func_get_args());
20+
$this->nodes = array_merge($this->nodes, $nodes);
21+
}
22+
23+
public function addNode()
24+
{
25+
$this->addNodes(func_get_args());
26+
}
27+
728
public function __toString()
829
{
930
return implode(' ', $this->nodes);

src/JsPhpize/Parser/Parser.php

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ class Parser extends Visitor
2828
*/
2929
protected $dependencies;
3030

31+
/**
32+
* @var array
33+
*/
34+
protected $stack;
35+
3136
public function __construct(JsPhpize $engine, $input, $filename)
3237
{
3338
$input = str_replace(array("\r\n", "\r"), array("\n", ''), $input);
@@ -60,7 +65,7 @@ protected function getHelper($helper)
6065
));
6166
}
6267

63-
return '$GLOBALS["__jp_' . $helper . '"]';
68+
return '$GLOBALS["' . $this->engine->getOption('varPrefix', JsPhpize::VAR_PREFIX) . 'h_' . $helper . '"]';
6469
}
6570

6671
protected function exceptionInfos()
@@ -124,13 +129,26 @@ protected function expect($type)
124129
return $token;
125130
}
126131

127-
public function parse()
132+
protected function getCurrentBlock()
133+
{
134+
return end($this->stack);
135+
}
136+
137+
public function parseBlock($block)
128138
{
129-
$block = new Main();
139+
$this->stack[] = $block;
140+
$prev = null;
130141
while ($token = $this->next()) {
142+
if ($token === $prev) {
143+
$this->unexpected($token);
144+
}
145+
$prev = $token;
131146
if ($token->type === ';') {
132147
continue;
133148
}
149+
if ($token->type === '}') {
150+
return;
151+
}
134152
$method = 'visit' . ucfirst($token->type);
135153
$token = method_exists($this, $method)
136154
? $this->$method($token)
@@ -139,10 +157,17 @@ public function parse()
139157
$token = array($token);
140158
}
141159
$block->addNodes($token);
142-
// if (!method_exists($this, $method)) {
143-
// $this->unexpected($token);
144-
// }
145-
// $block->addNodes((array) $this->$method($token));
160+
}
161+
array_pop($this->stack);
162+
}
163+
164+
public function parse()
165+
{
166+
$block = new Main($this->engine->getOption('varPrefix', JsPhpize::VAR_PREFIX) . 'h_');
167+
$this->stack = array();
168+
$this->parseBlock($block);
169+
if ($this->next()) {
170+
$this->unexpected('}');
146171
}
147172
$block->addDependencies($this->dependencies);
148173

src/JsPhpize/Parser/Visitor.php

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace JsPhpize\Parser;
44

5+
use JsPhpize\JsPhpize;
6+
use JsPhpize\Nodes\Block;
57
use JsPhpize\Nodes\Comment;
68

79
class Visitor extends ExpressionParser
@@ -11,6 +13,43 @@ protected function visitComment($token)
1113
return new Comment($token);
1214
}
1315

16+
public function visitLet($token)
17+
{
18+
$variable = $this->current();
19+
if (!$variable || $variable->type !== 'variable') {
20+
$this->unexpected($variable);
21+
}
22+
$varPrefix = $this->engine->getOption('varPrefix', JsPhpize::VAR_PREFIX);
23+
24+
$this->getCurrentBlock()->let($variable->value, $varPrefix);
25+
26+
return $this->getExpression();
27+
}
28+
29+
public function visitKeyword($token)
30+
{
31+
if (method_exists($this, $method = 'visit' . ucfirst($token->value))) {
32+
return $this->$method($token);
33+
}
34+
35+
if (($next = $this->current()) && in_array($next->type, array('(', '{'))) {
36+
$block = new Block($token->value);
37+
if ($next->type === '(') {
38+
$this->skip();
39+
$block->setParentheses($this->getExpression());
40+
$this->skip();
41+
}
42+
if (($next = $this->current()) && $next->type === '{') {
43+
$this->skip();
44+
$this->parseBlock($block);
45+
}
46+
47+
return $block;
48+
}
49+
50+
return $token->value . ' ' . $this->getExpression();
51+
}
52+
1453
protected function visitNode($token)
1554
{
1655
$this->prepend($token);
@@ -65,9 +104,4 @@ protected function visitVariable($token)
65104

66105
return 'call_user_func(' . $this->getHelper('dot') . ', ' . implode(', ', $variableParts) . ')';
67106
}
68-
69-
public function visitReturn($token)
70-
{
71-
return 'return ' . $this->getExpression();
72-
}
73107
}

0 commit comments

Comments
 (0)