Skip to content

Commit 4f94f6d

Browse files
committed
implement the declare node
1 parent 658f396 commit 4f94f6d

File tree

6 files changed

+242
-53
lines changed

6 files changed

+242
-53
lines changed

docs/AST.md

Lines changed: 92 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,6 @@
22

33
# AST
44

5-
The AST builder class
6-
7-
**Parameters**
8-
9-
- `withPositions`
10-
- `withSource`
11-
12-
**Properties**
13-
14-
- `withPositions` **[Boolean](#boolean)** Should locate any node (by default false)
15-
- `withSource` **[Boolean](#boolean)** Should extract the node original code (by default false)
16-
17-
## position
18-
19-
Create a position node from specified parser
20-
including it's lexer current state
21-
22-
**Parameters**
23-
24-
- `Parser`
25-
- `parser`
26-
27-
Returns **[Position](#position)**
28-
29-
## prepare
30-
31-
Prepares an AST node
32-
33-
**Parameters**
34-
35-
- `kind` **([String](#string) | null)** Defines the node type
36-
(if null, the kind must be passed at the function call)
37-
- `parser` **Parser** The parser instance (use for extracting locations)
38-
39-
Returns **[Function](#function)**
40-
41-
# AST
42-
435
## Class hierarchy
446

457
- [Location](#location)
@@ -82,6 +44,7 @@ Returns **[Function](#function)**
8244
- [Eval](#eval)
8345
- [Exit](#exit)
8446
- [Clone](#clone)
47+
- [Declare](#declare)
8548
- [Global](#global)
8649
- [Static](#static)
8750
- [Include](#include)
@@ -155,6 +118,44 @@ Prepares an AST node
155118

156119
Returns **[Function](#function)**
157120

121+
# AST
122+
123+
The AST builder class
124+
125+
**Parameters**
126+
127+
- `withPositions`
128+
- `withSource`
129+
130+
**Properties**
131+
132+
- `withPositions` **[Boolean](#boolean)** Should locate any node (by default false)
133+
- `withSource` **[Boolean](#boolean)** Should extract the node original code (by default false)
134+
135+
## position
136+
137+
Create a position node from specified parser
138+
including it's lexer current state
139+
140+
**Parameters**
141+
142+
- `Parser`
143+
- `parser`
144+
145+
Returns **[Position](#position)**
146+
147+
## prepare
148+
149+
Prepares an AST node
150+
151+
**Parameters**
152+
153+
- `kind` **([String](#string) | null)** Defines the node type
154+
(if null, the kind must be passed at the function call)
155+
- `parser` **Parser** The parser instance (use for extracting locations)
156+
157+
Returns **[Function](#function)**
158+
158159
# Array
159160

160161
**Extends Expression**
@@ -396,6 +397,59 @@ Generic flags parser
396397

397398
Returns **void**
398399

400+
# Declare
401+
402+
**Extends Block**
403+
404+
The declare construct is used to set execution directives for a block of code
405+
406+
**Properties**
407+
408+
- `what` **[Array](#array)<[Expression](#expression)>**
409+
- `mode` **[String](#string)**
410+
411+
## MODE_SHORT
412+
413+
The node is declared as a short tag syntax :
414+
415+
```php
416+
<?php
417+
declare(ticks=1):
418+
// some statements
419+
enddeclare;
420+
```
421+
422+
Type: [String](#string)
423+
424+
## MODE_BLOCK
425+
426+
The node is declared bracket enclosed code :
427+
428+
```php
429+
<?php
430+
declare(ticks=1) {
431+
// some statements
432+
}
433+
```
434+
435+
Type: [String](#string)
436+
437+
## MODE_NONE
438+
439+
The node is declared as a simple statement. In order to make things simpler
440+
children of the node are automatically collected until the next
441+
declare statement.
442+
443+
```php
444+
<?php
445+
declare(ticks=1);
446+
// some statements
447+
declare(ticks=2);
448+
// some statements
449+
```
450+
451+
Type: [String](#string)
452+
399453
# Do
400454

401455
**Extends Statement**

docs/parser.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ Reads a list of constants declaration
599599
Reads a list of constants declaration
600600

601601
```ebnf
602-
const_list ::= T_CONST T_STRING '=' expr (',' T_STRING '=' expr)*
602+
declare_list ::= T_STRING '=' expr (',' T_STRING '=' expr)*
603603
```
604604

605605
# read_inner_statement

src/ast.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ var Position = require('./ast/position');
5050
* - [Eval](#eval)
5151
* - [Exit](#exit)
5252
* - [Clone](#clone)
53+
* - [Declare](#declare)
5354
* - [Global](#global)
5455
* - [Static](#static)
5556
* - [Include](#include)
@@ -192,6 +193,7 @@ AST.prototype.prepare = function(kind, parser) {
192193
require('./ast/constant'),
193194
require('./ast/constref'),
194195
require('./ast/continue'),
196+
require('./ast/declare'),
195197
require('./ast/do'),
196198
require('./ast/doc'),
197199
require('./ast/echo'),

src/ast/declare.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*!
2+
* Copyright (C) 2017 Glayzzle (BSD3 License)
3+
* @authors https://github.com/glayzzle/php-parser/graphs/contributors
4+
* @url http://glayzzle.com
5+
*/
6+
7+
var Block = require('./block');
8+
var KIND = 'declare';
9+
10+
/**
11+
* The declare construct is used to set execution directives for a block of code
12+
* @constructor Declare
13+
* @extends {Block}
14+
* @property {Expression[]} what
15+
* @property {String} mode
16+
* @see http://php.net/manual/en/control-structures.declare.php
17+
*/
18+
var Declare = Block.extends(function Declare(what, body, mode, location) {
19+
Block.apply(this, [KIND, body, location]);
20+
this.what = what;
21+
this.mode = mode;
22+
});
23+
24+
25+
/**
26+
* The node is declared as a short tag syntax :
27+
* ```php
28+
* <?php
29+
* declare(ticks=1):
30+
* // some statements
31+
* enddeclare;
32+
* ```
33+
* @constant {String} MODE_SHORT
34+
*/
35+
Declare.MODE_SHORT = 'short';
36+
37+
/**
38+
* The node is declared bracket enclosed code :
39+
* ```php
40+
* <?php
41+
* declare(ticks=1) {
42+
* // some statements
43+
* }
44+
* ```
45+
* @constant {String} MODE_BLOCK
46+
*/
47+
Declare.MODE_BLOCK = 'block';
48+
49+
/**
50+
* The node is declared as a simple statement. In order to make things simpler
51+
* children of the node are automatically collected until the next
52+
* declare statement.
53+
* ```php
54+
* <?php
55+
* declare(ticks=1);
56+
* // some statements
57+
* declare(ticks=2);
58+
* // some statements
59+
* ```
60+
* @constant {String} MODE_NONE
61+
*/
62+
Declare.MODE_NONE = 'none';
63+
64+
module.exports = Declare;

src/parser/statement.js

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -118,19 +118,24 @@ module.exports = {
118118
/**
119119
* Reads a list of constants declaration
120120
* ```ebnf
121-
* const_list ::= T_CONST T_STRING '=' expr (',' T_STRING '=' expr)*
121+
* declare_list ::= T_STRING '=' expr (',' T_STRING '=' expr)*
122122
* ```
123+
* @retrurn {Object}
123124
*/
124125
,read_declare_list: function() {
125-
return this.read_list(function() {
126+
var result = {};
127+
while(this.token != this.EOF && this.token !== ')') {
126128
this.expect(this.tok.T_STRING);
127-
var name = this.text();
129+
var name = this.text().toLowerCase();
128130
if (this.next().expect('=')) {
129-
return [name, this.next().read_expr()];
131+
result[name] = this.next().read_expr();
130132
} else {
131-
return [name, null];
133+
result[name] = null;
132134
}
133-
}, ',');
135+
if (this.token !== ',') break;
136+
this.next();
137+
}
138+
return result;
134139
}
135140
/**
136141
* reads a simple inner statement
@@ -261,22 +266,36 @@ module.exports = {
261266
return result(items);
262267

263268
case this.tok.T_DECLARE:
264-
var result = this.node('declare'), options, body;
269+
var result = this.node('declare'),
270+
what,
271+
body = [],
272+
mode;
265273
this.next().expect('(') && this.next();
266-
options = this.read_declare_list();
267-
this.expect(')') && this.nextWithComments();
274+
what = this.read_declare_list();
275+
this.expect(')') && this.next();
268276
if (this.token === ':') {
269-
body = [];
270-
this.next();
277+
this.nextWithComments();
271278
while(this.token != this.EOF && this.token !== this.tok.T_ENDDECLARE) {
272279
body.push(this.read_statement());
273280
}
274-
this.ignoreComments().expect(this.tok.T_ENDDECLARE) && this.next();
281+
this.expect(this.tok.T_ENDDECLARE) && this.next();
275282
this.expectEndOfStatement();
283+
mode = this.ast.declare.MODE_SHORT;
284+
} else if (this.token === '{') {
285+
this.nextWithComments();
286+
while(this.token != this.EOF && this.token !== '}') {
287+
body.push(this.read_statement());
288+
}
289+
this.expect('}') && this.next();
290+
mode = this.ast.declare.MODE_BLOCK;
276291
} else {
277-
body = this.read_statement();
292+
this.expect(';') && this.next();
293+
while(this.token != this.EOF && this.token !== this.tok.T_DECLARE) {
294+
body.push(this.read_statement());
295+
}
296+
mode = this.ast.declare.MODE_NONE;
278297
}
279-
return result(options, body);
298+
return result(what, body, mode);
280299
break;
281300

282301
case this.tok.T_TRY:

test/statementTests.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,56 @@ describe('Test statements', function() {
6060

6161
});
6262

63+
it('test declare', function() {
64+
var ast = parser.parseEval([
65+
'declare(ticks=1);',
66+
'$a = 1;',
67+
'declare(ticks=2,encoding="ISO-8859-1");',
68+
'$b = 1;',
69+
'declare(ticks=1) {',
70+
' $c = 2;',
71+
'}',
72+
'declare(encoding="UTF-8"):',
73+
' $d = 3;',
74+
'enddeclare;',
75+
'$e = 4;' // <- single statement
76+
].join('\n'), {
77+
parser: { debug: false }
78+
});
79+
ast.children.length.should.be.exactly(5);
80+
81+
ast.children[0].kind.should.be.exactly('declare');
82+
ast.children[0].mode.should.be.exactly('none');
83+
ast.children[0].children.length.should.be.exactly(1);
84+
ast.children[0].children[0].left.name.should.be.exactly('a');
85+
ast.children[0].what.ticks.kind.should.be.exactly('number');
86+
ast.children[0].what.ticks.value.should.be.exactly('1');
87+
88+
ast.children[1].kind.should.be.exactly('declare');
89+
ast.children[1].mode.should.be.exactly('none');
90+
ast.children[1].children.length.should.be.exactly(1);
91+
ast.children[1].children[0].left.name.should.be.exactly('b');
92+
ast.children[1].what.ticks.kind.should.be.exactly('number');
93+
ast.children[1].what.ticks.value.should.be.exactly('2');
94+
ast.children[1].what.encoding.kind.should.be.exactly('string');
95+
ast.children[1].what.encoding.value.should.be.exactly('ISO-8859-1');
96+
97+
ast.children[2].kind.should.be.exactly('declare');
98+
ast.children[2].mode.should.be.exactly('block');
99+
ast.children[2].children.length.should.be.exactly(1);
100+
ast.children[2].children[0].left.name.should.be.exactly('c');
101+
ast.children[2].what.ticks.kind.should.be.exactly('number');
102+
ast.children[2].what.ticks.value.should.be.exactly('1');
103+
104+
ast.children[3].kind.should.be.exactly('declare');
105+
ast.children[3].mode.should.be.exactly('short');
106+
ast.children[3].children.length.should.be.exactly(1);
107+
ast.children[3].children[0].left.name.should.be.exactly('d');
108+
ast.children[3].what.encoding.kind.should.be.exactly('string');
109+
ast.children[3].what.encoding.value.should.be.exactly('UTF-8');
110+
111+
ast.children[4].kind.should.be.exactly('assign');
112+
});
63113

64114
it('test try', function() {
65115
var ast = parser.parseEval([

0 commit comments

Comments
 (0)