UNPKG

@effectful/transducers

Version:

JS syntax transformation framework for @effectful/js

574 lines (541 loc) 16.2 kB
"use strict"; exports.__esModule = true; exports.after = after; exports.ancestors = ancestors; exports.append = append; exports.appendBlock = appendBlock; exports.arr = arr; exports.assign = assign; exports.block = block; exports.bool = bool; exports.child = child; exports.childArr = childArr; exports.clone = clone; exports.consume = consume; exports.consumeRange = consumeRange; exports.detach = detach; exports.dft = dft; exports.empty = empty; exports.error = error; exports.fromIterable = fromIterable; exports.hier = hier; exports.id = id; exports.insertAfter = insertAfter; exports.insertBefore = insertBefore; exports.lookahead = lookahead; exports.next = next; exports.node = node; exports.num = num; exports.prepend = prepend; exports.prev = prev; exports.produce = produce; exports.produceNode = produceNode; exports.replace = replace; exports.resetFieldInfo = resetFieldInfo; exports.result = result; exports.share = share; exports.skip = skip; exports.str = str; exports.toArray = toArray; exports.tok = tok; var _types = require("./types"); var _config = require("./config"); function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* * drafts for the next version * * V2 (unlike V1) doesn't have tokens wraps all information * is stored directly in the Value, no entering and closing tokens * * # DOM-like AST representation * * type Value = { * pos: Pos, * type: Type, * node: BabelNode, * parent: Value | null, * id: number, * nextSibling: Value, * prevSibling: Value, * // ..... * } * */ let valCount = 0; function empty() { const res = { type: _types.Tag.Empty, pos: _types.Tag.Empty, id: valCount++, level: 0, node: null, parent: null, data: null //pure: false }; res.prevSibling = res; res.nextSibling = res; res.firstChild = null; return res; } /** * detaches `node` from the tree */ function detach(node) { const { nextSibling, parent } = node; (nextSibling.prevSibling = node.prevSibling).nextSibling = nextSibling; if (parent && parent.firstChild === node) { if (nextSibling === node) parent.firstChild = null;else parent.firstChild = nextSibling; } node.nextSibling = node.prevSibling = node; node.parent = null; return node; } /** inserts sibling `node` after `pos` node */ function insertAfter(pos, node) { const { nextSibling } = pos; node.prevSibling = pos; node.nextSibling = nextSibling; nextSibling.prevSibling = pos.nextSibling = node; node.parent = pos.parent; return node; } /** * inserts sibling `child` before `pos` * - expects `last` of the both nodes */ function insertBefore(pos, node) { const { prevSibling } = pos; node.nextSibling = pos; node.prevSibling = prevSibling; prevSibling.nextSibling = pos.prevSibling = node; const parent = pos.parent; node.parent = parent; if (parent.firstChild === pos) parent.firstChild = node; return node; } /** makes `child` to be the last child of `parent` */ function append(parent, child) { const old = parent.firstChild; if (old) return insertAfter(old.prevSibling, child); child.parent = parent; return parent.firstChild = child; } /** makes `child` to be the first child of `parent` */ function prepend(parent, child) { const old = parent.firstChild; if (old) return insertBefore(old, child); child.parent = parent; parent.firstChild = child; return child; } /** * replaces node `from` by `to` * - expects `last` of both */ function replace(from, to) { insertAfter(from, to); detach(from); return to; } /** builds default DOM node */ function node(pos, type) { return tok(pos, type, { type: "" }); } /** builds DOM node for `=` assignments */ function assign(pos) { return tok(pos, _types.Tag.AssignmentExpression, { operator: "=" }); } /** builds DOM node for an identifier with symbol */ function id(pos, sym) { const res = node(pos, _types.Tag.Identifier); res.sym = sym; return res; } /** builds DOM node for array */ function arr(pos) { return tok(pos, _types.Tag.Array, [], null); } function childArr(pos, par) { const res = tok(pos, _types.Tag.Array, [], null); append(par, res); return res; } function block(pos, par) { const res = node(pos, _types.Tag.BlockStatement); append(res, arr(_types.Tag.body)); append(par, res); return res; } function appendBlock(pos, par) { const block = node(pos, _types.Tag.BlockStatement); const res = childArr(_types.Tag.body, block); append(par, block); return res; } /** builds DOM node for a numeric constant */ function num(pos, value) { return tok(pos, _types.Tag.NumericLiteral, { value }); } /** builds DOM node for a string constant */ function str(pos, value) { return tok(pos, _types.Tag.StringLiteral, { value }); } /** builds DOM node for a boolean constant */ function bool(pos, value) { return tok(pos, _types.Tag.BooleanLiteral, { value }); } /** builds DOM node */ function tok(pos, type, node) { const res = empty(); res.pos = pos; res.type = type; res.node = node; return res; } /* same as `tok` but also sets `parent` */ function child(pos, type, node, parent) { const res = tok(pos, type, node); res.parent = parent; return res; } function Share(inner) { this.inner = inner[Symbol.iterator](); } Share.prototype[Symbol.iterator] = function () { return this; }; Share.prototype.next = function () { return this.inner.next(); }; /** returns same iterable which ignores `return` method call */ function share(inner) { return new Share(inner); } function* hierImpl(s) { let last = null; for (const i of s) { const level = i.level = i.parent ? i.parent.level + 1 : 0; if (last) for (let j = level, till = last.level; j <= till; ++j, last = last.parent) yield { start: false, value: last }; last = i; yield { start: true, value: i }; } for (; last && last.pos !== _types.Tag.root; last = last.parent) yield { start: false, value: last }; } /** * converts each element of `s:Iterable<Value>` into two elements * `{start:boolean,value:Value}` with `start:true` on entering the token * and `start:false` on exit */ function hier(inner) { return share(hierImpl(inner)); } function Reset(inner) { const iter = this.inner = inner[Symbol.iterator](); const item = iter.next(); if (item.done) { this.first = this.buf = null; return; } const buf = item.value; const first = buf.parent; buf.prev = first; first.next = buf; first.level = 0; this.buf = buf; this.first = first; this.levels = [first, null]; } Reset.prototype[Symbol.iterator] = function () { return this; }; Reset.prototype.next = function () { const value = this.step(); return value ? { done: false, value } : { done: true }; }; Reset.prototype.step = function step() { const cur = this.buf; if (!cur) return null; const nextItem = this.inner.next(); const levels = this.levels; if (nextItem.done) { const first = this.first; cur.nextSibling = first; this.buf = null; return cur; } const next = nextItem.value; const level = next.parent.level + 1; const prevSibling = levels[level]; levels[level] = cur; if (prevSibling) { prevSibling.nextSibling = next; next.prevSibling = prevSibling; } else { next.parent.firstChild = next; next.prevSibling = next; } levels[level + 1] = null; this.buf = next; cur.nextSibling = next; return cur; }; /** * takes an iterable of values and returns another iterable * with same elements but all the DOM fields re-calculated * this calculates only 1 token ahead */ function lookahead(s) { return new Reset(s); } /** * converts AST `node` into the AST representation with */ function produceNode(pos, babelNode) { if (Array.isArray(babelNode)) { const res = arr(pos); for (const i of babelNode) append(res, i ? produceNode(_types.Tag.push, i) : tok(_types.Tag.push, _types.Tag.Null, null)); return res; } const type = babelNode.type; const keys = _types.VISITOR_KEYS[type]; const res = tok(pos, _types.Tag[type], babelNode); if (keys.length) { for (const i of keys) { const v = babelNode[i]; if (v != null) append(res, produceNode(_types.Tag[i], v)); } } return res; } /** next node strictly after `node` */ function after(node) { for (let i = node;;) { const { parent, nextSibling } = i; if (!parent) return nextSibling; if (nextSibling !== parent.firstChild) return nextSibling; i = parent; } } /** next node in DFT order after `node` */ function next(node) { return node.firstChild || after(node); } /** prev node in DFT order before `node` */ function prev(node) { const { parent } = node; if (parent && parent.firstChild === node) return parent; for (let i = node.prevSibling;;) { const { firstChild } = i; if (!firstChild) return i; i = firstChild.prevSibling; } } /** traverses range [from,to) */ function* dft(from, to = from.nextSibling) { let i = from; do { const n = next(i); yield i; i = n; } while (i !== to); } /** * like `produce` but wraps the resulting node with `Tag.root` element */ function produce(babelNode) { const root = node(_types.Tag.root, _types.Tag.Root); append(root, produceNode(_types.Tag.top, babelNode)); return root; } /** * converts DOM root node into a babel AST node */ function consume(root) { consumeRange(root.firstChild, root); return root.firstChild.node; } /** * resets babel AST properties in [from..to) range */ function consumeRange(from, to) { for (const i of dft(from, to)) { const { parent, node } = i; if (parent) { if (i.pos === _types.Tag.push) { parent.node.push(node); } else parent.node[(0, _types.symName)(i.pos)] = node; } if (i.type === _types.Tag.Array) node.length = 0;else if (i.type === _types.Tag.Null) i.node = null;else { const ti = (0, _types.symInfo)(i.type); if (ti) { if (ti.esType != null) i.node.type = ti.esType; if (ti.fields) Object.assign(i.node, ti.fields); } } } } /** * Resets `fieldInfo` field in each `value`. * The field descripts AST node context. */ function resetFieldInfo(root) { root.typeInfo = (0, _types.typeInfo)(root); let i = root; do { let f = i.parent && i.parent.typeInfo; if (f && f.fieldsMap) f = i.fieldInfo = f.fieldsMap.get(i.pos); let ti = f && f.ti || (0, _types.typeInfo)(i); switch (i.type) { case _types.Tag.Array: ti = f; break; case _types.Tag.ArrayPattern: ti = f && f.declVar ? _types.arrayPattern : _types.arrayAssignmentPattern; break; case _types.Tag.ObjectPattern: ti = f && f.declVar ? _types.objectPattern : _types.objectAssignmentPattern; break; case _types.Tag.RestElement: ti = f && f.declVar ? _types.restElement : _types.restElementAssignment; break; case _types.Tag.AssignmentExpression: ti = i.node.operator === "=" ? _types.assignmentOpEq : _types.assignmentOpDefault; break; case _types.Tag.ObjectProperty: if (i.node.computed) ti = ti.propAlt;else if (f && f.declVar) ti = _types.assignmentProperty; break; case _types.Tag.AssignmentPattern: if (!f || !f.declVar) ti = (0, _types.symInfo)(_types.Tag.AssignmentExpression); break; case _types.Tag.MemberExpression: case _types.Tag.ObjectMethod: case _types.Tag.ClassProperty: case _types.Tag.ClassMethod: case _types.Tag.ClassPrivateMethod: case _types.Tag.ClassPrivateProperty: if (i.node.computed) ti = ti.propAlt; break; } i.typeInfo = ti; } while ((i = next(i)) !== root); return root; } /** runs iterable `s` and ignores its output, returns its result */ function skip(s) { const iter = s[Symbol.iterator](); let item; for (; !(item = iter.next()).done;) {} return item.value; } function error(msg, doc) { const file = _config.default.babelFile; if (!file) return new SyntaxError(msg); for (let i = doc; i; i = i.parent) { if (i.node.loc || i.node._loc) return file.buildCodeFrameError(i.node, msg); } return file.buildCodeFrameError(doc.node, msg); } /** same as `Array.from` but outputs the array into `buf` and returns iterable result */ function result(s, buf) { const iter = s[Symbol.iterator](); let item; while (!(item = iter.next()).done) buf.push(item.value); return item.value; } /** * same as `Array.from` but returns `s` if it is already `Array` */ function toArray(s) { if (Array.isArray(s)) return s; const res = []; result(s, res); return res; } /** converts stream to a fully calculated DOM node */ function fromIterable(s) { const iter = lookahead(s); while (iter.step()) {} return iter.first; } /** returns all ancestors of `node` (until and including root) */ function* ancestors(value) { for (let i = value.parent; i; i = i.parent) yield i; } function clone(doc) { if (!doc) return doc; let i = doc; do { const node = i.node; i.data = _objectSpread(_objectSpread({}, i), {}, { node: Array.isArray(node) ? _toConsumableArray(node) : _objectSpread({}, node), id: valCount++ }); } while ((i = next(i)) !== doc); i = doc; do { const { data } = i; if (i.parent) data.parent = i.parent.data; if (i.firstChild) data.firstChild = i.firstChild.data; data.prevSibling = i.prevSibling.data; data.nextSibling = i.nextSibling.data; } while ((i = next(i)) !== doc); const res = doc.data; do { i.data = null; } while ((i = next(i)) !== doc); res.prevSibling = res.nextSibling = res; return res; }