Skip to content

Commit ea0cd57

Browse files
committed
Pattern matching
commit 8c08a25 Author: William C. Johnson <wcjohnson@oigroup.net> Date: Sun May 14 00:01:56 2017 -0400 0.7.0-alpha.2 commit 4d75189 Author: William C. Johnson <wcjohnson@oigroup.net> Date: Sat May 13 23:52:15 2017 -0400 More fixtures commit 351ef9c Author: William C. Johnson <wcjohnson@oigroup.net> Date: Sat May 13 23:41:33 2017 -0400 Update match parsing for whiteblock changes commit d8b48c6 Author: William C. Johnson <wcjohnson@oigroup.net> Date: Sat May 13 23:24:59 2017 -0400 Whiteblock statement fixes - Unify whiteblock parsing - Track expression context and error on illegal stmts commit a8c0620 Author: William C. Johnson <wcjohnson@oigroup.net> Date: Sat May 13 20:54:47 2017 -0400 Add parsing fixture for oneline fix commit 0b0f5dc Author: William C. Johnson <wcjohnson@oigroup.net> Date: Sat May 13 13:16:24 2017 -0400 Fix for oneline if statement parsing commit 71edb07c58b6c2e7d46331dcebc2bb844c3ef2b1 Author: William C. Johnson <wcjohnson@oigroup.net> Date: Sat May 13 13:14:37 2017 -0400 More comprehensive fixture commit 68315ee2bdaddf7361eddcee40972434f1b27d9e Author: William C. Johnson <wcjohnson@oigroup.net> Date: Sat May 13 13:10:01 2017 -0400 Fix if expr oneline block parsing commit d5c0a2455f7b7933b9b550d8bc03e81e6b6aa451 Author: William C. Johnson <wcjohnson@oigroup.net> Date: Sat May 13 13:02:00 2017 -0400 Cleanup parseWhiteBlockBody commit 5aaed5d89c5133a70190752a925acd55a5641b9c Author: William C. Johnson <wcjohnson@oigroup.net> Date: Sat May 13 13:01:47 2017 -0400 Cleanup ASI code commit d769f57418d1d1c8b11547eff14eeabdd8f737d6 Author: William C. Johnson <wcjohnson@oigroup.net> Date: Sat May 13 13:01:39 2017 -0400 Add parsing fixture commit ebec649 Author: William C. Johnson <wcjohnson@oigroup.net> Date: Sat May 13 00:27:01 2017 -0400 0.7.0-alpha.1 commit 24b3fea Author: William C. Johnson <wcjohnson@oigroup.net> Date: Fri May 12 21:11:55 2017 -0400 Add fixture for straight nesting commit a86db93 Author: William C. Johnson <wcjohnson@oigroup.net> Date: Fri May 12 21:09:13 2017 -0400 Allow brace blocks commit 494f15e Author: William C. Johnson <wcjohnson@oigroup.net> Date: Fri May 12 20:55:23 2017 -0400 Match test parsing for `?` expressions commit c03cfde Author: William C. Johnson <wcjohnson@oigroup.net> Date: Fri May 12 20:22:38 2017 -0400 `with`/`:` syntax commit 38cfc28 Author: William C. Johnson <wcjohnson@oigroup.net> Date: Fri May 12 19:44:02 2017 -0400 Fix numeric subscripts; update fixtures commit 014e15a Author: William C. Johnson <wcjohnson@oigroup.net> Date: Fri May 12 19:43:39 2017 -0400 Remove fixtures dealing with removed bitwise ops commit 90c17f5 Author: Alex Rattray <rattray.alex@gmail.com> Date: Mon Apr 24 16:39:27 2017 -0700 Pattern Matching Merge from upstream # Conflicts: # src/plugins/lightscript.js
1 parent 8e76947 commit ea0cd57

File tree

92 files changed

+10682
-4
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+10682
-4
lines changed

src/parser/expression.js

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,12 @@ pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
244244
if (this.match(tt.plusMin) && !this.isNextCharWhitespace()) {
245245
return left;
246246
}
247+
// if it's a `|` in a match/case on a newline, assume it's for the "case"
248+
// TODO: consider using indentation to be more precise about this
249+
// TODO: just remove all bitwise operators so this isn't necessary.
250+
if (this.match(tt.bitwiseOR) && this.state.inMatchCaseConsequent) {
251+
return left;
252+
}
247253
}
248254

249255
if (this.hasPlugin("lightscript") && this.isBitwiseOp()) {
@@ -288,7 +294,12 @@ pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
288294
// Parse unary operators, both prefix and postfix.
289295

290296
pp.parseMaybeUnary = function (refShorthandDefaultPos) {
291-
if (this.state.type.prefix) {
297+
const matchCaseBinaryPlusMin = this.hasPlugin("lightscript") &&
298+
this.state.inMatchCaseTest &&
299+
this.match(tt.plusMin) &&
300+
this.isNextCharWhitespace();
301+
302+
if (this.state.type.prefix && !matchCaseBinaryPlusMin) {
292303
if (this.hasPlugin("lightscript") && this.match(tt.plusMin)) {
293304
if (this.isNextCharWhitespace()) this.unexpected(null, "Unary +/- cannot be followed by a space in lightscript.");
294305
}
@@ -407,7 +418,14 @@ pp.parseSubscripts = function (base, startPos, startLoc, noCalls) {
407418
this.unexpected();
408419
}
409420
base = this.finishNode(node, "SafeMemberExpression");
410-
} else if (this.hasPlugin("lightscript") && this.match(tt.question) && this.state.lastTokEnd === (this.state.pos - 1)) {
421+
} else if (
422+
this.hasPlugin("lightscript") &&
423+
this.match(tt.question) &&
424+
(
425+
this.state.inMatchCaseTest ||
426+
this.state.lastTokEnd === (this.state.pos - 1)
427+
)
428+
) {
411429
// A `?` immediately following an expr (no whitespace) could be a
412430
// safecall, ternary, or existential.
413431
const next = this.parseQuestionSubscript(base, startPos, startLoc, noCalls);
@@ -710,6 +728,11 @@ pp.parseExprAtom = function (refShorthandDefaultPos) {
710728
return this.parseIfExpression(node);
711729
}
712730

731+
case tt._match:
732+
if (this.hasPlugin("lightscript")) {
733+
return this.parseMatch();
734+
}
735+
713736
case tt.arrow:
714737
if (this.hasPlugin("lightscript")) {
715738
node = this.startNode();
@@ -730,11 +753,16 @@ pp.parseExprAtom = function (refShorthandDefaultPos) {
730753
}
731754

732755
case tt.dot:
733-
if (this.hasPlugin("lightscript") && this.lookahead().type === tt.num) {
756+
if (this.hasPlugin("lightscript") && !this.allowMatchCasePlaceholder() && this.lookahead().type === tt.num) {
734757
this.unexpected(null, "Decimal numbers must be prefixed with a `0` in LightScript (eg; `0.1`).");
735758
}
736759

737760
default:
761+
if (this.hasPlugin("lightscript") && this.allowMatchCasePlaceholder()) {
762+
// use the blank space as an empty value (perhaps 0-length would be better)
763+
node = this.startNodeAt(this.state.lastTokEnd, this.state.lastTokEndLoc);
764+
return this.finishNodeAt(node, "PlaceholderExpression", this.state.start, this.state.startLoc);
765+
}
738766
this.unexpected();
739767
}
740768
};

src/plugins/jsx/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,11 @@ export default function(instance) {
434434
return function(code) {
435435
if (this.state.inPropertyName) return inner.call(this, code);
436436

437+
// don't allow jsx inside match case tests
438+
if (this.hasPlugin("lightscript") && this.state.inMatchCaseTest) {
439+
return inner.call(this, code);
440+
}
441+
437442
const context = this.curContext();
438443

439444
if (this.hasPlugin("lightscript") && code === 60) {

src/plugins/lightscript.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,124 @@ pp.existentialToParameter = function(node) {
620620
return node.argument;
621621
};
622622

623+
pp.parseMatch = function () {
624+
const node = this.startNode();
625+
this.expect(tt._match);
626+
node.discriminant = this.parseParenExpression();
627+
628+
let isEnd;
629+
if (this.match(tt.colon)) {
630+
const indentLevel = this.state.indentLevel;
631+
this.next();
632+
isEnd = () => this.state.indentLevel <= indentLevel || this.match(tt.eof);
633+
} else {
634+
this.expect(tt.braceL);
635+
isEnd = () => this.eat(tt.braceR);
636+
}
637+
638+
node.cases = [];
639+
let hasUsedElse = false;
640+
while (!isEnd()) {
641+
if (hasUsedElse) {
642+
this.unexpected(null, "`else` must be last case.");
643+
}
644+
645+
const matchCase = this.parseMatchCase();
646+
if (matchCase.test && matchCase.test.type === "MatchElse") {
647+
hasUsedElse = true;
648+
}
649+
node.cases.push(matchCase);
650+
}
651+
652+
return this.finishNode(node, "MatchExpression");
653+
};
654+
655+
pp.parseMatchCase = function () {
656+
const node = this.startNode();
657+
658+
node.test = this.parseMatchCaseTest();
659+
660+
if (this.eat(tt._with)) {
661+
const oldInMatchCaseConsequent = this.state.inMatchCaseConsequent;
662+
this.state.inMatchCaseConsequent = true;
663+
node.consequent = this.parseMaybeAssign();
664+
this.state.inMatchCaseConsequent = oldInMatchCaseConsequent;
665+
if (node.consequent.type !== "ArrowFunctionExpression") {
666+
this.unexpected(node.consequent.start, tt.arrow);
667+
}
668+
node.functional = true;
669+
} else {
670+
const oldInMatchCaseConsequent = this.state.inMatchCaseConsequent;
671+
this.state.inMatchCaseConsequent = true;
672+
// c/p parseIf
673+
if (this.match(tt.braceL)) {
674+
node.consequent = this.parseBlock(false, true);
675+
} else {
676+
node.consequent = this.parseWhiteBlock(true);
677+
}
678+
this.state.inMatchCaseConsequent = oldInMatchCaseConsequent;
679+
}
680+
681+
return this.finishNode(node, "MatchCase");
682+
};
683+
684+
pp.parseMatchCaseTest = function () {
685+
// can't be nested so no need to read/restore old value
686+
this.state.inMatchCaseTest = true;
687+
688+
this.expect(tt.bitwiseOR);
689+
if (this.isLineBreak()) this.unexpected(this.state.lastTokEnd, "Illegal newline.");
690+
691+
let test;
692+
if (this.match(tt._else)) {
693+
const elseNode = this.startNode();
694+
this.next();
695+
test = this.finishNode(elseNode, "MatchElse");
696+
} else {
697+
test = this.parseExprOps();
698+
}
699+
700+
this.state.inMatchCaseTest = false;
701+
return test;
702+
};
703+
704+
pp.isBinaryTokenForMatchCase = function (tokenType) {
705+
return (
706+
tokenType.binop != null &&
707+
tokenType !== tt.logicalOR &&
708+
tokenType !== tt.logicalAND &&
709+
tokenType !== tt.bitwiseOR
710+
);
711+
};
712+
713+
pp.isSubscriptTokenForMatchCase = function (tokenType) {
714+
return (
715+
tokenType === tt.dot ||
716+
tokenType === tt.elvis ||
717+
tokenType === tt.tilde ||
718+
tokenType === tt.bracketL ||
719+
tokenType === tt.question
720+
);
721+
};
722+
723+
pp.allowMatchCasePlaceholder = function () {
724+
if (!this.state.inMatchCaseTest) {
725+
return false;
726+
}
727+
const cur = this.state.type;
728+
const prev = this.state.tokens[this.state.tokens.length - 1].type;
729+
730+
// don't allow two binary tokens in a row to use placeholders, eg; `+ *`
731+
if (this.isBinaryTokenForMatchCase(cur)) {
732+
return !this.isBinaryTokenForMatchCase(prev);
733+
}
734+
// don't allow two subscripts in a row to use placeholders, eg; `..`
735+
if (this.isSubscriptTokenForMatchCase(cur)) {
736+
return !this.isSubscriptTokenForMatchCase(prev);
737+
}
738+
return false;
739+
};
740+
623741
export default function (instance) {
624742

625743
// if, switch, while, with --> don't need no stinkin' parens no more

src/tokenizer/state.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ export default class State {
3838
this.pos = this.lineStart = 0;
3939
this.curLine = options.startLine;
4040

41+
// for lightscript
4142
this.indentLevel = 0;
43+
this.inMatchCaseConsequent =
44+
this.inMatchCaseTest =
45+
false;
4246

4347
this.type = tt.eof;
4448
this.value = null;

src/tokenizer/types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ export const keywords = {
127127
"or": types.logicalOR,
128128
"and": types.logicalAND,
129129
"not": new KeywordTokenType("not", { beforeExpr, prefix, startsExpr }),
130+
"match": new KeywordTokenType("match", { beforeExpr, startsExpr }),
130131

131132
"break": new KeywordTokenType("break"),
132133
"case": new KeywordTokenType("case", { beforeExpr }),

src/util/identifier.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
match x:
2+
| a: 1
3+
| b with b -> b
4+
| c with (c) -> c
5+
| d with ({ d }) -> d
6+
| e with ({ e = 1 }) -> e
7+
| f with ([ f ]) -> f
8+
| g with ([ g = 1 ]) -> g
9+
| h with => 1
10+
| i with i => i
11+
| j with (j) => j

0 commit comments

Comments
 (0)