Skip to content

Commit e85f9f8

Browse files
authored
Merge pull request glayzzle#862 from czosel/add-enum-support
Add enum support
2 parents e527375 + 0c17887 commit e85f9f8

File tree

12 files changed

+675
-15
lines changed

12 files changed

+675
-15
lines changed

src/ast.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,8 @@ AST.prototype.checkNodes = function () {
504504
require("./ast/encapsed"),
505505
require("./ast/encapsedpart"),
506506
require("./ast/entry"),
507+
require("./ast/enum"),
508+
require("./ast/enumcase"),
507509
require("./ast/error"),
508510
require("./ast/eval"),
509511
require("./ast/exit"),

src/ast/enum.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Copyright (C) 2018 Glayzzle (BSD3 License)
3+
* @authors https://github.com/glayzzle/php-parser/graphs/contributors
4+
* @url http://glayzzle.com
5+
*/
6+
"use strict";
7+
8+
const Declaration = require("./declaration");
9+
const KIND = "enum";
10+
11+
/**
12+
* A enum definition
13+
* @constructor Enum
14+
* @memberOf module:php-parser
15+
* @extends {Declaration}
16+
* @property {Identifier|null} valueType
17+
* @property {Identifier[]} implements
18+
* @property {Declaration[]} body
19+
* @property {AttrGroup[]} attrGroups
20+
*/
21+
module.exports = Declaration.extends(
22+
KIND,
23+
function Enum(name, valueType, impl, body, docs, location) {
24+
Declaration.apply(this, [KIND, name, docs, location]);
25+
this.valueType = valueType;
26+
this.implements = impl;
27+
this.body = body;
28+
this.attrGroups = [];
29+
}
30+
);

src/ast/enumcase.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Copyright (C) 2018 Glayzzle (BSD3 License)
3+
* @authors https://github.com/glayzzle/php-parser/graphs/contributors
4+
* @url http://glayzzle.com
5+
*/
6+
"use strict";
7+
8+
const Node = require("./node");
9+
const KIND = "enumcase";
10+
11+
/**
12+
* Declares a cases into the current scope
13+
* @constructor EnumCase
14+
* @memberOf module:php-parser
15+
* @extends {Node}
16+
* @property {string} name
17+
* @property {string|number|null} value
18+
*/
19+
module.exports = Node.extends(
20+
KIND,
21+
function EnumCase(name, value, docs, location) {
22+
Node.apply(this, [KIND, docs, location]);
23+
this.name = name;
24+
this.value = value;
25+
}
26+
);

src/lexer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ const Lexer = function (engine) {
7777
class: this.tok.T_CLASS,
7878
interface: this.tok.T_INTERFACE,
7979
trait: this.tok.T_TRAIT,
80+
enum: this.tok.T_ENUM,
8081
extends: this.tok.T_EXTENDS,
8182
implements: this.tok.T_IMPLEMENTS,
8283
new: this.tok.T_NEW,

src/parser.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ const Parser = function (lexer, ast) {
7373
this.tok.T_ENDIF,
7474
this.tok.T_ENDSWITCH,
7575
this.tok.T_ENDWHILE,
76+
this.tok.T_ENUM,
7677
this.tok.T_EVAL,
7778
this.tok.T_EXIT,
7879
this.tok.T_EXTENDS,
@@ -700,6 +701,7 @@ Parser.prototype.is = function (type) {
700701
require("./parser/class.js"),
701702
require("./parser/comment.js"),
702703
require("./parser/expr.js"),
704+
require("./parser/enum.js"),
703705
require("./parser/function.js"),
704706
require("./parser/if.js"),
705707
require("./parser/loops.js"),

src/parser/class.js

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ module.exports = {
2929
const propExtends = this.read_extends_from();
3030
const propImplements = this.read_implements_list();
3131
this.expect("{");
32-
const body = this.next().read_class_body();
32+
const body = this.next().read_class_body(true, false);
3333
const node = result(propName, propExtends, propImplements, body, flag);
3434
if (attrs) node.attrGroups = attrs;
3535
return node;
@@ -59,7 +59,7 @@ module.exports = {
5959
* class_body ::= (member_flags? (T_VAR | T_STRING | T_FUNCTION))*
6060
* ```
6161
*/
62-
read_class_body: function () {
62+
read_class_body: function (allow_variables, allow_enum_cases) {
6363
let result = [];
6464
let attrs = [];
6565
while (this.token !== this.EOF && this.token !== "}") {
@@ -79,6 +79,16 @@ module.exports = {
7979
continue;
8080
}
8181

82+
// check enum cases
83+
if (allow_enum_cases && this.token === this.tok.T_CASE) {
84+
const enumcase = this.read_enum_case();
85+
if (this.expect(";")) {
86+
this.next();
87+
}
88+
result = result.concat(enumcase);
89+
continue;
90+
}
91+
8292
if (this.token === this.tok.T_ATTRIBUTE) {
8393
attrs = this.read_attr_list();
8494
}
@@ -99,7 +109,7 @@ module.exports = {
99109
}
100110

101111
// jump over T_VAR then land on T_VARIABLE
102-
if (this.token === this.tok.T_VAR) {
112+
if (allow_variables && this.token === this.tok.T_VAR) {
103113
this.next().expect(this.tok.T_VARIABLE);
104114
flags[0] = null; // public (as null)
105115
flags[1] = 0; // non static var
@@ -110,15 +120,16 @@ module.exports = {
110120
result.push(this.read_function(false, flags, attrs, locStart));
111121
attrs = [];
112122
} else if (
113-
this.token === this.tok.T_VARIABLE ||
114-
// support https://wiki.php.net/rfc/typed_properties_v2
115-
(this.version >= 704 &&
116-
(this.token === "?" ||
117-
this.token === this.tok.T_CALLABLE ||
118-
this.token === this.tok.T_ARRAY ||
119-
this.token === this.tok.T_NS_SEPARATOR ||
120-
this.token === this.tok.T_STRING ||
121-
this.token === this.tok.T_NAMESPACE))
123+
allow_variables &&
124+
(this.token === this.tok.T_VARIABLE ||
125+
// support https://wiki.php.net/rfc/typed_properties_v2
126+
(this.version >= 704 &&
127+
(this.token === "?" ||
128+
this.token === this.tok.T_CALLABLE ||
129+
this.token === this.tok.T_ARRAY ||
130+
this.token === this.tok.T_NS_SEPARATOR ||
131+
this.token === this.tok.T_STRING ||
132+
this.token === this.tok.T_NAMESPACE)))
122133
) {
123134
// reads a variable
124135
const variables = this.read_variable_list(flags, attrs);
@@ -130,7 +141,8 @@ module.exports = {
130141
// raise an error
131142
this.error([
132143
this.tok.T_CONST,
133-
this.tok.T_VARIABLE,
144+
...(allow_variables ? [this.tok.T_VARIABLE] : []),
145+
...(allow_enum_cases ? [this.tok.T_CASE] : []),
134146
this.tok.T_FUNCTION,
135147
]);
136148
// ignore token
@@ -452,7 +464,7 @@ module.exports = {
452464
this.next();
453465
propName = propName(name);
454466
this.expect("{");
455-
const body = this.next().read_class_body();
467+
const body = this.next().read_class_body(true, false);
456468
return result(propName, body);
457469
},
458470
/*

src/parser/enum.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Copyright (C) 2018 Glayzzle (BSD3 License)
3+
* @authors https://github.com/glayzzle/php-parser/graphs/contributors
4+
* @url http://glayzzle.com
5+
*/
6+
"use strict";
7+
8+
module.exports = {
9+
/*
10+
* reading an enum
11+
* ```ebnf
12+
* enum ::= enum_scope? T_ENUM T_STRING (':' NAMESPACE_NAME)? (T_IMPLEMENTS (NAMESPACE_NAME ',')* NAMESPACE_NAME)? '{' ENUM_BODY '}'
13+
* ```
14+
*/
15+
read_enum_declaration_statement: function (attrs) {
16+
const result = this.node("enum");
17+
// graceful mode : ignore token & go next
18+
if (!this.expect(this.tok.T_ENUM)) {
19+
return null;
20+
}
21+
this.next().expect(this.tok.T_STRING);
22+
let propName = this.node("identifier");
23+
const name = this.text();
24+
this.next();
25+
propName = propName(name);
26+
const valueType = this.read_enum_value_type();
27+
const propImplements = this.read_implements_list();
28+
this.expect("{");
29+
const body = this.next().read_class_body(false, true);
30+
const node = result(propName, valueType, propImplements, body);
31+
if (attrs) node.attrGroups = attrs;
32+
return node;
33+
},
34+
35+
read_enum_value_type: function () {
36+
if (this.token === ":") {
37+
return this.next().read_namespace_name();
38+
}
39+
40+
return null;
41+
},
42+
43+
read_enum_case: function () {
44+
this.expect(this.tok.T_CASE);
45+
const result = this.node("enumcase");
46+
let caseName = this.node("identifier");
47+
const name = this.next().text();
48+
this.next();
49+
caseName = caseName(name);
50+
51+
const value = this.token === "=" ? this.next().read_expr() : null;
52+
this.expect(";");
53+
54+
return result(caseName, value);
55+
},
56+
};

src/parser/expr.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,7 @@ module.exports = {
724724
const propImplements = this.read_implements_list();
725725
let body = null;
726726
if (this.expect("{")) {
727-
body = this.next().read_class_body();
727+
body = this.next().read_class_body(true, false);
728728
}
729729
const whatNode = what(null, propExtends, propImplements, body, [0, 0, 0]);
730730
whatNode.attrGroups = attrs;

src/parser/statement.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ module.exports = {
5353
return this.read_interface_declaration_statement(attrs);
5454
case this.tok.T_TRAIT:
5555
return this.read_trait_declaration_statement();
56+
case this.tok.T_ENUM:
57+
return this.read_enum_declaration_statement(attrs);
5658
case this.tok.T_USE:
5759
return this.read_use_statement();
5860
case this.tok.T_CONST: {
@@ -173,6 +175,8 @@ module.exports = {
173175
return this.read_interface_declaration_statement();
174176
case this.tok.T_TRAIT:
175177
return this.read_trait_declaration_statement();
178+
case this.tok.T_ENUM:
179+
return this.read_enum_declaration_statement();
176180
case this.tok.T_HALT_COMPILER: {
177181
this.raiseError(
178182
"__HALT_COMPILER() can only be used from the outermost scope"

src/tokens.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ const tokens = {
154154
236: "T_NULLSAFE_OBJECT_OPERATOR",
155155
237: "T_MATCH",
156156
238: "T_ATTRIBUTE",
157+
239: "T_ENUM",
157158
},
158159
names: {
159160
T_HALT_COMPILER: 101,
@@ -294,6 +295,7 @@ const tokens = {
294295
T_NULLSAFE_OBJECT_OPERATOR: 236,
295296
T_MATCH: 237,
296297
T_ATTRIBUTE: 238,
298+
T_ENUM: 239,
297299
},
298300
};
299301

0 commit comments

Comments
 (0)