Skip to content

Commit a4f700c

Browse files
committed
wip
1 parent 64bd12b commit a4f700c

File tree

8 files changed

+376
-8
lines changed

8 files changed

+376
-8
lines changed

src/index.js

Lines changed: 137 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { parse } from "babylon-lightscript";
2-
import { defaultImports, lightscriptImports, lodashImports } from "./stdlib";
2+
import { defaultImports, lightscriptImports, lodashImports, runtimeHelpers } from "./stdlib";
33

44
export default function (babel) {
55
const { types: t } = babel;
@@ -580,6 +580,15 @@ export default function (babel) {
580580
}
581581
}
582582

583+
function collectRuntimeHelper(path, helperName) {
584+
const programScope = path.scope.getProgramParent();
585+
const helpers = programScope.lscRuntimeHelpers;
586+
if (!helpers[helperName]) {
587+
helpers[helperName] = programScope.generateUidIdentifier(helperName);
588+
}
589+
return helpers[helperName];
590+
}
591+
583592
function makeInlineStdlibFn(inlineFnName) {
584593
const fnId = t.identifier(inlineFnName);
585594
const aParam = t.identifier("a");
@@ -608,6 +617,16 @@ export default function (babel) {
608617
]));
609618
}
610619

620+
function insertAfterImports(path, nodes) {
621+
// insert inline fns before the first statement which isn't an import statement
622+
for (const p of path.get("body")) {
623+
if (!p.isImportDeclaration()) {
624+
p.insertBefore(nodes);
625+
break;
626+
}
627+
}
628+
}
629+
611630
function insertStdlibImports(path, imports: Imports, useRequire) {
612631
const declarations = [];
613632
const inlines = [];
@@ -653,14 +672,21 @@ export default function (babel) {
653672
for (const inlineFnName of inlines) {
654673
inlineDeclarations.push(makeInlineStdlibFn(inlineFnName));
655674
}
656-
// insert inline fns before the first statement which isn't an import statement
657-
for (const p of path.get("body")) {
658-
if (!p.isImportDeclaration()) {
659-
p.insertBefore(inlineDeclarations);
660-
break;
661-
}
662-
}
675+
insertAfterImports(path, inlineDeclarations);
676+
}
677+
}
678+
679+
function insertRuntimeHelpers(path) {
680+
const helpers = [];
681+
for (const helperName in path.scope.lscRuntimeHelpers) {
682+
const fn = runtimeHelpers[helperName];
683+
const uid = path.scope.lscRuntimeHelpers[helperName];
684+
const fnAST = babel.template(fn.toString())({
685+
[helperName]: uid,
686+
});
687+
helpers.push(fnAST);
663688
}
689+
insertAfterImports(path, helpers);
664690
}
665691

666692
function generateForInIterator(path, type: "array" | "object") {
@@ -827,6 +853,103 @@ export default function (babel) {
827853
}
828854
}
829855

856+
function extendAndChain(andChainPath, condition) {
857+
if (!andChainPath.node) {
858+
andChainPath.replaceWith(condition);
859+
} else {
860+
andChainPath.replaceWith(t.logicalExpression("&&", andChainPath.node, condition));
861+
}
862+
}
863+
864+
function buildAnd(left, right) {
865+
if (left && right) {
866+
return t.logicalExpression("&&", left, right);
867+
} else if (left) {
868+
return left;
869+
} else if (right) {
870+
return right;
871+
} else {
872+
return t.booleanLiteral(true);
873+
}
874+
}
875+
876+
function buildTestForBinding(test, bindingPath, argRef) {
877+
if (bindingPath.isObjectPattern()) {
878+
const isObjUid = collectRuntimeHelper(bindingPath, "hasProps");
879+
880+
const propsToCheck = [];
881+
const childPatterns = []; // list of [propName, path] tuples
882+
for (const propPath of bindingPath.get("properties")) {
883+
const propName = propPath.get("key").node.name;
884+
885+
if (propPath.get("value").isAssignmentPattern()) {
886+
if (propPath.get("value.left").isPattern()) {
887+
childPatterns.push([propName, propPath.get("value.left"), propPath.get("value.right").node]);
888+
}
889+
} else {
890+
const propStr = t.stringLiteral(propName);
891+
propsToCheck.push(propStr);
892+
893+
if (propPath.get("value").isPattern()) {
894+
childPatterns.push([propName, propPath.get("value")]);
895+
}
896+
}
897+
}
898+
899+
const isObjCall = t.callExpression(isObjUid, [argRef, t.arrayExpression(propsToCheck)]);
900+
test = buildAnd(test, isObjCall);
901+
902+
for (const [ propName, childPatternPath, defaultObj = null ] of childPatterns) {
903+
const propertyArgRef = t.memberExpression(argRef, t.identifier(propName));
904+
905+
if (defaultObj) {
906+
test = buildAnd(test, t.logicalExpression("||",
907+
buildTestForBinding(null, childPatternPath, propertyArgRef),
908+
buildTestForBinding(null, childPatternPath, defaultObj)
909+
));
910+
} else {
911+
test = buildTestForBinding(test, childPatternPath, propertyArgRef);
912+
}
913+
}
914+
} else if (bindingPath.isArrayPattern()) {
915+
const hasLengthUid = collectRuntimeHelper(bindingPath, "hasLength");
916+
917+
const childPatterns = []; // list of [index, path] tuples.
918+
let minLength = 0;
919+
let maxLength = 0;
920+
bindingPath.get("elements").forEach((elemPath, i) => {
921+
if (elemPath.isAssignmentPattern()) {
922+
++maxLength;
923+
if (elemPath.get("left").isPattern()) {
924+
childPatterns.push([i, elemPath.get("left")]);
925+
}
926+
} else if (elemPath.isRestElement()) {
927+
maxLength = null;
928+
} else {
929+
++minLength;
930+
++maxLength;
931+
if (elemPath.isPattern()) {
932+
childPatterns.push([i, elemPath]);
933+
}
934+
}
935+
});
936+
937+
const hasLengthCall = t.callExpression(hasLengthUid, [
938+
argRef,
939+
t.numericLiteral(minLength),
940+
maxLength === null ? null : t.numericLiteral(maxLength)
941+
].filter(x => x !== null));
942+
test = buildAnd(test, hasLengthCall);
943+
944+
for (const [index, childPatternPath] of childPatterns) {
945+
const elementArgRef = t.memberExpression(argRef, t.numericLiteral(index), true);
946+
test = buildTestForBinding(test, childPatternPath, elementArgRef);
947+
}
948+
} else throw new TypeError(`Expected Pattern, got ${bindingPath.node.type}`);
949+
950+
return test;
951+
}
952+
830953
function transformMatchCases(argRef, cases) {
831954
return cases.reduce((rootIf, path) => {
832955

@@ -843,6 +966,10 @@ export default function (babel) {
843966
// add binding (and always use block bodies)
844967
ensureBlockBody(path, "consequent");
845968
if (path.node.binding) {
969+
const bindingTest = buildTestForBinding(null, path.get("binding"), argRef);
970+
const testWithBindingTest = buildAnd(path.get("test").node, bindingTest)
971+
path.get("test").replaceWith(testWithBindingTest);
972+
846973
const bindingDecl = t.variableDeclaration("const", [
847974
t.variableDeclarator(path.node.binding, argRef)
848975
]);
@@ -1128,6 +1255,7 @@ export default function (babel) {
11281255
const stdlib: Stdlib = initializeStdlib(state.opts);
11291256
const useRequire = state.opts.stdlib && state.opts.stdlib.require === true;
11301257
const imports: Imports = {};
1258+
path.scope.lscRuntimeHelpers = {};
11311259

11321260
path.traverse({
11331261

@@ -1451,6 +1579,7 @@ export default function (babel) {
14511579
});
14521580

14531581
insertStdlibImports(path, imports, useRequire);
1582+
insertRuntimeHelpers(path);
14541583
}
14551584

14561585
return {

src/stdlib.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,27 @@ export const lightscriptImports = {
1111
"bitwiseZeroFillRightShift": "inline",
1212
};
1313

14+
export const runtimeHelpers = {
15+
hasProps: function hasProps(obj, props) {
16+
return (
17+
obj != null &&
18+
(typeof obj === "object" || typeof obj === "function") &&
19+
props.filter(prop => prop in obj).length === props.length
20+
);
21+
},
22+
hasLength: function hasLength(arr, minLength, maxLength) {
23+
minLength = minLength || 0;
24+
maxLength = maxLength != null ? maxLength : Number.MAX_SAFE_INTEGER;
25+
return (
26+
arr != null &&
27+
typeof arr !== "function" &&
28+
arr.length === arr.length|0 &&
29+
arr.length >= minLength &&
30+
arr.length <= maxLength
31+
);
32+
},
33+
};
34+
1435
export const everyLodashMethod = [
1536
"add",
1637
"after",
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
match x:
2+
| []:
3+
"empty"
4+
| [ a, b ]:
5+
a - b
6+
| [ a, b = 2 ]:
7+
a + b - 2
8+
| [ a, ...b ]:
9+
b.concat(a)
10+
| [
11+
[
12+
b
13+
d = 'e'
14+
]
15+
[ g, , h ]
16+
...j
17+
]:
18+
[b, d, g, ...j].join('')
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
assert.equal(
2+
"empty",
3+
match []:
4+
| []:
5+
"empty"
6+
)
7+
assert.equal(
8+
"empty",
9+
match []:
10+
| []:
11+
"empty"
12+
)
13+
assert.equal(
14+
undefined,
15+
match []:
16+
| [a]:
17+
a + 1
18+
)
19+
assert.equal(
20+
undefined,
21+
match [1]:
22+
| [a, b]:
23+
a + b
24+
)
25+
assert.equal(
26+
5,
27+
match [1]:
28+
| [a, b = 4]:
29+
a + b
30+
)
31+
assert.equal(
32+
3,
33+
match [1, 2]:
34+
| [a, b]:
35+
a + b
36+
)
37+
assert.equal(
38+
4,
39+
match [1, 2, 3]:
40+
| [a,, b]:
41+
a + b
42+
)
43+
assert.equal(
44+
undefined,
45+
match [1, 2]:
46+
| [a,, b]:
47+
a + b
48+
)
49+
assert.deepEqual(
50+
[2, 3, 1],
51+
match [1, 2, 3]:
52+
| [a, ...b]:
53+
b.concat(a)
54+
)
55+
assert.deepEqual(
56+
[1, 4],
57+
match [4]:
58+
| [a, b = 1, ...c]:
59+
c.concat([b, a])
60+
)
61+
assert.deepEqual(
62+
[6, 7, 5, 4],
63+
match [4, 5, 6, 7]:
64+
| [a, b = 1, ...c]:
65+
c.concat([b, a])
66+
)
67+
assert.deepEqual(
68+
[1, 2, 4, 6, 7, 8],
69+
match [[1], [4, 5, 6], 7, 8]:
70+
| [
71+
[
72+
b
73+
d = 2
74+
]
75+
[ g, , h ]
76+
...j
77+
]:
78+
[b, d, g, h, ...j]
79+
)
80+
assert.deepEqual(
81+
undefined,
82+
match [[1], [4, 5], 7, 8]:
83+
| [
84+
[
85+
b
86+
d = 2
87+
]
88+
[ g, , h ]
89+
...j
90+
]:
91+
[b, d, g, h, ...j]
92+
)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
function _hasLength(arr, minLength, maxLength) {
2+
minLength = minLength || 0;
3+
maxLength = maxLength != null ? maxLength : Number.MAX_SAFE_INTEGER;
4+
return arr != null && typeof arr !== "function" && arr.length === arr.length | 0 && arr.length >= minLength && arr.length <= maxLength;
5+
}
6+
7+
if (_hasLength(x, 0, 0)) {
8+
const [] = x;
9+
10+
"empty";
11+
} else if (_hasLength(x, 2, 2)) {
12+
const [a, b] = x;
13+
14+
a - b;
15+
} else if (_hasLength(x, 1, 2)) {
16+
const [a, b = 2] = x;
17+
18+
a + b - 2;
19+
} else if (_hasLength(x, 1)) {
20+
const [a, ...b] = x;
21+
22+
b.concat(a);
23+
} else if (_hasLength(x, 2) && _hasLength(x[0], 1, 2) && _hasLength(x[1], 3, 3)) {
24+
const [[b, d = 'e'], [g,, h], ...j] = x;
25+
26+
[b, d, g, ...j].join('');
27+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
match x:
2+
| { a }:
3+
a
4+
| { a, b }:
5+
a + b
6+
| { a, b = 1 }:
7+
a + b
8+
| { a, b: { ba, bb = 1 }, c: { ca, cb: { cba } } }:
9+
a + ba + bb + ca + cba
10+
| 1 or 2 with { a, b: { c } }:
11+
a + c
12+
| { a: { b: { c } } = otherObj }:
13+
c
14+
//TODO: | { a, ...b }: b

0 commit comments

Comments
 (0)