├── example ├── fibonacci.jsonnet ├── t.jsonnet ├── example_operators.jsonnet ├── bar_menu.3.jsonnet ├── evaluate_file.lua ├── bar_menu_utils.jsonnet ├── martinis.jsonnet ├── evaluate_snippet.lua ├── bar_menu.6.jsonnet ├── bar_menu.5.jsonnet └── bar_menu.2.jsonnet ├── libjsonnet ├── README ├── MANIFEST.in ├── jsonnet_test_file.py ├── jsonnet_test_snippet.py ├── static_analysis.h ├── libjsonnet_test_file.c ├── libjsonnet_test_snippet.c ├── parser.h ├── setup.py ├── static_error.h ├── vm.h ├── Makefile ├── lexer.h ├── _jsonnet.c ├── static_analysis.cpp ├── libjsonnet.h ├── libjsonnet.cpp ├── ast.h ├── LICENSE ├── state.h ├── jsonnet.cpp ├── lexer.cpp └── std.jsonnet ├── Makefile ├── .gitignore ├── README.markdown ├── jsonnet.lua └── LICENSE /example/fibonacci.jsonnet: -------------------------------------------------------------------------------- 1 | local fibonacci(n) = 2 | if n <= 1 then 3 | 1 4 | else 5 | fibonacci(n - 1) + fibonacci(n - 2); 6 | 7 | fibonacci(25) 8 | -------------------------------------------------------------------------------- /example/t.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | person1: { 3 | name: "Alice", 4 | welcome: "Hello " + self.name + "!", 5 | }, 6 | person2: self.person1 { name: "Bob" }, 7 | } -------------------------------------------------------------------------------- /libjsonnet/README: -------------------------------------------------------------------------------- 1 | Jsonnet - The data templating language 2 | 3 | Website: http://google.github.io/jsonnet/doc/ 4 | Discussion Forum: https://groups.google.com/forum/#!forum/jsonnet 5 | -------------------------------------------------------------------------------- /example/example_operators.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | foo: [1, 2, 3], 3 | bar: [x * x for x in self.foo if x >= 2], 4 | baz: { ["field" + x]: x for x in self.foo }, 5 | obj: { ["foo" + "bar"]: 3 }, 6 | } -------------------------------------------------------------------------------- /libjsonnet/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE libjsonnet.h libjsonnet.cpp _jsonnet.c lexer.h lexer.cpp ast.h parser.h parser.cpp static_error.h static_analysis.h static_analysis.cpp state.h vm.h vm.cpp std.jsonnet Makefile 2 | #recursive-include test_suite examples gc_stress benchmarks editors 3 | -------------------------------------------------------------------------------- /example/bar_menu.3.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | foo: 3, 3 | bar: 2 * self.foo, // Multiplication. 4 | baz: "The value " + self.bar + " is " 5 | + (if self.bar > 5 then "large" else "small") + ".", 6 | array: [1, 2, 3] + [4], 7 | obj: {a: 1, b: 2} + {b: 3, c: 4}, 8 | equality: 1 == "1", 9 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIB = /usr/lib64/ 2 | exist = $(shell if [ -f $(LIB) ]; then echo "true"; else echo "false"; fi;) 3 | ifeq (exist, "false") 4 | LIB = /usr/lib/ 5 | endif 6 | install: 7 | cd libjsonnet && make libjsonnet.so && cp libjsonnet.so $(LIB) && cd ../ && cp jsonnet.lua /usr/local/share/luajit-*/ 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/evaluate_file.lua: -------------------------------------------------------------------------------- 1 | local jso = require "jsonnet" 2 | jsonnet = jso:new() 3 | jsonnet:make() 4 | if arg[1] ~= nil then 5 | filename = arg[1] 6 | else 7 | filename = "t.jsonnet" 8 | end 9 | res,err = jsonnet:evaluate_file(filename) 10 | jsonnet:destroy() 11 | if err == nil then 12 | print(res) 13 | else 14 | print(err) 15 | end -------------------------------------------------------------------------------- /example/bar_menu_utils.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | equal_parts(size, ingredients):: 3 | if std.length(ingredients) == 0 then 4 | error "No ingredients specified." 5 | else [ 6 | { kind: i, qty: size/std.length(ingredients) } 7 | for i in ingredients 8 | ], 9 | id:: function(x) x, 10 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | -------------------------------------------------------------------------------- /example/martinis.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | "Vodka Martini": { 3 | ingredients: [ 4 | { kind: "Vodka", qty: 2 }, 5 | { kind: "Dry White Vermouth", qty: 1 }, 6 | ], 7 | garnish: "Olive", 8 | served: "Straight Up", 9 | }, 10 | Cosmopolitan: { 11 | ingredients: [ 12 | { kind: "Vodka", qty: 2 }, 13 | { kind: "Triple Sec", qty: 0.5 }, 14 | { kind: "Cranberry Juice", qty: 0.75 }, 15 | { kind: "Lime Juice", qty: 0.5 }, 16 | ], 17 | garnish: "Orange Peel", 18 | served: "Straight Up", 19 | }, 20 | } -------------------------------------------------------------------------------- /example/evaluate_snippet.lua: -------------------------------------------------------------------------------- 1 | local jso = require "jsonnet" 2 | jsonnet = jso:new() 3 | jsonnet:make() 4 | print("------- Normal test data --------") 5 | local snippet = '{ person1: { name: "Alice", welcome: "Hello " + self.name + "!", }, person2: self.person1 { name: "Bob" },}' 6 | res,err = jsonnet:evaluate_snippet(snippet) 7 | if err == nil then 8 | print(res) 9 | else 10 | print(err) 11 | end 12 | 13 | print("------- Abnormal test data --------") 14 | local snippet = '!@##$@%$%^%&{ person1: { name: "Alice", welcome: "Hello " + self.name + "!", }, person2: self.person1 { name: "Bob" },}' 15 | res,err = jsonnet:evaluate_snippet(snippet) 16 | if err == nil then 17 | print(res) 18 | else 19 | print(err) 20 | end 21 | jsonnet:destroy() -------------------------------------------------------------------------------- /example/bar_menu.6.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | cocktails: import "martinis.jsonnet" + { 3 | Manhattan: { 4 | ingredients: [ 5 | { kind: "Rye", qty: 2.5 }, 6 | { kind: "Sweet Red Vermouth", qty: 1 }, 7 | { kind: "Angostura", qty: "dash" }, 8 | ], 9 | garnish: "Maraschino Cherry", 10 | served: "Straight Up", 11 | }, 12 | Cosmopolitan: { 13 | ingredients: [ 14 | { kind: "Vodka", qty: 1.5 }, 15 | { kind: "Cointreau", qty: 1 }, 16 | { kind: "Cranberry Juice", qty: 2 }, 17 | { kind: "Lime Juice", qty: 1 }, 18 | ], 19 | garnish: "Lime Wheel", 20 | served: "Straight Up", 21 | }, 22 | } 23 | } -------------------------------------------------------------------------------- /libjsonnet/jsonnet_test_file.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import sys 16 | import _jsonnet 17 | 18 | if len(sys.argv) != 2: 19 | raise Exception("Usage: ") 20 | 21 | sys.stdout.write(_jsonnet.evaluate_file(sys.argv[1])) 22 | -------------------------------------------------------------------------------- /libjsonnet/jsonnet_test_snippet.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import sys 16 | import _jsonnet 17 | 18 | if len(sys.argv) != 2: 19 | raise Exception("Usage: ") 20 | 21 | sys.stdout.write(_jsonnet.evaluate_snippet("snippet", sys.argv[1])) 22 | -------------------------------------------------------------------------------- /example/bar_menu.5.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | cocktails: { 3 | "Bee's Knees": { 4 | // Construct the ingredients by using 4/3 oz 5 | // of each element in the given list. 6 | ingredients: [ // Array comprehension. 7 | { kind: i, qty: 4/3 } 8 | for i in ["Honey Syrup", "Lemon Juice", "Farmers Gin"] 9 | ], 10 | garnish: "Lemon Twist", 11 | served: "Straight Up", 12 | }, 13 | } + { // Object comprehension. 14 | [sd.name + "Screwdriver"]: { 15 | ingredients: [ 16 | { kind: "Vodka", qty: 1.5 }, 17 | { kind: sd.fruit, qty: 3 }, 18 | ], 19 | garnish: null, 20 | served: "On The Rocks" 21 | } for sd in [ 22 | {name: "Yellow ", fruit: "Lemonade"}, 23 | {name: "", fruit: "Orange Juice"}, 24 | ] 25 | } 26 | } -------------------------------------------------------------------------------- /libjsonnet/static_analysis.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef JSONNET_STATIC_ANALYSIS_H 18 | #define JSONNET_STATIC_ANALYSIS_H 19 | 20 | #include "ast.h" 21 | 22 | /** Check the ast for appropriate use of self, super, and correctly bound variables. Also 23 | * initialize the freeVariables member of function and object ASTs. 24 | */ 25 | void jsonnet_static_analysis(AST *ast); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /example/bar_menu.2.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | cocktails: { 3 | "Tom Collins": { 4 | ingredients: [ 5 | { kind: "Farmers Gin", qty: 1.5 }, 6 | { kind: "Lemon", qty: 1 }, 7 | { kind: "Simple Syrup", qty: 0.5 }, 8 | { kind: "Soda", qty: 2 }, 9 | { kind: "Angostura", qty: "dash" }, 10 | ], 11 | garnish: "Maraschino Cherry", 12 | served: "Tall", 13 | }, 14 | Martini: { 15 | ingredients: [ 16 | { 17 | // Evaluate a path to get the first ingredient of the Tom Collins. 18 | kind: $.cocktails["Tom Collins"].ingredients[0].kind, 19 | // or $["cocktails"]["Tom Collins"]["ingredients"][0]["kind"], 20 | qty: 1 21 | }, 22 | { kind: "Dry White Vermouth", qty: 1 }, 23 | ], 24 | garnish: "Olive", 25 | served: "Straight Up", 26 | }, 27 | "Gin Martini": self.Martini, 28 | } 29 | } -------------------------------------------------------------------------------- /libjsonnet/libjsonnet_test_file.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include "libjsonnet.h" 21 | 22 | int main(int argc, const char **argv) 23 | { 24 | int error; 25 | char *output; 26 | struct JsonnetVm *vm; 27 | if (argc != 2) { 28 | fprintf(stderr, "libjsonnet_test_file \n"); 29 | return EXIT_FAILURE; 30 | } 31 | vm = jsonnet_make(); 32 | output = jsonnet_evaluate_file(vm, argv[1], &error); 33 | if (error) { 34 | fprintf(stderr, "%s", output); 35 | jsonnet_realloc(vm, output, 0); 36 | jsonnet_destroy(vm); 37 | return EXIT_FAILURE; 38 | } 39 | printf("%s", output); 40 | jsonnet_realloc(vm, output, 0); 41 | jsonnet_destroy(vm); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /libjsonnet/libjsonnet_test_snippet.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include "libjsonnet.h" 21 | 22 | int main(int argc, const char **argv) 23 | { 24 | int error; 25 | char *output; 26 | struct JsonnetVm *vm; 27 | if (argc != 2) { 28 | fprintf(stderr, "libjsonnet_test_snippet \n"); 29 | return EXIT_FAILURE; 30 | } 31 | vm = jsonnet_make(); 32 | output = jsonnet_evaluate_snippet(vm, "snippet", argv[1], &error); 33 | if (error) { 34 | fprintf(stderr, "%s", output); 35 | jsonnet_realloc(vm, output, 0); 36 | jsonnet_destroy(vm); 37 | return EXIT_FAILURE; 38 | } 39 | printf("%s", output); 40 | jsonnet_realloc(vm, output, 0); 41 | jsonnet_destroy(vm); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /libjsonnet/parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef JSONNET_PARSER_H 18 | #define JSONNET_PARSER_H 19 | 20 | #include 21 | 22 | #include "lexer.h" 23 | #include "ast.h" 24 | 25 | /** Parse a given JSON++ string. 26 | * 27 | * \param alloc Used to allocate the AST nodes. The Allocator must outlive the 28 | * AST pointer returned. 29 | * \param file Used in error messages and embedded in the AST nodes. 30 | * \param input The string to be tokenized & parsed. 31 | * \returns The parsed abstract syntax tree. 32 | */ 33 | AST *jsonnet_parse(Allocator *alloc, const std::string &file, const char *input); 34 | 35 | /** Escapes a string for JSON output. 36 | */ 37 | std::string jsonnet_unparse_escape(const std::string &str); 38 | 39 | /** Outputs a number, trying to preserve precision as well as possible. 40 | */ 41 | std::string jsonnet_unparse_number(double v); 42 | 43 | struct BuiltinDecl { 44 | std::string name; 45 | std::vector params; 46 | }; 47 | 48 | /** Returns the name of each built-in function. */ 49 | BuiltinDecl jsonnet_builtin_decl(unsigned long builtin); 50 | 51 | /** The inverse of jsonnet_parse. Should also produce valid JSON, if given a 52 | * restricted AST. 53 | */ 54 | std::string jsonnet_unparse_jsonnet(const AST *ast); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /libjsonnet/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | from setuptools import setup 17 | from setuptools import Extension 18 | from setuptools.command.build_ext import build_ext as BuildExt 19 | from subprocess import Popen 20 | 21 | 22 | DIR = os.path.abspath(os.path.dirname(__file__)) 23 | LIB_OBJECTS = ['libjsonnet.o', 'lexer.o', 'parser.o', 'static_analysis.o', 'vm.o'] 24 | MODULE_SOURCES = ['_jsonnet.c'] 25 | 26 | 27 | def get_version(): 28 | """ 29 | Parses the version out of libjsonnet.h 30 | """ 31 | with open(os.path.join(DIR, 'libjsonnet.h')) as f: 32 | for line in f: 33 | if '#define' in line and 'LIB_JSONNET_VERSION' in line: 34 | return line.partition('LIB_JSONNET_VERSION')[2].strip('\n "') 35 | 36 | 37 | class BuildJsonnetExt(BuildExt): 38 | def run(self): 39 | p = Popen(['make'] + LIB_OBJECTS, cwd=DIR) 40 | p.wait() 41 | if p.returncode != 0: 42 | raise Exception('Could not build %s' % (', '.join(LIB_OBJECTS))) 43 | BuildExt.run(self) 44 | 45 | 46 | jsonnet_ext = Extension( 47 | '_jsonnet', 48 | sources=MODULE_SOURCES, 49 | extra_objects=LIB_OBJECTS, 50 | language='c++' 51 | ) 52 | 53 | 54 | setup(name='jsonnet', 55 | url='https://google.github.io/jsonnet/doc/', 56 | description='Python bindings for Jsonnet - The data templating language ', 57 | author='David Cunningham', 58 | author_email='dcunnin@google.com', 59 | version=get_version(), 60 | cmdclass={ 61 | 'build_ext': BuildJsonnetExt, 62 | }, 63 | ext_modules=[jsonnet_ext], 64 | ) 65 | -------------------------------------------------------------------------------- /libjsonnet/static_error.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef JSONNET_STATIC_ERROR_H 18 | #define JSONNET_STATIC_ERROR_H 19 | 20 | struct Location { 21 | unsigned long line; 22 | unsigned long column; 23 | Location(void) 24 | : line(0), column(0) 25 | { } 26 | Location(unsigned long line_number, unsigned long column) 27 | : line(line_number), column(column) 28 | { } 29 | bool isSet(void) const 30 | { 31 | return line != 0; 32 | } 33 | }; 34 | 35 | static inline std::ostream &operator<<(std::ostream &o, const Location &loc) 36 | { 37 | o << loc.line << ":" << loc.column; 38 | return o; 39 | } 40 | 41 | struct LocationRange { 42 | std::string file; 43 | Location begin, end; 44 | LocationRange(void) 45 | { } 46 | /** This is useful for special locations, e.g. manifestation entry point. */ 47 | LocationRange(const std::string &msg) 48 | : file(msg) 49 | { } 50 | LocationRange(const std::string &file, const Location &begin, const Location &end) 51 | : file(file), begin(begin), end(end) 52 | { } 53 | bool isSet(void) const 54 | { 55 | return begin.isSet(); 56 | } 57 | }; 58 | 59 | static inline std::ostream &operator<<(std::ostream &o, const LocationRange &loc) 60 | { 61 | if (loc.file.length() > 0) 62 | o << loc.file; 63 | if (loc.isSet()) { 64 | if (loc.file.length() > 0) 65 | o << ":"; 66 | if (loc.begin.line == loc.end.line) { 67 | if (loc.begin.column == loc.end.column) { 68 | o << loc.begin; 69 | } else { 70 | o << loc.begin << "-" << loc.end.column; 71 | } 72 | } else { 73 | o << "(" << loc.begin << ")-(" << loc.end << ")"; 74 | } 75 | } 76 | return o; 77 | } 78 | 79 | struct StaticError { 80 | LocationRange location; 81 | std::string msg; 82 | StaticError(const std::string &msg) 83 | : msg(msg) 84 | { 85 | } 86 | StaticError(const std::string &filename, const Location &location, const std::string &msg) 87 | : location(filename, location, location), msg(msg) 88 | { 89 | } 90 | StaticError(const LocationRange &location, const std::string &msg) 91 | : location(location), msg(msg) 92 | { 93 | } 94 | }; 95 | 96 | static inline std::ostream &operator<<(std::ostream &o, const StaticError &err) 97 | { 98 | if (err.location.isSet()) { 99 | o << err.location << ":"; 100 | } 101 | o << " " << err.msg; 102 | return o; 103 | } 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Name 2 | ==== 3 | |luarocks| 4 | |:------:| 5 | |[![](https://img.shields.io/badge/luarocks-0.1--1-blue.svg)](https://luarocks.org/modules/chenyuduan/jsonnet)| 6 | 7 | luajit-jsonnet - The Google Jsonnet( operation data template language) for Luajit 8 | 9 | 10 | Table of Contents 11 | ================= 12 | 13 | * [Name](#name) 14 | * [Description](#description) 15 | * [install](#install) 16 | * [Methods](#methods) 17 | * [new](#new) 18 | * [make](#make) 19 | * [evaluate_file](#evaluate_file) 20 | * [evaluate_snippet](#evaluate_snippet) 21 | * [evaluate_file_multi](#evaluate_file_multi) 22 | * [evaluate_snippet_multi](#evaluate_snippet_multi) 23 | * [destroy](#destroy) 24 | 25 | Description 26 | =========== 27 | 28 | 29 | jsonnet is a domain specific configuration language that helps you define JSON data. Jsonnet lets you compute fragments of JSON within the structure, bringing the same benefit to structured data that templating languages bring to plain text. The example below illustrates a few features -- referring to another part of the structure, overriding object fields, and string operations...... 30 | 31 | luajit-jsonnet - Use the luajit ffi jsonnet interface calls and operation 32 | 33 | Google jsonnet documet: (http://google.github.io/jsonnet/doc/) 34 | 35 | install 36 | =========== 37 | Simple executing the following command 38 | 39 | ``` 40 | make install 41 | 42 | ``` 43 | 44 | Methods 45 | ======= 46 | 47 | new 48 | --- 49 | Create a luajit - jsonnet object 50 | 51 | ``` 52 | local jso = require "jsonnet" 53 | jsonnet = jso:new() 54 | 55 | ``` 56 | make 57 | --- 58 | Create a new Jsonnet virtual machine. 59 | 60 | ``` 61 | syntax: res, err = jsonnet:make() 62 | ``` 63 | evaluate_file 64 | --- 65 | Evaluate a file containing Jsonnet code, return a JSON string. 66 | 67 | ``` 68 | syntax: res,err = jsonnet:evaluate_file("t.jsonnet") 69 | ``` 70 | ``` 71 | local jso = require "jsonnet" 72 | jsonnet = jso:new() 73 | jsonnet:make() 74 | res,err = jsonnet:evaluate_file("t.jsonnet") 75 | if err == nil then 76 | print(res) 77 | else 78 | print(err) 79 | end 80 | ``` 81 | evaluate_snippet 82 | --- 83 | Evaluate a file containing Jsonnet code, return a JSON string. 84 | 85 | ``` 86 | syntax: res,err = jsonnet:evaluate_snippet(snippet) 87 | ``` 88 | 89 | ``` 90 | local snippet = '{ person1: { name: "Alice", welcome: "Hello " + self.name + "!", }, person2: self.person1 { name: "Bob" },}' 91 | res,err = jsonnet:evaluate_snippet(snippet) 92 | if err == nil then 93 | print(res) 94 | else 95 | print(err) 96 | end 97 | ``` 98 | 99 | evaluate_file_multi 100 | --- 101 | Evaluate a file containing Jsonnet code, return a number of JSON files. The returned character buffer contains an even number of strings, the filename and JSON for each 102 | 103 | ``` 104 | syntax: res,err = jsonnet:evaluate_file_multi("t.jsonnet") 105 | ``` 106 | 107 | 108 | evaluate_snippet_multi 109 | --- 110 | Evaluate a string containing Jsonnet code, return a number of JSON files. The returned character buffer contains an even number of strings, the filename and JSON for each 111 | 112 | ``` 113 | syntax: res,err = jsonnet:evaluate_snippet_multi(snippet) 114 | ``` 115 | 116 | 117 | destroy 118 | --- 119 | Shut down and release Jsonnet virtual machine. 120 | 121 | ``` 122 | syntax: res,err = jsonnet:destroy(vm) 123 | ``` 124 | 125 | ``` 126 | local jso = require "jsonnet" 127 | jsonnet = jso:new() 128 | jsonnet:make() 129 | res,err = jsonnet:evaluate_file("t.jsonnet") 130 | jsonnet:destroy() 131 | ``` 132 | 133 | Author 134 | ====== 135 | 136 | Yuduan Chen (陈毓端) 137 | -------------------------------------------------------------------------------- /libjsonnet/vm.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef JSONNET_VM_H 18 | #define JSONNET_VM_H 19 | 20 | #include "ast.h" 21 | #include "libjsonnet.h" 22 | 23 | /** A single line of a stack trace from a runtime error. 24 | */ 25 | struct TraceFrame { 26 | LocationRange location; 27 | std::string name; 28 | TraceFrame(const LocationRange &location, const std::string &name="") 29 | : location(location), name(name) 30 | { } 31 | }; 32 | 33 | /** Exception that is thrown by the interpreter when it reaches an error construct, or divide by 34 | * zero, array bounds error, dynamic type error, etc. 35 | */ 36 | struct RuntimeError { 37 | std::vector stackTrace; 38 | std::string msg; 39 | RuntimeError(const std::vector stack_trace, const std::string &msg) 40 | : stackTrace(stack_trace), msg(msg) 41 | { } 42 | }; 43 | 44 | /** Execute the program and return the value as a JSON string. 45 | * 46 | * \param alloc The allocator used to create the ast. 47 | * \param ast The program to execute. 48 | * \param max_stack Recursion beyond this level gives an error. 49 | * \param gc_min_objects The garbage collector does not run when the heap is this small. 50 | * \param gc_growth_trigger Growth since last garbage collection cycle to trigger a new cycle. 51 | * \param import_callback A callback to handle imports 52 | * \param import_callback_ctx Context param for the import callback. 53 | * \param output_string Whether to expect a string and output it without JSON encoding 54 | * \throws RuntimeError reports runtime errors in the program. 55 | * \returns The JSON result in string form. 56 | */ 57 | std::string jsonnet_vm_execute(Allocator *alloc, const AST *ast, 58 | const std::map &ext_vars, 59 | unsigned max_stack, double gc_min_objects, 60 | double gc_growth_trigger, 61 | JsonnetImportCallback *import_callback, void *import_callback_ctx, 62 | bool string_output); 63 | 64 | /** Execute the program and return the value as a number of JSON files. 65 | * 66 | * This assumes the given program yields an object whose keys are filenames. 67 | * 68 | * \param alloc The allocator used to create the ast. 69 | * \param ast The program to execute. 70 | * \param max_stack Recursion beyond this level gives an error. 71 | * \param gc_min_objects The garbage collector does not run when the heap is this small. 72 | * \param gc_growth_trigger Growth since last garbage collection cycle to trigger a new cycle. 73 | * \param import_callback A callback to handle imports 74 | * \param import_callback_ctx Context param for the import callback. 75 | * \param output_string Whether to expect a string and output it without JSON encoding 76 | * \throws RuntimeError reports runtime errors in the program. 77 | * \returns A mapping from filename to the JSON strings for that file. 78 | */ 79 | std::map jsonnet_vm_execute_multi( 80 | Allocator *alloc, const AST *ast, const std::map &ext_vars, 81 | unsigned max_stack, double gc_min_objects, double gc_growth_trigger, 82 | JsonnetImportCallback *import_callback, void *import_callback_ctx, 83 | bool string_output); 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /jsonnet.lua: -------------------------------------------------------------------------------- 1 | local ffi = require "ffi" 2 | local ffi_new = ffi.new 3 | local ffi_str = ffi.string 4 | local C = ffi.C 5 | local setmetatable = setmetatable 6 | local error = error 7 | local libjsonnet = ffi.load(ffi.os == "Windows" and "libjsonnet" or "libjsonnet") 8 | local _M = { _VERSION = '0.01' } 9 | local mt = { __index = _M } 10 | ffi.cdef[[ 11 | const char *jsonnet_version(void); 12 | typedef struct JsonnetVm {struct JsonnetVm *vm;char* out; const char *version;} JsonnetVm_t; 13 | struct JsonnetVm *jsonnet_make(void); 14 | void jsonnet_max_stack(struct JsonnetVm *vm, unsigned v); 15 | void jsonnet_gc_min_objects(struct JsonnetVm *vm, unsigned v); 16 | void jsonnet_gc_growth_trigger(struct JsonnetVm *vm, double v); 17 | void jsonnet_string_output(struct JsonnetVm *vm, int v); 18 | typedef char *JsonnetImportCallback(void *ctx, const char *base, const char *rel, int *success); 19 | char *jsonnet_realloc(struct JsonnetVm *vm, char *buf, size_t sz); 20 | void jsonnet_import_callback(struct JsonnetVm *vm, JsonnetImportCallback *cb, void *ctx); 21 | void jsonnet_ext_var(struct JsonnetVm *vm, const char *key, const char *val); 22 | void jsonnet_debug_ast(struct JsonnetVm *vm, int v); 23 | char *jsonnet_evaluate_file(struct JsonnetVm *vm,const char *filename,int *error); 24 | char *jsonnet_evaluate_snippet(struct JsonnetVm *vm,const char *filename,const char *snippet,int *error); 25 | char *jsonnet_evaluate_file_multi(struct JsonnetVm *vm, const char *filename,int *error); 26 | char *jsonnet_evaluate_snippet_multi(struct JsonnetVm *vm,const char *filename, const char *snippet,int *error); 27 | void jsonnet_destroy(struct JsonnetVm *vm); 28 | ]] 29 | local int_ptr = ffi.typeof("int[1]") 30 | local JsonnetVm = ffi.new("JsonnetVm_t") 31 | local _vm; 32 | local _err_jsonnet_make_init = "jsonnet_make not initialized" 33 | local function _return( err,res ,out ) 34 | libjsonnet.jsonnet_realloc(_vm, out,0) 35 | if tonumber(err[0]) == 1 then 36 | return nil,res 37 | else 38 | return res,nil 39 | end 40 | end 41 | 42 | function _M.new(self) 43 | return setmetatable({ }, mt) 44 | end 45 | 46 | function _M.make(self) 47 | _vm = libjsonnet.jsonnet_make() 48 | end 49 | 50 | function _M.version(self) 51 | JsonnetVm.version = libjsonnet.jsonnet_version(); 52 | return ffi.string(JsonnetVm.version) 53 | end 54 | 55 | function _M.evaluate_file( self , filename ) 56 | local err = int_ptr() 57 | if _vm == nil then 58 | return nil ,_err_jsonnet_make_init 59 | end 60 | JsonnetVm.out = libjsonnet.jsonnet_evaluate_file(_vm,filename,err) 61 | local res = ffi.string(JsonnetVm.out) 62 | err,res = _return(err,res,JsonnetVm.out) 63 | return err,res 64 | end 65 | 66 | function _M.evaluate_snippet( self ,snippet ) 67 | local err = int_ptr() 68 | if _vm == nil then 69 | return nil ,_err_jsonnet_make_init 70 | end 71 | JsonnetVm.out = libjsonnet.jsonnet_evaluate_snippet(_vm,"snippet",snippet,err) 72 | local res = ffi.string(JsonnetVm.out) 73 | err,res = _return(err,res,JsonnetVm.out) 74 | return err,res 75 | end 76 | 77 | function _M.evaluate_file_multi( self , filename ) 78 | local err = int_ptr() 79 | if _vm == nil then 80 | return nil ,_err_jsonnet_make_init 81 | end 82 | JsonnetVm.out = libjsonnet.jsonnet_evaluate_file_multi(_vm,filename,err) 83 | local res = ffi.string(JsonnetVm.out) 84 | err,res = _return(err,res,JsonnetVm.out) 85 | return err,res 86 | end 87 | 88 | function _M.evaluate_snippet_multi( self ,snippet ) 89 | local err = int_ptr() 90 | if _vm == nil then 91 | return nil ,_err_jsonnet_make_init 92 | end 93 | JsonnetVm.out = libjsonnet.jsonnet_evaluate_snippet_multi(_vm,"snippet",snippet,err) 94 | local res = ffi.string(JsonnetVm.out) 95 | err,res = _return(err,res,JsonnetVm.out) 96 | return err,res 97 | end 98 | 99 | function _M.destroy( self ) 100 | if _vm == nil then 101 | return nil ,_err_jsonnet_make_init 102 | end 103 | libjsonnet.jsonnet_destroy(_vm) 104 | _vm = nil 105 | end 106 | 107 | return _M -------------------------------------------------------------------------------- /libjsonnet/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ################################################################################################### 16 | # User-servicable parts: 17 | ################################################################################################### 18 | 19 | # C/C++ compiler -- clang also works 20 | CXX ?= g++ 21 | CC ?= gcc 22 | 23 | # Emscripten -- For Jsonnet in the browser 24 | EMCXX ?= em++ 25 | EMCC ?= emcc 26 | 27 | CP ?= cp 28 | OD ?= od 29 | 30 | CXXFLAGS ?= -g -O3 -Wall -Wextra -pedantic -std=c++0x -fPIC 31 | CFLAGS ?= -g -O3 -Wall -Wextra -pedantic -std=c99 -fPIC 32 | EMCXXFLAGS = $(CXXFLAGS) --memory-init-file 0 -s DISABLE_EXCEPTION_CATCHING=0 33 | EMCFLAGS = $(CFLAGS) --memory-init-file 0 -s DISABLE_EXCEPTION_CATCHING=0 34 | LDFLAGS ?= 35 | 36 | SHARED_LDFLAGS ?= -shared 37 | 38 | ################################################################################################### 39 | # End of user-servicable parts 40 | ################################################################################################### 41 | 42 | LIB_SRC = lexer.cpp parser.cpp static_analysis.cpp vm.cpp libjsonnet.cpp 43 | LIB_OBJ = $(LIB_SRC:.cpp=.o) 44 | 45 | ALL = jsonnet libjsonnet.so libjsonnet_test_snippet libjsonnet_test_file libjsonnet.js doc/libjsonnet.js $(LIB_OBJ) 46 | ALL_HEADERS = libjsonnet.h vm.h static_analysis.h parser.h lexer.h ast.h static_error.h state.h std.jsonnet.h 47 | 48 | default: jsonnet 49 | 50 | all: $(ALL) 51 | 52 | TEST_SNIPPET = "std.assertEqual(({ x: 1, y: self.x } { x: 2 }).y, 2)" 53 | test: jsonnet libjsonnet.so libjsonnet_test_snippet libjsonnet_test_file 54 | ./jsonnet -e $(TEST_SNIPPET) 55 | LD_LIBRARY_PATH=. ./libjsonnet_test_snippet $(TEST_SNIPPET) 56 | LD_LIBRARY_PATH=. ./libjsonnet_test_file "test_suite/object.jsonnet" 57 | cd examples ; ./check.sh 58 | cd examples/terraform ; ./check.sh 59 | cd test_suite ; ./run_tests.sh 60 | 61 | depend: 62 | makedepend -f- $(LIB_SRC) jsonnet.cpp libjsonnet_test_snippet.c libjsonnet_test_file.c > Makefile.depend 63 | 64 | parser.cpp: std.jsonnet.h 65 | 66 | # Object files 67 | %.o: %.cpp 68 | $(CXX) -c $(CXXFLAGS) $< -o $@ 69 | 70 | # Commandline executable. 71 | jsonnet: jsonnet.cpp $(LIB_OBJ) 72 | $(CXX) $(CXXFLAGS) $(LDFLAGS) $< $(LIB_SRC:.cpp=.o) -o $@ 73 | 74 | # C binding. 75 | libjsonnet.so: $(LIB_OBJ) 76 | $(CXX) $(LDFLAGS) $(LIB_OBJ) $(SHARED_LDFLAGS) -o $@ 77 | 78 | # Javascript build of C binding 79 | libjsonnet.js: $(LIB_SRC) $(ALL_HEADERS) 80 | $(EMCXX) -s 'EXPORTED_FUNCTIONS=["_jsonnet_make", "_jsonnet_evaluate_snippet", "_jsonnet_realloc", "_jsonnet_destroy"]' $(EMCXXFLAGS) $(LDFLAGS) $(LIB_SRC) -o $@ 81 | 82 | # Copy javascript build to doc directory 83 | doc/libjsonnet.js: libjsonnet.js 84 | $(CP) $^ $@ 85 | 86 | # Tests for C binding. 87 | libjsonnet_test_snippet: libjsonnet_test_snippet.c libjsonnet.so libjsonnet.h 88 | $(CC) $(CFLAGS) $(LDFLAGS) $< -L. -ljsonnet -o $@ 89 | 90 | libjsonnet_test_file: libjsonnet_test_file.c libjsonnet.so libjsonnet.h 91 | $(CC) $(CFLAGS) $(LDFLAGS) $< -L. -ljsonnet -o $@ 92 | 93 | # Encode standard library for embedding in C 94 | %.jsonnet.h: %.jsonnet 95 | (($(OD) -v -Anone -t u1 $< | tr " " "\n" | grep -v "^$$" | tr "\n" "," ) && echo "0") > $@ 96 | echo >> $@ 97 | 98 | clean: 99 | rm -vf */*~ *~ .*~ */.*.swp .*.swp $(ALL) *.o *.jsonnet.h 100 | 101 | -include Makefile.depend 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /libjsonnet/lexer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef JSONNET_LEXER_H 18 | #define JSONNET_LEXER_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "static_error.h" 27 | 28 | struct Token { 29 | enum Kind { 30 | // Symbols 31 | BRACE_L, 32 | BRACE_R, 33 | BRACKET_L, 34 | BRACKET_R, 35 | COLON, 36 | COMMA, 37 | DOLLAR, 38 | DOT, 39 | PAREN_L, 40 | PAREN_R, 41 | SEMICOLON, 42 | 43 | // Arbitrary length lexemes 44 | IDENTIFIER, 45 | NUMBER, 46 | OPERATOR, 47 | STRING, 48 | 49 | // Keywords 50 | ELSE, 51 | ERROR, 52 | FALSE, 53 | FOR, 54 | FUNCTION, 55 | IF, 56 | IMPORT, 57 | IMPORTSTR, 58 | IN, 59 | LOCAL, 60 | NULL_LIT, 61 | TAILSTRICT, 62 | THEN, 63 | SELF, 64 | SUPER, 65 | TRUE, 66 | 67 | // A special token that holds line/column information about the end of the file. 68 | END_OF_FILE 69 | } kind; 70 | 71 | std::string data; 72 | 73 | LocationRange location; 74 | 75 | Token(Kind kind, const std::string &data, const LocationRange &location) 76 | : kind(kind), data(data), location(location) 77 | { } 78 | 79 | Token(Kind kind, const std::string &data="") : kind(kind), data(data) { } 80 | 81 | static const char *toString(Kind v) 82 | { 83 | switch (v) { 84 | case BRACE_L: return "\"{\""; 85 | case BRACE_R: return "\"}\""; 86 | case BRACKET_L: return "\"[\""; 87 | case BRACKET_R: return "\"]\""; 88 | case COLON: return "\":\""; 89 | case COMMA: return "\",\""; 90 | case DOLLAR: return "\"$\""; 91 | case DOT: return "\".\""; 92 | 93 | case PAREN_L: return "\"(\""; 94 | case PAREN_R: return "\")\""; 95 | case SEMICOLON: return "\";\""; 96 | 97 | case IDENTIFIER: return "IDENTIFIER"; 98 | case NUMBER: return "NUMBER"; 99 | case OPERATOR: return "OPERATOR"; 100 | case STRING: return "STRING"; 101 | 102 | case ELSE: return "else"; 103 | case ERROR: return "error"; 104 | case FALSE: return "false"; 105 | case FOR: return "for"; 106 | case FUNCTION: return "function"; 107 | case IF: return "if"; 108 | case IMPORT: return "import"; 109 | case IMPORTSTR: return "importstr"; 110 | case IN: return "in"; 111 | case LOCAL: return "local"; 112 | case NULL_LIT: return "null"; 113 | case SELF: return "self"; 114 | case SUPER: return "super"; 115 | case TAILSTRICT: return "tailstrict"; 116 | case THEN: return "then"; 117 | case TRUE: return "true"; 118 | 119 | case END_OF_FILE: return "end of file"; 120 | default: 121 | std::cerr << "INTERNAL ERROR: Unknown token kind: " << v << std::endl; 122 | std::abort(); 123 | } 124 | } 125 | }; 126 | 127 | static inline bool operator==(const Token &a, const Token &b) 128 | { 129 | if (a.kind != b.kind) return false; 130 | if (a.data != b.data) return false; 131 | return true; 132 | } 133 | 134 | static inline std::ostream &operator<<(std::ostream &o, Token::Kind v) 135 | { 136 | o << Token::toString(v); 137 | return o; 138 | } 139 | 140 | static inline std::ostream &operator<<(std::ostream &o, const Token &v) 141 | { 142 | if (v.data == "") { 143 | o << Token::toString(v.kind); 144 | } else if (v.kind == Token::OPERATOR) { 145 | o << "\"" << v.data << "\""; 146 | } else { 147 | o << "(" << Token::toString(v.kind) << ", \"" << v.data << "\")"; 148 | } 149 | return o; 150 | } 151 | 152 | std::list jsonnet_lex(const std::string &filename, const char *input); 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /libjsonnet/_jsonnet.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include "libjsonnet.h" 23 | 24 | static PyObject* evaluate_file(PyObject* self, PyObject* args, PyObject *keywds) 25 | { 26 | const char *filename; 27 | char *out; 28 | unsigned max_stack = 500, gc_min_objects = 1000, max_trace = 20; 29 | double gc_growth_trigger = 2; 30 | int debug_ast = 0, error; 31 | PyObject *ext_vars = NULL; 32 | struct JsonnetVm *vm; 33 | static char *kwlist[] = {"filename", "max_stack", "gc_min_objects", "gc_growth_trigger", "ext_vars", "debug_ast", "max_trace", NULL}; 34 | 35 | (void) self; 36 | 37 | if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|IIdOiI", kwlist, 38 | &filename, 39 | &max_stack, &gc_min_objects, &gc_growth_trigger, &ext_vars, &debug_ast, &max_trace)) { 40 | return NULL; 41 | } 42 | 43 | vm = jsonnet_make(); 44 | jsonnet_max_stack(vm, max_stack); 45 | jsonnet_gc_min_objects(vm, gc_min_objects); 46 | jsonnet_max_trace(vm, max_trace); 47 | jsonnet_gc_growth_trigger(vm, gc_growth_trigger); 48 | jsonnet_debug_ast(vm, debug_ast); 49 | if (ext_vars != NULL) { 50 | PyObject *key, *val; 51 | Py_ssize_t pos = 0; 52 | 53 | while (PyDict_Next(ext_vars, &pos, &key, &val)) { 54 | const char *key_ = PyString_AsString(key); 55 | if (key_ == NULL) { 56 | jsonnet_destroy(vm); 57 | return NULL; 58 | } 59 | const char *val_ = PyString_AsString(val); 60 | if (val_ == NULL) { 61 | jsonnet_destroy(vm); 62 | return NULL; 63 | } 64 | jsonnet_ext_var(vm, key_, val_); 65 | } 66 | } 67 | 68 | out = jsonnet_evaluate_file(vm, filename, &error); 69 | if (error) { 70 | PyErr_SetString(PyExc_RuntimeError, out); 71 | jsonnet_realloc(vm, out, 0); 72 | jsonnet_destroy(vm); 73 | return NULL; 74 | } else { 75 | PyObject *ret = PyString_FromString(out); 76 | jsonnet_realloc(vm, out, 0); 77 | jsonnet_destroy(vm); 78 | return ret; 79 | } 80 | } 81 | 82 | static PyObject* evaluate_snippet(PyObject* self, PyObject* args, PyObject *keywds) 83 | { 84 | const char *filename, *src; 85 | char *out; 86 | unsigned max_stack = 500, gc_min_objects = 1000, max_trace = 20; 87 | double gc_growth_trigger = 2; 88 | int debug_ast = 0, error; 89 | PyObject *ext_vars = NULL; 90 | struct JsonnetVm *vm; 91 | static char *kwlist[] = {"filename", "src", "max_stack", "gc_min_objects", "gc_growth_trigger", "ext_vars", "debug_ast", "max_trace", NULL}; 92 | 93 | (void) self; 94 | 95 | if (!PyArg_ParseTupleAndKeywords(args, keywds, "ss|IIdOiI", kwlist, 96 | &filename, &src, 97 | &max_stack, &gc_min_objects, &gc_growth_trigger, &ext_vars, &debug_ast, &max_trace)) { 98 | return NULL; 99 | } 100 | 101 | vm = jsonnet_make(); 102 | jsonnet_max_stack(vm, max_stack); 103 | jsonnet_gc_min_objects(vm, gc_min_objects); 104 | jsonnet_max_trace(vm, max_trace); 105 | jsonnet_gc_growth_trigger(vm, gc_growth_trigger); 106 | jsonnet_debug_ast(vm, debug_ast); 107 | if (ext_vars != NULL) { 108 | PyObject *key, *val; 109 | Py_ssize_t pos = 0; 110 | 111 | while (PyDict_Next(ext_vars, &pos, &key, &val)) { 112 | const char *key_ = PyString_AsString(key); 113 | if (key_ == NULL) { 114 | jsonnet_destroy(vm); 115 | return NULL; 116 | } 117 | const char *val_ = PyString_AsString(val); 118 | if (val_ == NULL) { 119 | jsonnet_destroy(vm); 120 | return NULL; 121 | } 122 | jsonnet_ext_var(vm, key_, val_); 123 | } 124 | } 125 | 126 | out = jsonnet_evaluate_snippet(vm, filename, src, &error); 127 | if (error) { 128 | PyErr_SetString(PyExc_RuntimeError, out); 129 | jsonnet_realloc(vm, out, 0); 130 | jsonnet_destroy(vm); 131 | return NULL; 132 | } else { 133 | PyObject *ret = PyString_FromString(out); 134 | jsonnet_realloc(vm, out, 0); 135 | jsonnet_destroy(vm); 136 | return ret; 137 | } 138 | } 139 | 140 | static PyMethodDef module_methods[] = { 141 | {"evaluate_file", (PyCFunction)evaluate_file, METH_VARARGS | METH_KEYWORDS, 142 | "Interpret the given Jsonnet file."}, 143 | {"evaluate_snippet", (PyCFunction)evaluate_snippet, METH_VARARGS | METH_KEYWORDS, 144 | "Interpret the given Jsonnet code."}, 145 | {NULL, NULL, 0, NULL} 146 | }; 147 | 148 | PyMODINIT_FUNC init_jsonnet(void) 149 | { 150 | Py_InitModule3("_jsonnet", module_methods, "A Python interface to Jsonnet."); 151 | } 152 | 153 | -------------------------------------------------------------------------------- /libjsonnet/static_analysis.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | #include "static_analysis.h" 20 | 21 | #include "static_error.h" 22 | #include "ast.h" 23 | 24 | typedef std::set IdSet; 25 | 26 | /** Inserts all of s into r. */ 27 | static void append(IdSet &r, const IdSet &s) 28 | { 29 | r.insert(s.begin(), s.end()); 30 | } 31 | 32 | /** Statically analyse the given ast. 33 | * 34 | * \param ast_ The AST. 35 | * \param in_object Whether or not ast_ is within the lexical scope of an object AST. 36 | * \param vars The variables defined within lexical scope of ast_. 37 | * \returns The free variables in ast_. 38 | */ 39 | static IdSet static_analysis(AST *ast_, bool in_object, const IdSet &vars) 40 | { 41 | IdSet r; 42 | 43 | if (auto *ast = dynamic_cast(ast_)) { 44 | append(r, static_analysis(ast->target, in_object, vars)); 45 | for (AST *arg : ast->arguments) 46 | append(r, static_analysis(arg, in_object, vars)); 47 | 48 | } else if (auto *ast = dynamic_cast(ast_)) { 49 | for (AST *el : ast->elements) 50 | append(r, static_analysis(el, in_object, vars)); 51 | 52 | } else if (auto *ast = dynamic_cast(ast_)) { 53 | append(r, static_analysis(ast->left, in_object, vars)); 54 | append(r, static_analysis(ast->right, in_object, vars)); 55 | 56 | } else if (dynamic_cast(ast_)) { 57 | // Nothing to do. 58 | 59 | } else if (auto *ast = dynamic_cast(ast_)) { 60 | append(r, static_analysis(ast->cond, in_object, vars)); 61 | append(r, static_analysis(ast->branchTrue, in_object, vars)); 62 | append(r, static_analysis(ast->branchFalse, in_object, vars)); 63 | 64 | } else if (auto *ast = dynamic_cast(ast_)) { 65 | append(r, static_analysis(ast->expr, in_object, vars)); 66 | 67 | } else if (auto *ast = dynamic_cast(ast_)) { 68 | auto new_vars = vars; 69 | IdSet params; 70 | for (auto *p : ast->parameters) { 71 | if (params.find(p) != params.end()) { 72 | throw StaticError(ast_->location, "Duplicate function parameter: " + p->name); 73 | } 74 | params.insert(p); 75 | new_vars.insert(p); 76 | } 77 | auto fv = static_analysis(ast->body, in_object, new_vars); 78 | for (auto *p : ast->parameters) 79 | fv.erase(p); 80 | append(r, fv); 81 | 82 | } else if (dynamic_cast(ast_)) { 83 | // Nothing to do. 84 | 85 | } else if (dynamic_cast(ast_)) { 86 | // Nothing to do. 87 | 88 | } else if (auto *ast = dynamic_cast(ast_)) { 89 | append(r, static_analysis(ast->target, in_object, vars)); 90 | append(r, static_analysis(ast->index, in_object, vars)); 91 | 92 | } else if (auto *ast = dynamic_cast(ast_)) { 93 | IdSet ast_vars; 94 | for (const auto &bind: ast->binds) { 95 | ast_vars.insert(bind.first); 96 | } 97 | auto new_vars = vars; 98 | append(new_vars, ast_vars); 99 | IdSet fvs; 100 | for (const auto &bind: ast->binds) 101 | append(fvs, static_analysis(bind.second, in_object, new_vars)); 102 | 103 | append(fvs, static_analysis(ast->body, in_object, new_vars)); 104 | 105 | for (const auto &bind: ast->binds) 106 | fvs.erase(bind.first); 107 | 108 | append(r, fvs); 109 | 110 | } else if (dynamic_cast(ast_)) { 111 | // Nothing to do. 112 | 113 | } else if (dynamic_cast(ast_)) { 114 | // Nothing to do. 115 | 116 | } else if (dynamic_cast(ast_)) { 117 | // Nothing to do. 118 | 119 | } else if (dynamic_cast(ast_)) { 120 | // Nothing to do. 121 | 122 | } else if (auto *ast = dynamic_cast(ast_)) { 123 | for (auto field : ast->fields) { 124 | append(r, static_analysis(field.name, in_object, vars)); 125 | append(r, static_analysis(field.body, true, vars)); 126 | } 127 | 128 | } else if (auto *ast = dynamic_cast(ast_)) { 129 | auto new_vars = vars; 130 | new_vars.insert(ast->id); 131 | append(r, static_analysis(ast->field, false, new_vars)); 132 | append(r, static_analysis(ast->value, true, new_vars)); 133 | r.erase(ast->id); 134 | append(r, static_analysis(ast->array, in_object, vars)); 135 | 136 | } else if (dynamic_cast(ast_)) { 137 | if (!in_object) 138 | throw StaticError(ast_->location, "Can't use self outside of an object."); 139 | 140 | } else if (dynamic_cast(ast_)) { 141 | if (!in_object) 142 | throw StaticError(ast_->location, "Can't use super outside of an object."); 143 | 144 | } else if (auto *ast = dynamic_cast(ast_)) { 145 | append(r, static_analysis(ast->expr, in_object, vars)); 146 | 147 | } else if (auto *ast = dynamic_cast(ast_)) { 148 | if (vars.find(ast->id) == vars.end()) { 149 | throw StaticError(ast->location, "Unknown variable: "+ast->id->name); 150 | } 151 | r.insert(ast->id); 152 | 153 | } else { 154 | std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_ << std::endl; 155 | std::abort(); 156 | 157 | } 158 | 159 | for (auto *id : r) 160 | ast_->freeVariables.push_back(id); 161 | 162 | return r; 163 | } 164 | 165 | void jsonnet_static_analysis(AST *ast) 166 | { 167 | static_analysis(ast, false, IdSet{}); 168 | } 169 | -------------------------------------------------------------------------------- /libjsonnet/libjsonnet.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef LIB_JSONNET_H 18 | #define LIB_JSONNET_H 19 | 20 | /** \file This file is a library interface for evaluating Jsonnet. It is written in C++ but exposes 21 | * a C interface for easier wrapping by other languages. See \see jsonnet_lib_test.c for an example 22 | * of using the library. 23 | */ 24 | 25 | 26 | #define LIB_JSONNET_VERSION "v0.7.7-dev1" 27 | 28 | 29 | /** Return the version string of the Jsonnet interpreter. Conforms to semantic versioning 30 | * http://semver.org/ If this does not match LIB_JSONNET_VERSION then there is a mismatch between 31 | * header and compiled library. 32 | */ 33 | const char *jsonnet_version(void); 34 | 35 | /** Jsonnet virtual machine context. */ 36 | struct JsonnetVm; 37 | 38 | /** Create a new Jsonnet virtual machine. */ 39 | struct JsonnetVm *jsonnet_make(void); 40 | 41 | /** Set the maximum stack depth. */ 42 | void jsonnet_max_stack(struct JsonnetVm *vm, unsigned v); 43 | 44 | /** Set the number of objects required before a garbage collection cycle is allowed. */ 45 | void jsonnet_gc_min_objects(struct JsonnetVm *vm, unsigned v); 46 | 47 | /** Run the garbage collector after this amount of growth in the number of objects. */ 48 | void jsonnet_gc_growth_trigger(struct JsonnetVm *vm, double v); 49 | 50 | /** Expect a string as output and don't JSON encode it. */ 51 | void jsonnet_string_output(struct JsonnetVm *vm, int v); 52 | 53 | /** Callback used to load imports. 54 | * 55 | * The returned char* should be allocated with jsonnet_realloc. It will be cleaned up by 56 | * libjsonnet when no-longer needed. 57 | * 58 | * \param ctx User pointer, given in jsonnet_import_callback. 59 | * \param base The directory containing the code that did the import. 60 | * \param rel The path imported by the code. 61 | *\ param success Set this byref param to 1 to indicate success and 0 for failure. 62 | * \returns The content of the imported file, or an error message. 63 | */ 64 | typedef char *JsonnetImportCallback(void *ctx, const char *base, const char *rel, int *success); 65 | 66 | /** Allocate, resize, or free a buffer. This will abort if the memory cannot be allocated. It will 67 | * only return NULL if sz was zero. 68 | * 69 | * \param buf If NULL, allocate a new buffer. If an previously allocated buffer, resize it. 70 | * \param sz The size of the buffer to return. If zero, frees the buffer. 71 | * \returns The new buffer. 72 | */ 73 | char *jsonnet_realloc(struct JsonnetVm *vm, char *buf, size_t sz); 74 | 75 | /** Override the callback used to locate imports. 76 | */ 77 | void jsonnet_import_callback(struct JsonnetVm *vm, JsonnetImportCallback *cb, void *ctx); 78 | 79 | /** Bind a Jsonnet external var to the given value. 80 | * 81 | * Argument values are copied so memory should be managed by caller. 82 | */ 83 | void jsonnet_ext_var(struct JsonnetVm *vm, const char *key, const char *val); 84 | 85 | /** If set to 1, will emit the Jsonnet input after parsing / desugaring. */ 86 | void jsonnet_debug_ast(struct JsonnetVm *vm, int v); 87 | 88 | /** Set the number of lines of stack trace to display (0 for all of them). */ 89 | void jsonnet_max_trace(struct JsonnetVm *vm, unsigned v); 90 | 91 | /** Evaluate a file containing Jsonnet code, return a JSON string. 92 | * 93 | * The returned string should be cleaned up with jsonnet_realloc. 94 | * 95 | * \param filename Path to a file containing Jsonnet code. 96 | * \param error Return by reference whether or not there was an error. 97 | * \returns Either JSON or the error message. 98 | */ 99 | char *jsonnet_evaluate_file(struct JsonnetVm *vm, 100 | const char *filename, 101 | int *error); 102 | 103 | /** Evaluate a string containing Jsonnet code, return a JSON string. 104 | * 105 | * The returned string should be cleaned up with jsonnet_realloc. 106 | * 107 | * \param filename Path to a file (used in error messages). 108 | * \param snippet Jsonnet code to execute. 109 | * \param error Return by reference whether or not there was an error. 110 | * \returns Either JSON or the error message. 111 | */ 112 | char *jsonnet_evaluate_snippet(struct JsonnetVm *vm, 113 | const char *filename, 114 | const char *snippet, 115 | int *error); 116 | 117 | /** Evaluate a file containing Jsonnet code, return a number of JSON files. 118 | * 119 | * The returned character buffer contains an even number of strings, the filename and JSON for each 120 | * JSON file interleaved. It should be cleaned up with jsonnet_realloc. 121 | * 122 | * \param filename Path to a file containing Jsonnet code. 123 | * \param error Return by reference whether or not there was an error. 124 | * \returns Either the error, or a sequence of strings separated by \0, terminated with \0\0. 125 | */ 126 | char *jsonnet_evaluate_file_multi(struct JsonnetVm *vm, 127 | const char *filename, 128 | int *error); 129 | 130 | /** Evaluate a string containing Jsonnet code, return a number of JSON files. 131 | * 132 | * The returned character buffer contains an even number of strings, the filename and JSON for each 133 | * JSON file interleaved. It should be cleaned up with jsonnet_realloc. 134 | * 135 | * \param filename Path to a file containing Jsonnet code. 136 | * \param snippet Jsonnet code to execute. 137 | * \param error Return by reference whether or not there was an error. 138 | * \returns Either the error, or a sequence of strings separated by \0, terminated with \0\0. 139 | */ 140 | char *jsonnet_evaluate_snippet_multi(struct JsonnetVm *vm, 141 | const char *filename, 142 | const char *snippet, 143 | int *error); 144 | 145 | /** Complement of \see jsonnet_vm_make. */ 146 | void jsonnet_destroy(struct JsonnetVm *vm); 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /libjsonnet/libjsonnet.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | extern "C" { 27 | #include "libjsonnet.h" 28 | } 29 | 30 | #include "parser.h" 31 | #include "static_analysis.h" 32 | #include "vm.h" 33 | 34 | static void memory_panic(void) 35 | { 36 | fputs("FATAL ERROR: A memory allocation error occurred.\n", stderr); 37 | abort(); 38 | } 39 | 40 | static char *from_string(JsonnetVm* vm, const std::string &v) 41 | { 42 | char *r = jsonnet_realloc(vm, nullptr, v.length() + 1); 43 | std::strcpy(r, v.c_str()); 44 | return r; 45 | } 46 | 47 | /** Resolve the absolute path and use C++ file io to load the file. 48 | */ 49 | static char *default_import_callback(void *ctx, const char *base, const char *file, int *success) 50 | { 51 | auto *vm = static_cast(ctx); 52 | 53 | if (std::strlen(file) == 0) { 54 | *success = 0; 55 | return from_string(vm, "The empty string is not a valid filename"); 56 | } 57 | 58 | if (file[std::strlen(file) - 1] == '/') { 59 | *success = 0; 60 | return from_string(vm, "Attempted to import a directory"); 61 | } 62 | 63 | std::string abs_path; 64 | // It is possible that file is an absolute path 65 | if (file[0] == '/') 66 | abs_path = file; 67 | else 68 | abs_path = std::string(base) + file; 69 | 70 | std::ifstream f; 71 | f.open(abs_path.c_str()); 72 | if (!f.good()) { 73 | *success = 0; 74 | return from_string(vm, std::strerror(errno)); 75 | } 76 | try { 77 | std::string input; 78 | input.assign(std::istreambuf_iterator(f), std::istreambuf_iterator()); 79 | *success = 1; 80 | return from_string(vm, input); 81 | } catch (const std::ios_base::failure &io_err) { 82 | *success = 0; 83 | return from_string(vm, io_err.what()); 84 | } 85 | } 86 | 87 | 88 | struct JsonnetVm { 89 | double gcGrowthTrigger; 90 | unsigned maxStack; 91 | unsigned gcMinObjects; 92 | bool debugAst; 93 | unsigned maxTrace; 94 | std::map extVars; 95 | JsonnetImportCallback *importCallback; 96 | void *importCallbackContext; 97 | bool stringOutput; 98 | JsonnetVm(void) 99 | : gcGrowthTrigger(2.0), maxStack(500), gcMinObjects(1000), debugAst(false), maxTrace(20), 100 | importCallback(default_import_callback), importCallbackContext(this), stringOutput(false) 101 | { } 102 | }; 103 | 104 | #define TRY try { 105 | #define CATCH(func) \ 106 | } catch (const std::bad_alloc &) {\ 107 | memory_panic(); \ 108 | } catch (const std::exception &e) {\ 109 | std::cerr << "Something went wrong during " func ", please report this: " \ 110 | << e.what() << std::endl; \ 111 | abort(); \ 112 | } 113 | 114 | const char *jsonnet_version(void) 115 | { 116 | return LIB_JSONNET_VERSION; 117 | } 118 | 119 | JsonnetVm *jsonnet_make(void) 120 | { 121 | TRY 122 | return new JsonnetVm(); 123 | CATCH("jsonnet_make") 124 | return nullptr; 125 | } 126 | 127 | void jsonnet_destroy(JsonnetVm *vm) 128 | { 129 | TRY 130 | delete vm; 131 | CATCH("jsonnet_destroy") 132 | } 133 | 134 | void jsonnet_max_stack(JsonnetVm *vm, unsigned v) 135 | { 136 | vm->maxStack = v; 137 | } 138 | 139 | void jsonnet_gc_min_objects(JsonnetVm *vm, unsigned v) 140 | { 141 | vm->gcMinObjects = v; 142 | } 143 | 144 | void jsonnet_gc_growth_trigger(JsonnetVm *vm, double v) 145 | { 146 | vm->gcGrowthTrigger = v; 147 | } 148 | 149 | void jsonnet_string_output(struct JsonnetVm *vm, int v) 150 | { 151 | vm->stringOutput = bool(v); 152 | } 153 | 154 | void jsonnet_import_callback(struct JsonnetVm *vm, JsonnetImportCallback *cb, void *ctx) 155 | { 156 | vm->importCallback = cb; 157 | vm->importCallbackContext = ctx; 158 | } 159 | 160 | void jsonnet_ext_var(JsonnetVm *vm, const char *key, const char *val) 161 | { 162 | vm->extVars[key] = val; 163 | } 164 | 165 | void jsonnet_debug_ast(JsonnetVm *vm, int v) 166 | { 167 | vm->debugAst = v; 168 | } 169 | 170 | void jsonnet_max_trace(JsonnetVm *vm, unsigned v) 171 | { 172 | vm->maxTrace = v; 173 | } 174 | 175 | static char *jsonnet_evaluate_snippet_aux(JsonnetVm *vm, const char *filename, 176 | const char *snippet, int *error, bool multi) 177 | { 178 | try { 179 | Allocator alloc; 180 | AST *expr = jsonnet_parse(&alloc, filename, snippet); 181 | std::string json_str; 182 | std::map files; 183 | if (vm->debugAst) { 184 | json_str = jsonnet_unparse_jsonnet(expr); 185 | } else { 186 | jsonnet_static_analysis(expr); 187 | if (multi) { 188 | files = jsonnet_vm_execute_multi(&alloc, expr, vm->extVars, vm->maxStack, 189 | vm->gcMinObjects, vm->gcGrowthTrigger, 190 | vm->importCallback, vm->importCallbackContext, 191 | vm->stringOutput); 192 | } else { 193 | json_str = jsonnet_vm_execute(&alloc, expr, vm->extVars, vm->maxStack, 194 | vm->gcMinObjects, vm->gcGrowthTrigger, 195 | vm->importCallback, vm->importCallbackContext, 196 | vm->stringOutput); 197 | } 198 | } 199 | if (multi) { 200 | size_t sz = 1; // final sentinel 201 | for (const auto &pair : files) { 202 | sz += pair.first.length() + 1; // include sentinel 203 | sz += pair.second.length() + 2; // Add a '\n' as well as sentinel 204 | } 205 | char *buf = (char*)::malloc(sz); 206 | if (buf == nullptr) memory_panic(); 207 | std::ptrdiff_t i = 0; 208 | for (const auto &pair : files) { 209 | memcpy(&buf[i], pair.first.c_str(), pair.first.length() + 1); 210 | i += pair.first.length() + 1; 211 | memcpy(&buf[i], pair.second.c_str(), pair.second.length()); 212 | i += pair.second.length(); 213 | buf[i] = '\n'; 214 | i++; 215 | buf[i] = '\0'; 216 | i++; 217 | } 218 | buf[i] = '\0'; // final sentinel 219 | *error = false; 220 | return buf; 221 | } else { 222 | json_str += "\n"; 223 | *error = false; 224 | return from_string(vm, json_str); 225 | } 226 | 227 | } catch (StaticError &e) { 228 | std::stringstream ss; 229 | ss << "STATIC ERROR: " << e << std::endl; 230 | *error = true; 231 | return from_string(vm, ss.str()); 232 | 233 | } catch (RuntimeError &e) { 234 | std::stringstream ss; 235 | ss << "RUNTIME ERROR: " << e.msg << std::endl; 236 | const long max_above = vm->maxTrace / 2; 237 | const long max_below = vm->maxTrace - max_above; 238 | const long sz = e.stackTrace.size(); 239 | for (long i = 0 ; i < sz ; ++i) { 240 | const auto &f = e.stackTrace[i]; 241 | if (vm->maxTrace > 0 && i >= max_above && i < sz - max_below) { 242 | if (i == max_above) 243 | ss << "\t..." << std::endl; 244 | } else { 245 | ss << "\t" << f.location << "\t" << f.name << std::endl; 246 | } 247 | } 248 | *error = true; 249 | return from_string(vm, ss.str()); 250 | } 251 | 252 | } 253 | 254 | static char *jsonnet_evaluate_file_aux(JsonnetVm *vm, const char *filename, int *error, bool multi) 255 | { 256 | std::ifstream f; 257 | f.open(filename); 258 | if (!f.good()) { 259 | std::stringstream ss; 260 | ss << "Opening input file: " << filename << ": " << strerror(errno); 261 | *error = true; 262 | return from_string(vm, ss.str()); 263 | } 264 | std::string input; 265 | input.assign(std::istreambuf_iterator(f), 266 | std::istreambuf_iterator()); 267 | 268 | return jsonnet_evaluate_snippet_aux(vm, filename, input.c_str(), error, multi); 269 | } 270 | 271 | char *jsonnet_evaluate_file(JsonnetVm *vm, const char *filename, int *error) 272 | { 273 | TRY 274 | return jsonnet_evaluate_file_aux(vm, filename, error, false); 275 | CATCH("jsonnet_evaluate_file") 276 | return nullptr; // Never happens. 277 | } 278 | 279 | char *jsonnet_evaluate_file_multi(JsonnetVm *vm, const char *filename, int *error) 280 | { 281 | TRY 282 | return jsonnet_evaluate_file_aux(vm, filename, error, true); 283 | CATCH("jsonnet_evaluate_file_multi") 284 | return nullptr; // Never happens. 285 | } 286 | 287 | char *jsonnet_evaluate_snippet(JsonnetVm *vm, const char *filename, const char *snippet, int *error) 288 | { 289 | TRY 290 | return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, false); 291 | CATCH("jsonnet_evaluate_snippet") 292 | return nullptr; // Never happens. 293 | } 294 | 295 | char *jsonnet_evaluate_snippet_multi(JsonnetVm *vm, const char *filename, 296 | const char *snippet, int *error) 297 | { 298 | TRY 299 | return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, true); 300 | CATCH("jsonnet_evaluate_snippet_multi") 301 | return nullptr; // Never happens. 302 | } 303 | 304 | char *jsonnet_realloc(JsonnetVm *vm, char *str, size_t sz) 305 | { 306 | (void) vm; 307 | if (str == nullptr) { 308 | if (sz == 0) return nullptr; 309 | auto *r = static_cast(::malloc(sz)); 310 | if (r == nullptr) memory_panic(); 311 | return r; 312 | } else { 313 | if (sz == 0) { 314 | ::free(str); 315 | return nullptr; 316 | } else { 317 | auto *r = static_cast(::realloc(str, sz)); 318 | if (r == nullptr) memory_panic(); 319 | return r; 320 | } 321 | } 322 | } 323 | 324 | -------------------------------------------------------------------------------- /libjsonnet/ast.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef JSONNET_AST_H 18 | #define JSONNET_AST_H 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "lexer.h" 28 | 29 | enum ASTType { 30 | AST_APPLY, 31 | AST_ARRAY, 32 | AST_BINARY, 33 | AST_BUILTIN_FUNCTION, 34 | AST_CONDITIONAL, 35 | AST_ERROR, 36 | AST_FUNCTION, 37 | AST_IMPORT, 38 | AST_IMPORTSTR, 39 | AST_INDEX, 40 | AST_LOCAL, 41 | AST_LITERAL_BOOLEAN, 42 | AST_LITERAL_NULL, 43 | AST_LITERAL_NUMBER, 44 | AST_LITERAL_STRING, 45 | AST_OBJECT, 46 | AST_OBJECT_COMPOSITION, 47 | AST_SELF, 48 | AST_SUPER, 49 | AST_UNARY, 50 | AST_VAR 51 | }; 52 | 53 | 54 | /** Represents a variable / parameter / field name. */ 55 | struct Identifier { 56 | std::string name; 57 | Identifier(const std::string &name) 58 | : name(name) 59 | { } 60 | }; 61 | 62 | static inline std::ostream &operator<<(std::ostream &o, const Identifier *id) 63 | { 64 | o << id->name; 65 | return o; 66 | } 67 | 68 | 69 | /** All AST nodes are subtypes of this class. 70 | */ 71 | struct AST { 72 | LocationRange location; 73 | ASTType type; 74 | std::vector freeVariables; 75 | AST(const LocationRange &location, ASTType type) 76 | : location(location), type(type) 77 | { 78 | } 79 | virtual ~AST(void) 80 | { 81 | } 82 | }; 83 | 84 | 85 | /** Represents function calls. */ 86 | struct Apply : public AST { 87 | AST *target; 88 | std::vector arguments; 89 | bool tailstrict; 90 | Apply(const LocationRange &lr, AST *target, const std::vector &arguments, bool tailstrict) 91 | : AST(lr, AST_APPLY), target(target), arguments(arguments), tailstrict(tailstrict) 92 | { } 93 | }; 94 | 95 | /** Represents array constructors [1, 2, 3]. */ 96 | struct Array : public AST { 97 | std::vector elements; 98 | Array(const LocationRange &lr, const std::vector &elements) 99 | : AST(lr, AST_ARRAY), elements(elements) 100 | { } 101 | }; 102 | 103 | enum BinaryOp { 104 | BOP_MULT, 105 | BOP_DIV, 106 | 107 | BOP_PLUS, 108 | BOP_MINUS, 109 | 110 | BOP_SHIFT_L, 111 | BOP_SHIFT_R, 112 | 113 | BOP_GREATER, 114 | BOP_GREATER_EQ, 115 | BOP_LESS, 116 | BOP_LESS_EQ, 117 | 118 | BOP_MANIFEST_EQUAL, 119 | BOP_MANIFEST_UNEQUAL, 120 | 121 | BOP_BITWISE_AND, 122 | BOP_BITWISE_XOR, 123 | BOP_BITWISE_OR, 124 | 125 | BOP_AND, 126 | BOP_OR 127 | }; 128 | 129 | static inline std::string bop_string (BinaryOp bop) 130 | { 131 | switch (bop) { 132 | case BOP_MULT: return "*"; 133 | case BOP_DIV: return "/"; 134 | 135 | case BOP_PLUS: return "+"; 136 | case BOP_MINUS: return "-"; 137 | 138 | case BOP_SHIFT_L: return "<<"; 139 | case BOP_SHIFT_R: return ">>"; 140 | 141 | case BOP_GREATER: return ">"; 142 | case BOP_GREATER_EQ: return ">="; 143 | case BOP_LESS: return "<"; 144 | case BOP_LESS_EQ: return "<="; 145 | 146 | case BOP_MANIFEST_EQUAL: return "=="; 147 | case BOP_MANIFEST_UNEQUAL: return "!="; 148 | 149 | case BOP_BITWISE_AND: return "&"; 150 | case BOP_BITWISE_XOR: return "^"; 151 | case BOP_BITWISE_OR: return "|"; 152 | 153 | case BOP_AND: return "&&"; 154 | case BOP_OR: return "||"; 155 | 156 | default: 157 | std::cerr << "INTERNAL ERROR: Unrecognised binary operator: " << bop << std::endl; 158 | std::abort(); 159 | } 160 | } 161 | 162 | /** Represents binary operators. */ 163 | struct Binary : public AST { 164 | AST *left; 165 | BinaryOp op; 166 | AST *right; 167 | Binary(const LocationRange &lr, AST *left, BinaryOp op, AST *right) 168 | : AST(lr, AST_BINARY), left(left), op(op), right(right) 169 | { } 170 | }; 171 | 172 | /** Represents built-in functions. 173 | * 174 | * There is no parse rule to build this AST. Instead, it is used to build the std object in the 175 | * interpreter. 176 | */ 177 | struct BuiltinFunction : public AST { 178 | unsigned long id; 179 | std::vector params; 180 | BuiltinFunction(const LocationRange &lr, unsigned long id, 181 | const std::vector ¶ms) 182 | : AST(lr, AST_BUILTIN_FUNCTION), id(id), params(params) 183 | { } 184 | }; 185 | 186 | /** Represents if then else. */ 187 | struct Conditional : public AST { 188 | AST *cond; 189 | AST *branchTrue; 190 | AST *branchFalse; 191 | Conditional(const LocationRange &lr, AST *cond, AST *branchTrue, AST *branchFalse) 192 | : AST(lr, AST_CONDITIONAL), cond(cond), branchTrue(branchTrue), branchFalse(branchFalse) 193 | { } 194 | }; 195 | 196 | /** Represents error e. */ 197 | struct Error : public AST { 198 | AST *expr; 199 | Error(const LocationRange &lr, AST *expr) 200 | : AST(lr, AST_ERROR), expr(expr) 201 | { } 202 | }; 203 | 204 | /** Represents function calls. */ 205 | struct Function : public AST { 206 | std::vector parameters; 207 | AST *body; 208 | Function(const LocationRange &lr, const std::vector ¶meters, AST *body) 209 | : AST(lr, AST_FUNCTION), parameters(parameters), body(body) 210 | { } 211 | }; 212 | 213 | /** Represents import "file". */ 214 | struct Import : public AST { 215 | std::string file; 216 | Import(const LocationRange &lr, const std::string &file) 217 | : AST(lr, AST_IMPORT), file(file) 218 | { } 219 | }; 220 | 221 | /** Represents importstr "file". */ 222 | struct Importstr : public AST { 223 | std::string file; 224 | Importstr(const LocationRange &lr, const std::string &file) 225 | : AST(lr, AST_IMPORTSTR), file(file) 226 | { } 227 | }; 228 | 229 | /** Represents both e[e] and the syntax sugar e.f. */ 230 | struct Index : public AST { 231 | AST *target; 232 | AST *index; 233 | Index(const LocationRange &lr, AST *target, AST *index) 234 | : AST(lr, AST_INDEX), target(target), index(index) 235 | { } 236 | }; 237 | 238 | /** Represents local x = e; e. */ 239 | struct Local : public AST { 240 | typedef std::map Binds; 241 | Binds binds; 242 | AST *body; 243 | Local(const LocationRange &lr, const Binds &binds, AST *body) 244 | : AST(lr, AST_LOCAL), binds(binds), body(body) 245 | { } 246 | }; 247 | 248 | /** Represents true and false. */ 249 | struct LiteralBoolean : public AST { 250 | bool value; 251 | LiteralBoolean(const LocationRange &lr, bool value) 252 | : AST(lr, AST_LITERAL_BOOLEAN), value(value) 253 | { } 254 | }; 255 | 256 | /** Represents the null keyword. */ 257 | struct LiteralNull : public AST { 258 | LiteralNull(const LocationRange &lr) 259 | : AST(lr, AST_LITERAL_NULL) 260 | { } 261 | }; 262 | 263 | /** Represents JSON numbers. */ 264 | struct LiteralNumber : public AST { 265 | double value; 266 | LiteralNumber(const LocationRange &lr, double value) 267 | : AST(lr, AST_LITERAL_NUMBER), value(value) 268 | { } 269 | }; 270 | 271 | /** Represents JSON strings. */ 272 | struct LiteralString : public AST { 273 | std::string value; 274 | LiteralString(const LocationRange &lr, const std::string &value) 275 | : AST(lr, AST_LITERAL_STRING), value(value) 276 | { } 277 | }; 278 | 279 | /** Represents object constructors { f: e ... }. */ 280 | struct Object : public AST { 281 | struct Field { 282 | enum Hide { 283 | INHERIT, // f: v 284 | HIDDEN, // f:: v 285 | VISIBLE // f::: v 286 | }; 287 | AST *name; 288 | enum Hide hide; 289 | AST *body; 290 | Field(AST *name, enum Hide hide, AST *body) 291 | : name(name), hide(hide), body(body) 292 | { } 293 | }; 294 | typedef std::list Fields; 295 | Fields fields; 296 | Object(const LocationRange &lr, const Fields &fields) 297 | : AST(lr, AST_OBJECT), fields(fields) 298 | { } 299 | }; 300 | 301 | /** Represents object composition { [e]: e for x in e }. */ 302 | struct ObjectComposition : public AST { 303 | AST *field; 304 | AST *value; 305 | const Identifier *id; 306 | AST *array; 307 | ObjectComposition(const LocationRange &lr, AST *field, AST *value, 308 | const Identifier *id, AST *array) 309 | : AST(lr, AST_OBJECT_COMPOSITION), field(field), value(value), id(id), array(array) 310 | { } 311 | }; 312 | 313 | /** Represents the self keyword. */ 314 | struct Self : public AST { 315 | Self(const LocationRange &lr) 316 | : AST(lr, AST_SELF) 317 | { } 318 | }; 319 | 320 | /** Represents the super keyword. */ 321 | struct Super : public AST { 322 | Super(const LocationRange &lr) 323 | : AST(lr, AST_SUPER) 324 | { } 325 | }; 326 | 327 | enum UnaryOp { 328 | UOP_NOT, 329 | UOP_BITWISE_NOT, 330 | UOP_PLUS, 331 | UOP_MINUS 332 | }; 333 | 334 | static inline std::string uop_string (UnaryOp uop) 335 | { 336 | switch (uop) { 337 | case UOP_PLUS: return "+"; 338 | case UOP_MINUS: return "-"; 339 | case UOP_BITWISE_NOT: return "~"; 340 | case UOP_NOT: return "!"; 341 | 342 | default: 343 | std::cerr << "INTERNAL ERROR: Unrecognised unary operator: " << uop << std::endl; 344 | std::abort(); 345 | } 346 | } 347 | 348 | /** Represents unary operators. */ 349 | struct Unary : public AST { 350 | UnaryOp op; 351 | AST *expr; 352 | Unary(const LocationRange &lr, UnaryOp op, AST *expr) 353 | : AST(lr, AST_UNARY), op(op), expr(expr) 354 | { } 355 | }; 356 | 357 | /** Represents variables. */ 358 | struct Var : public AST { 359 | const Identifier *id; 360 | const Identifier *original; 361 | Var(const LocationRange &lr, const Identifier *id) 362 | : AST(lr, AST_VAR), id(id), original(id) 363 | { } 364 | Var(const LocationRange &lr, const Identifier *id, const Identifier *original) 365 | : AST(lr, AST_VAR), id(id), original(original) 366 | { } 367 | }; 368 | 369 | 370 | /** Allocates ASTs on demand, frees them in its destructor. 371 | */ 372 | class Allocator { 373 | std::map internedIdentifiers; 374 | std::vector allocated; 375 | public: 376 | template T* make(Args... args) 377 | { 378 | auto r = new T(args...); 379 | allocated.push_back(r); 380 | return r; 381 | } 382 | /** Returns interned identifiers. 383 | * 384 | * The location used in the Identifier AST is that of the first one parsed. 385 | */ 386 | const Identifier *makeIdentifier(const std::string &name) 387 | { 388 | auto it = internedIdentifiers.find(name); 389 | if (it != internedIdentifiers.end()) { 390 | return it->second; 391 | } 392 | auto r = new Identifier(name); 393 | internedIdentifiers[name] = r; 394 | return r; 395 | } 396 | ~Allocator() 397 | { 398 | for (auto x : allocated) { 399 | delete x; 400 | } 401 | allocated.clear(); 402 | for (auto x : internedIdentifiers) { 403 | delete x.second; 404 | } 405 | internedIdentifiers.clear(); 406 | } 407 | }; 408 | 409 | #endif 410 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /libjsonnet/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /libjsonnet/state.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef JSONNET_STATE_H 18 | #define JSONNET_STATE_H 19 | 20 | namespace { 21 | 22 | /** Mark & sweep: advanced by 1 each GC cycle. 23 | */ 24 | typedef unsigned char GarbageCollectionMark; 25 | 26 | /** Supertype of everything that is allocated on the heap. 27 | */ 28 | struct HeapEntity { 29 | GarbageCollectionMark mark; 30 | virtual ~HeapEntity() { } 31 | }; 32 | 33 | /** Tagged union of all values. 34 | * 35 | * Primitives (<= 8 bytes) are copied by value. Otherwise a pointer to a HeapEntity is used. 36 | */ 37 | struct Value { 38 | enum Type { 39 | NULL_TYPE = 0x0, // Unfortunately NULL is a macro in C. 40 | BOOLEAN = 0x1, 41 | DOUBLE = 0x2, 42 | 43 | ARRAY = 0x10, 44 | FUNCTION = 0x11, 45 | OBJECT = 0x12, 46 | STRING = 0x13 47 | }; 48 | Type t; 49 | union { 50 | HeapEntity *h; 51 | double d; 52 | bool b; 53 | } v; 54 | bool isHeap(void) const 55 | { 56 | return t & 0x10; 57 | } 58 | }; 59 | 60 | /** Convert the type into a string, for error messages. */ 61 | std::string type_str(Value::Type t) 62 | { 63 | switch (t) { 64 | case Value::NULL_TYPE: return "null"; 65 | case Value::BOOLEAN: return "boolean"; 66 | case Value::DOUBLE: return "double"; 67 | case Value::ARRAY: return "array"; 68 | case Value::FUNCTION: return "function"; 69 | case Value::OBJECT: return "object"; 70 | case Value::STRING: return "string"; 71 | default: 72 | std::cerr << "INTERNAL ERROR: Unknown type: " << t << std::endl; 73 | std::abort(); 74 | return ""; // Quiet, compiler. 75 | } 76 | } 77 | 78 | /** Convert the value's type into a string, for error messages. */ 79 | std::string type_str(const Value &v) 80 | { 81 | return type_str(v.t); 82 | } 83 | 84 | struct HeapThunk; 85 | 86 | /** Stores the values bound to variables. 87 | * 88 | * Each nested local statement, function call, and field access has its own binding frame to 89 | * give the values for the local variable, function parameters, or upValues. 90 | */ 91 | typedef std::map BindingFrame; 92 | 93 | /** Supertype of all objects. Types of Value::OBJECT will point at these. */ 94 | struct HeapObject : public HeapEntity { 95 | }; 96 | 97 | /** Hold an unevaluated expression. This implements lazy semantics. 98 | */ 99 | struct HeapThunk : public HeapEntity { 100 | /** Whether or not the thunk was forced. */ 101 | bool filled; 102 | 103 | /** The result when the thunk was forced, if filled == true. */ 104 | Value content; 105 | 106 | /** Used in error tracebacks. */ 107 | const Identifier *name; 108 | 109 | /** The captured environment. 110 | * 111 | * Note, this is non-const because we have to add cyclic references to it. 112 | */ 113 | BindingFrame upValues; 114 | 115 | /** The captured self variable, or nullptr if there was none. \see CallFrame. */ 116 | HeapObject *self; 117 | 118 | /** The offset from the captured self variable. \see CallFrame. */ 119 | unsigned offset; 120 | 121 | /** Evaluated to force the thunk. */ 122 | const AST *body; 123 | 124 | HeapThunk(const Identifier *name, HeapObject *self, unsigned offset, const AST *body) 125 | : filled(false), name(name), self(self), offset(offset), body(body) 126 | { } 127 | 128 | void fill(const Value &v) 129 | { 130 | content = v; 131 | filled = true; 132 | self = nullptr; 133 | upValues.clear(); 134 | } 135 | }; 136 | 137 | struct HeapArray : public HeapEntity { 138 | // It is convenient for this to not be const, so that we can add elements to it one at a 139 | // time after creation. Thus, elements are not GCed as the array is being 140 | // created. 141 | std::vector elements; 142 | HeapArray(const std::vector &elements) 143 | : elements(elements) 144 | { } 145 | }; 146 | 147 | /** Supertype of all objects that are not super objects or extended objects. */ 148 | struct HeapLeafObject : public HeapObject { 149 | }; 150 | 151 | /** Objects created via the simple object constructor construct. */ 152 | struct HeapSimpleObject : public HeapLeafObject { 153 | /** The captured environment. */ 154 | const BindingFrame upValues; 155 | 156 | struct Field { 157 | /** Will the field appear in output? */ 158 | Object::Field::Hide hide; 159 | /** Expression that is evaluated when indexing this field. */ 160 | AST *body; 161 | }; 162 | 163 | /** The fields. 164 | * 165 | * These are evaluated in the captured environment and with self and super bound 166 | * dynamically. 167 | */ 168 | const std::map fields; 169 | 170 | HeapSimpleObject(const BindingFrame &up_values, 171 | const std::map fields) 172 | : upValues(up_values), fields(fields) 173 | { } 174 | }; 175 | 176 | /** Objects created by the extendby construct. */ 177 | struct HeapExtendedObject : public HeapObject { 178 | /** The left hand side of the construct. */ 179 | HeapObject *left; 180 | 181 | /** The right hand side of the construct. */ 182 | HeapObject *right; 183 | 184 | HeapExtendedObject(HeapObject *left, HeapObject *right) 185 | : left(left), right(right) 186 | { } 187 | }; 188 | 189 | /** Objects created by the super construct. */ 190 | struct HeapSuperObject : public HeapObject { 191 | /** The object to bind self when evaluating field bodies when indexing me. */ 192 | HeapObject *root; 193 | 194 | /** The object whose field definitions are used when indexing me. */ 195 | unsigned offset; 196 | 197 | HeapSuperObject(HeapObject *root, unsigned offset) 198 | : root(root), offset(offset) 199 | { } 200 | }; 201 | 202 | struct HeapComprehensionObject : public HeapLeafObject { 203 | 204 | /** The captured environment. */ 205 | const BindingFrame upValues; 206 | 207 | /** The expression used to compute the field values. */ 208 | const AST* value; 209 | 210 | /** The identifier of bound variable in that construct. */ 211 | const Identifier * const id; 212 | 213 | /** Binding for id. 214 | * 215 | * For each field, holds the value that should be bound to id. This is the corresponding 216 | * array element from the original array used to define this object. This should not really 217 | * be a thunk, but it makes the implementation easier. 218 | */ 219 | const std::map compValues; 220 | 221 | HeapComprehensionObject(const BindingFrame &up_values, const AST *value, 222 | const Identifier *id, 223 | const std::map &comp_values) 224 | : upValues(up_values), value(value), id(id), compValues(comp_values) 225 | { } 226 | }; 227 | 228 | /** Stores the function itself and also the captured environment. 229 | * 230 | * Either body is non-null and builtin is 0, or body is null and builtin refers to a built-in 231 | * function. In the former case, the closure represents a user function, otherwise calling it 232 | * will trigger the builtin function to execute. Params is empty when the function is a 233 | * builtin. 234 | */ 235 | struct HeapClosure : public HeapEntity { 236 | /** The captured environment. */ 237 | const BindingFrame upValues; 238 | /** The captured self variable, or nullptr if there was none. \see CallFrame. */ 239 | HeapObject *self; 240 | /** The offset from the captured self variable. \see CallFrame.*/ 241 | unsigned offset; 242 | const std::vector params; 243 | const AST *body; 244 | const unsigned long builtin; 245 | HeapClosure(const BindingFrame &up_values, 246 | HeapObject *self, 247 | unsigned offset, 248 | const std::vector ¶ms, 249 | const AST *body, unsigned long builtin) 250 | : upValues(up_values), self(self), offset(offset), 251 | params(params), body(body), builtin(builtin) 252 | { } 253 | }; 254 | 255 | /** Stores a simple string on the heap. */ 256 | struct HeapString : public HeapEntity { 257 | const std::string value; 258 | HeapString(const std::string &value) 259 | : value(value) 260 | { } 261 | }; 262 | 263 | /** The heap does memory management, i.e. garbage collection. */ 264 | class Heap { 265 | 266 | /** How many objects must exist in the heap before we bother doing garbage collection? 267 | */ 268 | unsigned gcTuneMinObjects; 269 | 270 | /** How much must the heap have grown since the last cycle to trigger a collection? 271 | */ 272 | double gcTuneGrowthTrigger; 273 | 274 | /** Value used to mark entities at the last garbage collection cycle. */ 275 | GarbageCollectionMark lastMark; 276 | 277 | /** The heap entities (strings, arrays, objects, functions, etc). 278 | * 279 | * Not all may be reachable, all should have o->mark == this->lastMark. Entities are 280 | * removed from the heap via O(1) swap with last element, so the ordering of entities is 281 | * arbitrary and changes every garbage collection cycle. 282 | */ 283 | std::vector entities; 284 | 285 | /** The number of heap entities at the last garbage collection cycle. */ 286 | unsigned long lastNumEntities; 287 | 288 | /** The number of heap entities now. */ 289 | unsigned long numEntities; 290 | 291 | /** Add the HeapEntity inside v to vec, if the value exists on the heap. 292 | */ 293 | void addIfHeapEntity(Value v, std::vector &vec) 294 | { 295 | if (v.isHeap()) vec.push_back(v.v.h); 296 | } 297 | 298 | /** Add the HeapEntity inside v to vec, if the value exists on the heap. 299 | */ 300 | void addIfHeapEntity(HeapEntity *v, std::vector &vec) 301 | { 302 | vec.push_back(v); 303 | } 304 | 305 | public: 306 | 307 | Heap(unsigned gc_tune_min_objects, double gc_tune_growth_trigger) 308 | : gcTuneMinObjects(gc_tune_min_objects), gcTuneGrowthTrigger(gc_tune_growth_trigger), 309 | lastMark(0), lastNumEntities(0), numEntities(0) 310 | { 311 | } 312 | 313 | ~Heap(void) 314 | { 315 | // Nothing is marked, everything will be collected. 316 | sweep(); 317 | } 318 | 319 | /** Garbage collection: Mark v, and entities reachable from v. */ 320 | void markFrom(Value v) 321 | { 322 | if (v.isHeap()) markFrom(v.v.h); 323 | } 324 | 325 | /** Garbage collection: Mark heap entities reachable from the given heap entity. */ 326 | void markFrom(HeapEntity *from) 327 | { 328 | assert(from != nullptr); 329 | const GarbageCollectionMark thisMark = lastMark + 1; 330 | struct State { 331 | HeapEntity *ent; 332 | std::vector children; 333 | State(HeapEntity *ent) : ent(ent) { } 334 | }; 335 | 336 | std::vector stack; 337 | stack.emplace_back(from); 338 | 339 | while (stack.size() > 0) { 340 | size_t curr_index = stack.size() - 1; 341 | State &s = stack[curr_index]; 342 | HeapEntity *curr = s.ent; 343 | if (curr->mark != thisMark) { 344 | curr->mark = thisMark; 345 | 346 | if (auto *obj = dynamic_cast(curr)) { 347 | for (auto upv : obj->upValues) 348 | addIfHeapEntity(upv.second, s.children); 349 | 350 | } else if (auto *obj = dynamic_cast(curr)) { 351 | addIfHeapEntity(obj->left, s.children); 352 | addIfHeapEntity(obj->right, s.children); 353 | 354 | } else if (auto *obj = dynamic_cast(curr)) { 355 | for (auto upv : obj->upValues) 356 | addIfHeapEntity(upv.second, s.children); 357 | for (auto upv : obj->compValues) 358 | addIfHeapEntity(upv.second, s.children); 359 | 360 | 361 | } else if (auto *obj = dynamic_cast(curr)) { 362 | addIfHeapEntity(obj->root, s.children); 363 | 364 | } else if (auto *arr = dynamic_cast(curr)) { 365 | for (auto el : arr->elements) 366 | addIfHeapEntity(el, s.children); 367 | 368 | } else if (auto *func = dynamic_cast(curr)) { 369 | for (auto upv : func->upValues) 370 | addIfHeapEntity(upv.second, s.children); 371 | if (func->self) 372 | addIfHeapEntity(func->self, s.children); 373 | 374 | } else if (auto *thunk = dynamic_cast(curr)) { 375 | if (thunk->filled) { 376 | if (thunk->content.isHeap()) 377 | addIfHeapEntity(thunk->content.v.h, s.children); 378 | } else { 379 | for (auto upv : thunk->upValues) 380 | addIfHeapEntity(upv.second, s.children); 381 | if (thunk->self) 382 | addIfHeapEntity(thunk->self, s.children); 383 | } 384 | } 385 | } 386 | 387 | if (s.children.size() > 0) { 388 | HeapEntity *next = s.children[s.children.size() - 1]; 389 | s.children.pop_back(); 390 | stack.emplace_back(next); // CAUTION: s invalidated here 391 | } else { 392 | stack.pop_back(); // CAUTION: s invalidated here 393 | } 394 | } 395 | } 396 | 397 | /** Delete everything that was not marked since the last collection. */ 398 | void sweep(void) 399 | { 400 | lastMark++; 401 | // Heap shrinks during this loop. Do not cache entities.size(). 402 | for (unsigned long i=0 ; imark != lastMark) { 405 | delete x; 406 | if (i != entities.size() - 1) { 407 | // Swap it with the back. 408 | entities[i] = entities[entities.size()-1]; 409 | } 410 | entities.pop_back(); 411 | --i; 412 | } 413 | } 414 | lastNumEntities = numEntities = entities.size(); 415 | } 416 | 417 | /** Is it time to initiate a GC cycle? */ 418 | bool checkHeap(void) 419 | { 420 | return numEntities > gcTuneMinObjects 421 | && numEntities > gcTuneGrowthTrigger * lastNumEntities; 422 | } 423 | 424 | /** Allocate a heap entity. 425 | * 426 | * If the heap is large enough (\see gcTuneMinObjects) and has grown by enough since the 427 | * last collection cycle (\see gcTuneGrowthTrigger), a collection cycle is performed. 428 | */ 429 | template T* makeEntity(Args... args) 430 | { 431 | T *r = new T(args...); 432 | entities.push_back(r); 433 | r->mark = lastMark; 434 | numEntities = entities.size(); 435 | return r; 436 | } 437 | 438 | }; 439 | 440 | } 441 | 442 | #endif 443 | -------------------------------------------------------------------------------- /libjsonnet/jsonnet.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | extern "C" { 29 | #include "libjsonnet.h" 30 | } 31 | 32 | struct ImportCallbackContext { 33 | JsonnetVm *vm; 34 | std::vector *jpaths; 35 | }; 36 | 37 | enum ImportStatus { 38 | IMPORT_STATUS_OK, 39 | IMPORT_STATUS_FILE_NOT_FOUND, 40 | IMPORT_STATUS_IO_ERROR 41 | }; 42 | 43 | static enum ImportStatus try_path(const std::string &dir, const std::string &rel, 44 | std::string &content, std::string &err_msg) 45 | { 46 | std::string abs_path; 47 | if (rel.length() == 0) { 48 | err_msg = "The empty string is not a valid filename"; 49 | return IMPORT_STATUS_IO_ERROR; 50 | } 51 | // It is possible that rel is actually absolute. 52 | if (rel[0] == '/') { 53 | abs_path = rel; 54 | } else { 55 | abs_path = dir + rel; 56 | } 57 | 58 | if (abs_path[abs_path.length() - 1] == '/') { 59 | err_msg = "Attempted to import a directory"; 60 | return IMPORT_STATUS_IO_ERROR; 61 | } 62 | 63 | std::ifstream f; 64 | f.open(abs_path.c_str()); 65 | if (!f.good()) return IMPORT_STATUS_FILE_NOT_FOUND; 66 | try { 67 | content.assign(std::istreambuf_iterator(f), std::istreambuf_iterator()); 68 | } catch (const std::ios_base::failure &io_err) { 69 | err_msg = io_err.what(); 70 | return IMPORT_STATUS_IO_ERROR; 71 | } 72 | if (!f.good()) { 73 | err_msg = strerror(errno); 74 | return IMPORT_STATUS_IO_ERROR; 75 | } 76 | 77 | return IMPORT_STATUS_OK; 78 | } 79 | 80 | static char *import_callback (void *ctx_, const char *dir, const char *file, int *success) 81 | { 82 | const auto &ctx = *static_cast(ctx_); 83 | 84 | std::string input; 85 | 86 | std::string err_msg; 87 | 88 | ImportStatus status = try_path(dir, file, input, err_msg); 89 | 90 | std::vector jpaths(*ctx.jpaths); 91 | 92 | // If not found, try library search path. 93 | while (status == IMPORT_STATUS_FILE_NOT_FOUND) { 94 | if (jpaths.size() == 0) { 95 | *success = 0; 96 | const char *err = "No match locally or in the Jsonnet library path."; 97 | char *r = jsonnet_realloc(ctx.vm, nullptr, std::strlen(err) + 1); 98 | std::strcpy(r, err); 99 | return r; 100 | } 101 | status = try_path(jpaths.back(), file, input, err_msg); 102 | jpaths.pop_back(); 103 | } 104 | 105 | if (status == IMPORT_STATUS_IO_ERROR) { 106 | *success = 0; 107 | char *r = jsonnet_realloc(ctx.vm, nullptr, err_msg.length() + 1); 108 | std::strcpy(r, err_msg.c_str()); 109 | return r; 110 | } else { 111 | assert(status == IMPORT_STATUS_OK); 112 | *success = 1; 113 | char *r = jsonnet_realloc(ctx.vm, nullptr, input.length() + 1); 114 | std::strcpy(r, input.c_str()); 115 | return r; 116 | } 117 | } 118 | 119 | std::string next_arg(unsigned &i, const std::vector &args) 120 | { 121 | i++; 122 | if (i >= args.size()) { 123 | std::cerr << "Expected another commandline argument." << std::endl; 124 | exit(EXIT_FAILURE); 125 | } 126 | return args[i]; 127 | } 128 | 129 | /** Collect commandline args into a vector of strings, and expand -foo to -f -o -o. */ 130 | std::vector simplify_args(int argc, const char **argv) 131 | { 132 | std::vector r; 133 | for (int i=1 ; i 2 && arg[0] == '-' && arg[1] != '-') { 144 | for (unsigned j=1 ; j} \n"; 164 | o << "where can be - (stdin)\n"; 165 | o << "and