Compare commits

...

126 Commits

Author SHA1 Message Date
8ce84460fd Merge changes from master 2022-06-22 16:32:41 -07:00
25d7c6c0fc Update to newer rexylib 2022-06-22 16:30:34 -07:00
a017b589cb Update to new rexylb 2022-06-19 13:37:21 -07:00
69fca74d3c Update to C++20 2022-05-24 17:32:46 -07:00
d0473cd136 Fix pkg-config files 2022-05-23 18:16:28 -07:00
4581f8da8d Update CMake to use pkg-config to find librexy 2022-05-23 17:55:36 -07:00
67469cc575 Update version 2022-05-22 15:17:49 -07:00
01f48a394d Update rexylib 2022-05-21 12:36:26 -07:00
5c261ed7ed Update to new librexy 2022-05-21 09:58:26 -07:00
a3ea13fc03 Update to newer rexylib 2022-03-20 14:17:04 -07:00
0d9cebedc0 Remove broken rjp::cast and rjp::convert. Added const iterator access to object and array 2022-03-19 10:37:57 -07:00
f763438c47 Add object and array size queries to rjp++ 2021-04-11 14:51:54 -07:00
cceb4eda7a Change shared library SOVERSION 2021-04-04 09:30:16 -07:00
47da1d454d #include cleanups 2021-04-04 09:08:51 -07:00
rexy712
451e6f2710 Update version 2021-01-03 10:15:52 -08:00
rexy712
d2af423057 Work with rexylib after small string optimization 2020-08-03 17:39:27 -07:00
rexy712
d1a7c3cf9c changed all rexy::string_base and rexy::static_strings to templated types 2020-08-01 01:29:46 -07:00
rexy712
0d226f3148 Update to work with new librexy allocators 2020-07-28 09:08:05 -07:00
rexy712
e77d20cae4 Add _c API functions to allow custom allocation/deallocation functions 2020-07-20 03:16:21 -07:00
rexy712
bf098ac96a Add escape functionality to rjp++ 2020-07-19 08:48:10 -07:00
rexy712
ad2138ba5d Allow rjp++ assignment of values containing nullptr. Makes user interface flow nicer 2020-05-16 15:14:14 -07:00
rexy712
924558c8c9 Merge branch 'master' of ssh://rexy712.chickenkiller.com:1995/var/git/repos/rexy712/rjp 2020-05-16 13:47:47 -07:00
rexy712
2cbc47a6bf Fix rjp.hpp not including parse.hpp 2020-05-16 13:47:41 -07:00
rexy712
8a6b323fec Remove explicit constructors from rjp++. Might regret this later 2020-05-02 14:05:24 -07:00
rexy712
034917177b Fix rjp++ not being able to be built against 2020-04-11 07:10:11 -07:00
rexy712
bb62727333 Fix CMakeLists.txt files to define the project before including GNUInstallDirs 2020-04-09 15:19:28 -07:00
rexy712
ecb3222605 Update API doc 2020-04-09 14:53:18 -07:00
rexy712
df95db7882 Change RJP_PARSE_NONE to RJP_PARSE_NO_EXT to better clarify its purpose 2020-04-09 14:53:05 -07:00
rexy712
6813cfc9ea Update TODO 2020-04-09 13:25:49 -07:00
rexy712
92704ae8f0 Add parse error handling to rjp++ 2020-04-09 13:06:53 -07:00
rexy712
d76252d473 Fix const correctness of error to string 2020-04-09 13:03:29 -07:00
rexy712
a309b9e772 Fix clang warnings 2020-04-09 03:12:37 -07:00
rexy712
0b32905cb8 Add to todo 2020-04-08 16:44:01 -07:00
rexy712
d7e8c5ccbb Change error output to be in the hands of the library user instead of the library 2020-04-08 16:20:18 -07:00
rexy712
1b649fb582 Cleanup macro checks in rjp.h 2020-04-07 13:07:14 -07:00
rexy712
a64e0e86ee Add license disclaimer to rjp++ files 2020-04-07 12:43:48 -07:00
rexy712
44b4bd8b19 Add middleman functions to prevent needing to also link rjp when linking against rjp++ 2020-04-07 12:21:59 -07:00
rexy712
662f24ef9a Add null check in member search as suggested by atlas moon 2020-04-06 17:46:41 -07:00
rexy712
040e5a5df9 Add output test cases for strings in rjp and rjp++ 2020-04-06 17:11:37 -07:00
rexy712
95b5b2cb83 Remove some uses of sprintf to improve speed a tiny bit 2020-04-06 16:55:44 -07:00
rexy712
fd09eb3b20 Make static analyzer happy 2020-04-06 15:09:49 -07:00
rexy712
7b643ff5c6 Separate rjp format flags into multiple for slightly more control over output formatting 2020-04-06 12:35:47 -07:00
rexy712
e6950db87c Disallow changing member key to empty string 2020-04-06 11:07:07 -07:00
rexy712
7395f41ccc Fix missing NULL check in rjp object key setting. Fix allowing empty key in object member addition 2020-04-06 06:43:46 -07:00
rexy712
16bb20a09f Add test cases for empty string values 2020-04-05 10:50:00 -07:00
rexy712
e708952c05 Allow empty string values. Disallow empty key strings 2020-04-05 10:47:12 -07:00
rexy712
06cfc1d0b6 Add default dispatch handling for generic case of 'const rjp::value&'. Will cause compilation error if return type is not void 2020-03-27 21:28:05 -07:00
rexy712
4bf41e8b42 Add dispatch test for rjp++ 2020-03-27 14:59:58 -07:00
rexy712
5bebbe1d33 Added rjp++ dispatcher and proxy type for get operations. Makes it easier to make generic programs around. 2020-03-27 12:40:37 -07:00
rexy712
ee0a1d48da Add rjp::cast specialization for rvalue To type. Acts as a std::move equivalent 2020-03-26 19:40:01 -07:00
rexy712
ed6e15db6e Update rjp++ libflags to work with new template layout 2020-03-26 19:22:45 -07:00
rexy712
295c27029b Rearrange some rjp++ files. Also realized that subobject return does not qualify for nrvo, so that's a problem. 2020-03-26 18:38:01 -07:00
rexy712
7121f8a26e Fix uninitialized value access 2020-03-26 18:23:11 -07:00
rexy712
ccfb2a91ec Add rjp++ tests 2020-03-26 16:20:55 -07:00
rexy712
f03495756f Turn off c++ build by default until librexy is released 2020-03-26 13:15:09 -07:00
rexy712
e82e469356 Add makefile to examples 2020-03-26 13:12:35 -07:00
rexy712
23dc0814a5 Removed comments in rjp.h. Moved API documentation to doc/api 2020-03-26 12:49:05 -07:00
r0nk
01b014f88e Null key check 2020-03-26 12:47:11 -05:00
rexy712
50137406e0 Fix incorrect comments in rjp.h 2020-03-26 09:14:42 -07:00
rexy712
79f0430d2c Fix parse example 2020-03-26 09:12:57 -07:00
rexy712
c42ee430c9 Add 2 basic examples 2020-03-26 09:09:00 -07:00
rexy712
4c94e11ff3 Decided to change rjp_set_member and related functions to default to allocation and copy since i kept getting them mixed up 2020-03-26 08:56:06 -07:00
rexy712
44fb59f602 Add ordered/unordered object conversion tests 2020-03-26 08:13:35 -07:00
rexy712
a4d1a98a0e Reworked rjp++ element/member addition 2020-03-24 16:34:07 -07:00
rexy712
86de4d2bb3 Add testing targets to cmake. Improve output test 2020-03-24 14:53:02 -07:00
rexy712
437d6f7bd0 Add additional test and make it easier to add more in the future 2020-03-23 17:34:57 -07:00
rexy712
330b3956e5 Fix small output bugs. Add ability to add member to object/array in one function call. 2020-03-23 17:28:12 -07:00
rexy712
968e74b182 Fix null pointer access in array iterator operations 2020-03-23 11:55:10 -07:00
rexy712
7177386801 Cleanup unneeded functions and includes 2020-03-23 10:58:56 -07:00
rexy712
eaa02d5ab4 Cleanup rjp++ includes 2020-03-23 10:38:39 -07:00
rexy712
caaf29842d Add callback based parsing to rjp++ 2020-03-22 19:08:26 -07:00
rexy712
32db7680d7 Cleanup and comment a bit more of the source 2020-03-22 16:07:52 -07:00
rexy712
d6213cfe1b Cleanup rjp.h 2020-03-22 14:46:57 -07:00
rexy712
f88bb0c4f2 Add callback based reading, essentially making chunked reading redundant 2020-03-22 11:14:10 -07:00
rexy712
841c179c24 Update .gitignore for new makefile 2020-03-22 11:12:32 -07:00
rexy712
52963dce22 Fix warning generated during release build 2020-03-19 18:55:58 -07:00
rexy712
6c526fc77c Update gitignore 2020-03-17 16:54:39 -07:00
rexy712
a345dbbff3 Add license disclaimer to rjp_lex files 2020-03-17 16:02:44 -07:00
rexy712
4387f9d39a Rename *_yacc_* to *_parse_* 2020-03-17 16:01:41 -07:00
rexy712
8e454b3d5a Separated lexing logic further. Added file for lexical analysis functions 2020-03-17 15:59:51 -07:00
rexy712
21e4583827 Separate tokenizing logic from loop 2020-03-16 18:28:28 -07:00
rexy712
e38245261e Remove parsing dependance on null terminator 2020-03-14 09:21:57 -07:00
rexy712
cc372b2941 Separated parsing loop and parsing logic 2020-03-13 12:29:52 -07:00
rexy712
e3d16e2112 Fix rjp_parse signature 2020-03-12 17:51:12 -07:00
rexy712
dd76bc9253 Change comment/excess comma support to runtime behavior 2020-03-11 12:55:30 -07:00
rexy712
e947976340 Remove array functions from rjp++ that should have been removed previously 2020-03-10 15:16:52 -07:00
rexy712
f2e4593847 Fix typo in parsing with trailing commas enabled 2020-03-10 15:07:16 -07:00
rexy712
bfd1777fef Add block comments and make RJP_yacc_target stack dynamically resize 2020-03-10 12:56:17 -07:00
rexy712
d6a5d3009b More TODO 2020-03-10 08:28:03 -07:00
rexy712
be11a8e482 Update todo 2020-03-09 17:36:57 -07:00
rexy712
466168f8a2 Add a couple more utf testes 2020-03-09 17:36:28 -07:00
rexy712
b60cfce6fc Add utf escapes to test program 2020-03-09 16:03:05 -07:00
rexy712
46e86e248b Fix utf escape support 2020-03-09 16:02:48 -07:00
rexy712
a8ba1cc2fb Update readme 2020-03-09 15:08:10 -07:00
rexy712
d27e8a8879 Update TODO 2020-03-09 15:00:18 -07:00
rexy712
a17e19bbc0 Updated parsing. Fixes many different sources of segfaults, simplifies modification, and (finally) adds support for scientific notation. Along with a handful of other small parsing changes/fixes. 2020-03-09 15:00:08 -07:00
rexy712
b5002db578 Fix rjp_internal to use newer configure option 2020-03-09 14:40:04 -07:00
rexy712
821ca95610 Update rjp++ CMake project to match rjp's 2020-03-09 14:39:37 -07:00
rexy712
67036521d3 Add more configure options to CMake project. 2020-03-09 14:39:17 -07:00
rexy712
c2f4131b63 Add test source file 2020-03-09 14:38:33 -07:00
rexy712
637f20e6b7 Supply correct lib flags for static/shared build 2020-03-08 19:10:01 -07:00
rexy712
91f810e404 Update readme 2020-03-07 13:25:22 -08:00
rexy712
65dabe16d9 Separate CMakeLists into separate projects for rjp and rjp++. Add install for pkg-config files 2020-03-06 16:14:59 -08:00
rexy712
5b0570f546 Updated gitignore for pc files 2020-03-06 16:13:44 -08:00
rexy712
59413d6d48 Separated rjp++'s single include file 2020-03-06 16:13:28 -08:00
rexy712
26e3cf4b2c Add pkg-config files 2020-03-06 16:12:49 -08:00
rexy712
24f9f608a8 Flesh out rjp++. Add conversion, parsing, type checking, key access, etc 2020-03-05 17:23:30 -08:00
rexy712
5c7be8fb66 Add null pointer checks to unordered object iterator 2020-03-05 17:22:56 -08:00
rexy712
f97826aac3 Change how rjp++ handles member/element addition 2020-03-04 14:49:20 -08:00
rexy712
be714c29ab Add rjp project as dependency of rjp++ 2020-03-03 18:23:43 -08:00
rexy712
fcad8e2062 Update rjp.h include guard to be less likely to cause collisions 2020-03-03 17:57:30 -08:00
rexy712
744021b967 Add license disclaimer to rjp++ files 2020-03-03 17:56:36 -08:00
rexy712
c6241dc37c Add link dependency to rjp++ 2020-03-03 17:56:12 -08:00
rexy712
72f720000e Add start of C++ wrapper 2020-03-03 17:49:39 -08:00
rexy712
2e9571115a Fix API function mk2 2020-02-29 13:07:05 -08:00
rexy712
3e1a785938 Add missing API function 2020-02-29 13:01:12 -08:00
rexy712
7b54b12e6e Add ordered objects using linked lists. Add wrapper for auto selecting functions to call given either object type. Add conversion functions between object types. 2020-02-29 11:58:11 -08:00
rexy712
99cc9a5154 Fix pretty printing 2020-02-27 10:17:33 -08:00
rexy712
ffac4718e8 Update TODO 2020-02-26 18:31:20 -08:00
rexy712
4b68e32298 Version update 2020-02-26 18:24:39 -08:00
rexy712
07c07cc06b Fix object iterator skipping values 2020-02-26 18:24:18 -08:00
rexy712
e15c54b7cc Revision bump 2020-02-24 18:38:35 -08:00
rexy712
092a6497f8 Add memory zeroing during object/array mutation 2020-02-24 18:31:09 -08:00
rexy712
88cdc61357 Actually implement key mutability 2020-02-24 18:30:38 -08:00
rexy712
4be1d9b2d0 Update version 2020-02-24 14:49:25 -08:00
rexy712
250c6b1e59 Fix c++ compatability and const correctness 2020-02-24 14:46:47 -08:00
68 changed files with 6402 additions and 1155 deletions

3
.gitignore vendored
View File

@ -8,3 +8,6 @@ include/config.h
makefile makefile
Makefile Makefile
src/tester.c src/tester.c
*.pc
.cflags.tmp
.ldflags.tmp

View File

@ -1,39 +1,72 @@
include(CMakeDependentOption) project(rjp)
cmake_minimum_required(VERSION 3.0.2)
include(GNUInstallDirs) include(GNUInstallDirs)
cmake_minimum_required(VERSION 3.0.2) set(rjp_VERSION_MAJOR 1)
project(rjp) set(rjp_VERSION_MINOR 1)
set(rjp_VERSION_MAJOR 0)
set(rjp_VERSION_MINOR 8)
set(rjp_VERSION_REVISION 0) set(rjp_VERSION_REVISION 0)
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include) set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
configure_file(
"${INCLUDE_PATH}/config.h.in"
"${INCLUDE_PATH}/config.h"
)
include_directories("${INCLUDE_PATH}") include_directories("${INCLUDE_PATH}")
option(ENABLE_DIAGNOSTICS "Print diagnostic messages when parsing json to help identify issues" ON) set(RJP_LIBFLAGS "-lrjp")
option(ENABLE_SHARED "Build shared library" OFF)
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) if(ENABLE_SHARED)
add_library(rjp SHARED ${SOURCE_LIST}) 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() else()
add_library(rjp STATIC ${SOURCE_LIST}) add_library(rjp STATIC ${SOURCE_LIST})
endif() 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) set_target_properties(rjp PROPERTIES PUBLIC_HEADER ${INCLUDE_PATH}/rjp.h)
target_compile_options(rjp PRIVATE -Wall -Wextra -pedantic) target_compile_options(rjp PRIVATE -Wall -Wextra -pedantic)
if(ENABLE_DIAGNOSTICS)
target_compile_definitions(rjp PRIVATE RJP_DIAGNOSTICS)
endif()
install(TARGETS rjp install(TARGETS rjp
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 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) add_custom_target(uninstall cat install_manifest.txt | xargs rm)

View File

@ -3,10 +3,10 @@
## About ## 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 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
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 ##### Run the following commands
``` ```

6
TODO
View File

@ -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
View 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
View File

@ -0,0 +1,4 @@
output
parse
parse_file
*.o

14
doc/examples/makefile Normal file
View 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
View 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
View 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
View 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);
}

View File

@ -23,5 +23,7 @@
#define RJP_VERSION_MINOR @rjp_VERSION_MINOR@ #define RJP_VERSION_MINOR @rjp_VERSION_MINOR@
#define RJP_VERSION_REVISION @rjp_VERSION_REVISION@ #define RJP_VERSION_REVISION @rjp_VERSION_REVISION@
#cmakedefine RJP_ENABLE_DIAGNOSTICS
#endif #endif

View File

@ -16,121 +16,178 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef RJP_H #ifndef RJP_H_INCLUDED
#define RJP_H #define RJP_H_INCLUDED
#ifdef __cplusplus #ifdef __cplusplus
extern "C"{ extern "C"{
#endif #endif
#if defined(__GNUC__) || defined(__clang__) #ifdef __cplusplus
#define DEPRECATED(str) __attribute__((deprecated(str))) # 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 #else
#define DEPRECATED(str) # define DEPRECATED(str)
#endif #endif
#ifndef RJP_int #ifndef RJP_int
#include <stdint.h> # include <stdint.h>
#define RJP_int int_fast64_t # define RJP_int int_fast64_t
#endif #endif
#ifndef RJP_index #ifndef RJP_index
#include <stddef.h> # include <stddef.h>
#define RJP_index size_t # define RJP_index size_t
#endif #endif
#ifndef RJP_float #ifndef RJP_float
#include <stdint.h> # define RJP_float double
#define RJP_float double
#endif #endif
#ifndef RJP_bool #ifndef RJP_bool
#define RJP_bool _Bool # define RJP_bool char
#endif #endif
//used with rjp_to_json typedef enum RJP_format_flag{
#define RJP_FORMAT_NONE 0 RJP_FORMAT_NONE = 0,
#define RJP_FORMAT_PRETTY 1 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{ typedef enum RJP_data_type{
rjp_json_object = 0, rjp_json_object = 1,
rjp_json_string, rjp_json_ordered_object = 3,
rjp_json_integer, rjp_json_string = 2,
rjp_json_dfloat, rjp_json_integer = 4,
rjp_json_boolean, rjp_json_dfloat = 8,
rjp_json_array, rjp_json_boolean = 16,
rjp_json_null rjp_json_array = 32,
rjp_json_null = 64
}RJP_data_type; }RJP_data_type;
typedef struct RJP_value RJP_value; typedef struct RJP_value RJP_value;
//Hold a C string and the length excluding NULL terminator
typedef struct RJP_string{ typedef struct RJP_string{
char* value; char* value;
RJP_index length; RJP_index length;
}RJP_string; }RJP_string;
//Forward declarations
struct RJP_object_iterator_impl; struct RJP_object_iterator_impl;
typedef struct RJP_object_iterator{ typedef struct RJP_object_iterator{
union{
struct RJP_object_iterator_impl* it; struct RJP_object_iterator_impl* it;
RJP_value* current;
};
RJP_data_type type;
}RJP_object_iterator; }RJP_object_iterator;
typedef struct RJP_array_iterator{ typedef struct RJP_array_iterator{
RJP_value* current; RJP_value* current;
}RJP_array_iterator; }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 *******************/ /***************** 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); void* rjp_alloc(RJP_index nbytes);
//Allocate heap space for rjp and initialize to 0.
void* rjp_calloc(RJP_index num, RJP_index nbytes); 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); void rjp_free(void* dest);
//copy input string and add json escape sequences
RJP_index rjp_escape_strcpy(char* dest, const char* src); 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_index rjp_escape_strlen(const char* str);
RJP_string rjp_escape(const char* src);
/***************** GENERIC OPERATIONS *******************/ /***************** GENERIC OPERATIONS *******************/
//Convert C string consisting of json data into RJP's format RJP_value* rjp_simple_parse(const char* str);
RJP_value* rjp_parse(const char* str); RJP_value* rjp_parse(const char* str, int flags, RJP_parse_error* err);
//RJP_value* rjp_parse_chunked(const char* str, RJP_value* prev_chunk); 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(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_null(void);
RJP_value* rjp_new_int(RJP_int val); RJP_value* rjp_new_int(RJP_int val);
RJP_value* rjp_new_float(RJP_float val); RJP_value* rjp_new_float(RJP_float val);
RJP_value* rjp_new_bool(RJP_bool 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(const char* val, RJP_index length);
RJP_value* rjp_new_string(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_object(void);
RJP_value* rjp_new_ordered_object(void);
RJP_value* rjp_new_array(void); RJP_value* rjp_new_array(void);
//Deallocate RJP_value RJP_value* rjp_new_null_c(const RJP_memory_fns* fns);
void rjp_free_value(RJP_value* root); 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(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); 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_null(RJP_value* v);
RJP_value* rjp_set_int(RJP_value* v, RJP_int val); 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_float(RJP_value* v, RJP_float val);
RJP_value* rjp_set_bool(RJP_value* v, RJP_bool 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, const char* val, RJP_index length);
RJP_value* rjp_set_string(RJP_value* v, 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_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_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_float rjp_get_float(const RJP_value* value);
RJP_int rjp_get_int(const RJP_value* value); RJP_int rjp_get_int(const RJP_value* value);
RJP_bool rjp_get_bool(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 *******************/ /***************** OBJECT/ARRAY SHARED OPERATIONS *******************/
RJP_value* rjp_value_parent(const RJP_value* element); RJP_value* rjp_value_parent(const RJP_value* element);
//Return type of value
RJP_data_type rjp_value_type(const RJP_value* value); RJP_data_type rjp_value_type(const RJP_value* value);
/***************** OBJECT OPERATIONS *******************/ /***************** OBJECT OPERATIONS *******************/
//add a member to a json object, allocating a copy of the key RJP_value* rjp_new_member(RJP_value* dest, const char* key, RJP_index keylen);
RJP_value* rjp_add_member_key_copy(RJP_value* dest, const char* key, RJP_index keylen); RJP_value* rjp_new_member_steal_key(RJP_value* dest, 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, const char* key, RJP_index keylen, RJP_value* src);
RJP_value* rjp_add_member(RJP_value* dest, char* key, RJP_index keylen); 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_by_key(RJP_value* obj, const char* key);
RJP_value* rjp_remove_member(RJP_value* obj, RJP_value* member); 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_by_key(RJP_value* obj, const char* key);
void rjp_free_member(RJP_value* obj, RJP_value* member); 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 RJP_value* rjp_set_key_steal(RJP_value* dest, char* key, RJP_index keylen);
void rjp_set_key(RJP_value* dest, char* key, RJP_index keylen); RJP_value* rjp_set_key(RJP_value* dest, const char* key, RJP_index keylen);
//set existing object member's key RJP_value* rjp_set_key_steal_c(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns);
void rjp_set_key_copy(RJP_value* dest, const char* key, RJP_index keylen); 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); 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); 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_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 *******************/ /***************** 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_init_object_iterator(RJP_object_iterator* iter, const RJP_value* object);
void rjp_delete_object_iterator(RJP_object_iterator* it); 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_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 *******************/ /***************** ARRAY OPERATIONS *******************/
//add an element to a json array RJP_value* rjp_new_element(RJP_value* dest);
RJP_value* rjp_add_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); 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(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); RJP_index rjp_num_elements(const RJP_value* arr);
/***************** ARRAY ITERATOR OPERATIONS *******************/ /***************** ARRAY ITERATOR OPERATIONS *******************/
void rjp_init_array_iterator(RJP_array_iterator* iter, const RJP_value* array); void rjp_init_array_iterator(RJP_array_iterator* iter, const RJP_value* array);
void rjp_delete_array_iterator(RJP_array_iterator* iter); 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_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 #ifdef __cplusplus

View File

@ -30,10 +30,9 @@ typedef struct RJP_array{
RJP_index num_elements; RJP_index num_elements;
}RJP_array; }RJP_array;
void irjp_add_element(RJP_array* j); void irjp_add_element(RJP_array* j, const RJP_memory_fns* fns);
void irjp_delete_value(RJP_value* root); void irjp_copy_array(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns);
void irjp_copy_array(RJP_value* dest, const RJP_value* src);
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 #endif

View File

@ -20,6 +20,7 @@
#define RJP_INTERNAL_H #define RJP_INTERNAL_H
#include "rjp.h" #include "rjp.h"
#include "config.h"
#include <stdio.h> #include <stdio.h>
#ifdef __GNUC__ #ifdef __GNUC__
@ -30,7 +31,7 @@
#define UNUSED_VARIABLE(thing) (void)(thing) #define UNUSED_VARIABLE(thing) (void)(thing)
#ifdef RJP_DIAGNOSTICS #ifdef RJP_ENABLE_DIAGNOSTICS
#define DIAG_PRINT(...) fprintf(__VA_ARGS__) #define DIAG_PRINT(...) fprintf(__VA_ARGS__)
#else #else
#define DIAG_PRINT(...) irjp_ignore_unused(__VA_ARGS__) #define DIAG_PRINT(...) irjp_ignore_unused(__VA_ARGS__)
@ -40,16 +41,6 @@ static inline void irjp_ignore_unused(FILE* fp, ...){
} }
#endif #endif
extern const RJP_memory_fns irjp_default_memory_fns;
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);
#endif #endif

94
include/rjp_lex.h Normal file
View 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

View File

@ -19,18 +19,13 @@
#ifndef RJP_OBJECT_H #ifndef RJP_OBJECT_H
#define RJP_OBJECT_H #define RJP_OBJECT_H
#include "rjp_unordered_object.h"
#include "rjp_ordered_object.h"
#include "rjp_internal.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{ RJP_index irjp_dump_object(const RJP_value* root, char* dest, const int flags, int depth);
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);
#endif #endif

View 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

View File

@ -20,13 +20,16 @@
#define RJP_STRINGS_H #define RJP_STRINGS_H
#include "rjp.h" #include "rjp.h"
#include "rjp_internal.h"
int irjp_is_whitespace(char c); int irjp_is_whitespace(char c);
char* irjp_parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column); int irjp_copy_string_quoted(char* restrict dest, const char* restrict string, RJP_index length);
RJP_index irjp_array_strlen(const RJP_value* arr); int irjp_copy_string_keyed(char* restrict dest, const char* restrict string, RJP_index length);
RJP_index irjp_object_strlen(const RJP_value* root); char* irjp_convert_string(const char* str, RJP_index length, RJP_index* newlen, const RJP_memory_fns* fns);
RJP_index irjp_value_strlen(const RJP_value* root); RJP_index irjp_array_strlen(const RJP_value* arr, const int flags, int depth);
RJP_index irjp_value_strlen_pretty(const RJP_value* root, int depth); RJP_index irjp_object_strlen(const RJP_value* root, const int flags, int depth);
void irjp_strcpy(RJP_string* dest, const RJP_string* src); 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 #endif

View 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

View File

@ -31,12 +31,15 @@ typedef struct RJP_value{
RJP_int integer; RJP_int integer;
RJP_float dfloat; RJP_float dfloat;
RJP_bool boolean; RJP_bool boolean;
struct RJP_object object; RJP_ordered_object orobject;
struct RJP_string string; RJP_unordered_object object;
struct RJP_array array; RJP_string string;
RJP_array array;
}; };
struct RJP_value* parent; //pointer to parent (either an array or object or NULL) 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 enum RJP_data_type type; //flag to determine active member of union
}RJP_value; }RJP_value;
void irjp_delete_value(RJP_value* root, const RJP_memory_fns* fns);
#endif #endif

View File

@ -20,8 +20,8 @@
#define RJP_TREE_H #define RJP_TREE_H
#include "rjp.h" #include "rjp.h"
#include "rjp_object.h"
#include "rjp_object_member.h" #include "rjp_object_member.h"
#include "rjp_value.h"
#define RJP_TREE_SUCCESS 0 #define RJP_TREE_SUCCESS 0
#define RJP_TREE_ERR_NULL_ROOT 1 #define RJP_TREE_ERR_NULL_ROOT 1
@ -37,14 +37,12 @@ struct RJP_tree_node{
unsigned color:1; unsigned color:1;
}; };
RJP_tree_node* irjp_new_node(char* key, RJP_index keylen); 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); 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_remove_value(RJP_tree_node* root, const char* key, RJP_value** removed_node, int* status); 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_tree_search_value(RJP_tree_node* root, const char* key);
RJP_tree_node* irjp_copy_tree(const 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); void irjp_free_tree(RJP_tree_node* root, const RJP_memory_fns* fns);
void irjp_dbg_print_tree(RJP_tree_node* root);
void irjp_dbg_print_tree_bfs(RJP_tree_node* root);
#endif #endif

9
pc/rjp.pc.cmake.in Normal file
View 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
View 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
View 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
View 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

View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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

View 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

View 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
View 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

View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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();
}
}

View 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
View 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
View 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
View 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;
}

View File

@ -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;
}

View File

@ -16,128 +16,206 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include "rjp.h" #include "rjp.h"
#include "rjp_internal.h" #include "rjp_internal.h"
#include "memory.h"
#include "rjp_string.h" #include "rjp_string.h"
#include "rjp_object.h" #include "rjp_object.h"
#include "rjp_value.h" #include "rjp_value.h"
#include "rjp_array.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h> //PRId64
#include <string.h> //strlen #include <string.h> //strlen
#include <stdio.h> //sprintf #include <stdio.h> //sprintf
RJP_value* rjp_new_null(void){ 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; ret->type = rjp_json_null;
return ret; return ret;
} }
RJP_value* rjp_new_int(RJP_int val){ RJP_value* rjp_new_int_c(RJP_int val, const RJP_memory_fns* fns){
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_integer; ret->type = rjp_json_integer;
ret->integer = val; ret->integer = val;
return ret; return ret;
} }
RJP_value* rjp_new_float(RJP_float val){ RJP_value* rjp_new_float_c(RJP_float val, const RJP_memory_fns* fns){
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_dfloat; ret->type = rjp_json_dfloat;
ret->dfloat = val; ret->dfloat = val;
return ret; return ret;
} }
RJP_value* rjp_new_bool(RJP_bool val){ RJP_value* rjp_new_bool_c(RJP_bool val, const RJP_memory_fns* fns){
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_boolean; ret->type = rjp_json_boolean;
ret->boolean = val; ret->boolean = val;
return ret; return ret;
} }
RJP_value* rjp_new_string(char* val, RJP_index length){ RJP_value* rjp_new_string_steal_c(char* val, RJP_index length, const RJP_memory_fns* fns){
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; ret->type = rjp_json_string;
ret->string.value = val; ret->string.value = val;
ret->string.length = length; ret->string.length = length;
return ret; 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); 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; ret->type = rjp_json_string;
RJP_index esclen = rjp_escape_strlen(value); 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); rjp_escape_strcpy(ret->string.value, value);
ret->string.value[esclen] = 0; ret->string.value[esclen] = 0;
ret->string.length = esclen; ret->string.length = esclen;
return ret; return ret;
} }
RJP_value* rjp_new_object(void){ RJP_value* rjp_new_object_c(const RJP_memory_fns* fns){
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_object; ret->type = rjp_json_object;
return ret; return ret;
} }
RJP_value* rjp_new_array(void){ RJP_value* rjp_new_ordered_object_c(const RJP_memory_fns* fns){
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_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; ret->type = rjp_json_array;
return ret; return ret;
} }
RJP_value* rjp_set_null(RJP_value* v){ 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; v->type = rjp_json_null;
return v; return v;
} }
RJP_value* rjp_set_int(RJP_value* v, RJP_int val){ RJP_value* rjp_set_int_c(RJP_value* v, RJP_int val, const RJP_memory_fns* fns){
irjp_delete_value(v); irjp_delete_value(v, fns);
v->type = rjp_json_integer; v->type = rjp_json_integer;
v->integer = val; v->integer = val;
return v; return v;
} }
RJP_value* rjp_set_float(RJP_value* v, RJP_float val){ RJP_value* rjp_set_float_c(RJP_value* v, RJP_float val, const RJP_memory_fns* fns){
irjp_delete_value(v); irjp_delete_value(v, fns);
v->type = rjp_json_dfloat; v->type = rjp_json_dfloat;
v->dfloat = val; v->dfloat = val;
return v; return v;
} }
RJP_value* rjp_set_bool(RJP_value* v, RJP_bool val){ RJP_value* rjp_set_bool_c(RJP_value* v, RJP_bool val, const RJP_memory_fns* fns){
irjp_delete_value(v); irjp_delete_value(v, fns);
v->type = rjp_json_boolean; v->type = rjp_json_boolean;
v->boolean = val; v->boolean = val;
return v; return v;
} }
RJP_value* rjp_set_string(RJP_value* v, char* val, RJP_index len){ RJP_value* rjp_set_string_steal_c(RJP_value* v, char* val, RJP_index len, const RJP_memory_fns* fns){
irjp_delete_value(v); irjp_delete_value(v, fns);
v->type = rjp_json_string; v->type = rjp_json_string;
v->string.value = val; v->string.value = val;
v->string.length = len; v->string.length = len;
return v; 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); UNUSED_VARIABLE(length);
irjp_delete_value(v); irjp_delete_value(v, fns);
v->type = rjp_json_string; v->type = rjp_json_string;
RJP_index esclen = rjp_escape_strlen(val); 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); rjp_escape_strcpy(v->string.value, val);
v->string.value[esclen] = 0; v->string.value[esclen] = 0;
v->string.length = esclen; v->string.length = esclen;
return v; 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) if(v->type == rjp_json_object)
return v; return v;
irjp_delete_value(v); irjp_delete_value(v, fns);
memset(&v->object, 0, sizeof(RJP_unordered_object));
v->type = rjp_json_object; v->type = rjp_json_object;
return v; 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) if(v->type == rjp_json_array)
return v; return v;
irjp_delete_value(v); irjp_delete_value(v, fns);
memset(&v->array, 0, sizeof(RJP_array));
v->type = rjp_json_array; v->type = rjp_json_array;
return v; 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; RJP_index ret;
switch(val->type){ switch(val->type){
case rjp_json_integer: 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); ret = sprintf(dest, "%lf", val->dfloat);
break; break;
case rjp_json_boolean: 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; break;
case rjp_json_null: case rjp_json_null:
ret = sprintf(dest, "null"); memcpy(dest, "null", 4);
ret = 4;
break; break;
case rjp_json_string:; case rjp_json_string:;
ret = sprintf(dest, "\"%s\"", val->string.value); ret = irjp_copy_string_quoted(dest, val->string.value, val->string.length);
break; break;
case rjp_json_array: case rjp_json_array:
ret = rjp_dump_array(val, dest); ret = irjp_dump_array(val, dest, flags, depth);
break; break;
case rjp_json_object: case rjp_json_object:
ret = rjp_dump_object(val, dest); case rjp_json_ordered_object:
break; ret = irjp_dump_object(val, dest, flags, depth);
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);
break; break;
default: default:
ret = 0; ret = 0;
@ -240,56 +254,106 @@ static RJP_index irjp_write_value_pretty(char* dest, const RJP_value* val, int d
return ret; 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_array_iterator it;
rjp_init_array_iterator(&it, arr); rjp_init_array_iterator(&it, arr);
RJP_index pos = 1; RJP_value* current = rjp_array_iterator_current(&it);
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);
if(rjp_array_iterator_peek(&it)) if(!current){
pos += sprintf(dest+pos, ","); rjp_delete_array_iterator(&it);
else dest[pos++] = '[';
break; 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; 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_object_iterator it;
rjp_init_object_iterator(&it, root); rjp_init_object_iterator(&it, root);
RJP_index pos = 1; RJP_index pos = 0;
sprintf(dest, "{"); RJP_value* current = rjp_object_iterator_current(&it);
if(!current){
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_delete_object_iterator(&it); 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; 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) if(!root)
return NULL; 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) if(!len)
return NULL; return NULL;
char* tmp = rjp_alloc(len + 1); char* tmp = fns->alloc(len + 1);
tmp[len] = 0; tmp[len] = 0;
if(pretty) irjp_write_value(tmp, root, flags, 0);
irjp_write_value_pretty(tmp, root, 0);
else
irjp_write_value(tmp, root);
return tmp; return tmp;
} }

View File

@ -19,9 +19,10 @@
#include "rjp_internal.h" #include "rjp_internal.h"
#include "rjp_string.h" #include "rjp_string.h"
#include "rjp_object.h" #include "rjp_object.h"
#include "rjp_array.h"
#include "rjp_value.h" #include "rjp_value.h"
#include <stdlib.h> //free, malloc #include <stdlib.h> //free, malloc, calloc
void* rjp_alloc(RJP_index nbytes){ void* rjp_alloc(RJP_index nbytes){
return malloc(nbytes); return malloc(nbytes);
@ -29,32 +30,37 @@ void* rjp_alloc(RJP_index nbytes){
void* rjp_calloc(RJP_index num, RJP_index nbytes){ void* rjp_calloc(RJP_index num, RJP_index nbytes){
return calloc(num, nbytes); return calloc(num, nbytes);
} }
void* rjp_realloc(void* ptr, RJP_index nbytes){
return realloc(ptr, nbytes);
}
void rjp_free(void* data){ void rjp_free(void* data){
free(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){ 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; RJP_value* ret;
if(dest){ if(dest){
irjp_delete_value(dest); irjp_delete_value(dest, fns);
ret = dest; ret = dest;
}else{ }else{
ret = rjp_new_null(); ret = rjp_new_null_c(fns);
} }
ret->type = src->type; ret->type = src->type;
switch(src->type){ switch(src->type){
case rjp_json_object: 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; break;
case rjp_json_array: case rjp_json_array:
irjp_copy_array(ret, src); irjp_copy_array(ret, src, fns);
break; break;
case rjp_json_string: case rjp_json_string:
irjp_strcpy(&ret->string, &src->string); irjp_strcpy(&ret->string, &src->string, fns);
break; break;
case rjp_json_integer: case rjp_json_integer:
ret->integer = src->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){ RJP_value* rjp_move_value(RJP_value* dest, RJP_value* src){
*dest = *src; *dest = *src;
src->type = rjp_json_null;
return dest; 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_array_iterator it;
rjp_init_array_iterator(&it, root); rjp_init_array_iterator(&it, root);
RJP_value* next = rjp_array_iterator_current(&it); RJP_value* next = rjp_array_iterator_current(&it);
for(RJP_value* current = next;current;current = next){ for(RJP_value* current = next;current;current = next){
next = rjp_array_iterator_next(&it); next = rjp_array_iterator_next(&it);
switch(current->type){ rjp_free_value_c(current, fns);
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);
} }
} }
void irjp_delete_value(RJP_value* root){ void irjp_delete_value(RJP_value* root, const RJP_memory_fns* fns){
if((root->type) == rjp_json_object) switch(root->type){
irjp_delete_object(root); case rjp_json_object:
else if((root->type) == rjp_json_array) case rjp_json_ordered_object:
irjp_delete_array(root); irjp_delete_object(root, fns);
else if((root->type) == rjp_json_string) break;
rjp_free(root->string.value); 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){ 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) if(!root)
return; return;
irjp_delete_value(root); irjp_delete_value(root, fns);
free(root); fns->free(root);
} }
/* VALUE SETTING */ /* VALUE SETTING */
@ -137,32 +141,3 @@ RJP_string* rjp_get_string(RJP_value* value){
const RJP_string* rjp_get_cstring(const RJP_value* value){ const RJP_string* rjp_get_cstring(const RJP_value* value){
return &(value->string); 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};
}

View File

@ -16,39 +16,57 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "rjp_array.h"
#include "rjp_internal.h" #include "rjp_internal.h"
#include "rjp_array.h"
#include "rjp_value.h" #include "rjp_value.h"
#include "rjp_array_element.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; dest->array.elements = dest->array.last = NULL;
for(RJP_array_element* curr = src->array.elements;curr;curr = curr->next){ for(RJP_array_element* curr = src->array.elements;curr;curr = curr->next){
RJP_value* copy_mem; RJP_value* copy_mem;
copy_mem = rjp_add_element(dest); copy_mem = rjp_new_element_c(dest, fns);
rjp_copy_value(copy_mem, &curr->value); 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; ++j->num_elements;
if(!j->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; j->last = j->elements;
}else{ }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 = j->last->next;
j->last->prev = j->last; j->last->prev = j->last;
} }
} }
RJP_value* rjp_add_element(RJP_value* dest){ RJP_value* rjp_new_element(RJP_value* dest){
irjp_add_element(&dest->array); 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; dest->array.last->value.parent = dest;
return &dest->array.last->value; return &dest->array.last->value;
} }
RJP_value* rjp_remove_element(RJP_value* dest, RJP_value* value){ RJP_value* rjp_add_element(RJP_value* dest, RJP_value* src){
RJP_array* j = &dest->array; return rjp_add_element_c(dest, src, &irjp_default_memory_fns);
if(!j->num_elements) }
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; 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; --j->num_elements;
RJP_array_element* elem = (RJP_array_element*)value; 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; return value;
} }
void rjp_free_element(RJP_value* dest, RJP_value* 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_remove_element(dest, value);
rjp_free_value(value); rjp_free_value_c(value, fns);
} }
RJP_index rjp_num_elements(const RJP_value* array){ RJP_index rjp_num_elements(const RJP_value* array){
return array->array.num_elements; return array->array.num_elements;
} }
void rjp_init_array_iterator(RJP_array_iterator* iter, const RJP_value* array){ 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){ void rjp_delete_array_iterator(RJP_array_iterator* it){
if(!it)
return;
it->current = NULL; it->current = NULL;
} }
RJP_value* rjp_get_element(const RJP_value* array){ RJP_value* rjp_array_iterator_current(const RJP_array_iterator* it){
return &array->array.elements->value;
}
RJP_value* rjp_array_iterator_current(RJP_array_iterator* it){
return it->current; return it->current;
} }
RJP_value* rjp_array_iterator_next(RJP_array_iterator* it){ RJP_value* rjp_array_iterator_next(RJP_array_iterator* it){
RJP_array_element* curr = (RJP_array_element*)it->current; 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; 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; RJP_array_element* curr = (RJP_array_element*)it->current;
return curr ? &(curr->next->value) : NULL; return curr ? &(curr->next->value) : NULL;
} }

374
src/rjp_lex.c Normal file
View 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;
}

View File

@ -20,217 +20,296 @@
#include "tree.h" #include "tree.h"
#include "rjp_object.h" #include "rjp_object.h"
#include "rjp_object_member.h" #include "rjp_object_member.h"
#include "rjp_value.h"
#include <string.h> //strlen, strncpy #include <string.h> //strlen, strncpy
#define RJP_TREE_ITERATOR_STACK_START_SIZE 32 void irjp_copy_object(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns){
switch(src->type){
typedef struct RJP_tree_stack{ case rjp_json_object:
RJP_tree_node** data; irjp_copy_unordered_object(dest, src, fns);
int size; break;
int pos; case rjp_json_ordered_object:
}RJP_tree_stack; irjp_copy_ordered_object(dest, src, fns);
break;
typedef struct RJP_object_iterator_impl{ default: break;
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;
} }
static void irjp_delete_tree_stack(RJP_tree_stack* stack){ void irjp_delete_object(RJP_value* obj, const RJP_memory_fns* fns){
rjp_free(stack->data); 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){ RJP_value* rjp_new_member_steal_key(RJP_value* dest, char* key, RJP_index keylen){
int newsize = stack->size*2; return rjp_new_member_steal_key_c(dest, key, keylen, &irjp_default_memory_fns);
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 RJP_value* rjp_new_member_steal_key_c(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns){
static void irjp_push_tree_stack(RJP_tree_stack* stack, RJP_tree_node* value){ if(!key)
stack->data[stack->pos++] = value; return NULL;
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){
if(!keylen) if(!keylen)
keylen = strlen(key); if(!(keylen = strlen(key)))
return irjp_add_member_impl(dest, key, keylen); 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) if(!keylen)
keylen = rjp_escape_strlen(key); if(!(keylen = rjp_escape_strlen(key)))
char* newkey = rjp_alloc(keylen+1); return NULL;
char* newkey = fns->alloc(keylen+1);
rjp_escape_strcpy(newkey, key); rjp_escape_strcpy(newkey, key);
newkey[keylen] = 0; 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){ RJP_value* rjp_remove_member_by_key(RJP_value* obj, const char* key){
int status; return rjp_remove_member_by_key_c(obj, key, &irjp_default_memory_fns);
RJP_value* removed; }
obj->object.root = irjp_tree_remove_value(obj->object.root, key, &removed, &status); RJP_value* rjp_remove_member_by_key_c(RJP_value* obj, const char* key, const RJP_memory_fns* fns){
if(removed) RJP_value* member = rjp_search_member(obj, key);
rjp_free(((RJP_object_member*)removed)->name.value); if(!member)
return removed; return NULL;
return rjp_remove_member_c(obj, member, fns);
} }
RJP_value* rjp_remove_member(RJP_value* obj, RJP_value* member){ 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){ 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_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){ 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_value* rjp_set_key(RJP_value* dest, const char* key, RJP_index keylen){
RJP_object_member* mem = (RJP_object_member*)dest; return rjp_set_key_c(dest, key, keylen, &irjp_default_memory_fns);
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;
} }
void rjp_set_key(RJP_value* dest, char* key, RJP_index keylen){ RJP_value* rjp_set_key_c(RJP_value* dest, const char* key, RJP_index keylen, const RJP_memory_fns* fns){
RJP_object_member* mem = (RJP_object_member*)dest; if(!key)
if(key){ return NULL;
if(!keylen){ if(!keylen){
keylen = strlen(key); if(!(keylen = strlen(key)))
return NULL;
} }
}else{ char* newkey = fns->alloc(keylen + 1);
keylen = 0; 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; switch(dest->parent->type){
mem->name.length = keylen; 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){ 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){ const RJP_string* rjp_member_key(const RJP_value* member){
return &(((RJP_object_member*)member)->name); return &(((RJP_object_member*)member)->name);
} }
RJP_value* rjp_search_member(const RJP_value* object, const char* search){ 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(!object)
if(!n) 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 NULL;
return &n->data.value;
} }
void rjp_init_object_iterator(RJP_object_iterator* it, const RJP_value* object){ void rjp_init_object_iterator(RJP_object_iterator* it, const RJP_value* object){
it->it = rjp_alloc(sizeof(RJP_object_iterator_impl)); switch(object->type){
irjp_init_object_iterator(it->it, object->object.root); 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){ void rjp_delete_object_iterator(RJP_object_iterator* it){
irjp_delete_object_iterator(it->it); switch(it->type){
rjp_free(it->it); case rjp_json_object:
it->it = NULL; 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_value* rjp_object_iterator_current(const RJP_object_iterator* it){
RJP_tree_node* n = irjp_object_iterator_current(it->it); switch(it->type){
if(!n) 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 NULL;
return &n->data.value;
} }
RJP_value* rjp_object_iterator_next(RJP_object_iterator* it){ RJP_value* rjp_object_iterator_next(RJP_object_iterator* it){
RJP_tree_node* n = irjp_object_iterator_next(it->it); switch(it->type){
if(!n) 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 NULL;
return &n->data.value;
} }
RJP_value* rjp_object_iterator_peek(RJP_object_iterator* it){ RJP_value* rjp_object_iterator_peek(const RJP_object_iterator* it){
RJP_tree_node* n = irjp_object_iterator_peek(it->it); switch(it->type){
if(!n) 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 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
View 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
View 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;
}
}

View File

@ -16,16 +16,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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_internal.h"
#include "rjp_object.h" #include "rjp_string.h"
#include "rjp_value.h" #include "rjp_value.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h> //PRId64
#include <stdio.h> //fprintf #include <stdio.h> //fprintf
#include <stdlib.h> //malloc, free
#include <stdint.h> //uintN_t #include <stdint.h> //uintN_t
#include <string.h> //strcpy #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; return *low = *high = 0;
} }
*low = utf_strtol_4(str+8); *low = utf_strtol_4(str+8);
if(!*low)
return *low = *high = 0;
return 12; return 12;
}else{ }else{
*low = 0; *low = 0;
@ -152,85 +151,88 @@ static int codepoint_to_u8(char* dest, uint32_t codepoint){
} }
}*/ }*/
//Convert escape sequences in strings int irjp_copy_string_quoted(char* restrict dest, const char* restrict string, RJP_index length){
char* irjp_parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column){ dest[0] = '"';
char* new_string; memcpy(dest+1, string, length);
++(*column); //account for starting quotation mark dest[length+1] = '"';
int oldpos = 0; return length+2;
int newpos = 0; }
for(;*(str+oldpos) != '"';++oldpos, ++(newpos), ++(*column)){ int irjp_copy_string_keyed(char* restrict dest, const char* restrict string, RJP_index length){
if(*(str+oldpos) == '\\'){ int offset = irjp_copy_string_quoted(dest, string, length);
if(*(str+oldpos+1) == 'u'){ 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; uint32_t high, low;
oldpos += (decode_unicode_escape(str+oldpos, &high, &low)-1); 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{ }else{
++oldpos; ++oldpos;
++(*column);
} }
}else if(*(str+oldpos) == '\0'){ }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){
return NULL; return NULL;
} }
new_string = rjp_alloc(newpos + 1); }
new_string[newpos] = 0; newstring = fns->alloc(newlength + 1);
for(int i = 0;*str != '"';++i,++str){ newstring[newlength] = 0;
if(*str == '\\'){ *newlen = newlength;
++str; oldpos = 1;
switch(*str){ for(RJP_index newpos = 0;oldpos < length;++newpos,++oldpos){
if(str[oldpos] == '\\'){
++oldpos;
switch(str[oldpos]){
case '"': case '"':
new_string[i] = '"'; newstring[newpos] = '"';
break; break;
case 'n': case 'n':
new_string[i] = '\n'; newstring[newpos] = '\n';
break; break;
case 'r': case 'r':
new_string[i] = '\r'; newstring[newpos] = '\r';
break; break;
case 'b': case 'b':
new_string[i] = '\b'; newstring[newpos] = '\b';
break; break;
case '\\': case '\\':
new_string[i] = '\\'; newstring[newpos] = '\\';
break; break;
case 't': case 't':
new_string[i] = '\t'; newstring[newpos] = '\t';
break; break;
case 'f': case 'f':
new_string[i] = '\f'; newstring[newpos] = '\f';
break; break;
case 'u':; case 'u':;
uint32_t high, low; uint32_t high, low;
uint32_t codepoint; uint32_t codepoint;
--str; --oldpos;
str += (decode_unicode_escape(str, &high, &low) - 1); oldpos += (decode_unicode_escape(str+oldpos, &high, &low)-1);
if(!high){ if(!high){
rjp_free(new_string); rjp_free(newstring);
return NULL; return NULL;
} }
codepoint = utf_to_codepoint(high, low); codepoint = utf_to_codepoint(high, low);
i += (codepoint_to_u8(new_string+i, codepoint)-1); newpos += (codepoint_to_u8(newstring+newpos, codepoint)-1);
break; break;
default: default:
new_string[i] = *str; newstring[newpos] = str[oldpos];
break; break;
} };
}else{ }else{
new_string[i] = *str; newstring[newpos] = str[oldpos];
} }
} }
return new_string; return newstring;
} }
RJP_index rjp_escape_strcpy(char* dest, const char* src){ 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; dest[j] = 0;
return j; return j;
} }
void irjp_strcpy(RJP_string* dest, const RJP_string* src){ void irjp_strcpy(RJP_string* dest, const RJP_string* src, const RJP_memory_fns* fns){
dest->value = rjp_alloc(src->length + 1); dest->value = fns->alloc(src->length + 1);
strcpy(dest->value, src->value); strcpy(dest->value, src->value);
dest->value[src->length] = 0; dest->value[src->length] = 0;
dest->length = src->length; dest->length = src->length;
@ -299,78 +301,69 @@ RJP_index rjp_escape_strlen(const char* str){
} }
return count; 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_index count = 2; //[]
RJP_array_iterator it; if(flags & RJP_FORMAT_NEWLINES)
rjp_init_array_iterator(&it, arr); ++count;
if(flags & RJP_FORMAT_TABBED_LINES)
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){ count += depth;
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]
++depth; ++depth;
RJP_array_iterator it; RJP_array_iterator it;
rjp_init_array_iterator(&it, arr); rjp_init_array_iterator(&it, arr);
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){ for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
count += irjp_value_strlen_pretty(current, depth); count += irjp_value_strlen(current, flags, depth);
count += depth; //tabs if(flags & RJP_FORMAT_NEWLINES)
++count; //newline ++count;
if(rjp_array_iterator_peek(&it)) if(flags & RJP_FORMAT_TABBED_LINES)
count += depth;
if(rjp_array_iterator_peek(&it)){
++count; //, ++count; //,
if(flags & RJP_FORMAT_COMMA_SPACES)
++count; //space
}
} }
return count; 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_index count = 2; //{}
RJP_object_iterator it; if(flags & RJP_FORMAT_NEWLINES)
rjp_init_object_iterator(&it, root); ++count;
RJP_value* current = rjp_object_iterator_current(&it); if(flags & RJP_FORMAT_TABBED_LINES)
while(current){ count += depth;
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}
++depth; ++depth;
RJP_object_iterator it; RJP_object_iterator it;
rjp_init_object_iterator(&it, root); rjp_init_object_iterator(&it, root);
RJP_value* current = rjp_object_iterator_current(&it); RJP_value* current = rjp_object_iterator_current(&it);
while(current){ while(current){
RJP_index namelen = rjp_member_key(current)->length; RJP_index namelen = rjp_member_key(current)->length;
const char* name = rjp_member_key(current)->value; count += namelen + 3; //"":
if(namelen == 0 || name[0] == 0){ if(flags & RJP_FORMAT_KEY_SPACES)
rjp_delete_object_iterator(&it); ++count;
return 0; count += irjp_value_strlen(current, flags, depth);
} if(flags & RJP_FORMAT_NEWLINES)
count += namelen + 4; //"":space ++count;
count += irjp_value_strlen_pretty(current, depth); if(flags & RJP_FORMAT_TABBED_LINES)
count += depth; //tabs count += depth;
++count; //newline if((current = rjp_object_iterator_next(&it))){
if((current = rjp_object_iterator_next(&it)))
++count; //, ++count; //,
if(flags & RJP_FORMAT_COMMA_SPACES)
++count; //space
}
} }
rjp_delete_object_iterator(&it); rjp_delete_object_iterator(&it);
return count; 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){ switch(root->type){
case rjp_json_integer: case rjp_json_integer:
return snprintf(NULL, 0, "%" PRId64, root->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: case rjp_json_null:
return 4; return 4;
case rjp_json_string: case rjp_json_string:
return rjp_escape_strlen(root->string.value) + 2; //""; return rjp_escape_strlen(root->string.value) + 2; //""
case rjp_json_array: case rjp_json_array:
return irjp_array_strlen(root); return irjp_array_strlen(root, flags, depth);
case rjp_json_object: case rjp_json_object:
return irjp_object_strlen(root); case rjp_json_ordered_object:
default: return irjp_object_strlen(root, flags, depth);
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);
default: default:
return 0; return 0;
}; };

176
src/rjp_unordered_object.c Normal file
View 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;
}

View File

@ -18,13 +18,11 @@
#include "tree.h" #include "tree.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "rjp_string.h" #include "rjp_string.h"
#include "rjp_internal.h" #include "rjp_internal.h"
#include "rjp_object.h" #include "rjp_value.h"
#include <string.h> //memset, strcmp
#define BLACK 0 #define BLACK 0
#define RED 1 #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_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 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_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); 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); static void irjp_free_node(RJP_tree_node* node, const RJP_memory_fns* fns);
static void irjp_delete_node(RJP_tree_node* node); 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_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_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 //Tree helpers
@ -125,8 +122,9 @@ static void irjp_replace_node(RJP_tree_node *restrict node, RJP_tree_node *restr
} }
//Node construction / destruction //Node construction / destruction
RJP_tree_node* irjp_new_node(char* key, RJP_index keylen){ RJP_tree_node* irjp_new_node(char* key, RJP_index keylen, const RJP_memory_fns* fns){
RJP_tree_node* node = rjp_calloc(sizeof(RJP_tree_node), 1); 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.value = key;
node->data.name.length = keylen; node->data.name.length = keylen;
node->parent = node->left = node->right = NULL; 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){ static void irjp_copy_node_data(RJP_tree_node *restrict dest, RJP_tree_node *restrict src){
dest->data = src->data; dest->data = src->data;
} }
static RJP_tree_node* irjp_clone_node(const RJP_tree_node* src, RJP_tree_node* parent){ 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 = rjp_alloc(sizeof(RJP_tree_node)); RJP_tree_node* dest = fns->alloc(sizeof(RJP_tree_node));
rjp_copy_value(&dest->data.value, &src->data.value); memset(dest, 0, sizeof(RJP_tree_node));
irjp_strcpy(&dest->data.name, &src->data.name); rjp_copy_value_c(&dest->data.value, &src->data.value, fns);
irjp_strcpy(&dest->data.name, &src->data.name, fns);
dest->parent = parent; dest->parent = parent;
return dest; 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){ if(!root){
return NULL; return NULL;
} }
RJP_tree_node* newnode = irjp_clone_node(root, parent); RJP_tree_node* newnode = irjp_clone_node(root, parent, fns);
newnode->left = irjp_copy_node(root->left, newnode); newnode->left = irjp_copy_node(root->left, newnode, fns);
newnode->right = irjp_copy_node(root->right, newnode); newnode->right = irjp_copy_node(root->right, newnode, fns);
return newnode; return newnode;
} }
static void irjp_free_node(RJP_tree_node* node){ static void irjp_free_node(RJP_tree_node* node, const RJP_memory_fns* fns){
irjp_delete_node(node); irjp_delete_node(node, fns);
rjp_free(node); fns->free(node);
} }
static void irjp_delete_node(RJP_tree_node* node){ static void irjp_delete_node(RJP_tree_node* node, const RJP_memory_fns* fns){
rjp_free(node->data.name.value); fns->free(node->data.name.value);
irjp_delete_value(&node->data.value); irjp_delete_value(&node->data.value, fns);
} }
/* TREE */ /* TREE */
//Tree construction / destruction //Tree construction / destruction
RJP_tree_node* irjp_copy_tree(const RJP_tree_node* root){ RJP_tree_node* irjp_copy_tree(const RJP_tree_node* root, const RJP_memory_fns* fns){
return irjp_copy_node(root, NULL); 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) if(!root)
return; return;
irjp_free_tree(root->left); irjp_free_tree(root->left, fns);
irjp_free_tree(root->right); irjp_free_tree(root->right, fns);
irjp_free_node(root); irjp_free_node(root, fns);
} }
//Tree operations //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; *status = RJP_TREE_SUCCESS;
if(!root){ if(!root){
root = irjp_new_node(key, keylen); root = irjp_new_node(key, keylen, fns);
if(added) if(added)
*added = &root->data.value; *added = &root->data.value;
return root; return root;
} }
RJP_tree_node* newnode = irjp_new_node(key, keylen); RJP_tree_node* newnode = irjp_new_node(key, keylen, fns);
if(added) if(added)
*added = &newnode->data.value; *added = &newnode->data.value;
return irjp_tree_insert_node(root, newnode); 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_insert_impl(root, newnode);
irjp_tree_repair(newnode); irjp_tree_repair(newnode);
while(root->parent) while(root->parent)
@ -270,96 +269,19 @@ RJP_tree_node* irjp_tree_search_value(RJP_tree_node* root, const char* key){
} }
return NULL; return NULL;
} }
//Debug printouts RJP_tree_node* irjp_tree_remove_value(RJP_tree_node* restrict root, RJP_tree_node* restrict member, RJP_tree_node** removed_node){
void irjp_dbg_print_tree(RJP_tree_node* root){ if(!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;
return root; return root;
} if(!member)
RJP_tree_node* n = irjp_tree_search_value(root, key);
if(!n){
*removed_node = NULL;
*status = RJP_TREE_ERR_NOT_FOUND;
return root; return root;
} return irjp_tree_remove_node(member, removed_node);
*status = RJP_TREE_SUCCESS;
return irjp_tree_remove_node(n, removed_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){
if(removed_node)
*removed_node = (RJP_value*)target;
while(target->right && target->left){ while(target->right && target->left){
irjp_copy_node_data(target, target->right); irjp_copy_node_data(target, target->right);
target = target->right; target = target->right;
} }
*removed_node = target;
RJP_tree_node* retval = target->parent; RJP_tree_node* retval = target->parent;
do{ do{
@ -453,6 +375,8 @@ static RJP_tree_node* irjp_tree_remove_node(RJP_tree_node* target, RJP_value** r
} }
}while(1); }while(1);
(*removed_node)->left = NULL;
(*removed_node)->right = NULL;
//return new root //return new root
if(!retval) if(!retval)
return NULL; return NULL;

17
tests/CMakeLists.txt Normal file
View 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
View 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
View 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;
}