Skip to content

Commit 9d1531d

Browse files
committed
Support recursion
1 parent 75c42cc commit 9d1531d

File tree

4 files changed

+147
-31
lines changed

4 files changed

+147
-31
lines changed

README.md

+3-13
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,11 @@ It exports the PipelineError class and the evaluate() function.
88
Usage looks like this:
99

1010
```javascript
11-
it(`Creates a named result but does not seek`, async () => {
12-
const output = await evaluate([
13-
"[10,20,30,40]",
14-
"-j",
15-
"x+1",
16-
"-j",
17-
"x+2",
18-
"-n",
19-
"add2",
20-
"-j",
21-
"x+10"
22-
]);
11+
it(`Passes an object to a shell command`, async () => {
12+
const output = await evaluate(["{ name: 'kai' }", "-e", "echo ${x.name}"]);
2313
(await toResult(output)).should.deepEqual({
2414
mustPrint: true,
25-
result: [23, 33, 43, 53]
15+
result: ["kai"]
2616
});
2717
});
2818
```

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "basho-eval",
3-
"version": "0.0.14",
3+
"version": "0.0.16",
44
"author": "Isotropy Team<[email protected]>",
55
"contributors": [
66
"Jeswin Kumar<[email protected]>"

src/basho-eval.ts

+105-17
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Seq, sequence } from "lazily-async";
55

66
import exception from "./exception";
77
import importModule = require("./import-module");
8+
import { currentId } from "async_hooks";
89

910
const exec = promisify(child_process.exec);
1011

@@ -15,6 +16,7 @@ const options = [
1516
"-c", // n1,n2,n3 combine a named stages
1617
"-e", // shell command
1718
"-f", // filter
19+
"-g", // n, exp recurse
1820
"-i", // import a file or module
1921
"-j", // JS expression
2022
"-l", // evaluate and log a value to console
@@ -392,21 +394,30 @@ export async function evaluate(
392394
) {
393395
return await evaluateInternal(
394396
args,
397+
[],
395398
Seq.of([]),
396399
mustPrint,
397400
onLog,
398401
onWrite,
399-
true
402+
true,
403+
[]
400404
);
401405
}
402406

407+
type ExpressionStackEntry = {
408+
name: string;
409+
args: Array<string>;
410+
};
411+
403412
async function evaluateInternal(
404413
args: Array<string>,
414+
prevArgs: Array<string>,
405415
input: Seq<PipelineItem>,
406416
mustPrint: boolean,
407417
onLog: BashoLogFn,
408418
onWrite: BashoLogFn,
409-
isInitialInput: boolean
419+
isInitialInput: boolean,
420+
expressionStack: Array<ExpressionStackEntry>
410421
): Promise<BashoEvaluationResult> {
411422
const cases: Array<[(arg: string) => boolean, () => Promise<any>]> = [
412423
/* Enumerate sequence into an array */
@@ -416,6 +427,7 @@ async function evaluateInternal(
416427
const items = await input.toArray();
417428
return await evalShorthand(
418429
args.slice(1),
430+
args,
419431
Seq.of([
420432
new PipelineValue(
421433
items.map(x => (x instanceof PipelineValue ? x.value : x))
@@ -432,6 +444,7 @@ async function evaluateInternal(
432444
async () =>
433445
await evalShorthand(
434446
args.slice(2),
447+
args,
435448
input.map(x => {
436449
const streams = args[1].split(",");
437450
return new PipelineValue(
@@ -453,6 +466,7 @@ async function evaluateInternal(
453466
const { cursor, expression } = munch(args.slice(1));
454467
return await evalShorthand(
455468
args.slice(cursor + 1),
469+
args,
456470
await shellCmd(
457471
expression,
458472
input,
@@ -485,7 +499,7 @@ async function evaluateInternal(
485499
: x;
486500
});
487501

488-
return await evalShorthand(args.slice(cursor + 1), newSeq, false);
502+
return await evalShorthand(args.slice(cursor + 1), args, newSeq, false);
489503
}
490504
],
491505

@@ -495,7 +509,61 @@ async function evaluateInternal(
495509
async () => {
496510
const { cursor, expression } = munch(args.slice(1));
497511
const filtered = await filter(expression, input);
498-
return await evalShorthand(args.slice(cursor + 1), filtered, false);
512+
return await evalShorthand(
513+
args.slice(cursor + 1),
514+
args,
515+
filtered,
516+
false
517+
);
518+
}
519+
],
520+
521+
/* Recurse/Goto */
522+
[
523+
x => x === "-g",
524+
async () => {
525+
const name = args[1];
526+
const { cursor, expression } = munch(args.slice(2));
527+
const recursePoint = expressionStack.find(e => e.name === name);
528+
const fn = await evalWithCatch(`(x, i) => (${expression})`);
529+
const newSeq = input.map(
530+
async (x, i) =>
531+
recursePoint
532+
? x instanceof PipelineValue
533+
? await (async () => {
534+
const predicateResult = await fn(await x.value, i);
535+
return predicateResult === true
536+
? await (async () => {
537+
const recurseArgs = recursePoint.args.slice(
538+
0,
539+
recursePoint.args.length -
540+
(args.length - (cursor + 2))
541+
);
542+
const innerEvalResult = await evaluateInternal(
543+
recurseArgs,
544+
[],
545+
Seq.of([x]),
546+
mustPrint,
547+
onLog,
548+
onWrite,
549+
false,
550+
[]
551+
);
552+
const results = await innerEvalResult.result.toArray();
553+
const result = results[0];
554+
return result instanceof PipelineValue
555+
? new PipelineValue(result.value, x)
556+
: result;
557+
})()
558+
: x;
559+
})()
560+
: x
561+
: new PipelineError(
562+
`The expression ${name} was not found.`,
563+
new Error(`Missing expression ${name}.`)
564+
)
565+
);
566+
return await evalShorthand(args.slice(cursor + 2), args, newSeq, false);
499567
}
500568
],
501569

@@ -506,11 +574,13 @@ async function evaluateInternal(
506574
evalImport(args[1], args[2]);
507575
return await evaluateInternal(
508576
args.slice(3),
577+
args,
509578
input,
510579
mustPrint,
511580
onLog,
512581
onWrite,
513-
isInitialInput
582+
isInitialInput,
583+
expressionStack
514584
);
515585
}
516586
],
@@ -522,6 +592,7 @@ async function evaluateInternal(
522592
const { cursor, expression } = munch(args.slice(1));
523593
return await evalShorthand(
524594
args.slice(cursor + 1),
595+
args,
525596
await evalExpression(
526597
expression,
527598
input,
@@ -534,15 +605,15 @@ async function evaluateInternal(
534605
],
535606

536607
/* Logging */
537-
[x => x === "-l", getPrinter(onLog)(input, args, evalShorthand)],
608+
[x => x === "-l", getPrinter(onLog)(input, args)],
538609

539610
/* Flatmap */
540611
[
541612
x => x === "-m",
542613
async () => {
543614
const { cursor, expression } = munch(args.slice(1));
544615
const mapped = await flatMap(expression, input);
545-
return await evalShorthand(args.slice(cursor + 1), mapped, false);
616+
return await evalShorthand(args.slice(cursor + 1), args, mapped, false);
546617
}
547618
],
548619

@@ -551,7 +622,16 @@ async function evaluateInternal(
551622
x => x === "-n",
552623
async () => {
553624
const newSeq = input.map(async (x, i) => x.clone(args[1]));
554-
return await evalShorthand(args.slice(2), newSeq, false);
625+
return await evaluateInternal(
626+
args.slice(2),
627+
args,
628+
newSeq,
629+
mustPrint,
630+
onLog,
631+
onWrite,
632+
false,
633+
expressionStack.concat({ name: args[1], args: prevArgs })
634+
);
555635
}
556636
],
557637

@@ -561,11 +641,13 @@ async function evaluateInternal(
561641
async () =>
562642
await evaluateInternal(
563643
args.slice(1),
644+
args,
564645
input,
565646
mustPrint,
566647
onLog,
567648
onWrite,
568-
isInitialInput
649+
isInitialInput,
650+
expressionStack
569651
)
570652
],
571653

@@ -575,11 +657,13 @@ async function evaluateInternal(
575657
async () =>
576658
await evaluateInternal(
577659
args.slice(1),
660+
args,
578661
input,
579662
false,
580663
onLog,
581664
onWrite,
582-
isInitialInput
665+
isInitialInput,
666+
expressionStack
583667
)
584668
],
585669

@@ -598,6 +682,7 @@ async function evaluateInternal(
598682
const reduced = await reduce(expression, input, initialValue);
599683
return await evalShorthand(
600684
args.slice(cursor + 1),
685+
args,
601686
Seq.of([reduced]),
602687
false
603688
);
@@ -613,6 +698,7 @@ async function evaluateInternal(
613698
async () =>
614699
await evalShorthand(
615700
args.slice(2),
701+
args,
616702
input.map(x => {
617703
return new PipelineValue(
618704
(() => {
@@ -656,14 +742,15 @@ async function evaluateInternal(
656742
}
657743
return await evalShorthand(
658744
args.slice(cursor + 1),
745+
args,
659746
new Seq(asyncGenerator),
660747
false
661748
);
662749
}
663750
],
664751

665752
/* Writing */
666-
[x => x === "-w", getPrinter(onWrite)(input, args, evalShorthand)],
753+
[x => x === "-w", getPrinter(onWrite)(input, args)],
667754

668755
/* Everything else as JS expressions */
669756
[
@@ -672,6 +759,7 @@ async function evaluateInternal(
672759
const { cursor, expression } = munch(args);
673760
return await evalShorthand(
674761
args.slice(cursor),
762+
args,
675763
await evalExpression(
676764
expression,
677765
input,
@@ -685,11 +773,7 @@ async function evaluateInternal(
685773
];
686774

687775
function getPrinter(printFn: BashoLogFn) {
688-
return (
689-
input: Seq<PipelineItem>,
690-
args: Array<string>,
691-
evalFn: typeof evalShorthand
692-
) => async () => {
776+
return (input: Seq<PipelineItem>, args: Array<string>) => async () => {
693777
const { cursor, expression } = munch(args.slice(1));
694778
const fn = await evalWithCatch(`(x, i) => (${expression})`);
695779
const newSeq = input.map(async (x, i) => {
@@ -705,6 +789,7 @@ async function evaluateInternal(
705789
});
706790
return await evalShorthand(
707791
args.slice(cursor + 1),
792+
args,
708793
newSeq,
709794
isInitialInput
710795
);
@@ -713,16 +798,19 @@ async function evaluateInternal(
713798

714799
async function evalShorthand(
715800
args: Array<string>,
801+
prevArgs: Array<string>,
716802
input: Seq<PipelineItem>,
717803
isInitialInput: boolean
718804
): Promise<BashoEvaluationResult> {
719805
return await evaluateInternal(
720806
args,
807+
prevArgs,
721808
input,
722809
mustPrint,
723810
onLog,
724811
onWrite,
725-
isInitialInput
812+
isInitialInput,
813+
expressionStack
726814
);
727815
}
728816

src/test/test.ts

+38
Original file line numberDiff line numberDiff line change
@@ -414,4 +414,42 @@ describe("basho", () => {
414414
result: [["a", "b"], "skipped", ["c", "d"]]
415415
});
416416
});
417+
418+
it(`Recurses`, async () => {
419+
const output = await evaluate([
420+
"[20]",
421+
"-j",
422+
"x+1",
423+
"-n",
424+
"add1",
425+
"-j",
426+
"x+2",
427+
"-g",
428+
"add1",
429+
"x<30"
430+
]);
431+
(await toResult(output)).should.deepEqual({
432+
mustPrint: true,
433+
result: [32]
434+
});
435+
});
436+
437+
it(`Computes Fibonacci Series`, async () => {
438+
const output = await evaluate([
439+
"[[[0], 1]]",
440+
"-j",
441+
"[x[0].concat(x[1]), x[0].slice(-1)[0] + x[1]]",
442+
"-n",
443+
"fib",
444+
"-g",
445+
"fib",
446+
"x[1]<300",
447+
"-j",
448+
"x[0]"
449+
]);
450+
(await toResult(output)).should.deepEqual({
451+
mustPrint: true,
452+
result: [[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]]
453+
});
454+
});
417455
});

0 commit comments

Comments
 (0)