Skip to content

Commit 7d8b38a

Browse files
author
John Messerly
committed
js_ast: implement rest/spread parsing
[email protected] Review URL: https://codereview.chromium.org/1183033004.
1 parent 4c9fb31 commit 7d8b38a

File tree

10 files changed

+139
-53
lines changed

10 files changed

+139
-53
lines changed

pkg/dev_compiler/lib/runtime/_operations.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,6 @@ dart_library.library('dart_runtime/_operations', null, /* Imports */[
162162
}
163163
exports.dsend = dsend;
164164

165-
function dsendArray(obj, method, args) {
166-
return dsend(obj, method, ...args);
167-
}
168-
exports.dsendArray = dsendArray;
169-
170165
function dindex(obj, index) {
171166
return callMethod(obj, 'get', [index], '[]');
172167
}

pkg/dev_compiler/lib/runtime/dart/_js_mirrors.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ dart_library.library('dart/_js_mirrors', null, /* Imports */[
4545
}
4646
dart.fn(_dput, dart.void, [core.Object, core.String, core.Object]);
4747
function _dsend(obj, name, args) {
48-
return exports._dart.dsendArray(obj, name, args);
48+
return exports._dart.dsend(obj, name, ...args);
4949
}
5050
dart.fn(_dsend, core.Object, [core.Object, core.String, core.List]);
5151
let _toJsMap = Symbol('_toJsMap');

pkg/dev_compiler/lib/runtime/dart_runtime.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ dart_library.library('dart_runtime/dart', null, /* Imports */[
7373
'dload',
7474
'dput',
7575
'dsend',
76-
'dsendArray',
7776
'dsetindex',
7877
'equals',
7978
'hashCode',

pkg/dev_compiler/lib/src/js/builder.dart

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -450,12 +450,16 @@ class MiniJsParser {
450450
static const COLON = 14;
451451
static const SEMICOLON = 15;
452452
static const ARROW = 16;
453-
static const HASH = 17;
454-
static const WHITESPACE = 18;
455-
static const OTHER = 19;
453+
static const ELLIPSIS = 17;
454+
static const HASH = 18;
455+
static const WHITESPACE = 19;
456+
static const OTHER = 20;
456457

457458
// Make sure that ]] is two symbols.
458-
bool singleCharCategory(int category) => category >= DOT;
459+
// TODO(jmesserly): => and ... are not single char tokens, should we change
460+
// their numbers? It shouldn't matter because this is only called on values
461+
// from the [CATEGORIES] table.
462+
bool singleCharCategory(int category) => category > DOT;
459463

460464
static String categoryToString(int cat) {
461465
switch (cat) {
@@ -477,6 +481,7 @@ class MiniJsParser {
477481
case COLON: return "COLON";
478482
case SEMICOLON: return "SEMICOLON";
479483
case ARROW: return "ARROW";
484+
case ELLIPSIS: return "ELLIPSIS";
480485
case HASH: return "HASH";
481486
case WHITESPACE: return "WHITESPACE";
482487
case OTHER: return "OTHER";
@@ -528,6 +533,7 @@ class MiniJsParser {
528533
.toSet();
529534

530535
static final ARROW_TOKEN = '=>';
536+
static final ELLIPSIS_TOKEN = '...';
531537

532538
static final OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS =
533539
['typeof', 'void', 'delete', 'in', 'instanceof', 'await'].toSet();
@@ -642,6 +648,12 @@ class MiniJsParser {
642648
double.parse(lastToken, (_) {
643649
error("Unparseable number");
644650
});
651+
} else if (cat == DOT && lastToken.length > 1) {
652+
if (lastToken == ELLIPSIS_TOKEN) {
653+
lastCategory = ELLIPSIS;
654+
} else {
655+
error("Unknown operator");
656+
}
645657
} else if (cat == SYMBOL) {
646658
if (lastToken == ARROW_TOKEN) {
647659
lastCategory = ARROW;
@@ -792,26 +804,42 @@ class MiniJsParser {
792804
Expression parseExpressionOrArrowFunction() {
793805
if (acceptCategory(RPAREN)) {
794806
expectCategory(ARROW);
795-
return parseArrowFunctionBody(<Identifier>[]);
807+
return parseArrowFunctionBody(<Parameter>[]);
808+
}
809+
if (acceptCategory(ELLIPSIS)) {
810+
var params = <Parameter>[new RestParameter(parseParameter())];
811+
expectCategory(RPAREN);
812+
expectCategory(ARROW);
813+
return parseArrowFunctionBody(params);
814+
}
815+
Expression expression = parseAssignment();
816+
while (acceptCategory(COMMA)) {
817+
if (acceptCategory(ELLIPSIS)) {
818+
var params = <Parameter>[];
819+
_expressionToParameterList(expression, params);
820+
params.add(new RestParameter(parseParameter()));
821+
expectCategory(RPAREN);
822+
expectCategory(ARROW);
823+
return parseArrowFunctionBody(params);
824+
}
825+
Expression right = parseAssignment();
826+
expression = new Binary(',', expression, right);
796827
}
797-
Expression expression = parseExpression();
798828
expectCategory(RPAREN);
799829
if (acceptCategory(ARROW)) {
800-
var params = <Identifier>[];
830+
var params = <Parameter>[];
801831
_expressionToParameterList(expression, params);
802832
return parseArrowFunctionBody(params);
803833
}
804834
return expression;
805-
806835
}
807836

808837
/**
809838
* Converts a parenthesized expression into a list of parameters, issuing an
810839
* error if the conversion fails.
811840
*/
812-
void _expressionToParameterList(Expression node, List<Identifier> params) {
841+
void _expressionToParameterList(Expression node, List<Parameter> params) {
813842
if (node is Identifier) {
814-
// TODO(jmesserly): support default/rest parameters
815843
params.add(node);
816844
} else if (node is Binary && node.op == ',') {
817845
// TODO(jmesserly): this will allow illegal parens, such as
@@ -827,7 +855,7 @@ class MiniJsParser {
827855
}
828856
}
829857

830-
Expression parseArrowFunctionBody(List<Identifier> params) {
858+
Expression parseArrowFunctionBody(List<Parameter> params) {
831859
Node body;
832860
if (acceptCategory(LBRACE)) {
833861
body = parseBlock();
@@ -848,25 +876,22 @@ class MiniJsParser {
848876
}
849877

850878
Expression parseFun() {
851-
List<Identifier> params = <Identifier>[];
879+
List<Parameter> params = <Parameter>[];
852880

853881
expectCategory(LPAREN);
854882
if (!acceptCategory(RPAREN)) {
855883
for (;;) {
856-
if (acceptCategory(HASH)) {
857-
var nameOrPosition = parseHash();
858-
InterpolatedParameter parameter =
859-
new InterpolatedParameter(nameOrPosition);
860-
interpolatedValues.add(parameter);
861-
params.add(parameter);
862-
} else {
863-
String argumentName = lastToken;
864-
expectCategory(ALPHA);
865-
params.add(new Identifier(argumentName));
884+
if (acceptCategory(ELLIPSIS)) {
885+
params.add(new RestParameter(parseParameter()));
886+
expectCategory(RPAREN);
887+
break;
888+
}
889+
890+
params.add(parseParameter());
891+
if (!acceptCategory(COMMA)) {
892+
expectCategory(RPAREN);
893+
break;
866894
}
867-
if (acceptCategory(COMMA)) continue;
868-
expectCategory(RPAREN);
869-
break;
870895
}
871896
}
872897
AsyncModifier asyncModifier;
@@ -887,6 +912,21 @@ class MiniJsParser {
887912
return new Fun(params, block, asyncModifier: asyncModifier);
888913
}
889914

915+
/** Parse parameter name or interpolated parameter. */
916+
Identifier parseParameter() {
917+
if (acceptCategory(HASH)) {
918+
var nameOrPosition = parseHash();
919+
var parameter = new InterpolatedParameter(nameOrPosition);
920+
interpolatedValues.add(parameter);
921+
return parameter;
922+
} else {
923+
// TODO(jmesserly): validate this is not a keyword
924+
String argumentName = lastToken;
925+
expectCategory(ALPHA);
926+
return new Identifier(argumentName);
927+
}
928+
}
929+
890930
Expression parseObjectInitializer() {
891931
List<Property> properties = <Property>[];
892932
for (;;) {
@@ -928,8 +968,12 @@ class MiniJsParser {
928968
final arguments = <Expression>[];
929969
if (!acceptCategory(RPAREN)) {
930970
while (true) {
931-
Expression argument = parseAssignment();
932-
arguments.add(argument);
971+
if (acceptCategory(ELLIPSIS)) {
972+
arguments.add(new Spread(parseAssignment()));
973+
expectCategory(RPAREN);
974+
break;
975+
}
976+
arguments.add(parseAssignment());
933977
if (acceptCategory(RPAREN)) break;
934978
expectCategory(COMMA);
935979
}

pkg/dev_compiler/lib/src/js/nodes.dart

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@ abstract class NodeVisitor<T> {
4040
T visitBinary(Binary node);
4141
T visitPrefix(Prefix node);
4242
T visitPostfix(Postfix node);
43+
T visitSpread(Spread node);
4344

4445
T visitIdentifier(Identifier node);
4546
T visitThis(This node);
4647
T visitSuper(Super node);
4748
T visitAccess(PropertyAccess node);
49+
T visitRestParameter(RestParameter node);
4850

4951
T visitNamedFunction(NamedFunction node);
5052
T visitFun(Fun node);
@@ -137,12 +139,15 @@ class BaseVisitor<T> implements NodeVisitor<T> {
137139
T visitBinary(Binary node) => visitExpression(node);
138140
T visitPrefix(Prefix node) => visitExpression(node);
139141
T visitPostfix(Postfix node) => visitExpression(node);
142+
T visitSpread(Spread node) => visitPrefix(node);
140143
T visitAccess(PropertyAccess node) => visitExpression(node);
141144

142145
T visitIdentifier(Identifier node) => visitExpression(node);
143146
T visitThis(This node) => visitExpression(node);
144147
T visitSuper(Super node) => visitExpression(node);
145148

149+
T visitRestParameter(RestParameter node) => visitNode(node);
150+
146151
T visitNamedFunction(NamedFunction node) => visitExpression(node);
147152
T visitFunctionExpression(FunctionExpression node) => visitExpression(node);
148153
T visitFun(Fun node) => visitFunctionExpression(node);
@@ -864,6 +869,17 @@ class Prefix extends Expression {
864869
int get precedenceLevel => UNARY;
865870
}
866871

872+
// SpreadElement isn't really a prefix expression, as it can only appear in
873+
// certain places such as ArgumentList and destructuring, but we pretend
874+
// it is for simplicity's sake.
875+
class Spread extends Prefix {
876+
Spread(Expression operand) : super('...', operand);
877+
int get precedenceLevel => SPREAD;
878+
879+
accept(NodeVisitor visitor) => visitor.visitSpread(this);
880+
Spread _clone() => new Spread(argument);
881+
}
882+
867883
class Postfix extends Expression {
868884
final String op;
869885
final Expression argument;
@@ -881,7 +897,9 @@ class Postfix extends Expression {
881897
int get precedenceLevel => UNARY;
882898
}
883899

884-
class Identifier extends Expression {
900+
abstract class Parameter implements Expression {}
901+
902+
class Identifier extends Expression implements Parameter {
885903
final String name;
886904
final bool allowRename;
887905

@@ -897,6 +915,20 @@ class Identifier extends Expression {
897915
void visitChildren(NodeVisitor visitor) {}
898916
}
899917

918+
// This is an expression for convenience in the AST.
919+
class RestParameter extends Expression implements Parameter {
920+
final Identifier parameter;
921+
922+
RestParameter(this.parameter);
923+
924+
RestParameter _clone() => new RestParameter(parameter);
925+
accept(NodeVisitor visitor) => visitor.visitRestParameter(this);
926+
void visitChildren(NodeVisitor visitor) {
927+
parameter.accept(visitor);
928+
}
929+
int get precedenceLevel => SPREAD;
930+
}
931+
900932
class This extends Expression {
901933
accept(NodeVisitor visitor) => visitor.visitThis(this);
902934
This _clone() => new This();
@@ -931,12 +963,12 @@ class NamedFunction extends Expression {
931963
}
932964

933965
abstract class FunctionExpression extends Expression {
934-
List<Identifier> get params;
966+
List<Parameter> get params;
935967
get body; // Expression or block
936968
}
937969

938970
class Fun extends FunctionExpression {
939-
final List<Identifier> params;
971+
final List<Parameter> params;
940972
final Block body;
941973
final AsyncModifier asyncModifier;
942974

@@ -945,7 +977,7 @@ class Fun extends FunctionExpression {
945977
accept(NodeVisitor visitor) => visitor.visitFun(this);
946978

947979
void visitChildren(NodeVisitor visitor) {
948-
for (Identifier param in params) param.accept(visitor);
980+
for (Parameter param in params) param.accept(visitor);
949981
body.accept(visitor);
950982
}
951983

@@ -955,15 +987,15 @@ class Fun extends FunctionExpression {
955987
}
956988

957989
class ArrowFun extends FunctionExpression {
958-
final List<Identifier> params;
990+
final List<Parameter> params;
959991
final body; // Expression or Block
960992

961993
ArrowFun(this.params, this.body);
962994

963995
accept(NodeVisitor visitor) => visitor.visitArrowFun(this);
964996

965997
void visitChildren(NodeVisitor visitor) {
966-
for (Identifier param in params) param.accept(visitor);
998+
for (Parameter param in params) param.accept(visitor);
967999
body.accept(visitor);
9681000
}
9691001

pkg/dev_compiler/lib/src/js/precedence.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
library precedence;
66

77
const EXPRESSION = 0;
8-
const ASSIGNMENT = EXPRESSION + 1;
8+
const SPREAD = EXPRESSION + 1;
9+
const ASSIGNMENT = SPREAD + 1;
910
const LOGICAL_OR = ASSIGNMENT + 1;
1011
const LOGICAL_AND = LOGICAL_OR + 1;
1112
const BIT_OR = LOGICAL_AND + 1;

0 commit comments

Comments
 (0)