Skip to content

Commit 66a0006

Browse files
angusleessparkprime
authored andcommitted
Add importbin statement
Add `importbin`. Like `importstr` but returns an array of numbers (integers 0-255). Note! This changes the `JsonnetImportCallback` function type signature, and requires modification to existing code that uses custom import callbacks. The included python bindings have been updated.
1 parent e43ca8e commit 66a0006

28 files changed

+237
-79
lines changed

case_studies/micromanage/validate.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ def err(path, msg, note=None):
3838
raise ConfigError('%s: %s' % (render_path(path), msg), note)
3939

4040
_KEYWORDS = {
41-
'import', 'importstr', 'function', 'self', 'super', 'assert', 'if', 'then',
42-
'else', 'for', 'in', 'local', 'tailstrict', 'true', 'false', 'null', 'error',
41+
'import', 'importstr', 'importbin', 'function', 'self', 'super', 'assert', 'if',
42+
'then', 'else', 'for', 'in', 'local', 'tailstrict', 'true', 'false', 'null', 'error',
4343
}
4444

4545
def _isidentifier(name):

core/ast.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ enum ASTType {
4545
AST_FUNCTION,
4646
AST_IMPORT,
4747
AST_IMPORTSTR,
48+
AST_IMPORTBIN,
4849
AST_INDEX,
4950
AST_IN_SUPER,
5051
AST_LITERAL_BOOLEAN,
@@ -80,6 +81,7 @@ static inline std::string ASTTypeToString(ASTType type)
8081
case AST_FUNCTION: return "AST_FUNCTION";
8182
case AST_IMPORT: return "AST_IMPORT";
8283
case AST_IMPORTSTR: return "AST_IMPORTSTR";
84+
case AST_IMPORTBIN: return "AST_IMPORTBIN";
8385
case AST_INDEX: return "AST_INDEX";
8486
case AST_IN_SUPER: return "AST_IN_SUPER";
8587
case AST_LITERAL_BOOLEAN: return "AST_LITERAL_BOOLEAN";
@@ -455,6 +457,15 @@ struct Importstr : public AST {
455457
}
456458
};
457459

460+
/** Represents importbin "file". */
461+
struct Importbin : public AST {
462+
LiteralString *file;
463+
Importbin(const LocationRange &lr, const Fodder &open_fodder, LiteralString *file)
464+
: AST(lr, AST_IMPORTBIN, open_fodder), file(file)
465+
{
466+
}
467+
};
468+
458469
/** Represents both e[e] and the syntax sugar e.f.
459470
*
460471
* One of index and id will be nullptr before desugaring. After desugaring id will be nullptr.
@@ -647,8 +658,8 @@ struct ObjectField {
647658

648659
ObjectField(enum Kind kind, const Fodder &fodder1, const Fodder &fodder2,
649660
const Fodder &fodder_l, const Fodder &fodder_r, enum Hide hide, bool super_sugar,
650-
bool method_sugar, AST *expr1, const Identifier *id, const LocationRange &id_lr,
651-
const ArgParams &params, bool trailing_comma, const Fodder &op_fodder, AST *expr2,
661+
bool method_sugar, AST *expr1, const Identifier *id, const LocationRange &id_lr,
662+
const ArgParams &params, bool trailing_comma, const Fodder &op_fodder, AST *expr2,
652663
AST *expr3, const Fodder &comma_fodder)
653664
: kind(kind),
654665
fodder1(fodder1),

core/desugarer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,12 @@ class Desugarer {
772772
desugar(file, obj_level);
773773
ast->file = dynamic_cast<LiteralString *>(file);
774774

775+
} else if (auto *ast = dynamic_cast<Importbin *>(ast_)) {
776+
// TODO(dcunnin): Abstract this into a template function if it becomes more common.
777+
AST *file = ast->file;
778+
desugar(file, obj_level);
779+
ast->file = dynamic_cast<LiteralString *>(file);
780+
775781
} else if (auto *ast = dynamic_cast<InSuper *>(ast_)) {
776782
desugar(ast->element, obj_level);
777783

core/formatter.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ static AST *left_recursive_deep(AST *ast_)
6767
* \param fodder The fodder to print
6868
* \param space_before Whether a space should be printed before any other output.
6969
* \param separate_token If the last fodder was an interstitial, whether a space should follow it.
70-
* \param final Whether fodder is the last one in
70+
* \param final Whether fodder is the last one in
7171
*/
7272
void fodder_fill(std::ostream &o, const Fodder &fodder, bool space_before, bool separate_token, bool final)
7373
{
@@ -414,6 +414,10 @@ class Unparser {
414414
o << "importstr";
415415
unparse(ast->file, true);
416416

417+
} else if (auto *ast = dynamic_cast<const Importbin *>(ast_)) {
418+
o << "importbin";
419+
unparse(ast->file, true);
420+
417421
} else if (auto *ast = dynamic_cast<const InSuper *>(ast_)) {
418422
unparse(ast->element, true);
419423
fill(ast->inFodder, true, true);
@@ -1784,6 +1788,11 @@ class FixIndentation {
17841788
Indent new_indent = newIndent(open_fodder(ast->file), indent, column + 1);
17851789
expr(ast->file, new_indent, true);
17861790

1791+
} else if (auto *ast = dynamic_cast<Importbin *>(ast_)) {
1792+
column += 9; // importbin
1793+
Indent new_indent = newIndent(open_fodder(ast->file), indent, column + 1);
1794+
expr(ast->file, new_indent, true);
1795+
17871796
} else if (auto *ast = dynamic_cast<InSuper *>(ast_)) {
17881797
expr(ast->element, indent, space_before);
17891798
fill(ast->inFodder, true, true, indent.lineUp);

core/lexer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ static const std::map<std::string, Token::Kind> keywords = {
190190
{"if", Token::IF},
191191
{"import", Token::IMPORT},
192192
{"importstr", Token::IMPORTSTR},
193+
{"importbin", Token::IMPORTBIN},
193194
{"in", Token::IN},
194195
{"local", Token::LOCAL},
195196
{"null", Token::NULL_LIT},

core/lexer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ struct Token {
263263
IF,
264264
IMPORT,
265265
IMPORTSTR,
266+
IMPORTBIN,
266267
IN,
267268
LOCAL,
268269
NULL_LIT,
@@ -346,6 +347,7 @@ struct Token {
346347
case IF: return "if";
347348
case IMPORT: return "import";
348349
case IMPORTSTR: return "importstr";
350+
case IMPORTBIN: return "importbin";
349351
case IN: return "in";
350352
case LOCAL: return "local";
351353
case NULL_LIT: return "null";

core/lexer_test.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ TEST(Lexer, TestKeywords)
309309
testLex("if", "if", {Token(Token::Kind::IF, "if")}, "");
310310
testLex("import", "import", {Token(Token::Kind::IMPORT, "import")}, "");
311311
testLex("importstr", "importstr", {Token(Token::Kind::IMPORTSTR, "importstr")}, "");
312+
testLex("importbin", "importbin", {Token(Token::Kind::IMPORTBIN, "importbin")}, "");
312313
testLex("in", "in", {Token(Token::Kind::IN, "in")}, "");
313314
testLex("local", "local", {Token(Token::Kind::LOCAL, "local")}, "");
314315
testLex("null", "null", {Token(Token::Kind::NULL_LIT, "null")}, "");

core/libjsonnet.cpp

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,20 @@ static void memory_panic(void)
4545
static char *from_string(JsonnetVm *vm, const std::string &v)
4646
{
4747
char *r = jsonnet_realloc(vm, nullptr, v.length() + 1);
48-
std::strcpy(r, v.c_str());
48+
std::memcpy(r, v.c_str(), v.length() + 1);
4949
return r;
5050
}
5151

52-
static char *default_import_callback(void *ctx, const char *dir, const char *file,
53-
char **found_here_cptr, int *success);
52+
static char *from_string_nonull(JsonnetVm *vm, const std::string &v, size_t *buflen)
53+
{
54+
char *r = jsonnet_realloc(vm, nullptr, v.length());
55+
std::memcpy(r, v.data(), v.length());
56+
*buflen = v.length();
57+
return r;
58+
}
59+
60+
static int default_import_callback(void *ctx, const char *dir, const char *file,
61+
char **found_here_cptr, char **buf, size_t *buflen);
5462

5563
const char *jsonnet_json_extract_string(JsonnetVm *vm, const struct JsonnetJsonValue *v)
5664
{
@@ -229,8 +237,8 @@ static enum ImportStatus try_path(const std::string &dir, const std::string &rel
229237
return IMPORT_STATUS_OK;
230238
}
231239

232-
static char *default_import_callback(void *ctx, const char *dir, const char *file,
233-
char **found_here_cptr, int *success)
240+
static int default_import_callback(void *ctx, const char *dir, const char *file,
241+
char **found_here_cptr, char **buf, size_t *buflen)
234242
{
235243
auto *vm = static_cast<JsonnetVm *>(ctx);
236244

@@ -243,25 +251,23 @@ static char *default_import_callback(void *ctx, const char *dir, const char *fil
243251
// If not found, try library search path.
244252
while (status == IMPORT_STATUS_FILE_NOT_FOUND) {
245253
if (jpaths.size() == 0) {
246-
*success = 0;
247254
const char *err = "no match locally or in the Jsonnet library paths.";
248-
char *r = jsonnet_realloc(vm, nullptr, std::strlen(err) + 1);
249-
std::strcpy(r, err);
250-
return r;
255+
*buf = from_string_nonull(vm, err, buflen);
256+
return 1; // failure
251257
}
252258
status = try_path(jpaths.back(), file, input, found_here, err_msg);
253259
jpaths.pop_back();
254260
}
255261

256262
if (status == IMPORT_STATUS_IO_ERROR) {
257-
*success = 0;
258-
return from_string(vm, err_msg);
259-
} else {
260-
assert(status == IMPORT_STATUS_OK);
261-
*success = 1;
262-
*found_here_cptr = from_string(vm, found_here);
263-
return from_string(vm, input);
263+
*buf = from_string_nonull(vm, err_msg, buflen);
264+
return 1; // failure
264265
}
266+
267+
assert(status == IMPORT_STATUS_OK);
268+
*found_here_cptr = from_string(vm, found_here);
269+
*buf = from_string_nonull(vm, input, buflen);
270+
return 0; // success
265271
}
266272

267273
#define TRY try {

core/parser.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,7 @@ class Parser {
584584
case Token::IN:
585585
case Token::IMPORT:
586586
case Token::IMPORTSTR:
587+
case Token::IMPORTBIN:
587588
case Token::LOCAL:
588589
case Token::PAREN_R:
589590
case Token::SEMICOLON:
@@ -860,6 +861,23 @@ class Parser {
860861
}
861862
}
862863

864+
case Token::IMPORTBIN: {
865+
pop();
866+
AST *body = parse(MAX_PRECEDENCE);
867+
if (body->type == AST_LITERAL_STRING) {
868+
auto *lit = static_cast<LiteralString *>(body);
869+
if (lit->tokenKind == LiteralString::BLOCK) {
870+
throw StaticError(lit->location,
871+
"Cannot use text blocks in import statements.");
872+
}
873+
return alloc->make<Importbin>(span(begin, body), begin.fodder, lit);
874+
} else {
875+
std::stringstream ss;
876+
ss << "computed imports are not allowed.";
877+
throw StaticError(body->location, ss.str());
878+
}
879+
}
880+
863881
case Token::LOCAL: {
864882
pop();
865883
Local::Binds binds;
@@ -934,7 +952,7 @@ class Parser {
934952
op_precedence = APPLY_PRECEDENCE;
935953
break;
936954

937-
default:
955+
default:
938956
// This happens when we reach EOF or the terminating token of an outer context.
939957
return lhs;
940958
}

core/parser_test.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ TEST(Parser, TestTuple)
118118

119119
testParse("import 'foo.jsonnet'");
120120
testParse("importstr 'foo.text'");
121+
testParse("importbin 'foo.text'");
121122

122123
testParse("{a: b} + {c: d}");
123124
testParse("{a: b}{c: d}");
@@ -315,6 +316,9 @@ TEST(Parser, TestInvalidImport)
315316
testParseError("importstr (a b)",
316317
R"_(test:1:14: expected token ")" but got (IDENTIFIER, "b"))_");
317318
testParseError("importstr (a+b)", "test:1:11-16: computed imports are not allowed.");
319+
testParseError("importbin (a b)",
320+
R"_(test:1:14: expected token ")" but got (IDENTIFIER, "b"))_");
321+
testParseError("importbin (a+b)", "test:1:11-16: computed imports are not allowed.");
318322
}
319323

320324
TEST(Parser, TestInvalidOperator)

0 commit comments

Comments
 (0)