Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 28 additions & 42 deletions .github/workflows/cpp-internal.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
name: C++ Internal

on:
schedule:
- cron: 0 1 * * * # Nightly at 01:00 UTC
- cron: 0 1 * * * # Nightly at 01:00 UTC
push:
branches:
- master
pull_request:
workflow_dispatch:
inputs:
extra_resolve_options:
description: Extra Resolve Options
required: false

jobs:
linux_cmake:
timeout-minutes: 45
Expand Down Expand Up @@ -47,18 +40,16 @@ jobs:
- ${{ matrix.config.runner }}
name: ${{ matrix.config.name }}
steps:
# This is sometimes needed when running docker builds since these
# sometimes produce files with root ownership
- name: Ensure correct owner of repository
run: sudo chown -R actions-runner:actions-runner .
- name: Checkout source code
uses: actions/checkout@v3
- name: Waf Clean
run: python3 waf clean --no_resolve
- name: Waf Configure
run: python3 waf configure --git_protocol=git@ --cmake_toolchain=${{ matrix.config.toolchain }} --cmake_verbose
- name: Waf Build
run: python3 waf build --run_tests
run: python3 waf build
- name: Waf Run Tests
run: python3 waf --run_tests

valgrind:
timeout-minutes: 45
Expand All @@ -70,41 +61,40 @@ jobs:
steps:
- name: Ensure correct owner of repository
run: sudo chown -R actions-runner:actions-runner .

- name: Checkout source code
uses: actions/checkout@v3

- name: Waf Clean
run: python3 waf clean --no_resolve

- name: Waf Configure
run: python3 waf configure --git_protocol=git@ --cmake_toolchain=./resolve_symlinks/toolchains/gcc-toolchain.cmake --cmake_verbose

- name: Waf Build
run: python3 waf build --run_tests --ctest_valgrind

run: python3 waf build
- name: Waf Run Tests
run: python3 waf --run_tests --ctest_valgrind
zig_toolchain_build:
name: Zig Toolchain Build (Docker)
runs-on: [self-hosted, vm, ubuntu-current]
container:
image: kassany/bookworm-ziglang
image: ghcr.io/steinwurf/build-images/zig-cpp
options: --user 0:0
volumes:
- /root/.ssh:/root/.ssh
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install dependencies
with:
persist-credentials: false
- name: Configure Github Authentication
run: |
apt-get update
apt-get install -y python3 python3-pip git cmake build-essential
- name: Waf Clean
run: python3 waf clean --no_resolve
git config --global credential.helper 'store'
git credential approve <<EOF
protocol=https
host=github.com
username=x-access-token
password=${{ secrets.GH_ACCESS_TOKEN }}
EOF
- name: Waf Configure with Zig Toolchain
run: python3 waf configure --git_protocol=git@ --cmake_toolchain=../resolve_symlinks/toolchains/zig-toolchain-x86_64-linux-musl.cmake --cmake_verbose
run: python3 waf configure --git_protocol=https:// --cmake_toolchain=./resolve_symlinks/toolchains/zig-toolchain-x86_64-linux-musl.cmake --cmake_verbose
- name: Waf Build with Zig Toolchain
run: python3 waf build --run_tests

run: python3 waf build
- name: Waf Run Tests
run: python3 waf --run_tests
macos_cmake:
timeout-minutes: 45
strategy:
Expand All @@ -126,13 +116,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Waf Clean
run: python3 waf clean --no_resolve
- name: Waf Configure
run: python3 waf configure --git_protocol=git@ --cmake_toolchain=${{ matrix.config.toolchain }} --cmake_verbose
- name: Waf Build
run: python3 waf build --run_tests

run: python3 waf build
- name: Waf Run Tests
run: python3 waf --run_tests
windows_cmake:
timeout-minutes: 45
strategy:
Expand All @@ -142,13 +131,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Waf Clean
run: python waf clean --no_resolve
- name: Waf Configure
run: python waf configure --git_protocol=git@ --cmake_verbose
- name: Waf Build
run: python waf build --run_tests

run: python waf build
- name: Waf Run Tests
run: python waf --run_tests
clang-format:
timeout-minutes: 45
name: Clang-Format
Expand All @@ -162,7 +150,6 @@ jobs:
uses: actions/checkout@v3
- name: Run Clang-format
run: find ./ -iname *.hpp -o -iname *.cpp -o -iname *.c -o -iname *.h | xargs clang-format --dry-run --Werror

workflow-keepalive:
if: github.event_name == 'schedule'
runs-on: [self-hosted, vm, ubuntu-current]
Expand All @@ -174,7 +161,6 @@ jobs:
sudo apt update
sudo apt install -y gh
- uses: liskin/gh-workflow-keepalive@v1

concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
cancel-in-progress: true
5 changes: 4 additions & 1 deletion NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ every change, see the Git log.

Latest
------
* tbd
* Minor: Added ``json::parse_options`` and new ``json::parse`` overloads for
strict parsing.
* Minor: Added ``bourne::error::parse_object_duplicate_key`` when strict
parsing detects duplicate object keys.

11.0.0
------
Expand Down
23 changes: 23 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,29 @@ Usage

Usage example is located in the examples folder.

Strict Parsing
==============

By default, parsing is permissive and later values overwrite earlier ones if
an object contains duplicate keys.

To enable strict parsing, pass ``parse_options`` with ``strict = true``. In
strict mode, parsing fails with
``bourne::error::parse_object_duplicate_key`` when duplicate keys are found.

::

bourne::json::parse_options options;
options.strict = true;

std::error_code error;
auto result = bourne::json::parse("{\"a\":1,\"a\":2}", options, error);

if (error == bourne::error::parse_object_duplicate_key)
{
// duplicate key found
}

Build
=====

Expand Down
1 change: 1 addition & 0 deletions src/bourne/detail/error_tags.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ BOURNE_ERROR_TAG(parse_found_multiple_unstructured_elements,
"Found multiple unstructured elements.")
BOURNE_ERROR_TAG(parse_object_expected_colon, "Expected \":\"")
BOURNE_ERROR_TAG(parse_object_expected_comma, "Expected \",\"")
BOURNE_ERROR_TAG(parse_object_duplicate_key, "Duplicate key in object")
BOURNE_ERROR_TAG(parse_array_expected_comma_or_closing_bracket,
"Expected \",\" or \"]\"")
BOURNE_ERROR_TAG(parse_string_expected_unicode_escape_hex_char,
Expand Down
53 changes: 42 additions & 11 deletions src/bourne/detail/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,40 @@ inline namespace STEINWURF_BOURNE_VERSION
namespace detail
{
json parser::parse(const std::string& input)
{
return parse(input, json::parse_options{});
}

json parser::parse(const std::string& input, const json::parse_options& options)
{
std::error_code error;
std::size_t offset = 0;
auto result = parse_next(input, offset, error);
auto result = parse_next(input, offset, error, options);
if (error)
{
throw_if_error(error);
}
consume_white_space(input, offset);
if (offset != input.size())
{
error = bourne::error::parse_found_multiple_unstructured_elements;
throw_if_error(error);
}
throw_if_error(error);
return result;
}

json parser::parse(const std::string& input, std::error_code& error)
{
return parse(input, json::parse_options{}, error);
}

json parser::parse(const std::string& input, const json::parse_options& options,
std::error_code& error)
{
assert(!error);
std::size_t offset = 0;
auto result = parse_next(input, offset, error);
auto result = parse_next(input, offset, error, options);
if (error)
return result;
consume_white_space(input, offset);
Expand All @@ -55,7 +77,8 @@ void parser::consume_white_space(const std::string& input, size_t& offset)
}

json parser::parse_object(const std::string& input, size_t& offset,
std::error_code& error)
std::error_code& error,
const json::parse_options& options)
{
assert(!error);
json object = json(class_type::object);
Expand All @@ -70,7 +93,7 @@ json parser::parse_object(const std::string& input, size_t& offset,

while (true)
{
json key = parse_next(input, offset, error);
json key = parse_next(input, offset, error, options);
if (error)
return json(class_type::null);

Expand All @@ -82,11 +105,17 @@ json parser::parse_object(const std::string& input, size_t& offset,
}
offset++;
consume_white_space(input, offset);
json value = parse_next(input, offset, error);
json value = parse_next(input, offset, error, options);
if (error)
return json(class_type::null);

object[key.to_string()] = value;
std::string key_string = key.to_string();
if (options.strict && object.has_key(key_string))
{
error = bourne::error::parse_object_duplicate_key;
return json(class_type::null);
}
object[key_string] = value;

consume_white_space(input, offset);
if (input[offset] == ',')
Expand All @@ -110,7 +139,8 @@ json parser::parse_object(const std::string& input, size_t& offset,
}

json parser::parse_array(const std::string& input, size_t& offset,
std::error_code& error)
std::error_code& error,
const json::parse_options& options)
{
assert(!error);
json array = json(class_type::array);
Expand All @@ -126,7 +156,7 @@ json parser::parse_array(const std::string& input, size_t& offset,

while (true)
{
array[index++] = parse_next(input, offset, error);
array[index++] = parse_next(input, offset, error, options);
if (error)
return json(class_type::null);
consume_white_space(input, offset);
Expand Down Expand Up @@ -335,7 +365,8 @@ json parser::parse_null(const std::string& input, size_t& offset,
}

json parser::parse_next(const std::string& input, size_t& offset,
std::error_code& error)
std::error_code& error,
const json::parse_options& options)
{
assert(!error);
char value;
Expand All @@ -344,9 +375,9 @@ json parser::parse_next(const std::string& input, size_t& offset,
switch (value)
{
case '[':
return parse_array(input, offset, error);
return parse_array(input, offset, error, options);
case '{':
return parse_object(input, offset, error);
return parse_object(input, offset, error, options);
case '\"':
return parse_string(input, offset, error);
case 't':
Expand Down
14 changes: 11 additions & 3 deletions src/bourne/detail/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,20 @@ class parser
public:
static json parse(const std::string& input);
static json parse(const std::string& input, std::error_code& error);
static json parse(const std::string& input,
const json::parse_options& options);
static json parse(const std::string& input,
const json::parse_options& options,
std::error_code& error);

private:
static void consume_white_space(const std::string& input, size_t& offset);
static json parse_object(const std::string& input, size_t& offset,
std::error_code& error);
std::error_code& error,
const json::parse_options& options);
static json parse_array(const std::string& input, size_t& offset,
std::error_code& error);
std::error_code& error,
const json::parse_options& options);
static json parse_string(const std::string& input, size_t& offset,
std::error_code& error);
static json parse_number(const std::string& input, size_t& offset,
Expand All @@ -39,7 +46,8 @@ class parser
static json parse_null(const std::string& input, size_t& offset,
std::error_code& error);
static json parse_next(const std::string& input, size_t& offset,
std::error_code& error);
std::error_code& error,
const json::parse_options& options);
};
}
}
Expand Down
16 changes: 14 additions & 2 deletions src/bourne/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,12 +537,24 @@ bool json::contains(const json& other) const
json json::parse(const std::string& input, std::error_code& error)
{
assert(!error);
return detail::parser::parse(input, error);
return detail::parser::parse(input, json::parse_options{}, error);
}

json json::parse(const std::string& input, const json::parse_options& options,
std::error_code& error)
{
assert(!error);
return detail::parser::parse(input, options, error);
}

json json::parse(const std::string& input)
{
return detail::parser::parse(input);
return detail::parser::parse(input, json::parse_options{});
}

json json::parse(const std::string& input, const json::parse_options& options)
{
return detail::parser::parse(input, options);
}

json json::array()
Expand Down
Loading
Loading