@@ -68,6 +68,8 @@ enum FrameKind {
68
68
FRAME_STRING_CONCAT, // Stores intermediate state while co-ercing objects
69
69
FRAME_SUPER_INDEX, // e in super[e]
70
70
FRAME_UNARY, // e in -e
71
+ FRAME_BUILTIN_JOIN_STRINGS, // When executing std.join over strings, used to hold intermediate state.
72
+ FRAME_BUILTIN_JOIN_ARRAYS, // When executing std.join over arrays, used to hold intermediate state.
71
73
};
72
74
73
75
/* * A frame on the stack.
@@ -126,6 +128,9 @@ struct Frame {
126
128
/* * Used for a variety of purposes. */
127
129
std::vector<HeapThunk *> thunks;
128
130
131
+ /* * Used for accumulating a joined string. */
132
+ UString str;
133
+
129
134
/* * The context is used in error messages to attempt to find a reasonable name for the
130
135
* object, function, or thunk value being executed. If it is a thunk, it is filled
131
136
* with the value when the frame terminates.
@@ -864,6 +869,7 @@ class Interpreter {
864
869
builtins[" strReplace" ] = &Interpreter::builtinStrReplace;
865
870
builtins[" asciiLower" ] = &Interpreter::builtinAsciiLower;
866
871
builtins[" asciiUpper" ] = &Interpreter::builtinAsciiUpper;
872
+ builtins[" join" ] = &Interpreter::builtinJoin;
867
873
}
868
874
869
875
/* * Clean up the heap, stack, stash, and builtin function ASTs. */
@@ -1429,6 +1435,114 @@ class Interpreter {
1429
1435
return nullptr ;
1430
1436
}
1431
1437
1438
+ void joinString (UString &running, const Value &sep, unsigned idx, const Value &elt)
1439
+ {
1440
+ if (elt.t == Value::NULL_TYPE) {
1441
+ return ;
1442
+ }
1443
+ if (elt.t != Value::STRING) {
1444
+ std::stringstream ss;
1445
+ ss << " expected string but arr[" << idx << " ] was " << type_str (elt);
1446
+ throw makeError (stack.top ().location , ss.str ());
1447
+ }
1448
+ if (!running.empty ()) {
1449
+ running.append (static_cast <HeapString *>(sep.v .h )->value );
1450
+ }
1451
+ running.append (static_cast <HeapString *>(elt.v .h )->value );
1452
+ }
1453
+
1454
+ const AST *joinStrings (const Value &sep, const Value &arr, unsigned idx, UString running)
1455
+ {
1456
+ const auto & elements = static_cast <HeapArray*>(arr.v .h )->elements ;
1457
+ if (idx >= elements.size ()) {
1458
+ scratch = makeString (running);
1459
+ } else {
1460
+ for (int i = idx; i < elements.size (); ++i) {
1461
+ auto *th = elements[i];
1462
+ if (th->filled ) {
1463
+ joinString (running, sep, i, th->content );
1464
+ } else {
1465
+ Frame &f = stack.top ();
1466
+ f.kind = FRAME_BUILTIN_JOIN_STRINGS;
1467
+ f.val = sep;
1468
+ f.val2 = arr;
1469
+ f.str = running;
1470
+ f.elementId = i;
1471
+ stack.newCall (f.location , th, th->self , th->offset , th->upValues );
1472
+ return th->body ;
1473
+ }
1474
+ }
1475
+ scratch = makeString (running);
1476
+ }
1477
+ return nullptr ;
1478
+ }
1479
+
1480
+ void joinArray (std::vector<HeapThunk*> &running, const Value &sep, unsigned idx, const Value &elt)
1481
+ {
1482
+ if (elt.t == Value::NULL_TYPE) {
1483
+ return ;
1484
+ }
1485
+ if (elt.t != Value::ARRAY) {
1486
+ std::stringstream ss;
1487
+ ss << " expected array but arr[" << idx << " ] was " << type_str (elt);
1488
+ throw makeError (stack.top ().location , ss.str ());
1489
+ }
1490
+ if (!running.empty ()) {
1491
+ auto & elts = static_cast <HeapArray *>(sep.v .h )->elements ;
1492
+ running.insert (running.end (), elts.begin (), elts.end ());
1493
+ }
1494
+ auto & elts = static_cast <HeapArray *>(elt.v .h )->elements ;
1495
+ running.insert (running.end (), elts.begin (), elts.end ());
1496
+ }
1497
+
1498
+ const AST *joinArrays (const Value &sep, const Value &arr, unsigned idx, std::vector<HeapThunk*> &running)
1499
+ {
1500
+ const auto & elements = static_cast <HeapArray*>(arr.v .h )->elements ;
1501
+ if (idx >= elements.size ()) {
1502
+ scratch = makeArray (running);
1503
+ } else {
1504
+ for (int i = idx; i < elements.size (); ++i) {
1505
+ auto *th = elements[i];
1506
+ if (th->filled ) {
1507
+ joinArray (running, sep, i, th->content );
1508
+ } else {
1509
+ Frame &f = stack.top ();
1510
+ f.kind = FRAME_BUILTIN_JOIN_ARRAYS;
1511
+ f.val = sep;
1512
+ f.val2 = arr;
1513
+ f.thunks = running;
1514
+ f.elementId = i;
1515
+ stack.newCall (f.location , th, th->self , th->offset , th->upValues );
1516
+ return th->body ;
1517
+ }
1518
+ }
1519
+ scratch = makeArray (running);
1520
+ }
1521
+ return nullptr ;
1522
+ }
1523
+
1524
+ const AST *builtinJoin (const LocationRange &loc, const std::vector<Value> &args)
1525
+ {
1526
+ if (args[0 ].t != Value::ARRAY && args[0 ].t != Value::STRING) {
1527
+ std::stringstream ss;
1528
+ ss << " join first parameter should be string or array, got " << type_str (args[0 ]);
1529
+ throw makeError (loc, ss.str ());
1530
+ }
1531
+ if (args[1 ].t != Value::ARRAY) {
1532
+ std::stringstream ss;
1533
+ ss << " join second parameter should be array, got " << type_str (args[1 ]);
1534
+ throw makeError (loc, ss.str ());
1535
+ }
1536
+ Frame &f = stack.top ();
1537
+ if (args[0 ].t == Value::STRING) {
1538
+ f.str .clear ();
1539
+ return joinStrings (args[0 ], args[1 ], 0 , f.str );
1540
+ } else {
1541
+ f.thunks .clear ();
1542
+ return joinArrays (args[0 ], args[1 ], 0 , f.thunks );
1543
+ }
1544
+ }
1545
+
1432
1546
void jsonToHeap (const std::unique_ptr<JsonnetJsonValue> &v, bool &filled, Value &attach)
1433
1547
{
1434
1548
// In order to not anger the garbage collector, assign to attach immediately after
@@ -2643,6 +2757,26 @@ class Interpreter {
2643
2757
}
2644
2758
} break ;
2645
2759
2760
+ case FRAME_BUILTIN_JOIN_STRINGS: {
2761
+ joinString (f.str , f.val , f.elementId , scratch);
2762
+ f.elementId ++;
2763
+ auto *ast = joinStrings (f.val , f.val2 , f.elementId , f.str );
2764
+ if (ast != nullptr ) {
2765
+ ast_ = ast;
2766
+ goto recurse;
2767
+ }
2768
+ } break ;
2769
+
2770
+ case FRAME_BUILTIN_JOIN_ARRAYS: {
2771
+ joinArray (f.thunks , f.val , f.elementId , scratch);
2772
+ f.elementId ++;
2773
+ auto *ast = joinArrays (f.val , f.val2 , f.elementId , f.thunks );
2774
+ if (ast != nullptr ) {
2775
+ ast_ = ast;
2776
+ goto recurse;
2777
+ }
2778
+ } break ;
2779
+
2646
2780
default :
2647
2781
std::cerr << " INTERNAL ERROR: Unknown FrameKind: " << f.kind << std::endl;
2648
2782
std::abort ();
0 commit comments