├── .gitignore ├── CREDITS ├── CodeTips └── JsonnetCodeTips.php ├── EXPERIMENTAL ├── LICENSE ├── README.md ├── config.m4 ├── config.w32 ├── include ├── Jsonnet.h └── php7_wrapper.h ├── jsonnet.c ├── libjsonnet ├── LICENSE ├── Makefile ├── README.md ├── cmd │ ├── BUILD │ └── jsonnet.cpp ├── core │ ├── BUILD │ ├── ast.h │ ├── desugarer.cpp │ ├── desugarer.h │ ├── formatter.cpp │ ├── formatter.h │ ├── json.h │ ├── lexer.cpp │ ├── lexer.h │ ├── lexer_test.cpp │ ├── libjsonnet.cpp │ ├── libjsonnet_test.cpp │ ├── libjsonnet_test.sh │ ├── libjsonnet_test_file.c │ ├── libjsonnet_test_snippet.c │ ├── parser.cpp │ ├── parser.h │ ├── parser_test.cpp │ ├── pass.cpp │ ├── pass.h │ ├── state.h │ ├── static_analysis.cpp │ ├── static_analysis.h │ ├── static_error.h │ ├── string_utils.cpp │ ├── string_utils.h │ ├── unicode.h │ ├── vm.cpp │ └── vm.h ├── cpp │ ├── BUILD │ └── libjsonnet++.cpp ├── include │ ├── BUILD │ ├── libjsonnet++.h │ └── libjsonnet.h ├── stdlib │ ├── BUILD │ ├── std.jsonnet │ └── to_c_array.cpp └── third_party │ └── md5 │ ├── BUILD │ ├── LICENSE │ ├── md5.cpp │ └── md5.h ├── package.xml ├── php_jsonnet.h └── test ├── bar_menu.1.jsonnet ├── bar_menu.2.jsonnet ├── bar_menu.3.jsonnet ├── bar_menu.5.jsonnet ├── bar_menu.6.jsonnet ├── martinis.jsonnet ├── test.jsonnet.php └── utf8.jsonnet /.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 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | Jsonnet 2 | Chitao Gao ( neeke@php.net ) -------------------------------------------------------------------------------- /CodeTips/JsonnetCodeTips.php: -------------------------------------------------------------------------------- 1 | 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 | 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_ */ -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/core/static_analysis.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | #include "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 | -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/test.jsonnet.php: -------------------------------------------------------------------------------- 1 | getCode()); 55 | var_dump($e->getMessage()); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /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 | } --------------------------------------------------------------------------------