Skip to content

Commit c09bdf9

Browse files
wcjohnsonrattrayalex
authored andcommitted
Oneline whiteblock parsing fixes
- Allow parsing of single statements in oneline whiteblocks - Change if and arrow parsing to call new subroutine - Allow ASI after existing semicolons and in other edge cases - Add class to startsExpr so `ClassExpression`s don’t get parsed as stmts - Added fixtures Block parsing cleanup - Move special parsing case for arrows into parseArrowBody - Break up parseWhiteBlockBody Whiteblocks don’t allow directives Revert changes adding `exprContext` to parsing functions Disallow empty whiteblocks (removes need for `couldBeginStatement`) - Disallow empty whiteblocks - Update fixtures - Remove some obsolete fixtures - Use idiomatic `-> return` in fixtures
1 parent c69be04 commit c09bdf9

File tree

28 files changed

+2372
-319
lines changed

28 files changed

+2372
-319
lines changed

src/parser/util.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,18 @@ pp.isLineBreak = function () {
6565
pp.canInsertSemicolon = function () {
6666
return this.match(tt.eof) ||
6767
this.match(tt.braceR) ||
68-
this.isLineBreak();
68+
this.isLineBreak() ||
69+
(this.hasPlugin("lightscript") && (
70+
// LSC oneline statement ASI cases
71+
// Allow if x: throw y else: throw z
72+
this.match(tt._else) ||
73+
this.match(tt._elif) ||
74+
// Allow (-> throw new Error)()
75+
this.match(tt.parenR) ||
76+
// Technically it is legal to insert a ; after a ;.
77+
// Allows -> throw new Error; f()
78+
this.state.tokens[this.state.tokens.length - 1].type === tt.semi
79+
));
6980
};
7081

7182
// TODO

src/plugins/lightscript.js

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -134,40 +134,41 @@ pp.parseObjectComprehension = function(node) {
134134
return this.finishNode(node, "ObjectComprehension");
135135
};
136136

137-
// c/p parseBlock
137+
pp.parseInlineWhiteBlock = function(node) {
138+
if (this.state.type.startsExpr) return this.parseMaybeAssign();
139+
// oneline statement case
140+
node.body = [this.parseStatement(true)];
141+
node.directives = [];
142+
this.addExtra(node, "curly", false);
143+
return this.finishNode(node, "BlockStatement");
144+
};
145+
146+
pp.parseMultilineWhiteBlock = function(node, indentLevel) {
147+
this.parseBlockBody(node, false, false, indentLevel);
148+
if (!node.body.length) {
149+
this.unexpected(node.start, "Expected an Indent or Statement");
150+
}
151+
152+
this.addExtra(node, "curly", false);
153+
return this.finishNode(node, "BlockStatement");
154+
};
138155

139-
pp.parseWhiteBlock = function (allowDirectives?, isExpression?) {
156+
pp.parseWhiteBlock = function (isExpression?) {
140157
const node = this.startNode(), indentLevel = this.state.indentLevel;
141158

142-
// must start with colon or arrow
143-
if (isExpression) {
144-
this.expect(tt.colon);
145-
if (!this.isLineBreak()) return this.parseMaybeAssign();
146-
} else if (this.eat(tt.colon)) {
147-
if (!this.isLineBreak()) return this.parseStatement(false);
148-
} else if (this.eat(tt.arrow)) {
149-
if (!this.isLineBreak()) {
150-
if (this.match(tt.braceL)) {
151-
// restart node at brace start instead of arrow start
152-
const node = this.startNode();
153-
this.next();
154-
this.parseBlockBody(node, allowDirectives, false, tt.braceR);
155-
this.addExtra(node, "curly", true);
156-
return this.finishNode(node, "BlockStatement");
157-
} else {
158-
return this.parseMaybeAssign();
159-
}
159+
if (!this.eat(tt.colon)) this.unexpected(null, "Whitespace Block must start with a colon or arrow");
160+
161+
// Oneline whiteblock
162+
if (!this.isLineBreak()) {
163+
if (isExpression) {
164+
return this.parseInlineWhiteBlock(node);
165+
} else {
166+
return this.parseStatement(false);
160167
}
161-
} else {
162-
this.unexpected(null, "Whitespace Block must start with a colon or arrow");
163168
}
164169

165-
// never parse directives if curly braces aren't used (TODO: document)
166-
this.parseBlockBody(node, false, false, indentLevel);
167-
this.addExtra(node, "curly", false);
168-
if (!node.body.length) this.unexpected(node.start, "Expected an Indent or Statement");
169-
170-
return this.finishNode(node, "BlockStatement");
170+
// TODO: document the fact that directives aren't parsed
171+
return this.parseMultilineWhiteBlock(node, indentLevel);
171172
};
172173

173174
pp.expectCommaOrLineBreak = function () {
@@ -301,7 +302,23 @@ pp.parseArrowFunctionBody = function (node) {
301302
this.state.labels = [];
302303
this.state.inFunction = true;
303304

304-
node.body = this.parseWhiteBlock(true);
305+
const indentLevel = this.state.indentLevel;
306+
const nodeAtArrow = this.startNode();
307+
this.expect(tt.arrow);
308+
if (!this.isLineBreak()) {
309+
if (this.match(tt.braceL)) {
310+
// restart node at brace start instead of arrow start
311+
node.body = this.startNode();
312+
this.next();
313+
this.parseBlockBody(node.body, true, false, tt.braceR);
314+
this.addExtra(node.body, "curly", true);
315+
node.body = this.finishNode(node.body, "BlockStatement");
316+
} else {
317+
node.body = this.parseInlineWhiteBlock(nodeAtArrow);
318+
}
319+
} else {
320+
node.body = this.parseMultilineWhiteBlock(nodeAtArrow, indentLevel);
321+
}
305322

306323
if (node.body.type !== "BlockStatement") {
307324
node.expression = true;
@@ -376,7 +393,7 @@ pp.parseIf = function (node, isExpression) {
376393
} else if (!isColon) {
377394
node.consequent = this.parseMaybeAssign();
378395
} else {
379-
node.consequent = this.parseWhiteBlock(false, true);
396+
node.consequent = this.parseWhiteBlock(true);
380397
}
381398
} else {
382399
node.consequent = this.parseStatement(false);
@@ -425,7 +442,7 @@ pp.parseIfAlternate = function (node, isExpression, ifIsWhiteBlock, ifIndentLeve
425442
} else if (!this.match(tt.colon)) {
426443
return this.parseMaybeAssign();
427444
} else {
428-
return this.parseWhiteBlock(false, true);
445+
return this.parseWhiteBlock(true);
429446
}
430447
}
431448

@@ -613,9 +630,9 @@ export default function (instance) {
613630
// whitespace following a colon
614631

615632
instance.extend("parseBlock", function (inner) {
616-
return function (allowDirectives) {
633+
return function () {
617634
if (this.match(tt.colon)) {
618-
return this.parseWhiteBlock(allowDirectives);
635+
return this.parseWhiteBlock();
619636
}
620637
const block = inner.apply(this, arguments);
621638
this.addExtra(block, "curly", true);

src/tokenizer/types.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export const types = {
8080
doubleColon: new TokenType("::", { beforeExpr }),
8181
dot: new TokenType("."),
8282
question: new TokenType("?", { beforeExpr }),
83-
arrow: new TokenType("=>", { beforeExpr }),
83+
arrow: new TokenType("=>", { beforeExpr, startsExpr }),
8484
template: new TokenType("template"),
8585
ellipsis: new TokenType("...", { beforeExpr }),
8686
backQuote: new TokenType("`", { startsExpr }),
@@ -152,7 +152,7 @@ export const keywords = {
152152
"new": new KeywordTokenType("new", { beforeExpr, startsExpr }),
153153
"this": new KeywordTokenType("this", { startsExpr }),
154154
"super": new KeywordTokenType("super", { startsExpr }),
155-
"class": new KeywordTokenType("class"),
155+
"class": new KeywordTokenType("class", { startsExpr }),
156156
"extends": new KeywordTokenType("extends", { beforeExpr }),
157157
"export": new KeywordTokenType("export"),
158158
"import": new KeywordTokenType("import", { startsExpr }),

0 commit comments

Comments
 (0)