123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- "use strict";
- const CodePathState = require("./code-path-state");
- const IdGenerator = require("./id-generator");
- class CodePath {
-
- constructor({ id, origin, upper, onLooped }) {
-
- this.id = id;
-
- this.origin = origin;
-
- this.upper = upper;
-
- this.childCodePaths = [];
-
- Object.defineProperty(
- this,
- "internal",
- { value: new CodePathState(new IdGenerator(`${id}_`), onLooped) }
- );
-
- if (upper) {
- upper.childCodePaths.push(this);
- }
- }
-
- static getState(codePath) {
- return codePath.internal;
- }
-
- get initialSegment() {
- return this.internal.initialSegment;
- }
-
- get finalSegments() {
- return this.internal.finalSegments;
- }
-
- get returnedSegments() {
- return this.internal.returnedForkContext;
- }
-
- get thrownSegments() {
- return this.internal.thrownForkContext;
- }
-
- get currentSegments() {
- return this.internal.currentSegments;
- }
-
- traverseSegments(optionsOrCallback, callback) {
-
- let resolvedOptions;
- let resolvedCallback;
- if (typeof optionsOrCallback === "function") {
- resolvedCallback = optionsOrCallback;
- resolvedOptions = {};
- } else {
- resolvedOptions = optionsOrCallback || {};
- resolvedCallback = callback;
- }
-
- const startSegment = resolvedOptions.first || this.internal.initialSegment;
- const lastSegment = resolvedOptions.last;
-
- let record = null;
- let index = 0;
- let end = 0;
- let segment = null;
-
- const visited = new Set();
-
- const stack = [[startSegment, 0]];
-
- let skippedSegment = null;
-
- let broken = false;
-
- const controller = {
-
- skip() {
- if (stack.length <= 1) {
- broken = true;
- } else {
- skippedSegment = stack[stack.length - 2][0];
- }
- },
-
- break() {
- broken = true;
- }
- };
-
- function isVisited(prevSegment) {
- return (
- visited.has(prevSegment) ||
- segment.isLoopedPrevSegment(prevSegment)
- );
- }
-
- while (stack.length > 0) {
-
- record = stack[stack.length - 1];
- segment = record[0];
- index = record[1];
- if (index === 0) {
-
- if (visited.has(segment)) {
- stack.pop();
- continue;
- }
-
- if (segment !== startSegment &&
- segment.prevSegments.length > 0 &&
- !segment.prevSegments.every(isVisited)
- ) {
- stack.pop();
- continue;
- }
-
- if (skippedSegment && segment.prevSegments.includes(skippedSegment)) {
- skippedSegment = null;
- }
- visited.add(segment);
-
- if (!skippedSegment) {
- resolvedCallback.call(this, segment, controller);
-
- if (segment === lastSegment) {
- controller.skip();
- }
-
- if (broken) {
- break;
- }
- }
- }
-
- end = segment.nextSegments.length - 1;
- if (index < end) {
-
- record[1] += 1;
- stack.push([segment.nextSegments[index], 0]);
- } else if (index === end) {
-
- record[0] = segment.nextSegments[index];
- record[1] = 0;
- } else {
-
- stack.pop();
- }
- }
- }
- }
- module.exports = CodePath;
|