Compare commits

...

154 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
rexy712
08b05e8162 Update rjp version. Remove debug source file 2020-02-23 10:49:16 -08:00
rexy712
dac026802f Update CmakeLists.txt 2020-02-23 10:15:16 -08:00
rexy712
656992233e Updated copyright year 2020-02-23 10:03:59 -08:00
rexy712
6a9597c274 Reorganized file structure 2020-02-23 09:59:37 -08:00
rexy712
debd8cc31d Move unused utility function into debug only section 2020-02-19 06:16:09 -08:00
rexy712
67208e38e0 Removed vinit, was a really dumb idea. Redid a lot of the API to match. Fixed some tree related memory leaks. 2020-02-18 17:36:54 -08:00
rexy712
0dfbbb5a5c Fix object member removal 2020-01-28 16:53:02 -08:00
rexy712
ff241e2a6f Successfully hid RJP_value implementation from API 2020-01-19 09:19:20 -08:00
rexy712
30cc8008af Separate object operations from tree operations 2020-01-19 08:12:34 -08:00
rexy712
34c918615f Update todo 2020-01-17 23:41:41 -08:00
rexy712
b447a745a3 Fix missing parent assignment in rjp_add_member 2020-01-17 23:41:26 -08:00
rexy712
45ed17d6ca Tree setup now working on all compilation units 2020-01-17 14:22:15 -08:00
rexy712
0b4562a95a Update rjp.h types 2020-01-17 13:02:53 -08:00
rexy712
b5fdb94fd3 Fix RJP_object_iterator to actually usable state 2020-01-17 12:48:26 -08:00
rexy712
ed5179c677 Fix tree related segfaults. Add member removal to API 2020-01-13 18:38:19 -08:00
rexy712
a646d1c4e8 Moved rjp.c, memory.c, and strings.c over to new tree-based objects. Unfortunately, the API did need modified because C lacks the 'mutable' keyword. 2020-01-13 15:19:27 -08:00
rexy712
6e53fe29e2 Fix memory management of trees 2020-01-13 13:22:49 -08:00
rexy712
05250e6af9 Merge branch 'master' into rbtree 2020-01-12 18:06:46 -08:00
rexy712
faccd2f31a Add naming convention documentation 2020-01-12 18:06:17 -08:00
rexy712
bc755098b3 Add tree iterator (untested) and tree copying 2020-01-12 18:05:24 -08:00
rexy712
ce1877f52a Basic working red black tree 2020-01-12 00:18:45 -08:00
rexy712
12388ac8f6 Fix handling line comments. Still need to convert to system that actually reads one character at a time... 2019-11-09 21:15:33 -08:00
rexy712
4cfd07df38 Add generic deprecated macro 2019-10-30 02:56:36 -07:00
rexy712
be71b9edb4 Merge branch 'master' of ssh://rexy712-server:1995/var/git/repos/rexy712/rjp 2019-10-29 13:52:10 -07:00
rexy712
0b50b3b21a Fix naming issue with RJP_type 2019-10-29 13:51:58 -07:00
rexy712
688dd22c73 Add pretty printing 2019-10-29 13:34:42 -07:00
rexy712
c781550b09 Fixed implicit functions 2019-09-14 06:54:52 -07:00
rexy712
0df7efabb7 Fixed standard noncompliant function naming convention 2019-08-25 07:49:56 -07:00
72 changed files with 7381 additions and 1095 deletions

6
.gitignore vendored
View File

@ -5,3 +5,9 @@ tester.exe
*.swp *.swp
build build
include/config.h include/config.h
makefile
Makefile
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 7)
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/strings.c" "src/memory.c" "src/rjp.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
``` ```

3
TODO
View File

@ -1,3 +0,0 @@
Change string handling to work with chunked reading
Change numeral handling to work with chunked reading
handle scientific notation

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

@ -0,0 +1,9 @@
rjp_init_* = initialize passed pointer
rjp_delete_* = cleanup internal structures
rjp_new_* = rjp_malloc new value and initialize it
rjp_free_* = cleanup internal structures and rjp_free passed pointer
irjp_* = internal function
rjp_* = api function
RJP_* = data type name/typedef

View File

@ -1,6 +1,6 @@
/** /**
rjp rjp
Copyright (C) 2018-2019 rexy712 Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -16,12 +16,14 @@
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 CONFIG_H #ifndef RJP_CONFIG_H
#define CONFIG_H #define RJP_CONFIG_H
#define RJP_VERSION_MAJOR @rjp_VERSION_MAJOR@ #define RJP_VERSION_MAJOR @rjp_VERSION_MAJOR@
#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

@ -1,6 +1,6 @@
/** /**
rjp rjp
Copyright (C) 2018-2019 rexy712 Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -16,184 +16,252 @@
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
#include <stddef.h> //size_t
#ifdef __cplusplus #ifdef __cplusplus
extern "C"{ extern "C"{
#endif #endif
#ifdef __cplusplus
# if __cplusplus >= 201402L
# define DEPRECATED(str) [[deprecated(str)]]
# elif defined(__GNUC__) || defined(__clang__)
# define DEPRECATED(str) __attribute__((deprecated(str)))
# elif __cplusplus >= 201103L
# define DEPRECATED(str) [[deprecated]]
# else
# define DEPRECATED(str)
# endif
#elif __STDC_VERSION__ > 201710L
# define DEPRECATED(str) [[deprecated(str)]]
#elif defined(__GNUC__) || defined(__clang__)
# define DEPRECATED(str) __attribute__((deprecated(str)))
#else
# define DEPRECATED(str)
#endif
#ifndef RJP_int #ifndef RJP_int
#include <stdint.h> # include <stdint.h>
#define RJP_int int64_t # define RJP_int int_fast64_t
#endif
#ifndef RJP_index
# include <stddef.h>
# 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
#ifndef RJP_bool
# define RJP_bool char
#endif #endif
//type of data typedef enum RJP_format_flag{
typedef enum RJP_type{ RJP_FORMAT_NONE = 0,
json_object = 0, RJP_FORMAT_KEY_SPACES = 1,
json_string, RJP_FORMAT_NEWLINES = 2,
json_integer, RJP_FORMAT_TABBED_LINES = RJP_FORMAT_NEWLINES | 4,
json_dfloat, RJP_FORMAT_COMMA_SPACES = 8,
json_boolean, RJP_FORMAT_PRETTY = RJP_FORMAT_KEY_SPACES | RJP_FORMAT_NEWLINES | RJP_FORMAT_TABBED_LINES
json_array, }RJP_format_flag;
json_null
}RJP_type; 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;
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;
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;
size_t length; RJP_index length;
}RJP_string; }RJP_string;
//Forward declarations struct RJP_object_iterator_impl;
struct RJP_object_member; typedef struct RJP_object_iterator{
struct RJP_array_element;
//Represents a json object
typedef struct RJP_object{
struct RJP_object_member* members; //linked list of members
struct RJP_object_member* last; //final member of linked list
size_t num_members;
}RJP_object;
//Represents a json array
typedef struct RJP_array{
struct RJP_array_element* elements; //linked list of elements
struct RJP_array_element* last; //final member of linked list
size_t num_elements;
}RJP_array;
//Represents json data
//hold any json data type
typedef struct RJP_value{
union{ union{
RJP_int integer; struct RJP_object_iterator_impl* it;
RJP_float dfloat; RJP_value* current;
char boolean;
struct RJP_object object;
struct RJP_string string;
struct RJP_array array;
}; };
struct RJP_value* parent; //pointer to parent (either an array or object or NULL) RJP_data_type type;
enum RJP_type type; //flag to determine active member of union }RJP_object_iterator;
}RJP_value;
//Result of an rjp search operation typedef struct RJP_array_iterator{
typedef struct RJP_search_res{ RJP_value* current;
RJP_value* value; //pointer to match }RJP_array_iterator;
size_t searchindex; //index in the search list
size_t objindex; //index in the searched object
}RJP_search_res;
//Convert C string consisting of json data into RJP's format typedef struct RJP_parse_callback{
RJP_value* rjp_parse(const char* str); int(*read)(char*,int,void*);
//RJP_value* rjp_parse_chunked(const char* str, RJP_value* prev_chunk); 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;
//Initialize a root RJP_value to NULL /***************** NON OBJECT OPERATIONS *******************/
RJP_value* rjp_init_json(void); void* rjp_alloc(RJP_index nbytes);
//Initialize a root RJP_value to copy of value void* rjp_calloc(RJP_index num, RJP_index nbytes);
RJP_value* rjp_init_json_as(RJP_value value);
//Free an RJP_value and all members/elements
void rjp_free_value(RJP_value* root);
//Deep copy RJP_value
void rjp_copy_value(RJP_value* dest, const RJP_value* src);
//Allocate heap space for rjp to use. Use if the memory is to be freed by rjp
void* rjp_alloc(size_t nbytes);
//Allocate heap space for rjp and initialize to 0.
void* rjp_calloc(size_t num, size_t nbytes);
//Resize heap allocated space for rjp
void* rjp_realloc(void* ptr, size_t nbytes);
//Free memory allocated by rjp_alloc or rjp_calloc
void rjp_free(void* dest); void rjp_free(void* dest);
//add a member to a json object, allocating a copy of the key RJP_index rjp_escape_strcpy(char* dest, const char* src);
RJP_value* rjp_add_member(RJP_value* dest, const char* key, size_t keylen, RJP_value value); RJP_index rjp_escape_strlen(const char* str);
//add a member to a json object without allocation. key must be allocated using rjp_alloc/rjp_calloc RJP_string rjp_escape(const char* src);
RJP_value* rjp_add_member_no_alloc(RJP_value* dest, char* key, size_t keylen, RJP_value value);
//add an element to a json array
RJP_value* rjp_add_element(RJP_value* dest, RJP_value value);
//set existing object member's key /***************** GENERIC OPERATIONS *******************/
void rjp_set_key(RJP_value* dest, const char* key, size_t keylen); RJP_value* rjp_simple_parse(const char* str);
//set existing object member's key without allocation. key must be allocated using rjp_alloc/rjp_calloc RJP_value* rjp_parse(const char* str, int flags, RJP_parse_error* err);
void rjp_set_key_no_alloc(RJP_value* dest, char* key, size_t keylen); RJP_value* rjp_parse_cback(int flags, RJP_parse_callback* cbacks, RJP_parse_error* err);
RJP_value* rjp_parse_c(const char* str, int flags, RJP_parse_error* err, const RJP_memory_fns* fns);
RJP_value* rjp_parse_cback_c(int flags, RJP_parse_callback* cbacks, RJP_parse_error* err, const RJP_memory_fns* fns);
char* rjp_to_json(const RJP_value* root, int pretty);
char* rjp_to_json_c(const RJP_value* root, int pretty, const RJP_memory_fns* fns);
//set existing value char* rjp_parse_error_to_string(const RJP_parse_error* err);
void rjp_set_value(RJP_value* dest, RJP_value value); 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 RJP_value to the requested type and value RJP_value* rjp_new_null(void);
RJP_value rjp_integer(RJP_int i); RJP_value* rjp_new_int(RJP_int val);
RJP_value rjp_boolean(char b); RJP_value* rjp_new_float(RJP_float val);
RJP_value rjp_dfloat(RJP_float d); RJP_value* rjp_new_bool(RJP_bool val);
RJP_value rjp_string(char* c, size_t len); RJP_value* rjp_new_string(const char* val, RJP_index length);
RJP_value rjp_string_copy(const char* c); RJP_value* rjp_new_string_steal(char* val, RJP_index length);
RJP_value rjp_null(void); RJP_value* rjp_new_object(void);
RJP_value rjp_object(void); RJP_value* rjp_new_ordered_object(void);
RJP_value rjp_array(void); RJP_value* rjp_new_array(void);
//Access first member of a json object RJP_value* rjp_new_null_c(const RJP_memory_fns* fns);
RJP_value* rjp_get_member(const RJP_value* object); RJP_value* rjp_new_int_c(RJP_int val, const RJP_memory_fns* fns);
//Access next member of a json object given the previous member RJP_value* rjp_new_float_c(RJP_float val, const RJP_memory_fns* fns);
RJP_value* rjp_next_member(const RJP_value* member); RJP_value* rjp_new_bool_c(RJP_bool val, const RJP_memory_fns* fns);
//Return number of members in the object RJP_value* rjp_new_string_c(const char* val, RJP_index length, const RJP_memory_fns* fns);
size_t rjp_num_members(const RJP_value* object); RJP_value* rjp_new_string_steal_c(char* val, RJP_index length, const RJP_memory_fns* fns);
//Return the object member's key name RJP_value* rjp_new_object_c(const RJP_memory_fns* fns);
char* rjp_member_name(const RJP_value* member); RJP_value* rjp_new_ordered_object_c(const RJP_memory_fns* fns);
//Return the object member's key name length excluding null terminator RJP_value* rjp_new_array_c(const RJP_memory_fns* fns);
size_t rjp_member_name_length(const RJP_value* member);
//Search for an object member with given key void rjp_free_value(RJP_value* root);
RJP_search_res rjp_search_member(const RJP_value* object, const char* search, size_t skip); void rjp_free_value_c(RJP_value* root, const RJP_memory_fns* fns);
//Search for first occurance of multiple keys. Returns first occurance of each.
//Assumes dest is large enough to hold maximum num items. RJP_value* rjp_copy_value(RJP_value* dest, const RJP_value* src);
size_t rjp_search_members(const RJP_value* object, size_t num, const char* const* searches, RJP_search_res* dest, size_t skip); RJP_value* rjp_copy_value_c(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns);
//Search for multiple occurances of multiple keys. Returns pointer to list of matches. RJP_value* rjp_move_value(RJP_value* dest, RJP_value* src);
//Returned pointer must be freed using rjp_free
RJP_search_res* rjp_search_members_multi(const RJP_value* object, size_t num, const char* const* searches, size_t* rsize, size_t skip); RJP_value* rjp_set_null(RJP_value* v);
//Access first element of a json array RJP_value* rjp_set_int(RJP_value* v, RJP_int val);
RJP_value* rjp_get_element(const RJP_value* array); RJP_value* rjp_set_float(RJP_value* v, RJP_float val);
//Access next element of a json array given the previous element RJP_value* rjp_set_bool(RJP_value* v, RJP_bool val);
RJP_value* rjp_next_element(const RJP_value* element); RJP_value* rjp_set_string(RJP_value* v, const char* val, RJP_index length);
//Return number of elements in the array RJP_value* rjp_set_string_steal(RJP_value* v, char* val, RJP_index length);
size_t rjp_num_elements(const RJP_value* array); RJP_value* rjp_set_object(RJP_value* v);
RJP_value* rjp_set_ordered_object(RJP_value* v);
RJP_value* rjp_set_array(RJP_value* v);
RJP_value* rjp_set_null_c(RJP_value* v, const RJP_memory_fns* fns);
RJP_value* rjp_set_int_c(RJP_value* v, RJP_int val, const RJP_memory_fns* fns);
RJP_value* rjp_set_float_c(RJP_value* v, RJP_float val, const RJP_memory_fns* fns);
RJP_value* rjp_set_bool_c(RJP_value* v, RJP_bool val, const RJP_memory_fns* fns);
RJP_value* rjp_set_string_c(RJP_value* v, const char* val, RJP_index length, const RJP_memory_fns* fns);
RJP_value* rjp_set_string_steal_c(RJP_value* v, char* val, RJP_index length, const RJP_memory_fns* fns);
RJP_value* rjp_set_object_c(RJP_value* v, const RJP_memory_fns* fns);
RJP_value* rjp_set_ordered_object_c(RJP_value* v, const RJP_memory_fns* fns);
RJP_value* rjp_set_array_c(RJP_value* v, const RJP_memory_fns* fns);
RJP_float rjp_get_float(const RJP_value* value);
RJP_int rjp_get_int(const RJP_value* value);
RJP_bool rjp_get_bool(const RJP_value* value);
RJP_string* rjp_get_string(RJP_value* value);
const RJP_string* rjp_get_cstring(const RJP_value* value);
//Return handle to parent of given value /***************** 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_type rjp_value_type(const RJP_value* value);
//value accessors /***************** OBJECT OPERATIONS *******************/
RJP_float rjp_value_dfloat(const RJP_value* value); RJP_value* rjp_new_member(RJP_value* dest, const char* key, RJP_index keylen);
RJP_int rjp_value_integer(const RJP_value* value); RJP_value* rjp_new_member_steal_key(RJP_value* dest, char* key, RJP_index keylen);
char rjp_value_boolean(const RJP_value* value); RJP_value* rjp_add_member(RJP_value* dest, const char* key, RJP_index keylen, RJP_value* src);
char* rjp_value_string(RJP_value* value); RJP_value* rjp_add_member_steal_key(RJP_value* dest, char* key, RJP_index keylen, RJP_value* src);
const char* rjp_value_cstring(const RJP_value* value);
size_t rjp_value_string_length(const RJP_value* value);
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);
//copy input string and add json escape sequences RJP_value* rjp_remove_member_by_key(RJP_value* obj, const char* key);
size_t rjp_escape_strcpy(char* dest, const char* src); RJP_value* rjp_remove_member(RJP_value* obj, RJP_value* member);
void rjp_free_member_by_key(RJP_value* obj, const char* key);
void rjp_free_member(RJP_value* obj, RJP_value* member);
RJP_value* rjp_remove_member_by_key_c(RJP_value* obj, const char* key, const RJP_memory_fns* fns);
RJP_value* rjp_remove_member_c(RJP_value* obj, RJP_value* member, const RJP_memory_fns* fns);
void rjp_free_member_by_key_c(RJP_value* obj, const char* key, const RJP_memory_fns* fns);
void rjp_free_member_c(RJP_value* obj, RJP_value* member, const RJP_memory_fns* fns);
//length of string including escape sequences RJP_value* rjp_set_key_steal(RJP_value* dest, char* key, RJP_index keylen);
size_t rjp_escape_strlen(const char* str); RJP_value* rjp_set_key(RJP_value* dest, const char* key, RJP_index keylen);
RJP_value* rjp_set_key_steal_c(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns);
RJP_value* rjp_set_key_c(RJP_value* dest, const char* key, RJP_index keylen, const RJP_memory_fns* fns);
RJP_index rjp_num_members(const RJP_value* object);
const RJP_string* rjp_member_key(const RJP_value* member);
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 *******************/
void rjp_init_object_iterator(RJP_object_iterator* iter, const RJP_value* object);
void rjp_delete_object_iterator(RJP_object_iterator* it);
RJP_value* rjp_object_iterator_current(const RJP_object_iterator* it);
RJP_value* rjp_object_iterator_next(RJP_object_iterator* it);
RJP_value* rjp_object_iterator_peek(const RJP_object_iterator* it);
/***************** ARRAY OPERATIONS *******************/
RJP_value* rjp_new_element(RJP_value* dest);
RJP_value* rjp_add_element(RJP_value* dest, RJP_value* src);
RJP_value* rjp_new_element_c(RJP_value* dest, const RJP_memory_fns* fns);
RJP_value* rjp_add_element_c(RJP_value* dest, RJP_value* src, const RJP_memory_fns* fns);
RJP_value* rjp_remove_element(RJP_value* arr, RJP_value* elem);
void rjp_free_element(RJP_value* arr, RJP_value* elem);
void rjp_free_element_c(RJP_value* arr, RJP_value* elem, const RJP_memory_fns* fns);
RJP_index rjp_num_elements(const RJP_value* arr);
/***************** ARRAY ITERATOR OPERATIONS *******************/
void rjp_init_array_iterator(RJP_array_iterator* iter, const RJP_value* array);
void rjp_delete_array_iterator(RJP_array_iterator* iter);
RJP_value* rjp_array_iterator_current(const RJP_array_iterator* it);
RJP_value* rjp_array_iterator_next(RJP_array_iterator* it);
RJP_value* rjp_array_iterator_peek(const RJP_array_iterator* it);
//Convert RJP_object to json string
size_t rjp_dump_object(const RJP_value* root, char* dest);
//Convert RJP_array to json string
size_t rjp_dump_array(const RJP_value* arr, char* dest);
//Convert root rjp value into json string
char* rjp_to_json(const RJP_value* root);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#undef DEPRECATED
#endif #endif

38
include/rjp_array.h Normal file
View File

@ -0,0 +1,38 @@
/**
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_ARRAY_H
#define RJP_ARRAY_H
#include "rjp.h"
struct RJP_array_element;
//Represents a json array
typedef struct RJP_array{
struct RJP_array_element* elements; //linked list of elements
struct RJP_array_element* last; //final member of linked list
RJP_index num_elements;
}RJP_array;
void irjp_add_element(RJP_array* j, const RJP_memory_fns* fns);
void irjp_copy_array(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns);
RJP_index irjp_dump_array(const RJP_value* arr, char* dest, const int flags, int depth);
#endif

View File

@ -1,6 +1,6 @@
/** /**
rjp rjp
Copyright (C) 2018-2019 rexy712 Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -16,17 +16,16 @@
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 STRINGS_H #ifndef RJP_ARRAY_ELEMENT_H
#define STRINGS_H #define RJP_ARRAY_ELEMENT_H
#include "rjp.h" #include "rjp_internal.h"
#include <stddef.h> //size_t #include "rjp_value.h"
int _rjp__is_whitespace(char c); typedef struct RJP_array_element{
char* _rjp__parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column); struct RJP_value value;
size_t _rjp__array_strlen(const RJP_value* arr); struct RJP_array_element* next;
size_t _rjp__object_strlen(const RJP_value* root); struct RJP_array_element* prev;
size_t _rjp__value_strlen(const RJP_value* root); }RJP_array_element;
void _rjp__strcpy(RJP_string* dest, const RJP_string* src);
#endif #endif

View File

@ -1,6 +1,6 @@
/** /**
rjp rjp
Copyright (C) 2018-2019 rexy712 Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -20,6 +20,8 @@
#define RJP_INTERNAL_H #define RJP_INTERNAL_H
#include "rjp.h" #include "rjp.h"
#include "config.h"
#include <stdio.h>
#ifdef __GNUC__ #ifdef __GNUC__
#define MAYBE_UNUSED __attribute__((unused)) #define MAYBE_UNUSED __attribute__((unused))
@ -27,27 +29,18 @@
#define MAYBE_UNUSED #define MAYBE_UNUSED
#endif #endif
#ifdef RJP_DIAGNOSTICS #define UNUSED_VARIABLE(thing) (void)(thing)
#include <stdio.h>
#ifdef RJP_ENABLE_DIAGNOSTICS
#define DIAG_PRINT(...) fprintf(__VA_ARGS__) #define DIAG_PRINT(...) fprintf(__VA_ARGS__)
#else #else
#define DIAG_PRINT(...) #define DIAG_PRINT(...) irjp_ignore_unused(__VA_ARGS__)
static inline void irjp_ignore_unused(FILE* fp, ...){
UNUSED_VARIABLE(fp);
}
#endif #endif
// extern const RJP_memory_fns irjp_default_memory_fns;
typedef struct RJP_array_element{
struct RJP_value value;
struct RJP_array_element* next;
}RJP_array_element;
//A member of an object
typedef struct RJP_object_member{
struct RJP_value value;
struct RJP_string name;
struct RJP_object_member* next;
}RJP_object_member;
void _rjp__add_element(RJP_array* j);
void _rjp__add_member(RJP_object* j, const char* str, size_t len);
void _rjp__add_member_no_alloc(RJP_object* j, char* str, size_t len);
#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

31
include/rjp_object.h Normal file
View File

@ -0,0 +1,31 @@
/**
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_OBJECT_H
#define RJP_OBJECT_H
#include "rjp_unordered_object.h"
#include "rjp_ordered_object.h"
#include "rjp_internal.h"
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);
RJP_index irjp_dump_object(const RJP_value* root, char* dest, const int flags, int depth);
#endif

View File

@ -0,0 +1,30 @@
/**
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_OBJECT_MEMBER_H
#define RJP_OBJECT_MEMBER_H
#include "rjp_internal.h"
#include "rjp_value.h"
typedef struct RJP_object_member{
RJP_value value;
RJP_string name;
}RJP_object_member;
#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

35
include/rjp_string.h Normal file
View File

@ -0,0 +1,35 @@
/**
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_STRINGS_H
#define RJP_STRINGS_H
#include "rjp.h"
#include "rjp_internal.h"
int irjp_is_whitespace(char c);
int irjp_copy_string_quoted(char* restrict dest, const char* restrict string, RJP_index length);
int irjp_copy_string_keyed(char* restrict dest, const char* restrict string, RJP_index length);
char* irjp_convert_string(const char* str, RJP_index length, RJP_index* newlen, const RJP_memory_fns* fns);
RJP_index irjp_array_strlen(const RJP_value* arr, const int flags, int depth);
RJP_index irjp_object_strlen(const RJP_value* root, const int flags, int depth);
RJP_index irjp_value_strlen(const RJP_value* root, const int flags, int depth);
void irjp_strcpy(RJP_string* dest, const RJP_string* src, const RJP_memory_fns* fns);
#endif

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

45
include/rjp_value.h Normal file
View File

@ -0,0 +1,45 @@
/**
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_VALUE_H
#define RJP_VALUE_H
#include "rjp_internal.h"
#include "rjp_object.h"
#include "rjp_array.h"
//Represents json data
//hold any json data typetypetypetype
typedef struct RJP_value{
union{
RJP_int integer;
RJP_float dfloat;
RJP_bool boolean;
RJP_ordered_object orobject;
RJP_unordered_object object;
RJP_string string;
RJP_array array;
};
struct RJP_value* parent; //pointer to parent (either an array or object or NULL)
enum RJP_data_type type; //flag to determine active member of union
}RJP_value;
void irjp_delete_value(RJP_value* root, const RJP_memory_fns* fns);
#endif

48
include/tree.h Normal file
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_TREE_H
#define RJP_TREE_H
#include "rjp.h"
#include "rjp_object_member.h"
#include "rjp_value.h"
#define RJP_TREE_SUCCESS 0
#define RJP_TREE_ERR_NULL_ROOT 1
#define RJP_TREE_ERR_NOT_FOUND 2
typedef struct RJP_object_iterator RJP_object_iterator;
typedef struct RJP_tree_node RJP_tree_node;
struct RJP_tree_node{
RJP_object_member data;
RJP_tree_node* parent;
RJP_tree_node* left;
RJP_tree_node* right;
unsigned color:1;
};
RJP_tree_node* irjp_new_node(char* key, RJP_index keylen, const RJP_memory_fns* fns);
RJP_tree_node* irjp_tree_insert_value(RJP_tree_node* root, char* key, RJP_index keylen, RJP_value** added, int* status, const RJP_memory_fns* fns);
RJP_tree_node* irjp_tree_insert_node(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode);
RJP_tree_node* irjp_tree_remove_value(RJP_tree_node* restrict root, RJP_tree_node* restrict member, RJP_tree_node** removed_node);
RJP_tree_node* irjp_tree_search_value(RJP_tree_node* root, const char* key);
RJP_tree_node* irjp_copy_tree(const RJP_tree_node* root, const RJP_memory_fns* fns);
void irjp_free_tree(RJP_tree_node* root, const RJP_memory_fns* fns);
#endif

9
pc/rjp.pc.cmake.in Normal file
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,375 +0,0 @@
/**
rjp
Copyright (C) 2018-2019 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 "strings.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 json_search_target{
json_target_key,
json_target_colon,
json_target_comma,
json_target_value,
json_target_string,
json_target_numeral,
json_target_none
}json_search_target;
static RJP_value* _rjp__add_value(RJP_value* curr, 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 == json_array){
_rjp__add_element(&curr->array);
curr->array.last->value = new_val;
return &curr->array.last->value;
}
curr->object.last->value = new_val;
return &curr->object.last->value;
}
#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;
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 _rjp__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 _rjp__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 _rjp__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 = _rjp__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;
}
_rjp__add_member_no_alloc(&(state->curr->object), new_string, keylen);
*state->target = 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(!_rjp__is_whitespace(c)){
syntax_error("Unexpected character, expected '\"'!", state);
return -1;
}
return 1;
}
int _rjp__handle_colon(const char* str, RJP_parse_state* state){
char c = *str;
//colon after a key
if(c == ':'){
*state->target = json_target_value;
//unrecognized character
}else if(!_rjp__is_whitespace(c)){
syntax_error( "Unexpected character, expected ':'!", state);
return -1;
}
return 1;
}
int _rjp__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 == json_array ? json_target_value : json_target_key);
//end of object
}else if(c == '}'){
if(state->curr->type == 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 == json_array){
state->curr = state->curr->parent;
//unrecognized character
}else if(!_rjp__is_whitespace(c)){
syntax_error("Unexpected character, expected ','!", state);
return -1;
}
return 1;
}
int _rjp__handle_value(const char* str, RJP_parse_state* state){
//object
char c = *str;
if(c == '{'){
if(!state->root){
state->root = _rjp__add_value(NULL, rjp_object());
state->curr = state->root;
*state->target = json_target_key;
}else{
state->curr = _rjp__add_value(state->curr, rjp_object());
*state->target = json_target_comma;
++state->target;
*state->target = json_target_key;
}
return 1;
}
else if(c == '['){
if(!state->root){
state->root = _rjp__add_value(NULL, rjp_array());
state->curr = state->root;
}else{
state->curr = _rjp__add_value(state->curr, rjp_array());
}
return 1;
}
else if(c == ']' && state->curr->type == json_array){ //empty array
*state->target = json_target_comma;
state->curr = state->curr->parent;
return 1;
}
//strings
else if(c == '"'){
int vallen, inclen;
char* new_string = _rjp__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;
}
}
_rjp__add_value(state->curr, rjp_string(new_string, vallen));
*state->target = json_target_comma;
return inclen+2;
}
//numbers
else if((c >= '0' && c <= '9') || c == '-'){
if(!state->curr)
*state->target = json_target_none;
else
*state->target = 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 = _rjp__add_value(NULL, rjp_dfloat(strtod(str, NULL)));
}else{
_rjp__add_value(state->curr, rjp_dfloat(strtod(str, NULL)));
}
}else{
if(!state->root){
state->root = state->curr = _rjp__add_value(NULL, rjp_integer(strtoll(str, NULL, 10)));
}else{
_rjp__add_value(state->curr, rjp_integer(strtoll(str, NULL, 10)));
}
}
state->column += numlen;
return numlen;
}
//booleans and null
else if(!strncmp(str, "true", 4)){
if(!state->curr){
*state->target = json_target_none;
state->root = state->curr = _rjp__add_value(state->curr, rjp_boolean(1));
}else{
*state->target = json_target_comma;
_rjp__add_value(state->curr, rjp_boolean(1));
}
state->column += 3;
return 4;
}else if(!strncmp(str, "false", 5)){
if(!state->curr){
*state->target = json_target_none;
state->root = state->curr = _rjp__add_value(state->curr, rjp_boolean(0));
}else{
*state->target = json_target_comma;
_rjp__add_value(state->curr, rjp_boolean(0));
}
state->column += 4;
return 5;
}else if(!strncmp(str, "null", 4)){
if(!state->curr){
*state->target = json_target_none;
state->root = state->curr = _rjp__add_value(state->curr, rjp_null());
}else{
*state->target = json_target_comma;
_rjp__add_value(state->curr, rjp_null());
}
state->column += 3;
return 4;
}
//unrecognized character
else if(!_rjp__is_whitespace(c)){
syntax_error("Unexpected character!", state);
return -1;
}
return 1;
}
RJP_value* rjp_parse(const char* str){
RJP_parse_state state;
_rjp__init_parse_state(&state);
//initially search for the root object
*state.target = 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 = _rjp__handle_comment(str, &state))){
continue;
}
switch(*state.target){
case json_target_key:
inc = _rjp__handle_key(str, &state);
break;
case json_target_colon:
inc = _rjp__handle_colon(str, &state);
break;
case json_target_comma:
inc = _rjp__handle_comma(str, &state);
break;
case json_target_value:
inc = _rjp__handle_value(str, &state);
break;
case json_target_none:
if(!_rjp__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

@ -1,124 +0,0 @@
/**
rjp
Copyright (C) 2018-2019 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 <stdlib.h>
#include <string.h>
void* rjp_alloc(size_t nbytes){
return malloc(nbytes);
}
void* rjp_calloc(size_t num, size_t nbytes){
return calloc(num, nbytes);
}
void* rjp_realloc(void* ptr, size_t nbytes){
return realloc(ptr, nbytes);
}
void rjp_free(void* data){
free(data);
}
static void _rjp__copy_object(RJP_value* dest, const RJP_value* src){
dest->object.members = dest->object.last = 0;
for(RJP_value* curr = rjp_get_member(src);curr;curr = rjp_next_member(curr)){
RJP_value copy_mem;
rjp_copy_value(&copy_mem, curr);
rjp_add_member(dest, rjp_member_name(curr), rjp_member_name_length(curr), copy_mem);
}
}
static void _rjp__copy_array(RJP_value* dest, const RJP_value* src){
dest->array.elements = dest->array.last = 0;
for(RJP_value* curr = rjp_get_element(src);curr;curr = rjp_next_element(curr)){
RJP_value copy_mem;
rjp_copy_value(&copy_mem, curr);
rjp_add_element(dest, copy_mem);
}
}
void rjp_copy_value(RJP_value* dest, const RJP_value* src){
dest->type = src->type;
switch(src->type){
case json_object:
_rjp__copy_object(dest, src);
break;
case json_array:
_rjp__copy_array(dest, src);
break;
case json_string:
_rjp__strcpy(&dest->string, &src->string);
break;
case json_integer:
dest->integer = src->integer;
break;
case json_boolean:
dest->boolean = src->boolean;
break;
case json_dfloat:
dest->dfloat = src->dfloat;
break;
default:
break;
};
}
static void _rjp__free_object_recurse(RJP_value* root);
static void _rjp__free_array(RJP_value* root){
RJP_array_element* arr = root->array.elements;
for(RJP_array_element* i = arr;i != NULL;i = arr){
arr = arr->next;
if(i->value.type == json_object){
_rjp__free_object_recurse(&i->value);
}else if(i->value.type == json_array){
_rjp__free_array(&i->value);
}else if(i->value.type == json_string){
free(i->value.string.value);
}
free(i);
}
}
//Recursively free JSON objects
static void _rjp__free_object_recurse(RJP_value* root){
RJP_object_member* next;
for(RJP_object_member* m = root->object.members;m;m = next){
next = m->next;
if(m->value.type == json_object)
_rjp__free_object_recurse(&m->value);
else if(m->value.type == json_string)
free(m->value.string.value);
else if(m->value.type == json_array)
_rjp__free_array(&m->value);
if(m->name.value)
free(m->name.value);
free(m);
}
}
//Same as recurse but also frees root node
void rjp_free_value(RJP_value* root){
if(!root)
return;
if((root->type) == json_object)
_rjp__free_object_recurse(root);
else if((root->type) == json_array)
_rjp__free_array(root);
free(root);
}

View File

@ -1,6 +1,6 @@
/** /**
rjp rjp
Copyright (C) 2018-2019 rexy712 Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -16,49 +16,236 @@
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_object.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_init_json(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;
return ret; return ret;
} }
RJP_value* rjp_init_json_as(RJP_value value){ RJP_value* rjp_new_int_c(RJP_int val, const RJP_memory_fns* fns){
RJP_value* ret = rjp_alloc(sizeof(RJP_value)); RJP_value* ret = fns->alloc(sizeof(RJP_value));
*ret = value; memset(ret, 0, sizeof(RJP_value));
ret->type = rjp_json_integer;
ret->integer = val;
return ret;
}
RJP_value* rjp_new_float_c(RJP_float val, const RJP_memory_fns* fns){
RJP_value* ret = fns->alloc(sizeof(RJP_value));
memset(ret, 0, sizeof(RJP_value));
ret->type = rjp_json_dfloat;
ret->dfloat = val;
return ret;
}
RJP_value* rjp_new_bool_c(RJP_bool val, const RJP_memory_fns* fns){
RJP_value* ret = fns->alloc(sizeof(RJP_value));
memset(ret, 0, sizeof(RJP_value));
ret->type = rjp_json_boolean;
ret->boolean = val;
return ret;
}
RJP_value* rjp_new_string_steal_c(char* val, RJP_index length, const RJP_memory_fns* fns){
RJP_value* ret = fns->alloc(sizeof(RJP_value));
memset(ret, 0, sizeof(RJP_value));
ret->type = rjp_json_string;
ret->string.value = val;
ret->string.length = length;
return ret;
}
RJP_value* rjp_new_string_c(const char* value, RJP_index length, const RJP_memory_fns* fns){
UNUSED_VARIABLE(length);
RJP_value* ret = fns->alloc(sizeof(RJP_value));
memset(ret, 0, sizeof(RJP_value));
ret->type = rjp_json_string;
RJP_index esclen = rjp_escape_strlen(value);
ret->string.value = fns->alloc(esclen+1);
rjp_escape_strcpy(ret->string.value, value);
ret->string.value[esclen] = 0;
ret->string.length = esclen;
return ret;
}
RJP_value* rjp_new_object_c(const RJP_memory_fns* fns){
RJP_value* ret = fns->alloc(sizeof(RJP_value));
memset(ret, 0, sizeof(RJP_value));
ret->type = rjp_json_object;
return ret;
}
RJP_value* rjp_new_ordered_object_c(const RJP_memory_fns* fns){
RJP_value* ret = fns->alloc(sizeof(RJP_value));
memset(ret, 0, sizeof(RJP_value));
ret->type = rjp_json_ordered_object;
return ret;
}
RJP_value* rjp_new_array_c(const RJP_memory_fns* fns){
RJP_value* ret = fns->alloc(sizeof(RJP_value));
memset(ret, 0, sizeof(RJP_value));
ret->type = rjp_json_array;
return ret; return ret;
} }
static size_t _rjp__write_value(char* dest, const RJP_value* val){ RJP_value* rjp_set_null(RJP_value* v){
size_t ret; return rjp_set_null_c(v, &irjp_default_memory_fns);
}
RJP_value* rjp_set_int(RJP_value* v, RJP_int val){
return rjp_set_int_c(v, val, &irjp_default_memory_fns);
}
RJP_value* rjp_set_float(RJP_value* v, RJP_float val){
return rjp_set_float_c(v, val, &irjp_default_memory_fns);
}
RJP_value* rjp_set_bool(RJP_value* v, RJP_bool val){
return rjp_set_bool_c(v, val, &irjp_default_memory_fns);
}
RJP_value* rjp_set_string_steal(RJP_value* v, char* val, RJP_index len){
return rjp_set_string_steal_c(v, val, len, &irjp_default_memory_fns);
}
RJP_value* rjp_set_string(RJP_value* v, const char* val, RJP_index length){
return rjp_set_string_c(v, val, length, &irjp_default_memory_fns);
}
RJP_value* rjp_set_object(RJP_value* v){
return rjp_set_object_c(v, &irjp_default_memory_fns);
}
RJP_value* rjp_set_ordered_object(RJP_value* v){
return rjp_set_ordered_object_c(v, &irjp_default_memory_fns);
}
RJP_value* rjp_set_array(RJP_value* v){
return rjp_set_array_c(v, &irjp_default_memory_fns);
}
RJP_value* rjp_set_null_c(RJP_value* v, const RJP_memory_fns* fns){
irjp_delete_value(v, fns);
v->type = rjp_json_null;
return v;
}
RJP_value* rjp_set_int_c(RJP_value* v, RJP_int val, const RJP_memory_fns* fns){
irjp_delete_value(v, fns);
v->type = rjp_json_integer;
v->integer = val;
return v;
}
RJP_value* rjp_set_float_c(RJP_value* v, RJP_float val, const RJP_memory_fns* fns){
irjp_delete_value(v, fns);
v->type = rjp_json_dfloat;
v->dfloat = val;
return v;
}
RJP_value* rjp_set_bool_c(RJP_value* v, RJP_bool val, const RJP_memory_fns* fns){
irjp_delete_value(v, fns);
v->type = rjp_json_boolean;
v->boolean = val;
return v;
}
RJP_value* rjp_set_string_steal_c(RJP_value* v, char* val, RJP_index len, const RJP_memory_fns* fns){
irjp_delete_value(v, fns);
v->type = rjp_json_string;
v->string.value = val;
v->string.length = len;
return v;
}
RJP_value* rjp_set_string_c(RJP_value* v, const char* val, RJP_index length, const RJP_memory_fns* fns){
UNUSED_VARIABLE(length);
irjp_delete_value(v, fns);
v->type = rjp_json_string;
RJP_index esclen = rjp_escape_strlen(val);
v->string.value = fns->alloc(esclen+1);
rjp_escape_strcpy(v->string.value, val);
v->string.value[esclen] = 0;
v->string.length = esclen;
return v;
}
RJP_value* rjp_set_object_c(RJP_value* v, const RJP_memory_fns* fns){
if(v->type == rjp_json_object)
return v;
irjp_delete_value(v, fns);
memset(&v->object, 0, sizeof(RJP_unordered_object));
v->type = rjp_json_object;
return v;
}
RJP_value* rjp_set_ordered_object_c(RJP_value* v, const RJP_memory_fns* fns){
if(v->type == rjp_json_ordered_object)
return v;
irjp_delete_value(v, fns);
memset(&v->orobject, 0, sizeof(RJP_ordered_object));
v->type = rjp_json_ordered_object;
return v;
}
RJP_value* rjp_set_array_c(RJP_value* v, const RJP_memory_fns* fns){
if(v->type == rjp_json_array)
return v;
irjp_delete_value(v, fns);
memset(&v->array, 0, sizeof(RJP_array));
v->type = rjp_json_array;
return v;
}
static RJP_index irjp_write_value(char* dest, const RJP_value* val, const int flags, int depth){
RJP_index ret;
switch(val->type){ switch(val->type){
case json_integer: case rjp_json_integer:
ret = sprintf(dest, "%" PRId64, val->integer); ret = sprintf(dest, "%" PRId64, val->integer);
break; break;
case json_dfloat: case rjp_json_dfloat:
ret = sprintf(dest, "%lf", val->dfloat); ret = sprintf(dest, "%lf", val->dfloat);
break; break;
case 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 json_null: case rjp_json_null:
ret = sprintf(dest, "null"); memcpy(dest, "null", 4);
ret = 4;
break; break;
case 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 json_array: case rjp_json_array:
ret = rjp_dump_array(val, dest); ret = irjp_dump_array(val, dest, flags, depth);
break; break;
case json_object: case rjp_json_object:
ret = rjp_dump_object(val, dest); case rjp_json_ordered_object:
ret = irjp_dump_object(val, dest, flags, depth);
break; break;
default: default:
ret = 0; ret = 0;
@ -67,52 +254,106 @@ static size_t _rjp__write_value(char* dest, const RJP_value* val){
return ret; return ret;
} }
size_t 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){
const RJP_array* array = &arr->array; RJP_index pos = 0;
const RJP_array_element* element_list = array->elements; RJP_array_iterator it;
size_t pos = 1; rjp_init_array_iterator(&it, arr);
sprintf(dest, "["); RJP_value* current = rjp_array_iterator_current(&it);
for(;element_list;element_list = element_list->next){
pos += _rjp__write_value(dest+pos, &element_list->value);
if(element_list->next) 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;
} }
size_t 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){
const RJP_object* root_obj = &root->object; RJP_object_iterator it;
const RJP_object_member* member_list = root_obj->members; rjp_init_object_iterator(&it, root);
size_t pos = 1; RJP_index pos = 0;
sprintf(dest, "{"); RJP_value* current = rjp_object_iterator_current(&it);
if(!current){
for(;member_list;member_list = member_list->next){ rjp_delete_object_iterator(&it);
pos += sprintf(dest+pos, "\"%s\":", member_list->name.value); dest[pos++] = '{';
pos += _rjp__write_value(dest+pos, &member_list->value); dest[pos++] = '}';
dest[pos] = 0;
if(member_list->next) return pos;
pos += sprintf(dest+pos, ",");
else
break;
} }
pos += sprintf(dest+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){ 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;
size_t len = _rjp__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;
_rjp__write_value(tmp, root); irjp_write_value(tmp, root, flags, 0);
return tmp; return tmp;
} }

328
src/rjp.c
View File

@ -1,6 +1,6 @@
/** /**
rjp rjp
Copyright (C) 2018-2019 rexy712 Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -17,249 +17,127 @@
*/ */
#include "rjp_internal.h" #include "rjp_internal.h"
#include "rjp_string.h"
#include "rjp_object.h"
#include "rjp_array.h"
#include "rjp_value.h"
#include <string.h> //strncpy, strlen #include <stdlib.h> //free, malloc, calloc
void _rjp__add_element(RJP_array* j){ void* rjp_alloc(RJP_index nbytes){
++j->num_elements; return malloc(nbytes);
if(!j->elements){ }
j->elements = rjp_calloc(1, sizeof(RJP_array_element)); void* rjp_calloc(RJP_index num, RJP_index nbytes){
j->last = j->elements; return calloc(num, nbytes);
}
void rjp_free(void* data){
free(data);
}
const RJP_memory_fns irjp_default_memory_fns = {rjp_free, rjp_alloc};
RJP_value* rjp_copy_value(RJP_value* dest, const RJP_value* src){
return rjp_copy_value_c(dest, src, &irjp_default_memory_fns);
}
RJP_value* rjp_copy_value_c(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns){
RJP_value* ret;
if(dest){
irjp_delete_value(dest, fns);
ret = dest;
}else{ }else{
j->last->next = rjp_calloc(1, sizeof(RJP_array_element)); ret = rjp_new_null_c(fns);
j->last = j->last->next;
} }
} ret->type = src->type;
//create member of the object as a linked list member and assign a name with name allocation
void _rjp__add_member(RJP_object* j, const char* str, size_t len){
++j->num_members;
if(!j->members){
j->members = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->members;
}else{
j->last->next = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->last->next;
}
j->last->name.value = rjp_alloc(len + 1);
strncpy(j->last->name.value, str, len);
j->last->name.value[len] = 0;
j->last->name.length = len;
}
void _rjp__add_member_no_alloc(RJP_object* j, char* str, size_t len){
++j->num_members;
if(!j->members){
j->members = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->members;
}else{
j->last->next = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->last->next;
}
j->last->name.value = str;
j->last->name.length = len;
}
RJP_value* rjp_add_member(RJP_value* dest, const char* key, size_t keylen, RJP_value value){ switch(src->type){
if(!keylen) case rjp_json_object:
keylen = strlen(key); irjp_copy_object(ret, src, fns);
_rjp__add_member(&dest->object, key, keylen); break;
dest->object.last->value = value; case rjp_json_ordered_object:
dest->object.last->value.parent = dest; irjp_copy_ordered_object(ret, src, fns);
return &dest->object.last->value; break;
} case rjp_json_array:
RJP_value* rjp_add_member_no_alloc(RJP_value* dest, char* key, size_t keylen, RJP_value value){ irjp_copy_array(ret, src, fns);
if(!keylen) break;
keylen = strlen(key); case rjp_json_string:
_rjp__add_member_no_alloc(&dest->object, key, keylen); irjp_strcpy(&ret->string, &src->string, fns);
dest->object.last->value = value; break;
dest->object.last->value.parent = dest; case rjp_json_integer:
return &dest->object.last->value; ret->integer = src->integer;
} break;
RJP_value* rjp_add_element(RJP_value* dest, RJP_value value){ case rjp_json_boolean:
_rjp__add_element(&dest->array); ret->boolean = src->boolean;
dest->array.last->value = value; break;
dest->array.last->value.parent = dest; case rjp_json_dfloat:
return &dest->array.last->value; ret->dfloat = src->dfloat;
} break;
void rjp_set_key(RJP_value* dest, const char* key, size_t keylen){ default:
RJP_object_member* mem = (RJP_object_member*)dest; break;
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_no_alloc(RJP_value* dest, char* key, size_t keylen){
RJP_object_member* mem = (RJP_object_member*)dest;
if(key){
if(!keylen){
keylen = strlen(key);
}
}else{
keylen = 0;
}
mem->name.value = key;
mem->name.length = keylen;
}
void rjp_set_value(RJP_value* dest, RJP_value value){
struct RJP_value* p = dest->parent;
*dest = value;
dest->parent = p;
}
RJP_value rjp_integer(RJP_int i){
return (RJP_value){.integer = i, .type = json_integer};
}
RJP_value rjp_boolean(char b){
return (RJP_value){.boolean = b, .type = json_boolean};
}
RJP_value rjp_dfloat(double d){
return (RJP_value){.dfloat = d, .type = json_dfloat};
}
RJP_value rjp_string(char* c, size_t len){
return (RJP_value){.string = {.value = c, .length = len}, .type = json_string};
}
RJP_value rjp_string_copy(const char* c){
size_t 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 = json_string};
}
RJP_value rjp_null(void){
return (RJP_value){.integer = 0, .type = json_null};
}
RJP_value rjp_object(void){
return (RJP_value){.object = {0}, .type = json_object};
}
RJP_value rjp_array(void){
return (RJP_value){.array = {0}, .type = json_array};
}
RJP_value* rjp_get_member(const RJP_value* object){
return &object->object.members->value;
}
RJP_value* rjp_next_member(const RJP_value* member){
//polymorphism
return (RJP_value*)(((RJP_object_member*)member)->next);
}
size_t rjp_num_members(const RJP_value* object){
return object->object.num_members;
}
char* rjp_member_name(const RJP_value* member){
return ((RJP_object_member*)member)->name.value;
}
size_t rjp_member_name_length(const RJP_value* member){
return ((RJP_object_member*)member)->name.length;
}
RJP_search_res rjp_search_member(const RJP_value* object, const char* search, size_t skip){
RJP_value* member = rjp_get_member(object);
size_t i = 0;
for(;i < skip && member;member = rjp_next_member(member),++i);
for(;member;member = rjp_next_member(member),++i){
if(!strcmp(((RJP_object_member*)member)->name.value, search)){
return (RJP_search_res){member, 0, i};
}
}
return (RJP_search_res){0};
}
size_t rjp_search_members(const RJP_value* object, size_t num, const char* const* searches, RJP_search_res* dest, size_t skip){
for(size_t i = 0;i < num;++i){
dest[i] = (RJP_search_res){0};
}
RJP_value* member = rjp_get_member(object);
size_t objindex = 0;
for(;objindex < skip && member;member = rjp_next_member(member),++objindex);
size_t matches = 0;
for(;member;member = rjp_next_member(member),++objindex){
for(size_t i = 0;i < num;++i){
if(dest[i].value != NULL)
continue;
if(!strcmp(((RJP_object_member*)member)->name.value, searches[i])){
dest[i].value = member;
dest[i].searchindex = i;
dest[i].objindex = objindex;
++matches;
break;
}
}
}
return matches;
}
RJP_search_res* rjp_search_members_multi(const RJP_value* object, size_t num, const char* const* searches, size_t* rsize, size_t skip){
RJP_search_res* ret = rjp_alloc(num*sizeof(RJP_search_res));
size_t ret_size = num;
size_t j = 0;
RJP_value* member = rjp_get_member(object);
size_t objindex = 0;
for(;objindex < skip && member;member = rjp_next_member(member),++objindex);
for(;member;member = rjp_next_member(member),++objindex){
for(size_t i = 0;i < num;++i){
if(!strcmp(((RJP_object_member*)member)->name.value, searches[i])){
ret[j].value = member;
ret[j].searchindex = i;
ret[j].objindex = objindex;
++j;
if(j == ret_size){
ret_size *= 2;
ret = rjp_realloc(ret, ret_size*sizeof(RJP_search_res));
}
break;
}
}
}
if(rsize)
*rsize = j;
if(j == 0){
rjp_free(ret);
return NULL;
}
return ret; return ret;
} }
RJP_value* rjp_move_value(RJP_value* dest, RJP_value* src){
RJP_value* rjp_get_element(const RJP_value* array){ *dest = *src;
return &array->array.elements->value; src->type = rjp_json_null;
} return dest;
RJP_value* rjp_next_element(const RJP_value* element){
return (RJP_value*)(((RJP_array_element*)element)->next);
}
size_t rjp_num_elements(const RJP_value* array){
return array->array.num_elements;
} }
static void irjp_delete_array(RJP_value* root, const RJP_memory_fns* fns){
RJP_array_iterator it;
rjp_init_array_iterator(&it, root);
RJP_value* next = rjp_array_iterator_current(&it);
for(RJP_value* current = next;current;current = next){
next = rjp_array_iterator_next(&it);
rjp_free_value_c(current, fns);
}
}
void irjp_delete_value(RJP_value* root, const RJP_memory_fns* fns){
switch(root->type){
case rjp_json_object:
case rjp_json_ordered_object:
irjp_delete_object(root, fns);
break;
case rjp_json_array:
irjp_delete_array(root, fns);
break;
case rjp_json_string:
fns->free(root->string.value);
break;
default: break;
};
}
void rjp_free_value(RJP_value* root){
rjp_free_value_c(root, &irjp_default_memory_fns);
}
void rjp_free_value_c(RJP_value* root, const RJP_memory_fns* fns){
if(!root)
return;
irjp_delete_value(root, fns);
fns->free(root);
}
/* VALUE SETTING */
/* GETTERS */
RJP_value* rjp_value_parent(const RJP_value* value){ RJP_value* rjp_value_parent(const RJP_value* value){
return value->parent; return value->parent;
} }
RJP_type rjp_value_type(const RJP_value* value){ RJP_data_type rjp_value_type(const RJP_value* value){
return value->type; return value->type;
} }
RJP_float rjp_value_dfloat(const RJP_value* value){ RJP_float rjp_get_float(const RJP_value* value){
return value->dfloat; return value->dfloat;
} }
RJP_int rjp_value_integer(const RJP_value* value){ RJP_int rjp_get_int(const RJP_value* value){
return value->integer; return value->integer;
} }
char rjp_value_boolean(const RJP_value* value){ RJP_bool rjp_get_bool(const RJP_value* value){
return value->boolean; return value->boolean;
} }
char* rjp_value_string(RJP_value* value){ RJP_string* rjp_get_string(RJP_value* value){
return value->string.value; return &(value->string);
} }
const char* rjp_value_cstring(const RJP_value* value){ const RJP_string* rjp_get_cstring(const RJP_value* value){
return value->string.value; return &(value->string);
} }
size_t rjp_value_string_length(const RJP_value* value){
return value->string.length;
}

115
src/rjp_array.c Normal file
View File

@ -0,0 +1,115 @@
/**
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_array.h"
#include "rjp_value.h"
#include "rjp_array_element.h"
#include <string.h> //memset
void irjp_copy_array(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns){
dest->array.elements = dest->array.last = NULL;
for(RJP_array_element* curr = src->array.elements;curr;curr = curr->next){
RJP_value* copy_mem;
copy_mem = rjp_new_element_c(dest, fns);
rjp_copy_value_c(copy_mem, &curr->value, fns);
}
}
void irjp_add_element(RJP_array* j, const RJP_memory_fns* fns){
++j->num_elements;
if(!j->elements){
j->elements = fns->alloc(sizeof(RJP_array_element));
memset(j->elements, 0, sizeof(RJP_array_element));
j->last = j->elements;
}else{
j->last->next = fns->alloc(sizeof(RJP_array_element));
memset(j->last->next, 0, sizeof(RJP_array_element));
j->last = j->last->next;
j->last->prev = j->last;
}
}
RJP_value* rjp_new_element(RJP_value* dest){
return rjp_new_element_c(dest, &irjp_default_memory_fns);
}
RJP_value* rjp_new_element_c(RJP_value* dest, const RJP_memory_fns* fns){
irjp_add_element(&dest->array, fns);
dest->array.last->value.parent = dest;
return &dest->array.last->value;
}
RJP_value* rjp_add_element(RJP_value* dest, RJP_value* src){
return rjp_add_element_c(dest, src, &irjp_default_memory_fns);
}
RJP_value* rjp_add_element_c(RJP_value* dest, RJP_value* src, const RJP_memory_fns* fns){
RJP_value* newelm = rjp_new_element_c(dest, fns);
if(!newelm)
return NULL;
rjp_move_value(newelm, src);
fns->free(src);
return newelm;
}
RJP_value* rjp_remove_element(RJP_value* dest, RJP_value* value){
if(value->parent != dest)
return NULL;
RJP_array* j = &dest->array;
--j->num_elements;
RJP_array_element* elem = (RJP_array_element*)value;
RJP_array_element* prev = elem->prev;
RJP_array_element* next = elem->next;
if(prev)
prev->next = elem->next;
if(next)
next->prev = prev;
if(elem == j->elements)
j->elements = next;
if(elem == j->last)
j->last = prev;
return value;
}
void rjp_free_element(RJP_value* dest, RJP_value* value){
rjp_free_element_c(dest, value, &irjp_default_memory_fns);
}
void rjp_free_element_c(RJP_value* dest, RJP_value* value, const RJP_memory_fns* fns){
rjp_remove_element(dest, value);
rjp_free_value_c(value, fns);
}
RJP_index rjp_num_elements(const RJP_value* array){
return array->array.num_elements;
}
void rjp_init_array_iterator(RJP_array_iterator* iter, const RJP_value* array){
iter->current = (RJP_value*)(array->array.elements);
}
void rjp_delete_array_iterator(RJP_array_iterator* it){
if(!it)
return;
it->current = NULL;
}
RJP_value* rjp_array_iterator_current(const RJP_array_iterator* it){
return it->current;
}
RJP_value* rjp_array_iterator_next(RJP_array_iterator* it){
RJP_array_element* curr = (RJP_array_element*)it->current;
it->current = curr ? (RJP_value*)(curr->next) : NULL;
return it->current;
}
RJP_value* rjp_array_iterator_peek(const RJP_array_iterator* it){
RJP_array_element* curr = (RJP_array_element*)it->current;
return curr ? &(curr->next->value) : NULL;
}

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

315
src/rjp_object.c Normal file
View File

@ -0,0 +1,315 @@
/**
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 "tree.h"
#include "rjp_object.h"
#include "rjp_object_member.h"
#include "rjp_value.h"
#include <string.h> //strlen, strncpy
void irjp_copy_object(RJP_value* dest, const RJP_value* src, const RJP_memory_fns* fns){
switch(src->type){
case rjp_json_object:
irjp_copy_unordered_object(dest, src, fns);
break;
case rjp_json_ordered_object:
irjp_copy_ordered_object(dest, src, fns);
break;
default: break;
};
}
void irjp_delete_object(RJP_value* obj, const RJP_memory_fns* fns){
switch(obj->type){
case rjp_json_object:
irjp_delete_unordered_object(obj, fns);
break;
case rjp_json_ordered_object:
irjp_delete_ordered_object(obj, fns);
break;
default: break;
};
}
RJP_value* rjp_new_member_steal_key(RJP_value* dest, char* key, RJP_index keylen){
return rjp_new_member_steal_key_c(dest, key, keylen, &irjp_default_memory_fns);
}
RJP_value* rjp_new_member_steal_key_c(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns){
if(!key)
return NULL;
if(!keylen)
if(!(keylen = strlen(key)))
return NULL;
switch(dest->type){
case rjp_json_object:
return irjp_add_unordered_member(dest, key, keylen, fns);
case rjp_json_ordered_object:
return irjp_add_ordered_member(dest, key, keylen, fns);
default: break;
};
return NULL;
}
RJP_value* rjp_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 = rjp_escape_strlen(key)))
return NULL;
char* newkey = fns->alloc(keylen+1);
rjp_escape_strcpy(newkey, key);
newkey[keylen] = 0;
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){
return rjp_remove_member_by_key_c(obj, key, &irjp_default_memory_fns);
}
RJP_value* rjp_remove_member_by_key_c(RJP_value* obj, const char* key, const RJP_memory_fns* fns){
RJP_value* member = rjp_search_member(obj, key);
if(!member)
return NULL;
return rjp_remove_member_c(obj, member, fns);
}
RJP_value* rjp_remove_member(RJP_value* obj, RJP_value* member){
return rjp_remove_member_c(obj, member, &irjp_default_memory_fns);
}
RJP_value* rjp_remove_member_c(RJP_value* obj, RJP_value* member, const RJP_memory_fns* fns){
if(member->parent != obj)
return NULL;
switch(obj->type){
case rjp_json_object:
return irjp_remove_unordered_member(obj, member, fns);
case rjp_json_ordered_object:
return irjp_remove_ordered_member(obj, member, fns);
default: break;
};
return NULL;
}
void rjp_free_member_by_key(RJP_value* obj, const char* key){
rjp_free_member_by_key_c(obj, key, &irjp_default_memory_fns);
}
void rjp_free_member_by_key_c(RJP_value* obj, const char* key, const RJP_memory_fns* fns){
RJP_value* removed = rjp_remove_member_by_key(obj, key);
rjp_free_value_c(removed, fns);
}
void rjp_free_member(RJP_value* obj, RJP_value* member){
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);
}
RJP_value* rjp_set_key(RJP_value* dest, const char* key, RJP_index keylen){
return rjp_set_key_c(dest, key, keylen, &irjp_default_memory_fns);
}
RJP_value* rjp_set_key_c(RJP_value* dest, const char* key, RJP_index keylen, const RJP_memory_fns* fns){
if(!key)
return NULL;
if(!keylen){
if(!(keylen = strlen(key)))
return NULL;
}
char* newkey = fns->alloc(keylen + 1);
strncpy(newkey, key, keylen+1);
return rjp_set_key_steal(dest, newkey, keylen);
}
RJP_value* rjp_set_key_steal(RJP_value* dest, char* key, RJP_index keylen){
return rjp_set_key_steal_c(dest, key, keylen, &irjp_default_memory_fns);
}
RJP_value* rjp_set_key_steal_c(RJP_value* dest, char* key, RJP_index keylen, const RJP_memory_fns* fns){
if(!key)
return NULL;
if(!keylen){
if(!(keylen = strlen(key)))
return NULL;
}
switch(dest->parent->type){
case rjp_json_object:
irjp_unordered_set_key(dest, key, keylen, fns);
break;
case rjp_json_ordered_object:
irjp_ordered_set_key(dest, key, keylen, fns);
break;
default: break;
};
return dest;
}
RJP_index rjp_num_members(const RJP_value* object){
switch(object->type){
case rjp_json_object:
return irjp_unordered_num_members(object);
case rjp_json_ordered_object:
return irjp_ordered_num_members(object);
default: break;
};
return 0;
}
const RJP_string* rjp_member_key(const RJP_value* member){
return &(((RJP_object_member*)member)->name);
}
RJP_value* rjp_search_member(const RJP_value* object, const char* search){
if(!object)
return NULL;
switch(object->type){
case rjp_json_object:
return irjp_unordered_search_member(object, search);
case rjp_json_ordered_object:
return irjp_ordered_search_member(object, search);
default: break;
};
return NULL;
}
void rjp_init_object_iterator(RJP_object_iterator* it, const RJP_value* object){
switch(object->type){
case rjp_json_object:
irjp_init_unordered_object_iterator(it, object);
it->type = rjp_json_object;
break;
case rjp_json_ordered_object:
irjp_init_ordered_object_iterator(it, object);
it->type = rjp_json_ordered_object;
break;
default: break;
};
}
void rjp_delete_object_iterator(RJP_object_iterator* it){
switch(it->type){
case rjp_json_object:
irjp_delete_unordered_object_iterator(it);
break;
case rjp_json_ordered_object:
irjp_delete_ordered_object_iterator(it);
break;
default: break;
};
}
RJP_value* rjp_object_iterator_current(const RJP_object_iterator* it){
switch(it->type){
case rjp_json_object:
return irjp_unordered_object_iterator_current(it);
break;
case rjp_json_ordered_object:
return irjp_ordered_object_iterator_current(it);
break;
default: break;
};
return NULL;
}
RJP_value* rjp_object_iterator_next(RJP_object_iterator* it){
switch(it->type){
case rjp_json_object:
return irjp_unordered_object_iterator_next(it);
break;
case rjp_json_ordered_object:
return irjp_ordered_object_iterator_next(it);
break;
default: break;
};
return NULL;
}
RJP_value* rjp_object_iterator_peek(const RJP_object_iterator* it){
switch(it->type){
case rjp_json_object:
return irjp_unordered_object_iterator_peek(it);
break;
case rjp_json_ordered_object:
return irjp_ordered_object_iterator_peek(it);
break;
default: break;
};
return NULL;
}
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

@ -1,6 +1,6 @@
/** /**
rjp rjp
Copyright (C) 2018-2019 rexy712 Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -16,25 +16,24 @@
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 "strings.h"
#include "rjp_internal.h" #include "rjp_internal.h"
#include "rjp_string.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
//Determine if the character is valid whitespace //Determine if the character is valid whitespace
int _rjp__is_whitespace(char c){ int irjp_is_whitespace(char c){
return c == ' ' || c == '\n' || c == '\r' || c == '\t'; return c == ' ' || c == '\n' || c == '\r' || c == '\t';
} }
static uint32_t utf_strtol_4(const char* c){ static uint32_t utf_strtol_4(const char* c){
uint32_t ret = 0; uint32_t ret = 0;
for(size_t i = 0;i < 4;++i){ for(RJP_index i = 0;i < 4;++i){
if(c[i] >= '0' && c[i] <= '9'){ if(c[i] >= '0' && c[i] <= '9'){
ret |= ((c[i] ^ 0x30) << (4*(3-i))); ret |= ((c[i] ^ 0x30) << (4*(3-i)));
}else if(c[i] >= 'A' && c[i] <= 'F'){ }else if(c[i] >= 'A' && c[i] <= 'F'){
@ -61,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;
@ -119,7 +120,7 @@ static int codepoint_to_u8(char* dest, uint32_t codepoint){
return 0; return 0;
} }
} }
static uint32_t u8_to_codepoint(char* u){ /*static uint32_t u8_to_codepoint(char* u){
if((u[0] & 0x80) == 0){ if((u[0] & 0x80) == 0){
//one byte //one byte
return u[0]; return u[0];
@ -148,92 +149,95 @@ static uint32_t u8_to_codepoint(char* u){
//invalid //invalid
return 0; return 0;
} }
} }*/
//Convert escape sequences in strings int irjp_copy_string_quoted(char* restrict dest, const char* restrict string, RJP_index length){
char* _rjp__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; return NULL;
}else if(*(str+oldpos) == '\n'){
++(*row);
*column = 0;
} }
} }
*inclen = oldpos; newstring = fns->alloc(newlength + 1);
*len = newpos; newstring[newlength] = 0;
if(newpos == 0){ *newlen = newlength;
return NULL; oldpos = 1;
} for(RJP_index newpos = 0;oldpos < length;++newpos,++oldpos){
new_string = rjp_alloc(newpos + 1); if(str[oldpos] == '\\'){
new_string[newpos] = 0; ++oldpos;
for(int i = 0;*str != '"';++i,++str){ switch(str[oldpos]){
if(*str == '\\'){
++str;
switch(*str){
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;
} }
size_t rjp_escape_strcpy(char* dest, const char* src){ RJP_index rjp_escape_strcpy(char* dest, const char* src){
size_t j = 0; RJP_index j = 0;
for(size_t i = 0;src[i];++i){ for(RJP_index i = 0;src[i];++i){
switch(src[i]){ switch(src[i]){
case '"': case '"':
dest[j++] = '\\'; dest[j++] = '\\';
@ -271,16 +275,16 @@ size_t rjp_escape_strcpy(char* dest, const char* src){
dest[j] = 0; dest[j] = 0;
return j; return j;
} }
void _rjp__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;
} }
size_t rjp_escape_strlen(const char* str){ RJP_index rjp_escape_strlen(const char* str){
size_t count = 0; RJP_index count = 0;
for(size_t i = 0;str[i];++i){ for(RJP_index i = 0;str[i];++i){
switch(str[i]){ switch(str[i]){
case '"': case '"':
case '\n': case '\n':
@ -297,75 +301,85 @@ size_t rjp_escape_strlen(const char* str){
} }
return count; return count;
} }
size_t _rjp__array_strlen(const RJP_value* arr){ RJP_string rjp_escape(const char* src){
size_t count = 2; //[] RJP_index esclen = rjp_escape_strlen(src);
const RJP_array* array = &arr->array; char* dest = rjp_alloc(esclen+1);
const RJP_array_element* element_list = array->elements; rjp_escape_strcpy(dest, src);
for(;element_list;element_list = element_list->next){ return (RJP_string){.value = dest, .length = esclen};
switch(element_list->value.type){ }
case json_integer: RJP_index irjp_array_strlen(const RJP_value* arr, const int flags, int depth){
count += snprintf(NULL, 0, "%" PRId64, element_list->value.integer); RJP_index count = 2; //[]
break; if(flags & RJP_FORMAT_NEWLINES)
case json_dfloat: ++count;
count += snprintf(NULL, 0, "%lf", element_list->value.dfloat); if(flags & RJP_FORMAT_TABBED_LINES)
break; count += depth;
case json_boolean: ++depth;
count += element_list->value.boolean ? 4 : 5;
break; RJP_array_iterator it;
case json_null: rjp_init_array_iterator(&it, arr);
count += 4; for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
break; count += irjp_value_strlen(current, flags, depth);
case json_string: if(flags & RJP_FORMAT_NEWLINES)
count += element_list->value.string.length;
break;
case json_array:
count += _rjp__array_strlen(&element_list->value);
break;
case json_object:
count += _rjp__object_strlen(&element_list->value);
break;
};
if(element_list->next)
++count; ++count;
else if(flags & RJP_FORMAT_TABBED_LINES)
break; count += depth;
} if(rjp_array_iterator_peek(&it)){
return count;
}
size_t _rjp__object_strlen(const RJP_value* root){
size_t count = 2; //{}
const RJP_object* root_obj = &root->object;
const RJP_object_member* member_list = root_obj->members;
for(;member_list;member_list = member_list->next){
if(member_list->name.length == 0 || member_list->name.value[0] == 0)
return 0;
count += member_list->name.length + 3; //"":
count += _rjp__value_strlen(&member_list->value);
if(member_list->next)
++count; //, ++count; //,
else if(flags & RJP_FORMAT_COMMA_SPACES)
break; ++count; //space
}
} }
return count; return count;
} }
size_t _rjp__value_strlen(const RJP_value* root){ RJP_index irjp_object_strlen(const RJP_value* root, const int flags, int depth){
RJP_index count = 2; //{}
if(flags & RJP_FORMAT_NEWLINES)
++count;
if(flags & RJP_FORMAT_TABBED_LINES)
count += depth;
++depth;
RJP_object_iterator it;
rjp_init_object_iterator(&it, root);
RJP_value* current = rjp_object_iterator_current(&it);
while(current){
RJP_index namelen = rjp_member_key(current)->length;
count += namelen + 3; //"":
if(flags & RJP_FORMAT_KEY_SPACES)
++count;
count += irjp_value_strlen(current, flags, depth);
if(flags & RJP_FORMAT_NEWLINES)
++count;
if(flags & RJP_FORMAT_TABBED_LINES)
count += depth;
if((current = rjp_object_iterator_next(&it))){
++count; //,
if(flags & RJP_FORMAT_COMMA_SPACES)
++count; //space
}
}
rjp_delete_object_iterator(&it);
return count;
}
RJP_index irjp_value_strlen(const RJP_value* root, const int flags, int depth){
switch(root->type){ switch(root->type){
case json_integer: case rjp_json_integer:
return snprintf(NULL, 0, "%" PRId64, root->integer); return snprintf(NULL, 0, "%" PRId64, root->integer);
case json_dfloat: case rjp_json_dfloat:
return snprintf(NULL, 0, "%lf", root->dfloat); return snprintf(NULL, 0, "%lf", root->dfloat);
case json_boolean: case rjp_json_boolean:
return root->boolean ? 4 : 5; //true, false return root->boolean ? 4 : 5; //true, false
case json_null: case rjp_json_null:
return 4; return 4;
case json_string: case rjp_json_string:
return rjp_escape_strlen(root->string.value) + 2; //""; return rjp_escape_strlen(root->string.value) + 2; //""
case json_array: case rjp_json_array:
return _rjp__array_strlen(root); return irjp_array_strlen(root, flags, depth);
case json_object: case rjp_json_object:
return _rjp__object_strlen(root); case rjp_json_ordered_object:
return irjp_object_strlen(root, flags, 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;
}

387
src/tree.c Normal file
View File

@ -0,0 +1,387 @@
/**
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 "tree.h"
#include "rjp_string.h"
#include "rjp_internal.h"
#include "rjp_value.h"
#include <string.h> //memset, strcmp
#define BLACK 0
#define RED 1
//Forward declare static functions
static inline RJP_tree_node* irjp_grandparent(RJP_tree_node* n);
static inline RJP_tree_node* irjp_sibling(RJP_tree_node* n);
static inline RJP_tree_node* irjp_uncle(RJP_tree_node* n);
static inline RJP_tree_node* irjp_rotate_left(RJP_tree_node* pivot);
static inline RJP_tree_node* irjp_rotate_right(RJP_tree_node* pivot);
static inline int irjp_is_inside_left(RJP_tree_node* n);
static inline int irjp_is_inside_right(RJP_tree_node* n);
static void irjp_replace_node(RJP_tree_node *restrict node, RJP_tree_node *restrict child);
static void irjp_copy_node_data(RJP_tree_node *restrict dest, RJP_tree_node *restrict src);
static RJP_tree_node* irjp_clone_node(const RJP_tree_node* src, RJP_tree_node* parent, const RJP_memory_fns* fns);
static RJP_tree_node* irjp_copy_node(const RJP_tree_node* root, RJP_tree_node* parent, const RJP_memory_fns* fns);
static void irjp_free_node(RJP_tree_node* node, const RJP_memory_fns* fns);
static void irjp_delete_node(RJP_tree_node* node, const RJP_memory_fns* fns);
static RJP_tree_node* irjp_tree_insert_impl(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode);
static RJP_tree_node* irjp_tree_repair(RJP_tree_node* node);
static RJP_tree_node* irjp_tree_remove_node(RJP_tree_node* target, RJP_tree_node** removed_node);
//Tree helpers
static inline RJP_tree_node* irjp_grandparent(RJP_tree_node* n){
if(!n->parent)
return NULL;
return n->parent->parent;
}
static inline RJP_tree_node* irjp_sibling(RJP_tree_node* n){
RJP_tree_node* p = n->parent;
if(!p)
return NULL;
if(p->left == n)
return p->right;
return p->left;
}
static inline RJP_tree_node* irjp_uncle(RJP_tree_node* n){
if(!n->parent)
return NULL;
return irjp_sibling(n->parent);
}
static inline RJP_tree_node* irjp_rotate_right(RJP_tree_node* pivot){
RJP_tree_node* newroot = pivot->left;
pivot->left = newroot->right;
newroot->right = pivot;
newroot->parent = pivot->parent;
pivot->parent = newroot;
if(newroot->parent){
if(newroot->parent->left == pivot)
newroot->parent->left = newroot;
else
newroot->parent->right = newroot;
}
if(pivot->left)
pivot->left->parent = pivot;
return newroot;
}
static inline RJP_tree_node* irjp_rotate_left(RJP_tree_node* pivot){
RJP_tree_node* newroot = pivot->right;
pivot->right = newroot->left;
newroot->left = pivot;
newroot->parent = pivot->parent;
pivot->parent = newroot;
if(newroot->parent){
if(newroot->parent->left == pivot)
newroot->parent->left = newroot;
else
newroot->parent->right = newroot;
}
if(pivot->right)
pivot->right->parent = pivot;
return newroot;
}
static inline int irjp_is_inside_left(RJP_tree_node* n){
RJP_tree_node* p = n->parent;
RJP_tree_node* g = irjp_grandparent(n);
return n == p->right && p == g->left;
}
static inline int irjp_is_inside_right(RJP_tree_node* n){
RJP_tree_node* p = n->parent;
RJP_tree_node* g = irjp_grandparent(n);
return n == p->left && p == g->right;
}
static void irjp_replace_node(RJP_tree_node *restrict node, RJP_tree_node *restrict child){
if(child)
child->parent = node->parent;
if(!node->parent)
return;
if(node == node->parent->left)
node->parent->left = child;
else
node->parent->right = child;
}
//Node construction / destruction
RJP_tree_node* irjp_new_node(char* key, RJP_index keylen, const RJP_memory_fns* fns){
RJP_tree_node* node = fns->alloc(sizeof(RJP_tree_node));
memset(node, 0, sizeof(RJP_tree_node));
node->data.name.value = key;
node->data.name.length = keylen;
node->parent = node->left = node->right = NULL;
node->color = BLACK;
return node;
}
static void irjp_copy_node_data(RJP_tree_node *restrict dest, RJP_tree_node *restrict src){
dest->data = src->data;
}
static RJP_tree_node* irjp_clone_node(const RJP_tree_node* src, RJP_tree_node* parent, const RJP_memory_fns* fns){
RJP_tree_node* dest = fns->alloc(sizeof(RJP_tree_node));
memset(dest, 0, sizeof(RJP_tree_node));
rjp_copy_value_c(&dest->data.value, &src->data.value, fns);
irjp_strcpy(&dest->data.name, &src->data.name, fns);
dest->parent = parent;
return dest;
}
static RJP_tree_node* irjp_copy_node(const RJP_tree_node* root, RJP_tree_node* parent, const RJP_memory_fns* fns){
if(!root){
return NULL;
}
RJP_tree_node* newnode = irjp_clone_node(root, parent, fns);
newnode->left = irjp_copy_node(root->left, newnode, fns);
newnode->right = irjp_copy_node(root->right, newnode, fns);
return newnode;
}
static void irjp_free_node(RJP_tree_node* node, const RJP_memory_fns* fns){
irjp_delete_node(node, fns);
fns->free(node);
}
static void irjp_delete_node(RJP_tree_node* node, const RJP_memory_fns* fns){
fns->free(node->data.name.value);
irjp_delete_value(&node->data.value, fns);
}
/* TREE */
//Tree construction / destruction
RJP_tree_node* irjp_copy_tree(const RJP_tree_node* root, const RJP_memory_fns* fns){
return irjp_copy_node(root, NULL, fns);
}
void irjp_free_tree(RJP_tree_node* root, const RJP_memory_fns* fns){
if(!root)
return;
irjp_free_tree(root->left, fns);
irjp_free_tree(root->right, fns);
irjp_free_node(root, fns);
}
//Tree operations
RJP_tree_node* irjp_tree_insert_value(RJP_tree_node* root, char* key, RJP_index keylen, RJP_value** added, int* status, const RJP_memory_fns* fns){
*status = RJP_TREE_SUCCESS;
if(!root){
root = irjp_new_node(key, keylen, fns);
if(added)
*added = &root->data.value;
return root;
}
RJP_tree_node* newnode = irjp_new_node(key, keylen, fns);
if(added)
*added = &newnode->data.value;
return irjp_tree_insert_node(root, newnode);
}
RJP_tree_node* irjp_tree_insert_node(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode){
irjp_tree_insert_impl(root, newnode);
irjp_tree_repair(newnode);
while(root->parent)
root = root->parent;
return root;
}
static RJP_tree_node* irjp_tree_insert_impl(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode){
if(!root){
return newnode;
}
RJP_tree_node* n = root;
while(1){
//compare json key strings
int cmpval = strcmp(newnode->data.name.value, n->data.name.value);
if(cmpval < 0){
if(n->left){
n = n->left;
}else{
n->left = newnode;
newnode->parent = n;
break;
}
}else{
if(n->right){
n = n->right;
}else{
n->right = newnode;
newnode->parent = n;
break;
}
}
}
return newnode;
}
static RJP_tree_node* irjp_tree_repair(RJP_tree_node* node){
node->color = RED;
while(1){
if(!node->parent){
node->color = BLACK;
return node;
}else if(node->parent->color == BLACK){
return node;
}else if(irjp_uncle(node) && irjp_uncle(node)->color == RED){
node->parent->color = BLACK;
irjp_uncle(node)->color = BLACK;
node = irjp_grandparent(node);
}else{
if(irjp_is_inside_left(node)){
irjp_rotate_left(node->parent);
node = node->left;
}else if(irjp_is_inside_right(node)){
irjp_rotate_right(node->parent);
node = node->right;
}
RJP_tree_node* pa = node->parent;
RJP_tree_node* gp = irjp_grandparent(node);
if(node == pa->left){
irjp_rotate_right(gp);
}else{
irjp_rotate_left(gp);
}
pa->color = BLACK;
gp->color = RED;
return node;
}
}
}
RJP_tree_node* irjp_tree_search_value(RJP_tree_node* root, const char* key){
while(root != NULL){
int cmpval = strcmp(key, root->data.name.value);
if(cmpval < 0){
root = root->left;
}else if(cmpval > 0){
root = root->right;
}else{
return root;
}
}
return NULL;
}
RJP_tree_node* irjp_tree_remove_value(RJP_tree_node* restrict root, RJP_tree_node* restrict member, RJP_tree_node** removed_node){
if(!root)
return root;
if(!member)
return root;
return irjp_tree_remove_node(member, removed_node);
}
static RJP_tree_node* irjp_tree_remove_node(RJP_tree_node* target, RJP_tree_node** removed_node){
while(target->right && target->left){
irjp_copy_node_data(target, target->right);
target = target->right;
}
*removed_node = target;
RJP_tree_node* retval = target->parent;
do{
RJP_tree_node* child = (target->left) ? target->left : target->right;
//handle trivial cases
if(target->color == RED){
irjp_replace_node(target, child);
target->parent = NULL;
break;
}else{
if(child && child->color == RED){
child->color = BLACK;
}
irjp_replace_node(target, child);
if(!retval)
retval = child;
break;
}
if(!target->parent){
retval = child;
break;
//handle harder cases
}else{
//parent and sibling guaranteed to exist at this point
RJP_tree_node* sibling = irjp_sibling(target);
//Deal with red sibling
if(sibling->color == RED){
sibling->color = BLACK;
target->parent->color = RED;
if(target == target->parent->left){
irjp_rotate_left(target->parent);
}else{
irjp_rotate_right(target->parent);
}
sibling = irjp_sibling(target);
}
//sibling guaranteed to be black here
if(target->parent->color == BLACK &&
((!sibling->left) || sibling->left->color == BLACK) &&
((!sibling->right) || sibling->right->color == BLACK))
{
sibling->color = RED;
target = target->parent;
continue;
}
//sibling guaranteed to be black here
if(target->parent->color == RED &&
((!sibling->left) || sibling->left->color == BLACK) &&
((!sibling->right) || sibling->right->color == BLACK))
{
sibling->color = RED;
target->parent->color = BLACK;
break;
}
//Orient the subtree for the following section
if((target == target->parent->left) && (!sibling->right || sibling->right->color == BLACK) &&
(sibling->left && sibling->left->color == RED))
{
sibling->color = RED;
if(sibling->left)
sibling->left->color = BLACK;
irjp_rotate_right(sibling);
sibling = irjp_sibling(target);
}else if((target == target->parent->right) && (sibling->right && sibling->right->color == RED) &&
(!sibling->left || sibling->left->color == BLACK))
{
sibling->color = RED;
if(sibling->right)
sibling->right->color = BLACK;
irjp_rotate_left(sibling);
sibling = irjp_sibling(target);
}
sibling->color = target->parent->color;
target->parent->color = BLACK;
if(target == target->parent->left){
//guaranteed to not be NULL
sibling->right->color = BLACK;
irjp_rotate_left(target->parent);
}else{
//guaranteed to not be NULL
sibling->left->color = BLACK;
irjp_rotate_right(target->parent);
}
break;
}
}while(1);
(*removed_node)->left = NULL;
(*removed_node)->right = NULL;
//return new root
if(!retval)
return NULL;
while(retval->parent)
retval = retval->parent;
return retval;
}

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