Hello Arc::Jason
All checks were successful
on-push / build-and-test (push) Successful in 20s

This commit is contained in:
Ryan 2024-06-02 13:05:13 +02:00
commit d258596ca2
Signed by: Ryan
GPG Key ID: 3BD93EABD1407B82
41 changed files with 2479 additions and 0 deletions

16
.editorconfig Normal file
View File

@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
[*.md]
indent_style = space
indent_size = 4
max_line_length = off
trim_trailing_whitespace = false

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto

View File

@ -0,0 +1,18 @@
name: on-push
on: [push]
jobs:
build-and-test:
runs-on: linux
container: code.helloryan.se/infra/buildenv/cxx-amd64-fedora-40:latest
volumes:
- /build
steps:
- name: Clone repository
uses: actions/checkout@v3
- name: Initialize
run: bdep init -C /build cc config.c=gcc config.cxx=g++ config.cc.coptions="-Wall -Werror" --forward
- name: Build
run: b
- name: Test
run: b test -s

31
.gitignore vendored Normal file
View File

@ -0,0 +1,31 @@
.bdep/
# Local default options files.
#
.build2/local/
# Compiler/linker output.
#
*.d
*.t
*.i
*.i.*
*.ii
*.ii.*
*.o
*.obj
*.gcm
*.pcm
*.ifc
*.so
*.dylib
*.dll
*.a
*.lib
*.exp
*.pdb
*.ilk
*.exe
*.exe.dlls/
*.exe.manifest
*.pc

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Arc Project Software License, version 1.0, June 2nd 2024
Permission to use, copy, modify, distribute, and sell this software
(the "Software") and its documentation for any purpose is hereby
granted without fee, provided that the copyright notices in the
Software and this license appear in supporting documentation.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGMENT,
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONJUNCTION WITH THE
SOFTWARE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name(s) of the above copyright
holders shall not be used in advertising or otherwise to promote the
sale, use or other dealing in this Software without prior written
authorization from the copyright holders.

46
README.md Normal file
View File

@ -0,0 +1,46 @@
# Arc Project - libarc-jason
![Build status](https://code.helloryan.se/arc/libarc-jason/actions/workflows/on-push.yaml/badge.svg)
## Contents
- [Description](#description)
- [Requirements](#requirements)
- [Building](#building)
- [Usage](#usage)
- [License](#license)
- [Contact](#contact)
- [Contributing](#contributing)
## Description
libarc-jason is a JSON manipulation library for C++.
## Requirements
None, other than a modern C++-compiler.
## Building
Arc::Validate::Cxx uses the build2 build-system.
## Usage
TODO
## License
Distributed under the Arc Project Software License, version 1.0.
See accompanying [LICENSE](LICENSE) file or copy at
https://code.helloryan.se/arc/LICENSE/raw/branch/master/LICENSE-v1.0.
## Contact
Please report bugs and issues by sending an e-mail to:
arc-project@helloryan.se.
## Contributing
Please send an e-mail to arc-project@helloryan.se to request an
account and write-access to the Arc::Jason repository.

9
arc/jason/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
# Generated version header.
#
version.hxx
# Unit test executables and Testscript output directories
# (can be symlinks).
#
*.test
test-*.test

61
arc/jason/buildfile Normal file
View File

@ -0,0 +1,61 @@
intf_libs = # Interface dependencies.
impl_libs = # Implementation dependencies.
import impl_libs = libarc-unicode%lib{arc-unicode}
./: lib{arc-jason}: libul{arc-jason}: {hxx ixx txx cxx}{** -**.test... -version} \
{hxx }{ version} \
$impl_libs $intf_libs
# Unit tests.
#
exe{*.test}:
{
test = true
install = false
}
test_libs =
import test_libs =+ libarc-validate-xx%lib{arc-validate-xx}
for t: cxx{**.test...}
{
d = $directory($t)
n = $name($t)...
./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n} $test_libs
$d/exe{$n}: libul{arc-jason}: bin.whole = false
$d/exe{$n}: test.arguments = -v
}
hxx{version}: in{version} $src_root/manifest
# Build options.
#
cxx.poptions =+ "-I$out_root" "-I$src_root"
# Export options.
#
lib{arc-jason}:
{
cxx.export.poptions = "-I$out_root" "-I$src_root"
cxx.export.libs = $intf_libs
}
# For pre-releases use the complete version to make sure they cannot
# be used in place of another pre-release or the final version. See
# the version module for details on the version.* variable values.
#
if $version.pre_release
lib{arc-jason}: bin.lib.version = "-$version.project_id"
else
lib{arc-jason}: bin.lib.version = "-$version.major.$version.minor"
# Install into the libarc-jason/ subdirectory of, say, /usr/include/
# recreating subdirectories.
#
{hxx ixx txx}{*}:
{
install = include/libarc-jason/
install.subdirs = true
}

View File

@ -0,0 +1 @@
#include <arc/jason/diagnostics.hxx>

69
arc/jason/diagnostics.hxx Normal file
View File

@ -0,0 +1,69 @@
#ifndef arc__jason__diagnostics_hxx_
#define arc__jason__diagnostics_hxx_
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
namespace Arc::Jason
{
/// Provides JSON parsing diagnostics.
///
class Diagnostics
{
public:
struct Location
{
std::uint32_t row;
std::uint32_t column;
};
struct Warning
{
Location location;
std::string what;
};
struct Error
{
Location location;
std::string what;
};
void
warning(Location l, std::string what)
{
warnings_.emplace_back(l, what);
}
void
error(Location l, std::string what)
{
errors_.emplace_back(l, what);
}
std::vector<Warning> const&
warnings() const
{
return warnings_;
}
std::vector<Error> const&
errors() const
{
return errors_;
}
private:
std::vector<Warning> warnings_;
std::vector<Error> errors_;
};
} // namespace Arc::Jason
#endif

31
arc/jason/except.cxx Normal file
View File

@ -0,0 +1,31 @@
#include <arc/jason/except.hxx>
namespace Arc::Jason
{
Invalid_type::
Invalid_type()
: std::runtime_error{"invalid type"}
{}
Invalid_syntax::
Invalid_syntax()
: std::runtime_error{"invalid syntax"}
{}
Unexpected_type::
Unexpected_type()
: std::runtime_error{"unexpected JSON type"}
{}
Invalid_object_key::
Invalid_object_key(std::string const& key)
: std::runtime_error{ "invalid key '" + key + "'" }
{}
Invalid_array_index::
Invalid_array_index(std::string const& index)
: std::runtime_error{"invalid array index '" + index + "'"}
{}
} // namespace Arc::Jason

54
arc/jason/except.hxx Normal file
View File

@ -0,0 +1,54 @@
#ifndef arc__jason__except_hxx_
#define arc__jason__except_hxx_
#include <stdexcept>
#include <string>
namespace Arc::Jason
{
class Invalid_type
: public std::runtime_error
{
public:
Invalid_type();
};
class Invalid_syntax
: public std::runtime_error
{
public:
Invalid_syntax();
};
class Unexpected_type
: public std::runtime_error
{
public:
Unexpected_type();
};
class Invalid_object_key
: public std::runtime_error
{
public:
explicit
Invalid_object_key(std::string const&);
};
class Invalid_array_index
: public std::runtime_error
{
public:
explicit
Invalid_array_index(std::string const&);
};
} // namespace Arc::Jason
#endif

View File

@ -0,0 +1,70 @@
#include <arc/jason/parser.hxx>
namespace Arc::Jason
{
std::optional<Variant>
Parser::
try_parse_array(Diagnostics& d, State& s) const
{
auto l = location(s);
if (s.iterator.get() != '[') {
d.error(location(s), "expected '['");
return std::nullopt;
}
advance(s); // skip '['
skip_whitespace(s);
if (s.iterator.at_end()) {
return std::nullopt;
}
if (s.iterator.get() == ']') {
advance(s);
return std::vector<Variant>{};
}
std::vector<Variant> a;
for (;;) {
auto v = try_parse_any(d, s);
if (!v)
return std::nullopt;
a.emplace_back(std::move(*v));
skip_whitespace(s);
if (s.iterator.at_end()) {
d.error(location(s), "premature end");
return std::nullopt;
}
if (',' != s.iterator.get())
break;
advance(s);
skip_whitespace(s);
}
if (s.iterator.at_end()) {
d.error(location(s), "premature end");
return std::nullopt;
}
if (s.iterator.get() != ']') {
d.error(location(s), "expected ']'");
return std::nullopt;
}
advance(s);
return Variant{std::move(l), std::move(a)};
}
} // namespace Arc::Jason

View File

@ -0,0 +1,73 @@
#include <arc/jason/parser.hxx>
namespace Arc::Jason
{
std::optional<Variant>
Parser::
try_parse_boolean(Diagnostics& d, State& s) const
{
auto l = location(s);
if (s.iterator.at_end()) {
return std::nullopt;
}
if (s.iterator.get() == 't') {
advance(s);
if (s.iterator.at_end() || s.iterator.get() != 'r') {
return std::nullopt;
}
advance(s);
if (s.iterator.at_end() || s.iterator.get() != 'u') {
return std::nullopt;
}
advance(s);
if (s.iterator.at_end() || s.iterator.get() != 'e') {
return std::nullopt;
}
advance(s);
return Variant{std::move(l), true};
}
if (s.iterator.at_end() || s.iterator.get() != 'f') {
return std::nullopt;
}
advance(s);
if (s.iterator.at_end() || s.iterator.get() != 'a') {
return std::nullopt;
}
advance(s);
if (s.iterator.at_end() || s.iterator.get() != 'l') {
return std::nullopt;
}
advance(s);
if (s.iterator.at_end() || s.iterator.get() != 's') {
return std::nullopt;
}
advance(s);
if (s.iterator.at_end() || s.iterator.get() != 'e') {
return std::nullopt;
}
advance(s);
return Variant{std::move(l), false};
}
} // namespace Arc::Jason

108
arc/jason/parser+number.cxx Normal file
View File

@ -0,0 +1,108 @@
#include <arc/jason/parser.hxx>
#include <limits>
namespace Arc::Jason
{
std::optional<Variant>
Parser::
try_parse_number(Diagnostics& d, State& s) const
{
auto l = location(s);
std::string buf;
buf.reserve(std::numeric_limits< long long >::digits10 * 2);
if (!s.iterator.at_end() && s.iterator.get() == '-') {
buf.push_back(s.iterator.get());
advance(s);
}
if (s.iterator.at_end()) {
d.error(location(s), "premature end");
return std::nullopt;
}
// int
if (s.iterator.get() == '0') {
buf.push_back(s.iterator.get());
advance(s);
}
else if ('1' <= s.iterator.get() && s.iterator.get() <= '9') {
do {
buf.push_back(s.iterator.get());
advance(s);
} while (!s.iterator.at_end() && '0' <= s.iterator.get() && s.iterator.get() <= '9');
}
else {
d.error(location(s), "unexpected character");
return std::nullopt;
}
bool is_real{false};
// frac
if (!s.iterator.at_end() && s.iterator.get() == '.') {
is_real = true;
buf.push_back(s.iterator.get());
advance(s);
if (s.iterator.at_end()) {
d.error(location(s), "premature end");
return std::nullopt;
}
if (s.iterator.get() < '0' || '9' < s.iterator.get()) {
d.error(location(s), "unexpected character");
return std::nullopt;
}
do {
buf.push_back(s.iterator.get());
advance(s);
} while (!s.iterator.at_end() && '0' <= s.iterator.get() && s.iterator.get() <= '9');
}
// exp
if (!s.iterator.at_end() && (s.iterator.get() == 'e' || s.iterator.get() == 'E')) {
is_real = true;
buf.push_back(s.iterator.get());
advance(s);
if (!s.iterator.at_end() && (s.iterator.get() == '-' || s.iterator.get() == '+')) {
buf.push_back(s.iterator.get());
advance(s);
}
if (s.iterator.at_end()) {
d.error(location(s), "premature end");
return std::nullopt;
}
if (s.iterator.get() < '0' || '9' < s.iterator.get()) {
d.error(location(s), "unexpected character");
return std::nullopt;
}
do {
buf.push_back(s.iterator.get());
advance(s);
} while (!s.iterator.at_end() && '0' <= s.iterator.get() && s.iterator.get() <= '9');
}
try {
if (is_real) {
return Variant{l, std::stold(buf)};
}
return Variant{l, std::stoll(buf)};
}
catch (std::out_of_range const&) {
d.error(l, "number too large");
}
return std::nullopt;
}
} // namespace Arc::Jason

View File

@ -0,0 +1,81 @@
#include <arc/jason/parser.hxx>
namespace Arc::Jason
{
std::optional<Variant>
Parser::
try_parse_object(Diagnostics& d, State& s) const
{
auto l = location(s);
if (s.iterator.get() != '{')
return std::nullopt;
advance(s);
skip_whitespace(s);
if (s.iterator.at_end())
return std::nullopt;
if (s.iterator.get() == '}') {
advance(s);
return std::map<std::string, Variant>{};
}
std::map<std::string, Variant> o;
for (;;) {
auto k = try_parse_string(d, s);
if (!k) {
d.error(location(s), "failed to parse key");
return std::nullopt;
}
skip_whitespace(s);
if (s.iterator.at_end() || s.iterator.get() != ':')
return std::nullopt;
advance(s);
skip_whitespace(s);
if (s.iterator.at_end()) {
d.error(location(s), "unexpected end");
return std::nullopt;
}
auto v = try_parse_any(d, s);
if (!v) {
d.error(location(s), "failed to parse value");
return std::nullopt;
}
Variant v_v{std::move(*v)};
o.emplace(k->get_string(), std::move(v_v));
skip_whitespace(s);
if (s.iterator.at_end())
return std::nullopt;
if (',' != s.iterator.get())
break;
advance(s);
skip_whitespace(s);
}
if (s.iterator.at_end() || s.iterator.get() != '}')
return std::nullopt;
advance(s);
return Variant{std::move(l), std::move(o)};
}
} // namespace Arc::Jason

199
arc/jason/parser+string.cxx Normal file
View File

@ -0,0 +1,199 @@
#include <arc/jason/parser.hxx>
#include <arc/unicode/decoding.hxx>
#include <arc/unicode/encoding.hxx>
#include <sstream>
namespace Arc::Jason
{
std::optional<Variant>
Parser::
try_parse_string(Diagnostics& d, State& s) const
{
auto l = location(s);
std::string text;
if (s.iterator.get() != '"') {
d.error(location(s), "unexpected character");
return std::nullopt;
}
advance(s);
if (s.iterator.at_end()) {
d.error(location(s), "premature end");
return std::nullopt;
}
while (!s.iterator.at_end() && s.iterator.get() != '"') {
if (s.iterator.get() == '\\') { // Parse escape sequence.
advance(s);
if (s.iterator.at_end()) {
d.error(location(s), "premature end");
return std::nullopt;
}
switch (s.iterator.get()) {
case '"':
text += '\x22';
advance(s);
break;
case '\\':
text += '\x5c';
advance(s);
break;
case '/':
text += '\x2f';
advance(s);
break;
case 'b':
text += '\x62';
advance(s);
break;
case 'f':
text += '\x66';
advance(s);
break;
case 'n':
text += '\x6e';
advance(s);
break;
case 'r':
text += '\x72';
advance(s);
break;
case 't':
text += '\x74';
advance(s);
break;
case 'u': {
advance(s);
std::uint32_t u[4];
for (int index = 0; index < 4; ++index, advance(s)) {
if (s.iterator.at_end()) {
d.error(location(s), "premature end");
return std::nullopt;
}
std::uint32_t c = (unsigned char)s.iterator.get();
if (U'0' <= c && c <= U'9') {
u[index] = c - U'0';
}
else if (U'a' <= c && c <= U'f') {
u[index] = (c - U'a') + 10;
}
else if (U'A' <= c && c <= U'F') {
u[index] = (c - U'A') + 10;
}
else {
d.error(location(s), "expected digit");
return std::nullopt;
}
}
std::uint32_t utf32 = u[0] << 12 | u[1] << 8 | u[2] << 4 | u[3];
if (0xdc00 <= utf32 && utf32 <= 0xdfff) {
d.error(location(s), "invalid unicode code point");
return std::nullopt;
}
if (0xd800 <= utf32 && utf32 <= 0xdbff) {
// We found a high surrogate, expect a following low-surrogate.
auto high = utf32;
if (s.iterator.at_end()) {
d.error(location(s), "premature end");
return std::nullopt;
}
if (s.iterator.get() != '\\') {
d.error(location(s), "unexpected character");
return std::nullopt;
}
advance(s); // skip backslash
if (s.iterator.at_end()) {
d.error(location(s), "premature end");
return std::nullopt;
}
if (s.iterator.get() != 'u') {
d.error(location(s), "unexpected character2");
return std::nullopt;
}
advance(s); // skip 'u'
for (int index = 0; index < 4; ++index, advance(s)) {
if (s.iterator.at_end()) {
d.error(location(s), "premature end");
return std::nullopt;
}
std::uint32_t c = (unsigned char)s.iterator.get();
if (U'0' <= c && c <= U'9') {
u[index] = c - U'0';
}
else if (U'a' <= c && c <= U'f') {
u[index] = (c - U'a') + 10;
}
else if (U'A' <= c && c <= U'F') {
u[index] = (c - U'A') + 10;
}
else {
d.error(location(s), "expected digit");
return {};
}
}
std::uint32_t low = u[0] << 12 | u[1] << 8 | u[2] << 4 | u[3];
utf32 = (high << 10) + low - 0x35fdc00;
}
std::ostringstream str{ std::ios::out | std::ios::binary };
Arc::Unicode::Utf8_encoder{}.encode(str, utf32);
text += str.str();
break;
}
}
continue;
}
if (is_control(s.iterator.get())) {
d.error(location(s), "invalid character detected");
return std::nullopt;
}
text += s.iterator.get();
advance(s);
}
if (s.iterator.at_end()) {
d.error(location(s), "premature end");
return std::nullopt;
}
if (s.iterator.get() != '"') {
d.error(location(s), "unexpected character");
return std::nullopt;
}
advance(s);
return Variant{std::move(l), std::move(text)};
}
} // namespace Arc::Jason

View File

@ -0,0 +1,41 @@
#include <arc/jason/parser.hxx>
namespace Arc::Jason
{
std::optional<Variant>
Parser::
try_parse_undefined(Diagnostics& d, State& s) const
{
auto l = location(s);
if (s.iterator.at_end()) {
return std::nullopt;
}
if (s.iterator.at_end() || s.iterator.get() != 'n') {
return std::nullopt;
}
if (s.iterator.at_end() || s.iterator.get() != 'u') {
return std::nullopt;
}
advance(s);
if (s.iterator.at_end() || s.iterator.get() != 'l') {
return std::nullopt;
}
advance(s);
if (s.iterator.at_end() || s.iterator.get() != 'l') {
return std::nullopt;
}
advance(s);
return Variant{std::move(l), Variant::undefined};
}
} // namespace Arc::Jason

181
arc/jason/parser.cxx Normal file
View File

@ -0,0 +1,181 @@
#include <arc/jason/parser.hxx>
#include <sstream>
namespace Arc::Jason
{
void
Parser::
skip_whitespace(State& s) const
{
while (!s.iterator.at_end() && is_whitespace(s.iterator.get())) {
advance(s);
}
}
void
Parser::
advance(State& s) const
{
if (s.iterator.at_end()) {
return;
}
if (s.iterator.get() == '\r') {
++s.row;
s.column = 1;
s.iterator.next();
if (!s.iterator.at_end() && s.iterator.get() == '\n') {
s.iterator.next();
}
return;
}
if (s.iterator.get() == '\n') {
++s.row;
s.column = 1;
s.iterator.next();
return;
}
++s.column;
s.iterator.next();
}
Diagnostics::Location
Parser::
location(State& s) const
{
return {s.row, s.column};
}
std::optional<Variant>
Parser::
do_parse(Diagnostics& d, State& s) const
{
skip_whitespace(s);
auto v = try_parse_any(d, s);
if (d.errors().size() > 0) {
return std::nullopt;
}
skip_whitespace(s);
if (!s.iterator.at_end()) {
d.error(location(s), "unexpected trailing data");
return std::nullopt;
}
return v;
}
std::optional<Variant>
Parser::
try_parse_any(Diagnostics& d, State& s) const
{
auto l = location(s);
if (s.iterator.at_end()) {
d.error(l, "premature end");
return Variant{};
}
switch (s.iterator.get()) {
case 'n':
return try_parse_undefined(d, s);
case 't':
case 'f':
return try_parse_boolean(d, s);
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return try_parse_number(d, s);
case '{':
return try_parse_object(d, s);
case '"':
return try_parse_string(d, s);
case '[':
return try_parse_array(d, s);
}
std::ostringstream what;
what << "unexpected character '" << s.iterator.get() << "'";
d.error(l, what.str());
return Variant{};
}
bool
Parser::
is_whitespace(std::uint8_t c)
{
switch (c) {
case 0x09: // tab
case 0x0a: // lf
case 0x0d: // cr
case 0x20: // space
return true;
}
return false;
}
bool
Parser::
is_control(std::uint8_t c)
{
switch (c) {
case '\x00':
case '\x01':
case '\x02':
case '\x03':
case '\x04':
case '\x05':
case '\x06':
case '\x07':
case '\x08':
case '\x09':
case '\x0A':
case '\x0B':
case '\x0C':
case '\x0D':
case '\x0E':
case '\x0F':
case '\x10':
case '\x11':
case '\x12':
case '\x13':
case '\x14':
case '\x15':
case '\x16':
case '\x17':
case '\x18':
case '\x19':
case '\x1A':
case '\x1B':
case '\x1C':
case '\x1D':
case '\x1E':
case '\x1F':
return true;
}
return false;
}
} // namespace Arc::Jason

134
arc/jason/parser.hxx Normal file
View File

@ -0,0 +1,134 @@
#ifndef arc__jason__parser_hxx_
#define arc__jason__parser_hxx_
#include <arc/jason/diagnostics.hxx>
#include <arc/jason/variant.hxx>
#include <optional>
namespace Arc::Jason
{
class Parser
{
public:
template<typename InputIterator>
std::optional<Variant>
try_parse(Diagnostics&, InputIterator&, InputIterator) const;
template<typename InputIterator>
std::optional<Variant>
try_parse(Diagnostics&, InputIterator&&, InputIterator) const;
private:
class Iterator
{
public:
virtual
bool
at_end() const = 0;
virtual
std::uint8_t
get() const = 0;
virtual
void
next() const = 0;
protected:
Iterator() = default;
~Iterator() noexcept = default;
};
template<typename InputIterator>
class Input_iterator
: public Iterator
{
public:
Input_iterator(InputIterator& begin, InputIterator end)
: current_{begin},
end_{end}
{}
bool
at_end() const override
{
return current_ == end_;
}
std::uint8_t
get() const override
{
return *current_;
}
void
next() const override
{
++current_;
}
private:
InputIterator& current_;
InputIterator end_;
};
struct State
{
Iterator& iterator;
std::uint32_t row{1};
std::uint32_t column{1};
};
void
skip_whitespace(State&) const;
void
advance(State&) const;
Diagnostics::Location
location(State&) const;
std::optional<Variant>
do_parse(Diagnostics&, State&) const;
std::optional<Variant>
try_parse_any(Diagnostics&, State&) const;
std::optional<Variant>
try_parse_undefined(Diagnostics&, State&) const;
std::optional<Variant>
try_parse_boolean(Diagnostics&, State&) const;
std::optional<Variant>
try_parse_number(Diagnostics&, State&) const;
std::optional<Variant>
try_parse_string(Diagnostics&, State&) const;
std::optional<Variant>
try_parse_array(Diagnostics&, State&) const;
std::optional<Variant>
try_parse_object(Diagnostics&, State&) const;
static
bool
is_whitespace(std::uint8_t);
static
bool
is_control(std::uint8_t);
};
} // namespace Arc::Jason
#include <arc/jason/parser.txx>
#endif

28
arc/jason/parser.txx Normal file
View File

@ -0,0 +1,28 @@
namespace Arc::Jason
{
template<typename InputIterator>
std::optional<Variant>
Parser::
try_parse(Diagnostics& d,
InputIterator& first,
InputIterator end) const
{
Input_iterator<InputIterator> iterator{first, end};
State state{iterator, 1, 1};
return do_parse(d, state);
}
template<typename InputIterator>
std::optional<Variant>
Parser::
try_parse(Diagnostics& d,
InputIterator&& first,
InputIterator end) const
{
return try_parse(d, first, end);
}
} // namespace Arc::Jason

166
arc/jason/pointer.cxx Normal file
View File

@ -0,0 +1,166 @@
#include <arc/jason/pointer.hxx>
#include <arc/jason/except.hxx>
namespace Arc::Jason
{
Pointer::
Pointer(std::string const& expression)
: refs_{parse_refs(expression)}
{}
std::optional<Variant>
Pointer::
read(Variant const& source) const
{
auto const* current = &source;
auto refs = refs_;
while (refs.size()> 0) {
auto cref = refs.front();
refs.pop();
if (current->is_object()) {
if (!current->contains(cref))
return std::nullopt;
current = &current->get(cref);
}
else if (current->is_array()) {
if (cref.size() == 1 && '~' == cref[0])
throw Invalid_array_index{cref};
auto i = std::stoul(cref);
if (current->size() <= i)
throw Invalid_array_index{cref};
current = &current->get(i);
}
else {
throw Unexpected_type{};
}
}
return *current;
}
void
Pointer::
write(Variant& target, Variant value) const
{
if (!target.is_object())
throw std::invalid_argument{"expected object"};
auto* current = &target;
auto refs = refs_;
while (refs.size()> 1) {
auto cref = refs.front();
refs.pop();
// Check what current is.
if (current->is_object()) {
if (current->contains(cref)) {
current = &current->get(cref);
}
else {
current->set(cref, std::map<std::string, Variant>{});
current = &current->get(cref);
}
}
else if (current->is_array()) {
if (cref.size() == 1 && '~' == cref[0])
throw Invalid_array_index{cref};
auto i = std::stoul(cref);
if (current->size() <= i)
throw Invalid_array_index{cref};
current = &current->get(i);
}
else {
throw Unexpected_type{};
}
}
if (refs.empty())
throw std::invalid_argument{"invalid argument"};
auto cref = refs.front();
refs.pop();
if (current->is_object()) {
current->set(cref, value);
}
else if (current->is_array()) {
// TODO: Implement.
}
else {
throw Unexpected_type{};
}
}
std::queue<std::string>
Pointer::
parse_refs(std::string const& expression)
{
std::queue<std::string> refs;
for (auto it = std::begin(expression); it != std::end(expression);) {
if ('/' != *it)
throw Invalid_syntax{};
++it;
std::string cref;
while (it != std::end(expression) && '/' != *it) {
switch (*it) {
case '~':
++it; // skip tilde
if (it == std::end(expression))
throw Invalid_syntax{};
else if ('0' == *it)
cref += '~';
else if ('1' == *it)
cref += '/';
else
throw Invalid_syntax{};
++it;
break;
default:
cref += *it;
++it;
break;
}
}
if (!cref.empty())
refs.push(std::move(cref));
}
return refs;
}
std::string
to_string(Pointer const& ptr)
{
std::string rendered;
auto refs = ptr.refs_;
while (refs.size() > 0) {
auto cref = refs.front();
refs.pop();
rendered += '/';
rendered += cref;
}
return rendered;
}
} // namespace json

41
arc/jason/pointer.hxx Normal file
View File

@ -0,0 +1,41 @@
#ifndef arc__jason__pointer_hxx_
#define arc__jason__pointer_hxx_
#include <arc/jason/variant.hxx>
#include <optional>
#include <queue>
#include <string>
namespace Arc::Jason
{
class Pointer
{
public:
explicit
Pointer(std::string const&);
std::optional<Variant>
read(Variant const&) const;
void
write(Variant&, Variant) const;
friend
std::string
to_string(Pointer const& ptr);
private:
std::queue<std::string>
parse_refs(std::string const& expression);
std::queue<std::string> refs_;
};
std::string
to_string(Pointer const& ptr);