Skip to content

Commit

Permalink
Add a way to supply structured data via external variables.
Browse files Browse the repository at this point in the history
  • Loading branch information
sparkprime committed Sep 2, 2015
1 parent c9e4369 commit 0299d84
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 14 deletions.
4 changes: 4 additions & 0 deletions doc/commandline.html
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,13 @@ <h2 id=usage>Usage</h2>
-J / --jpath &lt;dir&gt; Specify an additional library search dir
-V / --var &lt;var&gt;=&lt;val&gt; Specify an 'external' var to the given value
-E / --env &lt;var&gt; Bring in an environment var as an 'external' var
--code-var &lt;var&gt;=&lt;val&gt; As --var but value is Jsonnet code
--code-env &lt;var&gt; As --env but env var contains Jsonnet code
-m / --multi Write multiple files, list files on stdout
-S / --string Expect a string, manifest as plain text
-s / --max-stack &lt;n&gt; Number of allowed stack frames
-t / --max-trace &lt;n&gt; Max length of stack trace before cropping

--gc-min-objects &lt;n&gt; Do not run garbage collector until this many
--gc-growth-trigger &lt;n&gt; Run garbage collector after this amount of object growth
--debug-ast Unparse the parsed AST without executing it
Expand Down
4 changes: 4 additions & 0 deletions doc/commandline.html.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ and &lt;option&gt; can be:
-J / --jpath &lt;dir&gt; Specify an additional library search dir
-V / --var &lt;var&gt;=&lt;val&gt; Specify an 'external' var to the given value
-E / --env &lt;var&gt; Bring in an environment var as an 'external' var
--code-var &lt;var&gt;=&lt;val&gt; As --var but value is Jsonnet code
--code-env &lt;var&gt; As --env but env var contains Jsonnet code
-m / --multi Write multiple files, list files on stdout
-S / --string Expect a string, manifest as plain text
-s / --max-stack &lt;n&gt; Number of allowed stack frames
-t / --max-trace &lt;n&gt; Max length of stack trace before cropping

--gc-min-objects &lt;n&gt; Do not run garbage collector until this many
--gc-growth-trigger &lt;n&gt; Run garbage collector after this amount of object growth
--debug-ast Unparse the parsed AST without executing it
Expand Down
22 changes: 22 additions & 0 deletions jsonnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ void usage(std::ostream &o)
o << " -J / --jpath <dir> Specify an additional library search dir\n";
o << " -V / --var <var>=<val> Specify an 'external' var to the given value\n";
o << " -E / --env <var> Bring in an environment var as an 'external' var\n";
o << " --code-var <var>=<val> As --var but value is Jsonnet code\n";
o << " --code-env <var> As --env but env var contains Jsonnet code\n";
o << " -m / --multi Write multiple files, list files on stdout\n";
o << " -S / --string Expect a string, manifest as plain text\n";
o << " -s / --max-stack <n> Number of allowed stack frames\n";
Expand Down Expand Up @@ -262,6 +264,26 @@ int main(int argc, const char **argv)
const std::string var = var_val.substr(0, eq_pos);
const std::string val = var_val.substr(eq_pos + 1, std::string::npos);
jsonnet_ext_var(vm, var.c_str(), val.c_str());
} else if (arg == "--code-env") {
const std::string var = next_arg(i, args);
const char *val = ::getenv(var.c_str());
if (val == nullptr) {
std::cerr << "ERROR: Environment variable " << var
<< " was undefined." << std::endl;
return EXIT_FAILURE;
}
jsonnet_ext_code(vm, var.c_str(), val);
} else if (arg == "--code-var") {
const std::string var_val = next_arg(i, args);
size_t eq_pos = var_val.find_first_of('=', 0);
if (eq_pos == std::string::npos) {
std::cerr << "ERROR: argument not in form <var>=<val> \""
<< var_val << "\"." << std::endl;
return EXIT_FAILURE;
}
const std::string var = var_val.substr(0, eq_pos);
const std::string val = var_val.substr(eq_pos + 1, std::string::npos);
jsonnet_ext_code(vm, var.c_str(), val.c_str());
} else if (arg == "--gc-min-objects") {
long l = strtol_check(next_arg(i, args));
if (l < 0) {
Expand Down
17 changes: 11 additions & 6 deletions libjsonnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,20 @@ static char *default_import_callback(void *ctx, const char *base, const char *fi
}
}


struct JsonnetVm {
double gcGrowthTrigger;
unsigned maxStack;
unsigned gcMinObjects;
bool debugAst;
unsigned maxTrace;
std::map<std::string, std::string> extVars;
std::map<std::string, VmExt> ext;
JsonnetImportCallback *importCallback;
void *importCallbackContext;
bool stringOutput;
JsonnetVm(void)
: gcGrowthTrigger(2.0), maxStack(500), gcMinObjects(1000), debugAst(false), maxTrace(20),
importCallback(default_import_callback), importCallbackContext(this), stringOutput(false)
importCallback(default_import_callback), importCallbackContext(this),
stringOutput(false)
{ }
};

Expand Down Expand Up @@ -161,7 +161,12 @@ void jsonnet_import_callback(struct JsonnetVm *vm, JsonnetImportCallback *cb, vo

void jsonnet_ext_var(JsonnetVm *vm, const char *key, const char *val)
{
vm->extVars[key] = val;
vm->ext[key] = VmExt(val, false);
}

void jsonnet_ext_code(JsonnetVm *vm, const char *key, const char *val)
{
vm->ext[key] = VmExt(val, true);
}

void jsonnet_debug_ast(JsonnetVm *vm, int v)
Expand All @@ -187,12 +192,12 @@ static char *jsonnet_evaluate_snippet_aux(JsonnetVm *vm, const char *filename,
} else {
jsonnet_static_analysis(expr);
if (multi) {
files = jsonnet_vm_execute_multi(&alloc, expr, vm->extVars, vm->maxStack,
files = jsonnet_vm_execute_multi(&alloc, expr, vm->ext, vm->maxStack,
vm->gcMinObjects, vm->gcGrowthTrigger,
vm->importCallback, vm->importCallbackContext,
vm->stringOutput);
} else {
json_str = jsonnet_vm_execute(&alloc, expr, vm->extVars, vm->maxStack,
json_str = jsonnet_vm_execute(&alloc, expr, vm->ext, vm->maxStack,
vm->gcMinObjects, vm->gcGrowthTrigger,
vm->importCallback, vm->importCallbackContext,
vm->stringOutput);
Expand Down
6 changes: 6 additions & 0 deletions libjsonnet.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ void jsonnet_import_callback(struct JsonnetVm *vm, JsonnetImportCallback *cb, vo
*/
void jsonnet_ext_var(struct JsonnetVm *vm, const char *key, const char *val);

/** Bind a Jsonnet external code var to the given value.
*
* Argument values are copied so memory should be managed by caller.
*/
void jsonnet_ext_code(struct JsonnetVm *vm, const char *key, const char *val);

/** If set to 1, will emit the Jsonnet input after parsing / desugaring. */
void jsonnet_debug_ast(struct JsonnetVm *vm, int v);

Expand Down
2 changes: 1 addition & 1 deletion test_suite/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ for TEST in *.jsonnet ; do
if [ -r "$TEST.golden" ] ; then
GOLDEN=$(cat "$TEST.golden")
fi
OUTPUT="$(../jsonnet --gc-min-objects 1 --gc-growth-trigger 1 "$TEST" 2>&1 )"
OUTPUT="$(../jsonnet --gc-min-objects 1 --gc-growth-trigger 1 --var var1=test --code-var var2='{x:1, y: 2}' "$TEST" 2>&1 )"
EXIT_CODE=$?
if [ $EXIT_CODE -ne $EXPECTED_EXIT_CODE ] ; then
FAILED=$((FAILED + 1))
Expand Down
8 changes: 8 additions & 0 deletions test_suite/stdlib.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,12 @@ std.assertEqual(std.setMember("a", ["b", "c"]), false) &&
std.assertEqual(std.thisFile, "stdlib.jsonnet") &&
std.assertEqual(import "this_file/a.jsonnet", "this_file/a.jsonnet") &&
std.assertEqual(import "this_file/b.jsonnet", "this_file/a.jsonnet") &&


std.assertEqual(std.extVar("var1"), "test") &&

std.assertEqual(std.toString(std.extVar("var2")), "{\"x\": 1, \"y\": 2}") &&
std.assertEqual(std.extVar("var2"), {x: 1, y: 2}) &&
std.assertEqual(std.extVar("var2") { x+: 2}.x, 3) &&

true
24 changes: 19 additions & 5 deletions vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ namespace {
FRAME_APPLY_TARGET,
FRAME_BINARY_LEFT,
FRAME_BINARY_RIGHT,
FRAME_BUILTIN_EXT_VAR,
FRAME_BUILTIN_FILTER,
FRAME_BUILTIN_FORCE_THUNKS,
FRAME_CALL,
Expand Down Expand Up @@ -379,6 +380,9 @@ namespace {
}
};

/** Typedef to save some typing. */
typedef std::map<std::string, VmExt> ExtMap;

/** Typedef to save some typing. */
typedef std::map<std::string, std::string> StrMap;

Expand Down Expand Up @@ -421,7 +425,7 @@ namespace {
const ImportCacheValue *> cachedImports;

/** External variables for std.extVar. */
StrMap externalVars;
ExtMap externalVars;

/** The callback used for loading imported files. */
JsonnetImportCallback *importCallback;
Expand Down Expand Up @@ -735,7 +739,7 @@ namespace {
*
* \param loc The location range of the file to be executed.
*/
Interpreter(Allocator *alloc, const StrMap &ext_vars,
Interpreter(Allocator *alloc, const ExtMap &ext_vars,
unsigned max_stack, double gc_min_objects, double gc_growth_trigger,
JsonnetImportCallback *import_callback, void *import_callback_context)
: heap(gc_min_objects, gc_growth_trigger), stack(max_stack), alloc(alloc),
Expand Down Expand Up @@ -1756,7 +1760,17 @@ namespace {
if (externalVars.find(var) == externalVars.end()) {
throw makeError(ast.location, "Undefined external variable: " + var);
}
scratch = makeString(externalVars[var]);
const VmExt &ext = externalVars[var];
if (ext.isCode) {
std::string filename = "<extvar:" + var + ">";
AST *expr = jsonnet_parse(alloc, filename, ext.data.c_str());
jsonnet_static_analysis(expr);
ast_ = expr;
stack.pop();
goto recurse;
} else {
scratch = makeString(ext.data);
}
} break;

default:
Expand Down Expand Up @@ -2217,7 +2231,7 @@ namespace {
}

std::string jsonnet_vm_execute(Allocator *alloc, const AST *ast,
const StrMap &ext_vars,
const ExtMap &ext_vars,
unsigned max_stack, double gc_min_objects,
double gc_growth_trigger,
JsonnetImportCallback *import_callback, void *ctx,
Expand All @@ -2233,7 +2247,7 @@ std::string jsonnet_vm_execute(Allocator *alloc, const AST *ast,
}
}

StrMap jsonnet_vm_execute_multi(Allocator *alloc, const AST *ast, const StrMap &ext_vars,
StrMap jsonnet_vm_execute_multi(Allocator *alloc, const AST *ast, const ExtMap &ext_vars,
unsigned max_stack, double gc_min_objects, double gc_growth_trigger,
JsonnetImportCallback *import_callback, void *ctx,
bool string_output)
Expand Down
17 changes: 15 additions & 2 deletions vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,22 @@ struct RuntimeError {
{ }
};

/** Stores external values / code. */
struct VmExt {
std::string data;
bool isCode;
VmExt() : isCode(false) { }
VmExt(const std::string &data, bool is_code)
: data(data), isCode(is_code)
{ }
};


/** Execute the program and return the value as a JSON string.
*
* \param alloc The allocator used to create the ast.
* \param ast The program to execute.
* \param ext The external vars / code.
* \param max_stack Recursion beyond this level gives an error.
* \param gc_min_objects The garbage collector does not run when the heap is this small.
* \param gc_growth_trigger Growth since last garbage collection cycle to trigger a new cycle.
Expand All @@ -55,7 +67,7 @@ struct RuntimeError {
* \returns The JSON result in string form.
*/
std::string jsonnet_vm_execute(Allocator *alloc, const AST *ast,
const std::map<std::string, std::string> &ext_vars,
const std::map<std::string, VmExt> &ext,
unsigned max_stack, double gc_min_objects,
double gc_growth_trigger,
JsonnetImportCallback *import_callback, void *import_callback_ctx,
Expand All @@ -67,6 +79,7 @@ std::string jsonnet_vm_execute(Allocator *alloc, const AST *ast,
*
* \param alloc The allocator used to create the ast.
* \param ast The program to execute.
* \param ext The external vars / code.
* \param max_stack Recursion beyond this level gives an error.
* \param gc_min_objects The garbage collector does not run when the heap is this small.
* \param gc_growth_trigger Growth since last garbage collection cycle to trigger a new cycle.
Expand All @@ -77,7 +90,7 @@ std::string jsonnet_vm_execute(Allocator *alloc, const AST *ast,
* \returns A mapping from filename to the JSON strings for that file.
*/
std::map<std::string, std::string> jsonnet_vm_execute_multi(
Allocator *alloc, const AST *ast, const std::map<std::string, std::string> &ext_vars,
Allocator *alloc, const AST *ast, const std::map<std::string, VmExt> &ext,
unsigned max_stack, double gc_min_objects, double gc_growth_trigger,
JsonnetImportCallback *import_callback, void *import_callback_ctx,
bool string_output);
Expand Down

0 comments on commit 0299d84

Please sign in to comment.