Skip to content

Commit

Permalink
Merge pull request #389 from glayzzle/refactor-extends-and-implements
Browse files Browse the repository at this point in the history
refactor: extends and implements
  • Loading branch information
ichiriac authored Aug 31, 2019
2 parents f05e332 + 980cf46 commit 2cb5e99
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 27 deletions.
19 changes: 5 additions & 14 deletions src/parser/class.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = {
* class ::= class_scope? T_CLASS T_STRING (T_EXTENDS NAMESPACE_NAME)? (T_IMPLEMENTS (NAMESPACE_NAME ',')* NAMESPACE_NAME)? '{' CLASS_BODY '}'
* ```
*/
read_class: function() {
read_class_declaration_statement: function() {
const result = this.node("class");
const flag = this.read_class_scope();
// graceful mode : ignore token & go next
Expand All @@ -26,14 +26,8 @@ module.exports = {
const name = this.text();
this.next();
propName = propName(name);
let propExtends = null;
if (this.token == this.tok.T_EXTENDS) {
propExtends = this.next().read_namespace_name();
}
let propImplements = null;
if (this.token == this.tok.T_IMPLEMENTS) {
propImplements = this.next().read_name_list();
}
const propExtends = this.read_extends_from();
const propImplements = this.read_implements_list();
this.expect("{");
const body = this.next().read_class_body();
return result(propName, propExtends, propImplements, body, flag);
Expand Down Expand Up @@ -278,7 +272,7 @@ module.exports = {
* interface ::= T_INTERFACE T_STRING (T_EXTENDS (NAMESPACE_NAME ',')* NAMESPACE_NAME)? '{' INTERFACE_BODY '}'
* ```
*/
read_interface: function() {
read_interface_declaration_statement: function() {
const result = this.node("interface");
if (this.token !== this.tok.T_INTERFACE) {
this.error(this.tok.T_INTERFACE);
Expand All @@ -290,10 +284,7 @@ module.exports = {
const name = this.text();
this.next();
propName = propName(name);
let propExtends = null;
if (this.token === this.tok.T_EXTENDS) {
propExtends = this.next().read_name_list();
}
const propExtends = this.read_interface_extends_list();
this.expect("{");
const body = this.next().read_interface_body();
return result(propName, propExtends, body);
Expand Down
13 changes: 4 additions & 9 deletions src/parser/expr.js
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ module.exports = {
// returns variable | scalar
return expr;
},

/**
* ```ebnf
* new_expr ::= T_NEW (namespace_name function_argument_list) | (T_CLASS ... class declaration)
Expand All @@ -469,18 +470,12 @@ module.exports = {
if (this.token === this.tok.T_CLASS) {
const what = this.node("class");
// Annonymous class declaration
let propExtends = null,
propImplements = null,
body = null;
if (this.next().token === "(") {
args = this.read_function_argument_list();
}
if (this.token == this.tok.T_EXTENDS) {
propExtends = this.next().read_namespace_name();
}
if (this.token == this.tok.T_IMPLEMENTS) {
propImplements = this.next().read_name_list();
}
const propExtends = this.read_extends_from();
const propImplements = this.read_implements_list();
let body = null;
if (this.expect("{")) {
body = this.next().read_class_body();
}
Expand Down
8 changes: 4 additions & 4 deletions src/parser/statement.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ module.exports = {
case this.tok.T_ABSTRACT:
case this.tok.T_FINAL:
case this.tok.T_CLASS:
return this.read_class();
return this.read_class_declaration_statement();
case this.tok.T_INTERFACE:
return this.read_interface();
return this.read_interface_declaration_statement();
case this.tok.T_TRAIT:
return this.read_trait();
case this.tok.T_USE:
Expand Down Expand Up @@ -157,9 +157,9 @@ module.exports = {
case this.tok.T_ABSTRACT:
case this.tok.T_FINAL:
case this.tok.T_CLASS:
return this.read_class();
return this.read_class_declaration_statement();
case this.tok.T_INTERFACE:
return this.read_interface();
return this.read_interface_declaration_statement();
case this.tok.T_TRAIT:
return this.read_trait();
case this.tok.T_HALT_COMPILER: {
Expand Down
33 changes: 33 additions & 0 deletions src/parser/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,38 @@ module.exports = {
return variable;
}
}, ",");
},

/*
* Reads class extends
*/
read_extends_from: function() {
if (this.token === this.tok.T_EXTENDS) {
return this.next().read_namespace_name();
}

return null;
},

/*
* Reads interface extends list
*/
read_interface_extends_list: function() {
if (this.token === this.tok.T_EXTENDS) {
return this.next().read_name_list();
}

return null;
},

/*
* Reads implements list
*/
read_implements_list: function() {
if (this.token === this.tok.T_IMPLEMENTS) {
return this.next().read_name_list();
}

return null;
}
};
127 changes: 127 additions & 0 deletions test/snapshot/__snapshots__/new.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,133 @@ Program {
}
`;

exports[`new anonymous class #2 1`] = `
Program {
"children": Array [
ExpressionStatement {
"expression": Assign {
"kind": "assign",
"left": Variable {
"curly": false,
"kind": "variable",
"name": "var",
},
"operator": "=",
"right": New {
"arguments": Array [
Variable {
"curly": false,
"kind": "variable",
"name": "var",
},
],
"kind": "new",
"what": Class {
"body": Array [],
"extends": null,
"implements": null,
"isAbstract": false,
"isAnonymous": true,
"isFinal": false,
"kind": "class",
"name": null,
},
},
},
"kind": "expressionstatement",
},
],
"errors": Array [],
"kind": "program",
}
`;

exports[`new anonymous class #3 1`] = `
Program {
"children": Array [
ExpressionStatement {
"expression": Assign {
"kind": "assign",
"left": Variable {
"curly": false,
"kind": "variable",
"name": "var",
},
"operator": "=",
"right": New {
"arguments": Array [
Variable {
"curly": false,
"kind": "variable",
"name": "var",
},
],
"kind": "new",
"what": Class {
"body": Array [],
"extends": ClassReference {
"kind": "classreference",
"name": "SomeClass",
"resolution": "uqn",
},
"implements": Array [
ClassReference {
"kind": "classreference",
"name": "SomeInterface",
"resolution": "uqn",
},
],
"isAbstract": false,
"isAnonymous": true,
"isFinal": false,
"kind": "class",
"name": null,
},
},
},
"kind": "expressionstatement",
},
],
"errors": Array [],
"kind": "program",
}
`;

exports[`new anonymous class 1`] = `
Program {
"children": Array [
ExpressionStatement {
"expression": Assign {
"kind": "assign",
"left": Variable {
"curly": false,
"kind": "variable",
"name": "var",
},
"operator": "=",
"right": New {
"arguments": Array [],
"kind": "new",
"what": Class {
"body": Array [],
"extends": null,
"implements": null,
"isAbstract": false,
"isAnonymous": true,
"isFinal": false,
"kind": "class",
"name": null,
},
},
},
"kind": "expressionstatement",
},
],
"errors": Array [],
"kind": "program",
}
`;

exports[`new anonymous no parens 1`] = `
Program {
"children": Array [
Expand Down
9 changes: 9 additions & 0 deletions test/snapshot/new.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,13 @@ describe("new", function() {
it("static", function() {
expect(parser.parseEval('new static();')).toMatchSnapshot();
});
it("anonymous class", function() {
expect(parser.parseEval('$var = new class {};')).toMatchSnapshot();
});
it("anonymous class #2", function() {
expect(parser.parseEval('$var = new class($var) {};')).toMatchSnapshot();
});
it("anonymous class #3", function() {
expect(parser.parseEval('$var = new class($var) extends SomeClass implements SomeInterface {};')).toMatchSnapshot();
});
});

0 comments on commit 2cb5e99

Please sign in to comment.