Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ce84460fd | |||
| 25d7c6c0fc | |||
| a017b589cb | |||
| 69fca74d3c | |||
| d0473cd136 | |||
| 4581f8da8d | |||
| 67469cc575 | |||
| 01f48a394d | |||
| 5c261ed7ed | |||
| a3ea13fc03 | |||
| 0d9cebedc0 | |||
| f763438c47 | |||
| cceb4eda7a | |||
| 47da1d454d | |||
|
|
451e6f2710 | ||
|
|
d2af423057 | ||
|
|
d1a7c3cf9c | ||
|
|
0d226f3148 | ||
|
|
e77d20cae4 | ||
|
|
bf098ac96a | ||
|
|
ad2138ba5d | ||
|
|
924558c8c9 | ||
|
|
2cbc47a6bf | ||
|
|
8a6b323fec | ||
|
|
034917177b | ||
|
|
bb62727333 | ||
|
|
ecb3222605 | ||
|
|
df95db7882 | ||
|
|
6813cfc9ea | ||
|
|
92704ae8f0 | ||
|
|
d76252d473 | ||
|
|
a309b9e772 | ||
|
|
0b32905cb8 | ||
|
|
d7e8c5ccbb | ||
|
|
1b649fb582 | ||
|
|
a64e0e86ee | ||
|
|
44b4bd8b19 | ||
|
|
662f24ef9a | ||
|
|
040e5a5df9 | ||
|
|
95b5b2cb83 | ||
|
|
fd09eb3b20 | ||
|
|
7b643ff5c6 | ||
|
|
e6950db87c | ||
|
|
7395f41ccc | ||
|
|
16bb20a09f | ||
|
|
e708952c05 | ||
|
|
06cfc1d0b6 | ||
|
|
4bf41e8b42 | ||
|
|
5bebbe1d33 | ||
|
|
ee0a1d48da | ||
|
|
ed6e15db6e | ||
|
|
295c27029b | ||
|
|
7121f8a26e | ||
|
|
ccfb2a91ec | ||
|
|
f03495756f | ||
|
|
e82e469356 | ||
|
|
23dc0814a5 | ||
|
|
01b014f88e | ||
|
|
50137406e0 | ||
|
|
79f0430d2c | ||
|
|
c42ee430c9 | ||
|
|
4c94e11ff3 | ||
|
|
44fb59f602 | ||
|
|
a4d1a98a0e | ||
|
|
86de4d2bb3 | ||
|
|
437d6f7bd0 | ||
|
|
330b3956e5 | ||
|
|
968e74b182 | ||
|
|
7177386801 | ||
|
|
eaa02d5ab4 | ||
|
|
caaf29842d | ||
|
|
32db7680d7 | ||
|
|
d6213cfe1b | ||
|
|
f88bb0c4f2 | ||
|
|
841c179c24 | ||
|
|
52963dce22 | ||
|
|
6c526fc77c | ||
|
|
a345dbbff3 | ||
|
|
4387f9d39a | ||
|
|
8e454b3d5a | ||
|
|
21e4583827 | ||
|
|
e38245261e | ||
|
|
cc372b2941 | ||
|
|
e3d16e2112 | ||
|
|
dd76bc9253 | ||
|
|
e947976340 | ||
|
|
f2e4593847 | ||
|
|
bfd1777fef | ||
|
|
d6a5d3009b | ||
|
|
be11a8e482 | ||
|
|
466168f8a2 | ||
|
|
b60cfce6fc | ||
|
|
46e86e248b | ||
|
|
a8ba1cc2fb | ||
|
|
d27e8a8879 | ||
|
|
a17e19bbc0 | ||
|
|
b5002db578 | ||
|
|
821ca95610 | ||
|
|
67036521d3 | ||
|
|
c2f4131b63 | ||
|
|
637f20e6b7 | ||
|
|
91f810e404 | ||
|
|
65dabe16d9 | ||
|
|
5b0570f546 | ||
|
|
59413d6d48 | ||
|
|
26e3cf4b2c | ||
|
|
24f9f608a8 | ||
|
|
5c7be8fb66 | ||
|
|
f97826aac3 | ||
|
|
be714c29ab | ||
|
|
fcad8e2062 | ||
|
|
744021b967 | ||
|
|
c6241dc37c | ||
|
|
72f720000e | ||
|
|
2e9571115a | ||
|
|
3e1a785938 | ||
|
|
7b54b12e6e | ||
|
|
99cc9a5154 | ||
|
|
ffac4718e8 | ||
|
|
4b68e32298 | ||
|
|
07c07cc06b | ||
|
|
e15c54b7cc | ||
|
|
092a6497f8 | ||
|
|
88cdc61357 | ||
|
|
4be1d9b2d0 | ||
|
|
250c6b1e59 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -8,3 +8,6 @@ include/config.h
|
||||
makefile
|
||||
Makefile
|
||||
src/tester.c
|
||||
*.pc
|
||||
.cflags.tmp
|
||||
.ldflags.tmp
|
||||
|
||||
@ -1,39 +1,72 @@
|
||||
include(CMakeDependentOption)
|
||||
project(rjp)
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
project(rjp)
|
||||
set(rjp_VERSION_MAJOR 0)
|
||||
set(rjp_VERSION_MINOR 8)
|
||||
set(rjp_VERSION_MAJOR 1)
|
||||
set(rjp_VERSION_MINOR 1)
|
||||
set(rjp_VERSION_REVISION 0)
|
||||
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
|
||||
configure_file(
|
||||
"${INCLUDE_PATH}/config.h.in"
|
||||
"${INCLUDE_PATH}/config.h"
|
||||
)
|
||||
include_directories("${INCLUDE_PATH}")
|
||||
|
||||
option(ENABLE_DIAGNOSTICS "Print diagnostic messages when parsing json to help identify issues" ON)
|
||||
option(ENABLE_SHARED "Build shared library" OFF)
|
||||
set(RJP_LIBFLAGS "-lrjp")
|
||||
|
||||
set(SOURCE_LIST "src/input.c" "src/output.c" "src/rjp_array.c" "src/rjp.c" "src/rjp_object.c" "src/rjp_string.c" "src/tree.c")
|
||||
option(ENABLE_DIAGNOSTICS "Print diagnostic messages when parsing json to help identify issues" ON)
|
||||
option(ENABLE_SHARED "Build shared library" ON)
|
||||
option(ENABLE_C++ "Build C++ wrapper library" OFF)
|
||||
option(BUILD_TESTS "Build test programs" OFF)
|
||||
option(ENABLE_PROFILING "Enable asan" OFF)
|
||||
mark_as_advanced(ENABLE_PROFILING)
|
||||
|
||||
set(SOURCE_LIST "src/rjp_lex.c" "src/rjp_ordered_object.c" "src/rjp_unordered_object.c" "src/rjp_parse.c" "src/output.c" "src/rjp_array.c" "src/rjp.c" "src/rjp_object.c" "src/rjp_string.c" "src/tree.c")
|
||||
if(ENABLE_SHARED)
|
||||
add_library(rjp SHARED ${SOURCE_LIST})
|
||||
set_target_properties(rjp PROPERTIES SOVERSION "${rjp_VERSION_MAJOR}.${rjp_VERSION_MINOR}.${rjp_VERSION_REVISION}")
|
||||
set_target_properties(rjp PROPERTIES SOVERSION "${rjp_VERSION_MAJOR}.${rjp_VERSION_MINOR}")
|
||||
else()
|
||||
add_library(rjp STATIC ${SOURCE_LIST})
|
||||
endif()
|
||||
set_target_properties(rjp PROPERTIES VERSION "${rjp_VERSION_MAJOR}.${rjp_VERSION_MINOR}.${rjp_VERSION_REVISION}")
|
||||
|
||||
if(ENABLE_DIAGNOSTICS)
|
||||
set(RJP_ENABLE_DIAGNOSTICS 1)
|
||||
endif()
|
||||
if(ENABLE_PROFILING)
|
||||
target_compile_options(rjp PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
||||
target_link_options(rjp PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
||||
endif()
|
||||
|
||||
set_target_properties(rjp PROPERTIES PUBLIC_HEADER ${INCLUDE_PATH}/rjp.h)
|
||||
target_compile_options(rjp PRIVATE -Wall -Wextra -pedantic)
|
||||
if(ENABLE_DIAGNOSTICS)
|
||||
target_compile_definitions(rjp PRIVATE RJP_DIAGNOSTICS)
|
||||
endif()
|
||||
|
||||
install(TARGETS rjp
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/pc/rjp.pc"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
|
||||
)
|
||||
if(ENABLE_C++)
|
||||
add_subdirectory(rjp++)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
|
||||
--force-new-ctest-process
|
||||
--verbose
|
||||
--output-on-failure
|
||||
)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
configure_file(
|
||||
"${INCLUDE_PATH}/config.h.in"
|
||||
"${INCLUDE_PATH}/config.h"
|
||||
)
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/pc/rjp.pc.cmake.in"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/pc/rjp.pc"
|
||||
@ONLY
|
||||
)
|
||||
|
||||
add_custom_target(uninstall cat install_manifest.txt | xargs rm)
|
||||
|
||||
@ -3,10 +3,10 @@
|
||||
## About
|
||||
rjp (rexy's json parser) is a very simplistic json parser written in C. There is absolutely no reason for this to exist but I felt like making it :P
|
||||
|
||||
Currently only installs 2 files: librjp.a and rjp.h
|
||||
Also includes rjp++, a C++ wrapper around the core rjp library. Building of this library can be disabled with cmake and is currently impossible to build since I haven't released librexy to my public gitlab yet. Remind me to update this readme when I do release that library.
|
||||
|
||||
## Building
|
||||
Building will produce a static library by default but can be configured to build a shared library.
|
||||
Building will produce a shared library by default but can be configured to build a static library.
|
||||
|
||||
##### Run the following commands
|
||||
```
|
||||
|
||||
6
TODO
6
TODO
@ -1,6 +0,0 @@
|
||||
Change string handling to work with chunked reading
|
||||
Change numeral handling to work with chunked reading
|
||||
handle scientific notation
|
||||
|
||||
|
||||
modularize internal object management. eg tree.h should only be needed in object.c and tree.c
|
||||
621
doc/api
Normal file
621
doc/api
Normal file
@ -0,0 +1,621 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Control how rjp_to_json outputs
|
||||
*/
|
||||
typedef enum RJP_format_flag{
|
||||
RJP_FORMAT_NONE = 0,
|
||||
RJP_FORMAT_KEY_SPACES = 1,
|
||||
RJP_FORMAT_NEWLINES = 2,
|
||||
RJP_FORMAT_TABBED_LINES = RJP_FORMAT_NEWLINES | 4,
|
||||
RJP_FORMAT_COMMA_SPACES = 8,
|
||||
RJP_FORMAT_PRETTY = RJP_FORMAT_KEY_SPACES | RJP_FORMAT_NEWLINES | RJP_FORMAT_TABBED_LINES
|
||||
}RJP_format_flag;
|
||||
|
||||
/*
|
||||
* Control what extensions are allowed in rjp_parse
|
||||
*/
|
||||
typedef enum RJP_parse_flag{
|
||||
RJP_PARSE_NO_EXT = 0,
|
||||
RJP_PARSE_ALLOW_COMMENTS = 1,
|
||||
RJP_PARSE_ALLOW_TRAILING_COMMA = 2,
|
||||
RJP_PARSE_ALL_EXT = RJP_PARSE_ALLOW_COMMENTS | RJP_PARSE_ALLOW_TRAILING_COMMA
|
||||
}RJP_parse_flag;
|
||||
|
||||
/*
|
||||
* Different data types allowed in JSON. Used to determine
|
||||
* what type a given RJP_value holds.
|
||||
*/
|
||||
typedef enum RJP_data_type{
|
||||
rjp_json_object = 1,
|
||||
rjp_json_ordered_object = 3,
|
||||
rjp_json_string = 2,
|
||||
rjp_json_integer = 4,
|
||||
rjp_json_dfloat = 8,
|
||||
rjp_json_boolean = 16,
|
||||
rjp_json_array = 32,
|
||||
rjp_json_null = 64
|
||||
}RJP_data_type;
|
||||
|
||||
/*
|
||||
* Represent a C string
|
||||
@member value: NULL terminated C string
|
||||
@member length: Length of value excluding NULL terminator
|
||||
*/
|
||||
typedef struct RJP_string{
|
||||
char* value;
|
||||
RJP_index length;
|
||||
}RJP_string;
|
||||
|
||||
/*
|
||||
* Used to iterate over object members
|
||||
* Members should NOT be accessed directly by user
|
||||
*/
|
||||
typedef struct RJP_object_iterator{
|
||||
union{
|
||||
struct RJP_object_iterator_impl* it;
|
||||
RJP_value* current;
|
||||
};
|
||||
RJP_data_type type;
|
||||
}RJP_object_iterator;
|
||||
|
||||
/*
|
||||
* Used to iterate over array elements
|
||||
* Members should NOT be accessed directly by user
|
||||
*/
|
||||
typedef struct RJP_array_iterator{
|
||||
RJP_value* current;
|
||||
}RJP_array_iterator;
|
||||
|
||||
/*
|
||||
* Information needed by rjp_parse_cback
|
||||
@member read: callback to read JSON string from.
|
||||
required to retun number of bytes read.
|
||||
@member data: data to be passed to read callback function
|
||||
*/
|
||||
typedef struct RJP_parse_callback{
|
||||
int(*read)(char*,int,void*);
|
||||
void* data;
|
||||
}RJP_parse_callback;
|
||||
|
||||
/*
|
||||
* Retains enough information from a parse run to construct an error message and where the error occurred.
|
||||
@member parsestate: internal use only
|
||||
@member errcode: enumeration of what the error was
|
||||
@member row: what row of the input contained the error
|
||||
@member column: where in the row that the error occurred
|
||||
*/
|
||||
typedef struct RJP_parse_error{
|
||||
void* parsestate;
|
||||
int errcode;
|
||||
int row, column;
|
||||
}RJP_parse_error;
|
||||
|
||||
|
||||
/***************** NON OBJECT OPERATIONS *******************/
|
||||
/*
|
||||
* Allocate heap space for rjp to use. Use if the memory is to be freed
|
||||
* by rjp
|
||||
@param nbytes: number of bytes to allocate
|
||||
@returns: pointer to newly allocated memory
|
||||
*/
|
||||
void* rjp_alloc(RJP_index nbytes);
|
||||
|
||||
/*
|
||||
* Allocate heap space for rjp and initialize to 0.
|
||||
@param num: number of elements to allocate
|
||||
@param nbytes: number of bytes per element
|
||||
@returns: pointer to newly allocated memory
|
||||
*/
|
||||
void* rjp_calloc(RJP_index num, RJP_index nbytes);
|
||||
|
||||
/*
|
||||
* Free memory allocated by rjp_alloc or rjp_calloc
|
||||
@param dest: memory to free
|
||||
*/
|
||||
void rjp_free(void* dest);
|
||||
|
||||
/*
|
||||
* copy input string and add json escape sequences
|
||||
@param dest: output buffer
|
||||
@param src: source string
|
||||
@returns: length of string in dest excluding NULL terminator
|
||||
*/
|
||||
RJP_index rjp_escape_strcpy(char* dest, const char* src);
|
||||
|
||||
/*
|
||||
* length of string after escaping
|
||||
@param src: string to measure
|
||||
@returns: length of escaped string excluding NULL terminator
|
||||
*/
|
||||
RJP_index rjp_escape_strlen(const char* str);
|
||||
|
||||
/*
|
||||
* copy input string and add json escape sequences
|
||||
@param src: source string
|
||||
@returns: newly allocated string copy
|
||||
*/
|
||||
RJP_string rjp_escape(const char* src);
|
||||
|
||||
/***************** GENERIC OPERATIONS *******************/
|
||||
/*
|
||||
* Convert C string of json data into RJP's format using no extensions and without
|
||||
error reporting.
|
||||
@param str: input JSON
|
||||
@returns: pointer to root value or NULL on failure
|
||||
*/
|
||||
RJP_value* rjp_simple_parse(const char* str);
|
||||
/*
|
||||
* Convert C string consisting of json data into RJP's format
|
||||
@param str: input JSON
|
||||
@param flags: RJP_parse_flags OR'd together
|
||||
@returns: pointer to root value or NULL on failure
|
||||
*/
|
||||
RJP_value* rjp_parse(const char* str, int flags, RJP_parse_error* err);
|
||||
|
||||
/*
|
||||
* Read json data in using a user supplied callback and convert
|
||||
* it to RJP's format
|
||||
@param flags: RJP_parse_flags OR'd together
|
||||
@param cbacks: RJP_parse_callback with function/data used for reading
|
||||
@returns: pointer to root value or NULL on failure
|
||||
*/
|
||||
RJP_value* rjp_parse_cback(int flags, RJP_parse_callback* cbacks, RJP_parse_error* err);
|
||||
|
||||
/*
|
||||
* Allocate a string representing the error stored in RJP_parse_error structure
|
||||
@param err: pointer to error to stringify
|
||||
@returns: rjp_alloc'd pointer to string describing the error.
|
||||
Must be freed with rjp_free
|
||||
*/
|
||||
char* rjp_parse_error_to_string(const RJP_parse_error* err);
|
||||
/*
|
||||
* Cleanup a RJP_parse_error structure when finished
|
||||
Note: DO NOT call if rjp_parse* was successful. Error will contain garbage data and
|
||||
will cause a segfault.
|
||||
@param err: pointer to error which needs cleanup
|
||||
*/
|
||||
void rjp_delete_parse_error(RJP_parse_error* err);
|
||||
|
||||
/*
|
||||
* Convert RJP's representation to rjp_alloc'd JSON string
|
||||
@param root: RJP_value to print out
|
||||
@param pretty: RJP_format_flag on how to print
|
||||
@returns: rjp_alloc'd string with JSON data or NULL on error
|
||||
*/
|
||||
char* rjp_to_json(const RJP_value* root, int pretty);
|
||||
|
||||
/*
|
||||
* Create new RJP_value of null type
|
||||
@returns: are you stupid?
|
||||
*/
|
||||
RJP_value* rjp_new_null(void);
|
||||
/*
|
||||
* Create new RJP_value of int type
|
||||
@param val: really?
|
||||
@returns: are you stupid?
|
||||
*/
|
||||
RJP_value* rjp_new_int(RJP_int val);
|
||||
/*
|
||||
* Create new RJP_value of float type
|
||||
@param val: really?
|
||||
@returns: are you stupid?
|
||||
*/
|
||||
RJP_value* rjp_new_float(RJP_float val);
|
||||
/*
|
||||
* Create new RJP_value of bool type
|
||||
@param val: really?
|
||||
@returns: are you stupid?
|
||||
*/
|
||||
RJP_value* rjp_new_bool(RJP_bool val);
|
||||
/*
|
||||
* Create new RJP_value of string type, coping val
|
||||
@param val: really?
|
||||
@param length: c'mon
|
||||
@returns: are you stupid?
|
||||
*/
|
||||
RJP_value* rjp_new_string(const char* val, RJP_index length);
|
||||
/*
|
||||
* Create new RJP_value of string type, stealing value of val
|
||||
@param val: really?
|
||||
@param length: c'mon
|
||||
@returns: are you stupid?
|
||||
*/
|
||||
RJP_value* rjp_new_string_steal(char* val, RJP_index length);
|
||||
/*
|
||||
* Create new RJP_value of object type
|
||||
@returns: are you stupid?
|
||||
*/
|
||||
RJP_value* rjp_new_object(void);
|
||||
/*
|
||||
* Create new RJP_value of ordered object type
|
||||
@returns: are you stupid?
|
||||
*/
|
||||
RJP_value* rjp_new_ordered_object(void);
|
||||
/*
|
||||
* Create new RJP_value of array type
|
||||
@returns: are you stupid?
|
||||
*/
|
||||
RJP_value* rjp_new_array(void);
|
||||
|
||||
/*
|
||||
* Deallocate RJP_value and all its members/elements
|
||||
@param root: RJP_value to free
|
||||
*/
|
||||
void rjp_free_value(RJP_value* root);
|
||||
|
||||
/*
|
||||
* Deep copy RJP_value. When dest is not null, cleanup
|
||||
* and then fill the object pointed to by dest.
|
||||
* Otherwise allocate a new value.
|
||||
@param dest: value to copy to or NULL
|
||||
@param src: value to copy from
|
||||
@returns: pointer to dest or newly allocated copy of src
|
||||
*/
|
||||
RJP_value* rjp_copy_value(RJP_value* dest, const RJP_value* src);
|
||||
|
||||
/*
|
||||
* Steal resources from src and use them to populate dest
|
||||
* No copy is performed.
|
||||
@param dest: value to copy to
|
||||
@param src: value to copy from
|
||||
@returns: poniter to dest
|
||||
*/
|
||||
RJP_value* rjp_move_value(RJP_value* dest, RJP_value* src);
|
||||
|
||||
/*
|
||||
* change an RJP_value to null type
|
||||
@param v: RJP_value to set
|
||||
@returns: pointer to v
|
||||
*/
|
||||
RJP_value* rjp_set_null(RJP_value* v);
|
||||
/*
|
||||
* change an RJP_value to int type
|
||||
@param v: RJP_value to set
|
||||
@param val: really?
|
||||
@returns: pointer to v
|
||||
*/
|
||||
RJP_value* rjp_set_int(RJP_value* v, RJP_int val);
|
||||
/*
|
||||
* change an RJP_value to float type
|
||||
@param v: RJP_value to set
|
||||
@param val: really?
|
||||
@returns: pointer to v
|
||||
*/
|
||||
RJP_value* rjp_set_float(RJP_value* v, RJP_float val);
|
||||
/*
|
||||
* change an RJP_value to bool type
|
||||
@param v: RJP_value to set
|
||||
@param val: really?
|
||||
@returns: pointer to v
|
||||
*/
|
||||
RJP_value* rjp_set_bool(RJP_value* v, RJP_bool val);
|
||||
/*
|
||||
* change an RJP_value to string type, copying source
|
||||
@param v: RJP_value to set
|
||||
@param val: string to copy
|
||||
@param length: length of val
|
||||
@returns: pointer to v
|
||||
*/
|
||||
RJP_value* rjp_set_string(RJP_value* v, const char* val, RJP_index length);
|
||||
/*
|
||||
* change an RJP_value to string type, stealing source
|
||||
@param v: RJP_value to set
|
||||
@param val: string to steal
|
||||
@param length: length of val
|
||||
@returns: pointer to v
|
||||
*/
|
||||
RJP_value* rjp_set_string_steal(RJP_value* v, char* val, RJP_index length);
|
||||
/*
|
||||
* change an RJP_value to object type
|
||||
@param v: RJP_value to set
|
||||
@returns: pointer to v
|
||||
*/
|
||||
RJP_value* rjp_set_object(RJP_value* v);
|
||||
/*
|
||||
* change an RJP_value to ordered object type
|
||||
@param v: RJP_value to set
|
||||
@returns: pointer to v
|
||||
*/
|
||||
RJP_value* rjp_set_ordered_object(RJP_value* v);
|
||||
/*
|
||||
* change an RJP_value to array type
|
||||
@param v: RJP_value to set
|
||||
@returns: pointer to v
|
||||
*/
|
||||
RJP_value* rjp_set_array(RJP_value* v);
|
||||
|
||||
/*
|
||||
* Retrieve float value in value
|
||||
@param value: RJP_value to query
|
||||
@returns: duh
|
||||
*/
|
||||
RJP_float rjp_get_float(const RJP_value* value);
|
||||
/*
|
||||
* Retrieve int value in value
|
||||
@param value: RJP_value to query
|
||||
@returns: duh
|
||||
*/
|
||||
RJP_int rjp_get_int(const RJP_value* value);
|
||||
/*
|
||||
* Retrieve bool value in value
|
||||
@param value: RJP_value to query
|
||||
@returns: duh
|
||||
*/
|
||||
RJP_bool rjp_get_bool(const RJP_value* value);
|
||||
/*
|
||||
* Retrieve string value in value
|
||||
@param value: RJP_value to query
|
||||
@returns: RJP_string pointer with value and length inside
|
||||
*/
|
||||
RJP_string* rjp_get_string(RJP_value* value);
|
||||
/*
|
||||
* Retrieve float value in value
|
||||
@param value: RJP_value to query
|
||||
@returns: RJP_string pointer with value and length inside
|
||||
*/
|
||||
const RJP_string* rjp_get_cstring(const RJP_value* value);
|
||||
|
||||
|
||||
/***************** OBJECT/ARRAY SHARED OPERATIONS *******************/
|
||||
/*
|
||||
* Get pointer to parent of value
|
||||
@param element: value to query
|
||||
@returns: pointer to parent of element
|
||||
*/
|
||||
RJP_value* rjp_value_parent(const RJP_value* element);
|
||||
|
||||
/*
|
||||
* Return type of value
|
||||
@param value: value to query
|
||||
@returns: seek help if you can't figure this out
|
||||
*/
|
||||
RJP_data_type rjp_value_type(const RJP_value* value);
|
||||
|
||||
/***************** OBJECT OPERATIONS *******************/
|
||||
/*
|
||||
* create a member in an object
|
||||
@param dest: object to add to
|
||||
@param key: key to copy value of
|
||||
@param keylen: length of key
|
||||
@returns: pointer to new member
|
||||
*/
|
||||
RJP_value* rjp_new_member(RJP_value* dest, const char* key, RJP_index keylen);
|
||||
|
||||
/*
|
||||
* create a member in an object, stealing key
|
||||
@param dest: object to add to
|
||||
@param key: key to steal value from
|
||||
@param keylen: length of key
|
||||
@returns: pointer to new member
|
||||
*/
|
||||
RJP_value* rjp_new_member_steal_key(RJP_value* dest, char* key, RJP_index keylen);
|
||||
|
||||
/*
|
||||
* move a value into a new member in an object
|
||||
@param dest: object to add to
|
||||
@param key: key to copy value of
|
||||
@param keylen: length of key
|
||||
@param src: value to move from
|
||||
@returns: pointer to new member
|
||||
*/
|
||||
RJP_value* rjp_add_member(RJP_value* dest, const char* key, RJP_index keylen, RJP_value* src);
|
||||
|
||||
/*
|
||||
* move a value into a new meber in an object, stealing key
|
||||
@param dest: object to add to
|
||||
@param key: key to steal value from
|
||||
@param keylen: length of key
|
||||
@param src: value to move from
|
||||
@returns: pointer to new member
|
||||
*/
|
||||
RJP_value* rjp_add_member_steal_key(RJP_value* dest, char* key, RJP_index keylen, RJP_value* src);
|
||||
|
||||
/*
|
||||
* Remove member without freeing
|
||||
@param obj: object to remove from
|
||||
@param key: key to search for
|
||||
@returns: pointer to former member
|
||||
*/
|
||||
RJP_value* rjp_remove_member_by_key(RJP_value* obj, const char* key);
|
||||
|
||||
/*
|
||||
* Remove member without freeing
|
||||
@param obj: object to remove from
|
||||
@param member: pointer to object's member
|
||||
@returns: pointer to former member
|
||||
*/
|
||||
RJP_value* rjp_remove_member(RJP_value* obj, RJP_value* member);
|
||||
|
||||
/*
|
||||
* Remove and free member
|
||||
@param obj: object to remove from
|
||||
@param key: key to search for
|
||||
*/
|
||||
void rjp_free_member_by_key(RJP_value* obj, const char* key);
|
||||
|
||||
/*
|
||||
* Remove and free member
|
||||
@param obj: object to remove from
|
||||
@param member: pointer to object's member
|
||||
*/
|
||||
void rjp_free_member(RJP_value* obj, RJP_value* member);
|
||||
|
||||
/*
|
||||
* set existing object member's key. Steal's value of parameter.
|
||||
* key must be allocated using rjp_alloc/rjp_calloc
|
||||
@param dest: member to set key of
|
||||
@param key: key to steal value of
|
||||
@param keylen: length of key
|
||||
*/
|
||||
void rjp_set_key_steal(RJP_value* dest, char* key, RJP_index keylen);
|
||||
|
||||
/*
|
||||
* set existing object member's key. Copies key parameter
|
||||
@param dest: member to set key of
|
||||
@param key: key to copy value of
|
||||
@param keylen: length of key
|
||||
*/
|
||||
void rjp_set_key(RJP_value* dest, const char* key, RJP_index keylen);
|
||||
|
||||
/*
|
||||
* Return number of members in the object
|
||||
@param object: object to query
|
||||
@returns: dear god please help me
|
||||
*/
|
||||
RJP_index rjp_num_members(const RJP_value* object);
|
||||
|
||||
/*
|
||||
* Return the object member's key
|
||||
@param member: object member to query
|
||||
@returns: object member's key/length pair
|
||||
*/
|
||||
const RJP_string* rjp_member_key(const RJP_value* member);
|
||||
|
||||
/*
|
||||
* Search for an object member with given key
|
||||
@param object: object to search within
|
||||
@param search: key to search for
|
||||
@returns: pointer to member with given key or NULL if not found
|
||||
*/
|
||||
RJP_value* rjp_search_member(const RJP_value* object, const char* search);
|
||||
|
||||
/*
|
||||
* Convert unordered object to ordered object
|
||||
@param object: object to convert
|
||||
@returns: pointer to object
|
||||
*/
|
||||
RJP_value* rjp_object_to_ordered(RJP_value* object);
|
||||
|
||||
/*
|
||||
* Convert ordered object to unordered object
|
||||
@param object: object to convert
|
||||
@returns: pointer to object
|
||||
*/
|
||||
RJP_value* rjp_object_to_unordered(RJP_value* object);
|
||||
|
||||
/***************** OBJECT ITERATOR OPERATIONS *******************/
|
||||
|
||||
/*
|
||||
* Initialize object iterator to iterate over given object
|
||||
@param iter: iterator to initialize
|
||||
@param object: object to iterate over
|
||||
*/
|
||||
void rjp_init_object_iterator(RJP_object_iterator* iter, const RJP_value* object);
|
||||
|
||||
/*
|
||||
* Delete object iterator
|
||||
@param it: iterator to delete
|
||||
*/
|
||||
void rjp_delete_object_iterator(RJP_object_iterator* it);
|
||||
|
||||
/*
|
||||
* Get current value in iterator
|
||||
@param it: iterator to query
|
||||
@returns: pointer to current value in iterator
|
||||
*/
|
||||
RJP_value* rjp_object_iterator_current(const RJP_object_iterator* it);
|
||||
|
||||
/*
|
||||
* Advance iterator to next value and return it
|
||||
@param it: iterator to query
|
||||
@returns: pointer to next value in iterator
|
||||
*/
|
||||
RJP_value* rjp_object_iterator_next(RJP_object_iterator* it);
|
||||
|
||||
/*
|
||||
* View next value in iterator without advancing
|
||||
@param it: iterator to query
|
||||
@returns: pointer to next value in iterator
|
||||
*/
|
||||
RJP_value* rjp_object_iterator_peek(const RJP_object_iterator* it);
|
||||
|
||||
/***************** ARRAY OPERATIONS *******************/
|
||||
/*
|
||||
* create an element in an array
|
||||
@param dest: array to add element to
|
||||
@returns: pointer to new element
|
||||
*/
|
||||
RJP_value* rjp_new_element(RJP_value* dest);
|
||||
|
||||
/*
|
||||
* create an element in an array and steal value in src
|
||||
@param arr: array to add element to
|
||||
@param src: value to steal contents of
|
||||
@returns: pointer to new element
|
||||
*/
|
||||
RJP_value* rjp_add_element(RJP_value* dest, RJP_value* src);
|
||||
|
||||
/*
|
||||
* remove element from array without freeing it
|
||||
@param arr: array to remove element from
|
||||
@param elem: element to remove
|
||||
@returns: pointer to former element
|
||||
*/
|
||||
RJP_value* rjp_remove_element(RJP_value* arr, RJP_value* elem);
|
||||
/*
|
||||
* remove and free element from array
|
||||
@param arr: array to remove element from
|
||||
@param elem: element to free
|
||||
*/
|
||||
void rjp_free_element(RJP_value* arr, RJP_value* elem);
|
||||
|
||||
/*
|
||||
* Get number of element in array
|
||||
@param src: array to query
|
||||
@returns: don't be gay
|
||||
*/
|
||||
RJP_index rjp_num_elements(const RJP_value* arr);
|
||||
|
||||
/***************** ARRAY ITERATOR OPERATIONS *******************/
|
||||
|
||||
/*
|
||||
* Initialize object iterator to iterate over given object
|
||||
@param iter: iterator to initialize
|
||||
@param array: array to iterate over
|
||||
@returns: you're bein gay
|
||||
*/
|
||||
void rjp_init_array_iterator(RJP_array_iterator* iter, const RJP_value* array);
|
||||
|
||||
/*
|
||||
* Delete array iterator
|
||||
@param iter: iterator to delete
|
||||
*/
|
||||
void rjp_delete_array_iterator(RJP_array_iterator* iter);
|
||||
|
||||
/*
|
||||
* Get current value in iterator
|
||||
@param it: iterator to query
|
||||
@returns: pointer to current value in iterator
|
||||
*/
|
||||
RJP_value* rjp_array_iterator_current(const RJP_array_iterator* it);
|
||||
|
||||
/*
|
||||
* Advance iterator to next value and return it
|
||||
@param it: iterator to query
|
||||
@returns: pointer to next value in iterator
|
||||
*/
|
||||
RJP_value* rjp_array_iterator_next(RJP_array_iterator* it);
|
||||
|
||||
/*
|
||||
* View next value in iterator without advancing
|
||||
@param it: iterator to query
|
||||
@returns: pointer to next value in iterator
|
||||
*/
|
||||
RJP_value* rjp_array_iterator_peek(const RJP_array_iterator* it);
|
||||
4
doc/examples/.gitignore
vendored
Normal file
4
doc/examples/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
output
|
||||
parse
|
||||
parse_file
|
||||
*.o
|
||||
14
doc/examples/makefile
Normal file
14
doc/examples/makefile
Normal file
@ -0,0 +1,14 @@
|
||||
.PHONY: all
|
||||
all: parse parse_file output
|
||||
|
||||
parse: parse.c
|
||||
gcc -ggdb -I../../include -L../../build parse.c -Wall -Wextra -pedantic -o parse -lrjp
|
||||
output: output.c
|
||||
gcc -ggdb -I../../include -L../../build output.c -Wall -Wextra -pedantic -o output -lrjp
|
||||
parse_file: parse_file.c
|
||||
gcc -ggdb -I../../include -L../../build parse_file.c -Wall -Wextra -pedantic -o parse_file -lrjp
|
||||
|
||||
clean:
|
||||
rm parse
|
||||
rm parse_file
|
||||
rm output
|
||||
19
doc/examples/output.c
Normal file
19
doc/examples/output.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include <rjp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(){
|
||||
//create an initial value
|
||||
RJP_value* root = rjp_new_object();
|
||||
|
||||
//add a member and set its type
|
||||
RJP_value* member = rjp_new_member(root, "key", 0);
|
||||
rjp_set_int(member, 5);
|
||||
|
||||
//output into rjp_alloc'd buffer
|
||||
char* output = rjp_to_json(root, RJP_FORMAT_NONE);
|
||||
printf("%s\n", output);
|
||||
|
||||
//free memory
|
||||
rjp_free(output);
|
||||
rjp_free_value(root);
|
||||
}
|
||||
36
doc/examples/parse.c
Normal file
36
doc/examples/parse.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include <rjp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv){
|
||||
if(argc != 2){
|
||||
fprintf(stderr, "Requires exactly 1 argument\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
//Read in json argument allowing all RJP extensions (comments, trailing comma)
|
||||
RJP_value* root = rjp_parse(argv[1], RJP_PARSE_ALL_EXT, NULL);
|
||||
|
||||
//returns NULL on error
|
||||
if(!root){
|
||||
fprintf(stderr, "Invalid JSON\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Valid JSON\n");
|
||||
|
||||
//check value's type
|
||||
if(rjp_value_type(root) == rjp_json_object){
|
||||
|
||||
//Initialize an object iterator for the root value
|
||||
RJP_object_iterator it;
|
||||
rjp_init_object_iterator(&it, root);
|
||||
|
||||
//iterate over all members of root, printing their keys
|
||||
for(RJP_value* curr = rjp_object_iterator_current(&it);curr;curr = rjp_object_iterator_next(&it)){
|
||||
printf("Have member with key \"%s\"\n", rjp_member_key(curr)->value);
|
||||
}
|
||||
rjp_delete_object_iterator(&it);
|
||||
}
|
||||
|
||||
//clean memory
|
||||
rjp_free_value(root);
|
||||
}
|
||||
37
doc/examples/parse_file.c
Normal file
37
doc/examples/parse_file.c
Normal file
@ -0,0 +1,37 @@
|
||||
#include <rjp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char** argv){
|
||||
if(argc != 2){
|
||||
fprintf(stderr, "Requires exactly 1 argument\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE* fp = fopen(argv[1], "rb");
|
||||
if(!fp){
|
||||
fprintf(stderr, "Unable to open file\n");
|
||||
return 1;
|
||||
}
|
||||
fseek(fp, 0, SEEK_END);
|
||||
int buflen = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
char* buffer = malloc(buflen+1);
|
||||
fread(buffer, 1, buflen, fp);
|
||||
buffer[buflen] = 0;
|
||||
fclose(fp);
|
||||
|
||||
//Read in json argument allowing all RJP extensions (comments, trailing comma)
|
||||
RJP_value* root = rjp_parse(buffer, RJP_PARSE_ALL_EXT, NULL);
|
||||
|
||||
//returns NULL on error
|
||||
if(!root){
|
||||
fprintf(stderr, "Invalid JSON\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Valid JSON\n");
|
||||
|
||||
//clean memory
|
||||
free(buffer);
|
||||
rjp_free_value(root);
|
||||
}
|
||||
@ -23,5 +23,7 @@
|
||||
#define RJP_VERSION_MINOR @rjp_VERSION_MINOR@
|
||||
#define RJP_VERSION_REVISION @rjp_VERSION_REVISION@
|
||||
|
||||
#cmakedefine RJP_ENABLE_DIAGNOSTICS
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
203
include/rjp.h
203
include/rjp.h
@ -16,121 +16,178 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_H
|
||||
#define RJP_H
|
||||
#ifndef RJP_H_INCLUDED
|
||||
#define RJP_H_INCLUDED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define DEPRECATED(str) __attribute__((deprecated(str)))
|
||||
#ifdef __cplusplus
|
||||
# if __cplusplus >= 201402L
|
||||
# define DEPRECATED(str) [[deprecated(str)]]
|
||||
# elif defined(__GNUC__) || defined(__clang__)
|
||||
# define DEPRECATED(str) __attribute__((deprecated(str)))
|
||||
# elif __cplusplus >= 201103L
|
||||
# define DEPRECATED(str) [[deprecated]]
|
||||
# else
|
||||
# define DEPRECATED(str)
|
||||
# endif
|
||||
#elif __STDC_VERSION__ > 201710L
|
||||
# define DEPRECATED(str) [[deprecated(str)]]
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
# define DEPRECATED(str) __attribute__((deprecated(str)))
|
||||
#else
|
||||
#define DEPRECATED(str)
|
||||
# define DEPRECATED(str)
|
||||
#endif
|
||||
|
||||
#ifndef RJP_int
|
||||
#include <stdint.h>
|
||||
#define RJP_int int_fast64_t
|
||||
# include <stdint.h>
|
||||
# define RJP_int int_fast64_t
|
||||
#endif
|
||||
#ifndef RJP_index
|
||||
#include <stddef.h>
|
||||
#define RJP_index size_t
|
||||
# include <stddef.h>
|
||||
# define RJP_index size_t
|
||||
#endif
|
||||
#ifndef RJP_float
|
||||
#include <stdint.h>
|
||||
#define RJP_float double
|
||||
# define RJP_float double
|
||||
#endif
|
||||
#ifndef RJP_bool
|
||||
#define RJP_bool _Bool
|
||||
# define RJP_bool char
|
||||
#endif
|
||||
|
||||
//used with rjp_to_json
|
||||
#define RJP_FORMAT_NONE 0
|
||||
#define RJP_FORMAT_PRETTY 1
|
||||
typedef enum RJP_format_flag{
|
||||
RJP_FORMAT_NONE = 0,
|
||||
RJP_FORMAT_KEY_SPACES = 1,
|
||||
RJP_FORMAT_NEWLINES = 2,
|
||||
RJP_FORMAT_TABBED_LINES = RJP_FORMAT_NEWLINES | 4,
|
||||
RJP_FORMAT_COMMA_SPACES = 8,
|
||||
RJP_FORMAT_PRETTY = RJP_FORMAT_KEY_SPACES | RJP_FORMAT_NEWLINES | RJP_FORMAT_TABBED_LINES
|
||||
}RJP_format_flag;
|
||||
|
||||
typedef enum RJP_parse_flag{
|
||||
RJP_PARSE_NO_EXT = 0,
|
||||
RJP_PARSE_ALLOW_COMMENTS = 1,
|
||||
RJP_PARSE_ALLOW_TRAILING_COMMA = 2,
|
||||
RJP_PARSE_ALL_EXT = RJP_PARSE_ALLOW_COMMENTS | RJP_PARSE_ALLOW_TRAILING_COMMA
|
||||
}RJP_parse_flag;
|
||||
|
||||
//type of data
|
||||
typedef enum RJP_data_type{
|
||||
rjp_json_object = 0,
|
||||
rjp_json_string,
|
||||
rjp_json_integer,
|
||||
rjp_json_dfloat,
|
||||
rjp_json_boolean,
|
||||
rjp_json_array,
|
||||
rjp_json_null
|
||||
rjp_json_object = 1,
|
||||
rjp_json_ordered_object = 3,
|
||||
rjp_json_string = 2,
|
||||
rjp_json_integer = 4,
|
||||
rjp_json_dfloat = 8,
|
||||
rjp_json_boolean = 16,
|
||||
rjp_json_array = 32,
|
||||
rjp_json_null = 64
|
||||
}RJP_data_type;
|
||||
|
||||
|
||||
typedef struct RJP_value RJP_value;
|
||||
|
||||
//Hold a C string and the length excluding NULL terminator
|
||||
typedef struct RJP_string{
|
||||
char* value;
|
||||
RJP_index length;
|
||||
}RJP_string;
|
||||
|
||||
//Forward declarations
|
||||
struct RJP_object_iterator_impl;
|
||||
typedef struct RJP_object_iterator{
|
||||
union{
|
||||
struct RJP_object_iterator_impl* it;
|
||||
RJP_value* current;
|
||||
};
|
||||
RJP_data_type type;
|
||||
}RJP_object_iterator;
|
||||
|
||||
typedef struct RJP_array_iterator{
|
||||
RJP_value* current;
|
||||
}RJP_array_iterator;
|
||||
|
||||
|
||||
typedef struct RJP_parse_callback{
|
||||
int(*read)(char*,int,void*);
|
||||
void* data;
|
||||
}RJP_parse_callback;
|
||||
typedef struct RJP_parse_error{
|
||||
void* parsestate;
|
||||
int errcode;
|
||||
int row, column;
|
||||
}RJP_parse_error;
|
||||
typedef struct RJP_memory_fns{
|
||||
void (*free)(void*);
|
||||
void* (*alloc)(RJP_index);
|
||||
}RJP_memory_fns;
|
||||
|
||||
/***************** NON OBJECT OPERATIONS *******************/
|
||||
//Allocate heap space for rjp to use. Use if the memory is to be freed by rjp
|
||||
void* rjp_alloc(RJP_index nbytes);
|
||||
//Allocate heap space for rjp and initialize to 0.
|
||||
void* rjp_calloc(RJP_index num, RJP_index nbytes);
|
||||
//Resize heap allocated space for rjp
|
||||
void* rjp_realloc(void* ptr, RJP_index nbytes);
|
||||
//Free memory allocated by rjp_alloc or rjp_calloc
|
||||
void rjp_free(void* dest);
|
||||
|
||||
//copy input string and add json escape sequences
|
||||
RJP_index rjp_escape_strcpy(char* dest, const char* src);
|
||||
//length of string including escape sequences
|
||||
RJP_index rjp_escape_strlen(const char* str);
|
||||
RJP_string rjp_escape(const char* src);
|
||||
|
||||
/***************** GENERIC OPERATIONS *******************/
|
||||
//Convert C string consisting of json data into RJP's format
|
||||
RJP_value* rjp_parse(const char* str);
|
||||
//RJP_value* rjp_parse_chunked(const char* str, RJP_value* prev_chunk);
|
||||
RJP_value* rjp_simple_parse(const char* str);
|
||||
RJP_value* rjp_parse(const char* str, int flags, RJP_parse_error* err);
|
||||
RJP_value* rjp_parse_cback(int flags, RJP_parse_callback* cbacks, RJP_parse_error* err);
|
||||
RJP_value* rjp_parse_c(const char* str, int flags, RJP_parse_error* err, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_parse_cback_c(int flags, RJP_parse_callback* cbacks, RJP_parse_error* err, const RJP_memory_fns* fns);
|
||||
char* rjp_to_json(const RJP_value* root, int pretty);
|
||||
char* rjp_to_json_c(const RJP_value* root, int pretty, const RJP_memory_fns* fns);
|
||||
|
||||
char* rjp_parse_error_to_string(const RJP_parse_error* err);
|
||||
char* rjp_parse_error_to_string_c(const RJP_parse_error* err, const RJP_memory_fns* fns);
|
||||
void rjp_delete_parse_error(RJP_parse_error* err);
|
||||
void rjp_delete_parse_error_c(RJP_parse_error* err, const RJP_memory_fns* fns);
|
||||
|
||||
//Initialize a root RJP_value to copy of value
|
||||
RJP_value* rjp_new_null(void);
|
||||
RJP_value* rjp_new_int(RJP_int val);
|
||||
RJP_value* rjp_new_float(RJP_float val);
|
||||
RJP_value* rjp_new_bool(RJP_bool val);
|
||||
RJP_value* rjp_new_string_copy(const char* val, RJP_index length);
|
||||
RJP_value* rjp_new_string(char* val, RJP_index length);
|
||||
RJP_value* rjp_new_string(const char* val, RJP_index length);
|
||||
RJP_value* rjp_new_string_steal(char* val, RJP_index length);
|
||||
RJP_value* rjp_new_object(void);
|
||||
RJP_value* rjp_new_ordered_object(void);
|
||||
RJP_value* rjp_new_array(void);
|
||||
|
||||
//Deallocate RJP_value
|
||||
void rjp_free_value(RJP_value* root);
|
||||
RJP_value* rjp_new_null_c(const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_new_int_c(RJP_int val, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_new_float_c(RJP_float val, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_new_bool_c(RJP_bool val, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_new_string_c(const char* val, RJP_index length, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_new_string_steal_c(char* val, RJP_index length, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_new_object_c(const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_new_ordered_object_c(const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_new_array_c(const RJP_memory_fns* fns);
|
||||
|
||||
void rjp_free_value(RJP_value* root);
|
||||
void rjp_free_value_c(RJP_value* root, const RJP_memory_fns* fns);
|
||||
|
||||
//Deep copy RJP_value
|
||||
//When dest is not null, cleanup and then fill the object pointed to by dest. Otherwise allocate a new value.
|
||||
RJP_value* rjp_copy_value(RJP_value* dest, const RJP_value* src);
|
||||
RJP_value* rjp_copy_value_c(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_move_value(RJP_value* dest, RJP_value* src);
|
||||
|
||||
//set existing value
|
||||
//initialize a RJP_value to the requested type and value
|
||||
RJP_value* rjp_set_null(RJP_value* v);
|
||||
RJP_value* rjp_set_int(RJP_value* v, RJP_int val);
|
||||
RJP_value* rjp_set_float(RJP_value* v, RJP_float val);
|
||||
RJP_value* rjp_set_bool(RJP_value* v, RJP_bool val);
|
||||
RJP_value* rjp_set_string_copy(RJP_value* v, const char* val, RJP_index length);
|
||||
RJP_value* rjp_set_string(RJP_value* v, char* val, RJP_index length);
|
||||
RJP_value* rjp_set_string(RJP_value* v, const char* val, RJP_index length);
|
||||
RJP_value* rjp_set_string_steal(RJP_value* v, char* val, RJP_index length);
|
||||
RJP_value* rjp_set_object(RJP_value* v);
|
||||
RJP_value* rjp_set_ordered_object(RJP_value* v);
|
||||
RJP_value* rjp_set_array(RJP_value* v);
|
||||
|
||||
RJP_value* rjp_set_null_c(RJP_value* v, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_set_int_c(RJP_value* v, RJP_int val, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_set_float_c(RJP_value* v, RJP_float val, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_set_bool_c(RJP_value* v, RJP_bool val, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_set_string_c(RJP_value* v, const char* val, RJP_index length, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_set_string_steal_c(RJP_value* v, char* val, RJP_index length, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_set_object_c(RJP_value* v, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_set_ordered_object_c(RJP_value* v, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_set_array_c(RJP_value* v, const RJP_memory_fns* fns);
|
||||
|
||||
RJP_float rjp_get_float(const RJP_value* value);
|
||||
RJP_int rjp_get_int(const RJP_value* value);
|
||||
RJP_bool rjp_get_bool(const RJP_value* value);
|
||||
@ -140,56 +197,66 @@ const RJP_string* rjp_get_cstring(const RJP_value* value);
|
||||
|
||||
/***************** OBJECT/ARRAY SHARED OPERATIONS *******************/
|
||||
RJP_value* rjp_value_parent(const RJP_value* element);
|
||||
//Return type of value
|
||||
RJP_data_type rjp_value_type(const RJP_value* value);
|
||||
|
||||
/***************** OBJECT OPERATIONS *******************/
|
||||
//add a member to a json object, allocating a copy of the key
|
||||
RJP_value* rjp_add_member_key_copy(RJP_value* dest, const char* key, RJP_index keylen);
|
||||
//add a member to a json object without allocation. key must be allocated using rjp_alloc/rjp_calloc
|
||||
RJP_value* rjp_add_member(RJP_value* dest, char* key, RJP_index keylen);
|
||||
RJP_value* rjp_new_member(RJP_value* dest, const char* key, RJP_index keylen);
|
||||
RJP_value* rjp_new_member_steal_key(RJP_value* dest, char* key, RJP_index keylen);
|
||||
RJP_value* rjp_add_member(RJP_value* dest, const char* key, RJP_index keylen, RJP_value* src);
|
||||
RJP_value* rjp_add_member_steal_key(RJP_value* dest, char* key, RJP_index keylen, RJP_value* src);
|
||||
|
||||
RJP_value* rjp_new_member_c(RJP_value* dest, const char* key, RJP_index keylen, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_new_member_steal_key_c(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_add_member_c(RJP_value* dest, const char* key, RJP_index keylen, RJP_value* src, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_add_member_steal_key_c(RJP_value* dest, char* key, RJP_index keylen, RJP_value* src, const RJP_memory_fns* fns);
|
||||
|
||||
//Remove member without freeing
|
||||
RJP_value* rjp_remove_member_by_key(RJP_value* obj, const char* key);
|
||||
RJP_value* rjp_remove_member(RJP_value* obj, RJP_value* member);
|
||||
//Remove and free member
|
||||
void rjp_free_member_by_key(RJP_value* obj, const char* key);
|
||||
void rjp_free_member(RJP_value* obj, RJP_value* member);
|
||||
RJP_value* rjp_remove_member_by_key_c(RJP_value* obj, const char* key, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_remove_member_c(RJP_value* obj, RJP_value* member, const RJP_memory_fns* fns);
|
||||
void rjp_free_member_by_key_c(RJP_value* obj, const char* key, const RJP_memory_fns* fns);
|
||||
void rjp_free_member_c(RJP_value* obj, RJP_value* member, const RJP_memory_fns* fns);
|
||||
|
||||
//set existing object member's key without allocation. key must be allocated using rjp_alloc/rjp_calloc
|
||||
void rjp_set_key(RJP_value* dest, char* key, RJP_index keylen);
|
||||
//set existing object member's key
|
||||
void rjp_set_key_copy(RJP_value* dest, const char* key, RJP_index keylen);
|
||||
RJP_value* rjp_set_key_steal(RJP_value* dest, char* key, RJP_index keylen);
|
||||
RJP_value* rjp_set_key(RJP_value* dest, const char* key, RJP_index keylen);
|
||||
RJP_value* rjp_set_key_steal_c(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_set_key_c(RJP_value* dest, const char* key, RJP_index keylen, const RJP_memory_fns* fns);
|
||||
|
||||
//Return number of members in the object
|
||||
RJP_index rjp_num_members(const RJP_value* object);
|
||||
//Return the object member's key
|
||||
const RJP_string* rjp_member_key(const RJP_value* member);
|
||||
//Search for an object member with given key
|
||||
RJP_value* rjp_search_member(const RJP_value* object, const char* search);
|
||||
|
||||
RJP_value* rjp_object_to_ordered(RJP_value* object);
|
||||
RJP_value* rjp_object_to_unordered(RJP_value* object);
|
||||
RJP_value* rjp_object_to_ordered_c(RJP_value* object, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_object_to_unordered_c(RJP_value* object, const RJP_memory_fns* fns);
|
||||
|
||||
/***************** OBJECT ITERATOR OPERATIONS *******************/
|
||||
//Access first member of a json object
|
||||
void rjp_init_object_iterator(RJP_object_iterator* iter, const RJP_value* object);
|
||||
void rjp_delete_object_iterator(RJP_object_iterator* it);
|
||||
RJP_value* rjp_object_iterator_current(RJP_object_iterator* it);
|
||||
RJP_value* rjp_object_iterator_current(const RJP_object_iterator* it);
|
||||
RJP_value* rjp_object_iterator_next(RJP_object_iterator* it);
|
||||
RJP_value* rjp_object_iterator_peek(RJP_object_iterator* it);
|
||||
RJP_value* rjp_object_iterator_peek(const RJP_object_iterator* it);
|
||||
|
||||
/***************** ARRAY OPERATIONS *******************/
|
||||
//add an element to a json array
|
||||
RJP_value* rjp_add_element(RJP_value* dest);
|
||||
RJP_value* rjp_new_element(RJP_value* dest);
|
||||
RJP_value* rjp_add_element(RJP_value* dest, RJP_value* src);
|
||||
RJP_value* rjp_new_element_c(RJP_value* dest, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_add_element_c(RJP_value* dest, RJP_value* src, const RJP_memory_fns* fns);
|
||||
RJP_value* rjp_remove_element(RJP_value* arr, RJP_value* elem);
|
||||
void rjp_free_element(RJP_value* arr, RJP_value* elem);
|
||||
void rjp_free_element_c(RJP_value* arr, RJP_value* elem, const RJP_memory_fns* fns);
|
||||
|
||||
RJP_index rjp_num_elements(const RJP_value* arr);
|
||||
|
||||
/***************** ARRAY ITERATOR OPERATIONS *******************/
|
||||
void rjp_init_array_iterator(RJP_array_iterator* iter, const RJP_value* array);
|
||||
void rjp_delete_array_iterator(RJP_array_iterator* iter);
|
||||
RJP_value* rjp_array_iterator_current(RJP_array_iterator* it);
|
||||
RJP_value* rjp_array_iterator_current(const RJP_array_iterator* it);
|
||||
RJP_value* rjp_array_iterator_next(RJP_array_iterator* it);
|
||||
RJP_value* rjp_array_iterator_peek(RJP_array_iterator* it);
|
||||
RJP_value* rjp_array_iterator_peek(const RJP_array_iterator* it);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@ -30,10 +30,9 @@ typedef struct RJP_array{
|
||||
RJP_index num_elements;
|
||||
}RJP_array;
|
||||
|
||||
void irjp_add_element(RJP_array* j);
|
||||
void irjp_delete_value(RJP_value* root);
|
||||
void irjp_copy_array(RJP_value* dest, const RJP_value* src);
|
||||
void irjp_add_element(RJP_array* j, const RJP_memory_fns* fns);
|
||||
void irjp_copy_array(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns);
|
||||
|
||||
RJP_index rjp_dump_array(const RJP_value* arr, char* dest);
|
||||
RJP_index irjp_dump_array(const RJP_value* arr, char* dest, const int flags, int depth);
|
||||
|
||||
#endif
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#define RJP_INTERNAL_H
|
||||
|
||||
#include "rjp.h"
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
@ -30,7 +31,7 @@
|
||||
|
||||
#define UNUSED_VARIABLE(thing) (void)(thing)
|
||||
|
||||
#ifdef RJP_DIAGNOSTICS
|
||||
#ifdef RJP_ENABLE_DIAGNOSTICS
|
||||
#define DIAG_PRINT(...) fprintf(__VA_ARGS__)
|
||||
#else
|
||||
#define DIAG_PRINT(...) irjp_ignore_unused(__VA_ARGS__)
|
||||
@ -40,16 +41,6 @@ static inline void irjp_ignore_unused(FILE* fp, ...){
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
RJP_value irjp_integer(RJP_int i);
|
||||
RJP_value irjp_boolean(RJP_bool b);
|
||||
RJP_value irjp_dfloat(RJP_float d);
|
||||
RJP_value irjp_string(char* c, RJP_index len);
|
||||
RJP_value irjp_string_copy(const char* c);
|
||||
RJP_value irjp_null(void);
|
||||
RJP_value irjp_object(void);
|
||||
RJP_value irjp_array(void);
|
||||
|
||||
extern const RJP_memory_fns irjp_default_memory_fns;
|
||||
|
||||
#endif
|
||||
|
||||
94
include/rjp_lex.h
Normal file
94
include/rjp_lex.h
Normal file
@ -0,0 +1,94 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_LEX_H
|
||||
#define RJP_LEX_H
|
||||
|
||||
#include "rjp.h"
|
||||
|
||||
#define RJP_LEX_CBACK_BUFFER_SIZE 512
|
||||
#define RJP_LEX_CBACK_STR_SIZE 64
|
||||
|
||||
#define RJP_LEX_TYPE_NORMAL 0
|
||||
#define RJP_LEX_TYPE_CBACK 1
|
||||
|
||||
#define rjp_lex_accept 1
|
||||
//DFA states. odd numbers are accepting states
|
||||
typedef enum RJP_lex_category{
|
||||
rjp_lex_start = 0,
|
||||
rjp_lex_obracket = 3,
|
||||
rjp_lex_obrace = 5,
|
||||
rjp_lex_cbracket = 7,
|
||||
rjp_lex_cbrace = 9,
|
||||
rjp_lex_spaces = 11,
|
||||
rjp_lex_quote = 12,
|
||||
rjp_lex_t = 14,
|
||||
rjp_lex_tr = 16,
|
||||
rjp_lex_tru = 18,
|
||||
rjp_lex_true = 19,
|
||||
rjp_lex_f = 20,
|
||||
rjp_lex_fa = 22,
|
||||
rjp_lex_fal = 24,
|
||||
rjp_lex_fals = 26,
|
||||
rjp_lex_false = 27,
|
||||
rjp_lex_n = 28,
|
||||
rjp_lex_nu = 30,
|
||||
rjp_lex_nul = 32,
|
||||
rjp_lex_null = 33,
|
||||
rjp_lex_escaped = 34,
|
||||
rjp_lex_string = 35,
|
||||
rjp_lex_comma = 37,
|
||||
rjp_lex_colon = 39,
|
||||
rjp_lex_number = 41,
|
||||
rjp_lex_decimal = 42,
|
||||
rjp_lex_fnumber = 43,
|
||||
rjp_lex_fnum_e = 44,
|
||||
rjp_lex_sci_num = 45,
|
||||
rjp_lex_slash = 46,
|
||||
rjp_lex_line_comment = 47,
|
||||
rjp_lex_signed_number = 49,
|
||||
rjp_lex_sci_num_signed = 51,
|
||||
rjp_lex_newlines = 53,
|
||||
rjp_lex_block_comment_start = 54,
|
||||
rjp_lex_block_comment_end1 = 56,
|
||||
rjp_lex_block_comment = 57,
|
||||
rjp_lex_invalid = 1000,
|
||||
rjp_lex_unrecognized_word = 1002,
|
||||
rjp_lex_end = 1004,
|
||||
}RJP_lex_category;
|
||||
|
||||
typedef struct RJP_lex_state{
|
||||
char* str; //must hold value parser will use to create tokens. eg contents of strings
|
||||
char* buff; //holds temporary data in callback based lexer
|
||||
RJP_index strcap; //capacity of str. used in callback lexer
|
||||
RJP_index buffl; //length of buff currently in use. used in callback lexer
|
||||
RJP_index buffcap; //capacity of buff. used in callback lexer
|
||||
RJP_index buffpos; //current position in buff being read. used in callback lexer
|
||||
RJP_lex_category node; //tracks current dfa state
|
||||
RJP_index length; //length of current token which parser will utilize
|
||||
RJP_index offset; //offset in the str buffer that the parser should start from. must be 0 in callback lexer
|
||||
int type;
|
||||
}RJP_lex_state;
|
||||
|
||||
void irjp_init_lex_cback_state(RJP_lex_state* state);
|
||||
void irjp_init_lex_state(RJP_lex_state* state);
|
||||
void irjp_delete_lex_state(RJP_lex_state* state);
|
||||
RJP_lex_category irjp_lex(RJP_lex_state* state);
|
||||
RJP_lex_category irjp_lex_cback(RJP_lex_state* state, RJP_parse_callback* cbacks);
|
||||
|
||||
#endif
|
||||
@ -19,18 +19,13 @@
|
||||
#ifndef RJP_OBJECT_H
|
||||
#define RJP_OBJECT_H
|
||||
|
||||
#include "rjp_unordered_object.h"
|
||||
#include "rjp_ordered_object.h"
|
||||
#include "rjp_internal.h"
|
||||
|
||||
struct RJP_tree_node;
|
||||
void irjp_copy_object(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns);
|
||||
void irjp_delete_object(RJP_value* obj, const RJP_memory_fns* fns);
|
||||
|
||||
typedef struct RJP_object{
|
||||
struct RJP_tree_node* root;
|
||||
RJP_index num_members;
|
||||
}RJP_object;
|
||||
|
||||
void irjp_copy_object(RJP_value* dest, const RJP_value* src);
|
||||
void irjp_delete_object(RJP_value* obj);
|
||||
|
||||
RJP_index rjp_dump_object(const RJP_value* root, char* dest);
|
||||
RJP_index irjp_dump_object(const RJP_value* root, char* dest, const int flags, int depth);
|
||||
|
||||
#endif
|
||||
|
||||
48
include/rjp_ordered_object.h
Normal file
48
include/rjp_ordered_object.h
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_ORDERED_OBJECT_H
|
||||
#define RJP_ORDERED_OBJECT_H
|
||||
|
||||
#include "rjp_internal.h"
|
||||
|
||||
struct RJP_ordered_member;
|
||||
|
||||
typedef struct RJP_ordered_object{
|
||||
struct RJP_ordered_member* members;
|
||||
struct RJP_ordered_member* last;
|
||||
RJP_index num_members;
|
||||
}RJP_ordered_object;
|
||||
|
||||
void irjp_copy_ordered_object(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns);
|
||||
void irjp_delete_ordered_object(RJP_value* obj, const RJP_memory_fns* fns);
|
||||
|
||||
RJP_value* irjp_add_ordered_member(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns);
|
||||
RJP_value* irjp_remove_ordered_member(RJP_value* obj, RJP_value* member, const RJP_memory_fns* fns);
|
||||
void irjp_ordered_set_key(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns);
|
||||
RJP_index irjp_ordered_num_members(const RJP_value* object);
|
||||
RJP_value* irjp_ordered_search_member(const RJP_value* object, const char* search);
|
||||
void irjp_init_ordered_object_iterator(RJP_object_iterator* it, const RJP_value* object);
|
||||
void irjp_delete_ordered_object_iterator(RJP_object_iterator* it);
|
||||
RJP_value* irjp_ordered_object_iterator_current(const RJP_object_iterator* it);
|
||||
RJP_value* irjp_ordered_object_iterator_next(RJP_object_iterator* it);
|
||||
RJP_value* irjp_ordered_object_iterator_peek(const RJP_object_iterator* it);
|
||||
|
||||
RJP_index irjp_dump_ordered_object(const RJP_value* root, char* dest);
|
||||
|
||||
#endif
|
||||
@ -20,13 +20,16 @@
|
||||
#define RJP_STRINGS_H
|
||||
|
||||
#include "rjp.h"
|
||||
#include "rjp_internal.h"
|
||||
|
||||
int irjp_is_whitespace(char c);
|
||||
char* irjp_parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column);
|
||||
RJP_index irjp_array_strlen(const RJP_value* arr);
|
||||
RJP_index irjp_object_strlen(const RJP_value* root);
|
||||
RJP_index irjp_value_strlen(const RJP_value* root);
|
||||
RJP_index irjp_value_strlen_pretty(const RJP_value* root, int depth);
|
||||
void irjp_strcpy(RJP_string* dest, const RJP_string* src);
|
||||
int irjp_copy_string_quoted(char* restrict dest, const char* restrict string, RJP_index length);
|
||||
int irjp_copy_string_keyed(char* restrict dest, const char* restrict string, RJP_index length);
|
||||
char* irjp_convert_string(const char* str, RJP_index length, RJP_index* newlen, const RJP_memory_fns* fns);
|
||||
RJP_index irjp_array_strlen(const RJP_value* arr, const int flags, int depth);
|
||||
RJP_index irjp_object_strlen(const RJP_value* root, const int flags, int depth);
|
||||
RJP_index irjp_value_strlen(const RJP_value* root, const int flags, int depth);
|
||||
void irjp_strcpy(RJP_string* dest, const RJP_string* src, const RJP_memory_fns* fns);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
48
include/rjp_unordered_object.h
Normal file
48
include/rjp_unordered_object.h
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_UNORDERED_OBJECT_H
|
||||
#define RJP_UNORDERED_OBJECT_H
|
||||
|
||||
#include "rjp_internal.h"
|
||||
|
||||
struct RJP_tree_node;
|
||||
struct RJP_object_member;
|
||||
|
||||
typedef struct RJP_unordered_object{
|
||||
struct RJP_tree_node* root;
|
||||
RJP_index num_members;
|
||||
}RJP_unordered_object;
|
||||
|
||||
void irjp_copy_unordered_object(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns);
|
||||
void irjp_delete_unordered_object(RJP_value* obj, const RJP_memory_fns* fns);
|
||||
RJP_value* irjp_add_unordered_member(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns);
|
||||
RJP_value* irjp_remove_unordered_member(RJP_value* obj, RJP_value* member, const RJP_memory_fns* fns);
|
||||
void irjp_unordered_set_key(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns);
|
||||
RJP_index irjp_unordered_num_members(const RJP_value* object);
|
||||
RJP_value* irjp_unordered_search_member(const RJP_value* object, const char* search);
|
||||
void irjp_init_unordered_object_iterator(RJP_object_iterator* it, const RJP_value* object);
|
||||
void irjp_delete_unordered_object_iterator(RJP_object_iterator* it);
|
||||
RJP_value* irjp_unordered_object_iterator_current(const RJP_object_iterator* it);
|
||||
RJP_value* irjp_unordered_object_iterator_next(RJP_object_iterator* it);
|
||||
RJP_value* irjp_unordered_object_iterator_peek(const RJP_object_iterator* it);
|
||||
|
||||
RJP_index irjp_dump_unordered_object(const RJP_value* root, char* dest);
|
||||
|
||||
|
||||
#endif
|
||||
@ -31,12 +31,15 @@ typedef struct RJP_value{
|
||||
RJP_int integer;
|
||||
RJP_float dfloat;
|
||||
RJP_bool boolean;
|
||||
struct RJP_object object;
|
||||
struct RJP_string string;
|
||||
struct RJP_array array;
|
||||
RJP_ordered_object orobject;
|
||||
RJP_unordered_object object;
|
||||
RJP_string string;
|
||||
RJP_array array;
|
||||
};
|
||||
struct RJP_value* parent; //pointer to parent (either an array or object or NULL)
|
||||
enum RJP_data_type type; //flag to determine active member of union
|
||||
}RJP_value;
|
||||
|
||||
void irjp_delete_value(RJP_value* root, const RJP_memory_fns* fns);
|
||||
|
||||
#endif
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
#define RJP_TREE_H
|
||||
|
||||
#include "rjp.h"
|
||||
#include "rjp_object.h"
|
||||
#include "rjp_object_member.h"
|
||||
#include "rjp_value.h"
|
||||
|
||||
#define RJP_TREE_SUCCESS 0
|
||||
#define RJP_TREE_ERR_NULL_ROOT 1
|
||||
@ -37,14 +37,12 @@ struct RJP_tree_node{
|
||||
unsigned color:1;
|
||||
};
|
||||
|
||||
RJP_tree_node* irjp_new_node(char* key, RJP_index keylen);
|
||||
RJP_tree_node* irjp_tree_insert_value(RJP_tree_node* root, char* key, RJP_index keylen, RJP_value** added, int* status);
|
||||
RJP_tree_node* irjp_tree_remove_value(RJP_tree_node* root, const char* key, RJP_value** removed_node, int* status);
|
||||
RJP_tree_node* irjp_new_node(char* key, RJP_index keylen, const RJP_memory_fns* fns);
|
||||
RJP_tree_node* irjp_tree_insert_value(RJP_tree_node* root, char* key, RJP_index keylen, RJP_value** added, int* status, const RJP_memory_fns* fns);
|
||||
RJP_tree_node* irjp_tree_insert_node(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode);
|
||||
RJP_tree_node* irjp_tree_remove_value(RJP_tree_node* restrict root, RJP_tree_node* restrict member, RJP_tree_node** removed_node);
|
||||
RJP_tree_node* irjp_tree_search_value(RJP_tree_node* root, const char* key);
|
||||
RJP_tree_node* irjp_copy_tree(const RJP_tree_node* root);
|
||||
void irjp_free_tree(RJP_tree_node* root);
|
||||
|
||||
void irjp_dbg_print_tree(RJP_tree_node* root);
|
||||
void irjp_dbg_print_tree_bfs(RJP_tree_node* root);
|
||||
RJP_tree_node* irjp_copy_tree(const RJP_tree_node* root, const RJP_memory_fns* fns);
|
||||
void irjp_free_tree(RJP_tree_node* root, const RJP_memory_fns* fns);
|
||||
|
||||
#endif
|
||||
|
||||
9
pc/rjp.pc.cmake.in
Normal file
9
pc/rjp.pc.cmake.in
Normal file
@ -0,0 +1,9 @@
|
||||
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||
|
||||
Name: rjp
|
||||
Description: Rexy's JSON Parser
|
||||
URL: https://gitlab.com/rexy712/rjp
|
||||
Version: @rjp_VERSION_MAJOR@.@rjp_VERSION_MINOR@.@rjp_VERSION_REVISION@
|
||||
Libs: -L${libdir} @RJP_LIBFLAGS@
|
||||
Cflags: -I${includedir}
|
||||
10
rjp++/.gitignore
vendored
Normal file
10
rjp++/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
src/test.cpp
|
||||
*.swp
|
||||
obj
|
||||
tester
|
||||
lib*.so
|
||||
lib*.a
|
||||
*.o
|
||||
test.cpp
|
||||
test
|
||||
*.pc
|
||||
70
rjp++/CMakeLists.txt
Normal file
70
rjp++/CMakeLists.txt
Normal file
@ -0,0 +1,70 @@
|
||||
project(rjp++)
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
find_package(PkgConfig)
|
||||
|
||||
set(rjp++_VERSION_MAJOR 0)
|
||||
set(rjp++_VERSION_MINOR 9)
|
||||
set(rjp++_VERSION_REVISION 0)
|
||||
set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
include_directories("${INCLUDE_PATH}")
|
||||
|
||||
option(ENABLE_SHARED "Build shared library" OFF)
|
||||
option(BUILD_TESTS "Build test programs" OFF)
|
||||
option(ENABLE_PROFILING "Enable asan" OFF)
|
||||
mark_as_advanced(ENABLE_PROFILING)
|
||||
|
||||
pkg_check_modules(LIBREXY REQUIRED librexy)
|
||||
|
||||
set(LIBREXY_REAL_LIBRARIES ${LIBREXY_LIBRARIES})
|
||||
list(TRANSFORM LIBREXY_REAL_LIBRARIES PREPEND "-l")
|
||||
|
||||
set(SOURCE_LIST "src/allocator.cpp" "src/array.cpp" "src/integral.cpp" "src/object.cpp" "src/rjp.cpp" "src/string.cpp" "src/string_val.cpp" "src/value.cpp" "src/vget_proxy.cpp" "src/container.cpp" "src/member.cpp")
|
||||
if(ENABLE_SHARED)
|
||||
set(RJP++_LIBFLAGS "-lrjp++ ${LIBREXY_REAL_LIBRARIES}")
|
||||
add_library(rjp++ SHARED ${SOURCE_LIST})
|
||||
set_target_properties(rjp++ PROPERTIES SOVERSION "${rjp_VERSION_MAJOR}.${rjp_VERSION_MINOR}.${rjp_VERSION_REVISION}")
|
||||
else()
|
||||
set(RJP++_LIBFLAGS "-lrjp++ -lrjp ${LIBREXY_REAL_LIBRARIES}")
|
||||
add_library(rjp++ STATIC ${SOURCE_LIST})
|
||||
endif()
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/pc/rjp++.pc.cmake.in"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/pc/rjp++.pc"
|
||||
@ONLY
|
||||
)
|
||||
set_target_properties(rjp++ PROPERTIES PUBLIC_HEADER ${INCLUDE_PATH}/rjp.hpp)
|
||||
set(CPP_HEADERS ${INCLUDE_PATH}/array.hpp ${INCLUDE_PATH}/integral.hpp ${INCLUDE_PATH}/iterator.hpp
|
||||
${INCLUDE_PATH}/member.hpp ${INCLUDE_PATH}/object.hpp ${INCLUDE_PATH}/parse.hpp
|
||||
${INCLUDE_PATH}/string.hpp ${INCLUDE_PATH}/string_val.hpp ${INCLUDE_PATH}/value.hpp
|
||||
${INCLUDE_PATH}/rjp_util.hpp ${INCLUDE_PATH}/dispatch.hpp ${INCLUDE_PATH}/container.hpp)
|
||||
|
||||
|
||||
if(ENABLE_PROFILING)
|
||||
target_compile_options(rjp++ PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
||||
target_link_options(rjp++ PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
||||
endif()
|
||||
if(BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
|
||||
target_compile_options(rjp++ PUBLIC -Wall -Wextra -pedantic -std=c++20 ${LIBREXY_CFLAGS_OTHER})
|
||||
target_include_directories(rjp++ PUBLIC ${LIBREXY_INCLUDE_DIRS})
|
||||
target_link_libraries(rjp++ PUBLIC rjp ${LIBREXY_LINK_LIBRARIES})
|
||||
|
||||
install(TARGETS rjp++
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
install(FILES ${CPP_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rjp++)
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/pc/rjp++.pc"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
|
||||
)
|
||||
|
||||
|
||||
add_custom_target(uninstall_rjp++ cat install_manifest.txt | xargs rm)
|
||||
117
rjp++/include/array.hpp
Normal file
117
rjp++/include/array.hpp
Normal file
@ -0,0 +1,117 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_ARRAY_HPP
|
||||
#define RJP_ARRAY_HPP
|
||||
|
||||
#include <rjp.h>
|
||||
#include <cstdlib> //size_t
|
||||
#include "iterator.hpp"
|
||||
#include "value.hpp"
|
||||
#include "integral.hpp"
|
||||
#include "string_val.hpp"
|
||||
#include "rjp_util.hpp"
|
||||
#include "container.hpp"
|
||||
|
||||
namespace rjp{
|
||||
|
||||
class object;
|
||||
|
||||
class array_iterator : protected detail::iterator_base
|
||||
{
|
||||
protected:
|
||||
RJP_array_iterator m_it;
|
||||
|
||||
public:
|
||||
array_iterator(void) = default;
|
||||
array_iterator(RJP_value* v);
|
||||
array_iterator(const array_iterator&) = delete;
|
||||
array_iterator(array_iterator&& a);
|
||||
~array_iterator(void);
|
||||
array_iterator& operator=(const array_iterator&) = delete;
|
||||
array_iterator& operator=(array_iterator&& o);
|
||||
|
||||
bool operator==(const array_iterator& o)const;
|
||||
bool operator!=(const array_iterator& o)const;
|
||||
value operator*(void)const;
|
||||
value_wrapper operator->(void)const;
|
||||
|
||||
array_iterator& operator++(void);
|
||||
};
|
||||
class array : public value, private container
|
||||
{
|
||||
public:
|
||||
using iterator = array_iterator;
|
||||
using const_iterator = const iterator;
|
||||
using size_type = size_t;
|
||||
public:
|
||||
using value::value;
|
||||
array(void);
|
||||
array(const value& val);
|
||||
array(value&& val);
|
||||
|
||||
array(const array&) = default;
|
||||
array(array&&) = default;
|
||||
~array(void) = default;
|
||||
array& operator=(const array&) = default;
|
||||
array& operator=(array&&) = default;
|
||||
|
||||
template<class Val>
|
||||
std::decay_t<Val> add(typename std::decay_t<Val>::underlying_type t){
|
||||
RJP_value* newelem = create_element(m_value);
|
||||
detail::set_to_underlying<std::decay_t<Val>>(newelem, t);
|
||||
return std::decay_t<Val>(create_unmanaged(newelem));
|
||||
}
|
||||
template<class Val = null>
|
||||
std::decay_t<Val> add(void){
|
||||
RJP_value* newelem = create_element(m_value);
|
||||
if constexpr(std::is_same<std::decay_t<Val>,rjp::array>::value)
|
||||
set_array_value(newelem);
|
||||
else if constexpr(std::is_same<std::decay_t<Val>,rjp::object>::value)
|
||||
set_object_value(newelem);
|
||||
else if constexpr(std::is_void<typename std::decay_t<Val>::underlying_type>::value)
|
||||
set_null_value(newelem);
|
||||
else
|
||||
detail::set_to_underlying<std::decay_t<Val>>(newelem, 0);
|
||||
return std::decay_t<Val>(create_unmanaged(newelem));
|
||||
}
|
||||
string_val add(const RJP_string&);
|
||||
string_val add(RJP_string&&);
|
||||
string_val add(const char*, RJP_index len = 0);
|
||||
string_val add(rexy::string_view);
|
||||
string_val add(string&&);
|
||||
|
||||
value& remove(value& val);
|
||||
void destroy(value&& val);
|
||||
|
||||
iterator begin(void);
|
||||
iterator end(void);
|
||||
const_iterator begin(void)const;
|
||||
const_iterator end(void)const;
|
||||
const_iterator cbegin(void)const;
|
||||
const_iterator cend(void)const;
|
||||
|
||||
size_type size(void)const;
|
||||
|
||||
private:
|
||||
static RJP_value* create_element(RJP_value* arr);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
35
rjp++/include/container.hpp
Normal file
35
rjp++/include/container.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_CONTAINER_HPP
|
||||
#define RJP_CONTAINER_HPP
|
||||
|
||||
#include <rjp.h>
|
||||
|
||||
namespace rjp{
|
||||
|
||||
struct container
|
||||
{
|
||||
static void set_array_value(RJP_value* v);
|
||||
static void set_object_value(RJP_value* v);
|
||||
static void set_null_value(RJP_value* v);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
158
rjp++/include/dispatch.hpp
Normal file
158
rjp++/include/dispatch.hpp
Normal file
@ -0,0 +1,158 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_DISPATCH_HPP
|
||||
#define RJP_DISPATCH_HPP
|
||||
|
||||
#include "object.hpp"
|
||||
#include "integral.hpp"
|
||||
#include "array.hpp"
|
||||
#include "value.hpp"
|
||||
#include <type_traits> //is_same, true_type, false_type
|
||||
#include <utility> //forward
|
||||
#include <exception>
|
||||
|
||||
namespace rjp{
|
||||
|
||||
class missing_dispatch_handler : public std::exception
|
||||
{
|
||||
public:
|
||||
const char* what(void)const noexcept override{
|
||||
return "Argument not handled in rjp::dispatch";
|
||||
}
|
||||
};
|
||||
namespace detail{
|
||||
template<RJP_data_type T>
|
||||
struct to_value_type;
|
||||
|
||||
template<>
|
||||
struct to_value_type<rjp_json_object>{
|
||||
using type = rjp::object;
|
||||
};
|
||||
template<>
|
||||
struct to_value_type<rjp_json_string>{
|
||||
using type = rjp::string_val;
|
||||
};
|
||||
template<>
|
||||
struct to_value_type<rjp_json_integer>{
|
||||
using type = rjp::integer;
|
||||
};
|
||||
template<>
|
||||
struct to_value_type<rjp_json_dfloat>{
|
||||
using type = rjp::dfloat;
|
||||
};
|
||||
template<>
|
||||
struct to_value_type<rjp_json_boolean>{
|
||||
using type = rjp::boolean;
|
||||
};
|
||||
template<>
|
||||
struct to_value_type<rjp_json_array>{
|
||||
using type = rjp::array;
|
||||
};
|
||||
template<>
|
||||
struct to_value_type<rjp_json_null>{
|
||||
using type = rjp::null;
|
||||
};
|
||||
template<RJP_data_type T>
|
||||
using to_value_type_t = typename to_value_type<T>::type;
|
||||
|
||||
template<RJP_data_type T>
|
||||
struct next_data_type;
|
||||
template<>
|
||||
struct next_data_type<rjp_json_object>{
|
||||
static constexpr RJP_data_type value = rjp_json_string;
|
||||
};
|
||||
template<>
|
||||
struct next_data_type<rjp_json_string>{
|
||||
static constexpr RJP_data_type value = rjp_json_integer;
|
||||
};
|
||||
template<>
|
||||
struct next_data_type<rjp_json_integer>{
|
||||
static constexpr RJP_data_type value = rjp_json_dfloat;
|
||||
};
|
||||
template<>
|
||||
struct next_data_type<rjp_json_dfloat>{
|
||||
static constexpr RJP_data_type value = rjp_json_boolean;
|
||||
};
|
||||
template<>
|
||||
struct next_data_type<rjp_json_boolean>{
|
||||
static constexpr RJP_data_type value = rjp_json_array;
|
||||
};
|
||||
template<>
|
||||
struct next_data_type<rjp_json_array>{
|
||||
static constexpr RJP_data_type value = rjp_json_null;
|
||||
};
|
||||
|
||||
template<RJP_data_type T>
|
||||
struct has_next_data_type_helper{
|
||||
template<class U, bool = (sizeof(U) == sizeof(U))>
|
||||
static std::true_type check(U*);
|
||||
static std::false_type check(...);
|
||||
|
||||
static constexpr bool value = std::is_same<std::true_type,decltype(check((next_data_type<T>*)0))>::value;
|
||||
};
|
||||
template<RJP_data_type T>
|
||||
struct has_next_data_type{
|
||||
static constexpr bool value = has_next_data_type_helper<T>::value;
|
||||
};
|
||||
|
||||
template<class T, class Param>
|
||||
struct has_operator_for{
|
||||
template<class U, class = decltype(std::declval<U>()(std::declval<Param>()))>
|
||||
static std::true_type check(U*);
|
||||
static std::false_type check(...);
|
||||
|
||||
static constexpr bool value = std::is_same<std::true_type,decltype(check((void*)0))>::value;
|
||||
};
|
||||
template<class Param, class T, class... Ts>
|
||||
struct has_operator_for_any{
|
||||
static constexpr bool value = has_operator_for<T, Param>::value || has_operator_for_any<Param,Ts...>::value;
|
||||
};
|
||||
template<class Param, class T>
|
||||
struct has_operator_for_any<Param,T>{
|
||||
static constexpr bool value = has_operator_for<T,Param>::value;
|
||||
};
|
||||
template<class Func, class Val, RJP_data_type T>
|
||||
decltype(auto) dispatch_helper(Func&& fun, Val&& v){
|
||||
if(v.type() == T){
|
||||
return std::forward<Func>(fun)(static_cast<detail::to_value_type_t<T>>(std::forward<Val>(v)));
|
||||
}
|
||||
if constexpr(detail::has_next_data_type<T>::value){
|
||||
return dispatch_helper<Func,Val,detail::next_data_type<T>::value>(std::forward<Func>(fun), std::forward<Val>(v));
|
||||
}
|
||||
throw missing_dispatch_handler{}; //deals with -Wreturn-type warnings
|
||||
}
|
||||
}
|
||||
template<class Func, class Val>
|
||||
decltype(auto) dispatch(Func&& fun, Val&& v){
|
||||
return detail::dispatch_helper<Func,Val,rjp_json_object>(std::forward<Func>(fun), std::forward<Val>(v));
|
||||
}
|
||||
template<class... Ts>
|
||||
struct dispatcher : public Ts...
|
||||
{
|
||||
using Ts::operator()...;
|
||||
|
||||
template<typename std::enable_if<!detail::has_operator_for_any<const value&,Ts...>::value,void>::type* = nullptr>
|
||||
decltype(auto) operator()(const value&)const{}
|
||||
};
|
||||
|
||||
template<class... Ts>
|
||||
dispatcher(Ts&&...) -> dispatcher<Ts...>;
|
||||
}
|
||||
|
||||
#endif
|
||||
114
rjp++/include/integral.hpp
Normal file
114
rjp++/include/integral.hpp
Normal file
@ -0,0 +1,114 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_INTEGRAL_HPP
|
||||
#define RJP_INTEGRAL_HPP
|
||||
|
||||
#include "value.hpp"
|
||||
#include <rjp.h>
|
||||
|
||||
namespace rjp{
|
||||
|
||||
class integer : public value
|
||||
{
|
||||
public:
|
||||
using underlying_type = RJP_int;
|
||||
public:
|
||||
using value::value;
|
||||
integer(underlying_type i);
|
||||
integer(void);
|
||||
integer(const value& val);
|
||||
integer(value&& val);
|
||||
integer(const value& val, underlying_type i);
|
||||
integer(value&& val, underlying_type i);
|
||||
|
||||
integer(const integer&) = default;
|
||||
integer(integer&&) = default;
|
||||
~integer(void) = default;
|
||||
integer& operator=(const integer&) = default;
|
||||
integer& operator=(integer&&) = default;
|
||||
|
||||
underlying_type get(void)const;
|
||||
void set(underlying_type i);
|
||||
|
||||
};
|
||||
class dfloat : public value
|
||||
{
|
||||
public:
|
||||
using underlying_type = RJP_float;
|
||||
public:
|
||||
using value::value;
|
||||
dfloat(underlying_type i);
|
||||
dfloat(void);
|
||||
dfloat(const value& val);
|
||||
dfloat(value&& val);
|
||||
dfloat(const value& val, underlying_type i);
|
||||
dfloat(value&& val, underlying_type i);
|
||||
|
||||
dfloat(const dfloat&) = default;
|
||||
dfloat(dfloat&&) = default;
|
||||
~dfloat(void) = default;
|
||||
dfloat& operator=(const dfloat&) = default;
|
||||
dfloat& operator=(dfloat&&) = default;
|
||||
|
||||
underlying_type get(void)const;
|
||||
void set(underlying_type i);
|
||||
};
|
||||
class boolean : public value
|
||||
{
|
||||
public:
|
||||
using underlying_type = RJP_bool;
|
||||
public:
|
||||
using value::value;
|
||||
boolean(underlying_type i);
|
||||
boolean(void);
|
||||
boolean(const value& val);
|
||||
boolean(value&& val);
|
||||
boolean(const value& val, underlying_type i);
|
||||
boolean(value&& val, underlying_type i);
|
||||
|
||||
boolean(const boolean&) = default;
|
||||
boolean(boolean&&) = default;
|
||||
~boolean(void) = default;
|
||||
boolean& operator=(const boolean&) = default;
|
||||
boolean& operator=(boolean&&) = default;
|
||||
|
||||
bool get(void)const;
|
||||
void set(bool i);
|
||||
};
|
||||
class null : public value
|
||||
{
|
||||
public:
|
||||
using underlying_type = void;
|
||||
public:
|
||||
using value::value;
|
||||
null(void);
|
||||
null(const value& val);
|
||||
null(value&& val);
|
||||
|
||||
null(const null&) = default;
|
||||
null(null&&) = default;
|
||||
~null(void) = default;
|
||||
null& operator=(const null&) = default;
|
||||
null& operator=(null&&) = default;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
45
rjp++/include/iterator.hpp
Normal file
45
rjp++/include/iterator.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_ITERATOR_BASE_HPP
|
||||
#define RJP_ITERATOR_BASE_HPP
|
||||
|
||||
#include <rjp.h>
|
||||
|
||||
#include "member.hpp"
|
||||
#include "value.hpp"
|
||||
|
||||
namespace rjp::detail{
|
||||
class iterator_base
|
||||
{
|
||||
protected:
|
||||
class value_wrapper
|
||||
{
|
||||
private:
|
||||
member<void> m_value;
|
||||
public:
|
||||
value_wrapper(RJP_value* value, bool managed):
|
||||
m_value(value, managed){}
|
||||
member<void>* operator->(void){
|
||||
return &m_value;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
89
rjp++/include/member.hpp
Normal file
89
rjp++/include/member.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_MEMBER_HPP
|
||||
#define RJP_MEMBER_HPP
|
||||
|
||||
#include "value.hpp"
|
||||
#include "string.hpp"
|
||||
#include <rexy/string.hpp>
|
||||
|
||||
#include <utility> //move
|
||||
|
||||
namespace rjp{
|
||||
|
||||
struct member_base
|
||||
{
|
||||
static const RJP_string* member_key(RJP_value* v);
|
||||
};
|
||||
template<class Type>
|
||||
class member : public Type, private member_base
|
||||
{
|
||||
public:
|
||||
member(void) = default;
|
||||
member(const member&) = default;
|
||||
member(member&&) = default;
|
||||
member(const Type& val):
|
||||
Type(val){}
|
||||
member(Type&& val):
|
||||
Type(std::move(val)){}
|
||||
member(const value& val):
|
||||
Type(val){}
|
||||
member(value&& val):
|
||||
Type(std::move(val)){}
|
||||
using Type::Type;
|
||||
~member(void) = default;
|
||||
|
||||
member& operator=(const member&) = default;
|
||||
member& operator=(member&&) = default;
|
||||
|
||||
rexy::string_view key(void)const{
|
||||
return rexy::string_view(member_key(this->m_value)->value, member_key(this->m_value)->length);
|
||||
}
|
||||
string steal_key(void){
|
||||
return string(this->m_value);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
class member<void> : public value, private member_base
|
||||
{
|
||||
public:
|
||||
member(void) = default;
|
||||
member(const member&) = default;
|
||||
member(member&&) = default;
|
||||
member(const value& val):
|
||||
value(val){}
|
||||
member(value&& val):
|
||||
value(std::move(val)){}
|
||||
using value::value;
|
||||
~member(void) = default;
|
||||
|
||||
member& operator=(const member&) = default;
|
||||
member& operator=(member&&) = default;
|
||||
|
||||
rexy::string_view key(void)const{
|
||||
return rexy::string_view(member_key(m_value)->value, member_key(m_value)->length);
|
||||
}
|
||||
string steal_key(void){
|
||||
return string(m_value);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
154
rjp++/include/object.hpp
Normal file
154
rjp++/include/object.hpp
Normal file
@ -0,0 +1,154 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_OBJECT_HPP
|
||||
#define RJP_OBJECT_HPP
|
||||
|
||||
#include <rjp.h>
|
||||
#include <rexy/string_base.hpp>
|
||||
#include <cstdlib> //size_t
|
||||
#include "value.hpp"
|
||||
#include "iterator.hpp"
|
||||
#include "member.hpp"
|
||||
#include "integral.hpp"
|
||||
#include "string_val.hpp"
|
||||
#include "rjp_util.hpp"
|
||||
#include "container.hpp"
|
||||
|
||||
namespace rjp{
|
||||
|
||||
class array;
|
||||
|
||||
class object_iterator : protected detail::iterator_base
|
||||
{
|
||||
protected:
|
||||
RJP_object_iterator m_it;
|
||||
|
||||
public:
|
||||
object_iterator(void);
|
||||
object_iterator(RJP_value* v);
|
||||
object_iterator(const object_iterator&) = delete;
|
||||
object_iterator(object_iterator&& o);
|
||||
~object_iterator(void);
|
||||
object_iterator& operator=(const object_iterator&) = delete;
|
||||
object_iterator& operator=(object_iterator&& o);
|
||||
|
||||
bool operator==(const object_iterator& o)const;
|
||||
bool operator!=(const object_iterator& o)const;
|
||||
member<void> operator*(void)const;
|
||||
value_wrapper operator->(void)const;
|
||||
|
||||
object_iterator& operator++(void);
|
||||
};
|
||||
|
||||
class object : public value, private container
|
||||
{
|
||||
public:
|
||||
using iterator = object_iterator;
|
||||
using const_iterator = const iterator;
|
||||
using size_type = size_t;
|
||||
public:
|
||||
using value::value;
|
||||
object(void);
|
||||
object(const value& val);
|
||||
object(value&& val);
|
||||
|
||||
object(const object&) = default;
|
||||
object(object&&) = default;
|
||||
~object(void) = default;
|
||||
object& operator=(const object&) = default;
|
||||
object& operator=(object&&) = default;
|
||||
|
||||
template<class Val>
|
||||
member<std::decay_t<Val>> add(rexy::string_view key, typename std::decay_t<Val>::underlying_type t){
|
||||
RJP_value* newmem = add_member_impl(key);
|
||||
detail::set_to_underlying<std::decay_t<Val>>(newmem, t);
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
template<class Val = null>
|
||||
member<std::decay_t<Val>> add(rexy::string_view key){
|
||||
RJP_value* newmem = add_member_impl(key);
|
||||
if constexpr(std::is_same<std::decay_t<Val>,rjp::array>::value)
|
||||
set_array_value(newmem);
|
||||
else if constexpr(std::is_same<std::decay_t<Val>,rjp::object>::value)
|
||||
set_object_value(newmem);
|
||||
else if constexpr(std::is_void<typename std::decay_t<Val>::underlying_type>::value)
|
||||
set_null_value(newmem);
|
||||
else
|
||||
detail::set_to_underlying<std::decay_t<Val>>(newmem, 0);
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
member<string_val> add(rexy::string_view key, const RJP_string&);
|
||||
member<string_val> add(rexy::string_view key, RJP_string&&);
|
||||
member<string_val> add(rexy::string_view key, const char*, RJP_index len = 0);
|
||||
member<string_val> add(rexy::string_view key, rexy::string_view);
|
||||
member<string_val> add(rexy::string_view key, string&&);
|
||||
|
||||
template<class Val>
|
||||
member<std::decay_t<Val>> add(string&& key, typename std::decay_t<Val>::underlying_type t){
|
||||
RJP_value* newmem = add_member_impl(std::move(key));
|
||||
detail::set_to_underlying<std::decay_t<Val>>(newmem, t);
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
template<class Val = null>
|
||||
member<std::decay_t<Val>> add(string&& key){
|
||||
RJP_value* newmem = add_member_impl(std::move(key));
|
||||
if constexpr(std::is_same<std::decay_t<Val>,rjp::array>::value)
|
||||
set_array_value(newmem);
|
||||
else if constexpr(std::is_same<std::decay_t<Val>,rjp::object>::value)
|
||||
set_object_value(newmem);
|
||||
else if constexpr(std::is_void<typename std::decay_t<Val>::underlying_type>::value)
|
||||
set_null_value(newmem);
|
||||
else
|
||||
detail::set_to_underlying<std::decay_t<Val>>(newmem, 0);
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
member<string_val> add(string&& key, const RJP_string&);
|
||||
member<string_val> add(string&& key, RJP_string&&);
|
||||
member<string_val> add(string&& key, const char*, RJP_index len = 0);
|
||||
member<string_val> add(string&& key, rexy::string_view);
|
||||
member<string_val> add(string&& key, string&&);
|
||||
|
||||
value remove(rexy::string_view key);
|
||||
value& remove(value& val);
|
||||
|
||||
void destroy(rexy::string_view key);
|
||||
void destroy(value&& val);
|
||||
|
||||
bool has_child(const value&);
|
||||
|
||||
iterator begin(void);
|
||||
const_iterator begin(void)const;
|
||||
iterator end(void);
|
||||
const_iterator end(void)const;
|
||||
|
||||
const_iterator cbegin(void)const;
|
||||
const_iterator cend(void)const;
|
||||
|
||||
size_type size(void)const;
|
||||
|
||||
value search(rexy::string_view key)const;
|
||||
|
||||
private:
|
||||
RJP_value* add_member_impl(rexy::string_view key);
|
||||
RJP_value* add_member_impl(string&& key);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
110
rjp++/include/parse.hpp
Normal file
110
rjp++/include/parse.hpp
Normal file
@ -0,0 +1,110 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_PARSE_HPP
|
||||
#define RJP_PARSE_HPP
|
||||
|
||||
#include <rjp.h>
|
||||
#include "string.hpp"
|
||||
#include "value.hpp"
|
||||
|
||||
#include <utility> //move
|
||||
#include <tuple>
|
||||
|
||||
namespace rjp{
|
||||
|
||||
class parse_res
|
||||
{
|
||||
private:
|
||||
RJP_value* m_value;
|
||||
RJP_parse_error m_err;
|
||||
|
||||
public:
|
||||
parse_res(RJP_value* val, const RJP_parse_error& err);
|
||||
parse_res(const parse_res*) = delete;
|
||||
parse_res(parse_res&& p);
|
||||
|
||||
~parse_res(void);
|
||||
|
||||
operator bool(void)const;
|
||||
bool valid(void)const;
|
||||
|
||||
value get_value(void);
|
||||
string errstr(void)const;
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
string to_json(const value& val, int format = RJP_FORMAT_PRETTY);
|
||||
[[nodiscard]]
|
||||
parse_res parse_json(rexy::string_view str, RJP_parse_flag = RJP_PARSE_NO_EXT);
|
||||
[[nodiscard]]
|
||||
parse_res parse_json(const char* str, RJP_parse_flag = RJP_PARSE_NO_EXT);
|
||||
namespace detail{
|
||||
template<int... Indexes>
|
||||
struct sequence_tup{};
|
||||
template<int N, int... Indexes>
|
||||
struct sequence_gen : public sequence_gen<N-1, Indexes..., sizeof...(Indexes)>{};
|
||||
template<int... Indexes>
|
||||
struct sequence_gen<0, Indexes...>{
|
||||
using type = sequence_tup<Indexes...>;
|
||||
};
|
||||
|
||||
template<class Func, class... Args>
|
||||
struct parse_helper{
|
||||
Func&& func;
|
||||
std::tuple<Args...> tup;
|
||||
|
||||
template<int... Indexes>
|
||||
int operator()(char* c, int size, sequence_tup<Indexes...>)const{
|
||||
return std::forward<Func>(func)(c, size, std::get<Indexes>(tup)...);
|
||||
}
|
||||
};
|
||||
struct invoker{
|
||||
virtual ~invoker(void){}
|
||||
virtual int run(char*, int)const = 0;
|
||||
};
|
||||
template<class Func, class... Args>
|
||||
struct invoker_impl : public invoker{
|
||||
parse_helper<Func,Args...> ph;
|
||||
|
||||
template<class Fn, class... Ts>
|
||||
invoker_impl(Fn&& fn, Ts&&... ts):
|
||||
ph{std::forward<Fn>(fn), {std::forward<Ts>(ts)...}}{}
|
||||
int run(char* c, int size)const override{
|
||||
return ph(c, size, typename sequence_gen<sizeof...(Args)>::type{});
|
||||
}
|
||||
};
|
||||
RJP_value* parse_cback(RJP_parse_flag f, RJP_parse_callback* cb, RJP_parse_error& err);
|
||||
RJP_value* parse_cback(RJP_parse_flag f, RJP_parse_callback* cb);
|
||||
int irjp_parse_callback(char* dest, int size, void* userdata);
|
||||
}
|
||||
template<class Func, class... Args>
|
||||
[[nodiscard]]
|
||||
parse_res parse_json(RJP_parse_flag f, Func&& func, Args&&... args){
|
||||
RJP_parse_callback cb;
|
||||
detail::invoker_impl<Func,Args...> inv(std::forward<Func>(func), std::forward<Args>(args)...);
|
||||
cb.data = static_cast<void*>(&inv);
|
||||
cb.read = detail::irjp_parse_callback;
|
||||
RJP_parse_error err = {};
|
||||
RJP_value* v = detail::parse_cback(f, &cb, err);
|
||||
return parse_res(v, err);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
35
rjp++/include/rjp.hpp
Normal file
35
rjp++/include/rjp.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_HPP_INCLUDED
|
||||
#define RJP_HPP_INCLUDED
|
||||
|
||||
#include <rjp++/array.hpp>
|
||||
#include <rjp++/integral.hpp>
|
||||
#include <rjp++/iterator.hpp>
|
||||
#include <rjp++/member.hpp>
|
||||
#include <rjp++/object.hpp>
|
||||
#include <rjp++/parse.hpp>
|
||||
#include <rjp++/string.hpp>
|
||||
#include <rjp++/string_val.hpp>
|
||||
#include <rjp++/value.hpp>
|
||||
#include <rjp++/rjp_util.hpp>
|
||||
#include <rjp++/dispatch.hpp>
|
||||
#include <rjp++/parse.hpp>
|
||||
|
||||
#endif
|
||||
34
rjp++/include/rjp_internal.hpp
Normal file
34
rjp++/include/rjp_internal.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_HPP_INTERNAL
|
||||
#define RJP_HPP_INTERNAL
|
||||
|
||||
#include "array.hpp"
|
||||
#include "integral.hpp"
|
||||
#include "iterator.hpp"
|
||||
#include "member.hpp"
|
||||
#include "object.hpp"
|
||||
#include "rjp_internal.hpp"
|
||||
#include "string.hpp"
|
||||
#include "string_val.hpp"
|
||||
#include "value.hpp"
|
||||
#include "parse.hpp"
|
||||
#include "dispatch.hpp"
|
||||
|
||||
#endif
|
||||
53
rjp++/include/rjp_util.hpp
Normal file
53
rjp++/include/rjp_util.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_UTIL_HPP
|
||||
#define RJP_UTIL_HPP
|
||||
|
||||
#include <rjp.h>
|
||||
#include "integral.hpp"
|
||||
|
||||
namespace rjp{
|
||||
template<class To, class From>
|
||||
auto convert_to(From&& from){
|
||||
return To(std::forward<From>(from));
|
||||
}
|
||||
|
||||
template<class To, class From>
|
||||
auto steal_as(From&& from){
|
||||
return To(std::move(from));
|
||||
}
|
||||
|
||||
template<class To, class From>
|
||||
auto borrow_as(From&& from){
|
||||
return To(const_cast<RJP_value*>(from.raw()), false);
|
||||
}
|
||||
|
||||
namespace detail{
|
||||
template<class Val>
|
||||
void set_to_underlying(RJP_value* val, typename Val::underlying_type);
|
||||
template<>
|
||||
void set_to_underlying<rjp::integer>(RJP_value* val, RJP_int i);
|
||||
template<>
|
||||
void set_to_underlying<rjp::dfloat>(RJP_value* val, RJP_float f);
|
||||
template<>
|
||||
void set_to_underlying<rjp::boolean>(RJP_value* val, RJP_bool b);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
58
rjp++/include/string.hpp
Normal file
58
rjp++/include/string.hpp
Normal file
@ -0,0 +1,58 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_STRING_HPP
|
||||
#define RJP_STRING_HPP
|
||||
|
||||
#include <rexy/string.hpp>
|
||||
#include <rjp.h>
|
||||
|
||||
namespace rjp{
|
||||
|
||||
namespace detail{
|
||||
struct allocator{
|
||||
using value_type = char;
|
||||
using size_type = size_t;
|
||||
using pointer = char*;
|
||||
using const_pointer = const char*;
|
||||
|
||||
char* allocate(size_type n);
|
||||
void deallocate(pointer data, size_type n);
|
||||
};
|
||||
}
|
||||
|
||||
class string : public rexy::basic_string<char,detail::allocator>
|
||||
{
|
||||
public:
|
||||
string(const string&) = default;
|
||||
string(string&&) = default;
|
||||
string& operator=(const string&) = default;
|
||||
string& operator=(string&&) = default;
|
||||
|
||||
using rexy::basic_string<char,detail::allocator>::basic_string;
|
||||
string(RJP_value* r);
|
||||
using rexy::basic_string<char,detail::allocator>::operator=;
|
||||
string& operator=(RJP_value* r);
|
||||
};
|
||||
|
||||
string escape(const rexy::string_base<char>& str);
|
||||
string escape(const char* c);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
60
rjp++/include/string_val.hpp
Normal file
60
rjp++/include/string_val.hpp
Normal file
@ -0,0 +1,60 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_STRING_VAL_HPP
|
||||
#define RJP_STRING_VAL_HPP
|
||||
|
||||
#include <rjp.h>
|
||||
#include <rexy/string.hpp>
|
||||
#include "string.hpp"
|
||||
#include "value.hpp"
|
||||
|
||||
namespace rjp{
|
||||
|
||||
class string_val : public value
|
||||
{
|
||||
public:
|
||||
using underlying_type = RJP_string;
|
||||
public:
|
||||
using value::value;
|
||||
string_val(rexy::string_view str);
|
||||
string_val(string&& str);
|
||||
string_val(void);
|
||||
string_val(const value& val);
|
||||
string_val(value&& val);
|
||||
string_val(const value& val, rexy::string_view i);
|
||||
string_val(value&& val, rexy::string_view i);
|
||||
string_val(const value& val, string&& i);
|
||||
string_val(value&& val, string&& i);
|
||||
|
||||
string_val(const string_val&) = default;
|
||||
string_val(string_val&&) = default;
|
||||
~string_val(void) = default;
|
||||
string_val& operator=(const string_val&) = default;
|
||||
string_val& operator=(string_val&&) = default;
|
||||
|
||||
rexy::string_view get(void)const;
|
||||
string steal(void);
|
||||
void set(rexy::string_view str);
|
||||
void set(string&& str);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
82
rjp++/include/value.hpp
Normal file
82
rjp++/include/value.hpp
Normal file
@ -0,0 +1,82 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_VALUE_HPP
|
||||
#define RJP_VALUE_HPP
|
||||
|
||||
#include <rjp.h>
|
||||
#include "string.hpp"
|
||||
|
||||
namespace rjp{
|
||||
|
||||
class value;
|
||||
|
||||
namespace detail{
|
||||
class vget_proxy
|
||||
{
|
||||
private:
|
||||
const rjp::value* m_value;
|
||||
public:
|
||||
vget_proxy(const rjp::value* v);
|
||||
operator int(void)const;
|
||||
operator bool(void)const;
|
||||
operator rexy::string_view(void)const;
|
||||
operator double(void)const;
|
||||
};
|
||||
}
|
||||
|
||||
class value
|
||||
{
|
||||
protected:
|
||||
RJP_value* m_value;
|
||||
bool m_managed = true;
|
||||
|
||||
public:
|
||||
value(void);
|
||||
explicit value(RJP_value* val, bool managed);
|
||||
value(const value& val);
|
||||
value(value&& val);
|
||||
~value(void);
|
||||
|
||||
value& operator=(const value& val);
|
||||
value& operator=(value&& val);
|
||||
|
||||
operator bool(void)const;
|
||||
bool valid(void)const;
|
||||
|
||||
const RJP_value* raw(void)const;
|
||||
RJP_value* raw(void);
|
||||
|
||||
RJP_data_type type(void)const;
|
||||
|
||||
detail::vget_proxy get(void)const;
|
||||
|
||||
value parent(void)const;
|
||||
bool is_child_of(const value&)const;
|
||||
|
||||
string to_json(int flags = RJP_FORMAT_PRETTY);
|
||||
protected:
|
||||
static value create_unmanaged(RJP_value* val);
|
||||
static value create_managed(RJP_value* val);
|
||||
static void remove_management(value& val);
|
||||
static void add_management(value& val);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
9
rjp++/pc/rjp++.pc.cmake.in
Normal file
9
rjp++/pc/rjp++.pc.cmake.in
Normal file
@ -0,0 +1,9 @@
|
||||
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||
|
||||
Name: rjp++
|
||||
Description: C++ wrapper around rjp
|
||||
URL: https://gitlab.com/rexy712/rjp
|
||||
Version: @rjp++_VERSION_MAJOR@.@rjp++_VERSION_MINOR@.@rjp++_VERSION_REVISION@
|
||||
Libs: -L${libdir} @RJP++_LIBFLAGS@
|
||||
Cflags: -I${includedir}
|
||||
30
rjp++/src/allocator.cpp
Normal file
30
rjp++/src/allocator.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <rjp.h>
|
||||
#include "string.hpp"
|
||||
#include <cstring> //memcpy
|
||||
|
||||
namespace rjp::detail{
|
||||
void allocator::deallocate(pointer data, size_type){
|
||||
rjp_free(data);
|
||||
}
|
||||
auto allocator::allocate(size_type size) -> pointer{
|
||||
return reinterpret_cast<char*>(rjp_alloc(size));
|
||||
}
|
||||
}
|
||||
137
rjp++/src/array.cpp
Normal file
137
rjp++/src/array.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "array.hpp"
|
||||
#include <rjp.h>
|
||||
#include <utility> //move, swap
|
||||
|
||||
namespace rjp{
|
||||
|
||||
array::array(void):
|
||||
value(rjp_new_array(), true){}
|
||||
array::array(const value& val):
|
||||
value(val)
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
if(rjp_value_type(m_value) != rjp_json_array)
|
||||
rjp_set_array(m_value);
|
||||
}
|
||||
array::array(value&& val):
|
||||
value(std::move(val))
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
if(rjp_value_type(m_value) != rjp_json_array)
|
||||
rjp_set_array(m_value);
|
||||
}
|
||||
string_val array::add(const RJP_string& s){
|
||||
RJP_value* newelem = rjp_new_element(m_value);
|
||||
rjp_set_string(newelem, s.value, s.length);
|
||||
return string_val(create_unmanaged(newelem));
|
||||
}
|
||||
string_val array::add(RJP_string&& s){
|
||||
RJP_value* newelem = rjp_new_element(m_value);
|
||||
rjp_set_string_steal(newelem, s.value, s.length);
|
||||
return string_val(create_unmanaged(newelem));
|
||||
}
|
||||
string_val array::add(const char* c, RJP_index len){
|
||||
RJP_value* newelem = rjp_new_element(m_value);
|
||||
rjp_set_string(newelem, c, len);
|
||||
return string_val(create_unmanaged(newelem));
|
||||
}
|
||||
string_val array::add(rexy::string_view s){
|
||||
return add(s.data(), s.length());
|
||||
}
|
||||
string_val array::add(string&& s){
|
||||
RJP_value* newelem = rjp_new_element(m_value);
|
||||
auto len = s.length();
|
||||
rjp_set_string_steal(newelem, s.release(), len);
|
||||
return string_val(create_unmanaged(newelem));
|
||||
}
|
||||
|
||||
value& array::remove(value& val){
|
||||
rjp_remove_element(m_value, val.raw());
|
||||
add_management(val);
|
||||
return val;
|
||||
}
|
||||
void array::destroy(value&& val){
|
||||
rjp_free_element(m_value, val.raw());
|
||||
}
|
||||
array::iterator array::begin(void){
|
||||
return iterator(m_value);
|
||||
}
|
||||
array::iterator array::end(void){
|
||||
return iterator();
|
||||
}
|
||||
array::const_iterator array::begin(void)const{
|
||||
return const_iterator(m_value);
|
||||
}
|
||||
array::const_iterator array::end(void)const{
|
||||
return iterator();
|
||||
}
|
||||
array::const_iterator array::cbegin(void)const{
|
||||
return const_iterator(m_value);
|
||||
}
|
||||
array::const_iterator array::cend(void)const{
|
||||
return iterator();
|
||||
}
|
||||
array::size_type array::size(void)const{
|
||||
return rjp_num_elements(m_value);
|
||||
}
|
||||
RJP_value* array::create_element(RJP_value* arr){
|
||||
return rjp_new_element(arr);
|
||||
}
|
||||
|
||||
|
||||
array_iterator::array_iterator(RJP_value* v){
|
||||
rjp_init_array_iterator(&m_it, v);
|
||||
}
|
||||
array_iterator::array_iterator(array_iterator&& a):
|
||||
m_it(a.m_it)
|
||||
{
|
||||
a.m_it = RJP_array_iterator{};
|
||||
}
|
||||
array_iterator::~array_iterator(void){
|
||||
rjp_delete_array_iterator(&m_it);
|
||||
}
|
||||
array_iterator& array_iterator::operator=(array_iterator&& o){
|
||||
std::swap(m_it, o.m_it);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool array_iterator::operator==(const array_iterator& o)const{
|
||||
return rjp_array_iterator_current(&m_it) == rjp_array_iterator_current(&o.m_it);
|
||||
}
|
||||
bool array_iterator::operator!=(const array_iterator& o)const{
|
||||
return !(*this == o);
|
||||
}
|
||||
value array_iterator::operator*(void)const{
|
||||
return value(rjp_array_iterator_current(&m_it), false);
|
||||
}
|
||||
auto array_iterator::operator->(void)const -> value_wrapper{
|
||||
return value_wrapper(rjp_array_iterator_current(&m_it), false);
|
||||
}
|
||||
|
||||
array_iterator& array_iterator::operator++(void){
|
||||
rjp_array_iterator_next(&m_it);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
34
rjp++/src/container.cpp
Normal file
34
rjp++/src/container.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "container.hpp"
|
||||
#include <rjp.h>
|
||||
|
||||
namespace rjp{
|
||||
|
||||
void container::set_array_value(RJP_value* v){
|
||||
rjp_set_array(v);
|
||||
}
|
||||
void container::set_object_value(RJP_value* v){
|
||||
rjp_set_object(v);
|
||||
}
|
||||
void container::set_null_value(RJP_value* v){
|
||||
rjp_set_null(v);
|
||||
}
|
||||
|
||||
}
|
||||
154
rjp++/src/integral.cpp
Normal file
154
rjp++/src/integral.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "integral.hpp"
|
||||
#include <rjp.h>
|
||||
#include <utility> //move
|
||||
|
||||
namespace rjp{
|
||||
|
||||
integer::integer(underlying_type i):
|
||||
value(rjp_new_int(i), true){}
|
||||
integer::integer(void):
|
||||
integer(0){}
|
||||
integer::integer(const value& val):
|
||||
value(val)
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
if(rjp_value_type(m_value) != rjp_json_integer)
|
||||
rjp_set_int(m_value, rjp_get_int(val.raw()));
|
||||
}
|
||||
integer::integer(value&& val):
|
||||
value(std::move(val))
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
if(rjp_value_type(m_value) != rjp_json_integer)
|
||||
rjp_set_int(m_value, rjp_get_int(m_value));
|
||||
}
|
||||
integer::integer(const value& val, underlying_type i):
|
||||
value(val)
|
||||
{
|
||||
rjp_set_int(m_value, i);
|
||||
}
|
||||
integer::integer(value&& val, underlying_type i):
|
||||
value(std::move(val))
|
||||
{
|
||||
rjp_set_int(m_value, i);
|
||||
}
|
||||
integer::underlying_type integer::get(void)const{
|
||||
return rjp_get_int(m_value);
|
||||
}
|
||||
void integer::set(underlying_type i){
|
||||
rjp_set_int(m_value, i);
|
||||
}
|
||||
|
||||
dfloat::dfloat(underlying_type i):
|
||||
value(rjp_new_float(i), true){}
|
||||
dfloat::dfloat(void):
|
||||
dfloat(0){}
|
||||
dfloat::dfloat(const value& val):
|
||||
value(val)
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
if(rjp_value_type(m_value) != rjp_json_dfloat)
|
||||
rjp_set_float(m_value, rjp_get_float(val.raw()));
|
||||
}
|
||||
dfloat::dfloat(value&& val):
|
||||
value(std::move(val))
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
if(rjp_value_type(m_value) != rjp_json_dfloat)
|
||||
rjp_set_float(m_value, rjp_get_float(m_value));
|
||||
}
|
||||
dfloat::dfloat(const value& val, underlying_type i):
|
||||
value(val)
|
||||
{
|
||||
rjp_set_float(m_value, i);
|
||||
}
|
||||
dfloat::dfloat(value&& val, underlying_type i):
|
||||
value(std::move(val))
|
||||
{
|
||||
rjp_set_float(m_value, i);
|
||||
}
|
||||
|
||||
dfloat::underlying_type dfloat::get(void)const{
|
||||
return rjp_get_float(m_value);
|
||||
}
|
||||
void dfloat::set(underlying_type i){
|
||||
rjp_set_float(m_value, i);
|
||||
}
|
||||
|
||||
boolean::boolean(underlying_type i):
|
||||
value(rjp_new_bool(i), true){}
|
||||
boolean::boolean(void):
|
||||
boolean(0){}
|
||||
boolean::boolean(const value& val):
|
||||
value(val)
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
if(rjp_value_type(m_value) != rjp_json_boolean)
|
||||
rjp_set_bool(m_value, rjp_get_bool(val.raw()));
|
||||
}
|
||||
boolean::boolean(value&& val):
|
||||
value(std::move(val))
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
if(rjp_value_type(m_value) != rjp_json_boolean)
|
||||
rjp_set_bool(m_value, rjp_get_bool(m_value));
|
||||
}
|
||||
boolean::boolean(const value& val, underlying_type i):
|
||||
value(val)
|
||||
{
|
||||
rjp_set_bool(m_value, i);
|
||||
}
|
||||
boolean::boolean(value&& val, underlying_type i):
|
||||
value(std::move(val))
|
||||
{
|
||||
rjp_set_bool(m_value, i);
|
||||
}
|
||||
|
||||
bool boolean::get(void)const{
|
||||
return rjp_get_bool(m_value);
|
||||
}
|
||||
void boolean::set(bool i){
|
||||
rjp_set_bool(m_value, i);
|
||||
}
|
||||
|
||||
null::null(void):
|
||||
value(rjp_new_null(), true){}
|
||||
null::null(const value& val):
|
||||
value(val)
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
rjp_set_null(m_value);
|
||||
}
|
||||
null::null(value&& val):
|
||||
value(std::move(val))
|
||||
{
|
||||
rjp_set_null(m_value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
27
rjp++/src/member.cpp
Normal file
27
rjp++/src/member.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "member.hpp"
|
||||
|
||||
namespace rjp{
|
||||
|
||||
const RJP_string* member_base::member_key(RJP_value* v){
|
||||
return rjp_member_key(v);
|
||||
}
|
||||
|
||||
}
|
||||
187
rjp++/src/object.cpp
Normal file
187
rjp++/src/object.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "object.hpp"
|
||||
#include <rjp.h>
|
||||
#include <utility> //move
|
||||
|
||||
namespace rjp{
|
||||
object::object(void):
|
||||
value(rjp_new_object(), true){}
|
||||
object::object(const value& val):
|
||||
value(val)
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
if(rjp_value_type(m_value) != rjp_json_object)
|
||||
rjp_set_object(m_value);
|
||||
}
|
||||
object::object(value&& val):
|
||||
value(std::move(val))
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
if(rjp_value_type(m_value) != rjp_json_object)
|
||||
rjp_set_object(m_value);
|
||||
}
|
||||
member<string_val> object::add(rexy::string_view key, const RJP_string& s){
|
||||
RJP_value* newmem = rjp_new_member(m_value, key.data(), key.length());
|
||||
rjp_set_string(newmem, s.value, s.length);
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
member<string_val> object::add(rexy::string_view key, RJP_string&& s){
|
||||
RJP_value* newmem = rjp_new_member(m_value, key.data(), key.length());
|
||||
rjp_set_string_steal(newmem, s.value, s.length);
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
member<string_val> object::add(rexy::string_view key, const char* s, RJP_index len){
|
||||
RJP_value* newmem = rjp_new_member(m_value, key.data(), key.length());
|
||||
rjp_set_string(newmem, s, len);
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
member<string_val> object::add(rexy::string_view key, rexy::string_view s){
|
||||
RJP_value* newmem = rjp_new_member(m_value, key.data(), key.length());
|
||||
rjp_set_string(newmem, s.data(), s.length());
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
member<string_val> object::add(rexy::string_view key, string&& s){
|
||||
RJP_value* newmem = rjp_new_member(m_value, key.data(), key.length());
|
||||
auto len = s.length();
|
||||
rjp_set_string_steal(newmem, s.release(), len);
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
|
||||
member<string_val> object::add(string&& key, const RJP_string& s){
|
||||
RJP_value* newmem = rjp_new_member_steal_key(m_value, key.data(), key.length());
|
||||
rjp_set_string(newmem, s.value, s.length);
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
member<string_val> object::add(string&& key, RJP_string&& s){
|
||||
RJP_value* newmem = rjp_new_member_steal_key(m_value, key.data(), key.length());
|
||||
rjp_set_string_steal(newmem, s.value, s.length);
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
member<string_val> object::add(string&& key, const char* s, RJP_index len){
|
||||
RJP_value* newmem = rjp_new_member_steal_key(m_value, key.data(), key.length());
|
||||
rjp_set_string(newmem, s, len);
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
member<string_val> object::add(string&& key, rexy::string_view s){
|
||||
RJP_value* newmem = rjp_new_member_steal_key(m_value, key.data(), key.length());
|
||||
rjp_set_string(newmem, s.data(), s.length());
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
member<string_val> object::add(string&& key, string&& s){
|
||||
RJP_value* newmem = rjp_new_member_steal_key(m_value, key.data(), key.length());
|
||||
auto len = s.length();
|
||||
rjp_set_string_steal(newmem, s.release(), len);
|
||||
return create_unmanaged(newmem);
|
||||
}
|
||||
|
||||
value object::remove(rexy::string_view key){
|
||||
RJP_value* removed = rjp_remove_member_by_key(m_value, key.data());
|
||||
return create_managed(removed);
|
||||
}
|
||||
value& object::remove(value& val){
|
||||
rjp_remove_member(m_value, val.raw());
|
||||
add_management(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
void object::destroy(rexy::string_view key){
|
||||
rjp_free_member_by_key(m_value, key.data());
|
||||
}
|
||||
void object::destroy(value&& val){
|
||||
rjp_free_member(m_value, val.raw());
|
||||
}
|
||||
|
||||
bool object::has_child(const value& val){
|
||||
return val.parent().raw() == m_value;
|
||||
}
|
||||
object::iterator object::begin(void){
|
||||
return iterator(m_value);
|
||||
}
|
||||
object::const_iterator object::begin(void)const{
|
||||
return const_iterator(m_value);
|
||||
}
|
||||
object::iterator object::end(void){
|
||||
return iterator();
|
||||
}
|
||||
object::const_iterator object::end(void)const{
|
||||
return const_iterator();
|
||||
}
|
||||
object::const_iterator object::cbegin(void)const{
|
||||
return const_iterator(m_value);
|
||||
}
|
||||
object::const_iterator object::cend(void)const{
|
||||
return const_iterator();
|
||||
}
|
||||
object::size_type object::size(void)const{
|
||||
return rjp_num_members(m_value);
|
||||
}
|
||||
value object::search(rexy::string_view key)const{
|
||||
RJP_value* result = rjp_search_member(m_value, key.data());
|
||||
return create_unmanaged(result);
|
||||
}
|
||||
|
||||
RJP_value* object::add_member_impl(rexy::string_view key){
|
||||
return rjp_new_member(m_value, key.data(), key.length());
|
||||
}
|
||||
RJP_value* object::add_member_impl(string&& key){
|
||||
auto length = key.length();
|
||||
return rjp_new_member_steal_key(m_value, key.release(), length);
|
||||
}
|
||||
|
||||
|
||||
object_iterator::object_iterator(void):
|
||||
m_it(){}
|
||||
object_iterator::object_iterator(RJP_value* v){
|
||||
rjp_init_object_iterator(&m_it, v);
|
||||
}
|
||||
object_iterator::object_iterator(object_iterator&& o):
|
||||
m_it(o.m_it)
|
||||
{
|
||||
o.m_it = RJP_object_iterator{};
|
||||
}
|
||||
object_iterator::~object_iterator(void){
|
||||
rjp_delete_object_iterator(&m_it);
|
||||
}
|
||||
object_iterator& object_iterator::operator=(object_iterator&& o){
|
||||
std::swap(m_it, o.m_it);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool object_iterator::operator==(const object_iterator& o)const{
|
||||
return rjp_object_iterator_current(&m_it) == rjp_object_iterator_current(&o.m_it);
|
||||
}
|
||||
bool object_iterator::operator!=(const object_iterator& o)const{
|
||||
return !(*this == o);
|
||||
}
|
||||
member<void> object_iterator::operator*(void)const{
|
||||
return member<void>(rjp_object_iterator_current(&m_it), false);
|
||||
}
|
||||
auto object_iterator::operator->(void)const -> value_wrapper{
|
||||
return value_wrapper(rjp_object_iterator_current(&m_it), false);
|
||||
}
|
||||
|
||||
object_iterator& object_iterator::operator++(void){
|
||||
rjp_object_iterator_next(&m_it);
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
93
rjp++/src/rjp.cpp
Normal file
93
rjp++/src/rjp.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <rjp.h>
|
||||
#include <rexy/string_base.hpp>
|
||||
|
||||
#include "rjp_internal.hpp"
|
||||
#include "parse.hpp"
|
||||
#include "string.hpp"
|
||||
#include "value.hpp"
|
||||
#include "rjp_util.hpp"
|
||||
|
||||
namespace rjp{
|
||||
|
||||
parse_res::parse_res(RJP_value* val, const RJP_parse_error& err):
|
||||
m_value(val), m_err(err){}
|
||||
parse_res::parse_res(parse_res&& p):
|
||||
m_value(std::exchange(p.m_value, nullptr)), m_err(std::move(p.m_err)){}
|
||||
|
||||
parse_res::~parse_res(void){
|
||||
rjp_free_value(m_value);
|
||||
if(m_err.parsestate)
|
||||
rjp_delete_parse_error(&m_err);
|
||||
}
|
||||
|
||||
parse_res::operator bool(void)const{
|
||||
return valid();
|
||||
}
|
||||
bool parse_res::valid(void)const{
|
||||
return m_value != nullptr;
|
||||
}
|
||||
|
||||
value parse_res::get_value(void){
|
||||
return value(std::exchange(m_value, nullptr), true);
|
||||
}
|
||||
string parse_res::errstr(void)const{
|
||||
return string(rexy::steal(rjp_parse_error_to_string(&m_err)));
|
||||
}
|
||||
|
||||
string to_json(const value& val, int format){
|
||||
string s;
|
||||
s.reset(rjp_to_json(val.raw(), format));
|
||||
return s;
|
||||
}
|
||||
parse_res parse_json(rexy::string_view str, RJP_parse_flag flags){
|
||||
return parse_json(str.data(), flags);
|
||||
}
|
||||
parse_res parse_json(const char* str, RJP_parse_flag flags){
|
||||
RJP_parse_error err = {};
|
||||
RJP_value* v = rjp_parse(str, flags, &err);
|
||||
return parse_res(v, err);
|
||||
}
|
||||
namespace detail{
|
||||
int irjp_parse_callback(char* dest, int size, void* userdata){
|
||||
invoker* inv = static_cast<invoker*>(userdata);
|
||||
return inv->run(dest, size);
|
||||
}
|
||||
RJP_value* parse_cback(RJP_parse_flag f, RJP_parse_callback* cb, RJP_parse_error& err){
|
||||
return rjp_parse_cback(f, cb, &err);
|
||||
}
|
||||
RJP_value* parse_cback(RJP_parse_flag f, RJP_parse_callback* cb){
|
||||
return rjp_parse_cback(f, cb, NULL);
|
||||
}
|
||||
|
||||
template<>
|
||||
void set_to_underlying<rjp::integer>(RJP_value* val, RJP_int i){
|
||||
rjp_set_int(val, i);
|
||||
}
|
||||
template<>
|
||||
void set_to_underlying<rjp::dfloat>(RJP_value* val, RJP_float f){
|
||||
rjp_set_float(val, f);
|
||||
}
|
||||
template<>
|
||||
void set_to_underlying<rjp::boolean>(RJP_value* val, RJP_bool b){
|
||||
rjp_set_bool(val, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
rjp++/src/string.cpp
Normal file
57
rjp++/src/string.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <rexy/string.hpp>
|
||||
#include <rjp.h>
|
||||
#include "string.hpp"
|
||||
#include <utility> //exchange
|
||||
|
||||
namespace rjp{
|
||||
|
||||
string::string(RJP_value* r):
|
||||
rexy::basic_string<char,detail::allocator>(rexy::steal(r ? rjp_get_string(r)->value : nullptr),
|
||||
r ? rjp_get_string(r)->length : 0,
|
||||
r ? rjp_get_string(r)->length : 0)
|
||||
{
|
||||
if(r){
|
||||
RJP_string* str = rjp_get_string(r);
|
||||
str->value = nullptr;
|
||||
str->length = 0;
|
||||
rjp_set_null(r);
|
||||
}
|
||||
}
|
||||
string& string::operator=(RJP_value* r){
|
||||
if(!r)
|
||||
return *this;
|
||||
reset();
|
||||
RJP_string* str = rjp_get_string(r);
|
||||
set_long_ptr(std::exchange(str->value, nullptr));
|
||||
set_long_length(str->length);
|
||||
set_long_capacity(str->length);
|
||||
str->length = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
string escape(const rexy::string_base<char>& str){
|
||||
return escape(str.c_str());
|
||||
}
|
||||
string escape(const char* c){
|
||||
RJP_string tmp = rjp_escape(c);
|
||||
return string(rexy::steal<char*>(tmp.value), tmp.length);
|
||||
}
|
||||
}
|
||||
90
rjp++/src/string_val.cpp
Normal file
90
rjp++/src/string_val.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "string_val.hpp"
|
||||
#include <rjp.h>
|
||||
#include <utility> //move
|
||||
|
||||
namespace rjp{
|
||||
|
||||
using namespace rexy::str_literals;
|
||||
|
||||
string_val::string_val(rexy::string_view str):
|
||||
value(rjp_new_string(str.data(), str.length()), true){}
|
||||
string_val::string_val(string&& str):
|
||||
value(rjp_new_string_steal(str.data(), str.length()), true)
|
||||
{
|
||||
str.release();
|
||||
}
|
||||
string_val::string_val(void):
|
||||
string_val(""_sv){}
|
||||
string_val::string_val(const value& val):
|
||||
value(val)
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
if(rjp_value_type(m_value) != rjp_json_string)
|
||||
rjp_set_string(m_value, "", 0);
|
||||
}
|
||||
string_val::string_val(value&& val):
|
||||
value(std::move(val))
|
||||
{
|
||||
if(!m_value)
|
||||
return;
|
||||
if(rjp_value_type(m_value) != rjp_json_string)
|
||||
rjp_set_string(m_value, "", 0);
|
||||
}
|
||||
string_val::string_val(const value& val, rexy::string_view i):
|
||||
value(val)
|
||||
{
|
||||
rjp_set_string(m_value, i.data(), i.length());
|
||||
}
|
||||
string_val::string_val(value&& val, rexy::string_view i):
|
||||
value(std::move(val))
|
||||
{
|
||||
rjp_set_string(m_value, i.data(), i.length());
|
||||
}
|
||||
string_val::string_val(const value& val, string&& i):
|
||||
value(val)
|
||||
{
|
||||
auto length = i.length();
|
||||
rjp_set_string_steal(m_value, i.release(), length);
|
||||
}
|
||||
string_val::string_val(value&& val, string&& i):
|
||||
value(std::move(val))
|
||||
{
|
||||
auto length = i.length();
|
||||
rjp_set_string_steal(m_value, i.release(), length);
|
||||
}
|
||||
|
||||
rexy::string_view string_val::get(void)const{
|
||||
const RJP_string* str = rjp_get_cstring(m_value);
|
||||
return rexy::string_view(str->value, str->length);
|
||||
}
|
||||
string string_val::steal(void){
|
||||
return string(m_value);
|
||||
}
|
||||
void string_val::set(rexy::string_view str){
|
||||
rjp_set_string(m_value, str.data(), str.length());
|
||||
}
|
||||
void string_val::set(string&& str){
|
||||
auto length = str.length();
|
||||
rjp_set_string_steal(m_value, str.release(), length);
|
||||
}
|
||||
|
||||
}
|
||||
100
rjp++/src/value.cpp
Normal file
100
rjp++/src/value.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "value.hpp"
|
||||
#include <rjp.h>
|
||||
#include <utility> //swap, move, exchange
|
||||
|
||||
namespace rjp{
|
||||
|
||||
value::value(RJP_value* val, bool managed):
|
||||
m_value(val),
|
||||
m_managed(managed){}
|
||||
value::value(void):
|
||||
m_value(rjp_new_null()){}
|
||||
value::value(const value& val):
|
||||
value()
|
||||
{
|
||||
rjp_copy_value(m_value, val.m_value);
|
||||
}
|
||||
value::value(value&& val):
|
||||
m_value(std::exchange(val.m_value, nullptr)),
|
||||
m_managed(val.m_managed){}
|
||||
value::~value(void){
|
||||
if(m_managed)
|
||||
rjp_free_value(m_value);
|
||||
}
|
||||
|
||||
value& value::operator=(const value& val){
|
||||
value tmp(val);
|
||||
return (*this = std::move(tmp));
|
||||
}
|
||||
value& value::operator=(value&& val){
|
||||
std::swap(val.m_value, m_value);
|
||||
std::swap(val.m_managed, m_managed);
|
||||
return *this;
|
||||
}
|
||||
value::operator bool(void)const{
|
||||
return m_value != nullptr;
|
||||
}
|
||||
bool value::valid(void)const{
|
||||
return m_value != nullptr;
|
||||
}
|
||||
|
||||
const RJP_value* value::raw(void)const{
|
||||
return m_value;
|
||||
}
|
||||
RJP_value* value::raw(void){
|
||||
return m_value;
|
||||
}
|
||||
|
||||
RJP_data_type value::type(void)const{
|
||||
return rjp_value_type(m_value);
|
||||
}
|
||||
detail::vget_proxy value::get(void)const{
|
||||
return detail::vget_proxy(this);
|
||||
}
|
||||
|
||||
|
||||
value value::parent(void)const{
|
||||
return create_unmanaged(rjp_value_parent(m_value));
|
||||
}
|
||||
bool value::is_child_of(const value& val)const{
|
||||
return rjp_value_parent(m_value) == val.raw();
|
||||
}
|
||||
|
||||
string value::to_json(int flags){
|
||||
string s;
|
||||
s.reset(rjp_to_json(m_value, flags));
|
||||
return s;
|
||||
}
|
||||
|
||||
value value::create_unmanaged(RJP_value* val){
|
||||
return value(val, false);
|
||||
}
|
||||
void value::remove_management(value& val){
|
||||
val.m_managed = false;
|
||||
}
|
||||
value value::create_managed(RJP_value* val){
|
||||
return value(val, true);
|
||||
}
|
||||
void value::add_management(value& val){
|
||||
val.m_managed = true;
|
||||
}
|
||||
}
|
||||
|
||||
42
rjp++/src/vget_proxy.cpp
Normal file
42
rjp++/src/vget_proxy.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
/**
|
||||
rjp++
|
||||
Copyright (C) 2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "value.hpp"
|
||||
#include "integral.hpp"
|
||||
#include "string_val.hpp"
|
||||
#include "string.hpp"
|
||||
#include "rjp_util.hpp"
|
||||
|
||||
namespace rjp::detail{
|
||||
|
||||
vget_proxy::vget_proxy(const rjp::value* v):
|
||||
m_value(v){}
|
||||
vget_proxy::operator int(void)const{
|
||||
return rjp::borrow_as<rjp::integer>(*m_value).get();
|
||||
}
|
||||
vget_proxy::operator bool(void)const{
|
||||
return rjp::borrow_as<rjp::boolean>(*m_value).get();
|
||||
}
|
||||
vget_proxy::operator rexy::string_view(void)const{
|
||||
return rjp::borrow_as<rjp::string_val>(*m_value).get();
|
||||
}
|
||||
vget_proxy::operator double(void)const{
|
||||
return rjp::borrow_as<rjp::dfloat>(*m_value).get();
|
||||
}
|
||||
|
||||
}
|
||||
22
rjp++/tests/CMakeLists.txt
Normal file
22
rjp++/tests/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
project(rjp++_tests)
|
||||
set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../include)
|
||||
include_directories("${INCLUDE_PATH}")
|
||||
add_compile_options(-Wall -Wextra -pedantic -std=c++20)
|
||||
link_libraries(rjp++ rjp)
|
||||
|
||||
if(ENABLE_PROFILING)
|
||||
add_compile_options(-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
||||
add_link_options(-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
||||
endif()
|
||||
|
||||
add_executable(parseplusplus "parse.cpp")
|
||||
set_target_properties(parseplusplus PROPERTIES OUTPUT_NAME parse)
|
||||
add_executable(outputplusplus "output.cpp")
|
||||
set_target_properties(outputplusplus PROPERTIES OUTPUT_NAME output)
|
||||
add_executable(dispatchplusplus "dispatch.cpp")
|
||||
set_target_properties(dispatchplusplus PROPERTIES OUTPUT_NAME dispatch)
|
||||
|
||||
add_test(NAME parse-test-plusplus COMMAND parseplusplus)
|
||||
add_test(NAME output-test-plusplus COMMAND outputplusplus)
|
||||
add_test(NAME dispatch-test-plusplus COMMAND dispatchplusplus)
|
||||
50
rjp++/tests/dispatch.cpp
Normal file
50
rjp++/tests/dispatch.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "rjp_internal.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
|
||||
#define NUM_ELEMENTS 100
|
||||
|
||||
rjp::array initialize_array(int num){
|
||||
rjp::array root;
|
||||
|
||||
for(int i = 0;i < num;++i){
|
||||
switch(rand() % 4){
|
||||
case 0:
|
||||
root.add<rjp::integer>(rand() % 10);
|
||||
break;
|
||||
case 1:
|
||||
root.add<rjp::boolean>(rand() % 2);
|
||||
break;
|
||||
case 2:
|
||||
root.add<rjp::array>();
|
||||
break;
|
||||
case 3:
|
||||
root.add<rjp::object>();
|
||||
break;
|
||||
};
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
int main(){
|
||||
srand(time(0));
|
||||
int retval = 0;
|
||||
|
||||
rjp::array root = initialize_array(NUM_ELEMENTS);
|
||||
|
||||
for(auto&& val : root){
|
||||
RJP_data_type d = rjp_json_null;
|
||||
rjp::dispatch(rjp::dispatcher{
|
||||
[&d](const rjp::integer&){d = rjp_json_integer;},
|
||||
[&d](const rjp::boolean&){d = rjp_json_boolean;},
|
||||
[&d](const rjp::array&){d = rjp_json_array;},
|
||||
[&d](const rjp::object&){d = rjp_json_object;},
|
||||
}, val);
|
||||
if(d != val.type()){
|
||||
++retval;
|
||||
printf("Dispatch error\n");
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
371
rjp++/tests/output.cpp
Normal file
371
rjp++/tests/output.cpp
Normal file
@ -0,0 +1,371 @@
|
||||
#include "rjp_internal.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace rexy::str_literals;
|
||||
|
||||
typedef struct{
|
||||
rjp::value (*create)(void);
|
||||
const char* res;
|
||||
int fmt;
|
||||
}test_pair;
|
||||
|
||||
//Basic values excluding float because precision bs and stuff
|
||||
rjp::value case_1(void){
|
||||
return rjp::null();
|
||||
}
|
||||
rjp::value case_2(void){
|
||||
return rjp::integer(5);
|
||||
}
|
||||
rjp::value case_3(void){
|
||||
return rjp::boolean(true);
|
||||
}
|
||||
rjp::value case_4(void){
|
||||
return rjp::boolean(false);
|
||||
}
|
||||
rjp::value case_5(void){
|
||||
return rjp::object();
|
||||
}
|
||||
rjp::value case_6(void){
|
||||
return rjp::array();
|
||||
}
|
||||
/*RJP_value* case_7(void){
|
||||
return rjp_new_ordered_object();
|
||||
}*/
|
||||
//handle object with member
|
||||
rjp::value case_8(void){
|
||||
rjp::object obj;
|
||||
obj.add<rjp::integer>("key"_sv, 7);
|
||||
return rjp::steal_as<rjp::value>(obj);
|
||||
}
|
||||
//handle object with subobject
|
||||
rjp::value case_9(void){
|
||||
rjp::object obj;
|
||||
rjp::object sub = obj.add<rjp::object>("key"_sv);
|
||||
sub.add<rjp::boolean>("subkey"_sv, false);
|
||||
return rjp::steal_as<rjp::value>(obj);
|
||||
}
|
||||
//handle object with multiple members
|
||||
rjp::value case_10(void){
|
||||
rjp::object obj;
|
||||
rjp::object sub = obj.add<rjp::object>("key"_sv);
|
||||
sub.add<rjp::boolean>("subkey"_sv, false);
|
||||
sub.add<rjp::boolean>("subkey2"_sv, true);
|
||||
return rjp::steal_as<rjp::value>(obj);
|
||||
}
|
||||
//handle object member ordering
|
||||
rjp::value case_11(void){
|
||||
rjp::object obj;
|
||||
rjp::object sub = obj.add<rjp::object>("key"_sv);
|
||||
sub.add<rjp::boolean>("subkey2"_sv, true);
|
||||
sub.add<rjp::boolean>("subkey"_sv, false);
|
||||
return rjp::steal_as<rjp::value>(obj);
|
||||
}
|
||||
//handle orderedobject member ordering
|
||||
/*RJP_value* case_12(void){
|
||||
RJP_value* obj = rjp_new_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_ordered_object(sub);
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
return obj;
|
||||
}
|
||||
//handle orderedobject member ordering pt2
|
||||
RJP_value* case_13(void){
|
||||
RJP_value* obj = rjp_new_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_ordered_object(sub);
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
return obj;
|
||||
}*/
|
||||
//handle array with element
|
||||
rjp::value case_14(void){
|
||||
rjp::array arr;
|
||||
arr.add<rjp::integer>(5);
|
||||
return rjp::steal_as<rjp::value>(arr);
|
||||
}
|
||||
//handle array with subarray
|
||||
rjp::value case_15(void){
|
||||
rjp::array arr;
|
||||
arr.add<rjp::array>().add<rjp::boolean>(false);
|
||||
return rjp::steal_as<rjp::value>(arr);
|
||||
}
|
||||
//handle array with multiple elements
|
||||
rjp::value case_16(void){
|
||||
rjp::array arr;
|
||||
rjp::array sub = arr.add<rjp::array>();
|
||||
sub.add<rjp::boolean>(false);
|
||||
sub.add<rjp::boolean>(true);
|
||||
return rjp::steal_as<rjp::value>(arr);
|
||||
}
|
||||
//handle array with multiple elements and subarray
|
||||
rjp::value case_17(void){
|
||||
rjp::array arr;
|
||||
arr.add<rjp::integer>(5);
|
||||
rjp::array sub = arr.add<rjp::array>();
|
||||
sub.add<rjp::boolean>(false);
|
||||
sub.add<rjp::boolean>(true);
|
||||
return rjp::steal_as<rjp::value>(arr);
|
||||
}
|
||||
//handle array with subobject with subarray
|
||||
rjp::value case_18(void){
|
||||
rjp::array arr;
|
||||
arr.add<rjp::integer>(5);
|
||||
rjp::object subobj = arr.add<rjp::object>();
|
||||
rjp::array subarr = subobj.add<rjp::array>("key"_sv);
|
||||
subarr.add<rjp::boolean>(false);
|
||||
return rjp::steal_as<rjp::value>(arr);
|
||||
}
|
||||
//handle object with many members
|
||||
rjp::value case_19(void){
|
||||
char c[] = "key0";
|
||||
rjp::array arr;
|
||||
arr.add<rjp::integer>(5);
|
||||
rjp::object subobj = arr.add<rjp::object>();
|
||||
for(int i = 0;i < 10;++i){
|
||||
subobj.add<rjp::boolean>(rexy::string_view(c), i % 2 == 0);
|
||||
c[3] += 1;
|
||||
}
|
||||
return rjp::steal_as<rjp::value>(arr);
|
||||
}
|
||||
//handle orderedobject with many members as array element
|
||||
/*RJP_value* case_20(void){
|
||||
char c[] = "key9";
|
||||
RJP_value* arr = rjp_new_array();
|
||||
rjp_add_element(arr, rjp_new_int(5));
|
||||
RJP_value* subobj = rjp_new_element(arr);
|
||||
rjp_set_ordered_object(subobj);
|
||||
for(int i = 0;i < 10;++i){
|
||||
RJP_value* newmem = rjp_new_member(subobj, c, 0);
|
||||
c[3] -= 1;
|
||||
if(i % 2 == 0)
|
||||
rjp_set_bool(newmem, 1);
|
||||
else
|
||||
rjp_set_bool(newmem, 0);
|
||||
}
|
||||
return arr;
|
||||
}*/
|
||||
//handle array with many element as object member
|
||||
rjp::value case_21(void){
|
||||
rjp::object obj;
|
||||
rjp::array arr = obj.add<rjp::array>("arr"_sv);
|
||||
for(int i = 0;i < 10;++i)
|
||||
arr.add<rjp::integer>(i);
|
||||
return rjp::steal_as<rjp::value>(obj);
|
||||
}
|
||||
/*
|
||||
//handle unorderedobject conversion
|
||||
RJP_value* case_22(void){
|
||||
RJP_value* obj = rjp_new_ordered_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_ordered_object(sub);
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
rjp_object_to_unordered(sub);
|
||||
return obj;
|
||||
}
|
||||
//handle unorderedobject conversion
|
||||
RJP_value* case_23(void){
|
||||
RJP_value* obj = rjp_new_ordered_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_ordered_object(sub);
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
rjp_object_to_unordered(sub);
|
||||
return obj;
|
||||
}
|
||||
//handle orderedobject conversion
|
||||
RJP_value* case_24(void){
|
||||
RJP_value* obj = rjp_new_ordered_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_object(sub);
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
rjp_object_to_ordered(sub);
|
||||
return obj;
|
||||
}
|
||||
//handle orderedobject conversion
|
||||
RJP_value* case_25(void){
|
||||
RJP_value* obj = rjp_new_ordered_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_object(sub);
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
rjp_object_to_ordered(sub);
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
return obj;
|
||||
}
|
||||
*/
|
||||
rjp::value case_26(void){
|
||||
return rjp::string_val("string"_sv);
|
||||
}
|
||||
rjp::value case_27(void){
|
||||
return rjp::string_val(""_sv);
|
||||
}
|
||||
rjp::value case_28(void){
|
||||
rjp::object obj;
|
||||
obj.add("key"_sv, "string"_sv);
|
||||
return rjp::steal_as<rjp::value>(obj);
|
||||
}
|
||||
rjp::value case_29(void){
|
||||
rjp::object obj;
|
||||
obj.add("key"_sv, ""_sv);
|
||||
return rjp::steal_as<rjp::value>(obj);
|
||||
}
|
||||
|
||||
static test_pair tests[] = {
|
||||
{case_1, "null", RJP_FORMAT_NONE},
|
||||
{case_2, "5", RJP_FORMAT_NONE},
|
||||
{case_3, "true", RJP_FORMAT_NONE},
|
||||
{case_4, "false", RJP_FORMAT_NONE},
|
||||
{case_5, "{}", RJP_FORMAT_NONE},
|
||||
{case_6, "[]", RJP_FORMAT_NONE},
|
||||
// {case_7, "{}", RJP_FORMAT_NONE},
|
||||
{case_8, "{\"key\":7}", RJP_FORMAT_NONE},
|
||||
{case_9, "{\"key\":{\"subkey\":false}}", RJP_FORMAT_NONE},
|
||||
{case_10, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE},
|
||||
{case_11, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE},
|
||||
// {case_12, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE},
|
||||
// {case_13, "{\"key\":{\"subkey2\":true,\"subkey\":false}}", RJP_FORMAT_NONE},
|
||||
{case_14, "[5]", RJP_FORMAT_NONE},
|
||||
{case_15, "[[false]]", RJP_FORMAT_NONE},
|
||||
{case_16, "[[false,true]]", RJP_FORMAT_NONE},
|
||||
{case_17, "[5,[false,true]]", RJP_FORMAT_NONE},
|
||||
{case_18, "[5,{\"key\":[false]}]", RJP_FORMAT_NONE},
|
||||
{case_19, "[5,{\"key0\":true,\"key1\":false,\"key2\":true,\"key3\":false,\"key4\":true,\"key5\":false,\"key6\":true,\"key7\":false,\"key8\":true,\"key9\":false}]", RJP_FORMAT_NONE},
|
||||
// {case_20, "[5,{\"key9\":true,\"key8\":false,\"key7\":true,\"key6\":false,\"key5\":true,\"key4\":false,\"key3\":true,\"key2\":false,\"key1\":true,\"key0\":false}]", RJP_FORMAT_NONE},
|
||||
{case_21, "{\"arr\":[0,1,2,3,4,5,6,7,8,9]}", RJP_FORMAT_NONE},
|
||||
/* {case_22, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE},
|
||||
{case_23, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE},
|
||||
{case_24, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE},
|
||||
{case_25, "{\"key\":{\"subkey2\":true,\"subkey\":false}}", RJP_FORMAT_NONE},
|
||||
*/
|
||||
{case_26, "\"string\"", RJP_FORMAT_NONE},
|
||||
{case_27, "\"\"", RJP_FORMAT_NONE},
|
||||
{case_28, "{\"key\":\"string\"}", RJP_FORMAT_NONE},
|
||||
{case_29, "{\"key\":\"\"}", RJP_FORMAT_NONE},
|
||||
|
||||
{case_8, "{\"key\":7}", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_10, "{\"key\":{\"subkey\":false, \"subkey2\":true}}", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_11, "{\"key\":{\"subkey\":false, \"subkey2\":true}}", RJP_FORMAT_COMMA_SPACES},
|
||||
//{case_12, "{\"key\":{\"subkey\":false, \"subkey2\":true}}", RJP_FORMAT_COMMA_SPACES},
|
||||
//{case_13, "{\"key\":{\"subkey2\":true, \"subkey\":false}}", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_14, "[5]", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_16, "[[false, true]]", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_17, "[5, [false, true]]", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_18, "[5, {\"key\":[false]}]", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_19, "[5, {\"key0\":true, \"key1\":false, \"key2\":true, \"key3\":false, \"key4\":true, \"key5\":false, \"key6\":true, \"key7\":false, \"key8\":true, \"key9\":false}]", RJP_FORMAT_COMMA_SPACES},
|
||||
//{case_20, "[5, {\"key9\":true, \"key8\":false, \"key7\":true, \"key6\":false, \"key5\":true, \"key4\":false, \"key3\":true, \"key2\":false, \"key1\":true, \"key0\":false}]", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_21, "{\"arr\":[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}", RJP_FORMAT_COMMA_SPACES},
|
||||
//{case_22, "{\"key\":{\"subkey\":false, \"subkey2\":true}}", RJP_FORMAT_COMMA_SPACES},
|
||||
//{case_23, "{\"key\":{\"subkey\":false, \"subkey2\":true}}", RJP_FORMAT_COMMA_SPACES},
|
||||
//{case_24, "{\"key\":{\"subkey\":false, \"subkey2\":true}}", RJP_FORMAT_COMMA_SPACES},
|
||||
//{case_25, "{\"key\":{\"subkey2\":true, \"subkey\":false}}", RJP_FORMAT_COMMA_SPACES},
|
||||
|
||||
{case_8, "{\"key\": 7}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_9, "{\"key\": {\"subkey\": false}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_10, "{\"key\": {\"subkey\": false,\"subkey2\": true}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_11, "{\"key\": {\"subkey\": false,\"subkey2\": true}}", RJP_FORMAT_KEY_SPACES},
|
||||
//{case_12, "{\"key\": {\"subkey\": false,\"subkey2\": true}}", RJP_FORMAT_KEY_SPACES},
|
||||
//{case_13, "{\"key\": {\"subkey2\": true,\"subkey\": false}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_18, "[5,{\"key\": [false]}]", RJP_FORMAT_KEY_SPACES},
|
||||
{case_19, "[5,{\"key0\": true,\"key1\": false,\"key2\": true,\"key3\": false,\"key4\": true,\"key5\": false,\"key6\": true,\"key7\": false,\"key8\": true,\"key9\": false}]", RJP_FORMAT_KEY_SPACES},
|
||||
//{case_20, "[5,{\"key9\": true,\"key8\": false,\"key7\": true,\"key6\": false,\"key5\": true,\"key4\": false,\"key3\": true,\"key2\": false,\"key1\": true,\"key0\": false}]", RJP_FORMAT_KEY_SPACES},
|
||||
{case_21, "{\"arr\": [0,1,2,3,4,5,6,7,8,9]}", RJP_FORMAT_KEY_SPACES},
|
||||
//{case_22, "{\"key\": {\"subkey\": false,\"subkey2\": true}}", RJP_FORMAT_KEY_SPACES},
|
||||
//{case_23, "{\"key\": {\"subkey\": false,\"subkey2\": true}}", RJP_FORMAT_KEY_SPACES},
|
||||
//{case_24, "{\"key\": {\"subkey\": false,\"subkey2\": true}}", RJP_FORMAT_KEY_SPACES},
|
||||
//{case_25, "{\"key\": {\"subkey2\": true,\"subkey\": false}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_28, "{\"key\": \"string\"}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_29, "{\"key\": \"\"}", RJP_FORMAT_KEY_SPACES},
|
||||
|
||||
{case_5, "{}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_6, "[]", RJP_FORMAT_TABBED_LINES},
|
||||
//{case_7, "{}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_8, "{\n\t\"key\":7\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_9, "{\n\t\"key\":{\n\t\t\"subkey\":false\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_10, "{\n\t\"key\":{\n\t\t\"subkey\":false,\n\t\t\"subkey2\":true\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_11, "{\n\t\"key\":{\n\t\t\"subkey\":false,\n\t\t\"subkey2\":true\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
//{case_12, "{\n\t\"key\":{\n\t\t\"subkey\":false,\n\t\t\"subkey2\":true\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
//{case_13, "{\n\t\"key\":{\n\t\t\"subkey2\":true,\n\t\t\"subkey\":false\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_14, "[\n\t5\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_15, "[\n\t[\n\t\tfalse\n\t]\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_16, "[\n\t[\n\t\tfalse,\n\t\ttrue\n\t]\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_17, "[\n\t5,\n\t[\n\t\tfalse,\n\t\ttrue\n\t]\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_18, "[\n\t5,\n\t{\n\t\t\"key\":[\n\t\t\tfalse\n\t\t]\n\t}\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_19, "[\n\t5,\n\t{\n\t\t\"key0\":true,\n\t\t\"key1\":false,\n\t\t\"key2\":true,\n\t\t\"key3\":false,\n\t\t\"key4\":true,\n\t\t\"key5\":false,\n\t\t\"key6\":true,\n\t\t\"key7\":false,\n\t\t\"key8\":true,\n\t\t\"key9\":false\n\t}\n]", RJP_FORMAT_TABBED_LINES},
|
||||
//{case_20, "[\n\t5,\n\t{\n\t\t\"key9\":true,\n\t\t\"key8\":false,\n\t\t\"key7\":true,\n\t\t\"key6\":false,\n\t\t\"key5\":true,\n\t\t\"key4\":false,\n\t\t\"key3\":true,\n\t\t\"key2\":false,\n\t\t\"key1\":true,\n\t\t\"key0\":false\n\t}\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_21, "{\n\t\"arr\":[\n\t\t0,\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4,\n\t\t5,\n\t\t6,\n\t\t7,\n\t\t8,\n\t\t9\n\t]\n}", RJP_FORMAT_TABBED_LINES},
|
||||
//{case_22, "{\n\t\"key\":{\n\t\t\"subkey\":false,\n\t\t\"subkey2\":true\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
//{case_23, "{\n\t\"key\":{\n\t\t\"subkey\":false,\n\t\t\"subkey2\":true\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
//{case_24, "{\n\t\"key\":{\n\t\t\"subkey\":false,\n\t\t\"subkey2\":true\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
//{case_25, "{\n\t\"key\":{\n\t\t\"subkey2\":true,\n\t\t\"subkey\":false\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_28, "{\n\t\"key\":\"string\"\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_29, "{\n\t\"key\":\"\"\n}", RJP_FORMAT_TABBED_LINES},
|
||||
|
||||
{case_1, "null", RJP_FORMAT_PRETTY},
|
||||
{case_2, "5", RJP_FORMAT_PRETTY},
|
||||
{case_3, "true", RJP_FORMAT_PRETTY},
|
||||
{case_4, "false", RJP_FORMAT_PRETTY},
|
||||
{case_5, "{}", RJP_FORMAT_PRETTY},
|
||||
{case_6, "[]", RJP_FORMAT_PRETTY},
|
||||
// {case_7, "{}", RJP_FORMAT_PRETTY},
|
||||
{case_8, "{\n\t\"key\": 7\n}", RJP_FORMAT_PRETTY},
|
||||
{case_9, "{\n\t\"key\": {\n\t\t\"subkey\": false\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_10, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_11, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
// {case_12, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
// {case_13, "{\n\t\"key\": {\n\t\t\"subkey2\": true,\n\t\t\"subkey\": false\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_14, "[\n\t5\n]", RJP_FORMAT_PRETTY},
|
||||
{case_15, "[\n\t[\n\t\tfalse\n\t]\n]", RJP_FORMAT_PRETTY},
|
||||
{case_16, "[\n\t[\n\t\tfalse,\n\t\ttrue\n\t]\n]", RJP_FORMAT_PRETTY},
|
||||
{case_17, "[\n\t5,\n\t[\n\t\tfalse,\n\t\ttrue\n\t]\n]", RJP_FORMAT_PRETTY},
|
||||
{case_18, "[\n\t5,\n\t{\n\t\t\"key\": [\n\t\t\tfalse\n\t\t]\n\t}\n]", RJP_FORMAT_PRETTY},
|
||||
{case_19, "[\n\t5,\n\t{\n\t\t\"key0\": true,\n\t\t\"key1\": false,\n\t\t\"key2\": true,\n\t\t\"key3\": false,\n\t\t\"key4\": true,\n\t\t\"key5\": false,\n\t\t\"key6\": true,\n\t\t\"key7\": false,\n\t\t\"key8\": true,\n\t\t\"key9\": false\n\t}\n]", RJP_FORMAT_PRETTY},
|
||||
// {case_20, "[\n\t5,\n\t{\n\t\t\"key9\": true,\n\t\t\"key8\": false,\n\t\t\"key7\": true,\n\t\t\"key6\": false,\n\t\t\"key5\": true,\n\t\t\"key4\": false,\n\t\t\"key3\": true,\n\t\t\"key2\": false,\n\t\t\"key1\": true,\n\t\t\"key0\": false\n\t}\n]", RJP_FORMAT_PRETTY},
|
||||
{case_21, "{\n\t\"arr\": [\n\t\t0,\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4,\n\t\t5,\n\t\t6,\n\t\t7,\n\t\t8,\n\t\t9\n\t]\n}", RJP_FORMAT_PRETTY},
|
||||
/* {case_22, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_23, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_24, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_25, "{\n\t\"key\": {\n\t\t\"subkey2\": true,\n\t\t\"subkey\": false\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
*/
|
||||
{case_26, "\"string\"", RJP_FORMAT_PRETTY},
|
||||
{case_27, "\"\"", RJP_FORMAT_PRETTY},
|
||||
{case_28, "{\n\t\"key\": \"string\"\n}", RJP_FORMAT_PRETTY},
|
||||
{case_29, "{\n\t\"key\": \"\"\n}", RJP_FORMAT_PRETTY},
|
||||
};
|
||||
|
||||
int run_test(test_pair* p){
|
||||
rjp::value test = p->create();
|
||||
rjp::string buf = test.to_json(p->fmt);
|
||||
if(buf.empty()){
|
||||
if(!p->res)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
int retval = !strcmp(buf.c_str(), p->res);
|
||||
if(retval){
|
||||
fprintf(stderr, "Success\n");
|
||||
}else{
|
||||
fprintf(stderr, "Failure\n");
|
||||
fprintf(stderr, "Expected: '%s'\nGot: '%s'\n", p->res, buf.c_str());
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
int main(){
|
||||
const int num_tests = sizeof(tests) / sizeof(tests[0]);
|
||||
int passed = 0;
|
||||
fprintf(stderr, "Running %d tests that should succeed...\n", num_tests);
|
||||
for(int i = 0;i < num_tests;++i){
|
||||
fprintf(stderr, "%8d) ", i+1);
|
||||
if(run_test(&tests[i])){
|
||||
++passed;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\nResults: %d/%d tests passed\n", passed, num_tests);
|
||||
if(passed != num_tests)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
186
rjp++/tests/parse.cpp
Normal file
186
rjp++/tests/parse.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
#include "rjp_internal.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MIN(x, y) ((x < y) ? (x) : (y))
|
||||
|
||||
int read_callback(char* c, int size, const char* data, int datalen, int& datapos){
|
||||
if(datapos >= datalen)
|
||||
return 0;
|
||||
int min = MIN(size, datalen - datapos);
|
||||
int i;
|
||||
for(i = 0;i < min;++i){
|
||||
c[i] = data[datapos++];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int handle_res(const rjp::parse_res& res){
|
||||
if(res.valid()){
|
||||
fprintf(stderr, "Accepted\n");
|
||||
}else{
|
||||
fprintf(stderr, "%s\n", res.errstr().data());
|
||||
}
|
||||
return !res.valid();
|
||||
}
|
||||
int test_cbacks(const char* str, RJP_parse_flag flags){
|
||||
int pos = 0;
|
||||
rjp::parse_res res = rjp::parse_json(flags, read_callback, str, strlen(str), pos);
|
||||
return handle_res(res);
|
||||
}
|
||||
|
||||
int test(const char* str, RJP_parse_flag flags){
|
||||
rjp::parse_res res = rjp::parse_json(str, flags);
|
||||
return handle_res(res);
|
||||
}
|
||||
|
||||
struct parse_pair{
|
||||
const char* str;
|
||||
RJP_parse_flag flags;
|
||||
};
|
||||
|
||||
struct parse_pair should_pass_strings[] = {
|
||||
{"{}", RJP_PARSE_NO_EXT},
|
||||
{"[]", RJP_PARSE_NO_EXT},
|
||||
{"\"s\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\n\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\\"\"", RJP_PARSE_NO_EXT},
|
||||
{"\"str\\nstr\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\uD83D\\uDE10\"", RJP_PARSE_NO_EXT},
|
||||
{"true", RJP_PARSE_NO_EXT},
|
||||
{"false", RJP_PARSE_NO_EXT},
|
||||
{"null", RJP_PARSE_NO_EXT},
|
||||
{"5", RJP_PARSE_NO_EXT},
|
||||
{"-5", RJP_PARSE_NO_EXT},
|
||||
{"+5", RJP_PARSE_NO_EXT},
|
||||
{"5.5", RJP_PARSE_NO_EXT},
|
||||
{"-5.5", RJP_PARSE_NO_EXT},
|
||||
{"+5.5", RJP_PARSE_NO_EXT},
|
||||
{"5.5e6", RJP_PARSE_NO_EXT},
|
||||
{"-5.5e6", RJP_PARSE_NO_EXT},
|
||||
{"+5.5e6", RJP_PARSE_NO_EXT},
|
||||
{"5.5e+6", RJP_PARSE_NO_EXT},
|
||||
{"-5.5e+6", RJP_PARSE_NO_EXT},
|
||||
{"+5.5e+6", RJP_PARSE_NO_EXT},
|
||||
{"5.5e-6", RJP_PARSE_NO_EXT},
|
||||
{"-5.5e-6", RJP_PARSE_NO_EXT},
|
||||
{"+5.5e-6", RJP_PARSE_NO_EXT},
|
||||
{" {}", RJP_PARSE_NO_EXT},
|
||||
{"\n{}\n", RJP_PARSE_NO_EXT},
|
||||
{" { \"key\" \t:\n\n\n5 \n\t\n } ", RJP_PARSE_NO_EXT},
|
||||
{" {\t }\n", RJP_PARSE_NO_EXT},
|
||||
{"5.5 ", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":5}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":{}}", RJP_PARSE_NO_EXT},
|
||||
{"{\"\\uD83D\\uDE10\":5}", RJP_PARSE_NO_EXT},
|
||||
{"{\"😐\":5}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":{\"key\":5}}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":{\"key\":5,\"key2\":6}}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":{\"key\":5},\"key2\":6}", RJP_PARSE_NO_EXT},
|
||||
{"[5, 6, 7, 8, 9, \"10\"]", RJP_PARSE_NO_EXT},
|
||||
{"[[5,6],[7,8],[9,\"10\"]]", RJP_PARSE_NO_EXT},
|
||||
{"{\"arr\":[5,6,6]}", RJP_PARSE_NO_EXT},
|
||||
{"[{\"arr\":[5,6,6]}]", RJP_PARSE_NO_EXT},
|
||||
{"[{\"arr\":[5,6,6]}, 6]", RJP_PARSE_NO_EXT},
|
||||
{"[5,6,6,6,6.6]", RJP_PARSE_NO_EXT},
|
||||
{"[6,7,]", RJP_PARSE_ALLOW_TRAILING_COMMA},
|
||||
{"{\"1\":1,\"2\":2,}", RJP_PARSE_ALLOW_TRAILING_COMMA},
|
||||
{"[6,]", RJP_PARSE_ALLOW_TRAILING_COMMA},
|
||||
{"{\"1\":1,}", RJP_PARSE_ALLOW_TRAILING_COMMA},
|
||||
{"//comment\n{}", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"{\"key\"://comment\n5}", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"{\"key\"//comment\n:5}", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"{}//comment", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"{//\"key\":5\n}", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"5 //comment*/", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"{/*\"key\":5*/\"key\":5}", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"[5, /*comment*/6]", RJP_PARSE_ALLOW_COMMENTS},
|
||||
};
|
||||
const int should_pass_cnt = sizeof(should_pass_strings)/sizeof(should_pass_strings[0]);
|
||||
struct parse_pair should_fail_strings[] = {
|
||||
{"//comment\n{}", RJP_PARSE_NO_EXT},
|
||||
{"{", RJP_PARSE_NO_EXT},
|
||||
{"}", RJP_PARSE_NO_EXT},
|
||||
{"[", RJP_PARSE_NO_EXT},
|
||||
{"]", RJP_PARSE_NO_EXT},
|
||||
{"6.", RJP_PARSE_NO_EXT},
|
||||
{"6.6e", RJP_PARSE_NO_EXT},
|
||||
{"6.6e+", RJP_PARSE_NO_EXT},
|
||||
{"5e", RJP_PARSE_NO_EXT},
|
||||
{"{6}", RJP_PARSE_NO_EXT},
|
||||
{"[\"key\":5]", RJP_PARSE_NO_EXT},
|
||||
{"\"string\n\"", RJP_PARSE_NO_EXT},
|
||||
{"[3 4]", RJP_PARSE_NO_EXT},
|
||||
{"\"\\uD83D\\uDE1\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\uD83D\\uDE1Q\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\uD83\\uDE10\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\uF83D\\uDE10\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\uU83D\\uDE10\"", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":1 \"key2\":2}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\" 1}", RJP_PARSE_NO_EXT},
|
||||
{"6, 7", RJP_PARSE_NO_EXT},
|
||||
{"[,]", RJP_PARSE_NO_EXT},
|
||||
{"{, RJP_PARSE_NO_EXT}", RJP_PARSE_NO_EXT},
|
||||
{"[1, 2],", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\nkey\":5}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":\"key\n\"}", RJP_PARSE_NO_EXT},
|
||||
{"[6,7,]", RJP_PARSE_NO_EXT},
|
||||
{"{\"1\":1,\"2\":2, RJP_PARSE_NO_EXT}", RJP_PARSE_NO_EXT},
|
||||
{"[6,]", RJP_PARSE_NO_EXT},
|
||||
{"{\"1\":1, RJP_PARSE_NO_EXT}", RJP_PARSE_NO_EXT},
|
||||
{"{//comment\"key\":\n5}", RJP_PARSE_NO_EXT},
|
||||
{"{/*\"key\":*/5}", RJP_PARSE_NO_EXT},
|
||||
{"[5, /*6*/, 7]", RJP_PARSE_NO_EXT},
|
||||
{"{/*comment}", RJP_PARSE_NO_EXT},
|
||||
{"{//comment}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\"://comment\n5}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\"//comment\n:5}", RJP_PARSE_NO_EXT},
|
||||
{"{}//comment", RJP_PARSE_NO_EXT},
|
||||
{"{//\"key\":5\n}", RJP_PARSE_NO_EXT},
|
||||
{"5 //comment*/", RJP_PARSE_NO_EXT},
|
||||
{"{/*\"key\":5*/\"key\":5}", RJP_PARSE_NO_EXT},
|
||||
{"[5, /*comment*/6]", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\"//:5}", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"{,}", RJP_PARSE_ALLOW_TRAILING_COMMA},
|
||||
{"[,]", RJP_PARSE_ALLOW_TRAILING_COMMA},
|
||||
};
|
||||
const int should_fail_cnt = sizeof(should_fail_strings)/sizeof(should_fail_strings[0]);
|
||||
const int total_tests = should_pass_cnt + should_fail_cnt;
|
||||
|
||||
int run_test(int (*fun)(const char*,RJP_parse_flag)){
|
||||
int passed = 0;
|
||||
fprintf(stderr, "Running %d tests that should pass...\n", should_pass_cnt);
|
||||
for(unsigned i = 0;i < sizeof(should_pass_strings)/sizeof(should_pass_strings[0]);++i){
|
||||
fprintf(stderr, "%8d) ", i+1);
|
||||
if(!fun(should_pass_strings[i].str, should_pass_strings[i].flags)){
|
||||
++passed;
|
||||
}else{
|
||||
fprintf(stderr, "%13s%s\n", "", should_pass_strings[i].str);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Running %d tests that should fail...\n", should_fail_cnt);
|
||||
for(unsigned i = 0;i < sizeof(should_fail_strings)/sizeof(should_fail_strings[0]);++i){
|
||||
fprintf(stderr, "%8d) ", i+1);
|
||||
if(fun(should_fail_strings[i].str, should_fail_strings[i].flags)){
|
||||
++passed;
|
||||
}else{
|
||||
fprintf(stderr, "%13s%s\n", "", should_fail_strings[i].str);
|
||||
}
|
||||
}
|
||||
return passed;
|
||||
}
|
||||
int main(){
|
||||
|
||||
int normal_passed = run_test(test);
|
||||
int cback_passed = run_test(test_cbacks);
|
||||
int total_passed = normal_passed + cback_passed;
|
||||
|
||||
fprintf(stderr, "\nResults: %d/%d normal tests passed\n", normal_passed, total_tests);
|
||||
fprintf(stderr, "Results: %d/%d callback tests passed\n", cback_passed, total_tests);
|
||||
fprintf(stderr, "Results: %d/%d tests passed\n", total_passed, total_tests*2);
|
||||
if(total_passed != (total_tests*2))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
378
src/input.c
378
src/input.c
@ -1,378 +0,0 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//TODO: Scientific notation
|
||||
|
||||
#include "rjp.h"
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_value.h"
|
||||
#include "rjp_string.h"
|
||||
#include "memory.h"
|
||||
#include <stdlib.h> //strtod, strtol
|
||||
#include <stdio.h> //fprintf, stderr
|
||||
#include <string.h> //memset
|
||||
|
||||
//types of searches in the text
|
||||
typedef enum rjp_json_search_target{
|
||||
rjp_json_target_key,
|
||||
rjp_json_target_colon,
|
||||
rjp_json_target_comma,
|
||||
rjp_json_target_value,
|
||||
rjp_json_target_string,
|
||||
rjp_json_target_numeral,
|
||||
rjp_json_target_none
|
||||
}json_search_target;
|
||||
|
||||
static RJP_value* irjp_add_value(RJP_value* curr, RJP_value* lastadded, RJP_value new_val){
|
||||
new_val.parent = curr;
|
||||
if(!curr){
|
||||
curr = rjp_calloc(1, sizeof(RJP_value));
|
||||
*curr = new_val;
|
||||
return curr;
|
||||
}
|
||||
if(curr->type == rjp_json_array){
|
||||
RJP_value* last = rjp_add_element(curr);
|
||||
rjp_move_value(last, &new_val);
|
||||
return last;
|
||||
}
|
||||
*lastadded = new_val;
|
||||
return lastadded;
|
||||
}
|
||||
#define MAX_DEPTH 16
|
||||
|
||||
typedef struct RJP_string_state{
|
||||
int escaped;
|
||||
int in_utf_sequence;
|
||||
char* buffer; //store partial string here only when chunked reading and chunk ends mid string
|
||||
}RJP_string_state;
|
||||
|
||||
typedef struct RJP_numeral_state{
|
||||
int numlen;
|
||||
char* buffer; //store partial number string here only when chunked reading and chunk ends mid number
|
||||
}RJP_numeral_state;
|
||||
|
||||
typedef struct RJP_parse_state{
|
||||
RJP_value* root;
|
||||
RJP_value* curr;
|
||||
RJP_value* lastadded;
|
||||
union{
|
||||
RJP_string_state str_state;
|
||||
RJP_numeral_state num_state;
|
||||
};
|
||||
int row, column;
|
||||
int in_line_comment;
|
||||
int in_block_comment;
|
||||
int target_stack[MAX_DEPTH];
|
||||
int* target;
|
||||
}RJP_parse_state;
|
||||
|
||||
void irjp_init_parse_state(RJP_parse_state* state){
|
||||
state->root = NULL;
|
||||
state->curr = NULL;
|
||||
state->row = state->column = 0;
|
||||
state->in_line_comment = 0;
|
||||
state->in_block_comment = 0;
|
||||
memset(state->target_stack, 0, MAX_DEPTH*sizeof(int));
|
||||
state->target = state->target_stack;
|
||||
}
|
||||
|
||||
static void syntax_error(const char* msg, RJP_parse_state* state){
|
||||
DIAG_PRINT(stderr, "Syntax error! %s (%i:%i)\n", msg, state->row, state->column);
|
||||
rjp_free_value(state->root);
|
||||
}
|
||||
|
||||
//Return number of characters handled while processing comment
|
||||
int irjp_handle_comment(const char* str, RJP_parse_state* state){
|
||||
char c = *str;
|
||||
if(state->in_line_comment){
|
||||
if(c == '\n')
|
||||
state->in_line_comment = 0;
|
||||
return 1;
|
||||
}else if(state->in_block_comment){
|
||||
if(c == '*' && *(str+1) == '/'){
|
||||
state->in_block_comment = 0;
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}else if(c == '/' && *(str+1) == '*'){
|
||||
state->in_block_comment = 1;
|
||||
return 2;
|
||||
}else if(c == '/' && *(str+1) == '/'){
|
||||
state->in_line_comment = 1;
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int irjp_handle_key(const char* str, RJP_parse_state* state){
|
||||
char c = *str;
|
||||
//start of key
|
||||
if(c == '"'){
|
||||
if(state->curr == NULL){
|
||||
syntax_error("Key found outside of object definition!", state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int keylen;
|
||||
int inclen;
|
||||
char* new_string = irjp_parse_string(state->root, str+1, &inclen, &keylen, &state->row, &state->column);
|
||||
if(!new_string){
|
||||
if(!keylen)
|
||||
syntax_error("Cannot have empty key name!", state);
|
||||
return -1;
|
||||
}
|
||||
state->lastadded = rjp_add_member(state->curr, new_string, keylen);
|
||||
rjp_set_null(state->lastadded);
|
||||
*state->target = rjp_json_target_colon;
|
||||
return inclen+2;
|
||||
//end of this object (object is empty)
|
||||
}else if(c == '}'){
|
||||
state->curr = state->curr->parent;
|
||||
if(state->target != state->target_stack)
|
||||
--state->target;
|
||||
return 1;
|
||||
|
||||
//unrecognized character
|
||||
}else if(!irjp_is_whitespace(c)){
|
||||
syntax_error("Unexpected character, expected '\"'!", state);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int irjp_handle_colon(const char* str, RJP_parse_state* state){
|
||||
char c = *str;
|
||||
//colon after a key
|
||||
if(c == ':'){
|
||||
*state->target = rjp_json_target_value;
|
||||
//unrecognized character
|
||||
}else if(!irjp_is_whitespace(c)){
|
||||
syntax_error( "Unexpected character, expected ':'!", state);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
int irjp_handle_comma(const char* str, RJP_parse_state* state){
|
||||
char c = *str;
|
||||
//comma separating keys in an object or values in an array
|
||||
if(c == ','){
|
||||
*state->target = (state->curr->type == rjp_json_array ? rjp_json_target_value : rjp_json_target_key);
|
||||
|
||||
//end of object
|
||||
}else if(c == '}'){
|
||||
if(state->curr->type == rjp_json_array){
|
||||
syntax_error("Unexpected end of object within array!", state);
|
||||
return -1;
|
||||
}
|
||||
state->curr = state->curr->parent;
|
||||
if(state->target != state->target_stack)
|
||||
--state->target;
|
||||
//end of array
|
||||
}else if(c == ']' && state->curr->type == rjp_json_array){
|
||||
state->curr = state->curr->parent;
|
||||
//unrecognized character
|
||||
}else if(!irjp_is_whitespace(c)){
|
||||
syntax_error("Unexpected character, expected ','!", state);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int irjp_handle_value(const char* str, RJP_parse_state* state){
|
||||
//object
|
||||
char c = *str;
|
||||
if(c == '{'){
|
||||
if(!state->root){
|
||||
state->root = irjp_add_value(NULL, NULL, irjp_object());
|
||||
state->curr = state->root;
|
||||
*state->target = rjp_json_target_key;
|
||||
}else{
|
||||
state->curr = irjp_add_value(state->curr, state->lastadded, irjp_object());
|
||||
*state->target = rjp_json_target_comma;
|
||||
++state->target;
|
||||
*state->target = rjp_json_target_key;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if(c == '['){
|
||||
if(!state->root){
|
||||
state->root = irjp_add_value(NULL, NULL, irjp_array());
|
||||
state->curr = state->root;
|
||||
|
||||
}else{
|
||||
state->curr = irjp_add_value(state->curr, state->lastadded, irjp_array());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if(c == ']' && state->curr->type == rjp_json_array){ //empty array
|
||||
*state->target = rjp_json_target_comma;
|
||||
state->curr = state->curr->parent;
|
||||
return 1;
|
||||
}
|
||||
//strings
|
||||
else if(c == '"'){
|
||||
int vallen, inclen;
|
||||
char* new_string = irjp_parse_string(state->root, str+1, &inclen, &vallen, &state->row, &state->column);
|
||||
if(!new_string){
|
||||
if(vallen == 0){
|
||||
new_string = rjp_calloc(1, 1);
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
irjp_add_value(state->curr, state->lastadded, irjp_string(new_string, vallen));
|
||||
*state->target = rjp_json_target_comma;
|
||||
return inclen+2;
|
||||
}
|
||||
//numbers
|
||||
else if((c >= '0' && c <= '9') || c == '-'){
|
||||
if(!state->curr)
|
||||
*state->target = rjp_json_target_none;
|
||||
else
|
||||
*state->target = rjp_json_target_comma;
|
||||
int numlen;
|
||||
int floating = 0; //is an int or a double
|
||||
for(numlen = 1;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
|
||||
if(*(str+numlen) == '.'){ //if we have a decimal, make it a double and continue parsing as a number
|
||||
int i = ++numlen;
|
||||
for(;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
|
||||
if(i == numlen){ //no number after decimal
|
||||
syntax_error("Missing numerals after decimal place!", state);
|
||||
return -1;
|
||||
}
|
||||
floating = 1;
|
||||
}
|
||||
if(*(str+numlen) == '\0' && state->curr){ //hit EOF early
|
||||
syntax_error("Unexpected EOF before end of object!", state);
|
||||
return -1;
|
||||
}
|
||||
if(c == '-' && numlen == 1){ //only have a '-' with no numbers
|
||||
syntax_error("Missing numerals after '-' sign!", state);
|
||||
return -1;
|
||||
}
|
||||
if(floating){
|
||||
if(!state->root){
|
||||
state->root = state->curr = irjp_add_value(NULL, NULL, irjp_dfloat(strtod(str, NULL)));
|
||||
}else{
|
||||
irjp_add_value(state->curr, state->lastadded, irjp_dfloat(strtod(str, NULL)));
|
||||
}
|
||||
}else{
|
||||
if(!state->root){
|
||||
state->root = state->curr = irjp_add_value(NULL, NULL, irjp_integer(strtoll(str, NULL, 10)));
|
||||
}else{
|
||||
irjp_add_value(state->curr, state->lastadded, irjp_integer(strtoll(str, NULL, 10)));
|
||||
}
|
||||
}
|
||||
state->column += numlen;
|
||||
return numlen;
|
||||
}
|
||||
//booleans and null
|
||||
else if(!strncmp(str, "true", 4)){
|
||||
if(!state->curr){
|
||||
*state->target = rjp_json_target_none;
|
||||
state->root = state->curr = irjp_add_value(state->curr, NULL, irjp_boolean(1));
|
||||
}else{
|
||||
*state->target = rjp_json_target_comma;
|
||||
irjp_add_value(state->curr, state->lastadded, irjp_boolean(1));
|
||||
}
|
||||
state->column += 3;
|
||||
return 4;
|
||||
}else if(!strncmp(str, "false", 5)){
|
||||
if(!state->curr){
|
||||
*state->target = rjp_json_target_none;
|
||||
state->root = state->curr = irjp_add_value(state->curr, NULL, irjp_boolean(0));
|
||||
}else{
|
||||
*state->target = rjp_json_target_comma;
|
||||
irjp_add_value(state->curr, state->lastadded, irjp_boolean(0));
|
||||
}
|
||||
state->column += 4;
|
||||
return 5;
|
||||
}else if(!strncmp(str, "null", 4)){
|
||||
if(!state->curr){
|
||||
*state->target = rjp_json_target_none;
|
||||
state->root = state->curr = irjp_add_value(state->curr, NULL, irjp_null());
|
||||
}else{
|
||||
*state->target = rjp_json_target_comma;
|
||||
irjp_add_value(state->curr, state->lastadded, irjp_null());
|
||||
}
|
||||
state->column += 3;
|
||||
return 4;
|
||||
}
|
||||
//unrecognized character
|
||||
else if(!irjp_is_whitespace(c)){
|
||||
syntax_error("Unexpected character!", state);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
RJP_value* rjp_parse(const char* str){
|
||||
RJP_parse_state state;
|
||||
irjp_init_parse_state(&state);
|
||||
|
||||
//initially search for the root object
|
||||
*state.target = rjp_json_target_value;
|
||||
|
||||
int inc = 0;
|
||||
for(;*str != '\0';str += inc){
|
||||
char c = *str;
|
||||
|
||||
//keep track of position in input file
|
||||
if(c == '\n'){
|
||||
++state.row;
|
||||
state.column = 0;
|
||||
}else{
|
||||
++state.column;
|
||||
}
|
||||
|
||||
if((inc = irjp_handle_comment(str, &state))){
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(*state.target){
|
||||
case rjp_json_target_key:
|
||||
inc = irjp_handle_key(str, &state);
|
||||
break;
|
||||
case rjp_json_target_colon:
|
||||
inc = irjp_handle_colon(str, &state);
|
||||
break;
|
||||
case rjp_json_target_comma:
|
||||
inc = irjp_handle_comma(str, &state);
|
||||
break;
|
||||
case rjp_json_target_value:
|
||||
inc = irjp_handle_value(str, &state);
|
||||
break;
|
||||
case rjp_json_target_none:
|
||||
if(!irjp_is_whitespace(*str)){
|
||||
syntax_error("Unexpected character!", &state);
|
||||
return NULL;
|
||||
}
|
||||
inc = 1;
|
||||
break;
|
||||
default:
|
||||
inc = 1;
|
||||
break;
|
||||
};
|
||||
}
|
||||
return state.root;
|
||||
}
|
||||
RJP_value* rjp_parse_chunked(const char* str, RJP_value* prev_chunk){
|
||||
if(!prev_chunk){
|
||||
return rjp_parse(str);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
354
src/output.c
354
src/output.c
@ -16,128 +16,206 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "rjp.h"
|
||||
#include "rjp_internal.h"
|
||||
#include "memory.h"
|
||||
#include "rjp_string.h"
|
||||
#include "rjp_object.h"
|
||||
#include "rjp_value.h"
|
||||
#include "rjp_array.h"
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h> //PRId64
|
||||
#include <string.h> //strlen
|
||||
#include <stdio.h> //sprintf
|
||||
|
||||
RJP_value* rjp_new_null(void){
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
return rjp_new_null_c(&irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_new_int(RJP_int val){
|
||||
return rjp_new_int_c(val, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_new_float(RJP_float val){
|
||||
return rjp_new_float_c(val, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_new_bool(RJP_bool val){
|
||||
return rjp_new_bool_c(val, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_new_string_steal(char* val, RJP_index length){
|
||||
return rjp_new_string_steal_c(val, length, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_new_string(const char* value, RJP_index length){
|
||||
return rjp_new_string_c(value, length, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_new_object(void){
|
||||
return rjp_new_object_c(&irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_new_ordered_object(void){
|
||||
return rjp_new_ordered_object_c(&irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_new_array(void){
|
||||
return rjp_new_array_c(&irjp_default_memory_fns);
|
||||
}
|
||||
|
||||
RJP_value* rjp_new_null_c(const RJP_memory_fns* fns){
|
||||
RJP_value* ret = fns->alloc(sizeof(RJP_value));
|
||||
memset(ret, 0, sizeof(RJP_value));
|
||||
ret->type = rjp_json_null;
|
||||
return ret;
|
||||
}
|
||||
RJP_value* rjp_new_int(RJP_int val){
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
RJP_value* rjp_new_int_c(RJP_int val, const RJP_memory_fns* fns){
|
||||
RJP_value* ret = fns->alloc(sizeof(RJP_value));
|
||||
memset(ret, 0, sizeof(RJP_value));
|
||||
ret->type = rjp_json_integer;
|
||||
ret->integer = val;
|
||||
return ret;
|
||||
}
|
||||
RJP_value* rjp_new_float(RJP_float val){
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
RJP_value* rjp_new_float_c(RJP_float val, const RJP_memory_fns* fns){
|
||||
RJP_value* ret = fns->alloc(sizeof(RJP_value));
|
||||
memset(ret, 0, sizeof(RJP_value));
|
||||
ret->type = rjp_json_dfloat;
|
||||
ret->dfloat = val;
|
||||
return ret;
|
||||
}
|
||||
RJP_value* rjp_new_bool(RJP_bool val){
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
RJP_value* rjp_new_bool_c(RJP_bool val, const RJP_memory_fns* fns){
|
||||
RJP_value* ret = fns->alloc(sizeof(RJP_value));
|
||||
memset(ret, 0, sizeof(RJP_value));
|
||||
ret->type = rjp_json_boolean;
|
||||
ret->boolean = val;
|
||||
return ret;
|
||||
}
|
||||
RJP_value* rjp_new_string(char* val, RJP_index length){
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
RJP_value* rjp_new_string_steal_c(char* val, RJP_index length, const RJP_memory_fns* fns){
|
||||
RJP_value* ret = fns->alloc(sizeof(RJP_value));
|
||||
memset(ret, 0, sizeof(RJP_value));
|
||||
ret->type = rjp_json_string;
|
||||
ret->string.value = val;
|
||||
ret->string.length = length;
|
||||
return ret;
|
||||
}
|
||||
RJP_value* rjp_new_string_copy(const char* value, RJP_index length){
|
||||
RJP_value* rjp_new_string_c(const char* value, RJP_index length, const RJP_memory_fns* fns){
|
||||
UNUSED_VARIABLE(length);
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
RJP_value* ret = fns->alloc(sizeof(RJP_value));
|
||||
memset(ret, 0, sizeof(RJP_value));
|
||||
ret->type = rjp_json_string;
|
||||
RJP_index esclen = rjp_escape_strlen(value);
|
||||
ret->string.value = rjp_alloc(esclen+1);
|
||||
ret->string.value = fns->alloc(esclen+1);
|
||||
rjp_escape_strcpy(ret->string.value, value);
|
||||
ret->string.value[esclen] = 0;
|
||||
ret->string.length = esclen;
|
||||
return ret;
|
||||
}
|
||||
RJP_value* rjp_new_object(void){
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
RJP_value* rjp_new_object_c(const RJP_memory_fns* fns){
|
||||
RJP_value* ret = fns->alloc(sizeof(RJP_value));
|
||||
memset(ret, 0, sizeof(RJP_value));
|
||||
ret->type = rjp_json_object;
|
||||
return ret;
|
||||
}
|
||||
RJP_value* rjp_new_array(void){
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
RJP_value* rjp_new_ordered_object_c(const RJP_memory_fns* fns){
|
||||
RJP_value* ret = fns->alloc(sizeof(RJP_value));
|
||||
memset(ret, 0, sizeof(RJP_value));
|
||||
ret->type = rjp_json_ordered_object;
|
||||
return ret;
|
||||
}
|
||||
RJP_value* rjp_new_array_c(const RJP_memory_fns* fns){
|
||||
RJP_value* ret = fns->alloc(sizeof(RJP_value));
|
||||
memset(ret, 0, sizeof(RJP_value));
|
||||
ret->type = rjp_json_array;
|
||||
return ret;
|
||||
}
|
||||
|
||||
RJP_value* rjp_set_null(RJP_value* v){
|
||||
irjp_delete_value(v);
|
||||
return rjp_set_null_c(v, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_set_int(RJP_value* v, RJP_int val){
|
||||
return rjp_set_int_c(v, val, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_set_float(RJP_value* v, RJP_float val){
|
||||
return rjp_set_float_c(v, val, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_set_bool(RJP_value* v, RJP_bool val){
|
||||
return rjp_set_bool_c(v, val, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_set_string_steal(RJP_value* v, char* val, RJP_index len){
|
||||
return rjp_set_string_steal_c(v, val, len, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_set_string(RJP_value* v, const char* val, RJP_index length){
|
||||
return rjp_set_string_c(v, val, length, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_set_object(RJP_value* v){
|
||||
return rjp_set_object_c(v, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_set_ordered_object(RJP_value* v){
|
||||
return rjp_set_ordered_object_c(v, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_set_array(RJP_value* v){
|
||||
return rjp_set_array_c(v, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_set_null_c(RJP_value* v, const RJP_memory_fns* fns){
|
||||
irjp_delete_value(v, fns);
|
||||
v->type = rjp_json_null;
|
||||
return v;
|
||||
}
|
||||
RJP_value* rjp_set_int(RJP_value* v, RJP_int val){
|
||||
irjp_delete_value(v);
|
||||
RJP_value* rjp_set_int_c(RJP_value* v, RJP_int val, const RJP_memory_fns* fns){
|
||||
irjp_delete_value(v, fns);
|
||||
v->type = rjp_json_integer;
|
||||
v->integer = val;
|
||||
return v;
|
||||
}
|
||||
RJP_value* rjp_set_float(RJP_value* v, RJP_float val){
|
||||
irjp_delete_value(v);
|
||||
RJP_value* rjp_set_float_c(RJP_value* v, RJP_float val, const RJP_memory_fns* fns){
|
||||
irjp_delete_value(v, fns);
|
||||
v->type = rjp_json_dfloat;
|
||||
v->dfloat = val;
|
||||
return v;
|
||||
}
|
||||
RJP_value* rjp_set_bool(RJP_value* v, RJP_bool val){
|
||||
irjp_delete_value(v);
|
||||
RJP_value* rjp_set_bool_c(RJP_value* v, RJP_bool val, const RJP_memory_fns* fns){
|
||||
irjp_delete_value(v, fns);
|
||||
v->type = rjp_json_boolean;
|
||||
v->boolean = val;
|
||||
return v;
|
||||
}
|
||||
RJP_value* rjp_set_string(RJP_value* v, char* val, RJP_index len){
|
||||
irjp_delete_value(v);
|
||||
RJP_value* rjp_set_string_steal_c(RJP_value* v, char* val, RJP_index len, const RJP_memory_fns* fns){
|
||||
irjp_delete_value(v, fns);
|
||||
v->type = rjp_json_string;
|
||||
v->string.value = val;
|
||||
v->string.length = len;
|
||||
return v;
|
||||
}
|
||||
RJP_value* rjp_set_string_copy(RJP_value* v, const char* val, RJP_index length){
|
||||
RJP_value* rjp_set_string_c(RJP_value* v, const char* val, RJP_index length, const RJP_memory_fns* fns){
|
||||
UNUSED_VARIABLE(length);
|
||||
irjp_delete_value(v);
|
||||
irjp_delete_value(v, fns);
|
||||
v->type = rjp_json_string;
|
||||
RJP_index esclen = rjp_escape_strlen(val);
|
||||
v->string.value = rjp_alloc(esclen+1);
|
||||
v->string.value = fns->alloc(esclen+1);
|
||||
rjp_escape_strcpy(v->string.value, val);
|
||||
v->string.value[esclen] = 0;
|
||||
v->string.length = esclen;
|
||||
return v;
|
||||
}
|
||||
RJP_value* rjp_set_object(RJP_value* v){
|
||||
RJP_value* rjp_set_object_c(RJP_value* v, const RJP_memory_fns* fns){
|
||||
if(v->type == rjp_json_object)
|
||||
return v;
|
||||
irjp_delete_value(v);
|
||||
irjp_delete_value(v, fns);
|
||||
memset(&v->object, 0, sizeof(RJP_unordered_object));
|
||||
v->type = rjp_json_object;
|
||||
return v;
|
||||
}
|
||||
RJP_value* rjp_set_array(RJP_value* v){
|
||||
RJP_value* rjp_set_ordered_object_c(RJP_value* v, const RJP_memory_fns* fns){
|
||||
if(v->type == rjp_json_ordered_object)
|
||||
return v;
|
||||
irjp_delete_value(v, fns);
|
||||
memset(&v->orobject, 0, sizeof(RJP_ordered_object));
|
||||
v->type = rjp_json_ordered_object;
|
||||
return v;
|
||||
}
|
||||
RJP_value* rjp_set_array_c(RJP_value* v, const RJP_memory_fns* fns){
|
||||
if(v->type == rjp_json_array)
|
||||
return v;
|
||||
irjp_delete_value(v);
|
||||
irjp_delete_value(v, fns);
|
||||
memset(&v->array, 0, sizeof(RJP_array));
|
||||
v->type = rjp_json_array;
|
||||
return v;
|
||||
}
|
||||
|
||||
static RJP_index irjp_write_value(char* dest, const RJP_value* val){
|
||||
static RJP_index irjp_write_value(char* dest, const RJP_value* val, const int flags, int depth){
|
||||
RJP_index ret;
|
||||
switch(val->type){
|
||||
case rjp_json_integer:
|
||||
@ -147,91 +225,27 @@ static RJP_index irjp_write_value(char* dest, const RJP_value* val){
|
||||
ret = sprintf(dest, "%lf", val->dfloat);
|
||||
break;
|
||||
case rjp_json_boolean:
|
||||
ret = sprintf(dest, val->boolean ? "true" : "false");
|
||||
if(val->boolean){
|
||||
memcpy(dest, "true", 4);
|
||||
ret = 4;
|
||||
}else{
|
||||
memcpy(dest, "false", 5);
|
||||
ret = 5;
|
||||
}
|
||||
break;
|
||||
case rjp_json_null:
|
||||
ret = sprintf(dest, "null");
|
||||
memcpy(dest, "null", 4);
|
||||
ret = 4;
|
||||
break;
|
||||
case rjp_json_string:;
|
||||
ret = sprintf(dest, "\"%s\"", val->string.value);
|
||||
ret = irjp_copy_string_quoted(dest, val->string.value, val->string.length);
|
||||
break;
|
||||
case rjp_json_array:
|
||||
ret = rjp_dump_array(val, dest);
|
||||
ret = irjp_dump_array(val, dest, flags, depth);
|
||||
break;
|
||||
case rjp_json_object:
|
||||
ret = rjp_dump_object(val, dest);
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
static RJP_index irjp_write_value_pretty(char* dest, const RJP_value* val, int depth);
|
||||
static RJP_index irjp_dump_array_pretty(const RJP_value* arr, char* dest, int depth){
|
||||
RJP_array_iterator it;
|
||||
rjp_init_array_iterator(&it, arr);
|
||||
RJP_index pos = 2;
|
||||
sprintf(dest, "[\n");
|
||||
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
|
||||
for(int i = 0;i < (depth+1);++i)
|
||||
pos += sprintf(dest+pos, "\t");
|
||||
pos += irjp_write_value_pretty(dest+pos, current, depth+1);
|
||||
|
||||
if(rjp_array_iterator_peek(&it))
|
||||
pos += sprintf(dest+pos, ",");
|
||||
pos += sprintf(dest+pos, "\n");
|
||||
}
|
||||
for(int i = 0;i < depth;++i)
|
||||
pos += sprintf(dest+pos, "\t");
|
||||
pos += sprintf(dest+pos, "]");
|
||||
return pos;
|
||||
}
|
||||
static RJP_index irjp_dump_object_pretty(const RJP_value* root, char* dest, int depth){
|
||||
RJP_object_iterator it;
|
||||
rjp_init_object_iterator(&it, root);
|
||||
RJP_index pos = 2;
|
||||
sprintf(dest, "{\n");
|
||||
|
||||
for(RJP_value* current = rjp_object_iterator_current(&it);current;current = rjp_object_iterator_next(&it)){
|
||||
for(int i = 0;i < (depth+1);++i)
|
||||
pos += sprintf(dest+pos, "\t");
|
||||
pos += sprintf(dest+pos, "\"%s\": ", rjp_member_key(current)->value);
|
||||
pos += irjp_write_value_pretty(dest+pos, current, depth+1);
|
||||
|
||||
if(rjp_object_iterator_peek(&it))
|
||||
pos += sprintf(dest+pos, ",");
|
||||
pos += sprintf(dest+pos, "\n");
|
||||
}
|
||||
for(int i = 0;i < depth;++i)
|
||||
pos += sprintf(dest+pos, "\t");
|
||||
pos += sprintf(dest+pos, "}");
|
||||
rjp_delete_object_iterator(&it);
|
||||
return pos;
|
||||
}
|
||||
static RJP_index irjp_write_value_pretty(char* dest, const RJP_value* val, int depth){
|
||||
RJP_index ret;
|
||||
switch(val->type){
|
||||
case rjp_json_integer:
|
||||
ret = sprintf(dest, "%" PRId64, val->integer);
|
||||
break;
|
||||
case rjp_json_dfloat:
|
||||
ret = sprintf(dest, "%lf", val->dfloat);
|
||||
break;
|
||||
case rjp_json_boolean:
|
||||
ret = sprintf(dest, val->boolean ? "true" : "false");
|
||||
break;
|
||||
case rjp_json_null:
|
||||
ret = sprintf(dest, "null");
|
||||
break;
|
||||
case rjp_json_string:;
|
||||
ret = sprintf(dest, "\"%s\"", val->string.value);
|
||||
break;
|
||||
case rjp_json_array:
|
||||
ret = irjp_dump_array_pretty(val, dest, depth);
|
||||
break;
|
||||
case rjp_json_object:
|
||||
ret = irjp_dump_object_pretty(val, dest, depth);
|
||||
case rjp_json_ordered_object:
|
||||
ret = irjp_dump_object(val, dest, flags, depth);
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
@ -240,56 +254,106 @@ static RJP_index irjp_write_value_pretty(char* dest, const RJP_value* val, int d
|
||||
return ret;
|
||||
}
|
||||
|
||||
RJP_index rjp_dump_array(const RJP_value* arr, char* dest){
|
||||
RJP_index irjp_dump_array(const RJP_value* arr, char* dest, const int flags, int depth){
|
||||
RJP_index pos = 0;
|
||||
RJP_array_iterator it;
|
||||
rjp_init_array_iterator(&it, arr);
|
||||
RJP_index pos = 1;
|
||||
sprintf(dest, "[");
|
||||
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
|
||||
pos += irjp_write_value(dest+pos, current);
|
||||
RJP_value* current = rjp_array_iterator_current(&it);
|
||||
|
||||
if(rjp_array_iterator_peek(&it))
|
||||
pos += sprintf(dest+pos, ",");
|
||||
else
|
||||
break;
|
||||
if(!current){
|
||||
rjp_delete_array_iterator(&it);
|
||||
dest[pos++] = '[';
|
||||
dest[pos++] = ']';
|
||||
dest[pos] = 0;
|
||||
return pos;
|
||||
}
|
||||
pos += sprintf(dest+pos, "]");
|
||||
dest[pos++] = '[';
|
||||
if(flags & RJP_FORMAT_NEWLINES)
|
||||
dest[pos++] = '\n';
|
||||
|
||||
for(;current;current = rjp_array_iterator_next(&it)){
|
||||
if(flags & RJP_FORMAT_TABBED_LINES)
|
||||
for(int i = 0;i < (depth+1);++i)
|
||||
dest[pos++] = '\t';
|
||||
pos += irjp_write_value(dest+pos, current, flags, depth+1);
|
||||
|
||||
if(rjp_array_iterator_peek(&it)){
|
||||
dest[pos++] = ',';
|
||||
if(flags & RJP_FORMAT_COMMA_SPACES)
|
||||
dest[pos++] = ' ';
|
||||
}
|
||||
if(flags & RJP_FORMAT_NEWLINES)
|
||||
dest[pos++] = '\n';
|
||||
}
|
||||
rjp_delete_array_iterator(&it);
|
||||
|
||||
if(flags & RJP_FORMAT_TABBED_LINES)
|
||||
for(int i = 0;i < depth;++i)
|
||||
dest[pos++] = '\t';
|
||||
dest[pos++] = ']';
|
||||
dest[pos] = 0;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
RJP_index rjp_dump_object(const RJP_value* root, char* dest){
|
||||
RJP_index irjp_dump_object(const RJP_value* root, char* dest, const int flags, int depth){
|
||||
RJP_object_iterator it;
|
||||
rjp_init_object_iterator(&it, root);
|
||||
RJP_index pos = 1;
|
||||
sprintf(dest, "{");
|
||||
|
||||
for(RJP_value* current = rjp_object_iterator_current(&it);current;current = rjp_object_iterator_next(&it)){
|
||||
pos += sprintf(dest+pos, "\"%s\":", rjp_member_key(current)->value);
|
||||
pos += irjp_write_value(dest+pos, current);
|
||||
|
||||
if(rjp_object_iterator_peek(&it))
|
||||
pos += sprintf(dest+pos, ",");
|
||||
else
|
||||
break;
|
||||
}
|
||||
pos += sprintf(dest+pos, "}");
|
||||
RJP_index pos = 0;
|
||||
RJP_value* current = rjp_object_iterator_current(&it);
|
||||
if(!current){
|
||||
rjp_delete_object_iterator(&it);
|
||||
dest[pos++] = '{';
|
||||
dest[pos++] = '}';
|
||||
dest[pos] = 0;
|
||||
return pos;
|
||||
}
|
||||
dest[pos++] = '{';
|
||||
if(flags & RJP_FORMAT_NEWLINES)
|
||||
dest[pos++] = '\n';
|
||||
|
||||
for(;current;current = rjp_object_iterator_next(&it)){
|
||||
const RJP_string* key = rjp_member_key(current);
|
||||
if(flags & RJP_FORMAT_TABBED_LINES)
|
||||
for(int i = 0;i < (depth+1);++i)
|
||||
dest[pos++] = '\t';
|
||||
pos += irjp_copy_string_keyed(dest+pos, key->value, key->length);
|
||||
if(flags & RJP_FORMAT_KEY_SPACES)
|
||||
dest[pos++] = ' ';
|
||||
pos += irjp_write_value(dest+pos, current, flags, depth+1);
|
||||
|
||||
if(rjp_object_iterator_peek(&it)){
|
||||
dest[pos++] = ',';
|
||||
if(flags & RJP_FORMAT_COMMA_SPACES)
|
||||
dest[pos++] = ' ';
|
||||
}
|
||||
if(flags & RJP_FORMAT_NEWLINES)
|
||||
dest[pos++] = '\n';
|
||||
}
|
||||
rjp_delete_object_iterator(&it);
|
||||
|
||||
if(flags & RJP_FORMAT_TABBED_LINES)
|
||||
for(int i = 0;i < depth;++i)
|
||||
dest[pos++] = '\t';
|
||||
dest[pos++] = '}';
|
||||
dest[pos] = 0;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
char* rjp_to_json(const RJP_value* root, int pretty){
|
||||
char* rjp_to_json(const RJP_value* root, const int flags){
|
||||
return rjp_to_json_c(root, flags, &irjp_default_memory_fns);
|
||||
}
|
||||
char* rjp_to_json_c(const RJP_value* root, const int flags, const RJP_memory_fns* fns){
|
||||
if(!root)
|
||||
return NULL;
|
||||
RJP_index len = pretty ? irjp_value_strlen_pretty(root, 0) : irjp_value_strlen(root);
|
||||
RJP_index len = irjp_value_strlen(root, flags, 0);
|
||||
if(!len)
|
||||
return NULL;
|
||||
char* tmp = rjp_alloc(len + 1);
|
||||
char* tmp = fns->alloc(len + 1);
|
||||
tmp[len] = 0;
|
||||
|
||||
if(pretty)
|
||||
irjp_write_value_pretty(tmp, root, 0);
|
||||
else
|
||||
irjp_write_value(tmp, root);
|
||||
irjp_write_value(tmp, root, flags, 0);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
99
src/rjp.c
99
src/rjp.c
@ -19,9 +19,10 @@
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_string.h"
|
||||
#include "rjp_object.h"
|
||||
#include "rjp_array.h"
|
||||
#include "rjp_value.h"
|
||||
|
||||
#include <stdlib.h> //free, malloc
|
||||
#include <stdlib.h> //free, malloc, calloc
|
||||
|
||||
void* rjp_alloc(RJP_index nbytes){
|
||||
return malloc(nbytes);
|
||||
@ -29,32 +30,37 @@ void* rjp_alloc(RJP_index nbytes){
|
||||
void* rjp_calloc(RJP_index num, RJP_index nbytes){
|
||||
return calloc(num, nbytes);
|
||||
}
|
||||
void* rjp_realloc(void* ptr, RJP_index nbytes){
|
||||
return realloc(ptr, nbytes);
|
||||
}
|
||||
void rjp_free(void* data){
|
||||
free(data);
|
||||
}
|
||||
|
||||
const RJP_memory_fns irjp_default_memory_fns = {rjp_free, rjp_alloc};
|
||||
|
||||
RJP_value* rjp_copy_value(RJP_value* dest, const RJP_value* src){
|
||||
return rjp_copy_value_c(dest, src, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_copy_value_c(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns){
|
||||
RJP_value* ret;
|
||||
if(dest){
|
||||
irjp_delete_value(dest);
|
||||
irjp_delete_value(dest, fns);
|
||||
ret = dest;
|
||||
}else{
|
||||
ret = rjp_new_null();
|
||||
ret = rjp_new_null_c(fns);
|
||||
}
|
||||
ret->type = src->type;
|
||||
|
||||
switch(src->type){
|
||||
case rjp_json_object:
|
||||
irjp_copy_object(ret, src);
|
||||
irjp_copy_object(ret, src, fns);
|
||||
break;
|
||||
case rjp_json_ordered_object:
|
||||
irjp_copy_ordered_object(ret, src, fns);
|
||||
break;
|
||||
case rjp_json_array:
|
||||
irjp_copy_array(ret, src);
|
||||
irjp_copy_array(ret, src, fns);
|
||||
break;
|
||||
case rjp_json_string:
|
||||
irjp_strcpy(&ret->string, &src->string);
|
||||
irjp_strcpy(&ret->string, &src->string, fns);
|
||||
break;
|
||||
case rjp_json_integer:
|
||||
ret->integer = src->integer;
|
||||
@ -72,46 +78,44 @@ RJP_value* rjp_copy_value(RJP_value* dest, const RJP_value* src){
|
||||
}
|
||||
RJP_value* rjp_move_value(RJP_value* dest, RJP_value* src){
|
||||
*dest = *src;
|
||||
src->type = rjp_json_null;
|
||||
return dest;
|
||||
}
|
||||
|
||||
static void irjp_delete_array(RJP_value* root){
|
||||
static void irjp_delete_array(RJP_value* root, const RJP_memory_fns* fns){
|
||||
RJP_array_iterator it;
|
||||
rjp_init_array_iterator(&it, root);
|
||||
RJP_value* next = rjp_array_iterator_current(&it);
|
||||
|
||||
for(RJP_value* current = next;current;current = next){
|
||||
next = rjp_array_iterator_next(&it);
|
||||
switch(current->type){
|
||||
case rjp_json_object:
|
||||
irjp_delete_object(current);
|
||||
break;
|
||||
case rjp_json_array:
|
||||
irjp_delete_array(current);
|
||||
break;
|
||||
case rjp_json_string:
|
||||
free(current->string.value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
free(current);
|
||||
rjp_free_value_c(current, fns);
|
||||
}
|
||||
}
|
||||
|
||||
void irjp_delete_value(RJP_value* root){
|
||||
if((root->type) == rjp_json_object)
|
||||
irjp_delete_object(root);
|
||||
else if((root->type) == rjp_json_array)
|
||||
irjp_delete_array(root);
|
||||
else if((root->type) == rjp_json_string)
|
||||
rjp_free(root->string.value);
|
||||
void irjp_delete_value(RJP_value* root, const RJP_memory_fns* fns){
|
||||
switch(root->type){
|
||||
case rjp_json_object:
|
||||
case rjp_json_ordered_object:
|
||||
irjp_delete_object(root, fns);
|
||||
break;
|
||||
case rjp_json_array:
|
||||
irjp_delete_array(root, fns);
|
||||
break;
|
||||
case rjp_json_string:
|
||||
fns->free(root->string.value);
|
||||
break;
|
||||
default: break;
|
||||
};
|
||||
}
|
||||
void rjp_free_value(RJP_value* root){
|
||||
rjp_free_value_c(root, &irjp_default_memory_fns);
|
||||
}
|
||||
void rjp_free_value_c(RJP_value* root, const RJP_memory_fns* fns){
|
||||
if(!root)
|
||||
return;
|
||||
irjp_delete_value(root);
|
||||
free(root);
|
||||
irjp_delete_value(root, fns);
|
||||
fns->free(root);
|
||||
}
|
||||
|
||||
/* VALUE SETTING */
|
||||
@ -137,32 +141,3 @@ RJP_string* rjp_get_string(RJP_value* value){
|
||||
const RJP_string* rjp_get_cstring(const RJP_value* value){
|
||||
return &(value->string);
|
||||
}
|
||||
|
||||
|
||||
RJP_value irjp_integer(RJP_int i){
|
||||
return (RJP_value){.integer = i, .type = rjp_json_integer};
|
||||
}
|
||||
RJP_value irjp_boolean(RJP_bool b){
|
||||
return (RJP_value){.boolean = b, .type = rjp_json_boolean};
|
||||
}
|
||||
RJP_value irjp_dfloat(RJP_float d){
|
||||
return (RJP_value){.dfloat = d, .type = rjp_json_dfloat};
|
||||
}
|
||||
RJP_value irjp_string(char* c, RJP_index len){
|
||||
return (RJP_value){.string = {.value = c, .length = len}, .type = rjp_json_string};
|
||||
}
|
||||
RJP_value irjp_string_copy(const char* c){
|
||||
RJP_index esclen = rjp_escape_strlen(c);
|
||||
char* tmp = rjp_alloc(esclen+1);
|
||||
rjp_escape_strcpy(tmp, c);
|
||||
return (RJP_value){.string = {.value = tmp, .length = esclen}, .type = rjp_json_string};
|
||||
}
|
||||
RJP_value irjp_null(void){
|
||||
return (RJP_value){.integer = 0, .type = rjp_json_null};
|
||||
}
|
||||
RJP_value irjp_object(void){
|
||||
return (RJP_value){.object = {0}, .type = rjp_json_object};
|
||||
}
|
||||
RJP_value irjp_array(void){
|
||||
return (RJP_value){.array = {0}, .type = rjp_json_array};
|
||||
}
|
||||
|
||||
@ -16,39 +16,57 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rjp_array.h"
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_array.h"
|
||||
#include "rjp_value.h"
|
||||
#include "rjp_array_element.h"
|
||||
|
||||
void irjp_copy_array(RJP_value* dest, const RJP_value* src){
|
||||
#include <string.h> //memset
|
||||
|
||||
void irjp_copy_array(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns){
|
||||
dest->array.elements = dest->array.last = NULL;
|
||||
for(RJP_array_element* curr = src->array.elements;curr;curr = curr->next){
|
||||
RJP_value* copy_mem;
|
||||
copy_mem = rjp_add_element(dest);
|
||||
rjp_copy_value(copy_mem, &curr->value);
|
||||
copy_mem = rjp_new_element_c(dest, fns);
|
||||
rjp_copy_value_c(copy_mem, &curr->value, fns);
|
||||
}
|
||||
}
|
||||
void irjp_add_element(RJP_array* j){
|
||||
void irjp_add_element(RJP_array* j, const RJP_memory_fns* fns){
|
||||
++j->num_elements;
|
||||
if(!j->elements){
|
||||
j->elements = rjp_calloc(1, sizeof(RJP_array_element));
|
||||
j->elements = fns->alloc(sizeof(RJP_array_element));
|
||||
memset(j->elements, 0, sizeof(RJP_array_element));
|
||||
j->last = j->elements;
|
||||
}else{
|
||||
j->last->next = rjp_calloc(1, sizeof(RJP_array_element));
|
||||
j->last->next = fns->alloc(sizeof(RJP_array_element));
|
||||
memset(j->last->next, 0, sizeof(RJP_array_element));
|
||||
j->last = j->last->next;
|
||||
j->last->prev = j->last;
|
||||
}
|
||||
}
|
||||
RJP_value* rjp_add_element(RJP_value* dest){
|
||||
irjp_add_element(&dest->array);
|
||||
RJP_value* rjp_new_element(RJP_value* dest){
|
||||
return rjp_new_element_c(dest, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_new_element_c(RJP_value* dest, const RJP_memory_fns* fns){
|
||||
irjp_add_element(&dest->array, fns);
|
||||
dest->array.last->value.parent = dest;
|
||||
return &dest->array.last->value;
|
||||
}
|
||||
RJP_value* rjp_remove_element(RJP_value* dest, RJP_value* value){
|
||||
RJP_array* j = &dest->array;
|
||||
if(!j->num_elements)
|
||||
RJP_value* rjp_add_element(RJP_value* dest, RJP_value* src){
|
||||
return rjp_add_element_c(dest, src, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_add_element_c(RJP_value* dest, RJP_value* src, const RJP_memory_fns* fns){
|
||||
RJP_value* newelm = rjp_new_element_c(dest, fns);
|
||||
if(!newelm)
|
||||
return NULL;
|
||||
rjp_move_value(newelm, src);
|
||||
fns->free(src);
|
||||
return newelm;
|
||||
}
|
||||
RJP_value* rjp_remove_element(RJP_value* dest, RJP_value* value){
|
||||
if(value->parent != dest)
|
||||
return NULL;
|
||||
RJP_array* j = &dest->array;
|
||||
|
||||
--j->num_elements;
|
||||
RJP_array_element* elem = (RJP_array_element*)value;
|
||||
@ -65,31 +83,33 @@ RJP_value* rjp_remove_element(RJP_value* dest, RJP_value* value){
|
||||
return value;
|
||||
}
|
||||
void rjp_free_element(RJP_value* dest, RJP_value* value){
|
||||
rjp_free_element_c(dest, value, &irjp_default_memory_fns);
|
||||
}
|
||||
void rjp_free_element_c(RJP_value* dest, RJP_value* value, const RJP_memory_fns* fns){
|
||||
rjp_remove_element(dest, value);
|
||||
rjp_free_value(value);
|
||||
rjp_free_value_c(value, fns);
|
||||
}
|
||||
RJP_index rjp_num_elements(const RJP_value* array){
|
||||
return array->array.num_elements;
|
||||
}
|
||||
|
||||
void rjp_init_array_iterator(RJP_array_iterator* iter, const RJP_value* array){
|
||||
iter->current = &(array->array.elements->value);
|
||||
iter->current = (RJP_value*)(array->array.elements);
|
||||
}
|
||||
void rjp_delete_array_iterator(RJP_array_iterator* it){
|
||||
if(!it)
|
||||
return;
|
||||
it->current = NULL;
|
||||
}
|
||||
RJP_value* rjp_get_element(const RJP_value* array){
|
||||
return &array->array.elements->value;
|
||||
}
|
||||
RJP_value* rjp_array_iterator_current(RJP_array_iterator* it){
|
||||
RJP_value* rjp_array_iterator_current(const RJP_array_iterator* it){
|
||||
return it->current;
|
||||
}
|
||||
RJP_value* rjp_array_iterator_next(RJP_array_iterator* it){
|
||||
RJP_array_element* curr = (RJP_array_element*)it->current;
|
||||
it->current = curr ? &(curr->next->value) : NULL;
|
||||
it->current = curr ? (RJP_value*)(curr->next) : NULL;
|
||||
return it->current;
|
||||
}
|
||||
RJP_value* rjp_array_iterator_peek(RJP_array_iterator* it){
|
||||
RJP_value* rjp_array_iterator_peek(const RJP_array_iterator* it){
|
||||
RJP_array_element* curr = (RJP_array_element*)it->current;
|
||||
return curr ? &(curr->next->value) : NULL;
|
||||
}
|
||||
|
||||
374
src/rjp_lex.c
Normal file
374
src/rjp_lex.c
Normal file
@ -0,0 +1,374 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rjp_lex.h"
|
||||
#include "rjp.h"
|
||||
|
||||
#include <ctype.h> //isalpha, etc
|
||||
#include <string.h> //memcpy
|
||||
|
||||
void irjp_init_lex_cback_state(RJP_lex_state* state){
|
||||
state->str = rjp_alloc(RJP_LEX_CBACK_STR_SIZE+1);
|
||||
state->str[RJP_LEX_CBACK_STR_SIZE] = 0;
|
||||
state->strcap = RJP_LEX_CBACK_STR_SIZE;
|
||||
state->buff = rjp_alloc(RJP_LEX_CBACK_BUFFER_SIZE);
|
||||
state->buffcap = RJP_LEX_CBACK_BUFFER_SIZE;
|
||||
state->type = RJP_LEX_TYPE_CBACK;
|
||||
}
|
||||
void irjp_init_lex_state(RJP_lex_state* state){
|
||||
state->type = RJP_LEX_TYPE_NORMAL;
|
||||
}
|
||||
void irjp_delete_lex_state(RJP_lex_state* state){
|
||||
if(state->type == RJP_LEX_TYPE_CBACK){
|
||||
rjp_free(state->str);
|
||||
rjp_free(state->buff);
|
||||
}
|
||||
}
|
||||
|
||||
static RJP_lex_category irjp_lex_accept(RJP_lex_category val, RJP_lex_state* state){
|
||||
state->node = rjp_lex_start;
|
||||
return val;
|
||||
}
|
||||
static int irjp_is_space(char ch){
|
||||
switch(ch){
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\f':
|
||||
case '\v':
|
||||
return 1;
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_start(char ch){
|
||||
switch(ch){
|
||||
case 0:
|
||||
return rjp_lex_end;
|
||||
case '{':
|
||||
return rjp_lex_obrace;
|
||||
case '}':
|
||||
return rjp_lex_cbrace;
|
||||
case '[':
|
||||
return rjp_lex_obracket;
|
||||
case ']':
|
||||
return rjp_lex_cbracket;
|
||||
case '"':
|
||||
return rjp_lex_quote;
|
||||
case ',':
|
||||
return rjp_lex_comma;
|
||||
case ':':
|
||||
return rjp_lex_colon;
|
||||
case 't':
|
||||
return rjp_lex_t;
|
||||
case 'f':
|
||||
return rjp_lex_f;
|
||||
case 'n':
|
||||
return rjp_lex_n;
|
||||
case '/':
|
||||
return rjp_lex_slash;
|
||||
case '+':
|
||||
case '-':
|
||||
return rjp_lex_signed_number;
|
||||
case '\n':
|
||||
case '\r':
|
||||
return rjp_lex_newlines;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\f':
|
||||
return rjp_lex_spaces;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
return rjp_lex_number;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
return rjp_lex_invalid;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_spaces(char ch){
|
||||
if(irjp_is_space(ch))
|
||||
return rjp_lex_spaces;
|
||||
return rjp_lex_invalid;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_number(char ch){
|
||||
if(isdigit(ch))
|
||||
return rjp_lex_number;
|
||||
if(ch == '.')
|
||||
return rjp_lex_decimal;
|
||||
if(isalpha(ch))
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_invalid;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_signed_num(char ch){
|
||||
switch(ch){
|
||||
case '-':
|
||||
case '+':
|
||||
return rjp_lex_number;
|
||||
};
|
||||
return irjp_lex_do_number(ch);
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_decimal(char ch){
|
||||
if(isdigit(ch))
|
||||
return rjp_lex_fnumber;
|
||||
return rjp_lex_invalid;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_fnumber(char ch){
|
||||
if(isdigit(ch))
|
||||
return rjp_lex_fnumber;
|
||||
if(ch == 'e' || ch == 'E')
|
||||
return rjp_lex_fnum_e;
|
||||
if(isalpha(ch))
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_invalid;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_fnum_e(char ch){
|
||||
if(ch == '-' || ch == '+')
|
||||
return rjp_lex_sci_num_signed;
|
||||
if(isdigit(ch))
|
||||
return rjp_lex_sci_num;
|
||||
return rjp_lex_invalid;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_sci_num_signed(char ch){
|
||||
if(isdigit(ch))
|
||||
return rjp_lex_sci_num;
|
||||
return rjp_lex_invalid;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_sci_num(char ch){
|
||||
if(isdigit(ch))
|
||||
return rjp_lex_sci_num;
|
||||
if(isalpha(ch))
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_invalid;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_quote(char ch){
|
||||
switch(ch){
|
||||
case '\\':
|
||||
return rjp_lex_escaped;
|
||||
case '\n':
|
||||
case '\r':
|
||||
return rjp_lex_invalid;
|
||||
case '"':
|
||||
return rjp_lex_string;
|
||||
};
|
||||
return rjp_lex_quote;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_slash(char ch){
|
||||
switch(ch){
|
||||
case '/':
|
||||
return rjp_lex_line_comment;
|
||||
case '*':
|
||||
return rjp_lex_block_comment_start;
|
||||
};
|
||||
return rjp_lex_invalid;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_line_comment(char ch){
|
||||
switch(ch){
|
||||
case '\n':
|
||||
case '\r':
|
||||
case 0:
|
||||
return rjp_lex_invalid;
|
||||
};
|
||||
return rjp_lex_line_comment;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_block_comment_start(char ch){
|
||||
if(ch == '*')
|
||||
return rjp_lex_block_comment_end1;
|
||||
return rjp_lex_block_comment_start;
|
||||
}
|
||||
static inline RJP_lex_category irjp_lex_do_block_comment_end1(char ch){
|
||||
if(ch == '/')
|
||||
return rjp_lex_block_comment;
|
||||
return rjp_lex_block_comment_start;
|
||||
}
|
||||
static RJP_lex_category irjp_lex_char(char ch, RJP_lex_category node){
|
||||
switch(node){
|
||||
case rjp_lex_start:
|
||||
return irjp_lex_do_start(ch);
|
||||
//whitespace
|
||||
case rjp_lex_spaces:
|
||||
return irjp_lex_do_spaces(ch);
|
||||
|
||||
//numbers
|
||||
case rjp_lex_signed_number:
|
||||
return irjp_lex_do_signed_num(ch);
|
||||
case rjp_lex_number:
|
||||
return irjp_lex_do_number(ch);
|
||||
case rjp_lex_decimal:
|
||||
return irjp_lex_do_decimal(ch);
|
||||
case rjp_lex_fnumber:
|
||||
return irjp_lex_do_fnumber(ch);
|
||||
case rjp_lex_fnum_e:
|
||||
return irjp_lex_do_fnum_e(ch);
|
||||
case rjp_lex_sci_num_signed:
|
||||
return irjp_lex_do_sci_num_signed(ch);
|
||||
case rjp_lex_sci_num:
|
||||
return irjp_lex_do_sci_num(ch);
|
||||
|
||||
//strings
|
||||
case rjp_lex_quote:
|
||||
return irjp_lex_do_quote(ch);
|
||||
case rjp_lex_escaped:
|
||||
return rjp_lex_quote;
|
||||
|
||||
//comments
|
||||
case rjp_lex_slash:
|
||||
return irjp_lex_do_slash(ch);
|
||||
case rjp_lex_line_comment:
|
||||
return irjp_lex_do_line_comment(ch);
|
||||
case rjp_lex_block_comment_start:
|
||||
return irjp_lex_do_block_comment_start(ch);
|
||||
case rjp_lex_block_comment_end1:
|
||||
return irjp_lex_do_block_comment_end1(ch);
|
||||
|
||||
//true
|
||||
case rjp_lex_t:
|
||||
if(ch != 'r')
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_tr;
|
||||
case rjp_lex_tr:
|
||||
if(ch != 'u')
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_tru;
|
||||
case rjp_lex_tru:
|
||||
if(ch != 'e')
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_true;
|
||||
|
||||
//false
|
||||
case rjp_lex_f:
|
||||
if(ch != 'a')
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_fa;
|
||||
case rjp_lex_fa:
|
||||
if(ch != 'l')
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_fal;
|
||||
case rjp_lex_fal:
|
||||
if(ch != 's')
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_fals;
|
||||
case rjp_lex_fals:
|
||||
if(ch != 'e')
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_false;
|
||||
|
||||
//null
|
||||
case rjp_lex_n:
|
||||
if(ch != 'u')
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_nu;
|
||||
case rjp_lex_nu:
|
||||
if(ch != 'l')
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_nul;
|
||||
case rjp_lex_nul:
|
||||
if(ch != 'l')
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_null;
|
||||
|
||||
case rjp_lex_true:
|
||||
case rjp_lex_false:
|
||||
case rjp_lex_null:
|
||||
if(!isalnum(ch))
|
||||
return rjp_lex_invalid;
|
||||
return rjp_lex_unrecognized_word;
|
||||
|
||||
case rjp_lex_unrecognized_word:
|
||||
if(isalnum(ch))
|
||||
return rjp_lex_unrecognized_word;
|
||||
return rjp_lex_invalid;
|
||||
default:
|
||||
return rjp_lex_invalid;
|
||||
};
|
||||
return node;
|
||||
}
|
||||
//straight forward lex. All json data in single string
|
||||
//use state->str as constant string. index into it using state->offset and
|
||||
//state->length to acquire tokens in the parser
|
||||
RJP_lex_category irjp_lex(RJP_lex_state* state){
|
||||
state->offset += state->length;
|
||||
state->length = 0;
|
||||
for(const char* c = state->str+state->offset;1;++c,++state->length){
|
||||
RJP_lex_category cat = irjp_lex_char(*c, state->node);
|
||||
if(cat == rjp_lex_invalid)
|
||||
return irjp_lex_accept(state->node, state);
|
||||
state->node = cat;
|
||||
if(*c == 0)
|
||||
break;
|
||||
}
|
||||
return irjp_lex_accept(state->node, state);
|
||||
}
|
||||
|
||||
static void irjp_lex_resize_strbuf(RJP_lex_state* state, int newsize){
|
||||
char* newbuf = rjp_alloc(newsize+1);
|
||||
memcpy(newbuf, state->str, state->strcap);
|
||||
newbuf[newsize] = 0;
|
||||
rjp_free(state->str);
|
||||
state->str = newbuf;
|
||||
state->strcap = newsize;
|
||||
}
|
||||
//user callback based lexer. Not all json data is available at one time.
|
||||
//Tokens need saved in a secondary buffer for the parser to have access.
|
||||
//state->str is where the secondary buffer is located and state->length
|
||||
//is used to track its size. state->offset MUST be 0 for parser to get proper
|
||||
//token values
|
||||
RJP_lex_category irjp_lex_cback(RJP_lex_state* state, RJP_parse_callback* cbacks){
|
||||
state->length = 0;
|
||||
//pick up from previous invocation
|
||||
RJP_index chars_read = state->buffl;
|
||||
if(chars_read == 0){
|
||||
state->buffpos = 0;
|
||||
chars_read = cbacks->read(state->buff, state->buffcap, cbacks->data);
|
||||
state->buffl = chars_read;
|
||||
}
|
||||
|
||||
//loop until callback returns 0 new chars
|
||||
while(chars_read > 0){
|
||||
//loop over all characters in current buffer
|
||||
for(RJP_index i = 0;(i+state->buffpos) < chars_read;++i,++state->length){
|
||||
if(state->length == state->strcap) //need more space to store lex token
|
||||
irjp_lex_resize_strbuf(state, state->strcap*2);
|
||||
RJP_lex_category cat = irjp_lex_char(state->buff[state->buffpos+i], state->node);
|
||||
if(cat == rjp_lex_invalid){
|
||||
//save necessary state and return previous state
|
||||
state->buffpos = i + state->buffpos;
|
||||
state->str[state->length] = 0;
|
||||
return irjp_lex_accept(state->node, state);
|
||||
}
|
||||
state->str[state->length] = state->buff[state->buffpos+i];
|
||||
state->node = cat;
|
||||
}
|
||||
//read new values into buffer, reset buffer related state
|
||||
chars_read = cbacks->read(state->buff, state->buffcap, cbacks->data);
|
||||
state->buffpos = 0;
|
||||
state->buffl = chars_read;
|
||||
}
|
||||
//lexing cannot continue due to lack of input
|
||||
++state->buffpos;
|
||||
state->str[state->length] = 0;
|
||||
RJP_lex_category cat = state->node;
|
||||
state->node = rjp_lex_end;
|
||||
return cat;
|
||||
}
|
||||
409
src/rjp_object.c
409
src/rjp_object.c
@ -20,217 +20,296 @@
|
||||
#include "tree.h"
|
||||
#include "rjp_object.h"
|
||||
#include "rjp_object_member.h"
|
||||
#include "rjp_value.h"
|
||||
|
||||
#include <string.h> //strlen, strncpy
|
||||
|
||||
#define RJP_TREE_ITERATOR_STACK_START_SIZE 32
|
||||
|
||||
typedef struct RJP_tree_stack{
|
||||
RJP_tree_node** data;
|
||||
int size;
|
||||
int pos;
|
||||
}RJP_tree_stack;
|
||||
|
||||
typedef struct RJP_object_iterator_impl{
|
||||
RJP_tree_stack stack;
|
||||
}RJP_object_iterator_impl;
|
||||
|
||||
static int irjp_init_tree_stack(RJP_tree_stack* stack);
|
||||
static void irjp_delete_tree_stack(RJP_tree_stack* stack);
|
||||
static void irjp_resize_tree_stack(RJP_tree_stack* stack);
|
||||
static void irjp_push_tree_stack(RJP_tree_stack* stack, RJP_tree_node* value);
|
||||
static RJP_tree_node* irjp_pop_tree_stack(RJP_tree_stack* stack);
|
||||
static RJP_tree_node* irjp_peek_tree_stack(RJP_tree_stack* stack);
|
||||
|
||||
static int irjp_init_object_iterator(RJP_object_iterator_impl* it, const RJP_tree_node* root);
|
||||
static void irjp_delete_object_iterator(RJP_object_iterator_impl* it);
|
||||
static RJP_tree_node* irjp_object_iterator_next(RJP_object_iterator_impl* it);
|
||||
static RJP_tree_node* irjp_object_iterator_peek(RJP_object_iterator_impl* it);
|
||||
static RJP_tree_node* irjp_object_iterator_current(RJP_object_iterator_impl* it);
|
||||
|
||||
/* TREE STACK */
|
||||
//Stack construction / destruction
|
||||
static int irjp_init_tree_stack(RJP_tree_stack* stack){
|
||||
stack->data = rjp_alloc(sizeof(RJP_tree_node*)*RJP_TREE_ITERATOR_STACK_START_SIZE);
|
||||
stack->size = RJP_TREE_ITERATOR_STACK_START_SIZE;
|
||||
stack->pos = 0;
|
||||
return 0;
|
||||
void irjp_copy_object(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns){
|
||||
switch(src->type){
|
||||
case rjp_json_object:
|
||||
irjp_copy_unordered_object(dest, src, fns);
|
||||
break;
|
||||
case rjp_json_ordered_object:
|
||||
irjp_copy_ordered_object(dest, src, fns);
|
||||
break;
|
||||
default: break;
|
||||
};
|
||||
}
|
||||
static void irjp_delete_tree_stack(RJP_tree_stack* stack){
|
||||
rjp_free(stack->data);
|
||||
void irjp_delete_object(RJP_value* obj, const RJP_memory_fns* fns){
|
||||
switch(obj->type){
|
||||
case rjp_json_object:
|
||||
irjp_delete_unordered_object(obj, fns);
|
||||
break;
|
||||
case rjp_json_ordered_object:
|
||||
irjp_delete_ordered_object(obj, fns);
|
||||
break;
|
||||
default: break;
|
||||
};
|
||||
}
|
||||
static void irjp_resize_tree_stack(RJP_tree_stack* stack){
|
||||
int newsize = stack->size*2;
|
||||
RJP_tree_node** newdata = rjp_alloc(sizeof(RJP_tree_node*)*newsize);
|
||||
for(int i = 0;i < stack->size;++i){
|
||||
newdata[i] = stack->data[i];
|
||||
}
|
||||
rjp_free(stack->data);
|
||||
stack->data = newdata;
|
||||
stack->size = newsize;
|
||||
RJP_value* rjp_new_member_steal_key(RJP_value* dest, char* key, RJP_index keylen){
|
||||
return rjp_new_member_steal_key_c(dest, key, keylen, &irjp_default_memory_fns);
|
||||
}
|
||||
//Stack operations
|
||||
static void irjp_push_tree_stack(RJP_tree_stack* stack, RJP_tree_node* value){
|
||||
stack->data[stack->pos++] = value;
|
||||
if(stack->pos == stack->size)
|
||||
irjp_resize_tree_stack(stack);
|
||||
}
|
||||
static RJP_tree_node* irjp_pop_tree_stack(RJP_tree_stack* stack){
|
||||
return stack->data[--stack->pos];
|
||||
}
|
||||
static RJP_tree_node* irjp_peek_tree_stack(RJP_tree_stack* stack){
|
||||
return (stack->pos > 0) ? (stack->data[stack->pos-1]) : NULL;
|
||||
}
|
||||
static RJP_tree_node* irjp_double_peek_tree_stack(RJP_tree_stack* stack){
|
||||
return (stack->pos > 1) ? (stack->data[stack->pos-2]) : NULL;
|
||||
}
|
||||
|
||||
/* TREE ITERATOR */
|
||||
//Iterator construction / destruction
|
||||
static int irjp_init_object_iterator(RJP_object_iterator_impl* it, const RJP_tree_node* root){
|
||||
irjp_init_tree_stack(&it->stack);
|
||||
RJP_tree_node* current = (RJP_tree_node*)root;
|
||||
while(current){
|
||||
irjp_push_tree_stack(&it->stack, current);
|
||||
current = current->left;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static void irjp_delete_object_iterator(RJP_object_iterator_impl* it){
|
||||
irjp_delete_tree_stack(&it->stack);
|
||||
}
|
||||
//Iterator operations
|
||||
static RJP_tree_node* irjp_object_iterator_next(RJP_object_iterator_impl* it){
|
||||
RJP_tree_node* last = irjp_pop_tree_stack(&it->stack);
|
||||
if(last->right){
|
||||
irjp_push_tree_stack(&it->stack, last->right);
|
||||
}
|
||||
return irjp_object_iterator_current(it);
|
||||
}
|
||||
static RJP_tree_node* irjp_object_iterator_peek(RJP_object_iterator_impl* it){
|
||||
RJP_tree_node* n = irjp_peek_tree_stack(&it->stack)->right;
|
||||
if(n)
|
||||
return n;
|
||||
return irjp_double_peek_tree_stack(&it->stack);
|
||||
}
|
||||
static RJP_tree_node* irjp_object_iterator_current(RJP_object_iterator_impl* it){
|
||||
return irjp_peek_tree_stack(&it->stack);
|
||||
}
|
||||
|
||||
|
||||
/* RJP_OBJECT */
|
||||
void irjp_copy_object(RJP_value* dest, const RJP_value* src){
|
||||
irjp_free_tree(dest->object.root);
|
||||
dest->object.root = irjp_copy_tree(src->object.root);
|
||||
}
|
||||
void irjp_delete_object(RJP_value* obj){
|
||||
irjp_free_tree(obj->object.root);
|
||||
}
|
||||
static RJP_value* irjp_add_member_impl(RJP_value* dest, char* key, RJP_index keylen){
|
||||
++dest->object.num_members;
|
||||
int status;
|
||||
RJP_value* retval;
|
||||
dest->object.root = irjp_tree_insert_value(dest->object.root, key, keylen, &retval, &status);
|
||||
retval->parent = dest;
|
||||
return retval;
|
||||
}
|
||||
RJP_value* rjp_add_member(RJP_value* dest, char* key, RJP_index keylen){
|
||||
RJP_value* rjp_new_member_steal_key_c(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns){
|
||||
if(!key)
|
||||
return NULL;
|
||||
if(!keylen)
|
||||
keylen = strlen(key);
|
||||
return irjp_add_member_impl(dest, key, keylen);
|
||||
if(!(keylen = strlen(key)))
|
||||
return NULL;
|
||||
switch(dest->type){
|
||||
case rjp_json_object:
|
||||
return irjp_add_unordered_member(dest, key, keylen, fns);
|
||||
case rjp_json_ordered_object:
|
||||
return irjp_add_ordered_member(dest, key, keylen, fns);
|
||||
default: break;
|
||||
};
|
||||
return NULL;
|
||||
}
|
||||
RJP_value* rjp_add_member_key_copy(RJP_value* dest, const char* key, RJP_index keylen){
|
||||
RJP_value* rjp_new_member(RJP_value* dest, const char* key, RJP_index keylen){
|
||||
return rjp_new_member_c(dest, key, keylen, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_new_member_c(RJP_value* dest, const char* key, RJP_index keylen, const RJP_memory_fns* fns){
|
||||
if(!key)
|
||||
return NULL;
|
||||
if(!keylen)
|
||||
keylen = rjp_escape_strlen(key);
|
||||
char* newkey = rjp_alloc(keylen+1);
|
||||
if(!(keylen = rjp_escape_strlen(key)))
|
||||
return NULL;
|
||||
char* newkey = fns->alloc(keylen+1);
|
||||
rjp_escape_strcpy(newkey, key);
|
||||
newkey[keylen] = 0;
|
||||
return irjp_add_member_impl(dest, newkey, keylen);
|
||||
return rjp_new_member_steal_key_c(dest, newkey, keylen, fns);
|
||||
}
|
||||
RJP_value* rjp_add_member_steal_key(RJP_value* dest, char* key, RJP_index keylen, RJP_value* src){
|
||||
return rjp_add_member_steal_key_c(dest, key, keylen, src, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_add_member_steal_key_c(RJP_value* dest, char* key, RJP_index keylen, RJP_value* src, const RJP_memory_fns* fns){
|
||||
RJP_value* newval = rjp_new_member_steal_key_c(dest, key, keylen, fns);
|
||||
if(!newval)
|
||||
return NULL;
|
||||
rjp_move_value(newval, src);
|
||||
fns->free(src);
|
||||
return newval;
|
||||
}
|
||||
RJP_value* rjp_add_member(RJP_value* dest, const char* key, RJP_index keylen, RJP_value* src){
|
||||
return rjp_add_member_c(dest, key, keylen, src, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_add_member_c(RJP_value* dest, const char* key, RJP_index keylen, RJP_value* src, const RJP_memory_fns* fns){
|
||||
RJP_value* newval = rjp_new_member_c(dest, key, keylen, fns);
|
||||
if(!newval)
|
||||
return NULL;
|
||||
rjp_move_value(newval, src);
|
||||
fns->free(src);
|
||||
return newval;
|
||||
}
|
||||
RJP_value* rjp_remove_member_by_key(RJP_value* obj, const char* key){
|
||||
int status;
|
||||
RJP_value* removed;
|
||||
obj->object.root = irjp_tree_remove_value(obj->object.root, key, &removed, &status);
|
||||
if(removed)
|
||||
rjp_free(((RJP_object_member*)removed)->name.value);
|
||||
return removed;
|
||||
return rjp_remove_member_by_key_c(obj, key, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_remove_member_by_key_c(RJP_value* obj, const char* key, const RJP_memory_fns* fns){
|
||||
RJP_value* member = rjp_search_member(obj, key);
|
||||
if(!member)
|
||||
return NULL;
|
||||
return rjp_remove_member_c(obj, member, fns);
|
||||
}
|
||||
RJP_value* rjp_remove_member(RJP_value* obj, RJP_value* member){
|
||||
return rjp_remove_member_by_key(obj, ((RJP_object_member*)member)->name.value);
|
||||
return rjp_remove_member_c(obj, member, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_remove_member_c(RJP_value* obj, RJP_value* member, const RJP_memory_fns* fns){
|
||||
if(member->parent != obj)
|
||||
return NULL;
|
||||
switch(obj->type){
|
||||
case rjp_json_object:
|
||||
return irjp_remove_unordered_member(obj, member, fns);
|
||||
case rjp_json_ordered_object:
|
||||
return irjp_remove_ordered_member(obj, member, fns);
|
||||
default: break;
|
||||
};
|
||||
return NULL;
|
||||
}
|
||||
void rjp_free_member_by_key(RJP_value* obj, const char* key){
|
||||
rjp_free_member_by_key_c(obj, key, &irjp_default_memory_fns);
|
||||
}
|
||||
void rjp_free_member_by_key_c(RJP_value* obj, const char* key, const RJP_memory_fns* fns){
|
||||
RJP_value* removed = rjp_remove_member_by_key(obj, key);
|
||||
rjp_free_value(removed);
|
||||
rjp_free_value_c(removed, fns);
|
||||
}
|
||||
void rjp_free_member(RJP_value* obj, RJP_value* member){
|
||||
rjp_free_member_by_key(obj, ((RJP_object_member*)member)->name.value);
|
||||
rjp_free_member_c(obj, member, &irjp_default_memory_fns);
|
||||
}
|
||||
void rjp_free_member_c(RJP_value* obj, RJP_value* member, const RJP_memory_fns* fns){
|
||||
rjp_free_member_by_key_c(obj, ((RJP_object_member*)member)->name.value, fns);
|
||||
}
|
||||
|
||||
void rjp_set_key_copy(RJP_value* dest, const char* key, RJP_index keylen){
|
||||
RJP_object_member* mem = (RJP_object_member*)dest;
|
||||
if(key){
|
||||
if(!keylen){
|
||||
keylen = strlen(key);
|
||||
}
|
||||
}else{
|
||||
keylen = 0;
|
||||
}
|
||||
mem->name.value = rjp_alloc(keylen + 1);
|
||||
strncpy(mem->name.value, key, keylen);
|
||||
mem->name.value[keylen] = 0;
|
||||
mem->name.length = keylen;
|
||||
RJP_value* rjp_set_key(RJP_value* dest, const char* key, RJP_index keylen){
|
||||
return rjp_set_key_c(dest, key, keylen, &irjp_default_memory_fns);
|
||||
}
|
||||
void rjp_set_key(RJP_value* dest, char* key, RJP_index keylen){
|
||||
RJP_object_member* mem = (RJP_object_member*)dest;
|
||||
if(key){
|
||||
RJP_value* rjp_set_key_c(RJP_value* dest, const char* key, RJP_index keylen, const RJP_memory_fns* fns){
|
||||
if(!key)
|
||||
return NULL;
|
||||
if(!keylen){
|
||||
keylen = strlen(key);
|
||||
if(!(keylen = strlen(key)))
|
||||
return NULL;
|
||||
}
|
||||
}else{
|
||||
keylen = 0;
|
||||
char* newkey = fns->alloc(keylen + 1);
|
||||
strncpy(newkey, key, keylen+1);
|
||||
return rjp_set_key_steal(dest, newkey, keylen);
|
||||
}
|
||||
RJP_value* rjp_set_key_steal(RJP_value* dest, char* key, RJP_index keylen){
|
||||
return rjp_set_key_steal_c(dest, key, keylen, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_set_key_steal_c(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns){
|
||||
if(!key)
|
||||
return NULL;
|
||||
if(!keylen){
|
||||
if(!(keylen = strlen(key)))
|
||||
return NULL;
|
||||
}
|
||||
mem->name.value = key;
|
||||
mem->name.length = keylen;
|
||||
switch(dest->parent->type){
|
||||
case rjp_json_object:
|
||||
irjp_unordered_set_key(dest, key, keylen, fns);
|
||||
break;
|
||||
case rjp_json_ordered_object:
|
||||
irjp_ordered_set_key(dest, key, keylen, fns);
|
||||
break;
|
||||
default: break;
|
||||
};
|
||||
return dest;
|
||||
}
|
||||
RJP_index rjp_num_members(const RJP_value* object){
|
||||
return object->object.num_members;
|
||||
switch(object->type){
|
||||
case rjp_json_object:
|
||||
return irjp_unordered_num_members(object);
|
||||
case rjp_json_ordered_object:
|
||||
return irjp_ordered_num_members(object);
|
||||
default: break;
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
const RJP_string* rjp_member_key(const RJP_value* member){
|
||||
return &(((RJP_object_member*)member)->name);
|
||||
}
|
||||
|
||||
RJP_value* rjp_search_member(const RJP_value* object, const char* search){
|
||||
RJP_tree_node* n = irjp_tree_search_value(object->object.root, search);
|
||||
if(!n)
|
||||
if(!object)
|
||||
return NULL;
|
||||
switch(object->type){
|
||||
case rjp_json_object:
|
||||
return irjp_unordered_search_member(object, search);
|
||||
case rjp_json_ordered_object:
|
||||
return irjp_ordered_search_member(object, search);
|
||||
default: break;
|
||||
};
|
||||
return NULL;
|
||||
return &n->data.value;
|
||||
}
|
||||
|
||||
void rjp_init_object_iterator(RJP_object_iterator* it, const RJP_value* object){
|
||||
it->it = rjp_alloc(sizeof(RJP_object_iterator_impl));
|
||||
irjp_init_object_iterator(it->it, object->object.root);
|
||||
switch(object->type){
|
||||
case rjp_json_object:
|
||||
irjp_init_unordered_object_iterator(it, object);
|
||||
it->type = rjp_json_object;
|
||||
break;
|
||||
case rjp_json_ordered_object:
|
||||
irjp_init_ordered_object_iterator(it, object);
|
||||
it->type = rjp_json_ordered_object;
|
||||
break;
|
||||
default: break;
|
||||
};
|
||||
}
|
||||
void rjp_delete_object_iterator(RJP_object_iterator* it){
|
||||
irjp_delete_object_iterator(it->it);
|
||||
rjp_free(it->it);
|
||||
it->it = NULL;
|
||||
switch(it->type){
|
||||
case rjp_json_object:
|
||||
irjp_delete_unordered_object_iterator(it);
|
||||
break;
|
||||
case rjp_json_ordered_object:
|
||||
irjp_delete_ordered_object_iterator(it);
|
||||
break;
|
||||
default: break;
|
||||
};
|
||||
}
|
||||
RJP_value* rjp_object_iterator_current(RJP_object_iterator* it){
|
||||
RJP_tree_node* n = irjp_object_iterator_current(it->it);
|
||||
if(!n)
|
||||
RJP_value* rjp_object_iterator_current(const RJP_object_iterator* it){
|
||||
switch(it->type){
|
||||
case rjp_json_object:
|
||||
return irjp_unordered_object_iterator_current(it);
|
||||
break;
|
||||
case rjp_json_ordered_object:
|
||||
return irjp_ordered_object_iterator_current(it);
|
||||
break;
|
||||
default: break;
|
||||
};
|
||||
return NULL;
|
||||
return &n->data.value;
|
||||
}
|
||||
RJP_value* rjp_object_iterator_next(RJP_object_iterator* it){
|
||||
RJP_tree_node* n = irjp_object_iterator_next(it->it);
|
||||
if(!n)
|
||||
switch(it->type){
|
||||
case rjp_json_object:
|
||||
return irjp_unordered_object_iterator_next(it);
|
||||
break;
|
||||
case rjp_json_ordered_object:
|
||||
return irjp_ordered_object_iterator_next(it);
|
||||
break;
|
||||
default: break;
|
||||
};
|
||||
return NULL;
|
||||
return &n->data.value;
|
||||
}
|
||||
RJP_value* rjp_object_iterator_peek(RJP_object_iterator* it){
|
||||
RJP_tree_node* n = irjp_object_iterator_peek(it->it);
|
||||
if(!n)
|
||||
RJP_value* rjp_object_iterator_peek(const RJP_object_iterator* it){
|
||||
switch(it->type){
|
||||
case rjp_json_object:
|
||||
return irjp_unordered_object_iterator_peek(it);
|
||||
break;
|
||||
case rjp_json_ordered_object:
|
||||
return irjp_ordered_object_iterator_peek(it);
|
||||
break;
|
||||
default: break;
|
||||
};
|
||||
return NULL;
|
||||
return &n->data.value;
|
||||
}
|
||||
|
||||
RJP_value* rjp_object_to_ordered(RJP_value* object){
|
||||
return rjp_object_to_ordered_c(object, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_object_to_ordered_c(RJP_value* object, const RJP_memory_fns* fns){
|
||||
if(object->type == rjp_json_ordered_object)
|
||||
return object;
|
||||
RJP_value newobj = {0};
|
||||
newobj.type = rjp_json_ordered_object;
|
||||
|
||||
RJP_object_iterator it;
|
||||
irjp_init_unordered_object_iterator(&it, object);
|
||||
RJP_value* next = irjp_unordered_object_iterator_current(&it);
|
||||
for(RJP_value* current = next;current;current = next){
|
||||
RJP_value* newcur = irjp_add_ordered_member(&newobj, rjp_member_key(current)->value, rjp_member_key(current)->length, fns);
|
||||
((RJP_object_member*)current)->name.value = NULL;
|
||||
rjp_move_value(newcur, current);
|
||||
next = irjp_unordered_object_iterator_next(&it);
|
||||
fns->free(current);
|
||||
}
|
||||
irjp_delete_unordered_object_iterator(&it);
|
||||
|
||||
object->type = rjp_json_ordered_object;
|
||||
object->orobject = newobj.orobject;
|
||||
|
||||
return object;
|
||||
}
|
||||
RJP_value* rjp_object_to_unordered(RJP_value* object){
|
||||
return rjp_object_to_unordered_c(object, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_object_to_unordered_c(RJP_value* object, const RJP_memory_fns* fns){
|
||||
if(object->type == rjp_json_object)
|
||||
return object;
|
||||
RJP_value newobj = {0};
|
||||
newobj.type = rjp_json_object;
|
||||
|
||||
RJP_object_iterator it;
|
||||
irjp_init_ordered_object_iterator(&it, object);
|
||||
RJP_value* next = irjp_ordered_object_iterator_current(&it);
|
||||
for(RJP_value* current = next;current;current = next){
|
||||
RJP_value* newcur = irjp_add_unordered_member(&newobj, rjp_member_key(current)->value, rjp_member_key(current)->length, fns);
|
||||
((RJP_object_member*)current)->name.value = NULL;
|
||||
rjp_move_value(newcur, current);
|
||||
next = irjp_ordered_object_iterator_next(&it);
|
||||
fns->free(current);
|
||||
}
|
||||
irjp_delete_ordered_object_iterator(&it);
|
||||
|
||||
object->type = rjp_json_object;
|
||||
object->object = newobj.object;
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
146
src/rjp_ordered_object.c
Normal file
146
src/rjp_ordered_object.c
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_ordered_object.h"
|
||||
#include "rjp_object_member.h"
|
||||
#include "rjp_value.h"
|
||||
#include "rjp_string.h"
|
||||
|
||||
#include <string.h> //strcmp
|
||||
|
||||
typedef struct RJP_ordered_member{
|
||||
RJP_object_member member;
|
||||
struct RJP_ordered_member* prev;
|
||||
struct RJP_ordered_member* next;
|
||||
}RJP_ordered_member;
|
||||
|
||||
static void irjp_copy_ordered_member(RJP_ordered_member* dest, const RJP_ordered_member* src, const RJP_memory_fns* fns){
|
||||
irjp_strcpy(&dest->member.name, &src->member.name, fns);
|
||||
rjp_copy_value_c(&dest->member.value, &src->member.value, fns);
|
||||
}
|
||||
|
||||
void irjp_copy_ordered_object(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns){
|
||||
irjp_delete_ordered_object(dest, fns);
|
||||
dest->orobject.num_members = 0;
|
||||
|
||||
RJP_object_iterator it;
|
||||
irjp_init_ordered_object_iterator(&it, src);
|
||||
for(const RJP_value* current = irjp_ordered_object_iterator_current(&it);current;current = irjp_ordered_object_iterator_next(&it)){
|
||||
RJP_string keycopy;
|
||||
irjp_strcpy(&keycopy, &((RJP_object_member*)current)->name, fns);
|
||||
RJP_ordered_member* newmemb = (RJP_ordered_member*)irjp_add_ordered_member(dest, keycopy.value, keycopy.length, fns);
|
||||
irjp_copy_ordered_member(newmemb, (RJP_ordered_member*)current, fns);
|
||||
}
|
||||
irjp_delete_ordered_object_iterator(&it);
|
||||
}
|
||||
void irjp_delete_ordered_object(RJP_value* obj, const RJP_memory_fns* fns){
|
||||
RJP_object_iterator it;
|
||||
irjp_init_ordered_object_iterator(&it, obj);
|
||||
RJP_value* next = irjp_ordered_object_iterator_current(&it);
|
||||
for(RJP_value* current = next;current;current = next){
|
||||
next = irjp_ordered_object_iterator_next(&it);
|
||||
fns->free(((RJP_object_member*)current)->name.value);
|
||||
rjp_free_value_c(current, fns);
|
||||
}
|
||||
irjp_delete_ordered_object_iterator(&it);
|
||||
}
|
||||
static RJP_ordered_member* irjp_append_ordered_member(RJP_ordered_object* dest, RJP_ordered_member* newmemb){
|
||||
++dest->num_members;
|
||||
if(!dest->members){
|
||||
dest->members = dest->last = newmemb;
|
||||
newmemb->next = newmemb->prev = NULL;
|
||||
return dest->members;
|
||||
}
|
||||
dest->last->next = newmemb;
|
||||
newmemb->prev = dest->last;
|
||||
newmemb->next = NULL;
|
||||
dest->last = newmemb;
|
||||
return newmemb;
|
||||
}
|
||||
RJP_value* irjp_add_ordered_member(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns){
|
||||
RJP_ordered_member* newmemb = fns->alloc(sizeof(RJP_ordered_member));
|
||||
memset(newmemb, 0, sizeof(RJP_ordered_member));
|
||||
irjp_append_ordered_member((RJP_ordered_object*)dest, newmemb);
|
||||
irjp_ordered_set_key((RJP_value*)newmemb, key, keylen, fns);
|
||||
((RJP_value*)newmemb)->parent = dest;
|
||||
((RJP_value*)newmemb)->type = rjp_json_null;
|
||||
return (RJP_value*)newmemb;
|
||||
}
|
||||
RJP_value* irjp_remove_ordered_member(RJP_value* obj, RJP_value* member, const RJP_memory_fns* fns){
|
||||
RJP_ordered_member* current = (RJP_ordered_member*)member;
|
||||
RJP_ordered_member* prev = current->prev;
|
||||
RJP_ordered_member* next = current->next;
|
||||
|
||||
--obj->orobject.num_members;
|
||||
if(prev)
|
||||
prev->next = next;
|
||||
else
|
||||
obj->orobject.members = next;
|
||||
if(next)
|
||||
next->prev = prev;
|
||||
else
|
||||
obj->orobject.last = prev;
|
||||
|
||||
member->parent = NULL;
|
||||
fns->free(((RJP_object_member*)member)->name.value);
|
||||
return member;
|
||||
}
|
||||
void irjp_ordered_set_key(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns){
|
||||
RJP_object_member* mem = (RJP_object_member*)dest;
|
||||
fns->free(mem->name.value);
|
||||
mem->name.value = key;
|
||||
mem->name.length = keylen;
|
||||
}
|
||||
RJP_index irjp_ordered_num_members(const RJP_value* object){
|
||||
return object->orobject.num_members;
|
||||
}
|
||||
RJP_value* irjp_ordered_search_member(const RJP_value* object, const char* search){
|
||||
RJP_value* retval = NULL;
|
||||
RJP_object_iterator it;
|
||||
irjp_init_ordered_object_iterator(&it, object);
|
||||
for(RJP_value* current = irjp_ordered_object_iterator_current(&it);current;current = irjp_ordered_object_iterator_next(&it)){
|
||||
if(!strcmp(((RJP_object_member*)current)->name.value, search)){
|
||||
retval = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
irjp_delete_ordered_object_iterator(&it);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
void irjp_init_ordered_object_iterator(RJP_object_iterator* it, const RJP_value* object){
|
||||
it->current = &(object->orobject.members->member.value);
|
||||
it->type = rjp_json_ordered_object;
|
||||
}
|
||||
void irjp_delete_ordered_object_iterator(RJP_object_iterator* it){
|
||||
it->current = NULL;
|
||||
}
|
||||
RJP_value* irjp_ordered_object_iterator_current(const RJP_object_iterator* it){
|
||||
return it->current;
|
||||
}
|
||||
RJP_value* irjp_ordered_object_iterator_next(RJP_object_iterator* it){
|
||||
RJP_ordered_member* curr = (RJP_ordered_member*)it->current;
|
||||
it->current = curr ? &(curr->next->member.value) : NULL;
|
||||
return it->current;
|
||||
}
|
||||
RJP_value* irjp_ordered_object_iterator_peek(const RJP_object_iterator* it){
|
||||
RJP_ordered_member* curr = (RJP_ordered_member*)it->current;
|
||||
return curr ? &(curr->next->member.value) : NULL;
|
||||
}
|
||||
448
src/rjp_parse.c
Normal file
448
src/rjp_parse.c
Normal file
@ -0,0 +1,448 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//TODO: Scientific notation
|
||||
|
||||
#include "rjp.h"
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_value.h"
|
||||
#include "rjp_string.h"
|
||||
#include "rjp_lex.h"
|
||||
|
||||
#include <stdlib.h> //strtod, strtol
|
||||
#include <string.h> //memcpy
|
||||
|
||||
|
||||
#define RJP_INITIAL_PARSE_DEPTH 16
|
||||
|
||||
typedef enum RJP_parse_status{
|
||||
RJP_PARSE_STATUS_SUC,
|
||||
RJP_PARSE_STATUS_ERR,
|
||||
RJP_PARSE_STATUS_MISSING_VALUE,
|
||||
RJP_PARSE_STATUS_MISSING_COMMA,
|
||||
RJP_PARSE_STATUS_INVALID,
|
||||
RJP_PARSE_STATUS_NO_ROOT_VALUE,
|
||||
RJP_PARSE_STATUS_MISSING_KEY,
|
||||
RJP_PARSE_STATUS_MISSING_COLON,
|
||||
RJP_PARSE_STATUS_EXCESS_DATA,
|
||||
RJP_PARSE_STATUS_MISSING_CLOSE_BRACE,
|
||||
}RJP_parse_status;
|
||||
|
||||
|
||||
typedef enum RJP_parse_target{
|
||||
rjp_parse_end,
|
||||
rjp_parse_start,
|
||||
rjp_parse_first_mem_key,
|
||||
rjp_parse_mem_key,
|
||||
rjp_parse_arr_first_value,
|
||||
rjp_parse_arr_value,
|
||||
rjp_parse_arr_comma,
|
||||
rjp_parse_key_colon,
|
||||
rjp_parse_obj_value,
|
||||
rjp_parse_obj_comma
|
||||
}RJP_parse_target;
|
||||
|
||||
typedef struct RJP_parse_stack{
|
||||
RJP_parse_target* stack;
|
||||
RJP_index position;
|
||||
RJP_index size;
|
||||
}RJP_parse_stack;
|
||||
typedef struct RJP_parse_state{
|
||||
RJP_parse_stack target_stack;
|
||||
RJP_value* root;
|
||||
RJP_value* curr;
|
||||
RJP_value* lastadded;
|
||||
RJP_lex_state lexstate;
|
||||
int row, column;
|
||||
_Bool allow_comments;
|
||||
_Bool allow_trail_comma;
|
||||
}RJP_parse_state;
|
||||
|
||||
static void irjp_init_parse_stack(RJP_parse_stack* s){
|
||||
s->size = RJP_INITIAL_PARSE_DEPTH;
|
||||
s->stack = rjp_alloc(sizeof(RJP_parse_target)*s->size);
|
||||
s->position = 0;
|
||||
s->stack[0] = rjp_parse_start;
|
||||
}
|
||||
static void irjp_delete_parse_stack(RJP_parse_stack* s){
|
||||
rjp_free(s->stack);
|
||||
s->stack = NULL;
|
||||
}
|
||||
static void irjp_resize_parse_stack(RJP_parse_stack* s, RJP_index newsize){
|
||||
RJP_parse_target* newstack = rjp_alloc(sizeof(RJP_parse_target) * newsize);
|
||||
memcpy(newstack, s->stack, s->size*sizeof(RJP_parse_target));
|
||||
rjp_free(s->stack);
|
||||
s->stack = newstack;
|
||||
s->size = newsize;
|
||||
}
|
||||
static void irjp_parse_stack_push(RJP_parse_stack* s, RJP_parse_target target){
|
||||
if((s->position+1) == s->size)
|
||||
irjp_resize_parse_stack(s, s->size*2);
|
||||
s->stack[++s->position] = target;
|
||||
}
|
||||
static RJP_parse_target irjp_parse_stack_pop(RJP_parse_stack* s){
|
||||
return s->stack[s->position--];
|
||||
}
|
||||
static RJP_parse_target irjp_parse_stack_current(RJP_parse_stack* s){
|
||||
return s->stack[s->position];
|
||||
}
|
||||
static void irjp_parse_stack_set(RJP_parse_stack* s, RJP_parse_target target){
|
||||
s->stack[s->position] = target;
|
||||
}
|
||||
|
||||
static int irjp_init_value(RJP_value* newval, RJP_lex_category cat, RJP_parse_state* state, const RJP_memory_fns* fns){
|
||||
RJP_index length = state->lexstate.length;
|
||||
RJP_index offset = state->lexstate.offset;
|
||||
const char* str = state->lexstate.str + offset;
|
||||
switch(cat){
|
||||
case rjp_lex_string:;
|
||||
RJP_index newlength;
|
||||
newval->type = rjp_json_string;
|
||||
newval->string.value = irjp_convert_string(str, length, &newlength, fns);
|
||||
if(!newval->string.value)
|
||||
return 1;
|
||||
newval->string.length = newlength;
|
||||
break;
|
||||
case rjp_lex_number:
|
||||
newval->type = rjp_json_integer;
|
||||
newval->integer = strtoll(str, NULL, 10);
|
||||
break;
|
||||
case rjp_lex_fnumber:
|
||||
case rjp_lex_sci_num:
|
||||
newval->type = rjp_json_dfloat;
|
||||
newval->dfloat = strtod(str, NULL);
|
||||
break;
|
||||
case rjp_lex_true:
|
||||
newval->type = rjp_json_boolean;
|
||||
newval->boolean = 1;
|
||||
break;
|
||||
case rjp_lex_false:
|
||||
newval->type = rjp_json_boolean;
|
||||
newval->boolean = 0;
|
||||
break;
|
||||
case rjp_lex_null:
|
||||
newval->type = rjp_json_null;
|
||||
break;
|
||||
case rjp_lex_obrace:
|
||||
newval->type = rjp_json_object;
|
||||
irjp_parse_stack_push(&state->target_stack, rjp_parse_first_mem_key);
|
||||
state->curr = state->lastadded;
|
||||
break;
|
||||
case rjp_lex_obracket:
|
||||
newval->type = rjp_json_array;
|
||||
irjp_parse_stack_push(&state->target_stack, rjp_parse_arr_first_value);
|
||||
state->curr = state->lastadded;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
static RJP_value* irjp_add_value_to_array(RJP_lex_category cat, RJP_parse_state* state, const RJP_memory_fns* fns){
|
||||
state->lastadded = rjp_new_element_c(state->curr, fns);
|
||||
if(irjp_init_value(state->lastadded, cat, state, fns))
|
||||
return NULL;
|
||||
return state->lastadded;
|
||||
}
|
||||
static RJP_value* irjp_add_value_to_object(RJP_parse_state* state, const char* key, RJP_index keylen, const RJP_memory_fns* fns){
|
||||
RJP_index newlen;
|
||||
char* newkey = irjp_convert_string(key, keylen, &newlen, fns);
|
||||
if(!newlen){ //cannot have empty key
|
||||
fns->free(newkey);
|
||||
return NULL;
|
||||
}
|
||||
return (state->lastadded = rjp_new_member_steal_key_c(state->curr, newkey, newlen, fns));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static RJP_lex_category irjp_convert_comment(_Bool allow_comments){
|
||||
if(allow_comments)
|
||||
return rjp_lex_spaces;
|
||||
return rjp_lex_invalid;
|
||||
}
|
||||
|
||||
static void irjp_init_parse_state(RJP_parse_state* state, const char* str, const RJP_memory_fns* fns){
|
||||
state->column = 1;
|
||||
state->row = 1;
|
||||
|
||||
irjp_init_parse_stack(&state->target_stack);
|
||||
state->lexstate.str = (char*)str;
|
||||
state->root = state->curr = state->lastadded = fns->alloc(sizeof(RJP_value));
|
||||
memset(state->root, 0, sizeof(RJP_value));
|
||||
}
|
||||
static void irjp_delete_parse_state(RJP_parse_state* state){
|
||||
irjp_delete_parse_stack(&state->target_stack);
|
||||
irjp_delete_lex_state(&state->lexstate);
|
||||
}
|
||||
static void irjp_delete_parse_state_no_preserve_root(RJP_parse_state* state, const RJP_memory_fns* fns){
|
||||
irjp_delete_parse_state(state);
|
||||
rjp_free_value_c(state->root, fns);
|
||||
state->root = NULL;
|
||||
}
|
||||
|
||||
static int irjp_parse_handle_lexcat(RJP_lex_category cat, RJP_parse_state* state, const RJP_memory_fns* fns){
|
||||
if(cat == rjp_lex_line_comment || cat == rjp_lex_block_comment)
|
||||
cat = irjp_convert_comment(state->allow_comments);
|
||||
|
||||
if(cat == rjp_lex_spaces)
|
||||
return RJP_PARSE_STATUS_SUC;
|
||||
if(cat == rjp_lex_newlines){
|
||||
state->row = 1;
|
||||
++(state->column);
|
||||
return RJP_PARSE_STATUS_SUC;
|
||||
}
|
||||
if(cat == rjp_lex_invalid)
|
||||
return RJP_PARSE_STATUS_INVALID;
|
||||
|
||||
switch(irjp_parse_stack_current(&state->target_stack)){
|
||||
|
||||
case rjp_parse_start:
|
||||
irjp_parse_stack_set(&state->target_stack, rjp_parse_end);
|
||||
if(irjp_init_value(state->root, cat, state, fns)){
|
||||
return RJP_PARSE_STATUS_NO_ROOT_VALUE;
|
||||
}
|
||||
break;
|
||||
case rjp_parse_first_mem_key:
|
||||
if(cat == rjp_lex_cbrace){
|
||||
irjp_parse_stack_pop(&state->target_stack);
|
||||
state->curr = state->curr->parent;
|
||||
}else{
|
||||
//fallthrough
|
||||
case rjp_parse_mem_key:
|
||||
if(cat == rjp_lex_string){
|
||||
irjp_parse_stack_set(&state->target_stack, rjp_parse_key_colon);
|
||||
if(!irjp_add_value_to_object(state, state->lexstate.str+state->lexstate.offset, state->lexstate.length, fns)){
|
||||
return RJP_PARSE_STATUS_MISSING_KEY;
|
||||
}
|
||||
}else{
|
||||
return RJP_PARSE_STATUS_MISSING_KEY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case rjp_parse_arr_first_value:
|
||||
if(cat == rjp_lex_cbracket){
|
||||
irjp_parse_stack_pop(&state->target_stack);
|
||||
state->curr = state->curr->parent;
|
||||
}else{
|
||||
//fallthrough
|
||||
case rjp_parse_arr_value:
|
||||
irjp_parse_stack_set(&state->target_stack, rjp_parse_arr_comma);
|
||||
if(!irjp_add_value_to_array(cat, state, fns))
|
||||
return RJP_PARSE_STATUS_MISSING_VALUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case rjp_parse_key_colon:
|
||||
if(cat != rjp_lex_colon)
|
||||
return RJP_PARSE_STATUS_MISSING_COLON;
|
||||
irjp_parse_stack_set(&state->target_stack, rjp_parse_obj_value);
|
||||
break;
|
||||
case rjp_parse_obj_value:
|
||||
irjp_parse_stack_set(&state->target_stack, rjp_parse_obj_comma);
|
||||
if(irjp_init_value(state->lastadded, cat, state, fns)){
|
||||
return RJP_PARSE_STATUS_MISSING_VALUE;
|
||||
}
|
||||
break;
|
||||
case rjp_parse_obj_comma:
|
||||
if(cat == rjp_lex_comma){
|
||||
irjp_parse_stack_set(&state->target_stack, state->allow_trail_comma ? rjp_parse_first_mem_key : rjp_parse_mem_key);
|
||||
}else if(cat == rjp_lex_cbrace){
|
||||
irjp_parse_stack_pop(&state->target_stack);
|
||||
state->curr = state->curr->parent;
|
||||
}else{
|
||||
return RJP_PARSE_STATUS_MISSING_COMMA;
|
||||
}
|
||||
break;
|
||||
|
||||
case rjp_parse_arr_comma:
|
||||
if(cat == rjp_lex_comma){
|
||||
irjp_parse_stack_set(&state->target_stack, state->allow_trail_comma ? rjp_parse_arr_first_value : rjp_parse_arr_value);
|
||||
}else if(cat == rjp_lex_cbracket){
|
||||
irjp_parse_stack_pop(&state->target_stack);
|
||||
state->curr = state->curr->parent;
|
||||
}else{
|
||||
return RJP_PARSE_STATUS_MISSING_COMMA;
|
||||
}
|
||||
break;
|
||||
|
||||
case rjp_parse_end:
|
||||
if(state->lexstate.str[state->lexstate.offset] != 0)
|
||||
return RJP_PARSE_STATUS_EXCESS_DATA;
|
||||
};
|
||||
return RJP_PARSE_STATUS_SUC;
|
||||
}
|
||||
//Handle the final token returned by the lexer. rjp_lex_end is a nonaccepting state to break the
|
||||
//parse loop. it is a successful state though as it just indicates end of input.
|
||||
static int irjp_handle_final_parse_token(RJP_parse_state* state, RJP_lex_category cat){
|
||||
if(state->target_stack.position != 0)
|
||||
return RJP_PARSE_STATUS_MISSING_CLOSE_BRACE;
|
||||
if(cat == rjp_lex_end)
|
||||
return RJP_PARSE_STATUS_SUC;
|
||||
return RJP_PARSE_STATUS_INVALID;
|
||||
}
|
||||
|
||||
//Basic parse loop
|
||||
static int irjp_parse(RJP_parse_state* state, const RJP_memory_fns* fns){
|
||||
RJP_lex_category cat;
|
||||
RJP_parse_status status;
|
||||
for(cat = irjp_lex(&state->lexstate);cat & rjp_lex_accept;cat = irjp_lex(&state->lexstate),state->row += state->lexstate.length){
|
||||
if((status = irjp_parse_handle_lexcat(cat, state, fns)) != RJP_PARSE_STATUS_SUC)
|
||||
return status;
|
||||
}
|
||||
return irjp_handle_final_parse_token(state, cat);
|
||||
}
|
||||
|
||||
//Callback parse loop
|
||||
static int irjp_parse_cback(RJP_parse_state* state, RJP_parse_callback* cback, const RJP_memory_fns* fns){
|
||||
RJP_lex_category cat;
|
||||
RJP_parse_status status;
|
||||
for(cat = irjp_lex_cback(&state->lexstate, cback);cat & rjp_lex_accept;cat = irjp_lex_cback(&state->lexstate, cback),state->row += state->lexstate.length){
|
||||
if((status = irjp_parse_handle_lexcat(cat, state, fns)) != RJP_PARSE_STATUS_SUC)
|
||||
return status;
|
||||
}
|
||||
return irjp_handle_final_parse_token(state, cat);
|
||||
}
|
||||
|
||||
char* rjp_parse_error_to_string(const RJP_parse_error* err){
|
||||
return rjp_parse_error_to_string_c(err, &irjp_default_memory_fns);
|
||||
}
|
||||
char* rjp_parse_error_to_string_c(const RJP_parse_error* err, const RJP_memory_fns* fns){
|
||||
const RJP_parse_state* state = (const RJP_parse_state*)err->parsestate;
|
||||
RJP_parse_status status = err->errcode;
|
||||
char* buffer = NULL;
|
||||
const char* format = NULL;
|
||||
switch(status){
|
||||
case RJP_PARSE_STATUS_MISSING_VALUE:
|
||||
format = "Expected value before '%.*s'";
|
||||
buffer = fns->alloc(snprintf(NULL, 0, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset)) + 1);
|
||||
sprintf(buffer, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset));
|
||||
break;
|
||||
case RJP_PARSE_STATUS_MISSING_COMMA:
|
||||
format = "Expected comma before '%.*s'";
|
||||
buffer = fns->alloc(snprintf(NULL, 0, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset)) + 1);
|
||||
sprintf(buffer, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset));
|
||||
break;
|
||||
case RJP_PARSE_STATUS_INVALID:
|
||||
format = "Invalid lex token '%.*s'";
|
||||
buffer = fns->alloc(snprintf(NULL, 0, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset)) + 1);
|
||||
sprintf(buffer, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset));
|
||||
break;
|
||||
case RJP_PARSE_STATUS_NO_ROOT_VALUE:
|
||||
format = "Missing root JSON value";
|
||||
buffer = fns->alloc(snprintf(NULL, 0, "%s", format) + 1);
|
||||
sprintf(buffer, "%s", format);
|
||||
break;
|
||||
case RJP_PARSE_STATUS_MISSING_KEY:
|
||||
format = "Expected key before '%.*s'";
|
||||
buffer = fns->alloc(snprintf(NULL, 0, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset)) + 1);
|
||||
sprintf(buffer, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset));
|
||||
break;
|
||||
case RJP_PARSE_STATUS_MISSING_COLON:
|
||||
format = "Expected colon before '%.*s'";
|
||||
buffer = fns->alloc(snprintf(NULL, 0, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset)) + 1);
|
||||
sprintf(buffer, format, (int)state->lexstate.length, (state->lexstate.str + state->lexstate.offset));
|
||||
break;
|
||||
case RJP_PARSE_STATUS_EXCESS_DATA:
|
||||
format = "Excess data after JSON";
|
||||
buffer = fns->alloc(snprintf(NULL, 0, "%s", format) + 1);
|
||||
sprintf(buffer, "%s", format);
|
||||
break;
|
||||
case RJP_PARSE_STATUS_MISSING_CLOSE_BRACE:
|
||||
format = "Missing closing brace";
|
||||
buffer = fns->alloc(snprintf(NULL, 0, "%s", format) + 1);
|
||||
sprintf(buffer, "%s", format);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
return buffer;
|
||||
}
|
||||
void rjp_delete_parse_error(RJP_parse_error* err){
|
||||
rjp_delete_parse_error_c(err, &irjp_default_memory_fns);
|
||||
}
|
||||
void rjp_delete_parse_error_c(RJP_parse_error* err, const RJP_memory_fns* fns){
|
||||
irjp_delete_parse_state_no_preserve_root((RJP_parse_state*)err->parsestate, fns);
|
||||
rjp_free(err->parsestate);
|
||||
}
|
||||
|
||||
RJP_value* rjp_simple_parse(const char* str){
|
||||
return rjp_parse_c(str, RJP_PARSE_NO_EXT, NULL, &irjp_default_memory_fns);
|
||||
}
|
||||
|
||||
RJP_value* rjp_parse(const char* str, int flags, RJP_parse_error* err){
|
||||
return rjp_parse_c(str, flags, err, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_parse_c(const char* str, int flags, RJP_parse_error* err, const RJP_memory_fns* fns){
|
||||
RJP_parse_state* state = rjp_calloc(sizeof(RJP_parse_state), 1);
|
||||
state->allow_comments = (flags & RJP_PARSE_ALLOW_COMMENTS);
|
||||
state->allow_trail_comma = (flags & RJP_PARSE_ALLOW_TRAILING_COMMA);
|
||||
|
||||
irjp_init_parse_state(state, str, fns);
|
||||
irjp_init_lex_state(&state->lexstate);
|
||||
int status = irjp_parse(state, fns);
|
||||
if(status == RJP_PARSE_STATUS_SUC){
|
||||
irjp_delete_parse_state(state);
|
||||
RJP_value* root = state->root;
|
||||
rjp_free(state);
|
||||
return root;
|
||||
}else{
|
||||
if(err){
|
||||
err->parsestate = state;
|
||||
err->errcode = status;
|
||||
err->row = state->column;
|
||||
err->column = state->row;
|
||||
}else{
|
||||
irjp_delete_parse_state_no_preserve_root(state, fns);
|
||||
rjp_free(state);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//Callback based parse. Runs identical to normal parsing except sets up callback
|
||||
//lex state and calls callback lex function
|
||||
RJP_value* rjp_parse_cback(int flags, RJP_parse_callback* cback, RJP_parse_error* err){
|
||||
return rjp_parse_cback_c(flags, cback, err, &irjp_default_memory_fns);
|
||||
}
|
||||
RJP_value* rjp_parse_cback_c(int flags, RJP_parse_callback* cback, RJP_parse_error* err, const RJP_memory_fns* fns){
|
||||
RJP_parse_state* state = rjp_calloc(sizeof(RJP_parse_state), 1);
|
||||
state->allow_comments = (flags & RJP_PARSE_ALLOW_COMMENTS);
|
||||
state->allow_trail_comma = (flags & RJP_PARSE_ALLOW_TRAILING_COMMA);
|
||||
|
||||
irjp_init_parse_state(state, NULL, fns);
|
||||
irjp_init_lex_cback_state(&state->lexstate);
|
||||
int status = irjp_parse_cback(state, cback, fns);
|
||||
if(status == RJP_PARSE_STATUS_SUC){
|
||||
irjp_delete_parse_state(state);
|
||||
RJP_value* root = state->root;
|
||||
rjp_free(state);
|
||||
return root;
|
||||
}else{
|
||||
if(err){
|
||||
err->parsestate = state;
|
||||
err->errcode = status;
|
||||
err->row = state->column;
|
||||
err->column = state->row;
|
||||
}else{
|
||||
irjp_delete_parse_state_no_preserve_root(state, fns);
|
||||
rjp_free(state);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
224
src/rjp_string.c
224
src/rjp_string.c
@ -16,16 +16,13 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "rjp_string.h"
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_object.h"
|
||||
#include "rjp_string.h"
|
||||
#include "rjp_value.h"
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h> //PRId64
|
||||
#include <stdio.h> //fprintf
|
||||
#include <stdlib.h> //malloc, free
|
||||
#include <stdint.h> //uintN_t
|
||||
#include <string.h> //strcpy
|
||||
|
||||
@ -63,6 +60,8 @@ static int decode_unicode_escape(const char* str, uint32_t* high, uint32_t* low)
|
||||
return *low = *high = 0;
|
||||
}
|
||||
*low = utf_strtol_4(str+8);
|
||||
if(!*low)
|
||||
return *low = *high = 0;
|
||||
return 12;
|
||||
}else{
|
||||
*low = 0;
|
||||
@ -152,85 +151,88 @@ static int codepoint_to_u8(char* dest, uint32_t codepoint){
|
||||
}
|
||||
}*/
|
||||
|
||||
//Convert escape sequences in strings
|
||||
char* irjp_parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column){
|
||||
char* new_string;
|
||||
++(*column); //account for starting quotation mark
|
||||
int oldpos = 0;
|
||||
int newpos = 0;
|
||||
for(;*(str+oldpos) != '"';++oldpos, ++(newpos), ++(*column)){
|
||||
if(*(str+oldpos) == '\\'){
|
||||
if(*(str+oldpos+1) == 'u'){
|
||||
int irjp_copy_string_quoted(char* restrict dest, const char* restrict string, RJP_index length){
|
||||
dest[0] = '"';
|
||||
memcpy(dest+1, string, length);
|
||||
dest[length+1] = '"';
|
||||
return length+2;
|
||||
}
|
||||
int irjp_copy_string_keyed(char* restrict dest, const char* restrict string, RJP_index length){
|
||||
int offset = irjp_copy_string_quoted(dest, string, length);
|
||||
dest[offset] = ':';
|
||||
return offset+1;
|
||||
}
|
||||
char* irjp_convert_string(const char* str, RJP_index length, RJP_index* newlen, const RJP_memory_fns* fns){
|
||||
char* newstring;
|
||||
RJP_index oldpos = 1; //ignore opening quote
|
||||
--length; //ignore closing quote
|
||||
RJP_index newlength = 0;
|
||||
for(;oldpos < length;++newlength,++oldpos){
|
||||
if(str[oldpos] == '\\'){
|
||||
if(str[oldpos+1] == 'u'){
|
||||
//unicode escape
|
||||
uint32_t high, low;
|
||||
oldpos += (decode_unicode_escape(str+oldpos, &high, &low)-1);
|
||||
newpos += (codepoint_strlen(utf_to_codepoint(high, low))-1);
|
||||
if(high == 0)
|
||||
return NULL;
|
||||
newlength += (codepoint_strlen(utf_to_codepoint(high, low))-1);
|
||||
}else{
|
||||
++oldpos;
|
||||
++(*column);
|
||||
}
|
||||
}else if(*(str+oldpos) == '\0'){
|
||||
newpos = 1;
|
||||
fprintf(stderr, "Syntax error! %s (%i:%i)\n", "Unexpected EOF in string!", *row, *column);
|
||||
rjp_free_value(root);
|
||||
return NULL;
|
||||
}else if(*(str+oldpos) == '\n'){
|
||||
++(*row);
|
||||
*column = 0;
|
||||
}
|
||||
}
|
||||
*inclen = oldpos;
|
||||
*len = newpos;
|
||||
if(newpos == 0){
|
||||
}else if(str[oldpos] == 0){
|
||||
return NULL;
|
||||
}
|
||||
new_string = rjp_alloc(newpos + 1);
|
||||
new_string[newpos] = 0;
|
||||
for(int i = 0;*str != '"';++i,++str){
|
||||
if(*str == '\\'){
|
||||
++str;
|
||||
switch(*str){
|
||||
}
|
||||
newstring = fns->alloc(newlength + 1);
|
||||
newstring[newlength] = 0;
|
||||
*newlen = newlength;
|
||||
oldpos = 1;
|
||||
for(RJP_index newpos = 0;oldpos < length;++newpos,++oldpos){
|
||||
if(str[oldpos] == '\\'){
|
||||
++oldpos;
|
||||
switch(str[oldpos]){
|
||||
case '"':
|
||||
new_string[i] = '"';
|
||||
newstring[newpos] = '"';
|
||||
break;
|
||||
case 'n':
|
||||
new_string[i] = '\n';
|
||||
newstring[newpos] = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
new_string[i] = '\r';
|
||||
newstring[newpos] = '\r';
|
||||
break;
|
||||
case 'b':
|
||||
new_string[i] = '\b';
|
||||
newstring[newpos] = '\b';
|
||||
break;
|
||||
case '\\':
|
||||
new_string[i] = '\\';
|
||||
newstring[newpos] = '\\';
|
||||
break;
|
||||
case 't':
|
||||
new_string[i] = '\t';
|
||||
newstring[newpos] = '\t';
|
||||
break;
|
||||
case 'f':
|
||||
new_string[i] = '\f';
|
||||
newstring[newpos] = '\f';
|
||||
break;
|
||||
case 'u':;
|
||||
uint32_t high, low;
|
||||
uint32_t codepoint;
|
||||
--str;
|
||||
str += (decode_unicode_escape(str, &high, &low) - 1);
|
||||
--oldpos;
|
||||
oldpos += (decode_unicode_escape(str+oldpos, &high, &low)-1);
|
||||
if(!high){
|
||||
rjp_free(new_string);
|
||||
rjp_free(newstring);
|
||||
return NULL;
|
||||
}
|
||||
codepoint = utf_to_codepoint(high, low);
|
||||
i += (codepoint_to_u8(new_string+i, codepoint)-1);
|
||||
newpos += (codepoint_to_u8(newstring+newpos, codepoint)-1);
|
||||
break;
|
||||
default:
|
||||
new_string[i] = *str;
|
||||
newstring[newpos] = str[oldpos];
|
||||
break;
|
||||
}
|
||||
};
|
||||
}else{
|
||||
new_string[i] = *str;
|
||||
newstring[newpos] = str[oldpos];
|
||||
}
|
||||
}
|
||||
return new_string;
|
||||
return newstring;
|
||||
}
|
||||
|
||||
RJP_index rjp_escape_strcpy(char* dest, const char* src){
|
||||
@ -273,8 +275,8 @@ RJP_index rjp_escape_strcpy(char* dest, const char* src){
|
||||
dest[j] = 0;
|
||||
return j;
|
||||
}
|
||||
void irjp_strcpy(RJP_string* dest, const RJP_string* src){
|
||||
dest->value = rjp_alloc(src->length + 1);
|
||||
void irjp_strcpy(RJP_string* dest, const RJP_string* src, const RJP_memory_fns* fns){
|
||||
dest->value = fns->alloc(src->length + 1);
|
||||
strcpy(dest->value, src->value);
|
||||
dest->value[src->length] = 0;
|
||||
dest->length = src->length;
|
||||
@ -299,78 +301,69 @@ RJP_index rjp_escape_strlen(const char* str){
|
||||
}
|
||||
return count;
|
||||
}
|
||||
RJP_index irjp_array_strlen(const RJP_value* arr){
|
||||
RJP_string rjp_escape(const char* src){
|
||||
RJP_index esclen = rjp_escape_strlen(src);
|
||||
char* dest = rjp_alloc(esclen+1);
|
||||
rjp_escape_strcpy(dest, src);
|
||||
return (RJP_string){.value = dest, .length = esclen};
|
||||
}
|
||||
RJP_index irjp_array_strlen(const RJP_value* arr, const int flags, int depth){
|
||||
RJP_index count = 2; //[]
|
||||
RJP_array_iterator it;
|
||||
rjp_init_array_iterator(&it, arr);
|
||||
|
||||
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
|
||||
count += irjp_value_strlen(current);
|
||||
if(rjp_array_iterator_peek(&it))
|
||||
++count; //,
|
||||
}
|
||||
return count;
|
||||
}
|
||||
RJP_index irjp_array_strlen_pretty(const RJP_value* arr, int depth){
|
||||
RJP_index count = 3 + depth; //[\n\t]
|
||||
if(flags & RJP_FORMAT_NEWLINES)
|
||||
++count;
|
||||
if(flags & RJP_FORMAT_TABBED_LINES)
|
||||
count += depth;
|
||||
++depth;
|
||||
|
||||
RJP_array_iterator it;
|
||||
rjp_init_array_iterator(&it, arr);
|
||||
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
|
||||
count += irjp_value_strlen_pretty(current, depth);
|
||||
count += depth; //tabs
|
||||
++count; //newline
|
||||
if(rjp_array_iterator_peek(&it))
|
||||
count += irjp_value_strlen(current, flags, depth);
|
||||
if(flags & RJP_FORMAT_NEWLINES)
|
||||
++count;
|
||||
if(flags & RJP_FORMAT_TABBED_LINES)
|
||||
count += depth;
|
||||
if(rjp_array_iterator_peek(&it)){
|
||||
++count; //,
|
||||
if(flags & RJP_FORMAT_COMMA_SPACES)
|
||||
++count; //space
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
RJP_index irjp_object_strlen(const RJP_value* root){
|
||||
RJP_index irjp_object_strlen(const RJP_value* root, const int flags, int depth){
|
||||
RJP_index count = 2; //{}
|
||||
RJP_object_iterator it;
|
||||
rjp_init_object_iterator(&it, root);
|
||||
RJP_value* current = rjp_object_iterator_current(&it);
|
||||
while(current){
|
||||
RJP_index namelen = rjp_member_key(current)->length;
|
||||
const char* name = rjp_member_key(current)->value;
|
||||
if(namelen == 0 || name[0] == 0){
|
||||
rjp_delete_object_iterator(&it);
|
||||
return 0;
|
||||
}
|
||||
count += namelen + 3; //"":
|
||||
count += irjp_value_strlen(current);
|
||||
if((current = rjp_object_iterator_next(&it)))
|
||||
++count; //,
|
||||
}
|
||||
rjp_delete_object_iterator(&it);
|
||||
return count;
|
||||
}
|
||||
RJP_index irjp_object_strlen_pretty(const RJP_value* root, int depth){
|
||||
RJP_index count = 3 + depth; //{\n\t}
|
||||
if(flags & RJP_FORMAT_NEWLINES)
|
||||
++count;
|
||||
if(flags & RJP_FORMAT_TABBED_LINES)
|
||||
count += depth;
|
||||
++depth;
|
||||
|
||||
RJP_object_iterator it;
|
||||
rjp_init_object_iterator(&it, root);
|
||||
RJP_value* current = rjp_object_iterator_current(&it);
|
||||
while(current){
|
||||
RJP_index namelen = rjp_member_key(current)->length;
|
||||
const char* name = rjp_member_key(current)->value;
|
||||
if(namelen == 0 || name[0] == 0){
|
||||
rjp_delete_object_iterator(&it);
|
||||
return 0;
|
||||
}
|
||||
count += namelen + 4; //"":space
|
||||
count += irjp_value_strlen_pretty(current, depth);
|
||||
count += depth; //tabs
|
||||
++count; //newline
|
||||
if((current = rjp_object_iterator_next(&it)))
|
||||
count += namelen + 3; //"":
|
||||
if(flags & RJP_FORMAT_KEY_SPACES)
|
||||
++count;
|
||||
count += irjp_value_strlen(current, flags, depth);
|
||||
if(flags & RJP_FORMAT_NEWLINES)
|
||||
++count;
|
||||
if(flags & RJP_FORMAT_TABBED_LINES)
|
||||
count += depth;
|
||||
if((current = rjp_object_iterator_next(&it))){
|
||||
++count; //,
|
||||
if(flags & RJP_FORMAT_COMMA_SPACES)
|
||||
++count; //space
|
||||
}
|
||||
}
|
||||
rjp_delete_object_iterator(&it);
|
||||
return count;
|
||||
}
|
||||
|
||||
RJP_index irjp_value_strlen(const RJP_value* root){
|
||||
RJP_index irjp_value_strlen(const RJP_value* root, const int flags, int depth){
|
||||
switch(root->type){
|
||||
case rjp_json_integer:
|
||||
return snprintf(NULL, 0, "%" PRId64, root->integer);
|
||||
@ -381,31 +374,12 @@ RJP_index irjp_value_strlen(const RJP_value* root){
|
||||
case rjp_json_null:
|
||||
return 4;
|
||||
case rjp_json_string:
|
||||
return rjp_escape_strlen(root->string.value) + 2; //"";
|
||||
return rjp_escape_strlen(root->string.value) + 2; //""
|
||||
case rjp_json_array:
|
||||
return irjp_array_strlen(root);
|
||||
return irjp_array_strlen(root, flags, depth);
|
||||
case rjp_json_object:
|
||||
return irjp_object_strlen(root);
|
||||
default:
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
RJP_index irjp_value_strlen_pretty(const RJP_value* root, int depth){
|
||||
switch(root->type){
|
||||
case rjp_json_integer:
|
||||
return snprintf(NULL, 0, "%" PRId64, root->integer);
|
||||
case rjp_json_dfloat:
|
||||
return snprintf(NULL, 0, "%lf", root->dfloat);
|
||||
case rjp_json_boolean:
|
||||
return root->boolean ? 4 : 5; //true, false
|
||||
case rjp_json_null:
|
||||
return 4;
|
||||
case rjp_json_string:
|
||||
return rjp_escape_strlen(root->string.value) + 2; //"";
|
||||
case rjp_json_array:
|
||||
return irjp_array_strlen_pretty(root, depth);
|
||||
case rjp_json_object:
|
||||
return irjp_object_strlen_pretty(root, depth);
|
||||
case rjp_json_ordered_object:
|
||||
return irjp_object_strlen(root, flags, depth);
|
||||
default:
|
||||
return 0;
|
||||
};
|
||||
|
||||
176
src/rjp_unordered_object.c
Normal file
176
src/rjp_unordered_object.c
Normal file
@ -0,0 +1,176 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_unordered_object.h"
|
||||
#include "rjp_value.h"
|
||||
#include "rjp_object_member.h"
|
||||
#include "tree.h"
|
||||
|
||||
#define RJP_TREE_ITERATOR_STACK_START_SIZE 32
|
||||
|
||||
typedef struct RJP_tree_stack{
|
||||
RJP_tree_node** data;
|
||||
int size;
|
||||
int pos;
|
||||
}RJP_tree_stack;
|
||||
|
||||
typedef struct RJP_object_iterator_impl{
|
||||
RJP_tree_stack stack;
|
||||
}RJP_object_iterator_impl;
|
||||
|
||||
static int irjp_init_tree_stack(RJP_tree_stack* stack);
|
||||
static void irjp_delete_tree_stack(RJP_tree_stack* stack);
|
||||
static void irjp_resize_tree_stack(RJP_tree_stack* stack);
|
||||
static void irjp_push_tree_stack(RJP_tree_stack* stack, RJP_tree_node* value);
|
||||
static RJP_tree_node* irjp_pop_tree_stack(RJP_tree_stack* stack);
|
||||
static RJP_tree_node* irjp_peek_tree_stack(RJP_tree_stack* stack);
|
||||
|
||||
/* TREE STACK */
|
||||
//Stack construction / destruction
|
||||
static int irjp_init_tree_stack(RJP_tree_stack* stack){
|
||||
stack->data = rjp_alloc(sizeof(RJP_tree_node*)*RJP_TREE_ITERATOR_STACK_START_SIZE);
|
||||
stack->size = RJP_TREE_ITERATOR_STACK_START_SIZE;
|
||||
stack->pos = 0;
|
||||
return 0;
|
||||
}
|
||||
static void irjp_delete_tree_stack(RJP_tree_stack* stack){
|
||||
rjp_free(stack->data);
|
||||
}
|
||||
static void irjp_resize_tree_stack(RJP_tree_stack* stack){
|
||||
int newsize = stack->size*2;
|
||||
RJP_tree_node** newdata = rjp_alloc(sizeof(RJP_tree_node*)*newsize);
|
||||
for(int i = 0;i < stack->size;++i){
|
||||
newdata[i] = stack->data[i];
|
||||
}
|
||||
rjp_free(stack->data);
|
||||
stack->data = newdata;
|
||||
stack->size = newsize;
|
||||
}
|
||||
//Stack operations
|
||||
static void irjp_push_tree_stack(RJP_tree_stack* stack, RJP_tree_node* value){
|
||||
stack->data[stack->pos++] = value;
|
||||
if(stack->pos == stack->size)
|
||||
irjp_resize_tree_stack(stack);
|
||||
}
|
||||
static RJP_tree_node* irjp_pop_tree_stack(RJP_tree_stack* stack){
|
||||
return stack->data[--stack->pos];
|
||||
}
|
||||
static RJP_tree_node* irjp_peek_tree_stack(RJP_tree_stack* stack){
|
||||
return (stack->pos > 0) ? (stack->data[stack->pos-1]) : NULL;
|
||||
}
|
||||
static RJP_tree_node* irjp_double_peek_tree_stack(RJP_tree_stack* stack){
|
||||
return (stack->pos > 1) ? (stack->data[stack->pos-2]) : NULL;
|
||||
}
|
||||
|
||||
/* RJP_OBJECT */
|
||||
void irjp_copy_unordered_object(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns){
|
||||
dest->object.root = irjp_copy_tree(src->object.root, fns);
|
||||
}
|
||||
void irjp_delete_unordered_object(RJP_value* obj, const RJP_memory_fns* fns){
|
||||
irjp_free_tree(obj->object.root, fns);
|
||||
}
|
||||
RJP_value* irjp_add_unordered_member(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns){
|
||||
++dest->object.num_members;
|
||||
int status;
|
||||
RJP_value* retval;
|
||||
dest->object.root = irjp_tree_insert_value(dest->object.root, key, keylen, &retval, &status, fns);
|
||||
retval->parent = dest;
|
||||
retval->type = rjp_json_null;
|
||||
return retval;
|
||||
}
|
||||
RJP_value* irjp_remove_unordered_member(RJP_value* obj, RJP_value* member, const RJP_memory_fns* fns){
|
||||
RJP_tree_node* removed_node = NULL;
|
||||
obj->object.root = irjp_tree_remove_value(obj->object.root, (RJP_tree_node*)member, &removed_node);
|
||||
fns->free(((RJP_object_member*)removed_node)->name.value);
|
||||
member->parent = NULL;
|
||||
--obj->object.num_members;
|
||||
return member;
|
||||
}
|
||||
|
||||
void irjp_unordered_set_key(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns){
|
||||
RJP_value* parent = dest->parent;
|
||||
RJP_tree_node* removed_node = NULL;
|
||||
parent->object.root = irjp_tree_remove_value(parent->object.root, (RJP_tree_node*)dest, &removed_node);
|
||||
|
||||
fns->free(((RJP_object_member*)removed_node)->name.value);
|
||||
((RJP_object_member*)removed_node)->name.value = key;
|
||||
((RJP_object_member*)removed_node)->name.length = keylen;
|
||||
|
||||
parent->object.root = irjp_tree_insert_node(parent->object.root, removed_node);
|
||||
}
|
||||
RJP_index irjp_unordered_num_members(const RJP_value* object){
|
||||
return object->object.num_members;
|
||||
}
|
||||
|
||||
RJP_value* irjp_unordered_search_member(const RJP_value* object, const char* search){
|
||||
RJP_tree_node* n = irjp_tree_search_value(object->object.root, search);
|
||||
if(!n)
|
||||
return NULL;
|
||||
return &n->data.value;
|
||||
}
|
||||
|
||||
void irjp_init_unordered_object_iterator(RJP_object_iterator* it, const RJP_value* object){
|
||||
it->it = rjp_alloc(sizeof(RJP_object_iterator_impl));
|
||||
|
||||
RJP_object_iterator_impl* itimpl = it->it;
|
||||
RJP_tree_node* root = object ? object->object.root : NULL;
|
||||
irjp_init_tree_stack(&itimpl->stack);
|
||||
RJP_tree_node* current = (RJP_tree_node*)root;
|
||||
while(current){
|
||||
irjp_push_tree_stack(&itimpl->stack, current);
|
||||
current = current->left;
|
||||
}
|
||||
}
|
||||
void irjp_delete_unordered_object_iterator(RJP_object_iterator* it){
|
||||
if(!it->it)
|
||||
return;
|
||||
irjp_delete_tree_stack(&it->it->stack);
|
||||
rjp_free(it->it);
|
||||
it->it = NULL;
|
||||
}
|
||||
RJP_value* irjp_unordered_object_iterator_current(const RJP_object_iterator* it){
|
||||
RJP_tree_node* n = irjp_peek_tree_stack(&it->it->stack);
|
||||
if(!n)
|
||||
return NULL;
|
||||
return &n->data.value;
|
||||
}
|
||||
RJP_value* irjp_unordered_object_iterator_next(RJP_object_iterator* it){
|
||||
RJP_object_iterator_impl* itimp = it->it;
|
||||
RJP_tree_node* last = irjp_pop_tree_stack(&itimp->stack);
|
||||
if(last->right){
|
||||
irjp_push_tree_stack(&itimp->stack, last->right);
|
||||
last = last->right;
|
||||
while(last->left){
|
||||
irjp_push_tree_stack(&itimp->stack, last->left);
|
||||
last = last->left;
|
||||
}
|
||||
}
|
||||
return irjp_unordered_object_iterator_current(it);
|
||||
}
|
||||
RJP_value* irjp_unordered_object_iterator_peek(const RJP_object_iterator* it){
|
||||
RJP_object_iterator_impl* itimp = it->it;
|
||||
RJP_tree_node* n = irjp_peek_tree_stack(&itimp->stack)->right;
|
||||
if(n)
|
||||
return &n->data.value;
|
||||
n = irjp_double_peek_tree_stack(&itimp->stack);
|
||||
if(!n)
|
||||
return NULL;
|
||||
return &n->data.value;
|
||||
}
|
||||
|
||||
164
src/tree.c
164
src/tree.c
@ -18,13 +18,11 @@
|
||||
|
||||
#include "tree.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "rjp_string.h"
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_object.h"
|
||||
#include "rjp_value.h"
|
||||
|
||||
#include <string.h> //memset, strcmp
|
||||
|
||||
#define BLACK 0
|
||||
#define RED 1
|
||||
@ -41,15 +39,14 @@ static inline int irjp_is_inside_right(RJP_tree_node* n);
|
||||
static void irjp_replace_node(RJP_tree_node *restrict node, RJP_tree_node *restrict child);
|
||||
|
||||
static void irjp_copy_node_data(RJP_tree_node *restrict dest, RJP_tree_node *restrict src);
|
||||
static RJP_tree_node* irjp_clone_node(const RJP_tree_node* src, RJP_tree_node* parent);
|
||||
static RJP_tree_node* irjp_copy_node(const RJP_tree_node* root, RJP_tree_node* parent);
|
||||
static void irjp_free_node(RJP_tree_node* node);
|
||||
static void irjp_delete_node(RJP_tree_node* node);
|
||||
static RJP_tree_node* irjp_clone_node(const RJP_tree_node* src, RJP_tree_node* parent, const RJP_memory_fns* fns);
|
||||
static RJP_tree_node* irjp_copy_node(const RJP_tree_node* root, RJP_tree_node* parent, const RJP_memory_fns* fns);
|
||||
static void irjp_free_node(RJP_tree_node* node, const RJP_memory_fns* fns);
|
||||
static void irjp_delete_node(RJP_tree_node* node, const RJP_memory_fns* fns);
|
||||
|
||||
static RJP_tree_node* irjp_tree_insert_node(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode);
|
||||
static RJP_tree_node* irjp_tree_insert_impl(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode);
|
||||
static RJP_tree_node* irjp_tree_repair(RJP_tree_node* node);
|
||||
static RJP_tree_node* irjp_tree_remove_node(RJP_tree_node* target, RJP_value** removed_node);
|
||||
static RJP_tree_node* irjp_tree_remove_node(RJP_tree_node* target, RJP_tree_node** removed_node);
|
||||
|
||||
|
||||
//Tree helpers
|
||||
@ -125,8 +122,9 @@ static void irjp_replace_node(RJP_tree_node *restrict node, RJP_tree_node *restr
|
||||
}
|
||||
|
||||
//Node construction / destruction
|
||||
RJP_tree_node* irjp_new_node(char* key, RJP_index keylen){
|
||||
RJP_tree_node* node = rjp_calloc(sizeof(RJP_tree_node), 1);
|
||||
RJP_tree_node* irjp_new_node(char* key, RJP_index keylen, const RJP_memory_fns* fns){
|
||||
RJP_tree_node* node = fns->alloc(sizeof(RJP_tree_node));
|
||||
memset(node, 0, sizeof(RJP_tree_node));
|
||||
node->data.name.value = key;
|
||||
node->data.name.length = keylen;
|
||||
node->parent = node->left = node->right = NULL;
|
||||
@ -136,59 +134,60 @@ RJP_tree_node* irjp_new_node(char* key, RJP_index keylen){
|
||||
static void irjp_copy_node_data(RJP_tree_node *restrict dest, RJP_tree_node *restrict src){
|
||||
dest->data = src->data;
|
||||
}
|
||||
static RJP_tree_node* irjp_clone_node(const RJP_tree_node* src, RJP_tree_node* parent){
|
||||
RJP_tree_node* dest = rjp_alloc(sizeof(RJP_tree_node));
|
||||
rjp_copy_value(&dest->data.value, &src->data.value);
|
||||
irjp_strcpy(&dest->data.name, &src->data.name);
|
||||
static RJP_tree_node* irjp_clone_node(const RJP_tree_node* src, RJP_tree_node* parent, const RJP_memory_fns* fns){
|
||||
RJP_tree_node* dest = fns->alloc(sizeof(RJP_tree_node));
|
||||
memset(dest, 0, sizeof(RJP_tree_node));
|
||||
rjp_copy_value_c(&dest->data.value, &src->data.value, fns);
|
||||
irjp_strcpy(&dest->data.name, &src->data.name, fns);
|
||||
dest->parent = parent;
|
||||
return dest;
|
||||
}
|
||||
static RJP_tree_node* irjp_copy_node(const RJP_tree_node* root, RJP_tree_node* parent){
|
||||
static RJP_tree_node* irjp_copy_node(const RJP_tree_node* root, RJP_tree_node* parent, const RJP_memory_fns* fns){
|
||||
if(!root){
|
||||
return NULL;
|
||||
}
|
||||
RJP_tree_node* newnode = irjp_clone_node(root, parent);
|
||||
newnode->left = irjp_copy_node(root->left, newnode);
|
||||
newnode->right = irjp_copy_node(root->right, newnode);
|
||||
RJP_tree_node* newnode = irjp_clone_node(root, parent, fns);
|
||||
newnode->left = irjp_copy_node(root->left, newnode, fns);
|
||||
newnode->right = irjp_copy_node(root->right, newnode, fns);
|
||||
return newnode;
|
||||
}
|
||||
static void irjp_free_node(RJP_tree_node* node){
|
||||
irjp_delete_node(node);
|
||||
rjp_free(node);
|
||||
static void irjp_free_node(RJP_tree_node* node, const RJP_memory_fns* fns){
|
||||
irjp_delete_node(node, fns);
|
||||
fns->free(node);
|
||||
}
|
||||
static void irjp_delete_node(RJP_tree_node* node){
|
||||
rjp_free(node->data.name.value);
|
||||
irjp_delete_value(&node->data.value);
|
||||
static void irjp_delete_node(RJP_tree_node* node, const RJP_memory_fns* fns){
|
||||
fns->free(node->data.name.value);
|
||||
irjp_delete_value(&node->data.value, fns);
|
||||
}
|
||||
|
||||
|
||||
/* TREE */
|
||||
//Tree construction / destruction
|
||||
RJP_tree_node* irjp_copy_tree(const RJP_tree_node* root){
|
||||
return irjp_copy_node(root, NULL);
|
||||
RJP_tree_node* irjp_copy_tree(const RJP_tree_node* root, const RJP_memory_fns* fns){
|
||||
return irjp_copy_node(root, NULL, fns);
|
||||
}
|
||||
void irjp_free_tree(RJP_tree_node* root){
|
||||
void irjp_free_tree(RJP_tree_node* root, const RJP_memory_fns* fns){
|
||||
if(!root)
|
||||
return;
|
||||
irjp_free_tree(root->left);
|
||||
irjp_free_tree(root->right);
|
||||
irjp_free_node(root);
|
||||
irjp_free_tree(root->left, fns);
|
||||
irjp_free_tree(root->right, fns);
|
||||
irjp_free_node(root, fns);
|
||||
}
|
||||
//Tree operations
|
||||
RJP_tree_node* irjp_tree_insert_value(RJP_tree_node* root, char* key, RJP_index keylen, RJP_value** added, int* status){
|
||||
RJP_tree_node* irjp_tree_insert_value(RJP_tree_node* root, char* key, RJP_index keylen, RJP_value** added, int* status, const RJP_memory_fns* fns){
|
||||
*status = RJP_TREE_SUCCESS;
|
||||
if(!root){
|
||||
root = irjp_new_node(key, keylen);
|
||||
root = irjp_new_node(key, keylen, fns);
|
||||
if(added)
|
||||
*added = &root->data.value;
|
||||
return root;
|
||||
}
|
||||
RJP_tree_node* newnode = irjp_new_node(key, keylen);
|
||||
RJP_tree_node* newnode = irjp_new_node(key, keylen, fns);
|
||||
if(added)
|
||||
*added = &newnode->data.value;
|
||||
return irjp_tree_insert_node(root, newnode);
|
||||
}
|
||||
static RJP_tree_node* irjp_tree_insert_node(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode){
|
||||
RJP_tree_node* irjp_tree_insert_node(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode){
|
||||
irjp_tree_insert_impl(root, newnode);
|
||||
irjp_tree_repair(newnode);
|
||||
while(root->parent)
|
||||
@ -270,96 +269,19 @@ RJP_tree_node* irjp_tree_search_value(RJP_tree_node* root, const char* key){
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
//Debug printouts
|
||||
void irjp_dbg_print_tree(RJP_tree_node* root){
|
||||
if(!root){
|
||||
return;
|
||||
}
|
||||
RJP_tree_node* current = root, *pre;
|
||||
while(current){
|
||||
if(!current->left){
|
||||
printf("%s\n", current->data.name.value);
|
||||
current = current->right;
|
||||
}else{
|
||||
pre = current->left;
|
||||
while(pre->right && pre->right != current){
|
||||
pre = pre->right;
|
||||
}
|
||||
if(!pre->right){
|
||||
pre->right = current;
|
||||
current = current->left;
|
||||
}else{
|
||||
pre->right = NULL;
|
||||
printf("%s\n", current->data.name.value);
|
||||
current = current->right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#define pop() front = (front+1)%queuelen
|
||||
#define push(val, dp) do{queue[rear].n = (val);queue[rear].depth = (dp);rear = (rear+1)%queuelen;}while(0)
|
||||
struct tmpthing{
|
||||
RJP_tree_node* n;
|
||||
int depth;
|
||||
};
|
||||
|
||||
void irjp_dbg_print_tree_bfs(RJP_tree_node* root){
|
||||
int queuelen = 64;
|
||||
struct tmpthing queue[64];
|
||||
int front = 0, rear = 0;
|
||||
int lastdepth = 0;
|
||||
|
||||
push(root, lastdepth);
|
||||
while(front != rear){
|
||||
if(lastdepth != queue[front].depth){
|
||||
lastdepth = queue[front].depth;
|
||||
printf("\n");
|
||||
}
|
||||
if(!queue[front].n){
|
||||
printf("*");
|
||||
}else{
|
||||
printf("%s ", queue[front].n->data.name.value);
|
||||
if(queue[front].n->left){
|
||||
push(queue[front].n->left, queue[front].depth+1);
|
||||
}else{
|
||||
push(NULL,queue[front].depth+1);
|
||||
}
|
||||
if(queue[front].n->right){
|
||||
push(queue[front].n->right, queue[front].depth+1);
|
||||
}else{
|
||||
push(NULL,queue[front].depth+1);
|
||||
}
|
||||
}
|
||||
pop();
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#undef pop
|
||||
#undef push
|
||||
|
||||
RJP_tree_node* irjp_tree_remove_value(RJP_tree_node* root, const char* key, RJP_value** removed_node, int* status){
|
||||
if(!root){
|
||||
*removed_node = NULL;
|
||||
*status = RJP_TREE_ERR_NULL_ROOT;
|
||||
RJP_tree_node* irjp_tree_remove_value(RJP_tree_node* restrict root, RJP_tree_node* restrict member, RJP_tree_node** removed_node){
|
||||
if(!root)
|
||||
return root;
|
||||
}
|
||||
RJP_tree_node* n = irjp_tree_search_value(root, key);
|
||||
if(!n){
|
||||
*removed_node = NULL;
|
||||
*status = RJP_TREE_ERR_NOT_FOUND;
|
||||
if(!member)
|
||||
return root;
|
||||
}
|
||||
*status = RJP_TREE_SUCCESS;
|
||||
return irjp_tree_remove_node(n, removed_node);
|
||||
return irjp_tree_remove_node(member, removed_node);
|
||||
}
|
||||
static RJP_tree_node* irjp_tree_remove_node(RJP_tree_node* target, RJP_value** removed_node){
|
||||
if(removed_node)
|
||||
*removed_node = (RJP_value*)target;
|
||||
|
||||
static RJP_tree_node* irjp_tree_remove_node(RJP_tree_node* target, RJP_tree_node** removed_node){
|
||||
while(target->right && target->left){
|
||||
irjp_copy_node_data(target, target->right);
|
||||
target = target->right;
|
||||
}
|
||||
*removed_node = target;
|
||||
RJP_tree_node* retval = target->parent;
|
||||
|
||||
do{
|
||||
@ -453,6 +375,8 @@ static RJP_tree_node* irjp_tree_remove_node(RJP_tree_node* target, RJP_value** r
|
||||
}
|
||||
}while(1);
|
||||
|
||||
(*removed_node)->left = NULL;
|
||||
(*removed_node)->right = NULL;
|
||||
//return new root
|
||||
if(!retval)
|
||||
return NULL;
|
||||
|
||||
17
tests/CMakeLists.txt
Normal file
17
tests/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
project(rjp_tests)
|
||||
set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../include)
|
||||
include_directories("${INCLUDE_PATH}")
|
||||
add_compile_options(-Wall -Wextra -pedantic)
|
||||
link_libraries(rjp)
|
||||
|
||||
if(ENABLE_PROFILING)
|
||||
add_compile_options(-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
||||
add_link_options(-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
||||
endif()
|
||||
|
||||
add_executable(parse "parse.c")
|
||||
add_executable(output "output.c")
|
||||
|
||||
add_test(NAME parse-test COMMAND parse)
|
||||
add_test(NAME output-test COMMAND output)
|
||||
385
tests/output.c
Normal file
385
tests/output.c
Normal file
@ -0,0 +1,385 @@
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_value.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct{
|
||||
RJP_value* (*create)(void);
|
||||
const char* res;
|
||||
int fmt;
|
||||
}test_pair;
|
||||
|
||||
//Basic values excluding float because precision bs and stuff
|
||||
RJP_value* case_1(void){
|
||||
return rjp_new_null();
|
||||
}
|
||||
RJP_value* case_2(void){
|
||||
return rjp_new_int(5);
|
||||
}
|
||||
RJP_value* case_3(void){
|
||||
return rjp_new_bool(1);
|
||||
}
|
||||
RJP_value* case_4(void){
|
||||
return rjp_new_bool(0);
|
||||
}
|
||||
RJP_value* case_5(void){
|
||||
return rjp_new_object();
|
||||
}
|
||||
RJP_value* case_6(void){
|
||||
return rjp_new_array();
|
||||
}
|
||||
RJP_value* case_7(void){
|
||||
return rjp_new_ordered_object();
|
||||
}
|
||||
//handle object with member
|
||||
RJP_value* case_8(void){
|
||||
RJP_value* obj = rjp_new_object();
|
||||
rjp_add_member(obj, "key", 0, rjp_new_int(7));
|
||||
return obj;
|
||||
}
|
||||
//handle object with subobject
|
||||
RJP_value* case_9(void){
|
||||
RJP_value* obj = rjp_new_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_object(sub);
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
return obj;
|
||||
}
|
||||
//handle object with multiple members
|
||||
RJP_value* case_10(void){
|
||||
RJP_value* obj = rjp_new_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_object(sub);
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
return obj;
|
||||
}
|
||||
//handle object member ordering
|
||||
RJP_value* case_11(void){
|
||||
RJP_value* obj = rjp_new_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_object(sub);
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
return obj;
|
||||
}
|
||||
//handle orderedobject member ordering
|
||||
RJP_value* case_12(void){
|
||||
RJP_value* obj = rjp_new_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_ordered_object(sub);
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
return obj;
|
||||
}
|
||||
//handle orderedobject member ordering pt2
|
||||
RJP_value* case_13(void){
|
||||
RJP_value* obj = rjp_new_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_ordered_object(sub);
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
return obj;
|
||||
}
|
||||
//handle array with element
|
||||
RJP_value* case_14(void){
|
||||
RJP_value* arr = rjp_new_array();
|
||||
rjp_add_element(arr, rjp_new_int(5));
|
||||
return arr;
|
||||
}
|
||||
//handle array with subarray
|
||||
RJP_value* case_15(void){
|
||||
RJP_value* arr = rjp_new_array();
|
||||
RJP_value* sub = rjp_new_element(arr);
|
||||
rjp_set_array(sub);
|
||||
rjp_add_element(sub, rjp_new_bool(0));
|
||||
return arr;
|
||||
}
|
||||
//handle array with multiple elements
|
||||
RJP_value* case_16(void){
|
||||
RJP_value* arr = rjp_new_array();
|
||||
RJP_value* sub = rjp_new_element(arr);
|
||||
rjp_set_array(sub);
|
||||
rjp_add_element(sub, rjp_new_bool(0));
|
||||
rjp_add_element(sub, rjp_new_bool(1));
|
||||
return arr;
|
||||
}
|
||||
//handle array with multiple elements and subarray
|
||||
RJP_value* case_17(void){
|
||||
RJP_value* arr = rjp_new_array();
|
||||
rjp_add_element(arr, rjp_new_int(5));
|
||||
RJP_value* sub = rjp_new_element(arr);
|
||||
rjp_set_array(sub);
|
||||
rjp_add_element(sub, rjp_new_bool(0));
|
||||
rjp_add_element(sub, rjp_new_bool(1));
|
||||
return arr;
|
||||
}
|
||||
//handle array with subobject with subarray
|
||||
RJP_value* case_18(void){
|
||||
RJP_value* arr = rjp_new_array();
|
||||
rjp_add_element(arr, rjp_new_int(5));
|
||||
RJP_value* subobj = rjp_new_element(arr);
|
||||
rjp_set_object(subobj);
|
||||
RJP_value* subarr = rjp_new_member(subobj, "key", 0);
|
||||
rjp_set_array(subarr);
|
||||
rjp_add_element(subarr, rjp_new_bool(0));
|
||||
return arr;
|
||||
}
|
||||
//handle object with many members
|
||||
RJP_value* case_19(void){
|
||||
char c[] = "key0";
|
||||
RJP_value* arr = rjp_new_array();
|
||||
rjp_add_element(arr, rjp_new_int(5));
|
||||
RJP_value* subobj = rjp_new_element(arr);
|
||||
rjp_set_object(subobj);
|
||||
for(int i = 0;i < 10;++i){
|
||||
RJP_value* newmem = rjp_new_member(subobj, c, 0);
|
||||
c[3] += 1;
|
||||
if(i % 2 == 0)
|
||||
rjp_set_bool(newmem, 1);
|
||||
else
|
||||
rjp_set_bool(newmem, 0);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
//handle orderedobject with many members as array element
|
||||
RJP_value* case_20(void){
|
||||
char c[] = "key9";
|
||||
RJP_value* arr = rjp_new_array();
|
||||
rjp_add_element(arr, rjp_new_int(5));
|
||||
RJP_value* subobj = rjp_new_element(arr);
|
||||
rjp_set_ordered_object(subobj);
|
||||
for(int i = 0;i < 10;++i){
|
||||
RJP_value* newmem = rjp_new_member(subobj, c, 0);
|
||||
c[3] -= 1;
|
||||
if(i % 2 == 0)
|
||||
rjp_set_bool(newmem, 1);
|
||||
else
|
||||
rjp_set_bool(newmem, 0);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
//handle array with many element as object member
|
||||
RJP_value* case_21(void){
|
||||
RJP_value* obj = rjp_new_object();
|
||||
RJP_value* arr = rjp_new_member(obj, "arr", 0);
|
||||
rjp_set_array(arr);
|
||||
for(int i = 0;i < 10;++i)
|
||||
rjp_set_int(rjp_new_element(arr), i);
|
||||
return obj;
|
||||
}
|
||||
//handle unorderedobject conversion
|
||||
RJP_value* case_22(void){
|
||||
RJP_value* obj = rjp_new_ordered_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_ordered_object(sub);
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
rjp_object_to_unordered(sub);
|
||||
return obj;
|
||||
}
|
||||
//handle unorderedobject conversion
|
||||
RJP_value* case_23(void){
|
||||
RJP_value* obj = rjp_new_ordered_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_ordered_object(sub);
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
rjp_object_to_unordered(sub);
|
||||
return obj;
|
||||
}
|
||||
//handle orderedobject conversion
|
||||
RJP_value* case_24(void){
|
||||
RJP_value* obj = rjp_new_ordered_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_object(sub);
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
rjp_object_to_ordered(sub);
|
||||
return obj;
|
||||
}
|
||||
//handle orderedobject conversion
|
||||
RJP_value* case_25(void){
|
||||
RJP_value* obj = rjp_new_ordered_object();
|
||||
RJP_value* sub = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_object(sub);
|
||||
rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1));
|
||||
rjp_object_to_ordered(sub);
|
||||
rjp_add_member(sub, "subkey", 0, rjp_new_bool(0));
|
||||
return obj;
|
||||
}
|
||||
RJP_value* case_26(void){
|
||||
return rjp_new_string("string", 0);
|
||||
}
|
||||
RJP_value* case_27(void){
|
||||
return rjp_new_string("", 0);
|
||||
}
|
||||
RJP_value* case_28(void){
|
||||
RJP_value* obj = rjp_new_object();
|
||||
RJP_value* str = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_string(str, "string", 0);
|
||||
return obj;
|
||||
}
|
||||
RJP_value* case_29(void){
|
||||
RJP_value* obj = rjp_new_object();
|
||||
RJP_value* str = rjp_new_member(obj, "key", 0);
|
||||
rjp_set_string(str, "", 0);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static test_pair tests[] = {
|
||||
{case_1, "null", RJP_FORMAT_NONE},
|
||||
{case_2, "5", RJP_FORMAT_NONE},
|
||||
{case_3, "true", RJP_FORMAT_NONE},
|
||||
{case_4, "false", RJP_FORMAT_NONE},
|
||||
{case_5, "{}", RJP_FORMAT_NONE},
|
||||
{case_6, "[]", RJP_FORMAT_NONE},
|
||||
{case_7, "{}", RJP_FORMAT_NONE},
|
||||
{case_8, "{\"key\":7}", RJP_FORMAT_NONE},
|
||||
{case_9, "{\"key\":{\"subkey\":false}}", RJP_FORMAT_NONE},
|
||||
{case_10, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE},
|
||||
{case_11, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE},
|
||||
{case_12, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE},
|
||||
{case_13, "{\"key\":{\"subkey2\":true,\"subkey\":false}}", RJP_FORMAT_NONE},
|
||||
{case_14, "[5]", RJP_FORMAT_NONE},
|
||||
{case_15, "[[false]]", RJP_FORMAT_NONE},
|
||||
{case_16, "[[false,true]]", RJP_FORMAT_NONE},
|
||||
{case_17, "[5,[false,true]]", RJP_FORMAT_NONE},
|
||||
{case_18, "[5,{\"key\":[false]}]", RJP_FORMAT_NONE},
|
||||
{case_19, "[5,{\"key0\":true,\"key1\":false,\"key2\":true,\"key3\":false,\"key4\":true,\"key5\":false,\"key6\":true,\"key7\":false,\"key8\":true,\"key9\":false}]", RJP_FORMAT_NONE},
|
||||
{case_20, "[5,{\"key9\":true,\"key8\":false,\"key7\":true,\"key6\":false,\"key5\":true,\"key4\":false,\"key3\":true,\"key2\":false,\"key1\":true,\"key0\":false}]", RJP_FORMAT_NONE},
|
||||
{case_21, "{\"arr\":[0,1,2,3,4,5,6,7,8,9]}", RJP_FORMAT_NONE},
|
||||
{case_22, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE},
|
||||
{case_23, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE},
|
||||
{case_24, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE},
|
||||
{case_25, "{\"key\":{\"subkey2\":true,\"subkey\":false}}", RJP_FORMAT_NONE},
|
||||
{case_26, "\"string\"", RJP_FORMAT_NONE},
|
||||
{case_27, "\"\"", RJP_FORMAT_NONE},
|
||||
{case_28, "{\"key\":\"string\"}", RJP_FORMAT_NONE},
|
||||
{case_29, "{\"key\":\"\"}", RJP_FORMAT_NONE},
|
||||
|
||||
{case_8, "{\"key\":7}", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_10, "{\"key\":{\"subkey\":false, \"subkey2\":true}}", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_11, "{\"key\":{\"subkey\":false, \"subkey2\":true}}", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_12, "{\"key\":{\"subkey\":false, \"subkey2\":true}}", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_13, "{\"key\":{\"subkey2\":true, \"subkey\":false}}", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_14, "[5]", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_16, "[[false, true]]", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_17, "[5, [false, true]]", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_18, "[5, {\"key\":[false]}]", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_19, "[5, {\"key0\":true, \"key1\":false, \"key2\":true, \"key3\":false, \"key4\":true, \"key5\":false, \"key6\":true, \"key7\":false, \"key8\":true, \"key9\":false}]", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_20, "[5, {\"key9\":true, \"key8\":false, \"key7\":true, \"key6\":false, \"key5\":true, \"key4\":false, \"key3\":true, \"key2\":false, \"key1\":true, \"key0\":false}]", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_21, "{\"arr\":[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_22, "{\"key\":{\"subkey\":false, \"subkey2\":true}}", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_23, "{\"key\":{\"subkey\":false, \"subkey2\":true}}", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_24, "{\"key\":{\"subkey\":false, \"subkey2\":true}}", RJP_FORMAT_COMMA_SPACES},
|
||||
{case_25, "{\"key\":{\"subkey2\":true, \"subkey\":false}}", RJP_FORMAT_COMMA_SPACES},
|
||||
|
||||
{case_8, "{\"key\": 7}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_9, "{\"key\": {\"subkey\": false}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_10, "{\"key\": {\"subkey\": false,\"subkey2\": true}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_11, "{\"key\": {\"subkey\": false,\"subkey2\": true}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_12, "{\"key\": {\"subkey\": false,\"subkey2\": true}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_13, "{\"key\": {\"subkey2\": true,\"subkey\": false}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_18, "[5,{\"key\": [false]}]", RJP_FORMAT_KEY_SPACES},
|
||||
{case_19, "[5,{\"key0\": true,\"key1\": false,\"key2\": true,\"key3\": false,\"key4\": true,\"key5\": false,\"key6\": true,\"key7\": false,\"key8\": true,\"key9\": false}]", RJP_FORMAT_KEY_SPACES},
|
||||
{case_20, "[5,{\"key9\": true,\"key8\": false,\"key7\": true,\"key6\": false,\"key5\": true,\"key4\": false,\"key3\": true,\"key2\": false,\"key1\": true,\"key0\": false}]", RJP_FORMAT_KEY_SPACES},
|
||||
{case_21, "{\"arr\": [0,1,2,3,4,5,6,7,8,9]}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_22, "{\"key\": {\"subkey\": false,\"subkey2\": true}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_23, "{\"key\": {\"subkey\": false,\"subkey2\": true}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_24, "{\"key\": {\"subkey\": false,\"subkey2\": true}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_25, "{\"key\": {\"subkey2\": true,\"subkey\": false}}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_28, "{\"key\": \"string\"}", RJP_FORMAT_KEY_SPACES},
|
||||
{case_29, "{\"key\": \"\"}", RJP_FORMAT_KEY_SPACES},
|
||||
|
||||
{case_5, "{}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_6, "[]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_7, "{}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_8, "{\n\t\"key\":7\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_9, "{\n\t\"key\":{\n\t\t\"subkey\":false\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_10, "{\n\t\"key\":{\n\t\t\"subkey\":false,\n\t\t\"subkey2\":true\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_11, "{\n\t\"key\":{\n\t\t\"subkey\":false,\n\t\t\"subkey2\":true\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_12, "{\n\t\"key\":{\n\t\t\"subkey\":false,\n\t\t\"subkey2\":true\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_13, "{\n\t\"key\":{\n\t\t\"subkey2\":true,\n\t\t\"subkey\":false\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_14, "[\n\t5\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_15, "[\n\t[\n\t\tfalse\n\t]\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_16, "[\n\t[\n\t\tfalse,\n\t\ttrue\n\t]\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_17, "[\n\t5,\n\t[\n\t\tfalse,\n\t\ttrue\n\t]\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_18, "[\n\t5,\n\t{\n\t\t\"key\":[\n\t\t\tfalse\n\t\t]\n\t}\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_19, "[\n\t5,\n\t{\n\t\t\"key0\":true,\n\t\t\"key1\":false,\n\t\t\"key2\":true,\n\t\t\"key3\":false,\n\t\t\"key4\":true,\n\t\t\"key5\":false,\n\t\t\"key6\":true,\n\t\t\"key7\":false,\n\t\t\"key8\":true,\n\t\t\"key9\":false\n\t}\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_20, "[\n\t5,\n\t{\n\t\t\"key9\":true,\n\t\t\"key8\":false,\n\t\t\"key7\":true,\n\t\t\"key6\":false,\n\t\t\"key5\":true,\n\t\t\"key4\":false,\n\t\t\"key3\":true,\n\t\t\"key2\":false,\n\t\t\"key1\":true,\n\t\t\"key0\":false\n\t}\n]", RJP_FORMAT_TABBED_LINES},
|
||||
{case_21, "{\n\t\"arr\":[\n\t\t0,\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4,\n\t\t5,\n\t\t6,\n\t\t7,\n\t\t8,\n\t\t9\n\t]\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_22, "{\n\t\"key\":{\n\t\t\"subkey\":false,\n\t\t\"subkey2\":true\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_23, "{\n\t\"key\":{\n\t\t\"subkey\":false,\n\t\t\"subkey2\":true\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_24, "{\n\t\"key\":{\n\t\t\"subkey\":false,\n\t\t\"subkey2\":true\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_25, "{\n\t\"key\":{\n\t\t\"subkey2\":true,\n\t\t\"subkey\":false\n\t}\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_28, "{\n\t\"key\":\"string\"\n}", RJP_FORMAT_TABBED_LINES},
|
||||
{case_29, "{\n\t\"key\":\"\"\n}", RJP_FORMAT_TABBED_LINES},
|
||||
|
||||
{case_1, "null", RJP_FORMAT_PRETTY},
|
||||
{case_2, "5", RJP_FORMAT_PRETTY},
|
||||
{case_3, "true", RJP_FORMAT_PRETTY},
|
||||
{case_4, "false", RJP_FORMAT_PRETTY},
|
||||
{case_5, "{}", RJP_FORMAT_PRETTY},
|
||||
{case_6, "[]", RJP_FORMAT_PRETTY},
|
||||
{case_7, "{}", RJP_FORMAT_PRETTY},
|
||||
{case_8, "{\n\t\"key\": 7\n}", RJP_FORMAT_PRETTY},
|
||||
{case_9, "{\n\t\"key\": {\n\t\t\"subkey\": false\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_10, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_11, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_12, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_13, "{\n\t\"key\": {\n\t\t\"subkey2\": true,\n\t\t\"subkey\": false\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_14, "[\n\t5\n]", RJP_FORMAT_PRETTY},
|
||||
{case_15, "[\n\t[\n\t\tfalse\n\t]\n]", RJP_FORMAT_PRETTY},
|
||||
{case_16, "[\n\t[\n\t\tfalse,\n\t\ttrue\n\t]\n]", RJP_FORMAT_PRETTY},
|
||||
{case_17, "[\n\t5,\n\t[\n\t\tfalse,\n\t\ttrue\n\t]\n]", RJP_FORMAT_PRETTY},
|
||||
{case_18, "[\n\t5,\n\t{\n\t\t\"key\": [\n\t\t\tfalse\n\t\t]\n\t}\n]", RJP_FORMAT_PRETTY},
|
||||
{case_19, "[\n\t5,\n\t{\n\t\t\"key0\": true,\n\t\t\"key1\": false,\n\t\t\"key2\": true,\n\t\t\"key3\": false,\n\t\t\"key4\": true,\n\t\t\"key5\": false,\n\t\t\"key6\": true,\n\t\t\"key7\": false,\n\t\t\"key8\": true,\n\t\t\"key9\": false\n\t}\n]", RJP_FORMAT_PRETTY},
|
||||
{case_20, "[\n\t5,\n\t{\n\t\t\"key9\": true,\n\t\t\"key8\": false,\n\t\t\"key7\": true,\n\t\t\"key6\": false,\n\t\t\"key5\": true,\n\t\t\"key4\": false,\n\t\t\"key3\": true,\n\t\t\"key2\": false,\n\t\t\"key1\": true,\n\t\t\"key0\": false\n\t}\n]", RJP_FORMAT_PRETTY},
|
||||
{case_21, "{\n\t\"arr\": [\n\t\t0,\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4,\n\t\t5,\n\t\t6,\n\t\t7,\n\t\t8,\n\t\t9\n\t]\n}", RJP_FORMAT_PRETTY},
|
||||
{case_22, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_23, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_24, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_25, "{\n\t\"key\": {\n\t\t\"subkey2\": true,\n\t\t\"subkey\": false\n\t}\n}", RJP_FORMAT_PRETTY},
|
||||
{case_26, "\"string\"", RJP_FORMAT_PRETTY},
|
||||
{case_27, "\"\"", RJP_FORMAT_PRETTY},
|
||||
{case_28, "{\n\t\"key\": \"string\"\n}", RJP_FORMAT_PRETTY},
|
||||
{case_29, "{\n\t\"key\": \"\"\n}", RJP_FORMAT_PRETTY},
|
||||
};
|
||||
|
||||
int run_test(test_pair* p){
|
||||
RJP_value* test = p->create();
|
||||
char* buf = rjp_to_json(test, p->fmt);
|
||||
if(!buf){
|
||||
if(!p->res)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
int retval = !strcmp(buf, p->res);
|
||||
if(retval){
|
||||
fprintf(stderr, "Success\n");
|
||||
}else{
|
||||
fprintf(stderr, "Failure\n");
|
||||
fprintf(stderr, "Expected: '%s'\nGot: '%s'\n", p->res, buf);
|
||||
}
|
||||
rjp_free(buf);
|
||||
rjp_free_value(test);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int main(){
|
||||
const int num_tests = sizeof(tests) / sizeof(tests[0]);
|
||||
int passed = 0;
|
||||
fprintf(stderr, "Running %d tests that should succeed...\n", num_tests);
|
||||
for(int i = 0;i < num_tests;++i){
|
||||
fprintf(stderr, "%8d) ", i+1);
|
||||
if(run_test(&tests[i])){
|
||||
++passed;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\nResults: %d/%d tests passed\n", passed, num_tests);
|
||||
if(passed != num_tests)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
212
tests/parse.c
Normal file
212
tests/parse.c
Normal file
@ -0,0 +1,212 @@
|
||||
#include "rjp.h"
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_string.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MIN(x, y) ((x < y) ? (x) : (y))
|
||||
|
||||
typedef struct parse_stuff{
|
||||
const char* str;
|
||||
int len;
|
||||
int pos;
|
||||
}parse_stuff;
|
||||
|
||||
int read_callback(char* c, int size, void* userdata){
|
||||
parse_stuff* data = (parse_stuff*)userdata;
|
||||
if(data->pos >= data->len)
|
||||
return 0;
|
||||
int min = MIN(size, data->len - data->pos);
|
||||
int i;
|
||||
for(i = 0;i < min;++i){
|
||||
c[i] = data->str[data->pos++];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int handle_res(RJP_value* res, RJP_parse_error* err){
|
||||
if(res){
|
||||
fprintf(stderr, "Accepted\n");
|
||||
}
|
||||
else{
|
||||
char* buf = rjp_parse_error_to_string(err);
|
||||
rjp_delete_parse_error(err);
|
||||
fprintf(stderr, "%s\n", buf);
|
||||
rjp_free(buf);
|
||||
}
|
||||
int failed = res == NULL;
|
||||
rjp_free_value(res);
|
||||
return failed;
|
||||
}
|
||||
int test_cbacks(const char* str, RJP_parse_flag flags){
|
||||
RJP_parse_callback cbacks;
|
||||
parse_stuff cback_data = {str, strlen(str), 0};
|
||||
cbacks.read = read_callback;
|
||||
cbacks.data = &cback_data;
|
||||
RJP_value* res;
|
||||
RJP_parse_error err;
|
||||
res = rjp_parse_cback(flags, &cbacks, &err);
|
||||
return handle_res(res, &err);
|
||||
}
|
||||
|
||||
int test(const char* str, RJP_parse_flag flags){
|
||||
RJP_value* res;
|
||||
RJP_parse_error err;
|
||||
res = rjp_parse(str, flags, &err);
|
||||
return handle_res(res, &err);
|
||||
}
|
||||
|
||||
struct parse_pair{
|
||||
const char* str;
|
||||
RJP_parse_flag flags;
|
||||
};
|
||||
|
||||
struct parse_pair should_pass_strings[] = {
|
||||
{"{}", RJP_PARSE_NO_EXT},
|
||||
{"[]", RJP_PARSE_NO_EXT},
|
||||
{"\"s\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\n\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\\"\"", RJP_PARSE_NO_EXT},
|
||||
{"\"str\\nstr\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\uD83D\\uDE10\"", RJP_PARSE_NO_EXT},
|
||||
{"true", RJP_PARSE_NO_EXT},
|
||||
{"false", RJP_PARSE_NO_EXT},
|
||||
{"null", RJP_PARSE_NO_EXT},
|
||||
{"5", RJP_PARSE_NO_EXT},
|
||||
{"-5", RJP_PARSE_NO_EXT},
|
||||
{"+5", RJP_PARSE_NO_EXT},
|
||||
{"5.5", RJP_PARSE_NO_EXT},
|
||||
{"-5.5", RJP_PARSE_NO_EXT},
|
||||
{"+5.5", RJP_PARSE_NO_EXT},
|
||||
{"5.5e6", RJP_PARSE_NO_EXT},
|
||||
{"-5.5e6", RJP_PARSE_NO_EXT},
|
||||
{"+5.5e6", RJP_PARSE_NO_EXT},
|
||||
{"5.5e+6", RJP_PARSE_NO_EXT},
|
||||
{"-5.5e+6", RJP_PARSE_NO_EXT},
|
||||
{"+5.5e+6", RJP_PARSE_NO_EXT},
|
||||
{"5.5e-6", RJP_PARSE_NO_EXT},
|
||||
{"-5.5e-6", RJP_PARSE_NO_EXT},
|
||||
{"+5.5e-6", RJP_PARSE_NO_EXT},
|
||||
{" {}", RJP_PARSE_NO_EXT},
|
||||
{"\n{}\n", RJP_PARSE_NO_EXT},
|
||||
{" { \"key\" \t:\n\n\n5 \n\t\n } ", RJP_PARSE_NO_EXT},
|
||||
{" {\t }\n", RJP_PARSE_NO_EXT},
|
||||
{"5.5 ", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":5}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":\"\"}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":{}}", RJP_PARSE_NO_EXT},
|
||||
{"{\"\\uD83D\\uDE10\":5}", RJP_PARSE_NO_EXT},
|
||||
{"{\"😐\":5}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":{\"key\":5}}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":{\"key\":5,\"key2\":6}}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":{\"key\":5},\"key2\":6}", RJP_PARSE_NO_EXT},
|
||||
{"[5, 6, 7, 8, 9, \"10\"]", RJP_PARSE_NO_EXT},
|
||||
{"[[5,6],[7,8],[9,\"10\"]]", RJP_PARSE_NO_EXT},
|
||||
{"{\"arr\":[5,6,6]}", RJP_PARSE_NO_EXT},
|
||||
{"[{\"arr\":[5,6,6]}]", RJP_PARSE_NO_EXT},
|
||||
{"[{\"arr\":[5,6,6]}, 6]", RJP_PARSE_NO_EXT},
|
||||
{"[5,6,6,6,6.6]", RJP_PARSE_NO_EXT},
|
||||
{"[6,7,]", RJP_PARSE_ALLOW_TRAILING_COMMA},
|
||||
{"[\"value\",\"\"]", RJP_PARSE_NO_EXT},
|
||||
{"{\"1\":1,\"2\":2,}", RJP_PARSE_ALLOW_TRAILING_COMMA},
|
||||
{"[6,]", RJP_PARSE_ALLOW_TRAILING_COMMA},
|
||||
{"{\"1\":1,}", RJP_PARSE_ALLOW_TRAILING_COMMA},
|
||||
{"//comment\n{}", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"{\"key\"://comment\n5}", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"{\"key\"//comment\n:5}", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"{}//comment", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"{//\"key\":5\n}", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"5 //comment*/", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"{/*\"key\":5*/\"key\":5}", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"[5, /*comment*/6]", RJP_PARSE_ALLOW_COMMENTS},
|
||||
};
|
||||
const int should_pass_cnt = sizeof(should_pass_strings)/sizeof(should_pass_strings[0]);
|
||||
struct parse_pair should_fail_strings[] = {
|
||||
{"//comment\n{}", RJP_PARSE_NO_EXT},
|
||||
{"{", RJP_PARSE_NO_EXT},
|
||||
{"}", RJP_PARSE_NO_EXT},
|
||||
{"[", RJP_PARSE_NO_EXT},
|
||||
{"]", RJP_PARSE_NO_EXT},
|
||||
{"6.", RJP_PARSE_NO_EXT},
|
||||
{"6.6e", RJP_PARSE_NO_EXT},
|
||||
{"6.6e+", RJP_PARSE_NO_EXT},
|
||||
{"5e", RJP_PARSE_NO_EXT},
|
||||
{"{6}", RJP_PARSE_NO_EXT},
|
||||
{"{\"\":5}", RJP_PARSE_NO_EXT},
|
||||
{"[\"key\":5]", RJP_PARSE_NO_EXT},
|
||||
{"\"string\n\"", RJP_PARSE_NO_EXT},
|
||||
{"[3 4]", RJP_PARSE_NO_EXT},
|
||||
{"\"\\uD83D\\uDE1\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\uD83D\\uDE1Q\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\uD83\\uDE10\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\uF83D\\uDE10\"", RJP_PARSE_NO_EXT},
|
||||
{"\"\\uU83D\\uDE10\"", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":1 \"key2\":2}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\" 1}", RJP_PARSE_NO_EXT},
|
||||
{"6, 7", RJP_PARSE_NO_EXT},
|
||||
{"[,]", RJP_PARSE_NO_EXT},
|
||||
{"{, RJP_PARSE_NO_EXT}", RJP_PARSE_NO_EXT},
|
||||
{"[1, 2],", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\nkey\":5}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\":\"key\n\"}", RJP_PARSE_NO_EXT},
|
||||
{"[6,7,]", RJP_PARSE_NO_EXT},
|
||||
{"{\"1\":1,\"2\":2, RJP_PARSE_NO_EXT}", RJP_PARSE_NO_EXT},
|
||||
{"[6,]", RJP_PARSE_NO_EXT},
|
||||
{"{\"1\":1, RJP_PARSE_NO_EXT}", RJP_PARSE_NO_EXT},
|
||||
{"{//comment\"key\":\n5}", RJP_PARSE_NO_EXT},
|
||||
{"{/*\"key\":*/5}", RJP_PARSE_NO_EXT},
|
||||
{"[5, /*6*/, 7]", RJP_PARSE_NO_EXT},
|
||||
{"{/*comment}", RJP_PARSE_NO_EXT},
|
||||
{"{//comment}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\"://comment\n5}", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\"//comment\n:5}", RJP_PARSE_NO_EXT},
|
||||
{"{}//comment", RJP_PARSE_NO_EXT},
|
||||
{"{//\"key\":5\n}", RJP_PARSE_NO_EXT},
|
||||
{"5 //comment*/", RJP_PARSE_NO_EXT},
|
||||
{"{/*\"key\":5*/\"key\":5}", RJP_PARSE_NO_EXT},
|
||||
{"[5, /*comment*/6]", RJP_PARSE_NO_EXT},
|
||||
{"{\"key\"//:5}", RJP_PARSE_ALLOW_COMMENTS},
|
||||
{"{,}", RJP_PARSE_ALLOW_TRAILING_COMMA},
|
||||
{"[,]", RJP_PARSE_ALLOW_TRAILING_COMMA},
|
||||
};
|
||||
const int should_fail_cnt = sizeof(should_fail_strings)/sizeof(should_fail_strings[0]);
|
||||
const int total_tests = should_pass_cnt + should_fail_cnt;
|
||||
|
||||
int run_test(int (*fun)(const char*,RJP_parse_flag)){
|
||||
int passed = 0;
|
||||
fprintf(stderr, "Running %d tests that should pass...\n", should_pass_cnt);
|
||||
for(unsigned i = 0;i < sizeof(should_pass_strings)/sizeof(should_pass_strings[0]);++i){
|
||||
fprintf(stderr, "%8d) ", i+1);
|
||||
if(!fun(should_pass_strings[i].str, should_pass_strings[i].flags)){
|
||||
++passed;
|
||||
}else{
|
||||
fprintf(stderr, "%13s%s\n", "", should_pass_strings[i].str);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Running %d tests that should fail...\n", should_fail_cnt);
|
||||
for(unsigned i = 0;i < sizeof(should_fail_strings)/sizeof(should_fail_strings[0]);++i){
|
||||
fprintf(stderr, "%8d) ", i+1);
|
||||
if(fun(should_fail_strings[i].str, should_fail_strings[i].flags)){
|
||||
++passed;
|
||||
}else{
|
||||
fprintf(stderr, "%13s%s\n", "", should_fail_strings[i].str);
|
||||
}
|
||||
}
|
||||
return passed;
|
||||
}
|
||||
int main(){
|
||||
|
||||
int normal_passed = run_test(test);
|
||||
int cback_passed = run_test(test_cbacks);
|
||||
int total_passed = normal_passed + cback_passed;
|
||||
|
||||
fprintf(stderr, "\nResults: %d/%d normal tests passed\n", normal_passed, total_tests);
|
||||
fprintf(stderr, "Results: %d/%d callback tests passed\n", cback_passed, total_tests);
|
||||
fprintf(stderr, "Results: %d/%d tests passed\n", total_passed, total_tests*2);
|
||||
if(total_passed != (total_tests*2))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user