├── EXPERIMENTAL ├── CREDITS ├── libjsonnet ├── cmd │ └── BUILD ├── include │ ├── BUILD │ └── libjsonnet++.h ├── third_party │ └── md5 │ │ ├── BUILD │ │ ├── LICENSE │ │ ├── md5.h │ │ └── md5.cpp ├── cpp │ ├── BUILD │ └── libjsonnet++.cpp ├── stdlib │ ├── BUILD │ └── to_c_array.cpp ├── core │ ├── static_analysis.h │ ├── libjsonnet_test.cpp │ ├── desugarer.h │ ├── libjsonnet_test.sh │ ├── string_utils.h │ ├── libjsonnet_test_file.c │ ├── json.h │ ├── parser.h │ ├── BUILD │ ├── formatter.h │ ├── pass.h │ ├── static_error.h │ ├── libjsonnet_test_snippet.c │ ├── string_utils.cpp │ ├── vm.h │ ├── unicode.h │ ├── static_analysis.cpp │ ├── pass.cpp │ ├── lexer_test.cpp │ ├── lexer.h │ └── parser_test.cpp ├── README.md ├── Makefile └── LICENSE ├── config.w32 ├── test ├── utf8.jsonnet ├── bar_menu.3.jsonnet ├── martinis.jsonnet ├── bar_menu.6.jsonnet ├── bar_menu.1.jsonnet ├── bar_menu.5.jsonnet ├── bar_menu.2.jsonnet └── test.jsonnet.php ├── .gitignore ├── php_jsonnet.h ├── include ├── Jsonnet.h └── php7_wrapper.h ├── CodeTips └── JsonnetCodeTips.php ├── config.m4 ├── README.md ├── package.xml ├── LICENSE └── jsonnet.c /EXPERIMENTAL: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | Jsonnet 2 | Chitao Gao ( neeke@php.net ) -------------------------------------------------------------------------------- /libjsonnet/cmd/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | cc_binary( 4 | name = "jsonnet", 5 | srcs = ["jsonnet.cpp"], 6 | deps = ["//core:libjsonnet"], 7 | ) 8 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | ARG_ENABLE("jsonnet", "enable jsonnet support", "no"); 5 | 6 | if (PHP_SEASLOG != "no") { 7 | EXTENSION("jsonnet", "jsonnet.c"); 8 | } 9 | 10 | -------------------------------------------------------------------------------- /test/utf8.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | params: { 3 | "person" : {"name": "姓名"} , 4 | "content": "我是汉字", 5 | "d": "123456", 6 | "e.portal": "www.cloudwise.com" 7 | }, 8 | content: { 9 | "n" : $.params.person.name, 10 | "c" : $.params.content, 11 | "e" : $.params["e.portal"] 12 | } 13 | } -------------------------------------------------------------------------------- /libjsonnet/include/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | cc_library( 4 | name = "libjsonnet", 5 | hdrs = ["libjsonnet.h"], 6 | includes = ["."], 7 | ) 8 | 9 | cc_library( 10 | name = "libjsonnet++", 11 | hdrs = ["libjsonnet++.h"], 12 | includes = ["."], 13 | ) 14 | -------------------------------------------------------------------------------- /libjsonnet/third_party/md5/BUILD: -------------------------------------------------------------------------------- 1 | licenses(["permissive"]) 2 | package(default_visibility = ["//visibility:public"]) 3 | 4 | cc_library( 5 | name = "libmd5", 6 | srcs = [ 7 | "md5.cpp", 8 | ], 9 | hdrs = [ 10 | "md5.h", 11 | ], 12 | linkopts = ["-lm"], 13 | includes = ["."], 14 | ) 15 | 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | -------------------------------------------------------------------------------- /libjsonnet/cpp/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | cc_library( 4 | name = "libjsonnet++", 5 | srcs = ["libjsonnet++.cpp"], 6 | deps = [ 7 | "//core:libjsonnet", 8 | "//include:libjsonnet++", 9 | ], 10 | ) 11 | 12 | cc_test( 13 | name = "libjsonnet++_test", 14 | srcs = ["libjsonnet++_test.cpp"], 15 | data = ["//cpp/testdata"], 16 | deps = [ 17 | ":libjsonnet++", 18 | "//external:googletest_main", 19 | ], 20 | ) 21 | -------------------------------------------------------------------------------- /libjsonnet/stdlib/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | filegroup( 4 | name = "stdlib", 5 | srcs = ["std.jsonnet"], 6 | ) 7 | 8 | cc_library( 9 | name = "std", 10 | hdrs = ["std.jsonnet.h"], 11 | srcs = [":gen-std-jsonnet-h"], 12 | includes = ["."], 13 | linkstatic = 1, 14 | ) 15 | 16 | genrule( 17 | name = "gen-std-jsonnet-h", 18 | srcs = ["std.jsonnet"], 19 | outs = ["std.jsonnet.h"], 20 | cmd = "((od -v -Anone -t u1 $< | tr \" \" \"\n\" | grep -v \"^$$\" " + 21 | "| tr \"\n\" \",\" ) && echo \"0\") > $@; " + 22 | "echo >> $@", 23 | ) 24 | -------------------------------------------------------------------------------- /test/bar_menu.3.jsonnet: -------------------------------------------------------------------------------- 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 | // bar_menu.3.jsonnet 18 | { 19 | foo: 3, 20 | bar: 2 * self.foo, // Multiplication. 21 | baz: "The value " + self.bar + " is " 22 | + (if self.bar > 5 then "large" else "small") + ".", 23 | array: [1, 2, 3] + [4], 24 | obj: {a: 1, b: 2} + {b: 3, c: 4}, 25 | equality: 1 == "1", 26 | } 27 | -------------------------------------------------------------------------------- /libjsonnet/core/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 | -------------------------------------------------------------------------------- /libjsonnet/stdlib/to_c_array.cpp: -------------------------------------------------------------------------------- 1 | // Converts stdin string to a comma-separated list of byte values and prints the 2 | // result. Used for transforming the standard library into a C array. 3 | // 4 | // Usage: 5 | // to_c_array 6 | #include 7 | #include 8 | #include 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | if (argc < 3) { 13 | std::cerr << "usage: to_c_array \n"; 14 | return 1; 15 | } 16 | 17 | std::ifstream in_file(argv[1]); 18 | if (!in_file.is_open()) { 19 | std::cerr << "Can't open input file."; 20 | return 1; 21 | } 22 | 23 | std::ofstream out_file(argv[2]); 24 | if (!out_file.is_open()) { 25 | std::cerr << "Can't open output file."; 26 | return 1; 27 | } 28 | 29 | char c; 30 | bool first_character = true; 31 | while (in_file.get(c)) { 32 | if (first_character) { 33 | first_character = false; 34 | } else { 35 | out_file << ","; 36 | } 37 | // Write byte value of c to stdout. 38 | out_file << (int)c; 39 | } 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /libjsonnet/third_party/md5/LICENSE: -------------------------------------------------------------------------------- 1 | MD5 2 | Converted to C++ class by Frank Thilo (thilo@unix-ag.org) 3 | for bzflag (http://www.bzflag.org) 4 | 5 | based on: 6 | 7 | md5.h and md5.c 8 | reference implementation of RFC 1321 9 | 10 | Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All 11 | rights reserved. 12 | 13 | License to copy and use this software is granted provided that it 14 | is identified as the "RSA Data Security, Inc. MD5 Message-Digest 15 | Algorithm" in all material mentioning or referencing this software 16 | or this function. 17 | 18 | License is also granted to make and use derivative works provided 19 | that such works are identified as "derived from the RSA Data 20 | Security, Inc. MD5 Message-Digest Algorithm" in all material 21 | mentioning or referencing the derived work. 22 | 23 | RSA Data Security, Inc. makes no representations concerning either 24 | the merchantability of this software or the suitability of this 25 | software for any particular purpose. It is provided "as is" 26 | without express or implied warranty of any kind. 27 | 28 | These notices must be retained in any copies of any part of this 29 | documentation and/or software. 30 | -------------------------------------------------------------------------------- /libjsonnet/core/libjsonnet_test.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 | extern "C" { 18 | #include "libjsonnet.h" 19 | } 20 | 21 | #include "gtest/gtest.h" 22 | 23 | TEST(JsonnetTest, TestEvaluateSnippet) 24 | { 25 | const char* snippet = "std.assertEqual(({ x: 1, y: self.x } { x: 2 }).y, 2)"; 26 | struct JsonnetVm* vm = jsonnet_make(); 27 | ASSERT_FALSE(vm == nullptr); 28 | int error = 0; 29 | char* output = jsonnet_evaluate_snippet(vm, "snippet", snippet, &error); 30 | EXPECT_EQ(0, error); 31 | jsonnet_realloc(vm, output, 0); 32 | jsonnet_destroy(vm); 33 | } 34 | -------------------------------------------------------------------------------- /libjsonnet/core/desugarer.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_DESUGARING_H 18 | #define JSONNET_DESUGARING_H 19 | 20 | #include 21 | #include 22 | 23 | #include "ast.h" 24 | #include "vm.h" 25 | 26 | /** Translate the AST to remove syntax sugar. 27 | * \param alloc Allocator for making new identifiers / ASTs. 28 | * \param ast The AST to change. 29 | * \param tla the top level arguments. If null then do not try to process 30 | * top-level functions. 31 | */ 32 | void jsonnet_desugar(Allocator *alloc, AST *&ast, std::map *tla); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /test/martinis.jsonnet: -------------------------------------------------------------------------------- 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 | // martinis.jsonnet 18 | { 19 | "Vodka Martini": { 20 | ingredients: [ 21 | { kind: "Vodka", qty: 2 }, 22 | { kind: "Dry White Vermouth", qty: 1 }, 23 | ], 24 | garnish: "Olive", 25 | served: "Straight Up", 26 | }, 27 | Cosmopolitan: { 28 | ingredients: [ 29 | { kind: "Vodka", qty: 2 }, 30 | { kind: "Triple Sec", qty: 0.5 }, 31 | { kind: "Cranberry Juice", qty: 0.75 }, 32 | { kind: "Lime Juice", qty: 0.5 }, 33 | ], 34 | garnish: "Orange Peel", 35 | served: "Straight Up", 36 | }, 37 | } 38 | -------------------------------------------------------------------------------- /libjsonnet/core/libjsonnet_test.sh: -------------------------------------------------------------------------------- 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 | readonly TEST_SNIPPET="std.assertEqual(({ x: 1, y: self.x } { x: 2 }).y, 2)" 16 | readonly JSONNET="jsonnet" 17 | readonly LIBJSONNET_TEST_SNIPPET="libjsonnet_test_snippet" 18 | readonly LIBJSONNET_TEST_FILE="libjsonnet_test_file" 19 | readonly OBJECT_JSONNET="test_suite/object.jsonnet" 20 | 21 | function test_snippet { 22 | $JSONNET -e $TEST_SNIPPET 23 | } 24 | 25 | function test_libjsonnet_snippet { 26 | $LIBJSONNET_TEST_SNIPPET $TEST_SNIPPET 27 | } 28 | 29 | function test_libjsonnet_file { 30 | $LIBJSONNET_TEST_FILE $OBJECT_JSONNET 31 | } 32 | 33 | function main { 34 | test_snippet 35 | test_libjsonnet_snippet 36 | test_libjsonnet_file 37 | } 38 | 39 | main 40 | -------------------------------------------------------------------------------- /libjsonnet/core/string_utils.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_STRING_H 18 | #define JSONNET_STRING_H 19 | 20 | #include "lexer.h" 21 | 22 | /** Unparse the string. */ 23 | UString jsonnet_string_unparse(const UString &str, bool single); 24 | 25 | // Note that the following two functions do not handle the quoting of ' and " 26 | // inside verbatim strings because that quoting is reversible. Thus, that 27 | // quoting is done at lexing time and undone again at pretty-printing time. 28 | 29 | /** Escape special characters. */ 30 | UString jsonnet_string_escape(const UString &str, bool single); 31 | 32 | /** Resolve escape chracters in the string. */ 33 | UString jsonnet_string_unescape(const LocationRange &loc, const UString &s); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /libjsonnet/core/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 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 | -------------------------------------------------------------------------------- /test/bar_menu.6.jsonnet: -------------------------------------------------------------------------------- 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 | // bar_menu.6.jsonnet 18 | { 19 | cocktails: (import "martinis.jsonnet") + { 20 | Manhattan: { 21 | ingredients: [ 22 | { kind: "Rye", qty: 2.5 }, 23 | { kind: "Sweet Red Vermouth", qty: 1 }, 24 | { kind: "Angostura", qty: "dash" }, 25 | ], 26 | garnish: "Maraschino Cherry", 27 | served: "Straight Up", 28 | }, 29 | Cosmopolitan: { 30 | ingredients: [ 31 | { kind: "Vodka", qty: 1.5 }, 32 | { kind: "Cointreau", qty: 1 }, 33 | { kind: "Cranberry Juice", qty: 2 }, 34 | { kind: "Lime Juice", qty: 1 }, 35 | ], 36 | garnish: "Lime Wheel", 37 | served: "Straight Up", 38 | }, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /php_jsonnet.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PHP_JSONNET_H 3 | #define PHP_JSONNET_H 4 | 5 | #ifdef HAVE_CONFIG_H 6 | #include "config.h" 7 | #endif 8 | 9 | #include "include/Jsonnet.h" 10 | 11 | extern zend_module_entry jsonnet_module_entry; 12 | #define phpext_jsonnet_ptr &jsonnet_module_entry 13 | 14 | #ifdef PHP_WIN32 15 | # define PHP_JSONNET_API __declspec(dllexport) 16 | #elif defined(__GNUC__) && __GNUC__ >= 4 17 | # define PHP_JSONNET_API __attribute__ ((visibility("default"))) 18 | #else 19 | # define PHP_JSONNET_API 20 | #endif 21 | 22 | #ifdef ZTS 23 | #include "TSRM.h" 24 | #endif 25 | 26 | PHP_MINIT_FUNCTION(jsonnet); 27 | PHP_MSHUTDOWN_FUNCTION(jsonnet); 28 | PHP_RINIT_FUNCTION(jsonnet); 29 | PHP_RSHUTDOWN_FUNCTION(jsonnet); 30 | PHP_MINFO_FUNCTION(jsonnet); 31 | 32 | PHP_FUNCTION(jsonnet_get_version); 33 | PHP_FUNCTION(jsonnet_get_author); 34 | 35 | zend_class_entry *jsonnet_ce,*php_com_exception_class_entry; 36 | 37 | PHP_METHOD(JSONNET_RES_NAME, __construct); 38 | PHP_METHOD(JSONNET_RES_NAME, __destruct); 39 | PHP_METHOD(JSONNET_RES_NAME, evaluateFile); 40 | PHP_METHOD(JSONNET_RES_NAME, evaluateSnippet); 41 | PHP_METHOD(JSONNET_RES_NAME, fmtFile); 42 | PHP_METHOD(JSONNET_RES_NAME, fmtSnippet); 43 | 44 | ZEND_BEGIN_MODULE_GLOBALS(jsonnet) 45 | 46 | ZEND_END_MODULE_GLOBALS(jsonnet) 47 | 48 | extern ZEND_DECLARE_MODULE_GLOBALS(jsonnet); 49 | 50 | #ifdef ZTS 51 | #define JSONNET_G(v) TSRMG(jsonnet_globals_id, zend_jsonnet_globals *, v) 52 | #else 53 | #define JSONNET_G(v) (jsonnet_globals.v) 54 | #endif 55 | 56 | #endif /* PHP_JSONNET_H */ 57 | 58 | -------------------------------------------------------------------------------- /test/bar_menu.1.jsonnet: -------------------------------------------------------------------------------- 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 | /* bar_menu.1.jsonnet */ 18 | { 19 | cocktails: { 20 | // Ingredient quantities are in fluid ounces. 21 | "Tom Collins": { 22 | ingredients: [ 23 | { kind: "Farmers Gin", qty: 1.5 }, 24 | { kind: "Lemon", qty: 1 }, 25 | { kind: "Simple Syrup", qty: 0.5 }, 26 | { kind: "Soda", qty: 2 }, 27 | { kind: "Angostura", qty: "dash" }, 28 | ], 29 | garnish: "Maraschino Cherry", 30 | served: "Tall", 31 | }, 32 | Manhattan: { 33 | ingredients: [ 34 | { kind: "Rye", qty: 2.5 }, 35 | { kind: "Sweet Red Vermouth", qty: 1 }, 36 | { kind: "Angostura", qty: "dash" }, 37 | ], 38 | garnish: "Maraschino Cherry", 39 | served: "Straight Up", 40 | }, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /libjsonnet/core/json.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_JSON_H 18 | #define JSONNET_JSON_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | struct JsonnetJsonValue { 27 | enum Kind { 28 | ARRAY, 29 | BOOL, 30 | NULL_KIND, 31 | NUMBER, 32 | OBJECT, 33 | STRING, 34 | }; 35 | 36 | JsonnetJsonValue() = default; 37 | JsonnetJsonValue(JsonnetJsonValue&) = delete; 38 | JsonnetJsonValue(JsonnetJsonValue&&) = default; 39 | 40 | JsonnetJsonValue(Kind kind, std::string string, double number) 41 | : kind(kind), string(string), number(number) 42 | { 43 | } 44 | 45 | Kind kind; 46 | std::string string; 47 | double number; // Also used for bool (0.0 and 1.0) 48 | std::vector> elements; 49 | std::map> fields; 50 | }; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /libjsonnet/core/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 "ast.h" 23 | #include "lexer.h" 24 | #include "unicode.h" 25 | 26 | /** Parse a given JSON++ string. 27 | * 28 | * \param alloc Used to allocate the AST nodes. The Allocator must outlive the 29 | * AST pointer returned. 30 | * \param tokens The list of tokens (all tokens are popped except EOF). 31 | * \returns The parsed abstract syntax tree. 32 | */ 33 | AST *jsonnet_parse(Allocator *alloc, Tokens &tokens); 34 | 35 | /** Outputs a number, trying to preserve precision as well as possible. 36 | */ 37 | std::string jsonnet_unparse_number(double v); 38 | 39 | /** The inverse of jsonnet_parse. 40 | */ 41 | std::string jsonnet_unparse_jsonnet(const AST *ast, const Fodder &final_fodder, unsigned indent, 42 | bool pad_arrays, bool pad_objects, char comment_style); 43 | 44 | #endif // JSONNET_PARSER_H 45 | -------------------------------------------------------------------------------- /test/bar_menu.5.jsonnet: -------------------------------------------------------------------------------- 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 | // bar_menu.5.jsonnet 18 | { 19 | cocktails: { 20 | "Bee's Knees": { 21 | // Construct the ingredients by using 4/3 oz 22 | // of each element in the given list. 23 | ingredients: [ // Array comprehension. 24 | { kind: i, qty: 4/3 } 25 | for i in ["Honey Syrup", "Lemon Juice", "Farmers Gin"] 26 | ], 27 | garnish: "Lemon Twist", 28 | served: "Straight Up", 29 | }, 30 | } + { // Object comprehension. 31 | [sd.name + "Screwdriver"]: { 32 | ingredients: [ 33 | { kind: "Vodka", qty: 1.5 }, 34 | { kind: sd.fruit, qty: 3 }, 35 | ], 36 | garnish: null, 37 | served: "On The Rocks" 38 | } for sd in [ 39 | {name: "Yellow ", fruit: "Lemonade"}, 40 | {name: "", fruit: "Orange Juice"}, 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /libjsonnet/core/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | cc_library( 4 | name = "libjsonnet", 5 | srcs = [ 6 | "desugarer.cpp", 7 | "formatter.cpp", 8 | "lexer.cpp", 9 | "libjsonnet.cpp", 10 | "parser.cpp", 11 | "pass.cpp", 12 | "static_analysis.cpp", 13 | "string_utils.cpp", 14 | "vm.cpp", 15 | ], 16 | hdrs = [ 17 | "ast.h", 18 | "desugarer.h", 19 | "formatter.h", 20 | "json.h", 21 | "lexer.h", 22 | "parser.h", 23 | "pass.h", 24 | "state.h", 25 | "static_analysis.h", 26 | "static_error.h", 27 | "string_utils.h", 28 | "unicode.h", 29 | "vm.h", 30 | ], 31 | deps = [ 32 | "//include:libjsonnet", 33 | "//stdlib:std", 34 | "//third_party/md5:libmd5", 35 | ], 36 | linkopts = ["-lm"], 37 | includes = ["."], 38 | ) 39 | 40 | cc_test( 41 | name = "lexer_test", 42 | srcs = ["lexer_test.cpp"], 43 | deps = [ 44 | ":libjsonnet", 45 | # Note: On Ubuntu, apt-get install libgtest-dev google-mock 46 | "//external:googletest_main", 47 | ], 48 | ) 49 | 50 | cc_test( 51 | name = "parser_test", 52 | srcs = ["parser_test.cpp"], 53 | deps = [ 54 | ":libjsonnet", 55 | "//external:googletest_main", 56 | ], 57 | ) 58 | 59 | cc_test( 60 | name = "libjsonnet_test", 61 | srcs = ["libjsonnet_test.cpp"], 62 | deps = [ 63 | ":libjsonnet", 64 | "//external:googletest_main", 65 | ], 66 | ) 67 | -------------------------------------------------------------------------------- /libjsonnet/README.md: -------------------------------------------------------------------------------- 1 | # Jsonnet - The data templating language 2 | 3 | [![Build Status](https://travis-ci.org/google/jsonnet.svg?branch=master)](https://travis-ci.org/google/jsonnet) 4 | 5 | For an introduction to Jsonnet and documentation, 6 | [visit our website](http://jsonnet.org). 7 | 8 | Visit our [discussion forum](https://groups.google.com/forum/#!forum/jsonnet). 9 | 10 | ## Building Jsonnet 11 | 12 | You can use either GCC or Clang to build Jsonnet. Note that on recent versions 13 | of macOS, `/usr/bin/gcc` and `/usr/bin/g++` are actually Clang, so there is no 14 | difference. 15 | 16 | ### Makefile 17 | 18 | To build Jsonnet with GCC, run: 19 | 20 | ``` 21 | make 22 | ``` 23 | 24 | To build Jsonnet with Clang, run: 25 | 26 | ``` 27 | make CC=clang CXX=clang++ 28 | ``` 29 | 30 | To run the output binary, run: 31 | 32 | ``` 33 | ./jsonnet 34 | ``` 35 | 36 | ### Bazel 37 | 38 | Bazel builds are also supported. 39 | Install [Bazel](https://www.bazel.io/versions/master/docs/install.html) if it is 40 | not installed already. Then, run the following command to build with GCC: 41 | 42 | ``` 43 | bazel build -c opt //cmd:jsonnet 44 | ``` 45 | 46 | To build with Clang, use one of these two options: 47 | 48 | ``` 49 | env CC=clang CXX=clang++ bazel build -c opt //cmd:jsonnet 50 | 51 | # OR 52 | 53 | bazel build -c opt --action_env=CC=clang --action_env=CXX=clang++ //cmd:jsonnet 54 | ``` 55 | 56 | This builds the `jsonnet` target defined in [`cmd/BUILD`](./cmd/BUILD). To 57 | launch the output binary, run: 58 | 59 | ``` 60 | bazel-bin/cmd/jsonnet 61 | ``` 62 | 63 | 64 | ## Contributing 65 | 66 | See the [contributing page](http://jsonnet.org/contributing.html) on our website. 67 | -------------------------------------------------------------------------------- /libjsonnet/core/formatter.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_FORMATTER_H 18 | #define JSONNET_FORMATTER_H 19 | 20 | #include "ast.h" 21 | 22 | struct FmtOpts { 23 | char stringStyle; 24 | char commentStyle; 25 | unsigned indent; 26 | unsigned maxBlankLines; 27 | bool padArrays; 28 | bool padObjects; 29 | bool stripComments; 30 | bool stripAllButComments; 31 | bool stripEverything; 32 | bool prettyFieldNames; 33 | bool sortImports; 34 | FmtOpts(void) 35 | : stringStyle('s'), 36 | commentStyle('s'), 37 | indent(2), 38 | maxBlankLines(2), 39 | padArrays(false), 40 | padObjects(true), 41 | stripComments(false), 42 | stripAllButComments(false), 43 | stripEverything(false), 44 | prettyFieldNames(true), 45 | sortImports(true) 46 | { 47 | } 48 | }; 49 | 50 | /** The inverse of jsonnet_parse. 51 | */ 52 | std::string jsonnet_fmt(AST *ast, Fodder &final_fodder, const FmtOpts &opts); 53 | 54 | #endif // JSONNET_PARSER_H 55 | -------------------------------------------------------------------------------- /include/Jsonnet.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Jsonnet | 4 | +----------------------------------------------------------------------+ 5 | | This source file is subject to version 2.0 of the Apache license, | 6 | | that is bundled with this package in the file LICENSE, and is | 7 | | available through the world-wide-web at the following url: | 8 | | http://www.apache.org/licenses/LICENSE-2.0.html | 9 | | If you did not receive a copy of the Apache2.0 license and are unable| 10 | | to obtain it through the world-wide-web, please send a note to | 11 | | license@php.net so we can mail you a copy immediately. | 12 | +----------------------------------------------------------------------+ 13 | | Author: Neeke.Gao | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifndef _JSONNET_H_ 18 | #define _JSONNET_H_ 19 | 20 | #include "php.h" 21 | #include "php_ini.h" 22 | #include "ext/standard/info.h" 23 | #include "ext/standard/file.h" 24 | #include "ext/standard/url.h" 25 | #include "ext/standard/php_string.h" 26 | #include "ext/json/php_json.h" 27 | #include "Zend/zend_exceptions.h" 28 | #include "php7_wrapper.h" 29 | 30 | #define JSONNET_RES_NAME "Jsonnet" 31 | #define JSONNET_PHP_VERSION "v1.3.1" 32 | #define JSONNET_PHP_AUTHOR "Chitao.Gao [ neeke@php.net ]" 33 | 34 | #define SL_S(s) s, sizeof(s) - 1 35 | 36 | #define CODE_SUCCESS 1000 37 | #define CODE_ERROR 900 38 | 39 | #endif /* _JSONNET_H_ */ -------------------------------------------------------------------------------- /test/bar_menu.2.jsonnet: -------------------------------------------------------------------------------- 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 | // bar_menu.2.jsonnet 18 | { 19 | cocktails: { 20 | "Tom Collins": { 21 | ingredients: [ 22 | { kind: "Farmers Gin", qty: 1.5 }, 23 | { kind: "Lemon", qty: 1 }, 24 | { kind: "Simple Syrup", qty: 0.5 }, 25 | { kind: "Soda", qty: 2 }, 26 | { kind: "Angostura", qty: "dash" }, 27 | ], 28 | garnish: "Maraschino Cherry", 29 | served: "Tall", 30 | }, 31 | Martini: { 32 | ingredients: [ 33 | { 34 | // Evaluate a path to get the first ingredient of the Tom Collins. 35 | kind: $.cocktails["Tom Collins"].ingredients[0].kind, 36 | // or $["cocktails"]["Tom Collins"]["ingredients"][0]["kind"], 37 | qty: 1 38 | }, 39 | { kind: "Dry White Vermouth", qty: 1 }, 40 | ], 41 | garnish: "Olive", 42 | served: "Straight Up", 43 | }, 44 | "Gin Martini": self.Martini, 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /test/test.jsonnet.php: -------------------------------------------------------------------------------- 1 | getCode()); 55 | var_dump($e->getMessage()); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /include/php7_wrapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Jsonnet | 4 | +----------------------------------------------------------------------+ 5 | | This source file is subject to version 2.0 of the Apache license, | 6 | | that is bundled with this package in the file LICENSE, and is | 7 | | available through the world-wide-web at the following url: | 8 | | http://www.apache.org/licenses/LICENSE-2.0.html | 9 | | If you did not receive a copy of the Apache2.0 license and are unable| 10 | | to obtain it through the world-wide-web, please send a note to | 11 | | license@php.net so we can mail you a copy immediately. | 12 | +----------------------------------------------------------------------+ 13 | | Author: Neeke.Gao | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #if PHP_VERSION_ID >= 70000 18 | 19 | # define JSONNET_MAKE_ZVAL(z) zval _stack_zval_##z; z = &(_stack_zval_##z) 20 | 21 | # define JSONNET_ZEND_REGISTER_CLASS(z) zend_register_internal_class_ex(z, NULL); 22 | # define JSONNET_ZVAL_STRING(z, s) ZVAL_STRING(z, s) 23 | # define JSONNET_ZVAL_STRINGL(z, s, l) ZVAL_STRINGL(z, s, l) 24 | # define JSONNET_RETURN_STRINGL(k, l) RETURN_STRINGL(k, l) 25 | # define JSONNET_ZVAL_PTR_DTOR(z) zval_ptr_dtor(z) 26 | 27 | #else 28 | 29 | # define JSONNET_MAKE_ZVAL(z) MAKE_STD_ZVAL(z) 30 | # define JSONNET_ZEND_REGISTER_CLASS(z) zend_register_internal_class_ex(z, NULL, NULL TSRMLS_CC); 31 | # define JSONNET_ZVAL_STRING(z, s) ZVAL_STRING(z, s, 1) 32 | # define JSONNET_ZVAL_STRINGL(z, s, l) ZVAL_STRINGL(z, s, l, 1) 33 | # define JSONNET_RETURN_STRINGL(k, l) RETURN_STRINGL(k, l, 1) 34 | # define JSONNET_ZVAL_PTR_DTOR(z) zval_ptr_dtor(&z) 35 | 36 | #endif -------------------------------------------------------------------------------- /CodeTips/JsonnetCodeTips.php: -------------------------------------------------------------------------------- 1 | 27 | ], [ 28 | int epollfd; 29 | struct epoll_event e; 30 | 31 | epollfd = epoll_create(1); 32 | if (epollfd < 0) { 33 | return 1; 34 | } 35 | 36 | e.events = EPOLLIN | EPOLLET; 37 | e.data.fd = 0; 38 | 39 | if (epoll_ctl(epollfd, EPOLL_CTL_ADD, 0, &e) == -1) { 40 | return 1; 41 | } 42 | 43 | e.events = 0; 44 | if (epoll_wait(epollfd, &e, 1, 1) < 0) { 45 | return 1; 46 | } 47 | ], [ 48 | AC_DEFINE([HAVE_EPOLL], 1, [do we have epoll?]) 49 | AC_MSG_RESULT([yes]) 50 | ], [ 51 | AC_MSG_RESULT([no]) 52 | ]) 53 | ]) 54 | 55 | if test "$PHP_JSONNET" != "no"; then 56 | THIS_DIR=`dirname $0` 57 | MAKE_LIB_JSONNET=`cd ${THIS_DIR}/libjsonnet && make libjsonnet.so` 58 | 59 | # --with-jsonnet -> check with-path 60 | SEARCH_PATH="/usr/local/lib" 61 | SEARCH_FOR="libjsonnet.h" 62 | if test -r $PHP_JSONNET/$SEARCH_FOR; then # path given as parameter 63 | JSONNET_DIR=$PHP_JSONNET 64 | else # search default path list 65 | AC_MSG_CHECKING([for jsonnet files in default path]) 66 | for i in $SEARCH_PATH ; do 67 | if test -r $i/$SEARCH_FOR; then 68 | JSONNET_DIR=$i 69 | AC_MSG_RESULT(found in $i) 70 | fi 71 | done 72 | fi 73 | 74 | if test -z "$JSONNET_DIR"; then 75 | AC_MSG_RESULT([not found]) 76 | AC_MSG_ERROR([Please reinstall the jsonnet distribution]) 77 | fi 78 | 79 | # --with-jsonnet -> add include path 80 | PHP_ADD_INCLUDE($JSONNET_DIR) 81 | 82 | # --with-jsonnet -> check for lib and symbol presence 83 | 84 | PHP_ADD_INCLUDE($JSONNET_DIR) 85 | PHP_EVAL_LIBLINE($JSONNET_DIR, JSONNET_SHARED_LIBADD) 86 | PHP_ADD_LIBRARY_WITH_PATH(jsonnet, $JSONNET_DIR, JSONNET_SHARED_LIBADD) 87 | 88 | AC_JSONNET_EPOLL() 89 | 90 | PHP_SUBST(JSONNET_SHARED_LIBADD) 91 | 92 | PHP_NEW_EXTENSION(jsonnet, jsonnet.c, $ext_shared) 93 | fi 94 | -------------------------------------------------------------------------------- /libjsonnet/core/pass.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_PASS_H 18 | #define JSONNET_PASS_H 19 | 20 | #include "ast.h" 21 | 22 | /** A generic Pass that does nothing but can be extended to easily define real passes. 23 | */ 24 | class CompilerPass { 25 | public: 26 | protected: 27 | Allocator &alloc; 28 | 29 | public: 30 | CompilerPass(Allocator &alloc) : alloc(alloc) {} 31 | 32 | virtual void fodderElement(FodderElement &) {} 33 | 34 | virtual void fodder(Fodder &fodder); 35 | 36 | virtual void specs(std::vector &specs); 37 | 38 | virtual void params(Fodder &fodder_l, ArgParams ¶ms, Fodder &fodder_r); 39 | 40 | virtual void fieldParams(ObjectField &field); 41 | 42 | virtual void fields(ObjectFields &fields); 43 | 44 | virtual void expr(AST *&ast_); 45 | 46 | virtual void visit(Apply *ast); 47 | 48 | virtual void visit(ApplyBrace *ast); 49 | 50 | virtual void visit(Array *ast); 51 | 52 | virtual void visit(ArrayComprehension *ast); 53 | 54 | virtual void visit(Assert *ast); 55 | 56 | virtual void visit(Binary *ast); 57 | 58 | virtual void visit(BuiltinFunction *) {} 59 | 60 | virtual void visit(Conditional *ast); 61 | 62 | virtual void visit(Dollar *) {} 63 | 64 | virtual void visit(Error *ast); 65 | 66 | virtual void visit(Function *ast); 67 | 68 | virtual void visit(Import *ast); 69 | 70 | virtual void visit(Importstr *ast); 71 | 72 | virtual void visit(InSuper *ast); 73 | 74 | virtual void visit(Index *ast); 75 | 76 | virtual void visit(Local *ast); 77 | 78 | virtual void visit(LiteralBoolean *) {} 79 | 80 | virtual void visit(LiteralNumber *) {} 81 | 82 | virtual void visit(LiteralString *) {} 83 | 84 | virtual void visit(LiteralNull *) {} 85 | 86 | virtual void visit(Object *ast); 87 | 88 | virtual void visit(DesugaredObject *ast); 89 | 90 | virtual void visit(ObjectComprehension *ast); 91 | 92 | virtual void visit(ObjectComprehensionSimple *ast); 93 | 94 | virtual void visit(Parens *ast); 95 | 96 | virtual void visit(Self *) {} 97 | 98 | virtual void visit(SuperIndex *ast); 99 | 100 | virtual void visit(Unary *ast); 101 | 102 | virtual void visit(Var *) {} 103 | 104 | virtual void visitExpr(AST *&ast_); 105 | 106 | virtual void file(AST *&body, Fodder &final_fodder); 107 | }; 108 | 109 | /** Return an equivalent AST that can be modified without affecting the original. 110 | * 111 | * This is a deep copy. 112 | */ 113 | AST *clone_ast(Allocator &alloc, AST *ast); 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /libjsonnet/third_party/md5/md5.h: -------------------------------------------------------------------------------- 1 | /* MD5 2 | converted to C++ class by Frank Thilo (thilo@unix-ag.org) 3 | for bzflag (http://www.bzflag.org) 4 | 5 | based on: 6 | 7 | md5.h and md5.c 8 | reference implementation of RFC 1321 9 | 10 | Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All 11 | rights reserved. 12 | 13 | License to copy and use this software is granted provided that it 14 | is identified as the "RSA Data Security, Inc. MD5 Message-Digest 15 | Algorithm" in all material mentioning or referencing this software 16 | or this function. 17 | 18 | License is also granted to make and use derivative works provided 19 | that such works are identified as "derived from the RSA Data 20 | Security, Inc. MD5 Message-Digest Algorithm" in all material 21 | mentioning or referencing the derived work. 22 | 23 | RSA Data Security, Inc. makes no representations concerning either 24 | the merchantability of this software or the suitability of this 25 | software for any particular purpose. It is provided "as is" 26 | without express or implied warranty of any kind. 27 | 28 | These notices must be retained in any copies of any part of this 29 | documentation and/or software. 30 | 31 | */ 32 | 33 | #ifndef BZF_MD5_H 34 | #define BZF_MD5_H 35 | 36 | #include 37 | #include 38 | 39 | 40 | // a small class for calculating MD5 hashes of strings or byte arrays 41 | // it is not meant to be fast or secure 42 | // 43 | // usage: 1) feed it blocks of uchars with update() 44 | // 2) finalize() 45 | // 3) get hexdigest() string 46 | // or 47 | // MD5(std::string).hexdigest() 48 | // 49 | // assumes that char is 8 bit and int is 32 bit 50 | class MD5 51 | { 52 | public: 53 | typedef unsigned int size_type; // must be 32bit 54 | 55 | MD5(); 56 | MD5(const std::string& text); 57 | void update(const unsigned char *buf, size_type length); 58 | void update(const char *buf, size_type length); 59 | MD5& finalize(); 60 | std::string hexdigest() const; 61 | friend std::ostream& operator<<(std::ostream&, MD5 md5); 62 | 63 | private: 64 | void init(); 65 | typedef unsigned char uint1; // 8bit 66 | typedef unsigned int uint4; // 32bit 67 | enum {blocksize = 64}; // VC6 won't eat a const static int here 68 | 69 | void transform(const uint1 block[blocksize]); 70 | static void decode(uint4 output[], const uint1 input[], size_type len); 71 | static void encode(uint1 output[], const uint4 input[], size_type len); 72 | 73 | bool finalized; 74 | uint1 buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk 75 | uint4 count[2]; // 64bit counter for number of bits (lo, hi) 76 | uint4 state[4]; // digest so far 77 | uint1 digest[16]; // the result 78 | 79 | // low level logic operations 80 | static inline uint4 F(uint4 x, uint4 y, uint4 z); 81 | static inline uint4 G(uint4 x, uint4 y, uint4 z); 82 | static inline uint4 H(uint4 x, uint4 y, uint4 z); 83 | static inline uint4 I(uint4 x, uint4 y, uint4 z); 84 | static inline uint4 rotate_left(uint4 x, int n); 85 | static inline void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); 86 | static inline void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); 87 | static inline void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); 88 | static inline void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); 89 | }; 90 | 91 | std::string md5(const std::string str); 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /libjsonnet/core/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 | #include 21 | #include 22 | 23 | struct Location { 24 | unsigned long line; 25 | unsigned long column; 26 | Location(void) : line(0), column(0) {} 27 | Location(unsigned long line, unsigned long column) : line(line), column(column) {} 28 | bool isSet(void) const 29 | { 30 | return line != 0; 31 | } 32 | Location successor(void) const 33 | { 34 | return Location(this->line, this->column + 1); 35 | } 36 | }; 37 | 38 | static inline std::ostream &operator<<(std::ostream &o, const Location &loc) 39 | { 40 | o << loc.line << ":" << loc.column; 41 | return o; 42 | } 43 | 44 | struct LocationRange { 45 | std::string file; 46 | // [begin, end) 47 | Location begin, end; 48 | LocationRange(void) {} 49 | /** This is useful for special locations, e.g. manifestation entry point. */ 50 | LocationRange(const std::string &msg) : file(msg) {} 51 | LocationRange(const std::string &file, const Location &begin, const Location &end) 52 | : file(file), begin(begin), end(end) 53 | { 54 | } 55 | bool isSet(void) const 56 | { 57 | return begin.isSet(); 58 | } 59 | }; 60 | 61 | static inline std::ostream &operator<<(std::ostream &o, const LocationRange &loc) 62 | { 63 | if (loc.file.length() > 0) 64 | o << loc.file; 65 | if (loc.isSet()) { 66 | if (loc.file.length() > 0) 67 | o << ":"; 68 | if (loc.begin.line == loc.end.line) { 69 | if (loc.begin.column == loc.end.column - 1) { 70 | o << loc.begin; 71 | } else { 72 | o << loc.begin << "-" << loc.end.column; 73 | } 74 | } else { 75 | o << "(" << loc.begin << ")-(" << loc.end << ")"; 76 | } 77 | } 78 | return o; 79 | } 80 | 81 | struct StaticError { 82 | LocationRange location; 83 | std::string msg; 84 | StaticError(const std::string &msg) : msg(msg) {} 85 | StaticError(const std::string &filename, const Location &location, const std::string &msg) 86 | : location(filename, location, location.successor()), msg(msg) 87 | { 88 | } 89 | StaticError(const LocationRange &location, const std::string &msg) 90 | : location(location), msg(msg) 91 | { 92 | } 93 | 94 | std::string toString() const 95 | { 96 | std::stringstream ss; 97 | if (location.isSet()) { 98 | ss << location << ":"; 99 | } 100 | ss << " " << msg; 101 | return ss.str(); 102 | } 103 | }; 104 | 105 | static inline std::ostream &operator<<(std::ostream &o, const StaticError &err) 106 | { 107 | o << err.toString(); 108 | return o; 109 | } 110 | 111 | #endif // JSONNET_ERROR_H 112 | -------------------------------------------------------------------------------- /libjsonnet/core/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 | #include 20 | #include 21 | 22 | #include 23 | 24 | typedef struct JsonnetJsonValue JJV; 25 | 26 | static JJV *native_concat(void *ctx, const JJV * const *argv, int *succ) 27 | { 28 | struct JsonnetVm *vm = (struct JsonnetVm *)ctx; 29 | const char *a = jsonnet_json_extract_string(vm, argv[0]); 30 | const char *b = jsonnet_json_extract_string(vm, argv[1]); 31 | if (a == NULL || b == NULL) { 32 | *succ = 0; 33 | return jsonnet_json_make_string(vm, "Bad params."); 34 | } 35 | char *str = malloc(strlen(a) + strlen(b) + 1); 36 | sprintf(str, "%s%s", a, b); 37 | JJV *r = jsonnet_json_make_string(vm, str); 38 | free(str); 39 | *succ = 1; 40 | return r; 41 | } 42 | 43 | static JJV *native_square(void *ctx, const JJV * const *argv, int *succ) 44 | { 45 | struct JsonnetVm *vm = (struct JsonnetVm *)ctx; 46 | double a; 47 | if (!jsonnet_json_extract_number(vm, argv[0], &a)) { 48 | *succ = 0; 49 | return jsonnet_json_make_string(vm, "Bad param 'a'."); 50 | } 51 | *succ = 1; 52 | return jsonnet_json_make_number(vm, a * a); 53 | } 54 | 55 | static JJV *native_build(void *ctx, const JJV * const *argv, int *succ) 56 | { 57 | struct JsonnetVm *vm = (struct JsonnetVm *)ctx; 58 | (void) argv; 59 | JJV *obj_top = jsonnet_json_make_object(vm); 60 | JJV *arr_top = jsonnet_json_make_array(vm); 61 | JJV *arr1 = jsonnet_json_make_array(vm); 62 | jsonnet_json_array_append(vm, arr1, jsonnet_json_make_string(vm, "Test 1.1")); 63 | jsonnet_json_array_append(vm, arr1, jsonnet_json_make_string(vm, "Test 1.2")); 64 | jsonnet_json_array_append(vm, arr1, jsonnet_json_make_string(vm, "Test 1.3")); 65 | jsonnet_json_array_append(vm, arr1, jsonnet_json_make_bool(vm, 1)); 66 | jsonnet_json_array_append(vm, arr1, jsonnet_json_make_number(vm, 42)); 67 | jsonnet_json_array_append(vm, arr1, jsonnet_json_make_null(vm)); 68 | jsonnet_json_array_append(vm, arr1, jsonnet_json_make_object(vm)); 69 | jsonnet_json_array_append(vm, arr_top, arr1); 70 | JJV *arr2 = jsonnet_json_make_array(vm); 71 | jsonnet_json_array_append(vm, arr2, jsonnet_json_make_string(vm, "Test 2.1")); 72 | jsonnet_json_array_append(vm, arr2, jsonnet_json_make_string(vm, "Test 2.2")); 73 | jsonnet_json_array_append(vm, arr2, jsonnet_json_make_string(vm, "Test 2.3")); 74 | jsonnet_json_array_append(vm, arr2, jsonnet_json_make_bool(vm, 0)); 75 | jsonnet_json_array_append(vm, arr2, jsonnet_json_make_number(vm, -42)); 76 | jsonnet_json_array_append(vm, arr2, jsonnet_json_make_null(vm)); 77 | JJV *little_obj = jsonnet_json_make_object(vm); 78 | jsonnet_json_object_append(vm, little_obj, "f", jsonnet_json_make_string(vm, "foo")); 79 | jsonnet_json_object_append(vm, little_obj, "g", jsonnet_json_make_string(vm, "bar")); 80 | jsonnet_json_array_append(vm, arr2, little_obj); 81 | jsonnet_json_array_append(vm, arr_top, arr2); 82 | jsonnet_json_object_append(vm, obj_top, "field", arr_top); 83 | *succ = 1; 84 | return obj_top; 85 | } 86 | 87 | int main(int argc, const char **argv) 88 | { 89 | int error; 90 | char *output; 91 | struct JsonnetVm *vm; 92 | const char *params0[] = {NULL}; 93 | const char *params1[] = {"a", NULL}; 94 | const char *params2[] = {"a", "b", NULL}; 95 | if (argc != 2) { 96 | fprintf(stderr, "libjsonnet_test_snippet \n"); 97 | return EXIT_FAILURE; 98 | } 99 | vm = jsonnet_make(); 100 | jsonnet_native_callback(vm, "concat", native_concat, vm, params2); 101 | jsonnet_native_callback(vm, "square", native_square, vm, params1); 102 | jsonnet_native_callback(vm, "build", native_build, vm, params0); 103 | output = jsonnet_evaluate_snippet(vm, "snippet", argv[1], &error); 104 | if (error) { 105 | fprintf(stderr, "%s", output); 106 | jsonnet_realloc(vm, output, 0); 107 | jsonnet_destroy(vm); 108 | return EXIT_FAILURE; 109 | } 110 | printf("%s", output); 111 | jsonnet_realloc(vm, output, 0); 112 | jsonnet_destroy(vm); 113 | return EXIT_SUCCESS; 114 | } 115 | -------------------------------------------------------------------------------- /libjsonnet/cpp/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 "libjsonnet++.h" 18 | 19 | namespace jsonnet { 20 | Jsonnet::Jsonnet() {} 21 | 22 | Jsonnet::~Jsonnet() 23 | { 24 | if (vm_ != nullptr) { 25 | ::jsonnet_destroy(vm_); 26 | } 27 | } 28 | 29 | /* static */ 30 | std::string Jsonnet::version() 31 | { 32 | return ::jsonnet_version(); 33 | } 34 | 35 | bool Jsonnet::init() 36 | { 37 | vm_ = static_cast(::jsonnet_make()); 38 | return vm_ != nullptr; 39 | } 40 | 41 | void Jsonnet::setMaxStack(uint32_t depth) 42 | { 43 | ::jsonnet_max_stack(vm_, static_cast(depth)); 44 | } 45 | 46 | void Jsonnet::setGcMinObjects(uint32_t objects) 47 | { 48 | ::jsonnet_gc_min_objects(vm_, static_cast(objects)); 49 | } 50 | 51 | void Jsonnet::setGcGrowthTrigger(double growth) 52 | { 53 | ::jsonnet_gc_growth_trigger(vm_, growth); 54 | } 55 | 56 | void Jsonnet::setStringOutput(bool string_output) 57 | { 58 | ::jsonnet_string_output(vm_, string_output); 59 | } 60 | 61 | void Jsonnet::addImportPath(const std::string& path) 62 | { 63 | ::jsonnet_jpath_add(vm_, path.c_str()); 64 | } 65 | 66 | void Jsonnet::setMaxTrace(uint32_t lines) 67 | { 68 | ::jsonnet_max_trace(vm_, static_cast(lines)); 69 | } 70 | 71 | void Jsonnet::bindExtVar(const std::string& key, const std::string& value) 72 | { 73 | ::jsonnet_ext_var(vm_, key.c_str(), value.c_str()); 74 | } 75 | 76 | void Jsonnet::bindExtCodeVar(const std::string& key, const std::string& value) 77 | { 78 | ::jsonnet_ext_code(vm_, key.c_str(), value.c_str()); 79 | } 80 | 81 | bool Jsonnet::evaluateFile(const std::string& filename, std::string* output) 82 | { 83 | if (output == nullptr) { 84 | return false; 85 | } 86 | int error = 0; 87 | const char* jsonnet_output = ::jsonnet_evaluate_file(vm_, filename.c_str(), &error); 88 | if (error != 0) { 89 | last_error_.assign(jsonnet_output); 90 | return false; 91 | } 92 | output->assign(jsonnet_output); 93 | return true; 94 | } 95 | 96 | bool Jsonnet::evaluateSnippet(const std::string& filename, const std::string& snippet, 97 | std::string* output) 98 | { 99 | if (output == nullptr) { 100 | return false; 101 | } 102 | int error = 0; 103 | const char* jsonnet_output = 104 | ::jsonnet_evaluate_snippet(vm_, filename.c_str(), snippet.c_str(), &error); 105 | if (error != 0) { 106 | last_error_.assign(jsonnet_output); 107 | return false; 108 | } 109 | output->assign(jsonnet_output); 110 | return true; 111 | } 112 | 113 | namespace { 114 | void parseMultiOutput(const char* jsonnet_output, std::map* outputs) 115 | { 116 | for (const char* c = jsonnet_output; *c != '\0';) { 117 | const char* filename = c; 118 | const char* c2 = c; 119 | while (*c2 != '\0') 120 | ++c2; 121 | ++c2; 122 | const char* json = c2; 123 | while (*c2 != '\0') 124 | ++c2; 125 | ++c2; 126 | c = c2; 127 | outputs->insert(std::make_pair(filename, json)); 128 | } 129 | } 130 | } // namespace 131 | 132 | bool Jsonnet::evaluateFileMulti(const std::string& filename, 133 | std::map* outputs) 134 | { 135 | if (outputs == nullptr) { 136 | return false; 137 | } 138 | int error = 0; 139 | const char* jsonnet_output = ::jsonnet_evaluate_file_multi(vm_, filename.c_str(), &error); 140 | if (error != 0) { 141 | last_error_.assign(jsonnet_output); 142 | return false; 143 | } 144 | parseMultiOutput(jsonnet_output, outputs); 145 | return true; 146 | } 147 | 148 | bool Jsonnet::evaluateSnippetMulti(const std::string& filename, const std::string& snippet, 149 | std::map* outputs) 150 | { 151 | if (outputs == nullptr) { 152 | return false; 153 | } 154 | int error = 0; 155 | const char* jsonnet_output = 156 | ::jsonnet_evaluate_snippet_multi(vm_, filename.c_str(), snippet.c_str(), &error); 157 | if (error != 0) { 158 | last_error_.assign(jsonnet_output); 159 | return false; 160 | } 161 | parseMultiOutput(jsonnet_output, outputs); 162 | return true; 163 | } 164 | 165 | std::string Jsonnet::lastError() const 166 | { 167 | return last_error_; 168 | } 169 | 170 | } // namespace jsonnet 171 | -------------------------------------------------------------------------------- /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; to use Clang, build with `make CC=clang CXX=clang++` 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 | OPT ?= -O3 31 | 32 | CXXFLAGS ?= -g $(OPT) -Wall -Wextra -Woverloaded-virtual -pedantic -std=c++0x -fPIC -Iinclude -Ithird_party/md5 33 | CFLAGS ?= -g $(OPT) -Wall -Wextra -pedantic -std=c99 -fPIC -Iinclude 34 | MAKEDEPENDFLAGS ?= -Iinclude -Ithird_party/md5 35 | EMCXXFLAGS = $(CXXFLAGS) -Os --memory-init-file 0 -s DISABLE_EXCEPTION_CATCHING=0 -s OUTLINING_LIMIT=10000 36 | EMCFLAGS = $(CFLAGS) --memory-init-file 0 -s DISABLE_EXCEPTION_CATCHING=0 37 | LDFLAGS ?= 38 | 39 | SHARED_LDFLAGS ?= -shared 40 | 41 | ################################################################################ 42 | # End of user-servicable parts 43 | ################################################################################ 44 | 45 | LIB_SRC = \ 46 | core/desugarer.cpp \ 47 | core/formatter.cpp \ 48 | core/lexer.cpp \ 49 | core/libjsonnet.cpp \ 50 | core/parser.cpp \ 51 | core/pass.cpp \ 52 | core/static_analysis.cpp \ 53 | core/string_utils.cpp \ 54 | core/vm.cpp \ 55 | third_party/md5/md5.cpp 56 | 57 | LIB_OBJ = $(LIB_SRC:.cpp=.o) 58 | 59 | LIB_CPP_SRC = \ 60 | cpp/libjsonnet++.cpp 61 | 62 | LIB_CPP_OBJ = $(LIB_OBJ) $(LIB_CPP_SRC:.cpp=.o) 63 | 64 | ALL = \ 65 | jsonnet \ 66 | libjsonnet.so \ 67 | libjsonnet++.so \ 68 | libjsonnet_test_snippet \ 69 | libjsonnet_test_file \ 70 | libjsonnet.js \ 71 | doc/js/libjsonnet.js \ 72 | $(LIB_OBJ) 73 | 74 | ALL_HEADERS = \ 75 | core/ast.h \ 76 | core/desugarer.h \ 77 | core/formatter.h \ 78 | core/lexer.h \ 79 | core/parser.h \ 80 | core/state.h \ 81 | core/static_analysis.h \ 82 | core/static_error.h \ 83 | core/string_utils.h \ 84 | core/vm.h \ 85 | core/std.jsonnet.h \ 86 | include/libjsonnet.h \ 87 | include/libjsonnet++.h \ 88 | third_party/md5/md5.h 89 | 90 | LIB_JSONNET_PATH = /usr/local/lib 91 | 92 | default: jsonnet 93 | 94 | all: $(ALL) 95 | 96 | test: jsonnet libjsonnet.so libjsonnet_test_snippet libjsonnet_test_file 97 | ./tests.sh 98 | 99 | reformat: 100 | clang-format -i -style=file **/*.cpp **/*.h 101 | 102 | test-formatting: 103 | test "`clang-format -style=file -output-replacements-xml **/*.cpp **/*.h | grep -c "> Makefile.depend ; done 113 | 114 | core/desugarer.cpp: core/std.jsonnet.h 115 | 116 | # Object files 117 | %.o: %.cpp 118 | $(CXX) -c $(CXXFLAGS) $< -o $@ 119 | 120 | # Commandline executable. 121 | jsonnet: cmd/jsonnet.cpp $(LIB_OBJ) 122 | $(CXX) $(CXXFLAGS) $(LDFLAGS) $< $(LIB_SRC:.cpp=.o) -o $@ 123 | 124 | # C binding. 125 | libjsonnet.so: $(LIB_OBJ) 126 | $(CXX) $(LDFLAGS) $(LIB_OBJ) $(SHARED_LDFLAGS) -o $@ 127 | mkdir -p $(LIB_JSONNET_PATH) 128 | cp -rf include/libjsonnet.h $(LIB_JSONNET_PATH) 129 | cp -rf libjsonnet.so $(LIB_JSONNET_PATH) 130 | 131 | 132 | libjsonnet++.so: $(LIB_CPP_OBJ) 133 | $(CXX) $(LDFLAGS) $(LIB_CPP_OBJ) $(SHARED_LDFLAGS) -o $@ 134 | 135 | # JavaScript build of C binding 136 | JS_EXPORTED_FUNCTIONS = 'EXPORTED_FUNCTIONS=["_jsonnet_make", "_jsonnet_evaluate_snippet", "_jsonnet_realloc", "_jsonnet_destroy"]' 137 | 138 | libjsonnet.js: $(LIB_SRC) $(ALL_HEADERS) 139 | $(EMCXX) -s $(JS_EXPORTED_FUNCTIONS) $(EMCXXFLAGS) $(LDFLAGS) $(LIB_SRC) -o $@ 140 | 141 | # Copy javascript build to doc directory 142 | doc/js/libjsonnet.js: libjsonnet.js 143 | $(CP) $^ $@ 144 | 145 | # Tests for C binding. 146 | LIBJSONNET_TEST_SNIPPET_SRCS = \ 147 | core/libjsonnet_test_snippet.c \ 148 | libjsonnet.so \ 149 | include/libjsonnet.h 150 | 151 | libjsonnet_test_snippet: $(LIBJSONNET_TEST_SNIPPET_SRCS) 152 | $(CC) $(CFLAGS) $(LDFLAGS) $< -L. -ljsonnet -o $@ 153 | 154 | LIBJSONNET_TEST_FILE_SRCS = \ 155 | core/libjsonnet_test_file.c \ 156 | libjsonnet.so \ 157 | include/libjsonnet.h 158 | 159 | libjsonnet_test_file: $(LIBJSONNET_TEST_FILE_SRCS) 160 | $(CC) $(CFLAGS) $(LDFLAGS) $< -L. -ljsonnet -o $@ 161 | 162 | # Encode standard library for embedding in C 163 | core/%.jsonnet.h: stdlib/%.jsonnet 164 | (($(OD) -v -Anone -t u1 $< \ 165 | | tr " " "\n" \ 166 | | grep -v "^$$" \ 167 | | tr "\n" "," ) && echo "0") > $@ 168 | echo >> $@ 169 | 170 | clean: 171 | rm -vf */*~ *~ .*~ */.*.swp .*.swp $(ALL) *.o core/*.jsonnet.h Makefile.depend 172 | 173 | -include Makefile.depend 174 | 175 | .PHONY: default all depend clean reformat test test-formatting 176 | -------------------------------------------------------------------------------- /libjsonnet/core/string_utils.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_error.h" 20 | #include "string_utils.h" 21 | 22 | UString jsonnet_string_unparse(const UString &str, bool single) 23 | { 24 | UStringStream ss; 25 | ss << (single ? U'\'' : U'\"'); 26 | ss << jsonnet_string_escape(str, single); 27 | ss << (single ? U'\'' : U'\"'); 28 | return ss.str(); 29 | } 30 | 31 | UString jsonnet_string_escape(const UString &str, bool single) 32 | { 33 | UStringStream ss; 34 | for (std::size_t i = 0; i < str.length(); ++i) { 35 | char32_t c = str[i]; 36 | switch (c) { 37 | case U'\"': ss << (single ? U"\"" : U"\\\""); break; 38 | case U'\'': ss << (single ? U"\\\'" : U"\'"); break; 39 | case U'\\': ss << U"\\\\"; break; 40 | case U'\b': ss << U"\\b"; break; 41 | case U'\f': ss << U"\\f"; break; 42 | case U'\n': ss << U"\\n"; break; 43 | case U'\r': ss << U"\\r"; break; 44 | case U'\t': ss << U"\\t"; break; 45 | case U'\0': ss << U"\\u0000"; break; 46 | default: { 47 | if (c < 0x20 || (c >= 0x7f && c <= 0x9f)) { 48 | // Unprintable, use \u 49 | std::stringstream ss8; 50 | ss8 << "\\u" << std::hex << std::setfill('0') << std::setw(4) 51 | << (unsigned long)(c); 52 | ss << decode_utf8(ss8.str()); 53 | } else { 54 | // Printable, write verbatim 55 | ss << c; 56 | } 57 | } 58 | } 59 | } 60 | return ss.str(); 61 | } 62 | 63 | UString jsonnet_string_unescape(const LocationRange &loc, const UString &s) 64 | { 65 | UString r; 66 | const char32_t *s_ptr = s.c_str(); 67 | for (const char32_t *c = s_ptr; *c != U'\0'; ++c) { 68 | switch (*c) { 69 | case '\\': 70 | switch (*(++c)) { 71 | case '"': 72 | case '\'': r += *c; break; 73 | 74 | case '\\': r += *c; break; 75 | 76 | case '/': r += *c; break; 77 | 78 | case 'b': r += '\b'; break; 79 | 80 | case 'f': r += '\f'; break; 81 | 82 | case 'n': r += '\n'; break; 83 | 84 | case 'r': r += '\r'; break; 85 | 86 | case 't': r += '\t'; break; 87 | 88 | case 'u': { 89 | ++c; // Consume the 'u'. 90 | unsigned long codepoint = 0; 91 | // Expect 4 hex digits. 92 | for (unsigned i = 0; i < 4; ++i) { 93 | auto x = (unsigned char)(c[i]); 94 | unsigned digit; 95 | if (x == '\0') { 96 | auto msg = "Truncated unicode escape sequence in string literal."; 97 | throw StaticError(loc, msg); 98 | } else if (x >= '0' && x <= '9') { 99 | digit = x - '0'; 100 | } else if (x >= 'a' && x <= 'f') { 101 | digit = x - 'a' + 10; 102 | } else if (x >= 'A' && x <= 'F') { 103 | digit = x - 'A' + 10; 104 | } else { 105 | std::stringstream ss; 106 | ss << "Malformed unicode escape character, " 107 | << "should be hex: '" << x << "'"; 108 | throw StaticError(loc, ss.str()); 109 | } 110 | codepoint *= 16; 111 | codepoint += digit; 112 | } 113 | 114 | r += codepoint; 115 | 116 | // Leave us on the last char, ready for the ++c at 117 | // the outer for loop. 118 | c += 3; 119 | } break; 120 | 121 | case '\0': { 122 | auto msg = "Truncated escape sequence in string literal."; 123 | throw StaticError(loc, msg); 124 | } 125 | 126 | default: { 127 | std::stringstream ss; 128 | std::string utf8; 129 | encode_utf8(*c, utf8); 130 | ss << "Unknown escape sequence in string literal: '" << utf8 << "'"; 131 | throw StaticError(loc, ss.str()); 132 | } 133 | } 134 | break; 135 | 136 | default: 137 | // Just a regular letter. 138 | r += *c; 139 | } 140 | } 141 | return r; 142 | } 143 | -------------------------------------------------------------------------------- /libjsonnet/include/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 CPP_JSONNET_H_ 18 | #define CPP_JSONNET_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | extern "C" { 26 | #include "libjsonnet.h" 27 | } 28 | 29 | namespace jsonnet { 30 | class Jsonnet { 31 | public: 32 | Jsonnet(); 33 | ~Jsonnet(); 34 | 35 | /// Return the version string of the Jsonnet interpreter. Conforms to 36 | /// semantic versioning http://semver.org/. If this does not match 37 | /// LIB_JSONNET_VERSION then there is a mismatch between header and compiled 38 | /// library. 39 | static std::string version(); 40 | 41 | /// Initializes the Jsonnet VM. This method must be called before calling any 42 | /// of the other methods. 43 | /// 44 | /// @return true if the Jsonnet VM was successfully initialized, false 45 | /// otherwise. 46 | bool init(); 47 | 48 | /// Sets the maximum stack depth. 49 | void setMaxStack(uint32_t depth); 50 | 51 | /// Sets the number of objects required before a carbage collection cycle is 52 | /// allowed. 53 | void setGcMinObjects(uint32_t objects); 54 | 55 | /// Run the garbage collector after this amount of growth in the number of 56 | /// objects. 57 | void setGcGrowthTrigger(double growth); 58 | 59 | /// Set whether to expect a string as output and don't JSON encode it. 60 | void setStringOutput(bool string_output); 61 | 62 | /// Set the number of lines of stack trace to display (0 to display all). 63 | void setMaxTrace(uint32_t lines); 64 | 65 | /// Add to the default import callback's library search path. 66 | void addImportPath(const std::string& path); 67 | 68 | /// Bind a Jsonnet external variable to the given value. 69 | /// 70 | /// Argument values are copied so memory should be managed by caller. 71 | void bindExtVar(const std::string& key, const std::string& value); 72 | 73 | /// Bind a Jsonnet external code variable to the given value. 74 | /// 75 | /// Argument values are copied so memory should be managed by caller. 76 | void bindExtCodeVar(const std::string& key, const std::string& value); 77 | 78 | /// Evaluate a file containing Jsonnet code to return a JSON string. 79 | /// 80 | /// This method returns true if the Jsonnet code is successfully evaluated. 81 | /// Otherwise, it returns false, and the error output can be returned by 82 | /// calling LastError(); 83 | /// 84 | /// @param filename Path to a file containing Jsonnet code. 85 | /// @param output Pointer to string to contain the output. 86 | /// @return true if the Jsonnet code was successfully evaluated, false 87 | /// otherwise. 88 | bool evaluateFile(const std::string& filename, std::string* output); 89 | 90 | /// Evaluate a string containing Jsonnet code to return a JSON string. 91 | /// 92 | /// This method returns true if the Jsonnet code is successfully evaluated. 93 | /// Otherwise, it returns false, and the error output can be returned by 94 | /// calling LastError(); 95 | /// 96 | /// @param filename Path to a file (used in error message). 97 | /// @param snippet Jsonnet code to execute. 98 | /// @param output Pointer to string to contain the output. 99 | /// @return true if the Jsonnet code was successfully evaluated, false 100 | /// otherwise. 101 | bool evaluateSnippet(const std::string& filename, const std::string& snippet, 102 | std::string* output); 103 | 104 | /// Evaluate a file containing Jsonnet code, return a number of JSON files. 105 | /// 106 | /// This method returns true if the Jsonnet code is successfully evaluated. 107 | /// Otherwise, it returns false, and the error output can be returned by 108 | /// calling LastError(); 109 | /// 110 | /// @param filename Path to a file containing Jsonnet code. 111 | /// @param outputs Pointer to map which will store the output map of filename 112 | /// to JSON string. 113 | bool evaluateFileMulti(const std::string& filename, 114 | std::map* outputs); 115 | 116 | /// Evaluate a string containing Jsonnet code, return a number of JSON files. 117 | /// 118 | /// This method returns true if the Jsonnet code is successfully evaluated. 119 | /// Otherwise, it returns false, and the error output can be returned by 120 | /// calling LastError(); 121 | /// 122 | /// @param filename Path to a file containing Jsonnet code. 123 | /// @param snippet Jsonnet code to execute. 124 | /// @param outputs Pointer to map which will store the output map of filename 125 | /// to JSON string. 126 | /// @return true if the Jsonnet code was successfully evaluated, false 127 | /// otherwise. 128 | bool evaluateSnippetMulti(const std::string& filename, const std::string& snippet, 129 | std::map* outputs); 130 | 131 | /// Returns the last error raised by Jsonnet. 132 | std::string lastError() const; 133 | 134 | private: 135 | struct JsonnetVm* vm_; 136 | std::string last_error_; 137 | }; 138 | 139 | } // namespace jsonnet 140 | 141 | #endif // CPP_JSONNET_H_ 142 | -------------------------------------------------------------------------------- /libjsonnet/core/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 21 | 22 | #include "ast.h" 23 | 24 | /** A single line of a stack trace from a runtime error. 25 | */ 26 | struct TraceFrame { 27 | LocationRange location; 28 | std::string name; 29 | TraceFrame(const LocationRange &location, const std::string &name = "") 30 | : location(location), name(name) 31 | { 32 | } 33 | }; 34 | 35 | /** Exception that is thrown by the interpreter when it reaches an error construct, or divide by 36 | * zero, array bounds error, dynamic type error, etc. 37 | */ 38 | struct RuntimeError { 39 | std::vector stackTrace; 40 | std::string msg; 41 | RuntimeError(const std::vector stack_trace, const std::string &msg) 42 | : stackTrace(stack_trace), msg(msg) 43 | { 44 | } 45 | }; 46 | 47 | /** Holds native callback and context. */ 48 | struct VmNativeCallback { 49 | JsonnetNativeCallback *cb; 50 | void *ctx; 51 | std::vector params; 52 | }; 53 | 54 | typedef std::map VmNativeCallbackMap; 55 | 56 | /** Stores external values / code. */ 57 | struct VmExt { 58 | std::string data; 59 | bool isCode; 60 | VmExt() : isCode(false) {} 61 | VmExt(const std::string &data, bool is_code) : data(data), isCode(is_code) {} 62 | }; 63 | 64 | /** Execute the program and return the value as a JSON string. 65 | * 66 | * \param alloc The allocator used to create the ast. 67 | * \param ast The program to execute. 68 | * \param ext The external vars / code. 69 | * \param max_stack Recursion beyond this level gives an error. 70 | * \param gc_min_objects The garbage collector does not run when the heap is this small. 71 | * \param gc_growth_trigger Growth since last garbage collection cycle to trigger a new cycle. 72 | * \param import_callback A callback to handle imports 73 | * \param import_callback_ctx Context param for the import callback. 74 | * \param output_string Whether to expect a string and output it without JSON encoding 75 | * \throws RuntimeError reports runtime errors in the program. 76 | * \returns The JSON result in string form. 77 | */ 78 | std::string jsonnet_vm_execute(Allocator *alloc, const AST *ast, 79 | const std::map &ext, unsigned max_stack, 80 | double gc_min_objects, double gc_growth_trigger, 81 | const VmNativeCallbackMap &natives, 82 | JsonnetImportCallback *import_callback, void *import_callback_ctx, 83 | bool string_output); 84 | 85 | /** Execute the program and return the value as a number of named JSON files. 86 | * 87 | * This assumes the given program yields an object whose keys are filenames. 88 | * 89 | * \param alloc The allocator used to create the ast. 90 | * \param ast The program to execute. 91 | * \param ext The external vars / code. 92 | * \param tla The top-level arguments (strings or code). 93 | * \param max_stack Recursion beyond this level gives an error. 94 | * \param gc_min_objects The garbage collector does not run when the heap is this small. 95 | * \param gc_growth_trigger Growth since last garbage collection cycle to trigger a new cycle. 96 | * \param import_callback A callback to handle imports 97 | * \param import_callback_ctx Context param for the import callback. 98 | * \param output_string Whether to expect a string and output it without JSON encoding 99 | * \throws RuntimeError reports runtime errors in the program. 100 | * \returns A mapping from filename to the JSON strings for that file. 101 | */ 102 | std::map jsonnet_vm_execute_multi( 103 | Allocator *alloc, const AST *ast, const std::map &ext, unsigned max_stack, 104 | double gc_min_objects, double gc_growth_trigger, const VmNativeCallbackMap &natives, 105 | JsonnetImportCallback *import_callback, void *import_callback_ctx, bool string_output); 106 | 107 | /** Execute the program and return the value as a stream of JSON files. 108 | * 109 | * This assumes the given program yields an array whose elements are individual 110 | * JSON files. 111 | * 112 | * \param alloc The allocator used to create the ast. 113 | * \param ast The program to execute. 114 | * \param ext The external vars / code. 115 | * \param tla The top-level arguments (strings or code). 116 | * \param max_stack Recursion beyond this level gives an error. 117 | * \param gc_min_objects The garbage collector does not run when the heap is this small. 118 | * \param gc_growth_trigger Growth since last garbage collection cycle to trigger a new cycle. 119 | * \param import_callback A callback to handle imports 120 | * \param import_callback_ctx Context param for the import callback. 121 | * \param output_string Whether to expect a string and output it without JSON encoding 122 | * \throws RuntimeError reports runtime errors in the program. 123 | * \returns A mapping from filename to the JSON strings for that file. 124 | */ 125 | std::vector jsonnet_vm_execute_stream( 126 | Allocator *alloc, const AST *ast, const std::map &ext, unsigned max_stack, 127 | double gc_min_objects, double gc_growth_trigger, const VmNativeCallbackMap &natives, 128 | JsonnetImportCallback *import_callback, void *import_callback_ctx); 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /libjsonnet/core/unicode.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_UNICODE_H 18 | #define JSONNET_UNICODE_H 19 | 20 | /** Substituted when a unicode translation format encoding error is encountered. */ 21 | #define JSONNET_CODEPOINT_ERROR 0xfffd 22 | #define JSONNET_CODEPOINT_MAX 0x110000 23 | 24 | /** Convert a unicode codepoint to UTF8. 25 | * 26 | * \param x The unicode codepoint. 27 | * \param s The UTF-8 string to append to. 28 | * \returns The number of characters appended. 29 | */ 30 | static inline int encode_utf8(char32_t x, std::string &s) 31 | { 32 | if (x >= JSONNET_CODEPOINT_MAX) 33 | x = JSONNET_CODEPOINT_ERROR; 34 | 35 | // 00ZZZzzz 00zzYYYY 00Yyyyxx 00xxxxxx 36 | long bytes = ((x & 0x1C0000) << 6) | ((x & 0x03F000) << 4) | ((x & 0x0FC0) << 2) | (x & 0x3F); 37 | 38 | if (x < 0x80) { 39 | s.push_back((char)x); 40 | return 1; 41 | } else if (x < 0x800) { // note that capital 'Y' bits must be 0 42 | bytes |= 0xC080; 43 | s.push_back((bytes >> 8) & 0xFF); 44 | s.push_back((bytes >> 0) & 0xFF); 45 | return 2; 46 | } else if (x < 0x10000) { // note that 'z' bits must be 0 47 | bytes |= 0xE08080; 48 | s.push_back((bytes >> 16) & 0xFF); 49 | s.push_back((bytes >> 8) & 0xFF); 50 | s.push_back((bytes >> 0) & 0xFF); 51 | return 3; 52 | } else if (x < 0x110000) { // note that capital 'Z' bits must be 0 53 | bytes |= 0xF0808080; 54 | s.push_back((bytes >> 24) & 0xFF); 55 | s.push_back((bytes >> 16) & 0xFF); 56 | s.push_back((bytes >> 8) & 0xFF); 57 | s.push_back((bytes >> 0) & 0xFF); 58 | return 4; 59 | } else { 60 | std::cerr << "Should never get here." << std::endl; 61 | abort(); 62 | } 63 | } 64 | 65 | /** Convert the UTF8 byte sequence in the given string to a unicode code point. 66 | * 67 | * \param str The string. 68 | * \param i The index of the string from which to start decoding and returns the index of the last 69 | * byte of the encoded codepoint. 70 | * \returns The decoded unicode codepoint. 71 | */ 72 | static inline char32_t decode_utf8(const std::string &str, size_t &i) 73 | { 74 | char c0 = str[i]; 75 | if ((c0 & 0x80) == 0) { // 0xxxxxxx 76 | return c0; 77 | } else if ((c0 & 0xE0) == 0xC0) { // 110yyyxx 10xxxxxx 78 | if (i + 1 >= str.length()) { 79 | return JSONNET_CODEPOINT_ERROR; 80 | } 81 | char c1 = str[++i]; 82 | if ((c1 & 0xC0) != 0x80) { 83 | return JSONNET_CODEPOINT_ERROR; 84 | } 85 | return ((c0 & 0x1F) << 6ul) | (c1 & 0x3F); 86 | } else if ((c0 & 0xF0) == 0xE0) { // 1110yyyy 10yyyyxx 10xxxxxx 87 | if (i + 2 >= str.length()) { 88 | return JSONNET_CODEPOINT_ERROR; 89 | } 90 | char c1 = str[++i]; 91 | if ((c1 & 0xC0) != 0x80) { 92 | return JSONNET_CODEPOINT_ERROR; 93 | } 94 | char c2 = str[++i]; 95 | if ((c2 & 0xC0) != 0x80) { 96 | return JSONNET_CODEPOINT_ERROR; 97 | } 98 | return ((c0 & 0xF) << 12ul) | ((c1 & 0x3F) << 6) | (c2 & 0x3F); 99 | } else if ((c0 & 0xF8) == 0xF0) { // 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx 100 | if (i + 3 >= str.length()) { 101 | return JSONNET_CODEPOINT_ERROR; 102 | } 103 | char c1 = str[++i]; 104 | if ((c1 & 0xC0) != 0x80) { 105 | return JSONNET_CODEPOINT_ERROR; 106 | } 107 | char c2 = str[++i]; 108 | if ((c2 & 0xC0) != 0x80) { 109 | return JSONNET_CODEPOINT_ERROR; 110 | } 111 | char c3 = str[++i]; 112 | if ((c3 & 0xC0) != 0x80) { 113 | return JSONNET_CODEPOINT_ERROR; 114 | } 115 | return ((c0 & 0x7) << 24ul) | ((c1 & 0x3F) << 12ul) | ((c2 & 0x3F) << 6) | (c3 & 0x3F); 116 | } else { 117 | return JSONNET_CODEPOINT_ERROR; 118 | } 119 | } 120 | 121 | /** A string class capable of holding unicode codepoints. */ 122 | typedef std::basic_string UString; 123 | 124 | static inline void encode_utf8(const UString &s, std::string &r) 125 | { 126 | for (char32_t cp : s) 127 | encode_utf8(cp, r); 128 | } 129 | 130 | static inline std::string encode_utf8(const UString &s) 131 | { 132 | std::string r; 133 | encode_utf8(s, r); 134 | return r; 135 | } 136 | 137 | static inline UString decode_utf8(const std::string &s) 138 | { 139 | UString r; 140 | for (size_t i = 0; i < s.length(); ++i) 141 | r.push_back(decode_utf8(s, i)); 142 | return r; 143 | } 144 | 145 | /** A stringstream-like class capable of holding unicode codepoints. 146 | * The C++ standard does not support std::basic_stringstream 168 | UStringStream &operator<<(T c) 169 | { 170 | std::stringstream ss; 171 | ss << c; 172 | for (char c : ss.str()) 173 | buf.push_back(char32_t(c)); 174 | return *this; 175 | } 176 | UString str() 177 | { 178 | return buf; 179 | } 180 | }; 181 | 182 | #endif // JSONNET_UNICODE_H 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jsonnet 2 | The Google Jsonnet for PHP 3 | 4 | ### Google Jsonnet Tutorial 5 | Jsonnet language, from its most basic features to its powerful object model, punctuated with examples drawn from the world of cocktails. These examples are meant to be fun, and although a little contrived, do not restrict our thinking to any one particular application of Jsonnet. 6 | 7 | Caveat: Note that Jsonnet unparses JSON in a simple way. In particular, it alphabetically reorders object fields in its output. This is natural and compatible with JSON, since if order is meaningful, an array of pairs should be used instead of an object. Also, unparsing JSON using a canonical ordering of field names makes it possible to use diff to compare outputs. However, the example output on this page has been manually re-ordered in order to allow easier visual comparison to the given input. The whitespace of the output has also been tweaked to make it fit more neatly on the page. So, if you run these examples yourself, the output might be different (but equivalent). 8 | 9 | For more information on Jsonnet, please see: 10 | 11 | * [Jsonnet home page](https://jsonnet.org) 12 | * [Jsonnet git repo](https://github.com/google/jsonnet) 13 | 14 | ### Install Jsonnet for PHP 15 | 16 | The pecl package is: http://pecl.php.net/package/jsonnet 17 | 18 | To install Jsonnet, run: 19 | 20 | ``` 21 | pecl install jsonnet 22 | ``` 23 | 24 | or: 25 | 26 | ``` 27 | wget -O Jsonnet.Latest.tar.gz https://pecl.php.net/get/jsonnet 28 | mkdir Jsonnet-PHP && tar -xzvf Jsonnet.Latest.tar.gz -C ./Jsonnet-PHP 29 | cd Jsonnet-PHP/Jsonnet-1.0.0 30 | 31 | phpize 32 | ./configure 33 | make && make install 34 | ``` 35 | 36 | 37 | ### Simple Syntax Improvements 38 | 39 | #### Input (Jsonnet) 40 | 41 | ```jsonnet 42 | { 43 | cocktails: { 44 | // Ingredient quantities are in fluid ounces. 45 | "Tom Collins": { 46 | ingredients: [ 47 | { kind: "Farmers Gin", qty: 1.5 }, 48 | { kind: "Lemon", qty: 1 }, 49 | { kind: "Simple Syrup", qty: 0.5 }, 50 | { kind: "Soda", qty: 2 }, 51 | { kind: "Angostura", qty: "dash" }, 52 | ], 53 | garnish: "Maraschino Cherry", 54 | served: "Tall", 55 | }, 56 | Manhattan: { 57 | ingredients: [ 58 | { kind: "Rye", qty: 2.5 }, 59 | { kind: "Sweet Red Vermouth", qty: 1 }, 60 | { kind: "Angostura", qty: "dash" }, 61 | ], 62 | garnish: "Maraschino Cherry", 63 | served: "Straight Up", 64 | }, 65 | } 66 | } 67 | ``` 68 | 69 | #### Output (JSON) 70 | 71 | ```json 72 | { 73 | "cocktails": { 74 | 75 | "Tom Collins": { 76 | "ingredients": [ 77 | { "kind": "Farmers Gin", "qty": 1.5 }, 78 | { "kind": "Lemon", "qty": 1 }, 79 | { "kind": "Simple Syrup", "qty": 0.5 }, 80 | { "kind": "Soda", "qty": 2 }, 81 | { "kind": "Angostura", "qty": "dash" } 82 | ], 83 | "garnish": "Maraschino Cherry", 84 | "served": "Tall" 85 | }, 86 | "Manhattan": { 87 | "ingredients": [ 88 | { "kind": "Rye", "qty": 2.5 }, 89 | { "kind": "Sweet Red Vermouth", "qty": 1 }, 90 | { "kind": "Angostura", "qty": "dash" } 91 | ], 92 | "garnish": "Maraschino Cherry", 93 | "served": "Straight Up" 94 | } 95 | } 96 | } 97 | ``` 98 | 99 | ### Demo of PHP 100 | 101 | ```php 102 | Jsonnet::evaluateFile('bar_menu.1.jsonnet'); 103 | 104 | $Snippet = ' 105 | { 106 | cocktails: { 107 | // Ingredient quantities are in fluid ounces. 108 | "Tom Collins": { 109 | ingredients: [ 110 | { kind: "Farmers Gin", qty: 1.5 }, 111 | { kind: "Lemon", qty: 1 }, 112 | { kind: "Simple Syrup", qty: 0.5 }, 113 | { kind: "Soda", qty: 2 }, 114 | { kind: "Angostura", qty: "dash" }, 115 | ], 116 | garnish: "Maraschino Cherry", 117 | served: "Tall", 118 | }, 119 | Manhattan: { 120 | ingredients: [ 121 | { kind: "Rye", qty: 2.5 }, 122 | { kind: "Sweet Red Vermouth", qty: 1 }, 123 | { kind: "Angostura", qty: "dash" }, 124 | ], 125 | garnish: "Maraschino Cherry", 126 | served: "Straight Up", 127 | }, 128 | } 129 | } 130 | '; 131 | 132 | var_dump(Jsonnet::evaluateSnippet($Snippet)); 133 | ``` 134 | 135 | ### PHP Re Result 136 | 137 | ``` 138 | /usr/local/php/php-7.0.6-zts-debug/bin/php --re jsonnet 139 | 140 | Extension [ extension #40 Jsonnet version v1.3.1 ] { 141 | 142 | - Constants [2] { 143 | Constant [ string JSONNET_PHP_VERSION ] { v1.3.1 } 144 | Constant [ string JSONNET_PHP_AUTHOR ] { Chitao.Gao [ neeke@php.net ] } 145 | } 146 | 147 | - Functions { 148 | Function [ function jsonnet_get_version ] { 149 | } 150 | Function [ function jsonnet_get_author ] { 151 | } 152 | } 153 | 154 | - Classes [1] { 155 | Class [ class Jsonnet ] { 156 | 157 | - Constants [0] { 158 | } 159 | 160 | - Static properties [0] { 161 | } 162 | 163 | - Static methods [4] { 164 | Method [ static public method evaluateFile ] { 165 | 166 | - Parameters [1] { 167 | Parameter #0 [ $file_path ] 168 | } 169 | } 170 | 171 | Method [ static public method evaluateSnippet ] { 172 | 173 | - Parameters [1] { 174 | Parameter #0 [ $snippet_string ] 175 | } 176 | } 177 | 178 | Method [ static public method fmtFile ] { 179 | 180 | - Parameters [1] { 181 | Parameter #0 [ $file_path ] 182 | } 183 | } 184 | 185 | Method [ static public method fmtSnippet ] { 186 | 187 | - Parameters [1] { 188 | Parameter #0 [ $snippet_string ] 189 | } 190 | } 191 | } 192 | 193 | - Properties [0] { 194 | } 195 | 196 | - Methods [2] { 197 | Method [ public method __construct ] { 198 | } 199 | 200 | Method [ public method __destruct ] { 201 | } 202 | } 203 | } 204 | } 205 | } 206 | ``` 207 | 208 | ### CodeTips 209 | 210 | ```php 211 | 18 | 19 | #include "ast.h" 20 | #include "static_analysis.h" 21 | #include "static_error.h" 22 | 23 | typedef std::set IdSet; 24 | 25 | /** Inserts all of s into r. */ 26 | static void append(IdSet &r, const IdSet &s) 27 | { 28 | r.insert(s.begin(), s.end()); 29 | } 30 | 31 | /** Statically analyse the given ast. 32 | * 33 | * \param ast_ The AST. 34 | * \param in_object Whether or not ast_ is within the lexical scope of an object AST. 35 | * \param vars The variables defined within lexical scope of ast_. 36 | * \returns The free variables in ast_. 37 | */ 38 | static IdSet static_analysis(AST *ast_, bool in_object, const IdSet &vars) 39 | { 40 | IdSet r; 41 | 42 | switch (ast_->type) { 43 | case AST_APPLY: { 44 | assert(dynamic_cast(ast_)); 45 | auto* ast = static_cast(ast_); 46 | append(r, static_analysis(ast->target, in_object, vars)); 47 | for (const auto &arg : ast->args) 48 | append(r, static_analysis(arg.expr, in_object, vars)); 49 | } break; 50 | case AST_APPLY_BRACE: { 51 | assert(dynamic_cast(ast_)); 52 | // Nothing to do. 53 | } break; 54 | case AST_ARRAY: { 55 | assert(dynamic_cast(ast_)); 56 | auto* ast = static_cast(ast_); 57 | for (auto &el : ast->elements) 58 | append(r, static_analysis(el.expr, in_object, vars)); 59 | } break; 60 | case AST_BINARY: { 61 | assert(dynamic_cast(ast_)); 62 | auto* ast = static_cast(ast_); 63 | append(r, static_analysis(ast->left, in_object, vars)); 64 | append(r, static_analysis(ast->right, in_object, vars)); 65 | } break; 66 | case AST_BUILTIN_FUNCTION: { 67 | assert(dynamic_cast(ast_)); 68 | // Nothing to do. 69 | } break; 70 | case AST_CONDITIONAL: { 71 | assert(dynamic_cast(ast_)); 72 | auto* ast = static_cast(ast_); 73 | append(r, static_analysis(ast->cond, in_object, vars)); 74 | append(r, static_analysis(ast->branchTrue, in_object, vars)); 75 | append(r, static_analysis(ast->branchFalse, in_object, vars)); 76 | } break; 77 | case AST_ERROR: { 78 | assert(dynamic_cast(ast_)); 79 | auto* ast = static_cast(ast_); 80 | append(r, static_analysis(ast->expr, in_object, vars)); 81 | } break; 82 | case AST_FUNCTION: { 83 | assert(dynamic_cast(ast_)); 84 | auto* ast = static_cast(ast_); 85 | auto new_vars = vars; 86 | IdSet params; 87 | for (const auto &p : ast->params) { 88 | if (params.find(p.id) != params.end()) { 89 | std::string msg = "Duplicate function parameter: " + encode_utf8(p.id->name); 90 | throw StaticError(ast_->location, msg); 91 | } 92 | params.insert(p.id); 93 | new_vars.insert(p.id); 94 | } 95 | 96 | auto fv = static_analysis(ast->body, in_object, new_vars); 97 | for (const auto &p : ast->params) { 98 | if (p.expr != nullptr) 99 | append(fv, static_analysis(p.expr, in_object, new_vars)); 100 | } 101 | for (const auto &p : ast->params) 102 | fv.erase(p.id); 103 | append(r, fv); 104 | } break; 105 | case AST_IMPORT: { 106 | assert(dynamic_cast(ast_)); 107 | // Nothing to do. 108 | } break; 109 | case AST_IMPORTSTR: { 110 | assert(dynamic_cast(ast_)); 111 | // Nothing to do. 112 | } break; 113 | case AST_IN_SUPER: { 114 | assert(dynamic_cast(ast_)); 115 | auto* ast = static_cast(ast_); 116 | if (!in_object) 117 | throw StaticError(ast_->location, "Can't use super outside of an object."); 118 | append(r, static_analysis(ast->element, in_object, vars)); 119 | } break; 120 | case AST_INDEX: { 121 | assert(dynamic_cast(ast_)); 122 | auto* ast = static_cast(ast_); 123 | append(r, static_analysis(ast->target, in_object, vars)); 124 | append(r, static_analysis(ast->index, in_object, vars)); 125 | } break; 126 | case AST_LOCAL: { 127 | assert(dynamic_cast(ast_)); 128 | auto* ast = static_cast(ast_); 129 | IdSet ast_vars; 130 | for (const auto &bind : ast->binds) { 131 | ast_vars.insert(bind.var); 132 | } 133 | auto new_vars = vars; 134 | append(new_vars, ast_vars); 135 | IdSet fvs; 136 | for (const auto &bind : ast->binds) { 137 | append(fvs, static_analysis(bind.body, in_object, new_vars)); 138 | } 139 | 140 | append(fvs, static_analysis(ast->body, in_object, new_vars)); 141 | 142 | for (const auto &bind : ast->binds) 143 | fvs.erase(bind.var); 144 | 145 | append(r, fvs); 146 | } break; 147 | case AST_LITERAL_BOOLEAN: { 148 | assert(dynamic_cast(ast_)); 149 | // Nothing to do. 150 | } break; 151 | case AST_LITERAL_NUMBER: { 152 | assert(dynamic_cast(ast_)); 153 | // Nothing to do. 154 | } break; 155 | case AST_LITERAL_STRING: { 156 | assert(dynamic_cast(ast_)); 157 | // Nothing to do. 158 | } break; 159 | case AST_LITERAL_NULL: { 160 | assert(dynamic_cast(ast_)); 161 | // Nothing to do. 162 | } break; 163 | case AST_DESUGARED_OBJECT: { 164 | assert(dynamic_cast(ast_)); 165 | auto* ast = static_cast(ast_); 166 | for (auto &field : ast->fields) { 167 | append(r, static_analysis(field.name, in_object, vars)); 168 | append(r, static_analysis(field.body, true, vars)); 169 | } 170 | for (AST *assert : ast->asserts) { 171 | append(r, static_analysis(assert, true, vars)); 172 | } 173 | } break; 174 | case AST_OBJECT_COMPREHENSION_SIMPLE: { 175 | assert(dynamic_cast(ast_)); 176 | auto* ast = static_cast(ast_); 177 | auto new_vars = vars; 178 | new_vars.insert(ast->id); 179 | append(r, static_analysis(ast->field, false, new_vars)); 180 | append(r, static_analysis(ast->value, true, new_vars)); 181 | r.erase(ast->id); 182 | append(r, static_analysis(ast->array, in_object, vars)); 183 | } break; 184 | case AST_SELF: { 185 | assert(dynamic_cast(ast_)); 186 | if (!in_object) 187 | throw StaticError(ast_->location, "Can't use self outside of an object."); 188 | } break; 189 | case AST_SUPER_INDEX: { 190 | assert(dynamic_cast(ast_)); 191 | auto* ast = static_cast(ast_); 192 | if (!in_object) 193 | throw StaticError(ast_->location, "Can't use super outside of an object."); 194 | append(r, static_analysis(ast->index, in_object, vars)); 195 | } break; 196 | case AST_UNARY: { 197 | assert(dynamic_cast(ast_)); 198 | auto* ast = static_cast(ast_); 199 | append(r, static_analysis(ast->expr, in_object, vars)); 200 | } break; 201 | case AST_VAR: { 202 | assert(dynamic_cast(ast_)); 203 | auto* ast = static_cast(ast_); 204 | if (vars.find(ast->id) == vars.end()) { 205 | throw StaticError(ast->location, "Unknown variable: " + encode_utf8(ast->id->name)); 206 | } 207 | r.insert(ast->id); 208 | } break; 209 | default: 210 | std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_ << std::endl; 211 | std::abort(); 212 | break; 213 | } 214 | 215 | for (auto *id : r) 216 | ast_->freeVariables.push_back(id); 217 | 218 | return r; 219 | } 220 | 221 | void jsonnet_static_analysis(AST *ast) 222 | { 223 | static_analysis(ast, false, IdSet{}); 224 | } 225 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | Jsonnet 6 | pecl.php.net 7 | The Google Jsonnet for PHP. 8 | 9 | The Google Jsonnet for PHP. 10 | 11 | Jsonnet language, from its most basic features to its powerful object model, punctuated with examples drawn from 12 | the world of cocktails. These examples are meant to be fun, and although a little contrived, do not restrict our 13 | thinking to any one particular application of Jsonnet. 14 | 15 | Caveat: Note that Jsonnet unparses JSON in a simple way. In particular, it alphabetically reorders object fields 16 | in its output. This is natural and compatible with JSON, since if order is meaningful, an array of pairs should 17 | be used instead of an object. Also, unparsing JSON using a canonical ordering of field names makes it possible 18 | to use diff to compare outputs. However, the example output on this page has been manually re-ordered in order 19 | to allow easier visual comparison to the given input. The whitespace of the output has also been tweaked to make 20 | it fit more neatly on the page. So, if you run these examples yourself, the output might be different (but 21 | equivalent). 22 | 23 | 24 | Chitao Gao 25 | neeke 26 | neeke@php.net 27 | yes 28 | 29 | 2018-05-27 30 | 31 | 32 | 1.3.1 33 | 1.3.1 34 | 35 | 36 | stable 37 | stable 38 | 39 | Apache2.0 40 | 41 | - Merged formatting cleanups for README from google. 42 | - Rename class from JsonNet to Jsonnet. 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 5.2.0 140 | 141 | 142 | 1.4.0 143 | 144 | 145 | 146 | jsonnet 147 | 148 | 149 | 150 | 2015-05-31 151 | 152 | 153 | 1.0.0 154 | 1.0.0 155 | 156 | 157 | stable 158 | stable 159 | 160 | Apache2.0 161 | 162 | - First version. 163 | 164 | 165 | 166 | 2016-07-14 167 | 168 | 169 | 1.1.0 170 | 1.1.0 171 | 172 | 173 | stable 174 | stable 175 | 176 | Apache2.0 177 | 178 | - Update Lib Jsonnet use v0.8.9. 179 | - Fixed issue #1 #2, install error and .so load failed. 180 | 181 | 182 | 183 | 2016-08-07 184 | 185 | 186 | 1.1.1 187 | 1.1.0 188 | 189 | 190 | stable 191 | stable 192 | 193 | Apache2.0 194 | 195 | - Fixed issue #4, pecl install failed. 196 | 197 | 198 | 199 | 2017-11-28 200 | 201 | 202 | 1.2.0 203 | 1.2.0 204 | 205 | 206 | stable 207 | stable 208 | 209 | Apache2.0 210 | 211 | - Update Lib Jsonnet use v0.9.5. 212 | - Add function Jsonnet::fmtFile. 213 | - Add function Jsonnet::fmtSnippet. 214 | 215 | 216 | 217 | 2018-03-29 218 | 219 | 220 | 1.3.0 221 | 1.3.0 222 | 223 | 224 | stable 225 | stable 226 | 227 | Apache2.0 228 | 229 | - Update Lib Jsonnet use v0.10.0. 230 | - Support PHP 7. 231 | 232 | 233 | 234 | -------------------------------------------------------------------------------- /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/third_party/md5/md5.cpp: -------------------------------------------------------------------------------- 1 | /* MD5 2 | converted to C++ class by Frank Thilo (thilo@unix-ag.org) 3 | for bzflag (http://www.bzflag.org) 4 | 5 | based on: 6 | 7 | md5.h and md5.c 8 | reference implemantion of RFC 1321 9 | 10 | Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All 11 | rights reserved. 12 | 13 | License to copy and use this software is granted provided that it 14 | is identified as the "RSA Data Security, Inc. MD5 Message-Digest 15 | Algorithm" in all material mentioning or referencing this software 16 | or this function. 17 | 18 | License is also granted to make and use derivative works provided 19 | that such works are identified as "derived from the RSA Data 20 | Security, Inc. MD5 Message-Digest Algorithm" in all material 21 | mentioning or referencing the derived work. 22 | 23 | RSA Data Security, Inc. makes no representations concerning either 24 | the merchantability of this software or the suitability of this 25 | software for any particular purpose. It is provided "as is" 26 | without express or implied warranty of any kind. 27 | 28 | These notices must be retained in any copies of any part of this 29 | documentation and/or software. 30 | 31 | */ 32 | 33 | /* interface header */ 34 | #include "md5.h" 35 | 36 | /* system implementation headers */ 37 | #include 38 | #include 39 | 40 | 41 | // Constants for MD5Transform routine. 42 | #define S11 7 43 | #define S12 12 44 | #define S13 17 45 | #define S14 22 46 | #define S21 5 47 | #define S22 9 48 | #define S23 14 49 | #define S24 20 50 | #define S31 4 51 | #define S32 11 52 | #define S33 16 53 | #define S34 23 54 | #define S41 6 55 | #define S42 10 56 | #define S43 15 57 | #define S44 21 58 | 59 | /////////////////////////////////////////////// 60 | 61 | // F, G, H and I are basic MD5 functions. 62 | inline MD5::uint4 MD5::F(uint4 x, uint4 y, uint4 z) { 63 | return (x&y) | (~x&z); 64 | } 65 | 66 | inline MD5::uint4 MD5::G(uint4 x, uint4 y, uint4 z) { 67 | return (x&z) | (y&~z); 68 | } 69 | 70 | inline MD5::uint4 MD5::H(uint4 x, uint4 y, uint4 z) { 71 | return x^y^z; 72 | } 73 | 74 | inline MD5::uint4 MD5::I(uint4 x, uint4 y, uint4 z) { 75 | return y ^ (x | ~z); 76 | } 77 | 78 | // rotate_left rotates x left n bits. 79 | inline MD5::uint4 MD5::rotate_left(uint4 x, int n) { 80 | return (x << n) | (x >> (32-n)); 81 | } 82 | 83 | // FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. 84 | // Rotation is separate from addition to prevent recomputation. 85 | inline void MD5::FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { 86 | a = rotate_left(a+ F(b,c,d) + x + ac, s) + b; 87 | } 88 | 89 | inline void MD5::GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { 90 | a = rotate_left(a + G(b,c,d) + x + ac, s) + b; 91 | } 92 | 93 | inline void MD5::HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { 94 | a = rotate_left(a + H(b,c,d) + x + ac, s) + b; 95 | } 96 | 97 | inline void MD5::II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { 98 | a = rotate_left(a + I(b,c,d) + x + ac, s) + b; 99 | } 100 | 101 | ////////////////////////////////////////////// 102 | 103 | // default ctor, just initailize 104 | MD5::MD5() 105 | { 106 | init(); 107 | } 108 | 109 | ////////////////////////////////////////////// 110 | 111 | // nifty shortcut ctor, compute MD5 for string and finalize it right away 112 | MD5::MD5(const std::string &text) 113 | { 114 | init(); 115 | update(text.c_str(), text.length()); 116 | finalize(); 117 | } 118 | 119 | ////////////////////////////// 120 | 121 | void MD5::init() 122 | { 123 | finalized=false; 124 | 125 | count[0] = 0; 126 | count[1] = 0; 127 | 128 | // load magic initialization constants. 129 | state[0] = 0x67452301; 130 | state[1] = 0xefcdab89; 131 | state[2] = 0x98badcfe; 132 | state[3] = 0x10325476; 133 | } 134 | 135 | ////////////////////////////// 136 | 137 | // decodes input (unsigned char) into output (uint4). Assumes len is a multiple of 4. 138 | void MD5::decode(uint4 output[], const uint1 input[], size_type len) 139 | { 140 | for (unsigned int i = 0, j = 0; j < len; i++, j += 4) 141 | output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | 142 | (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); 143 | } 144 | 145 | ////////////////////////////// 146 | 147 | // encodes input (uint4) into output (unsigned char). Assumes len is 148 | // a multiple of 4. 149 | void MD5::encode(uint1 output[], const uint4 input[], size_type len) 150 | { 151 | for (size_type i = 0, j = 0; j < len; i++, j += 4) { 152 | output[j] = input[i] & 0xff; 153 | output[j+1] = (input[i] >> 8) & 0xff; 154 | output[j+2] = (input[i] >> 16) & 0xff; 155 | output[j+3] = (input[i] >> 24) & 0xff; 156 | } 157 | } 158 | 159 | ////////////////////////////// 160 | 161 | // apply MD5 algo on a block 162 | void MD5::transform(const uint1 block[blocksize]) 163 | { 164 | uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; 165 | decode (x, block, blocksize); 166 | 167 | /* Round 1 */ 168 | FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ 169 | FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ 170 | FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ 171 | FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ 172 | FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ 173 | FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ 174 | FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ 175 | FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ 176 | FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ 177 | FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ 178 | FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ 179 | FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ 180 | FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ 181 | FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ 182 | FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ 183 | FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ 184 | 185 | /* Round 2 */ 186 | GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ 187 | GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ 188 | GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ 189 | GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ 190 | GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ 191 | GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ 192 | GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ 193 | GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ 194 | GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ 195 | GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ 196 | GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ 197 | GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ 198 | GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ 199 | GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ 200 | GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ 201 | GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ 202 | 203 | /* Round 3 */ 204 | HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ 205 | HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ 206 | HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ 207 | HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ 208 | HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ 209 | HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ 210 | HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ 211 | HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ 212 | HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ 213 | HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ 214 | HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ 215 | HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ 216 | HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ 217 | HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ 218 | HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ 219 | HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ 220 | 221 | /* Round 4 */ 222 | II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ 223 | II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ 224 | II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ 225 | II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ 226 | II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ 227 | II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ 228 | II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ 229 | II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ 230 | II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ 231 | II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ 232 | II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ 233 | II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ 234 | II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ 235 | II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ 236 | II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ 237 | II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ 238 | 239 | state[0] += a; 240 | state[1] += b; 241 | state[2] += c; 242 | state[3] += d; 243 | 244 | // Zeroize sensitive information. 245 | memset(x, 0, sizeof x); 246 | } 247 | 248 | ////////////////////////////// 249 | 250 | // MD5 block update operation. Continues an MD5 message-digest 251 | // operation, processing another message block 252 | void MD5::update(const unsigned char input[], size_type length) 253 | { 254 | // compute number of bytes mod 64 255 | size_type index = count[0] / 8 % blocksize; 256 | 257 | // Update number of bits 258 | if ((count[0] += (length << 3)) < (length << 3)) 259 | count[1]++; 260 | count[1] += (length >> 29); 261 | 262 | // number of bytes we need to fill in buffer 263 | size_type firstpart = 64 - index; 264 | 265 | size_type i; 266 | 267 | // transform as many times as possible. 268 | if (length >= firstpart) 269 | { 270 | // fill buffer first, transform 271 | memcpy(&buffer[index], input, firstpart); 272 | transform(buffer); 273 | 274 | // transform chunks of blocksize (64 bytes) 275 | for (i = firstpart; i + blocksize <= length; i += blocksize) 276 | transform(&input[i]); 277 | 278 | index = 0; 279 | } 280 | else 281 | i = 0; 282 | 283 | // buffer remaining input 284 | memcpy(&buffer[index], &input[i], length-i); 285 | } 286 | 287 | ////////////////////////////// 288 | 289 | // for convenience provide a verson with signed char 290 | void MD5::update(const char input[], size_type length) 291 | { 292 | update((const unsigned char*)input, length); 293 | } 294 | 295 | ////////////////////////////// 296 | 297 | // MD5 finalization. Ends an MD5 message-digest operation, writing the 298 | // the message digest and zeroizing the context. 299 | MD5& MD5::finalize() 300 | { 301 | static unsigned char padding[64] = { 302 | 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 303 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 304 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 305 | }; 306 | 307 | if (!finalized) { 308 | // Save number of bits 309 | unsigned char bits[8]; 310 | encode(bits, count, 8); 311 | 312 | // pad out to 56 mod 64. 313 | size_type index = count[0] / 8 % 64; 314 | size_type padLen = (index < 56) ? (56 - index) : (120 - index); 315 | update(padding, padLen); 316 | 317 | // Append length (before padding) 318 | update(bits, 8); 319 | 320 | // Store state in digest 321 | encode(digest, state, 16); 322 | 323 | // Zeroize sensitive information. 324 | memset(buffer, 0, sizeof buffer); 325 | memset(count, 0, sizeof count); 326 | 327 | finalized=true; 328 | } 329 | 330 | return *this; 331 | } 332 | 333 | ////////////////////////////// 334 | 335 | // return hex representation of digest as string 336 | std::string MD5::hexdigest() const 337 | { 338 | if (!finalized) 339 | return ""; 340 | 341 | char buf[33]; 342 | for (int i=0; i<16; i++) 343 | sprintf(buf+i*2, "%02x", digest[i]); 344 | buf[32]=0; 345 | 346 | return std::string(buf); 347 | } 348 | 349 | ////////////////////////////// 350 | 351 | std::ostream& operator<<(std::ostream& out, MD5 md5) 352 | { 353 | return out << md5.hexdigest(); 354 | } 355 | 356 | ////////////////////////////// 357 | 358 | std::string md5(const std::string str) 359 | { 360 | MD5 md5 = MD5(str); 361 | 362 | return md5.hexdigest(); 363 | } 364 | 365 | -------------------------------------------------------------------------------- /libjsonnet/core/pass.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 "pass.h" 18 | 19 | void CompilerPass::fodder(Fodder &fodder) 20 | { 21 | for (auto &f : fodder) 22 | fodderElement(f); 23 | } 24 | 25 | void CompilerPass::specs(std::vector &specs) 26 | { 27 | for (auto &spec : specs) { 28 | fodder(spec.openFodder); 29 | switch (spec.kind) { 30 | case ComprehensionSpec::FOR: 31 | fodder(spec.varFodder); 32 | fodder(spec.inFodder); 33 | expr(spec.expr); 34 | break; 35 | case ComprehensionSpec::IF: expr(spec.expr); break; 36 | } 37 | } 38 | } 39 | 40 | void CompilerPass::params(Fodder &fodder_l, ArgParams ¶ms, Fodder &fodder_r) 41 | { 42 | fodder(fodder_l); 43 | for (auto ¶m : params) { 44 | fodder(param.idFodder); 45 | if (param.expr) { 46 | fodder(param.eqFodder); 47 | expr(param.expr); 48 | } 49 | fodder(param.commaFodder); 50 | } 51 | fodder(fodder_r); 52 | } 53 | 54 | void CompilerPass::fieldParams(ObjectField &field) 55 | { 56 | if (field.methodSugar) { 57 | params(field.fodderL, field.params, field.fodderR); 58 | } 59 | } 60 | 61 | void CompilerPass::fields(ObjectFields &fields) 62 | { 63 | for (auto &field : fields) { 64 | switch (field.kind) { 65 | case ObjectField::LOCAL: { 66 | fodder(field.fodder1); 67 | fodder(field.fodder2); 68 | fieldParams(field); 69 | fodder(field.opFodder); 70 | expr(field.expr2); 71 | } break; 72 | 73 | case ObjectField::FIELD_ID: 74 | case ObjectField::FIELD_STR: 75 | case ObjectField::FIELD_EXPR: { 76 | if (field.kind == ObjectField::FIELD_ID) { 77 | fodder(field.fodder1); 78 | 79 | } else if (field.kind == ObjectField::FIELD_STR) { 80 | expr(field.expr1); 81 | 82 | } else if (field.kind == ObjectField::FIELD_EXPR) { 83 | fodder(field.fodder1); 84 | expr(field.expr1); 85 | fodder(field.fodder2); 86 | } 87 | fieldParams(field); 88 | fodder(field.opFodder); 89 | expr(field.expr2); 90 | 91 | } break; 92 | 93 | case ObjectField::ASSERT: { 94 | fodder(field.fodder1); 95 | expr(field.expr2); 96 | if (field.expr3 != nullptr) { 97 | fodder(field.opFodder); 98 | expr(field.expr3); 99 | } 100 | } break; 101 | } 102 | 103 | fodder(field.commaFodder); 104 | } 105 | } 106 | 107 | void CompilerPass::expr(AST *&ast_) 108 | { 109 | fodder(ast_->openFodder); 110 | visitExpr(ast_); 111 | } 112 | 113 | void CompilerPass::visit(Apply *ast) 114 | { 115 | expr(ast->target); 116 | params(ast->fodderL, ast->args, ast->fodderR); 117 | if (ast->tailstrict) { 118 | fodder(ast->tailstrictFodder); 119 | } 120 | } 121 | 122 | void CompilerPass::visit(ApplyBrace *ast) 123 | { 124 | expr(ast->left); 125 | expr(ast->right); 126 | } 127 | 128 | void CompilerPass::visit(Array *ast) 129 | { 130 | for (auto &element : ast->elements) { 131 | expr(element.expr); 132 | fodder(element.commaFodder); 133 | } 134 | fodder(ast->closeFodder); 135 | } 136 | 137 | void CompilerPass::visit(ArrayComprehension *ast) 138 | { 139 | expr(ast->body); 140 | fodder(ast->commaFodder); 141 | specs(ast->specs); 142 | fodder(ast->closeFodder); 143 | } 144 | 145 | void CompilerPass::visit(Assert *ast) 146 | { 147 | expr(ast->cond); 148 | if (ast->message != nullptr) { 149 | fodder(ast->colonFodder); 150 | expr(ast->message); 151 | } 152 | fodder(ast->semicolonFodder); 153 | expr(ast->rest); 154 | } 155 | 156 | void CompilerPass::visit(Binary *ast) 157 | { 158 | expr(ast->left); 159 | fodder(ast->opFodder); 160 | expr(ast->right); 161 | } 162 | 163 | void CompilerPass::visit(Conditional *ast) 164 | { 165 | expr(ast->cond); 166 | fodder(ast->thenFodder); 167 | if (ast->branchFalse != nullptr) { 168 | expr(ast->branchTrue); 169 | fodder(ast->elseFodder); 170 | expr(ast->branchFalse); 171 | } else { 172 | expr(ast->branchTrue); 173 | } 174 | } 175 | 176 | void CompilerPass::visit(Error *ast) 177 | { 178 | expr(ast->expr); 179 | } 180 | 181 | void CompilerPass::visit(Function *ast) 182 | { 183 | params(ast->parenLeftFodder, ast->params, ast->parenRightFodder); 184 | expr(ast->body); 185 | } 186 | 187 | void CompilerPass::visit(Import *ast) 188 | { 189 | visit(ast->file); 190 | } 191 | 192 | void CompilerPass::visit(Importstr *ast) 193 | { 194 | visit(ast->file); 195 | } 196 | 197 | void CompilerPass::visit(InSuper *ast) 198 | { 199 | expr(ast->element); 200 | } 201 | 202 | void CompilerPass::visit(Index *ast) 203 | { 204 | expr(ast->target); 205 | if (ast->id != nullptr) { 206 | } else { 207 | if (ast->isSlice) { 208 | if (ast->index != nullptr) 209 | expr(ast->index); 210 | if (ast->end != nullptr) 211 | expr(ast->end); 212 | if (ast->step != nullptr) 213 | expr(ast->step); 214 | } else { 215 | expr(ast->index); 216 | } 217 | } 218 | } 219 | 220 | void CompilerPass::visit(Local *ast) 221 | { 222 | assert(ast->binds.size() > 0); 223 | for (auto &bind : ast->binds) { 224 | fodder(bind.varFodder); 225 | if (bind.functionSugar) { 226 | params(bind.parenLeftFodder, bind.params, bind.parenRightFodder); 227 | } 228 | fodder(bind.opFodder); 229 | expr(bind.body); 230 | fodder(bind.closeFodder); 231 | } 232 | expr(ast->body); 233 | } 234 | 235 | void CompilerPass::visit(Object *ast) 236 | { 237 | fields(ast->fields); 238 | fodder(ast->closeFodder); 239 | } 240 | 241 | void CompilerPass::visit(DesugaredObject *ast) 242 | { 243 | for (AST *assert : ast->asserts) { 244 | expr(assert); 245 | } 246 | for (auto &field : ast->fields) { 247 | expr(field.name); 248 | expr(field.body); 249 | } 250 | } 251 | 252 | void CompilerPass::visit(ObjectComprehension *ast) 253 | { 254 | fields(ast->fields); 255 | specs(ast->specs); 256 | fodder(ast->closeFodder); 257 | } 258 | 259 | void CompilerPass::visit(ObjectComprehensionSimple *ast) 260 | { 261 | expr(ast->field); 262 | expr(ast->value); 263 | expr(ast->array); 264 | } 265 | 266 | void CompilerPass::visit(Parens *ast) 267 | { 268 | expr(ast->expr); 269 | fodder(ast->closeFodder); 270 | } 271 | 272 | void CompilerPass::visit(SuperIndex *ast) 273 | { 274 | if (ast->id != nullptr) { 275 | } else { 276 | expr(ast->index); 277 | } 278 | } 279 | 280 | void CompilerPass::visit(Unary *ast) 281 | { 282 | expr(ast->expr); 283 | } 284 | 285 | #define VISIT(var,astType,astClass) \ 286 | case astType: { \ 287 | assert(dynamic_cast(var)); \ 288 | auto *ast = static_cast(var); \ 289 | visit(ast); \ 290 | } break 291 | 292 | void CompilerPass::visitExpr(AST *&ast_) 293 | { 294 | switch(ast_->type) { 295 | VISIT(ast_, AST_APPLY, Apply); 296 | VISIT(ast_, AST_APPLY_BRACE, ApplyBrace); 297 | VISIT(ast_, AST_ARRAY, Array); 298 | VISIT(ast_, AST_ARRAY_COMPREHENSION, ArrayComprehension); 299 | // VISIT(ast_, AST_ARRAY_COMPREHENSION, ArrayComprehensionSimple); 300 | VISIT(ast_, AST_ASSERT, Assert); 301 | VISIT(ast_, AST_BINARY, Binary); 302 | VISIT(ast_, AST_BUILTIN_FUNCTION, BuiltinFunction); 303 | VISIT(ast_, AST_CONDITIONAL, Conditional); 304 | VISIT(ast_, AST_DESUGARED_OBJECT, DesugaredObject); 305 | VISIT(ast_, AST_DOLLAR, Dollar); 306 | VISIT(ast_, AST_ERROR, Error); 307 | VISIT(ast_, AST_FUNCTION, Function); 308 | VISIT(ast_, AST_IMPORT, Import); 309 | VISIT(ast_, AST_IMPORTSTR, Importstr); 310 | VISIT(ast_, AST_INDEX, Index); 311 | VISIT(ast_, AST_IN_SUPER, InSuper); 312 | VISIT(ast_, AST_LITERAL_BOOLEAN, LiteralBoolean); 313 | VISIT(ast_, AST_LITERAL_NULL, LiteralNull); 314 | VISIT(ast_, AST_LITERAL_NUMBER, LiteralNumber); 315 | VISIT(ast_, AST_LITERAL_STRING, LiteralString); 316 | VISIT(ast_, AST_LOCAL, Local); 317 | VISIT(ast_, AST_OBJECT, Object); 318 | VISIT(ast_, AST_OBJECT_COMPREHENSION, ObjectComprehension); 319 | VISIT(ast_, AST_OBJECT_COMPREHENSION_SIMPLE, ObjectComprehensionSimple); 320 | VISIT(ast_, AST_PARENS, Parens); 321 | VISIT(ast_, AST_SELF, Self); 322 | VISIT(ast_, AST_SUPER_INDEX, SuperIndex); 323 | VISIT(ast_, AST_UNARY, Unary); 324 | VISIT(ast_, AST_VAR, Var); 325 | default: 326 | std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_ << std::endl; 327 | std::abort(); 328 | break; 329 | } 330 | } 331 | 332 | void CompilerPass::file(AST *&body, Fodder &final_fodder) 333 | { 334 | expr(body); 335 | fodder(final_fodder); 336 | } 337 | 338 | /** A pass that clones the AST it is given. */ 339 | class ClonePass : public CompilerPass { 340 | public: 341 | ClonePass(Allocator &alloc) : CompilerPass(alloc) {} 342 | virtual void expr(AST *&ast); 343 | }; 344 | 345 | #define CLONE(var,astType,astClass) \ 346 | case astType: { \ 347 | assert(dynamic_cast(var)); \ 348 | auto *ast = static_cast(var); \ 349 | var = alloc.clone(ast); \ 350 | } break 351 | 352 | void ClonePass::expr(AST *&ast_) 353 | { 354 | switch(ast_->type) { 355 | CLONE(ast_, AST_APPLY, Apply); 356 | CLONE(ast_, AST_APPLY_BRACE, ApplyBrace); 357 | CLONE(ast_, AST_ARRAY, Array); 358 | CLONE(ast_, AST_ARRAY_COMPREHENSION, ArrayComprehension); 359 | // CLONE(ast_, AST_ARRAY_COMPREHENSION, ArrayComprehensionSimple); 360 | CLONE(ast_, AST_ASSERT, Assert); 361 | CLONE(ast_, AST_BINARY, Binary); 362 | CLONE(ast_, AST_BUILTIN_FUNCTION, BuiltinFunction); 363 | CLONE(ast_, AST_CONDITIONAL, Conditional); 364 | CLONE(ast_, AST_DESUGARED_OBJECT, DesugaredObject); 365 | CLONE(ast_, AST_DOLLAR, Dollar); 366 | CLONE(ast_, AST_ERROR, Error); 367 | CLONE(ast_, AST_FUNCTION, Function); 368 | CLONE(ast_, AST_IMPORT, Import); 369 | CLONE(ast_, AST_IMPORTSTR, Importstr); 370 | CLONE(ast_, AST_INDEX, Index); 371 | CLONE(ast_, AST_IN_SUPER, InSuper); 372 | CLONE(ast_, AST_LITERAL_BOOLEAN, LiteralBoolean); 373 | CLONE(ast_, AST_LITERAL_NULL, LiteralNull); 374 | CLONE(ast_, AST_LITERAL_NUMBER, LiteralNumber); 375 | CLONE(ast_, AST_LITERAL_STRING, LiteralString); 376 | CLONE(ast_, AST_LOCAL, Local); 377 | CLONE(ast_, AST_OBJECT, Object); 378 | CLONE(ast_, AST_OBJECT_COMPREHENSION, ObjectComprehension); 379 | CLONE(ast_, AST_OBJECT_COMPREHENSION_SIMPLE, ObjectComprehensionSimple); 380 | CLONE(ast_, AST_PARENS, Parens); 381 | CLONE(ast_, AST_SELF, Self); 382 | CLONE(ast_, AST_SUPER_INDEX, SuperIndex); 383 | CLONE(ast_, AST_UNARY, Unary); 384 | CLONE(ast_, AST_VAR, Var); 385 | default: 386 | std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_ << std::endl; 387 | std::abort(); 388 | break; 389 | } 390 | 391 | CompilerPass::expr(ast_); 392 | } 393 | 394 | AST *clone_ast(Allocator &alloc, AST *ast) 395 | { 396 | AST *r = ast; 397 | ClonePass(alloc).expr(r); 398 | return r; 399 | } 400 | -------------------------------------------------------------------------------- /jsonnet.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Jsonnet | 4 | +----------------------------------------------------------------------+ 5 | | This source file is subject to version 2.0 of the Apache license, | 6 | | that is bundled with this package in the file LICENSE, and is | 7 | | available through the world-wide-web at the following url: | 8 | | http://www.apache.org/licenses/LICENSE-2.0.html | 9 | | If you did not receive a copy of the Apache2.0 license and are unable| 10 | | to obtain it through the world-wide-web, please send a note to | 11 | | license@php.net so we can mail you a copy immediately. | 12 | +----------------------------------------------------------------------+ 13 | | Author: Neeke.Gao | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #include "php_jsonnet.h" 18 | #include "libjsonnet.h" 19 | 20 | 21 | ZEND_DECLARE_MODULE_GLOBALS(jsonnet) 22 | 23 | static int le_jsonnet; 24 | 25 | const zend_function_entry jsonnet_functions[] = 26 | { 27 | PHP_FE(jsonnet_get_version, NULL) 28 | PHP_FE(jsonnet_get_author, NULL) 29 | { 30 | NULL, NULL, NULL 31 | } 32 | }; 33 | 34 | ZEND_BEGIN_ARG_INFO_EX(jsonnet_evaluateFile_arginfo, 0, 0, 1) 35 | ZEND_ARG_INFO(0, file_path) 36 | ZEND_END_ARG_INFO() 37 | 38 | ZEND_BEGIN_ARG_INFO_EX(jsonnet_evaluateSnippet_arginfo, 0, 0, 1) 39 | ZEND_ARG_INFO(0, snippet_string) 40 | ZEND_END_ARG_INFO() 41 | 42 | ZEND_BEGIN_ARG_INFO_EX(jsonnet_fmtFile_arginfo, 0, 0, 1) 43 | ZEND_ARG_INFO(0, file_path) 44 | ZEND_END_ARG_INFO() 45 | 46 | ZEND_BEGIN_ARG_INFO_EX(jsonnet_fmtSnippet_arginfo, 0, 0, 1) 47 | ZEND_ARG_INFO(0, snippet_string) 48 | ZEND_END_ARG_INFO() 49 | 50 | const zend_function_entry jsonnet_methods[] = 51 | { 52 | PHP_ME(JSONNET_RES_NAME, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) 53 | PHP_ME(JSONNET_RES_NAME, __destruct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_DTOR) 54 | 55 | PHP_ME(JSONNET_RES_NAME, evaluateFile, jsonnet_evaluateFile_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) 56 | PHP_ME(JSONNET_RES_NAME, evaluateSnippet, jsonnet_evaluateSnippet_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) 57 | PHP_ME(JSONNET_RES_NAME, fmtFile, jsonnet_fmtFile_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) 58 | PHP_ME(JSONNET_RES_NAME, fmtSnippet, jsonnet_fmtSnippet_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) 59 | 60 | { 61 | NULL, NULL, NULL 62 | } 63 | }; 64 | 65 | zend_module_entry jsonnet_module_entry = 66 | { 67 | #if ZEND_MODULE_API_NO >= 20010901 68 | STANDARD_MODULE_HEADER, 69 | #endif 70 | JSONNET_RES_NAME, 71 | jsonnet_functions, 72 | PHP_MINIT(jsonnet), 73 | PHP_MSHUTDOWN(jsonnet), 74 | PHP_RINIT(jsonnet), 75 | PHP_RSHUTDOWN(jsonnet), 76 | PHP_MINFO(jsonnet), 77 | #if ZEND_MODULE_API_NO >= 20010901 78 | JSONNET_PHP_VERSION, 79 | #endif 80 | STANDARD_MODULE_PROPERTIES 81 | }; 82 | 83 | #ifdef COMPILE_DL_JSONNET 84 | ZEND_GET_MODULE(jsonnet) 85 | #endif 86 | 87 | PHP_INI_BEGIN() 88 | 89 | PHP_INI_END() 90 | 91 | 92 | static void php_jsonnet_init_globals(zend_jsonnet_globals *jsonnet_globals) 93 | { 94 | 95 | } 96 | 97 | 98 | PHP_MINIT_FUNCTION(jsonnet) 99 | { 100 | ZEND_INIT_MODULE_GLOBALS(jsonnet, php_jsonnet_init_globals, NULL); 101 | 102 | REGISTER_INI_ENTRIES(); 103 | 104 | REGISTER_STRINGL_CONSTANT("JSONNET_PHP_VERSION", JSONNET_PHP_VERSION, sizeof(JSONNET_PHP_VERSION) - 1, CONST_PERSISTENT | CONST_CS); 105 | REGISTER_STRINGL_CONSTANT("JSONNET_PHP_AUTHOR", JSONNET_PHP_AUTHOR, sizeof(JSONNET_PHP_AUTHOR) - 1, CONST_PERSISTENT | CONST_CS); 106 | 107 | zend_class_entry jsonnet; 108 | INIT_CLASS_ENTRY(jsonnet, JSONNET_RES_NAME, jsonnet_methods); 109 | jsonnet_ce = JSONNET_ZEND_REGISTER_CLASS(&jsonnet); 110 | jsonnet_ce->ce_flags = ZEND_ACC_IMPLICIT_PUBLIC; 111 | 112 | return SUCCESS; 113 | } 114 | 115 | PHP_MSHUTDOWN_FUNCTION(jsonnet) 116 | { 117 | UNREGISTER_INI_ENTRIES(); 118 | 119 | return SUCCESS; 120 | } 121 | 122 | PHP_RINIT_FUNCTION(jsonnet) 123 | { 124 | return SUCCESS; 125 | } 126 | 127 | PHP_RSHUTDOWN_FUNCTION(jsonnet) 128 | { 129 | return SUCCESS; 130 | } 131 | 132 | PHP_MINFO_FUNCTION(jsonnet) 133 | { 134 | php_info_print_table_start(); 135 | php_info_print_table_header(2, "Jsonnet support", "Enabled"); 136 | php_info_print_table_row(2, "Jsonnet LibJsonnet Information", LIB_JSONNET_VERSION); 137 | php_info_print_table_row(2, "Jsonnet Version", JSONNET_PHP_VERSION); 138 | php_info_print_table_row(2, "Jsonnet Author", JSONNET_PHP_AUTHOR); 139 | php_info_print_table_row(2, "Jsonnet Supports", "https://github.com/Neeke/Jsonnet-PHP"); 140 | php_info_print_table_end(); 141 | 142 | DISPLAY_INI_ENTRIES(); 143 | } 144 | 145 | 146 | PHP_FUNCTION(jsonnet_get_version) 147 | { 148 | JSONNET_RETURN_STRINGL(JSONNET_PHP_VERSION, strlen(JSONNET_PHP_VERSION)); 149 | } 150 | 151 | PHP_FUNCTION(jsonnet_get_author) 152 | { 153 | JSONNET_RETURN_STRINGL(JSONNET_PHP_AUTHOR, strlen(JSONNET_PHP_AUTHOR)); 154 | } 155 | 156 | PHP_METHOD(JSONNET_RES_NAME, __construct) 157 | { 158 | 159 | } 160 | 161 | PHP_METHOD(JSONNET_RES_NAME, __destruct) 162 | { 163 | 164 | } 165 | 166 | PHP_METHOD(JSONNET_RES_NAME, evaluateFile) 167 | { 168 | int argc = ZEND_NUM_ARGS(); 169 | int _file_path_len, error; 170 | 171 | char *output, *_file_path = NULL; 172 | struct JsonnetVm *vm; 173 | 174 | zval *err, *result, *resultZval; 175 | 176 | if (zend_parse_parameters(argc TSRMLS_CC, "s", &_file_path, &_file_path_len) == FAILURE) 177 | return; 178 | 179 | if (argc > 0 && _file_path_len > 0) 180 | { 181 | 182 | vm = jsonnet_make(); 183 | output = jsonnet_evaluate_file(vm, _file_path, &error); 184 | 185 | if (error) 186 | { 187 | JSONNET_MAKE_ZVAL(err); 188 | JSONNET_ZVAL_STRING(err,output); 189 | 190 | jsonnet_realloc(vm, output, 0); 191 | jsonnet_destroy(vm); 192 | 193 | zend_throw_exception(php_com_exception_class_entry, Z_STRVAL_P(err), CODE_ERROR TSRMLS_CC); 194 | JSONNET_ZVAL_PTR_DTOR(err); 195 | RETURN_FALSE; 196 | } 197 | 198 | JSONNET_MAKE_ZVAL(result); 199 | JSONNET_ZVAL_STRING(result,output); 200 | 201 | jsonnet_realloc(vm, output, 0); 202 | jsonnet_destroy(vm); 203 | 204 | JSONNET_MAKE_ZVAL(resultZval); 205 | php_json_decode(resultZval, Z_STRVAL_P(result), Z_STRLEN_P(result), 1, 512 TSRMLS_CC); 206 | 207 | if(Z_TYPE_P(resultZval) == IS_NULL) 208 | { 209 | JSONNET_ZVAL_PTR_DTOR(result); 210 | JSONNET_ZVAL_PTR_DTOR(resultZval); 211 | zend_throw_exception(php_com_exception_class_entry, "Jsonnet::evaluateFile #error", CODE_ERROR TSRMLS_CC); 212 | return; 213 | } 214 | 215 | JSONNET_ZVAL_PTR_DTOR(result); 216 | RETURN_ZVAL(resultZval,1,1); 217 | } 218 | 219 | zend_throw_exception(php_com_exception_class_entry, "Jsonnet::evaluateFile('filePath'), filePath is null", CODE_ERROR TSRMLS_CC); 220 | } 221 | 222 | PHP_METHOD(JSONNET_RES_NAME, evaluateSnippet) 223 | { 224 | int argc = ZEND_NUM_ARGS(); 225 | int _snippet_string_len, error; 226 | 227 | char *output, *_snippet_string = NULL; 228 | struct JsonnetVm *vm; 229 | 230 | zval *err, *result, *resultZval; 231 | 232 | if (zend_parse_parameters(argc TSRMLS_CC, "s", &_snippet_string, &_snippet_string_len) == FAILURE) 233 | return; 234 | 235 | if (argc > 0 && _snippet_string_len > 0) 236 | { 237 | vm = jsonnet_make(); 238 | output = jsonnet_evaluate_snippet(vm, "snippet", _snippet_string, &error); 239 | 240 | if (error) 241 | { 242 | JSONNET_MAKE_ZVAL(err); 243 | JSONNET_ZVAL_STRING(err,output); 244 | 245 | jsonnet_realloc(vm, output, 0); 246 | jsonnet_destroy(vm); 247 | 248 | zend_throw_exception(php_com_exception_class_entry, Z_STRVAL_P(err), CODE_ERROR TSRMLS_CC); 249 | JSONNET_ZVAL_PTR_DTOR(err); 250 | RETURN_FALSE; 251 | } 252 | 253 | JSONNET_MAKE_ZVAL(result); 254 | JSONNET_ZVAL_STRING(result,output); 255 | 256 | jsonnet_realloc(vm, output, 0); 257 | jsonnet_destroy(vm); 258 | 259 | JSONNET_MAKE_ZVAL(resultZval); 260 | php_json_decode(resultZval, Z_STRVAL_P(result), Z_STRLEN_P(result), 1, 512 TSRMLS_CC); 261 | 262 | if(Z_TYPE_P(resultZval) == IS_NULL) 263 | { 264 | JSONNET_ZVAL_PTR_DTOR(result); 265 | JSONNET_ZVAL_PTR_DTOR(resultZval); 266 | zend_throw_exception(php_com_exception_class_entry, "Jsonnet::evaluateSnippet #error", CODE_ERROR TSRMLS_CC); 267 | return; 268 | } 269 | 270 | JSONNET_ZVAL_PTR_DTOR(result); 271 | RETURN_ZVAL(resultZval,1,1); 272 | } 273 | 274 | zend_throw_exception(php_com_exception_class_entry, "Jsonnet::evaluateSnippet('string'), string is null", CODE_ERROR TSRMLS_CC); 275 | } 276 | 277 | PHP_METHOD(JSONNET_RES_NAME, fmtFile) 278 | { 279 | int argc = ZEND_NUM_ARGS(); 280 | int _file_path_len, error; 281 | 282 | char *output, *_file_path = NULL; 283 | struct JsonnetVm *vm; 284 | 285 | zval *err, *result; 286 | 287 | if (zend_parse_parameters(argc TSRMLS_CC, "s", &_file_path, &_file_path_len) == FAILURE) 288 | return; 289 | 290 | if (argc > 0 && _file_path_len > 0) 291 | { 292 | 293 | vm = jsonnet_make(); 294 | output = jsonnet_fmt_file(vm, _file_path, &error); 295 | 296 | if (error) 297 | { 298 | JSONNET_MAKE_ZVAL(err); 299 | JSONNET_ZVAL_STRING(err,output); 300 | 301 | jsonnet_realloc(vm, output, 0); 302 | jsonnet_destroy(vm); 303 | 304 | zend_throw_exception(php_com_exception_class_entry, Z_STRVAL_P(err), CODE_ERROR TSRMLS_CC); 305 | JSONNET_ZVAL_PTR_DTOR(err); 306 | RETURN_FALSE; 307 | } 308 | 309 | JSONNET_MAKE_ZVAL(result); 310 | JSONNET_ZVAL_STRING(result,output); 311 | 312 | jsonnet_realloc(vm, output, 0); 313 | jsonnet_destroy(vm); 314 | 315 | if(Z_TYPE_P(result) == IS_NULL) 316 | { 317 | JSONNET_ZVAL_PTR_DTOR(result); 318 | zend_throw_exception(php_com_exception_class_entry, "Jsonnet::fmtFile #error", CODE_ERROR TSRMLS_CC); 319 | return; 320 | } 321 | 322 | RETURN_ZVAL(result,1,1); 323 | } 324 | 325 | zend_throw_exception(php_com_exception_class_entry, "Jsonnet::fmtFile('filePath'), filePath is null", CODE_ERROR TSRMLS_CC); 326 | } 327 | 328 | PHP_METHOD(JSONNET_RES_NAME, fmtSnippet) 329 | { 330 | int argc = ZEND_NUM_ARGS(); 331 | int _snippet_string_len, error; 332 | 333 | char *output, *_snippet_string = NULL; 334 | struct JsonnetVm *vm; 335 | 336 | zval *err, *result; 337 | 338 | if (zend_parse_parameters(argc TSRMLS_CC, "s", &_snippet_string, &_snippet_string_len) == FAILURE) 339 | return; 340 | 341 | if (argc > 0 && _snippet_string_len > 0) 342 | { 343 | 344 | vm = jsonnet_make(); 345 | output = jsonnet_fmt_snippet(vm, "snippet", _snippet_string, &error); 346 | 347 | if (error) 348 | { 349 | JSONNET_MAKE_ZVAL(err); 350 | JSONNET_ZVAL_STRING(err,output); 351 | 352 | jsonnet_realloc(vm, output, 0); 353 | jsonnet_destroy(vm); 354 | 355 | zend_throw_exception(php_com_exception_class_entry, Z_STRVAL_P(err), CODE_ERROR TSRMLS_CC); 356 | JSONNET_ZVAL_PTR_DTOR(err); 357 | RETURN_FALSE; 358 | } 359 | 360 | JSONNET_MAKE_ZVAL(result); 361 | JSONNET_ZVAL_STRING(result,output); 362 | 363 | jsonnet_realloc(vm, output, 0); 364 | jsonnet_destroy(vm); 365 | 366 | if(Z_TYPE_P(result) == IS_NULL) 367 | { 368 | JSONNET_ZVAL_PTR_DTOR(result); 369 | zend_throw_exception(php_com_exception_class_entry, "Jsonnet::fmtSnippet #error", CODE_ERROR TSRMLS_CC); 370 | return; 371 | } 372 | 373 | RETURN_ZVAL(result,1,1); 374 | } 375 | 376 | zend_throw_exception(php_com_exception_class_entry, "Jsonnet::fmtSnippet('string'), string is null", CODE_ERROR TSRMLS_CC); 377 | } 378 | -------------------------------------------------------------------------------- /libjsonnet/core/lexer_test.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 "lexer.h" 18 | 19 | #include 20 | #include "gtest/gtest.h" 21 | 22 | namespace { 23 | 24 | void testLex(const char* name, const char* input, const std::list& tokens, 25 | const std::string& error) 26 | { 27 | std::list test_tokens(tokens); 28 | test_tokens.push_back(Token(Token::Kind::END_OF_FILE, "")); 29 | 30 | try { 31 | std::list lexed_tokens = jsonnet_lex(name, input); 32 | ASSERT_EQ(test_tokens, lexed_tokens) << "Test failed: " << name << std::endl; 33 | } catch (StaticError& e) { 34 | ASSERT_EQ(error, e.toString()); 35 | } 36 | } 37 | 38 | TEST(Lexer, TestWhitespace) 39 | { 40 | testLex("empty", "", {}, ""); 41 | testLex("whitespace", " \t\n\r\r\n", {}, ""); 42 | } 43 | 44 | TEST(Lexer, TestOperators) 45 | { 46 | testLex("brace L", "{", {Token(Token::Kind::BRACE_L, "")}, ""); 47 | testLex("brace R", "}", {Token(Token::Kind::BRACE_R, "")}, ""); 48 | testLex("bracket L", "[", {Token(Token::Kind::BRACKET_L, "")}, ""); 49 | testLex("bracket R", "]", {Token(Token::Kind::BRACKET_R, "")}, ""); 50 | testLex("colon ", ":", {Token(Token::Kind::OPERATOR, ":")}, ""); 51 | testLex("colon 2", "::", {Token(Token::Kind::OPERATOR, "::")}, ""); 52 | testLex("colon 2", ":::", {Token(Token::Kind::OPERATOR, ":::")}, ""); 53 | testLex("arrow right", "->", {Token(Token::Kind::OPERATOR, "->")}, ""); 54 | testLex("less than minus", 55 | "<-", 56 | {Token(Token::Kind::OPERATOR, "<"), Token(Token::Kind::OPERATOR, "-")}, 57 | ""); 58 | testLex("comma", ",", {Token(Token::Kind::COMMA, "")}, ""); 59 | testLex("dollar", "$", {Token(Token::Kind::DOLLAR, "")}, ""); 60 | testLex("dot", ".", {Token(Token::Kind::DOT, "")}, ""); 61 | testLex("paren L", "(", {Token(Token::Kind::PAREN_L, "")}, ""); 62 | testLex("paren R", ")", {Token(Token::Kind::PAREN_R, "")}, ""); 63 | testLex("semicolon", ";", {Token(Token::Kind::SEMICOLON, "")}, ""); 64 | 65 | testLex("not 1", "!", {Token(Token::Kind::OPERATOR, "!")}, ""); 66 | testLex("not 2", "! ", {Token(Token::Kind::OPERATOR, "!")}, ""); 67 | testLex("not equal", "!=", {Token(Token::Kind::OPERATOR, "!=")}, ""); 68 | testLex("tilde", "~", {Token(Token::Kind::OPERATOR, "~")}, ""); 69 | testLex("plus", "+", {Token(Token::Kind::OPERATOR, "+")}, ""); 70 | testLex("minus", "-", {Token(Token::Kind::OPERATOR, "-")}, ""); 71 | } 72 | 73 | TEST(Lexer, TestMiscOperators) 74 | { 75 | testLex("op *", "*", {Token(Token::Kind::OPERATOR, "*")}, ""); 76 | testLex("op /", "/", {Token(Token::Kind::OPERATOR, "/")}, ""); 77 | testLex("op %", "%", {Token(Token::Kind::OPERATOR, "%")}, ""); 78 | testLex("op &", "&", {Token(Token::Kind::OPERATOR, "&")}, ""); 79 | testLex("op |", "|", {Token(Token::Kind::OPERATOR, "|")}, ""); 80 | testLex("op ^", "^", {Token(Token::Kind::OPERATOR, "^")}, ""); 81 | testLex("op =", "=", {Token(Token::Kind::OPERATOR, "=")}, ""); 82 | testLex("op <", "<", {Token(Token::Kind::OPERATOR, "<")}, ""); 83 | testLex("op >", ">", {Token(Token::Kind::OPERATOR, ">")}, ""); 84 | testLex("op >==|", ">==|", {Token(Token::Kind::OPERATOR, ">==|")}, ""); 85 | } 86 | 87 | TEST(Lexer, TestNumbers) 88 | { 89 | testLex("number 0", "0", {Token(Token::Kind::NUMBER, "0")}, ""); 90 | testLex("number 1", "1", {Token(Token::Kind::NUMBER, "1")}, ""); 91 | testLex("number 1.0", "1.0", {Token(Token::Kind::NUMBER, "1.0")}, ""); 92 | testLex("number 0.10", "0.10", {Token(Token::Kind::NUMBER, "0.10")}, ""); 93 | testLex("number 0e100", "0e100", {Token(Token::Kind::NUMBER, "0e100")}, ""); 94 | testLex("number 1e100", "1e100", {Token(Token::Kind::NUMBER, "1e100")}, ""); 95 | testLex("number 1.1e100", "1.1e100", {Token(Token::Kind::NUMBER, "1.1e100")}, ""); 96 | testLex("number 1.1e-100", "1.1e-100", {Token(Token::Kind::NUMBER, "1.1e-100")}, ""); 97 | testLex("number 1.1e+100", "1.1e+100", {Token(Token::Kind::NUMBER, "1.1e+100")}, ""); 98 | testLex("number 0100", 99 | "0100", 100 | {Token(Token::Kind::NUMBER, "0"), Token(Token::Kind::NUMBER, "100")}, 101 | ""); 102 | testLex("number 10+10", 103 | "10+10", 104 | {Token(Token::Kind::NUMBER, "10"), 105 | Token(Token::Kind::OPERATOR, "+"), 106 | Token(Token::Kind::NUMBER, "10")}, 107 | ""); 108 | testLex("number 1.+3", 109 | "1.+3", 110 | {}, 111 | "number 1.+3:1:1: couldn't lex number, junk after decimal point: +"); 112 | testLex("number 1e!", "1e!", {}, "number 1e!:1:1: couldn't lex number, junk after 'E': !"); 113 | testLex("number 1e+!", 114 | "1e+!", 115 | {}, 116 | "number 1e+!:1:1: couldn't lex number, junk after exponent sign: !"); 117 | } 118 | 119 | TEST(Lexer, TestDoubleStrings) 120 | { 121 | testLex("double string \"hi\"", "\"hi\"", {Token(Token::Kind::STRING_DOUBLE, "hi")}, ""); 122 | testLex("double string \"hi nl\"", "\"hi\n\"", {Token(Token::Kind::STRING_DOUBLE, "hi\n")}, ""); 123 | testLex("double string \"hi\\\"\"", 124 | "\"hi\\\"\"", 125 | {Token(Token::Kind::STRING_DOUBLE, "hi\\\"")}, 126 | ""); 127 | testLex("double string \"hi\\nl\"", 128 | "\"hi\\\n\"", 129 | {Token(Token::Kind::STRING_DOUBLE, "hi\\\n")}, 130 | ""); 131 | testLex("double string \"hi", "\"hi", {}, "double string \"hi:1:1: unterminated string"); 132 | } 133 | 134 | TEST(Lexer, TestSingleStrings) 135 | { 136 | testLex("single string 'hi'", "'hi'", {Token(Token::Kind::STRING_SINGLE, "hi")}, ""); 137 | testLex("single string 'hi nl'", "'hi\n'", {Token(Token::Kind::STRING_SINGLE, "hi\n")}, ""); 138 | testLex("single string 'hi\\''", "'hi\\''", {Token(Token::Kind::STRING_SINGLE, "hi\\'")}, ""); 139 | testLex( 140 | "single string 'hi\\nl'", "'hi\\\n'", {Token(Token::Kind::STRING_SINGLE, "hi\\\n")}, ""); 141 | testLex("single string 'hi", "'hi", {}, "single string 'hi:1:1: unterminated string"); 142 | } 143 | 144 | TEST(Lexer, TestVerbatimDoubleStrings) 145 | { 146 | testLex("verbatim double string @\"hi\"", 147 | "@\"hi\"", 148 | {Token(Token::Kind::VERBATIM_STRING_DOUBLE, "hi")}, 149 | ""); 150 | testLex("verbatim double string @\"hi nl\"", 151 | "@\"hi\n\"", 152 | {Token(Token::Kind::VERBATIM_STRING_DOUBLE, "hi\n")}, 153 | ""); 154 | testLex("verbatim double string @\"hi\\\"", 155 | "@\"hi\\\"", 156 | {Token(Token::Kind::VERBATIM_STRING_DOUBLE, "hi\\")}, 157 | ""); 158 | testLex("verbatim double string @\"hi\\\\\"", 159 | "@\"hi\\\\\"", 160 | {Token(Token::Kind::VERBATIM_STRING_DOUBLE, "hi\\\\")}, 161 | ""); 162 | testLex("verbatim double string @\"hi\"\"\"", 163 | "@\"hi\"\"\"", 164 | {Token(Token::Kind::VERBATIM_STRING_DOUBLE, "hi\"")}, 165 | ""); 166 | testLex("verbatim double string @\"\"\"hi\"", 167 | "@\"\"\"hi\"", 168 | {Token(Token::Kind::VERBATIM_STRING_DOUBLE, "\"hi")}, 169 | ""); 170 | } 171 | 172 | TEST(Lexer, TestVerbatimSingleStrings) 173 | { 174 | testLex("verbatim single string @'hi'", 175 | "@'hi'", 176 | {Token(Token::Kind::VERBATIM_STRING_SINGLE, "hi")}, 177 | ""); 178 | testLex("verbatim single string @'hi nl'", 179 | "@'hi\n'", 180 | {Token(Token::Kind::VERBATIM_STRING_SINGLE, "hi\n")}, 181 | ""); 182 | testLex("verbatim single string @'hi\\'", 183 | "@'hi\\'", 184 | {Token(Token::Kind::VERBATIM_STRING_SINGLE, "hi\\")}, 185 | ""); 186 | testLex("verbatim single string @'hi\\\\'", 187 | "@'hi\\\\'", 188 | {Token(Token::Kind::VERBATIM_STRING_SINGLE, "hi\\\\")}, 189 | ""); 190 | testLex("verbatim single string @'hi'''", 191 | "@'hi'''", 192 | {Token(Token::Kind::VERBATIM_STRING_SINGLE, "hi'")}, 193 | ""); 194 | testLex("verbatim single string @'''hi'", 195 | "@'''hi'", 196 | {Token(Token::Kind::VERBATIM_STRING_SINGLE, "'hi")}, 197 | ""); 198 | } 199 | 200 | TEST(Lexer, TestBlockStringSpaces) 201 | { 202 | const char str[] = 203 | "|||\n" 204 | " test\n" 205 | " more\n" 206 | " |||\n" 207 | " foo\n" 208 | "|||"; 209 | const Token token = 210 | Token(Token::Kind::STRING_BLOCK, {}, "test\n more\n|||\n foo\n", " ", "", {}); 211 | testLex("block string spaces", str, {token}, ""); 212 | } 213 | 214 | TEST(Lexer, TestBlockStringTabs) 215 | { 216 | const char str[] = 217 | "|||\n" 218 | "\ttest\n" 219 | "\t more\n" 220 | "\t|||\n" 221 | "\t foo\n" 222 | "|||"; 223 | const Token token = 224 | Token(Token::Kind::STRING_BLOCK, {}, "test\n more\n|||\n foo\n", "\t", "", {}); 225 | testLex("block string tabs", str, {token}, ""); 226 | } 227 | 228 | TEST(Lexer, TestBlockStringsMixed) 229 | { 230 | const char str[] = 231 | "|||\n" 232 | "\t \ttest\n" 233 | "\t \t more\n" 234 | "\t \t|||\n" 235 | "\t \t foo\n" 236 | "|||"; 237 | const Token token = 238 | Token(Token::Kind::STRING_BLOCK, {}, "test\n more\n|||\n foo\n", "\t \t", "", {}); 239 | testLex("block string mixed", str, {token}, ""); 240 | } 241 | 242 | TEST(Lexer, TestBlockStringBlanks) 243 | { 244 | const char str[] = 245 | "|||\n\n" 246 | " test\n\n\n" 247 | " more\n" 248 | " |||\n" 249 | " foo\n" 250 | "|||"; 251 | const Token token = 252 | Token(Token::Kind::STRING_BLOCK, {}, "\ntest\n\n\n more\n|||\n foo\n", " ", "", {}); 253 | testLex("block string blanks", str, {token}, ""); 254 | } 255 | 256 | TEST(Lexer, TestBlockStringBadIndent) 257 | { 258 | const char str[] = 259 | "|||\n" 260 | " test\n" 261 | " foo\n" 262 | "|||"; 263 | testLex("block string bad indent", 264 | str, 265 | {}, 266 | "block string bad indent:1:1: text block not terminated with |||"); 267 | } 268 | 269 | TEST(Lexer, TestBlockStringEof) 270 | { 271 | const char str[] = 272 | "|||\n" 273 | " test"; 274 | testLex("block string eof", str, {}, "block string eof:1:1: unexpected EOF"); 275 | } 276 | 277 | TEST(Lexer, TestBlockStringNotTerm) 278 | { 279 | const char str[] = 280 | "|||\n" 281 | " test\n"; 282 | testLex("block string not term", 283 | str, 284 | {}, 285 | "block string not term:1:1: text block not terminated with |||"); 286 | } 287 | 288 | TEST(Lexer, TestBlockStringNoWs) 289 | { 290 | const char str[] = 291 | "|||\n" 292 | "test\n" 293 | "|||"; 294 | testLex("block string no ws", 295 | str, 296 | {}, 297 | "block string no ws:1:1: text block's first line must start with" 298 | " whitespace."); 299 | } 300 | 301 | TEST(Lexer, TestKeywords) 302 | { 303 | testLex("assert", "assert", {Token(Token::Kind::ASSERT, "assert")}, ""); 304 | testLex("else", "else", {Token(Token::Kind::ELSE, "else")}, ""); 305 | testLex("error", "error", {Token(Token::Kind::ERROR, "error")}, ""); 306 | testLex("false", "false", {Token(Token::Kind::FALSE, "false")}, ""); 307 | testLex("for", "for", {Token(Token::Kind::FOR, "for")}, ""); 308 | testLex("function", "function", {Token(Token::Kind::FUNCTION, "function")}, ""); 309 | testLex("if", "if", {Token(Token::Kind::IF, "if")}, ""); 310 | testLex("import", "import", {Token(Token::Kind::IMPORT, "import")}, ""); 311 | testLex("importstr", "importstr", {Token(Token::Kind::IMPORTSTR, "importstr")}, ""); 312 | testLex("in", "in", {Token(Token::Kind::IN, "in")}, ""); 313 | testLex("local", "local", {Token(Token::Kind::LOCAL, "local")}, ""); 314 | testLex("null", "null", {Token(Token::Kind::NULL_LIT, "null")}, ""); 315 | testLex("self", "self", {Token(Token::Kind::SELF, "self")}, ""); 316 | testLex("super", "super", {Token(Token::Kind::SUPER, "super")}, ""); 317 | testLex("tailstrict", "tailstrict", {Token(Token::Kind::TAILSTRICT, "tailstrict")}, ""); 318 | testLex("then", "then", {Token(Token::Kind::THEN, "then")}, ""); 319 | testLex("true", "true", {Token(Token::Kind::TRUE, "true")}, ""); 320 | } 321 | 322 | TEST(Lexer, TestIdentifier) 323 | { 324 | testLex("identifier", "foobar123", {Token(Token::Kind::IDENTIFIER, "foobar123")}, ""); 325 | testLex("identifier", 326 | "foo bar123", 327 | {Token(Token::Kind::IDENTIFIER, "foo"), Token(Token::Kind::IDENTIFIER, "bar123")}, 328 | ""); 329 | } 330 | 331 | TEST(Lexer, TestComments) 332 | { 333 | // TODO(dzc): Test does not look at fodder yet. 334 | testLex("c++ comment", "// hi", {}, ""); 335 | testLex("hash comment", "# hi", {}, ""); 336 | testLex("c comment", "/* hi */", {}, ""); 337 | testLex("c comment no term", 338 | "/* hi", 339 | {}, 340 | "c comment no term:1:1: multi-line comment has no terminating */."); 341 | } 342 | 343 | } // namespace 344 | -------------------------------------------------------------------------------- /libjsonnet/core/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 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "static_error.h" 30 | #include "unicode.h" 31 | 32 | /** Whitespace and comments. 33 | * 34 | * "Fodder" (as in cannon fodder) implies this data is expendable. 35 | */ 36 | struct FodderElement { 37 | enum Kind { 38 | /** The next token, paragraph, or interstitial should be on a new line. 39 | * 40 | * A single comment string is allowed, which flows before the new line. 41 | * 42 | * The LINE_END fodder specifies the indentation level and vertical spacing before whatever 43 | * comes next. 44 | */ 45 | LINE_END, 46 | 47 | /** A C-style comment that begins and ends on the same line. 48 | * 49 | * If it follows a token (i.e., it is the first fodder element) then it appears after the 50 | * token on the same line. If it follows another interstitial, it will also flow after it 51 | * on the same line. If it follows a new line or a paragraph, it is the first thing on the 52 | * following line, after the blank lines and indentation specified by the previous fodder. 53 | * 54 | * There is exactly one comment string. 55 | */ 56 | INTERSTITIAL, 57 | 58 | /** A comment consisting of at least one line. 59 | * 60 | * // and # style commes have exactly one line. C-style comments can have more than one 61 | * line. 62 | * 63 | * All lines of the comment are indented according to the indentation level of the previous 64 | * new line / paragraph fodder. 65 | * 66 | * The PARAGRAPH fodder specifies the indentation level and vertical spacing before whatever 67 | * comes next. 68 | */ 69 | PARAGRAPH, 70 | }; 71 | Kind kind; 72 | 73 | /** How many blank lines (vertical space) before the next fodder / token. */ 74 | unsigned blanks; 75 | 76 | /** How far the next fodder / token should be indented. */ 77 | unsigned indent; 78 | 79 | /** Whatever comments are part of this fodder. 80 | * 81 | * Constraints apply. See Kind, above. 82 | * 83 | * The strings include any delimiting characters, e.g. // # and C-style comment delimiters but 84 | * not newline characters or indentation. 85 | */ 86 | std::vector comment; 87 | 88 | FodderElement(Kind kind, unsigned blanks, unsigned indent, 89 | const std::vector &comment) 90 | : kind(kind), blanks(blanks), indent(indent), comment(comment) 91 | { 92 | assert(kind != LINE_END || comment.size() <= 1); 93 | assert(kind != INTERSTITIAL || (blanks == 0 && indent == 0 && comment.size() == 1)); 94 | assert(kind != PARAGRAPH || comment.size() >= 1); 95 | } 96 | }; 97 | 98 | static inline std::ostream &operator<<(std::ostream &o, const FodderElement &f) 99 | { 100 | switch (f.kind) { 101 | case FodderElement::LINE_END: 102 | o << "END(" << f.blanks << ", " << f.indent; 103 | if (!f.comment.empty()) { 104 | o << ", " << f.comment[0]; 105 | } 106 | o << ")"; 107 | break; 108 | case FodderElement::INTERSTITIAL: 109 | o << "INT(" << f.blanks << ", " << f.indent << ", " << f.comment[0] << ")"; 110 | break; 111 | case FodderElement::PARAGRAPH: 112 | o << "PAR(" << f.blanks << ", " << f.indent << ", " << f.comment[0] << "...)"; 113 | break; 114 | } 115 | return o; 116 | } 117 | 118 | /** A sequence of fodder elements, typically between two tokens. 119 | * 120 | * A LINE_END is not allowed to follow a PARAGRAPH or a LINE_END. This can be represented by 121 | * replacing the indent of the prior fodder and increasing the number of blank lines if necessary. 122 | * If there was a comment, it can be represented by changing the LINE_END to a paragraph containing 123 | * the same single comment string. 124 | * 125 | * There must be a LINE_END or a PARAGRAPH before a PARAGRAPH. 126 | * 127 | * TODO(sbarzowski) Make it a proper class 128 | */ 129 | typedef std::vector Fodder; 130 | 131 | static inline bool fodder_has_clean_endline(const Fodder &fodder) 132 | { 133 | return !fodder.empty() && fodder.back().kind != FodderElement::INTERSTITIAL; 134 | } 135 | 136 | /** As a.push_back(elem) but preserves constraints. 137 | * 138 | * See concat_fodder below. 139 | */ 140 | static inline void fodder_push_back(Fodder &a, const FodderElement &elem) 141 | { 142 | if (fodder_has_clean_endline(a) && elem.kind == FodderElement::LINE_END) { 143 | if (elem.comment.size() > 0) { 144 | // The line end had a comment, so create a single line paragraph for it. 145 | a.emplace_back(FodderElement::PARAGRAPH, elem.blanks, elem.indent, elem.comment); 146 | } else { 147 | // Merge it into the previous line end. 148 | a.back().indent = elem.indent; 149 | a.back().blanks += elem.blanks; 150 | } 151 | } else { 152 | if (!fodder_has_clean_endline(a) && elem.kind == FodderElement::PARAGRAPH) { 153 | a.emplace_back(FodderElement::LINE_END, 0, elem.indent, std::vector()); 154 | } 155 | a.push_back(elem); 156 | } 157 | } 158 | 159 | /** As a + b but preserves constraints. 160 | * 161 | * Namely, a LINE_END is not allowed to follow a PARAGRAPH or a LINE_END. 162 | */ 163 | static inline Fodder concat_fodder(const Fodder &a, const Fodder &b) 164 | { 165 | if (a.size() == 0) 166 | return b; 167 | if (b.size() == 0) 168 | return a; 169 | Fodder r = a; 170 | // Carefully add the first element of b. 171 | fodder_push_back(r, b[0]); 172 | // Add the rest of b. 173 | for (unsigned i = 1; i < b.size(); ++i) { 174 | r.push_back(b[i]); 175 | } 176 | return r; 177 | } 178 | 179 | /** Move b to the front of a. */ 180 | static inline void fodder_move_front(Fodder &a, Fodder &b) 181 | { 182 | a = concat_fodder(b, a); 183 | b.clear(); 184 | } 185 | 186 | static inline Fodder make_fodder(const FodderElement &elem) 187 | { 188 | Fodder fodder; 189 | fodder_push_back(fodder, elem); 190 | return fodder; 191 | } 192 | 193 | static inline void ensureCleanNewline(Fodder &fodder) 194 | { 195 | if (!fodder_has_clean_endline(fodder)) { 196 | fodder_push_back(fodder, FodderElement(FodderElement::Kind::LINE_END, 0, 0, {})); 197 | } 198 | } 199 | 200 | static inline int countNewlines(const FodderElement &elem) 201 | { 202 | switch (elem.kind) { 203 | case FodderElement::INTERSTITIAL: return 0; 204 | case FodderElement::LINE_END: return 1; 205 | case FodderElement::PARAGRAPH: return elem.comment.size() + elem.blanks; 206 | } 207 | std::cerr << "Unknown FodderElement kind" << std::endl; 208 | abort(); 209 | } 210 | 211 | static inline int countNewlines(const Fodder &fodder) 212 | { 213 | int sum = 0; 214 | for (const auto &elem : fodder) { 215 | sum += countNewlines(elem); 216 | } 217 | return sum; 218 | } 219 | 220 | static inline std::ostream &operator<<(std::ostream &o, const Fodder &fodder) 221 | { 222 | bool first = true; 223 | for (const auto &f : fodder) { 224 | o << (first ? "[" : ", "); 225 | first = false; 226 | o << f; 227 | } 228 | o << (first ? "[]" : "]"); 229 | return o; 230 | } 231 | 232 | struct Token { 233 | enum Kind { 234 | // Symbols 235 | BRACE_L, 236 | BRACE_R, 237 | BRACKET_L, 238 | BRACKET_R, 239 | COMMA, 240 | DOLLAR, 241 | DOT, 242 | PAREN_L, 243 | PAREN_R, 244 | SEMICOLON, 245 | 246 | // Arbitrary length lexemes 247 | IDENTIFIER, 248 | NUMBER, 249 | OPERATOR, 250 | STRING_DOUBLE, 251 | STRING_SINGLE, 252 | STRING_BLOCK, 253 | VERBATIM_STRING_SINGLE, 254 | VERBATIM_STRING_DOUBLE, 255 | 256 | // Keywords 257 | ASSERT, 258 | ELSE, 259 | ERROR, 260 | FALSE, 261 | FOR, 262 | FUNCTION, 263 | IF, 264 | IMPORT, 265 | IMPORTSTR, 266 | IN, 267 | LOCAL, 268 | NULL_LIT, 269 | TAILSTRICT, 270 | THEN, 271 | SELF, 272 | SUPER, 273 | TRUE, 274 | 275 | // A special token that holds line/column information about the end of the file. 276 | END_OF_FILE 277 | } kind; 278 | 279 | /** Fodder before this token. */ 280 | Fodder fodder; 281 | 282 | /** Content of the token if it wasn't a keyword. */ 283 | std::string data; 284 | 285 | /** If kind == STRING_BLOCK then stores the sequence of whitespace that indented the block. */ 286 | std::string stringBlockIndent; 287 | 288 | /** If kind == STRING_BLOCK then stores the sequence of whitespace that indented the end of 289 | * the block. 290 | * 291 | * This is always fewer whitespace characters than in stringBlockIndent. 292 | */ 293 | std::string stringBlockTermIndent; 294 | 295 | UString data32(void) const 296 | { 297 | return decode_utf8(data); 298 | } 299 | 300 | LocationRange location; 301 | 302 | Token(Kind kind, const Fodder &fodder, const std::string &data, 303 | const std::string &string_block_indent, const std::string &string_block_term_indent, 304 | const LocationRange &location) 305 | : kind(kind), 306 | fodder(fodder), 307 | data(data), 308 | stringBlockIndent(string_block_indent), 309 | stringBlockTermIndent(string_block_term_indent), 310 | location(location) 311 | { 312 | } 313 | 314 | Token(Kind kind, const std::string &data = "") : kind(kind), data(data) {} 315 | 316 | static const char *toString(Kind v) 317 | { 318 | switch (v) { 319 | case BRACE_L: return "\"{\""; 320 | case BRACE_R: return "\"}\""; 321 | case BRACKET_L: return "\"[\""; 322 | case BRACKET_R: return "\"]\""; 323 | case COMMA: return "\",\""; 324 | case DOLLAR: return "\"$\""; 325 | case DOT: return "\".\""; 326 | 327 | case PAREN_L: return "\"(\""; 328 | case PAREN_R: return "\")\""; 329 | case SEMICOLON: return "\";\""; 330 | 331 | case IDENTIFIER: return "IDENTIFIER"; 332 | case NUMBER: return "NUMBER"; 333 | case OPERATOR: return "OPERATOR"; 334 | case STRING_SINGLE: return "STRING_SINGLE"; 335 | case STRING_DOUBLE: return "STRING_DOUBLE"; 336 | case VERBATIM_STRING_SINGLE: return "VERBATIM_STRING_SINGLE"; 337 | case VERBATIM_STRING_DOUBLE: return "VERBATIM_STRING_DOUBLE"; 338 | case STRING_BLOCK: return "STRING_BLOCK"; 339 | 340 | case ASSERT: return "assert"; 341 | case ELSE: return "else"; 342 | case ERROR: return "error"; 343 | case FALSE: return "false"; 344 | case FOR: return "for"; 345 | case FUNCTION: return "function"; 346 | case IF: return "if"; 347 | case IMPORT: return "import"; 348 | case IMPORTSTR: return "importstr"; 349 | case IN: return "in"; 350 | case LOCAL: return "local"; 351 | case NULL_LIT: return "null"; 352 | case SELF: return "self"; 353 | case SUPER: return "super"; 354 | case TAILSTRICT: return "tailstrict"; 355 | case THEN: return "then"; 356 | case TRUE: return "true"; 357 | 358 | case END_OF_FILE: return "end of file"; 359 | default: 360 | std::cerr << "INTERNAL ERROR: Unknown token kind: " << v << std::endl; 361 | std::abort(); 362 | } 363 | } 364 | }; 365 | 366 | /** The result of lexing. 367 | * 368 | * Because of the EOF token, this will always contain at least one token. So element 0 can be used 369 | * to get the filename. 370 | */ 371 | typedef std::list Tokens; 372 | 373 | static inline bool operator==(const Token &a, const Token &b) 374 | { 375 | if (a.kind != b.kind) 376 | return false; 377 | if (a.data != b.data) 378 | return false; 379 | return true; 380 | } 381 | 382 | static inline std::ostream &operator<<(std::ostream &o, Token::Kind v) 383 | { 384 | o << Token::toString(v); 385 | return o; 386 | } 387 | 388 | static inline std::ostream &operator<<(std::ostream &o, const Token &v) 389 | { 390 | if (v.data == "") { 391 | o << Token::toString(v.kind); 392 | } else if (v.kind == Token::OPERATOR) { 393 | o << "\"" << v.data << "\""; 394 | } else { 395 | o << "(" << Token::toString(v.kind) << ", \"" << v.data << "\")"; 396 | } 397 | return o; 398 | } 399 | 400 | /** IF the given identifier is a keyword, return its kind, otherwise return IDENTIFIER. */ 401 | Token::Kind lex_get_keyword_kind(const std::string &identifier); 402 | 403 | Tokens jsonnet_lex(const std::string &filename, const char *input); 404 | 405 | std::string jsonnet_unlex(const Tokens &tokens); 406 | 407 | #endif // JSONNET_LEXER_H 408 | -------------------------------------------------------------------------------- /libjsonnet/core/parser_test.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 "parser.h" 18 | 19 | #include 20 | #include "ast.h" 21 | #include "gtest/gtest.h" 22 | #include "lexer.h" 23 | 24 | namespace { 25 | 26 | // Checks whether the provided snippet parses successfully. 27 | // TODO(dzc): Update this test to check the parsed AST against an expected AST. 28 | void testParse(const char* snippet) 29 | { 30 | try { 31 | std::list tokens = jsonnet_lex("test", snippet); 32 | Allocator allocator; 33 | AST* ast = jsonnet_parse(&allocator, tokens); 34 | (void)ast; 35 | } catch (StaticError& e) { 36 | ASSERT_TRUE(false) << "Static error: " << e.toString() << std::endl 37 | << "Snippet:" << std::endl 38 | << snippet << std::endl; 39 | } 40 | } 41 | 42 | TEST(Parser, TestLiterals) 43 | { 44 | testParse("true"); 45 | testParse("1"); 46 | testParse("1.2e3"); 47 | testParse("!true"); 48 | testParse("null"); 49 | testParse(R"("world")"); 50 | testParse(R"("world")"); 51 | testParse("|||\n world\n|||"); 52 | } 53 | 54 | TEST(Parser, TestExpressions) 55 | { 56 | testParse("$.foo.bar"); 57 | testParse("self.foo.bar"); 58 | testParse("super.foo.bar"); 59 | testParse("super[1]"); 60 | testParse(R"(error "Error!")"); 61 | 62 | testParse("foo(bar)"); 63 | testParse("foo(bar) tailstrict"); 64 | testParse("foo.bar"); 65 | testParse("foo[bar]"); 66 | 67 | testParse("true || false"); 68 | testParse("0 && 1 || 0"); 69 | testParse("0 && (1 || 0)"); 70 | } 71 | 72 | TEST(Parser, TestLocal) 73 | { 74 | testParse(R"(local foo = "bar"; foo)"); 75 | testParse("local foo(bar) = bar; foo(1)"); 76 | testParse(R"({ local foo = "bar", baz: 1})"); 77 | testParse("{ local foo(bar) = bar, baz: foo(1)}"); 78 | } 79 | 80 | TEST(Parser, TestArray) 81 | { 82 | testParse("[]"); 83 | testParse("[a, b, c]"); 84 | testParse("[x for x in [1,2,3] ]"); 85 | testParse("[x for x in [1,2,3] if x <= 2]"); 86 | testParse("[x+y for x in [1,2,3] if x <= 2 for y in [4, 5, 6]]"); 87 | } 88 | 89 | TEST(Parser, TestTuple) 90 | { 91 | testParse("{ foo(bar, baz): bar+baz }"); 92 | 93 | testParse(R"({ ["foo" + "bar"]: 3 })"); 94 | testParse(R"({ ["field" + x]: x for x in [1, 2, 3] })"); 95 | testParse(R"({ local y = x, ["field" + x]: x for x in [1, 2, 3] })"); 96 | testParse(R"({ ["field" + x]: x for x in [1, 2, 3] if x <= 2 })"); 97 | testParse(R"({ ["field" + x + y]:)" 98 | R"( x + y for x in [1, 2, 3] if x <= 2 for y in [4, 5, 6]})"); 99 | 100 | testParse("{}"); 101 | testParse(R"({ hello: "world" })"); 102 | testParse(R"({ hello +: "world" })"); 103 | testParse(R"({ 104 | hello: "world", 105 | "name":: joe, 106 | 'mood'::: "happy", 107 | ||| 108 | key type 109 | |||: "block", 110 | })"); 111 | 112 | testParse("assert true: 'woah!'; true"); 113 | testParse("{ assert true: 'woah!', foo: bar }"); 114 | 115 | testParse("if n > 1 then 'foos' else 'foo'"); 116 | 117 | testParse("local foo = function(x) x + 1; true"); 118 | 119 | testParse("import 'foo.jsonnet'"); 120 | testParse("importstr 'foo.text'"); 121 | 122 | testParse("{a: b} + {c: d}"); 123 | testParse("{a: b}{c: d}"); 124 | } 125 | 126 | void testParseError(const char* snippet, const std::string& expectedError) 127 | { 128 | try { 129 | std::list tokens = jsonnet_lex("test", snippet); 130 | Allocator allocator; 131 | AST* ast = jsonnet_parse(&allocator, tokens); 132 | (void)ast; 133 | } catch (StaticError& e) { 134 | ASSERT_EQ(expectedError, e.toString()) << "Snippet:" << std::endl << snippet << std::endl; 135 | } 136 | } 137 | 138 | TEST(Parser, TestInvalidFunctionCall) 139 | { 140 | testParseError("function(a, b c)", 141 | "test:1:15: expected a comma before next function parameter."); 142 | testParseError("function(a, 1)", "test:1:13: could not parse parameter here."); 143 | testParseError("a b", R"(test:1:3: did not expect: (IDENTIFIER, "b"))"); 144 | testParseError("foo(a, bar(a b))", 145 | "test:1:14: expected a comma before next function argument."); 146 | } 147 | 148 | TEST(Parser, TestInvalidLocal) 149 | { 150 | testParseError("local", "test:1:6: expected token IDENTIFIER but got end of file"); 151 | testParseError("local foo = 1, foo = 2; true", "test:1:16-19: duplicate local var: foo"); 152 | testParseError("local foo(a b) = a; true", 153 | "test:1:13: expected a comma before next function parameter."); 154 | testParseError("local foo(a): a; true", "test:1:13: expected operator = but got :"); 155 | testParseError("local foo(a) = bar(a b); true", 156 | "test:1:22: expected a comma before next function argument."); 157 | testParseError("local foo: 1; true", "test:1:10: expected operator = but got :"); 158 | testParseError("local foo = bar(a b); true", 159 | "test:1:19: expected a comma before next function argument."); 160 | 161 | testParseError("local a = b ()", "test:1:15: expected , or ; but got end of file"); 162 | testParseError("local a = b; (a b)", 163 | R"_(test:1:17: expected token ")" but got (IDENTIFIER, "b"))_"); 164 | } 165 | 166 | TEST(Parser, TestInvalidTuple) 167 | { 168 | testParseError("{a b}", 169 | R"(test:1:4: expected token OPERATOR but got (IDENTIFIER, "b"))"); 170 | testParseError("{a = b}", "test:1:2: expected one of :, ::, :::, +:, +::, +:::, got: ="); 171 | testParseError("{a :::: b}", "test:1:2: expected one of :, ::, :::, +:, +::, +:::, got: ::::"); 172 | } 173 | 174 | TEST(Parser, TestInvalidComprehension) 175 | { 176 | testParseError("{assert x for x in [1, 2, 3]}", 177 | "test:1:11-14: object comprehension cannot have asserts."); 178 | testParseError("{['foo' + x]: true, [x]: x for x in [1, 2, 3]}", 179 | "test:1:28-31: object comprehension can only have one field."); 180 | testParseError("{foo: x for x in [1, 2, 3]}", 181 | "test:1:9-12: object comprehensions can only have [e] fields."); 182 | testParseError("{[x]:: true for x in [1, 2, 3]}", 183 | "test:1:13-16: object comprehensions cannot have hidden fields."); 184 | testParseError("{[x]: true for 1 in [1, 2, 3]}", 185 | R"(test:1:16: expected token IDENTIFIER but got (NUMBER, "1"))"); 186 | testParseError("{[x]: true for x at [1, 2, 3]}", 187 | R"(test:1:18-20: expected token in but got (IDENTIFIER, "at"))"); 188 | testParseError("{[x]: true for x in [1, 2 3]}", 189 | "test:1:27: expected a comma before next array element."); 190 | testParseError("{[x]: true for x in [1, 2, 3] if (a b)}", 191 | R"_(test:1:37: expected token ")" but got (IDENTIFIER, "b"))_"); 192 | testParseError("{[x]: true for x in [1, 2, 3] if a b}", 193 | R"(test:1:36: expected for, if or "}" after for clause,)" 194 | R"( got: (IDENTIFIER, "b"))"); 195 | } 196 | 197 | TEST(Parser, TestInvalidNoComma) 198 | { 199 | testParseError("{a: b c:d}", "test:1:7: expected a comma before next field."); 200 | } 201 | 202 | TEST(Parser, TestInvalidArrayKey) 203 | { 204 | testParseError("{[(x y)]: z}", R"_(test:1:6: expected token ")" but got (IDENTIFIER, "y"))_"); 205 | testParseError("{[x y]: z}", R"(test:1:5: expected token "]" but got (IDENTIFIER, "y"))"); 206 | } 207 | 208 | TEST(Parser, TestInvalidFields) 209 | { 210 | testParseError("{foo(x y): z}", "test:1:8: expected a comma before next method parameter."); 211 | testParseError("{foo(x)+: z}", "test:1:2-5: cannot use +: syntax sugar in a method: foo"); 212 | testParseError("{foo: 1, foo: 2}", "test:1:10-13: duplicate field: foo"); 213 | testParseError("{foo: (1 2)}", R"_(test:1:10: expected token ")" but got (NUMBER, "2"))_"); 214 | } 215 | 216 | TEST(Parser, TestInvalidLocalInTuple) 217 | { 218 | testParseError("{local 1 = 3, true}", 219 | R"(test:1:8: expected token IDENTIFIER but got (NUMBER, "1"))"); 220 | testParseError("{local foo = 1, local foo = 2, true}", 221 | "test:1:23-26: duplicate local var: foo"); 222 | testParseError("{local foo(a b) = 1, a: true}", 223 | "test:1:14: expected a comma before next function parameter."); 224 | testParseError("{local foo(a): 1, a: true}", "test:1:14: expected operator = but got :"); 225 | testParseError("{local foo(a) = (a b), a: true}", 226 | R"_(test:1:20: expected token ")" but got (IDENTIFIER, "b"))_"); 227 | } 228 | 229 | TEST(Parser, TestInvalidAssertInTuple) 230 | { 231 | testParseError("{assert (a b), a: true}", 232 | R"_(test:1:12: expected token ")" but got (IDENTIFIER, "b"))_"); 233 | testParseError("{assert a: (a b), a: true}", 234 | R"_(test:1:15: expected token ")" but got (IDENTIFIER, "b"))_"); 235 | } 236 | 237 | TEST(Parser, TestInvalidUnexpectedFunction) 238 | { 239 | // TODO(jsonnet-team): The following error output differs from the Go 240 | // implementation, which is: 241 | // test:1:2-10 Unexpected: (function, "function") while parsing field 242 | // definition. 243 | testParseError("{function(a, b) a+b: true}", 244 | "test:1:2-10: unexpected: function while parsing field definition"); 245 | } 246 | 247 | TEST(Parser, TestInvalidArray) 248 | { 249 | testParseError("[(a b), 2, 3]", 250 | R"_(test:1:5: expected token ")" but got (IDENTIFIER, "b"))_"); 251 | testParseError("[1, (a b), 2, 3]", 252 | R"_(test:1:8: expected token ")" but got (IDENTIFIER, "b"))_"); 253 | testParseError("[a for b in [1 2 3]]", 254 | "test:1:16: expected a comma before next array element."); 255 | } 256 | 257 | TEST(Parser, TestInvalidExpression) 258 | { 259 | // TODO(jsonnet-team): The error output of the following differs from the Go 260 | // implementation, which is: 261 | // test:1:1-4 Unexpected: (for, "for") while parsing terminal) 262 | testParseError("for", "test:1:1-4: unexpected: for while parsing terminal"); 263 | testParseError("", "test:1:1: unexpected end of file."); 264 | testParseError("((a b))", R"_(test:1:5: expected token ")" but got (IDENTIFIER, "b"))_"); 265 | testParseError("a.1", R"(test:1:3: expected token IDENTIFIER but got (NUMBER, "1"))"); 266 | testParseError("super.1", R"(test:1:7: expected token IDENTIFIER but got (NUMBER, "1"))"); 267 | testParseError("super[(a b)]", R"_(test:1:10: expected token ")" but got (IDENTIFIER, "b"))_"); 268 | testParseError("super[a b]", R"(test:1:9: expected token "]" but got (IDENTIFIER, "b"))"); 269 | testParseError("super", "test:1:1-6: expected . or [ after super."); 270 | } 271 | 272 | TEST(Parser, TestInvalidAssert) 273 | { 274 | testParseError("assert (a b); true", 275 | R"_(test:1:11: expected token ")" but got (IDENTIFIER, "b"))_"); 276 | testParseError("assert a: (a b); true", 277 | R"_(test:1:14: expected token ")" but got (IDENTIFIER, "b"))_"); 278 | // TODO(jsonnet-team): The error output of this differs from the Go 279 | // implementation, which is: 280 | // test:1:16: expected token ";" but got (",", ",") 281 | testParseError("assert a: 'foo', true", 282 | R"(test:1:16: expected token ";" but got ",")"); 283 | testParseError("assert a: 'foo'; (a b)", 284 | R"_(test:1:21: expected token ")" but got (IDENTIFIER, "b"))_"); 285 | } 286 | 287 | TEST(Parser, TestInvalidError) 288 | { 289 | testParseError("error (a b)", R"_(test:1:10: expected token ")" but got (IDENTIFIER, "b"))_"); 290 | } 291 | 292 | TEST(Parser, TestInvalidIf) 293 | { 294 | testParseError("if (a b) then c", 295 | R"_(test:1:7: expected token ")" but got (IDENTIFIER, "b"))_"); 296 | testParseError("if a b c", 297 | R"(test:1:6: expected token then but got (IDENTIFIER, "b"))"); 298 | testParseError("if a then (b c)", 299 | R"_(test:1:14: expected token ")" but got (IDENTIFIER, "c"))_"); 300 | testParseError("if a then b else (c d)", 301 | R"_(test:1:21: expected token ")" but got (IDENTIFIER, "d"))_"); 302 | } 303 | 304 | TEST(Parser, TestInvalidFunction) 305 | { 306 | testParseError("function(a) (a b)", 307 | R"_(test:1:16: expected token ")" but got (IDENTIFIER, "b"))_"); 308 | testParseError("function a a", R"(test:1:10: expected ( but got (IDENTIFIER, "a"))"); 309 | } 310 | 311 | TEST(Parser, TestInvalidImport) 312 | { 313 | testParseError("import (a b)", R"_(test:1:11: expected token ")" but got (IDENTIFIER, "b"))_"); 314 | testParseError("import (a+b)", "test:1:8-13: computed imports are not allowed."); 315 | testParseError("importstr (a b)", 316 | R"_(test:1:14: expected token ")" but got (IDENTIFIER, "b"))_"); 317 | testParseError("importstr (a+b)", "test:1:11-16: computed imports are not allowed."); 318 | } 319 | 320 | TEST(Parser, TestInvalidOperator) 321 | { 322 | testParseError("1+ <<", "test:1:4-6: not a unary operator: <<"); 323 | testParseError("-(a b)", R"_(test:1:5: expected token ")" but got (IDENTIFIER, "b"))_"); 324 | testParseError("1~2", "test:1:2: not a binary operator: ~"); 325 | } 326 | 327 | TEST(Parser, TestInvalidArrayAccess) 328 | { 329 | testParseError("a[(b c)]", R"_(test:1:6: expected token ")" but got (IDENTIFIER, "c"))_"); 330 | // TODO(jsonnet-team): The error output of this differs from the Go 331 | // implementation, which is: 332 | // test:1:5: expected token "]" but got (IDENTIFIER, "c") 333 | testParseError("a[b c]", "test:1:5: unexpected: IDENTIFIER while parsing slice"); 334 | } 335 | 336 | TEST(Parser, TestInvalidOverride) 337 | { 338 | testParseError("a{b c}", R"(test:1:5: expected token OPERATOR but got (IDENTIFIER, "c"))"); 339 | } 340 | 341 | } // namespace 342 | --------------------------------------------------------------------------------