diff options
Diffstat (limited to 'ext/json/json_parser.y')
| -rw-r--r-- | ext/json/json_parser.y | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/ext/json/json_parser.y b/ext/json/json_parser.y new file mode 100644 index 0000000000..158bf1be80 --- /dev/null +++ b/ext/json/json_parser.y @@ -0,0 +1,242 @@ +%code top { +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Jakub Zelenka <bukka@php.net> | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "php_json.h" +#include "php_json_parser.h" + +#define YYDEBUG 0 + +#if YYDEBUG +int json_yydebug = 1; +#endif + +#define PHP_JSON_USE(uv) ((void) (uv)) +#define PHP_JSON_USE_1(uvr, uv1) PHP_JSON_USE(uvr); PHP_JSON_USE(uv1) +#define PHP_JSON_USE_2(uvr, uv1, uv2) PHP_JSON_USE(uvr); PHP_JSON_USE(uv1); PHP_JSON_USE(uv2) + +} + +%pure-parser +%name-prefix "php_json_yy" +%lex-param { php_json_parser *parser } +%parse-param { php_json_parser *parser } + +%union { + zval value; + struct { + zval key; + zval val; + } pair; + HashTable *ht; +} + + +%token <value> PHP_JSON_T_NUL +%token <value> PHP_JSON_T_TRUE +%token <value> PHP_JSON_T_FALSE +%token <value> PHP_JSON_T_INT +%token <value> PHP_JSON_T_DOUBLE +%token <value> PHP_JSON_T_STRING +%token <value> PHP_JSON_T_ESTRING +%token <value> PHP_JSON_T_EOI +%token <value> PHP_JSON_T_ERROR + +%type <value> start object key value array errlex +%type <ht> members member elements element +%type <pair> pair + +%destructor { zval_dtor(&$$); } <value> +%destructor { zend_hash_destroy($$); FREE_HASHTABLE($$); } <ht> +%destructor { zval_dtor(&$$.key); zval_dtor(&$$.val); } <pair> + +%code { +int php_json_yylex(union YYSTYPE *value, php_json_parser *parser); +void php_json_yyerror(php_json_parser *parser, char const *msg); +void php_json_parser_object_to_zval(php_json_parser *parser, zval *zv, HashTable *ht); +void php_json_parser_array_to_zval(zval *zv, HashTable *ht); +void php_json_parser_ht_init(HashTable **ht, uint nSize); +void php_json_parser_ht_update(php_json_parser *parser, HashTable *ht, zval *zkey, zval *zvalue); +void php_json_parser_ht_append(HashTable *ht, zval *zvalue); + +#define PHP_JSON_DEPTH_DEC --parser->depth +#define PHP_JSON_DEPTH_INC \ + if (parser->max_depth && parser->depth >= parser->max_depth) { \ + parser->scanner.errcode = PHP_JSON_ERROR_DEPTH; \ + YYERROR; \ + } \ + ++parser->depth +} + +%% /* Rules */ + +start: + value PHP_JSON_T_EOI { $$ = $1; INIT_PZVAL_COPY(parser->return_value, &$1); PHP_JSON_USE($2); YYACCEPT; } + | value errlex { PHP_JSON_USE_2($$, $1, $2); } +; + +object: + '{' { PHP_JSON_DEPTH_INC; } members object_end { PHP_JSON_DEPTH_DEC; php_json_parser_object_to_zval(parser, &$$, $3); } +; + +object_end: + '}' + | ']' { parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH; YYERROR; } +; + +members: + /* empty */ { php_json_parser_ht_init(&$$, 0); } + | member +; + +member: + pair { php_json_parser_ht_init(&$$, 4); php_json_parser_ht_update(parser, $$, &$1.key, &$1.val); } + | member ',' pair { php_json_parser_ht_update(parser, $1, &$3.key, &$3.val); $$ = $1; } + | member errlex { PHP_JSON_USE_2($$, $1, $2); } +; + +pair: + key ':' value { $$.key = $1; $$.val = $3; } + | key errlex { PHP_JSON_USE_2($$, $1, $2); } +; + +array: + '[' { PHP_JSON_DEPTH_INC; } elements array_end { PHP_JSON_DEPTH_DEC; php_json_parser_array_to_zval(&$$, $3); } +; + +array_end: + ']' + | '}' { parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH; YYERROR; } +; + +elements: + /* empty */ { php_json_parser_ht_init(&$$, 0); } + | element +; + +element: + value { php_json_parser_ht_init(&$$, 4); php_json_parser_ht_append($$, &$1); } + | element ',' value { php_json_parser_ht_append($1, &$3); $$ = $1; } + | element errlex { PHP_JSON_USE_2($$, $1, $2); } +; + +key: + PHP_JSON_T_STRING + | PHP_JSON_T_ESTRING +; + +value: + object + | array + | PHP_JSON_T_STRING + | PHP_JSON_T_ESTRING + | PHP_JSON_T_INT + | PHP_JSON_T_DOUBLE + | PHP_JSON_T_NUL + | PHP_JSON_T_TRUE + | PHP_JSON_T_FALSE + | errlex +; + +errlex: + PHP_JSON_T_ERROR { PHP_JSON_USE_1($$, $1); YYERROR; } +; + +%% /* Functions */ + +void php_json_parser_init(php_json_parser *parser, zval *return_value, char *str, int str_len, long options, long max_depth TSRMLS_DC) +{ + memset(parser, 0, sizeof(php_json_parser)); + php_json_scanner_init(&parser->scanner, str, str_len, options); + parser->depth = 1; + parser->max_depth = max_depth; + parser->return_value = return_value; + TSRMLS_SET_CTX(parser->zts_ctx); +} + +php_json_error_code php_json_parser_error_code(php_json_parser *parser) +{ + return parser->scanner.errcode; +} + +void php_json_parser_object_to_zval(php_json_parser *parser, zval *zv, HashTable *ht) +{ + TSRMLS_FETCH_FROM_CTX(parser->zts_ctx); + + if (parser->scanner.options & PHP_JSON_OBJECT_AS_ARRAY) { + php_json_parser_array_to_zval(zv, ht); + } else { + object_and_properties_init(zv, zend_standard_class_def, ht); + } +} + +void php_json_parser_array_to_zval(zval *zv, HashTable *ht) +{ + Z_TYPE_P(zv) = IS_ARRAY; + Z_ARRVAL_P(zv) = ht; +} + +void php_json_parser_ht_init(HashTable **ht, uint nSize) +{ + ALLOC_HASHTABLE(*ht); + zend_hash_init(*ht, nSize, NULL, ZVAL_PTR_DTOR, 0); +} + +void php_json_parser_ht_update(php_json_parser *parser, HashTable *ht, zval *zkey, zval *zvalue) +{ + zval *data; + char *key = Z_STRVAL_P(zkey); + int key_len = Z_STRLEN_P(zkey)+1; + MAKE_STD_ZVAL(data); + ZVAL_ZVAL(data, zvalue, 0, 0); + + if (parser->scanner.options & PHP_JSON_OBJECT_AS_ARRAY) { + zend_symtable_update(ht, key, key_len, &data, sizeof(zval *), NULL); + } else { + if (key_len == 1) { + key = "_empty_"; + key_len = sizeof("_empty_"); + } + zend_hash_update(ht, key, key_len, &data, sizeof(zval *), NULL); + } + + zval_dtor(zkey); +} + +void php_json_parser_ht_append(HashTable *ht, zval *zvalue) +{ + zval *data; + MAKE_STD_ZVAL(data); + ZVAL_ZVAL(data, zvalue, 0, 0); + zend_hash_next_index_insert(ht, &data, sizeof(zval *), NULL); +} + +int php_json_yylex(union YYSTYPE *value, php_json_parser *parser) +{ + int token = php_json_scan(&parser->scanner); + value->value = parser->scanner.value; + return token; +} + +void php_json_yyerror(php_json_parser *parser, char const *msg) +{ + if (!parser->scanner.errcode) { + parser->scanner.errcode = PHP_JSON_ERROR_SYNTAX; + } +} |
