Skip to content

Commit 96ccefa

Browse files
committed
Add importstr, closes #14
1 parent 291c3e6 commit 96ccefa

File tree

9 files changed

+61
-11
lines changed

9 files changed

+61
-11
lines changed

ast.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ enum ASTType {
3535
AST_ERROR,
3636
AST_FUNCTION,
3737
AST_IMPORT,
38+
AST_IMPORTSTR,
3839
AST_INDEX,
3940
AST_LOCAL,
4041
AST_LITERAL_BOOLEAN,
@@ -204,14 +205,22 @@ struct Function : public AST {
204205
{ }
205206
};
206207

207-
/** Represents import e. */
208+
/** Represents import "file". */
208209
struct Import : public AST {
209210
std::string file;
210211
Import(const LocationRange &lr, const std::string &file)
211212
: AST(lr, AST_IMPORT), file(file)
212213
{ }
213214
};
214215

216+
/** Represents importstr "file". */
217+
struct Importstr : public AST {
218+
std::string file;
219+
Importstr(const LocationRange &lr, const std::string &file)
220+
: AST(lr, AST_IMPORTSTR), file(file)
221+
{ }
222+
};
223+
215224
/** Represents both e[e] and the syntax sugar e.f. */
216225
struct Index : public AST {
217226
AST *target;

lexer.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,8 @@ std::list<Token> jsonnet_lex(const std::string &filename, const char *input)
473473
kind = Token::IF;
474474
} else if (id == "import") {
475475
kind = Token::IMPORT;
476+
} else if (id == "importstr") {
477+
kind = Token::IMPORTSTR;
476478
} else if (id == "in") {
477479
kind = Token::IN;
478480
} else if (id == "local") {

lexer.h

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ struct Token {
5454
FUNCTION,
5555
IF,
5656
IMPORT,
57+
IMPORTSTR,
5758
IN,
5859
LOCAL,
5960
NULL_LIT,
@@ -104,6 +105,7 @@ struct Token {
104105
case FUNCTION: return "function";
105106
case IF: return "if";
106107
case IMPORT: return "import";
108+
case IMPORTSTR: return "importstr";
107109
case IN: return "in";
108110
case LOCAL: return "local";
109111
case NULL_LIT: return "null";

parser.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,11 @@ namespace {
536536
return alloc.make<Import>(span(tok, file), file.data);
537537
}
538538

539+
case Token::IMPORTSTR: {
540+
Token file = popExpect(Token::STRING);
541+
return alloc.make<Importstr>(span(tok, file), file.data);
542+
}
543+
539544

540545
// Variables
541546
case Token::DOLLAR:
@@ -994,6 +999,9 @@ static std::string unparse(const AST *ast_)
994999
} else if (auto *ast = dynamic_cast<const Import*>(ast_)) {
9951000
ss << "import " << jsonnet_unparse_escape(ast->file);
9961001

1002+
} else if (auto *ast = dynamic_cast<const Importstr*>(ast_)) {
1003+
ss << "importstr " << jsonnet_unparse_escape(ast->file);
1004+
9971005
} else if (auto *ast = dynamic_cast<const Index*>(ast_)) {
9981006
ss << unparse(ast->target) << "["
9991007
<< unparse(ast->index) << "]";

static_analysis.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ static IdSet static_analysis(AST *ast_, bool in_object, const IdSet &vars)
7676
} else if (dynamic_cast<const Import*>(ast_)) {
7777
// Nothing to do.
7878

79+
} else if (dynamic_cast<const Importstr*>(ast_)) {
80+
// Nothing to do.
81+
7982
} else if (auto *ast = dynamic_cast<const Index*>(ast_)) {
8083
append(r, static_analysis(ast->target, in_object, vars));
8184
append(r, static_analysis(ast->index, in_object, vars));

test_suite/import.jsonnet

+1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ std.assertEqual((import "lib/A_20_func.jsonnet")(), 20) &&
2020
// Each import has its own environment, can't be overidden.
2121
std.assertEqual(local A = 7; local lib = import "lib/A_20.jsonnet"; lib, 20) &&
2222
std.assertEqual(local A = 7, lib = import "lib/A_20.jsonnet"; lib, 20) &&
23+
std.assertEqual(importstr "lib/some_file.txt", "Hello World!\n") &&
2324

2425
true

test_suite/lib/some_file.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello World!

vim/syntax/jsonnet.vim

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ syn match Type "std.length"
2424
syn match Type "std.join"
2525
syn match Type "std.objectFields"
2626
syn match Type "std.filterMap"
27+
syn match Type "std.makeArray"
2728

2829
syn match Type "\$"
2930

@@ -34,7 +35,7 @@ syn match Comment "//.*$"
3435

3536
syn match Keyword "\<[a-zA-Z_][a-z0-9A-Z_]*\w*:"
3637

37-
syntax keyword Special import
38+
syntax keyword Special import importstr
3839
syntax keyword Type local function self super if then else for in
3940
syntax keyword Constant true false null
4041
syntax keyword Error error

vm.cpp

+32-9
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ namespace {
372372
const Identifier *idArrayElement;
373373

374374
/** Cache for imported Jsonnet files. */
375-
std::map<std::string, AST *> cachedImports;
375+
std::map<std::string, const std::string *> cachedImports;
376376

377377
RuntimeError makeError(const LocationRange &loc, const std::string &msg)
378378
{
@@ -581,22 +581,36 @@ namespace {
581581
*/
582582
AST *import(const LocationRange &loc, const std::string &file)
583583
{
584-
auto *expr = cachedImports[file];
585-
if (expr != nullptr) return expr;
584+
const std::string *input = importString(loc, file);
585+
auto *expr = jsonnet_parse(alloc, file, input->c_str());
586+
jsonnet_static_analysis(expr);
587+
return expr;
588+
}
586589

587-
std::string input;
590+
/** Import a file as a string.
591+
*
592+
* If the file has already been imported, then use that version. This maintains
593+
* referential transparency in the case of writes to disk during execution.
594+
*
595+
* \param loc Location of the import statement.
596+
* \param file Path to the filename.
597+
*/
598+
const std::string *importString(const LocationRange &loc, const std::string &file)
599+
{
600+
const std::string *str = cachedImports[file];
601+
if (str != nullptr) return str;
602+
603+
std::string *input = new std::string();
588604
std::ifstream f;
589605
f.open(file.c_str());
590606
if (!f.good()) {
591607
std::string msg = "Couldn't open import \"" + file + "\": ";
592608
msg += std::strerror(errno);
593609
throw makeError(loc, msg);
594610
}
595-
input.assign(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());
596-
expr = jsonnet_parse(alloc, file, input.c_str());
597-
jsonnet_static_analysis(expr);
598-
cachedImports[file] = expr;
599-
return expr;
611+
input->assign(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());
612+
cachedImports[file] = input;
613+
return input;
600614
}
601615

602616
/** Capture the required variables from the environment. */
@@ -645,6 +659,9 @@ namespace {
645659
/** Clean up the heap, stack, stash, and builtin function ASTs. */
646660
~Interpreter()
647661
{
662+
for (const auto &pair : cachedImports) {
663+
delete pair.second;
664+
}
648665
}
649666

650667
const Value &getScratchRegister(void)
@@ -913,6 +930,12 @@ namespace {
913930
goto recurse;
914931
} break;
915932

933+
case AST_IMPORTSTR: {
934+
const auto &ast = *static_cast<const Importstr*>(ast_);
935+
const std::string *str = importString(ast.location, ast.file);
936+
scratch = makeString(*str);
937+
} break;
938+
916939
case AST_INDEX: {
917940
const auto &ast = *static_cast<const Index*>(ast_);
918941
stack.newFrame(FRAME_INDEX_TARGET, ast_);

0 commit comments

Comments
 (0)