Skip to content

Commit 2e9ac53

Browse files
dmuirsparkprime
authored andcommitted
Migrate std.join to a native implementation. (#581)
* Move std.join to a native implementation.
1 parent bc8a9c8 commit 2e9ac53

File tree

4 files changed

+138
-8
lines changed

4 files changed

+138
-8
lines changed

core/desugarer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct BuiltinDecl {
3434
std::vector<UString> params;
3535
};
3636

37-
static unsigned long max_builtin = 33;
37+
static unsigned long max_builtin = 34;
3838
BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
3939
{
4040
switch (builtin) {
@@ -72,6 +72,7 @@ BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
7272
case 31: return {U"strReplace", {U"str", U"from", U"to"}};
7373
case 32: return {U"asciiLower", {U"str"}};
7474
case 33: return {U"asciiUpper", {U"str"}};
75+
case 34: return {U"join", {U"sep", U"arr"}};
7576
default:
7677
std::cerr << "INTERNAL ERROR: Unrecognized builtin function: " << builtin << std::endl;
7778
std::abort();

core/vm.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ enum FrameKind {
6868
FRAME_STRING_CONCAT, // Stores intermediate state while co-ercing objects
6969
FRAME_SUPER_INDEX, // e in super[e]
7070
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.
7173
};
7274

7375
/** A frame on the stack.
@@ -126,6 +128,9 @@ struct Frame {
126128
/** Used for a variety of purposes. */
127129
std::vector<HeapThunk *> thunks;
128130

131+
/** Used for accumulating a joined string. */
132+
UString str;
133+
129134
/** The context is used in error messages to attempt to find a reasonable name for the
130135
* object, function, or thunk value being executed. If it is a thunk, it is filled
131136
* with the value when the frame terminates.
@@ -864,6 +869,7 @@ class Interpreter {
864869
builtins["strReplace"] = &Interpreter::builtinStrReplace;
865870
builtins["asciiLower"] = &Interpreter::builtinAsciiLower;
866871
builtins["asciiUpper"] = &Interpreter::builtinAsciiUpper;
872+
builtins["join"] = &Interpreter::builtinJoin;
867873
}
868874

869875
/** Clean up the heap, stack, stash, and builtin function ASTs. */
@@ -1429,6 +1435,114 @@ class Interpreter {
14291435
return nullptr;
14301436
}
14311437

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+
14321546
void jsonToHeap(const std::unique_ptr<JsonnetJsonValue> &v, bool &filled, Value &attach)
14331547
{
14341548
// In order to not anger the garbage collector, assign to attach immediately after
@@ -2643,6 +2757,26 @@ class Interpreter {
26432757
}
26442758
} break;
26452759

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+
26462780
default:
26472781
std::cerr << "INTERNAL ERROR: Unknown FrameKind: " << f.kind << std::endl;
26482782
std::abort();
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,2 @@
1-
RUNTIME ERROR: expected string but arr[1] was array
2-
std.jsonnet:262:9-87 function <aux>
3-
std.jsonnet:264:9-49 function <aux>
4-
std.jsonnet:270:7-28 function <anonymous>
1+
RUNTIME ERROR: expected string but arr[1] was array
52
error.std_join_types1.jsonnet:17:1-26
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
1-
RUNTIME ERROR: expected array but arr[0] was string
2-
std.jsonnet:262:9-87 function <aux>
3-
std.jsonnet:272:7-28 function <anonymous>
1+
RUNTIME ERROR: expected array but arr[0] was string
42
error.std_join_types2.jsonnet:17:1-31

0 commit comments

Comments
 (0)