mirror of
https://github.com/lighttransport/tinyusdz.git
synced 2026-01-18 01:11:17 +01:00
sandbox/usda -> examples/usda_parser
This commit is contained in:
@@ -597,7 +597,7 @@ endif()
|
||||
|
||||
if(TINYUSDZ_WITH_TOOL_USDA_PARSER AND TINYUSDZ_WITH_MODULE_USDA_READER)
|
||||
add_executable(usda_parser
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/sandbox/usda/parser-main.cc)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/usda_parser/parser-main.cc)
|
||||
add_sanitizers(usda_parser)
|
||||
target_include_directories(usda_parser PRIVATE ${PROJECT_SOURCE_DIR}/src/)
|
||||
target_link_libraries(usda_parser PRIVATE ${TINYUSDZ_TARGET_STATIC})
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
CC=gcc
|
||||
CXX=g++
|
||||
INCFLAGS=-I. -I../../src/external/ryu -I../../src
|
||||
|
||||
# only applied to usda-parser.cc
|
||||
#EXTRA_COMPILER_FLAGS=-Weverything -Werror -Wno-padded -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-unused-function -Wno-unused-member-function
|
||||
|
||||
CXXFLAGS=-fPIC -std=c++14 -O0 -DTINYUSDZ_USE_USDOBJ
|
||||
CFLAGS=-fPIC -O0
|
||||
LDFLAGS=-fuse-ld=lld
|
||||
|
||||
# ASAN
|
||||
#CXXFLAGS+=-fsanitize=address -g
|
||||
#CFLAGS+=-fsanitize=address -g
|
||||
#LDFLAGS+=-fsanitize=address
|
||||
|
||||
# On some macOS
|
||||
#EXTRA_COMPILER_FLAGS+=-Wno-poison-system-directories
|
||||
|
||||
# gcc
|
||||
#EXTRA_COMPILER_FLAGS=-Wall
|
||||
#CXXFLAGS=-std=c++11 -g -O0
|
||||
#CFLAGS=-g -O0
|
||||
#LDFLAGS=
|
||||
|
||||
.PHONY: clean all t
|
||||
|
||||
all: usda-parser t
|
||||
|
||||
# unit tester
|
||||
u: usda-parser
|
||||
python unit-runner.py
|
||||
|
||||
# single test
|
||||
t: usda-parser
|
||||
./usda-parser ../../models/usdObj-001.usda
|
||||
#./usda-parser tests/dict.usda
|
||||
#./usda-parser tests/simple-blender-exported.usda
|
||||
#./usda-parser tests/test.usda
|
||||
|
||||
usda-parser: usda-parser.o prim-types.o d2s.o f2s.o s2f.o s2d.o simple-serialize.o usdObj.o io-util.o pprinter.o parser-main.o
|
||||
$(CXX) $(LDFLAGS) -o $@ $^
|
||||
|
||||
usda-parser.o: ../../src/usda-parser.cc ../../src/simple-serialize.hh ../../src/prim-types.hh
|
||||
$(CXX) $(INCFLAGS) $(CXXFLAGS) $(EXTRA_COMPILER_FLAGS) -c -o $@ $<
|
||||
|
||||
parser-main.o: parser-main.cc
|
||||
$(CXX) $(INCFLAGS) $(CXXFLAGS) $(EXTRA_COMPILER_FLAGS) -c -o $@ $<
|
||||
|
||||
prim-types.o: ../../src/prim-types.cc ../../src/prim-types.hh
|
||||
$(CXX) $(INCFLAGS) $(CXXFLAGS) $(EXTRA_COMPILER_FLAGS) -c -o $@ $<
|
||||
|
||||
simple-serialize.o: ../../src/simple-serialize.cc ../../src/simple-serialize.hh
|
||||
$(CXX) $(INCFLAGS) $(CXXFLAGS) $(EXTRA_COMPILER_FLAGS) -c -o $@ $<
|
||||
|
||||
pprinter.o: ../../src/pprinter.cc ../../src/pprinter.hh
|
||||
$(CXX) $(INCFLAGS) $(CXXFLAGS) $(EXTRA_COMPILER_FLAGS) -c -o $@ $<
|
||||
|
||||
# Define TINYOBJ_IMPLEMENTATION
|
||||
usdObj.o: ../../src/usdObj.cc ../../src/usdObj.hh
|
||||
$(CXX) $(INCFLAGS) $(CXXFLAGS) -DTINYOBJLOADER_IMPLEMENTATION $(EXTRA_COMPILER_FLAGS) -c -o $@ $<
|
||||
|
||||
io-util.o: ../../src/io-util.cc ../../src/io-util.hh
|
||||
$(CXX) $(INCFLAGS) $(CXXFLAGS) $(EXTRA_COMPILER_FLAGS) -c -o $@ $<
|
||||
|
||||
d2s.o: ../../src/external/ryu/ryu/d2s.c
|
||||
$(CC) $(INCFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
f2s.o: ../../src/external/ryu/ryu/f2s.c
|
||||
$(CC) $(INCFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
s2f.o: ../../src/external/ryu/ryu/s2f.c
|
||||
$(CC) $(INCFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
s2d.o: ../../src/external/ryu/ryu/s2d.c
|
||||
$(CC) $(INCFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
#file.o: lexy/file.cpp
|
||||
# $(CXX) $(INCFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
@rm -rf usda-parser usda-parser.o d2s.o f2s.o s2d.o s2f.o simple-serialize.o prim-types.o usdObj.o io-util.o pprinter.o parser-main.o
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DETAIL_ASSERT_HPP_INCLUDED
|
||||
#define LEXY_DETAIL_ASSERT_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/config.hpp>
|
||||
|
||||
#ifndef LEXY_ENABLE_ASSERT
|
||||
|
||||
// By default, enable assertions if NDEBUG is not defined.
|
||||
|
||||
# if NDEBUG
|
||||
# define LEXY_ENABLE_ASSERT 0
|
||||
# else
|
||||
# define LEXY_ENABLE_ASSERT 1
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
#if LEXY_ENABLE_ASSERT
|
||||
|
||||
// We want assertions: use assert() if that's available, otherwise abort.
|
||||
// We don't use assert() directly as that's not constexpr.
|
||||
|
||||
# if NDEBUG
|
||||
|
||||
# include <cstdlib>
|
||||
# define LEXY_PRECONDITION(Expr) ((Expr) ? void(0) : std::abort())
|
||||
# define LEXY_ASSERT(Expr, Msg) ((Expr) ? void(0) : std::abort())
|
||||
|
||||
# else
|
||||
|
||||
# include <cassert>
|
||||
|
||||
# define LEXY_PRECONDITION(Expr) ((Expr) ? void(0) : assert(Expr))
|
||||
# define LEXY_ASSERT(Expr, Msg) ((Expr) ? void(0) : assert((Expr) && Msg))
|
||||
|
||||
# endif
|
||||
|
||||
#else
|
||||
|
||||
// We don't want assertions.
|
||||
|
||||
# define LEXY_PRECONDITION(Expr) static_cast<void>(sizeof(Expr))
|
||||
# define LEXY_ASSERT(Expr, Msg) static_cast<void>(sizeof(Expr))
|
||||
|
||||
#endif
|
||||
|
||||
#endif // LEXY_DETAIL_ASSERT_HPP_INCLUDED
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED
|
||||
#define LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED
|
||||
|
||||
#include <cstring>
|
||||
#include <lexy/_detail/assert.hpp>
|
||||
#include <lexy/_detail/config.hpp>
|
||||
#include <lexy/_detail/std.hpp>
|
||||
#include <new>
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
// Builds a buffer: it has a read are and a write area.
|
||||
// The characters in the read area are already valid and can be read.
|
||||
// The characters in the write area are not valid, but can be written too.
|
||||
template <typename T>
|
||||
class buffer_builder
|
||||
{
|
||||
static_assert(std::is_trivial_v<T>);
|
||||
|
||||
static constexpr std::size_t total_size_bytes = 1024;
|
||||
static constexpr std::size_t stack_buffer_size
|
||||
= (total_size_bytes - 3 * sizeof(T*)) / sizeof(T);
|
||||
static constexpr auto growth_factor = 2;
|
||||
|
||||
public:
|
||||
buffer_builder() noexcept : _data(_stack_buffer), _read_size(0), _write_size(stack_buffer_size)
|
||||
{
|
||||
static_assert(sizeof(*this) == total_size_bytes, "invalid buffer size calculation");
|
||||
}
|
||||
|
||||
~buffer_builder() noexcept
|
||||
{
|
||||
// Free memory if we allocated any.
|
||||
if (_data != _stack_buffer)
|
||||
::operator delete(_data);
|
||||
}
|
||||
|
||||
buffer_builder(const buffer_builder&) = delete;
|
||||
buffer_builder& operator=(const buffer_builder&) = delete;
|
||||
|
||||
// The total capacity: read + write.
|
||||
std::size_t capacity() const noexcept
|
||||
{
|
||||
return _read_size + _write_size;
|
||||
}
|
||||
|
||||
// The read area.
|
||||
const T* read_data() const noexcept
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
std::size_t read_size() const noexcept
|
||||
{
|
||||
return _read_size;
|
||||
}
|
||||
|
||||
// The write area.
|
||||
T* write_data() noexcept
|
||||
{
|
||||
return _data + _read_size;
|
||||
}
|
||||
std::size_t write_size() const noexcept
|
||||
{
|
||||
return _write_size;
|
||||
}
|
||||
|
||||
// Clears the read area.
|
||||
void clear() noexcept
|
||||
{
|
||||
_write_size += _read_size;
|
||||
_read_size = 0;
|
||||
}
|
||||
|
||||
// Takes the first n characters of the write area and appends them to the read area.
|
||||
void commit(std::size_t n) noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(n <= _write_size);
|
||||
_read_size += n;
|
||||
_write_size -= n;
|
||||
}
|
||||
|
||||
// Increases the write area, invalidates all pointers.
|
||||
void grow()
|
||||
{
|
||||
const auto cur_cap = capacity();
|
||||
const auto new_cap = growth_factor * cur_cap;
|
||||
|
||||
// Allocate new memory.
|
||||
auto memory = static_cast<T*>(::operator new(new_cap * sizeof(T)));
|
||||
// Copy the read area into the new memory.
|
||||
std::memcpy(memory, _data, _read_size);
|
||||
|
||||
// Release the old memory, if there was any.
|
||||
if (_data != _stack_buffer)
|
||||
::operator delete(_data);
|
||||
|
||||
// Update for the new area.
|
||||
_data = memory;
|
||||
// _read_size hasn't been changed
|
||||
_write_size = new_cap - _read_size;
|
||||
}
|
||||
|
||||
//=== iterator ===//
|
||||
// Stable iterator over the memory.
|
||||
class stable_iterator
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using reference = const value_type&;
|
||||
using pointer = const value_type*;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
constexpr stable_iterator() = default;
|
||||
|
||||
explicit constexpr stable_iterator(const _detail::buffer_builder<T>& buffer,
|
||||
std::size_t idx) noexcept
|
||||
: _buffer(&buffer), _idx(idx)
|
||||
{}
|
||||
|
||||
//=== dereference ===//
|
||||
constexpr reference operator*() const noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(_idx != _buffer->read_size());
|
||||
return _buffer->read_data()[_idx];
|
||||
}
|
||||
|
||||
//=== positioning ===//
|
||||
constexpr stable_iterator& operator++() noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(_idx != _buffer->read_size());
|
||||
++_idx;
|
||||
return *this;
|
||||
}
|
||||
constexpr stable_iterator operator++(int) noexcept
|
||||
{
|
||||
stable_iterator tmp(*this);
|
||||
++*this;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
friend constexpr difference_type operator-(stable_iterator lhs,
|
||||
stable_iterator rhs) noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(lhs._buffer == rhs._buffer);
|
||||
return difference_type(lhs._idx) - difference_type(rhs._idx);
|
||||
}
|
||||
|
||||
//=== comparison ===//
|
||||
friend constexpr bool operator==(stable_iterator lhs, stable_iterator rhs) noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(lhs._buffer == rhs._buffer);
|
||||
return lhs._idx == rhs._idx;
|
||||
}
|
||||
friend constexpr bool operator!=(stable_iterator lhs, stable_iterator rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
const _detail::buffer_builder<T>* _buffer = nullptr;
|
||||
std::size_t _idx = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
T* _data;
|
||||
std::size_t _read_size;
|
||||
std::size_t _write_size;
|
||||
T _stack_buffer[stack_buffer_size];
|
||||
};
|
||||
} // namespace lexy::_detail
|
||||
|
||||
#endif // LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED
|
||||
@@ -1,150 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DETAIL_CONFIG_HPP_INCLUDED
|
||||
#define LEXY_DETAIL_CONFIG_HPP_INCLUDED
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(LEXY_USER_CONFIG_HEADER)
|
||||
# include LEXY_USER_CONFIG_HEADER
|
||||
#elif defined(__has_include)
|
||||
# if __has_include(<lexy_user_config.hpp>)
|
||||
# include <lexy_user_config.hpp>
|
||||
# elif __has_include("lexy_user_config.hpp")
|
||||
# include "lexy_user_config.hpp"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//=== move/fwd/declval/swap ===//
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <typename T>
|
||||
using add_rvalue_ref = T&&;
|
||||
|
||||
template <typename... T>
|
||||
constexpr bool error = false;
|
||||
} // namespace lexy::_detail
|
||||
|
||||
#define LEXY_MOV(...) static_cast<std::remove_reference_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__)
|
||||
#define LEXY_FWD(...) static_cast<decltype(__VA_ARGS__)>(__VA_ARGS__)
|
||||
|
||||
#define LEXY_DECLVAL(...) \
|
||||
reinterpret_cast<::lexy::_detail::add_rvalue_ref<__VA_ARGS__>>(*reinterpret_cast<char*>(1024))
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <typename T>
|
||||
constexpr void swap(T& lhs, T& rhs)
|
||||
{
|
||||
T tmp = LEXY_MOV(lhs);
|
||||
lhs = LEXY_MOV(rhs);
|
||||
rhs = LEXY_MOV(tmp);
|
||||
}
|
||||
} // namespace lexy::_detail
|
||||
|
||||
//=== NTTP ===//
|
||||
#ifndef LEXY_HAS_NTTP
|
||||
# if defined(__GNUC__) && __GNUC__ <= 10
|
||||
// GCC <= 10 has buggy support for NTTP string literals and CTAD
|
||||
# define LEXY_HAS_NTTP 0
|
||||
# elif __cpp_nontype_template_parameter_class
|
||||
# define LEXY_HAS_NTTP 1
|
||||
# else
|
||||
# define LEXY_HAS_NTTP 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//=== consteval ===//
|
||||
#ifndef LEXY_HAS_CONSTEVAL
|
||||
# if __cpp_consteval
|
||||
# define LEXY_HAS_CONSTEVAL 1
|
||||
# else
|
||||
# define LEXY_HAS_CONSTEVAL 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if LEXY_HAS_CONSTEVAL
|
||||
# define LEXY_CONSTEVAL consteval
|
||||
#else
|
||||
# define LEXY_CONSTEVAL constexpr
|
||||
#endif
|
||||
|
||||
//=== char8_t ===//
|
||||
#ifndef LEXY_HAS_CHAR8_T
|
||||
# if __cpp_char8_t
|
||||
# define LEXY_HAS_CHAR8_T 1
|
||||
# else
|
||||
# define LEXY_HAS_CHAR8_T 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if LEXY_HAS_CHAR8_T
|
||||
|
||||
# define LEXY_CHAR8_T char8_t
|
||||
|
||||
#else
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
using _char8_t = unsigned char;
|
||||
} // namespace lexy
|
||||
|
||||
# define LEXY_CHAR8_T ::lexy::_char8_t
|
||||
|
||||
#endif
|
||||
|
||||
//=== endianness ===//
|
||||
#ifndef LEXY_IS_LITTLE_ENDIAN
|
||||
# if defined(__BYTE_ORDER__)
|
||||
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define LEXY_IS_LITTLE_ENDIAN 1
|
||||
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define LEXY_IS_LITTLE_ENDIAN 0
|
||||
# else
|
||||
# error "unsupported byte order"
|
||||
# endif
|
||||
# elif defined(_MSC_VER)
|
||||
# define LEXY_IS_LITTLE_ENDIAN 1
|
||||
# else
|
||||
# error "unknown endianness"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//=== force inline ===//
|
||||
#ifndef LEXY_FORCE_INLINE
|
||||
# if defined(__has_cpp_attribute)
|
||||
# if __has_cpp_attribute(gnu::always_inline)
|
||||
# define LEXY_FORCE_INLINE [[gnu::always_inline]]
|
||||
# endif
|
||||
# endif
|
||||
#
|
||||
# ifndef LEXY_FORCE_INLINE
|
||||
# define LEXY_FORCE_INLINE inline
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//=== empty_member ===//
|
||||
#ifndef LEXY_EMPTY_MEMBER
|
||||
|
||||
# if defined(__has_cpp_attribute)
|
||||
# if __has_cpp_attribute(no_unique_address)
|
||||
# define LEXY_HAS_EMPTY_MEMBER 1
|
||||
# endif
|
||||
# endif
|
||||
# ifndef LEXY_HAS_EMPTY_MEMBER
|
||||
# define LEXY_HAS_EMPTY_MEMBER 0
|
||||
# endif
|
||||
|
||||
# if LEXY_HAS_EMPTY_MEMBER
|
||||
# define LEXY_EMPTY_MEMBER [[no_unique_address]]
|
||||
# else
|
||||
# define LEXY_EMPTY_MEMBER
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // LEXY_DETAIL_CONFIG_HPP_INCLUDED
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (C) 2020 Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DETAIL_DETECT_HPP_INCLUDED
|
||||
#define LEXY_DETAIL_DETECT_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/config.hpp>
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <typename... Args>
|
||||
using void_t = void;
|
||||
|
||||
template <template <typename...> typename Op, typename Void, typename... Args>
|
||||
struct _detector : std::false_type
|
||||
{};
|
||||
template <template <typename...> typename Op, typename... Args>
|
||||
struct _detector<Op, void_t<Op<Args...>>, Args...> : std::true_type
|
||||
{};
|
||||
|
||||
template <template <typename...> typename Op, typename... Args>
|
||||
constexpr bool is_detected = _detector<Op, void, Args...>::value;
|
||||
} // namespace lexy::_detail
|
||||
|
||||
#endif // LEXY_DETAIL_DETECT_HPP_INCLUDED
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DETAIL_INTEGER_SEQUENCE_HPP_INCLUDED
|
||||
#define LEXY_DETAIL_INTEGER_SEQUENCE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/config.hpp>
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <typename T, T... Indices>
|
||||
struct integer_sequence
|
||||
{
|
||||
using type = integer_sequence<T, Indices...>;
|
||||
};
|
||||
template <std::size_t... Indices>
|
||||
using index_sequence = integer_sequence<std::size_t, Indices...>;
|
||||
|
||||
#if defined(__clang__)
|
||||
template <std::size_t Size>
|
||||
using make_index_sequence = __make_integer_seq<integer_sequence, std::size_t, Size>;
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 8
|
||||
template <std::size_t Size>
|
||||
using make_index_sequence = index_sequence<__integer_pack(Size)...>;
|
||||
#elif defined(_MSC_VER)
|
||||
template <std::size_t Size>
|
||||
using make_index_sequence = __make_integer_seq<integer_sequence, std::size_t, Size>;
|
||||
#else
|
||||
|
||||
// Adapted from https://stackoverflow.com/a/32223343.
|
||||
template <class Sequence1, class Sequence2>
|
||||
struct concat_seq;
|
||||
template <std::size_t... I1, std::size_t... I2>
|
||||
struct concat_seq<index_sequence<I1...>, index_sequence<I2...>>
|
||||
{
|
||||
using type = index_sequence<I1..., (sizeof...(I1) + I2)...>;
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
struct _make_index_sequence : concat_seq<typename _make_index_sequence<N / 2>::type,
|
||||
typename _make_index_sequence<N - N / 2>::type>
|
||||
{};
|
||||
template <>
|
||||
struct _make_index_sequence<0>
|
||||
{
|
||||
using type = index_sequence<>;
|
||||
};
|
||||
template <>
|
||||
struct _make_index_sequence<1>
|
||||
{
|
||||
using type = index_sequence<0>;
|
||||
};
|
||||
|
||||
template <std::size_t Size>
|
||||
using make_index_sequence = typename _make_index_sequence<Size>::type;
|
||||
|
||||
#endif
|
||||
|
||||
template <typename... T>
|
||||
using index_sequence_for = make_index_sequence<sizeof...(T)>;
|
||||
} // namespace lexy::_detail
|
||||
|
||||
#endif // LEXY_DETAIL_INTEGER_SEQUENCE_HPP_INCLUDED
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DETAIL_INVOKE_HPP_INCLUDED
|
||||
#define LEXY_DETAIL_INVOKE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/config.hpp>
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <typename MemberPtr, bool = std::is_member_object_pointer_v<MemberPtr>>
|
||||
struct _mem_invoker;
|
||||
template <typename R, typename ClassT>
|
||||
struct _mem_invoker<R ClassT::*, true>
|
||||
{
|
||||
static constexpr decltype(auto) invoke(R ClassT::*f, ClassT& object)
|
||||
{
|
||||
return object.*f;
|
||||
}
|
||||
static constexpr decltype(auto) invoke(R ClassT::*f, const ClassT& object)
|
||||
{
|
||||
return object.*f;
|
||||
}
|
||||
|
||||
template <typename Ptr>
|
||||
static constexpr auto invoke(R ClassT::*f, Ptr&& ptr) -> decltype((*LEXY_FWD(ptr)).*f)
|
||||
{
|
||||
return (*LEXY_FWD(ptr)).*f;
|
||||
}
|
||||
};
|
||||
template <typename F, typename ClassT>
|
||||
struct _mem_invoker<F ClassT::*, false>
|
||||
{
|
||||
template <typename ObjectT, typename... Args>
|
||||
static constexpr auto _invoke(int, F ClassT::*f, ObjectT&& object, Args&&... args)
|
||||
-> decltype((LEXY_FWD(object).*f)(LEXY_FWD(args)...))
|
||||
{
|
||||
return (LEXY_FWD(object).*f)(LEXY_FWD(args)...);
|
||||
}
|
||||
template <typename PtrT, typename... Args>
|
||||
static constexpr auto _invoke(short, F ClassT::*f, PtrT&& ptr, Args&&... args)
|
||||
-> decltype(((*LEXY_FWD(ptr)).*f)(LEXY_FWD(args)...))
|
||||
{
|
||||
return ((*LEXY_FWD(ptr)).*f)(LEXY_FWD(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static constexpr auto invoke(F ClassT::*f, Args&&... args)
|
||||
-> decltype(_invoke(0, f, LEXY_FWD(args)...))
|
||||
{
|
||||
return _invoke(0, f, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ClassT, typename F, typename... Args>
|
||||
constexpr auto invoke(F ClassT::*f, Args&&... args)
|
||||
-> decltype(_mem_invoker<F ClassT::*>::invoke(f, LEXY_FWD(args)...))
|
||||
{
|
||||
return _mem_invoker<F ClassT::*>::invoke(f, LEXY_FWD(args)...);
|
||||
}
|
||||
|
||||
template <typename F, typename... Args>
|
||||
constexpr auto invoke(F&& f, Args&&... args) -> decltype(LEXY_FWD(f)(LEXY_FWD(args)...))
|
||||
{
|
||||
return LEXY_FWD(f)(LEXY_FWD(args)...);
|
||||
}
|
||||
} // namespace lexy::_detail
|
||||
|
||||
#endif // LEXY_DETAIL_INVOKE_HPP_INCLUDED
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DETAIL_MEMORY_RESOURCE_HPP_INCLUDED
|
||||
#define LEXY_DETAIL_MEMORY_RESOURCE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/assert.hpp>
|
||||
#include <lexy/_detail/config.hpp>
|
||||
#include <new>
|
||||
|
||||
#if 0
|
||||
// Subset of the interface of std::pmr::memory_resource.
|
||||
class MemoryResource
|
||||
{
|
||||
public:
|
||||
void* allocate(std::size_t bytes, std::size_t alignment);
|
||||
void deallocate(void* ptr, std::size_t bytes, std::size_t alignment);
|
||||
|
||||
friend bool operator==(const MemoryResource& lhs, const MemoryResource& rhs);
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
class default_memory_resource
|
||||
{
|
||||
public:
|
||||
void* allocate(std::size_t bytes, std::size_t alignment)
|
||||
{
|
||||
if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
|
||||
return ::operator new (bytes, std::align_val_t{alignment});
|
||||
else
|
||||
return ::operator new(bytes);
|
||||
}
|
||||
|
||||
void deallocate(void* ptr, std::size_t bytes, std::size_t alignment) noexcept
|
||||
{
|
||||
#ifdef __cpp_sized_deallocation
|
||||
if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
|
||||
::operator delete (ptr, bytes, std::align_val_t{alignment});
|
||||
else
|
||||
::operator delete(ptr, bytes);
|
||||
#else
|
||||
(void)bytes;
|
||||
|
||||
if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
|
||||
::operator delete (ptr, std::align_val_t{alignment});
|
||||
else
|
||||
::operator delete(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(default_memory_resource, default_memory_resource) noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace lexy::_detail
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <typename MemoryResource>
|
||||
class _memory_resource_ptr_empty
|
||||
{
|
||||
public:
|
||||
constexpr explicit _memory_resource_ptr_empty(MemoryResource*) noexcept {}
|
||||
|
||||
constexpr auto operator*() const noexcept
|
||||
{
|
||||
return MemoryResource{};
|
||||
}
|
||||
|
||||
constexpr auto operator->() const noexcept
|
||||
{
|
||||
struct proxy
|
||||
{
|
||||
MemoryResource _resource;
|
||||
|
||||
constexpr MemoryResource* operator->() noexcept
|
||||
{
|
||||
return &_resource;
|
||||
}
|
||||
};
|
||||
|
||||
return proxy{};
|
||||
}
|
||||
|
||||
constexpr MemoryResource* get() const noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename MemoryResource>
|
||||
class _memory_resource_ptr
|
||||
{
|
||||
public:
|
||||
constexpr explicit _memory_resource_ptr(MemoryResource* resource) noexcept : _resource(resource)
|
||||
{
|
||||
LEXY_PRECONDITION(resource);
|
||||
}
|
||||
|
||||
constexpr MemoryResource& operator*() const noexcept
|
||||
{
|
||||
return *_resource;
|
||||
}
|
||||
|
||||
constexpr MemoryResource* operator->() const noexcept
|
||||
{
|
||||
return _resource;
|
||||
}
|
||||
|
||||
constexpr MemoryResource* get() const noexcept
|
||||
{
|
||||
return _resource;
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryResource* _resource;
|
||||
};
|
||||
|
||||
template <typename MemoryResource>
|
||||
using memory_resource_ptr = std::conditional_t<std::is_empty_v<MemoryResource>,
|
||||
_memory_resource_ptr_empty<MemoryResource>,
|
||||
_memory_resource_ptr<MemoryResource>>;
|
||||
|
||||
template <typename MemoryResource>
|
||||
constexpr MemoryResource* get_memory_resource()
|
||||
{
|
||||
static_assert(std::is_empty_v<MemoryResource>, "need to pass a MemoryResource ptr");
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace lexy::_detail
|
||||
|
||||
#endif // LEXY_DETAIL_MEMORY_RESOURCE_HPP_INCLUDED
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DETAIL_NTTP_STRING_HPP_INCLUDED
|
||||
#define LEXY_DETAIL_NTTP_STRING_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/assert.hpp>
|
||||
#include <lexy/_detail/config.hpp>
|
||||
#include <lexy/_detail/string_view.hpp>
|
||||
|
||||
#if LEXY_HAS_NTTP // string NTTP implementation
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <std::size_t N, typename CharT>
|
||||
struct string_literal
|
||||
{
|
||||
CharT string[N];
|
||||
|
||||
LEXY_CONSTEVAL string_literal(const CharT* str) : string{}
|
||||
{
|
||||
for (auto i = 0u; i != N; ++i)
|
||||
string[i] = str[i];
|
||||
}
|
||||
template <typename OtherCharT>
|
||||
LEXY_CONSTEVAL string_literal(const OtherCharT* str) : string{}
|
||||
{
|
||||
for (auto i = 0u; i != N; ++i)
|
||||
string[i] = CharT(str[i]);
|
||||
}
|
||||
|
||||
LEXY_CONSTEVAL auto size() const
|
||||
{
|
||||
return N;
|
||||
}
|
||||
};
|
||||
template <std::size_t N, typename CharT>
|
||||
string_literal(const CharT (&)[N]) -> string_literal<N - 1, CharT>;
|
||||
|
||||
template <auto Str>
|
||||
struct type_string
|
||||
{
|
||||
using char_type = std::decay_t<decltype(Str.string[0])>;
|
||||
|
||||
template <typename CharT>
|
||||
struct _lazy
|
||||
{
|
||||
static inline constexpr string_literal<N, CharT> str = Str.string;
|
||||
};
|
||||
|
||||
template <typename CharT = char_type>
|
||||
static LEXY_CONSTEVAL auto get()
|
||||
{
|
||||
if constexpr (std::is_same_v<CharT, char_type>)
|
||||
return basic_string_view<CharT>(Str.string, Str.size());
|
||||
else
|
||||
{
|
||||
constexpr auto str = _lazy<CharT>::str;
|
||||
return basic_string_viewCharT > (str.string, str.size());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <auto C>
|
||||
struct type_char
|
||||
{
|
||||
using char_type = std::decay_t<decltype(C)>;
|
||||
|
||||
template <typename CharT>
|
||||
static constexpr auto c = C;
|
||||
|
||||
template <typename CharT>
|
||||
static LEXY_CONSTEVAL auto get()
|
||||
{
|
||||
return basic_string_view<CharT>(&c<CharT>, 1);
|
||||
}
|
||||
};
|
||||
} // namespace lexy::_detail
|
||||
|
||||
# define LEXY_NTTP_STRING(Str) ::lexy::_detail::type_string<::lexy::_detail::string_literal(Str)>
|
||||
|
||||
#else // string<Cs...> implementation
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <typename CharT, CharT... Cs>
|
||||
struct type_string
|
||||
{
|
||||
using char_type = CharT;
|
||||
|
||||
template <typename OtherCharT>
|
||||
struct _lazy
|
||||
{
|
||||
static inline constexpr OtherCharT str[] = {OtherCharT(Cs)...};
|
||||
};
|
||||
|
||||
template <typename OtherCharT = char_type>
|
||||
static LEXY_CONSTEVAL auto get()
|
||||
{
|
||||
return basic_string_view<OtherCharT>(_lazy<OtherCharT>::str, sizeof...(Cs));
|
||||
}
|
||||
};
|
||||
|
||||
template <auto C>
|
||||
using type_char = type_string<std::decay_t<decltype(C)>, C>;
|
||||
} // namespace lexy::_detail
|
||||
|
||||
# if defined(__GNUC__) // string<Cs...> literal implementation
|
||||
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wpedantic"
|
||||
# ifdef __clang__
|
||||
# pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template"
|
||||
# endif
|
||||
|
||||
template <typename CharT, CharT... Cs>
|
||||
constexpr ::lexy::_detail::type_string<CharT, Cs...> operator""_lexy_string_udl()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
# define LEXY_NTTP_STRING(Str) decltype(Str##_lexy_string_udl)
|
||||
|
||||
# pragma GCC diagnostic pop
|
||||
|
||||
# else // string<Cs...> macro implementation
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <typename A, typename B>
|
||||
struct cat_;
|
||||
template <typename CharT, CharT... C1, CharT... C2>
|
||||
struct cat_<type_string<CharT, C1...>, type_string<CharT, C2...>>
|
||||
{
|
||||
using type = type_string<CharT, C1..., C2...>;
|
||||
};
|
||||
template <typename A, typename B>
|
||||
using cat = typename cat_<A, B>::type;
|
||||
|
||||
template <typename T, std::size_t Size, std::size_t MaxSize>
|
||||
struct check_size
|
||||
{
|
||||
static_assert(Size <= MaxSize, "string out of range");
|
||||
using type = T;
|
||||
};
|
||||
|
||||
} // namespace lexy::_detail
|
||||
|
||||
# define LEXY_NTTP_STRING_LENGTH(Str) (sizeof(Str) / sizeof(Str[0]) - 1)
|
||||
|
||||
// extract Ith character if not out of bounds
|
||||
# define LEXY_NTTP_STRING1(Str, I) \
|
||||
::std::conditional_t<(I < LEXY_NTTP_STRING_LENGTH(Str)), \
|
||||
::lexy::_detail::type_string< \
|
||||
::std::decay_t<decltype(Str[0])>, \
|
||||
(I >= LEXY_NTTP_STRING_LENGTH(Str) ? Str[0] : Str[I])>, \
|
||||
::lexy::_detail::type_string<::std::decay_t<decltype(Str[0])>>>
|
||||
|
||||
// recursively split the string in two
|
||||
# define LEXY_NTTP_STRING2(Str, I) \
|
||||
::lexy::_detail::cat<LEXY_NTTP_STRING1(Str, I), LEXY_NTTP_STRING1(Str, I + 1)>
|
||||
# define LEXY_NTTP_STRING4(Str, I) \
|
||||
::lexy::_detail::cat<LEXY_NTTP_STRING2(Str, I), LEXY_NTTP_STRING2(Str, I + 2)>
|
||||
# define LEXY_NTTP_STRING8(Str, I) \
|
||||
::lexy::_detail::cat<LEXY_NTTP_STRING4(Str, I), LEXY_NTTP_STRING4(Str, I + 4)>
|
||||
# define LEXY_NTTP_STRING16(Str, I) \
|
||||
::lexy::_detail::cat<LEXY_NTTP_STRING8(Str, I), LEXY_NTTP_STRING8(Str, I + 8)>
|
||||
# define LEXY_NTTP_STRING32(Str, I) \
|
||||
::lexy::_detail::cat<LEXY_NTTP_STRING16(Str, I), LEXY_NTTP_STRING16(Str, I + 16)>
|
||||
# define LEXY_NTTP_STRING64(Str, I) \
|
||||
::lexy::_detail::cat<LEXY_NTTP_STRING32(Str, I), LEXY_NTTP_STRING32(Str, I + 32)>
|
||||
# define LEXY_NTTP_STRING128(Str, I) \
|
||||
::lexy::_detail::cat<LEXY_NTTP_STRING64(Str, I), LEXY_NTTP_STRING64(Str, I + 64)>
|
||||
# define LEXY_NTTP_STRING256(Str, I) \
|
||||
::lexy::_detail::cat<LEXY_NTTP_STRING128(Str, I), LEXY_NTTP_STRING128(Str, I + 128)>
|
||||
|
||||
// instantiate with overflow check
|
||||
# define LEXY_NTTP_STRING(Str) \
|
||||
::lexy::_detail::check_size<LEXY_NTTP_STRING256(Str, 0), LEXY_NTTP_STRING_LENGTH(Str), \
|
||||
256>::type
|
||||
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // LEXY_DETAIL_NTTP_STRING_HPP_INCLUDED
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DETAIL_STATELESS_LAMBDA_HPP_INCLUDED
|
||||
#define LEXY_DETAIL_STATELESS_LAMBDA_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/config.hpp>
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <typename Lambda>
|
||||
struct stateless_lambda
|
||||
{
|
||||
static_assert(std::is_class_v<Lambda>);
|
||||
static_assert(std::is_empty_v<Lambda>);
|
||||
|
||||
static constexpr Lambda get()
|
||||
{
|
||||
if constexpr (std::is_default_constructible_v<Lambda>)
|
||||
{
|
||||
// We're using C++20, lambdas are default constructible.
|
||||
return Lambda();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're not having C++20; use a sequence of weird workarounds to legally construct a
|
||||
// Lambda object without invoking any constructors.
|
||||
// This works and is well-defined, but sadly not constexpr.
|
||||
// Taken from: https://www.youtube.com/watch?v=yTb6xz_FSkY
|
||||
|
||||
// We're defining two standard layout types that have a char as a common initial
|
||||
// sequence (as the Lambda is empty, it doesn't add anymore members to B).
|
||||
struct A
|
||||
{
|
||||
char member;
|
||||
};
|
||||
struct B : Lambda
|
||||
{
|
||||
char member;
|
||||
};
|
||||
static_assert(std::is_standard_layout_v<A> && std::is_standard_layout_v<B>);
|
||||
|
||||
// We put the two types in a union and initialize the a member, which we can do.
|
||||
union storage_t
|
||||
{
|
||||
A a;
|
||||
B b;
|
||||
} storage{};
|
||||
|
||||
// We can now take the address of member via b, as it is in the common initial sequence.
|
||||
auto char_ptr = &storage.b.member;
|
||||
// char_ptr is a pointer to the first member of B, so we can reinterpret_cast it to a
|
||||
// pointer to B.
|
||||
auto b_ptr = reinterpret_cast<B*>(char_ptr);
|
||||
// Now we're having a pointer to a B object, which can we can cast to the base class
|
||||
// Lambda.
|
||||
auto lambda_ptr = static_cast<Lambda*>(b_ptr);
|
||||
// Dereference the pointer to get the lambda object.
|
||||
return *lambda_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr decltype(auto) operator()(Args&&... args) const
|
||||
{
|
||||
return get()(LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
} // namespace lexy::_detail
|
||||
|
||||
#endif // LEXY_DETAIL_STATELESS_LAMBDA_HPP_INCLUDED
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DETAIL_STD_HPP_INCLUDED
|
||||
#define LEXY_DETAIL_STD_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/config.hpp>
|
||||
|
||||
#if defined(__GLIBCXX__)
|
||||
|
||||
namespace std
|
||||
{
|
||||
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
struct forward_iterator_tag;
|
||||
struct bidirectional_iterator_tag;
|
||||
_GLIBCXX_END_NAMESPACE_VERSION
|
||||
} // namespace std
|
||||
|
||||
#elif defined(_LIBCPP_VERSION)
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
struct forward_iterator_tag;
|
||||
struct bidirectional_iterator_tag;
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#else
|
||||
|
||||
// Forward declaring things in std is not allowed, but I'm willing to take the risk.
|
||||
|
||||
namespace std
|
||||
{
|
||||
struct forward_iterator_tag;
|
||||
struct bidirectional_iterator_tag;
|
||||
} // namespace std
|
||||
|
||||
#endif
|
||||
|
||||
#endif // LEXY_DETAIL_STD_HPP_INCLUDED
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DETAIL_STRING_VIEW_HPP_INCLUDED
|
||||
#define LEXY_DETAIL_STRING_VIEW_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/assert.hpp>
|
||||
#include <lexy/_detail/config.hpp>
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <typename CharT>
|
||||
class basic_string_view
|
||||
{
|
||||
public:
|
||||
using char_type = CharT;
|
||||
|
||||
//=== constructor ===//
|
||||
constexpr basic_string_view() noexcept : _begin(nullptr), _end(nullptr) {}
|
||||
|
||||
constexpr basic_string_view(const char_type* str) noexcept : _begin(str), _end(str)
|
||||
{
|
||||
while (*_end)
|
||||
++_end;
|
||||
}
|
||||
|
||||
constexpr basic_string_view(const char_type* ptr, std::size_t size) noexcept
|
||||
: _begin(ptr), _end(ptr + size)
|
||||
{}
|
||||
|
||||
constexpr basic_string_view(const char_type* begin, const char_type* end) noexcept
|
||||
: _begin(begin), _end(end)
|
||||
{}
|
||||
|
||||
//=== access ===//
|
||||
using iterator = const char_type*;
|
||||
|
||||
constexpr iterator begin() const noexcept
|
||||
{
|
||||
return _begin;
|
||||
}
|
||||
constexpr iterator end() const noexcept
|
||||
{
|
||||
return _end;
|
||||
}
|
||||
|
||||
constexpr bool empty() const noexcept
|
||||
{
|
||||
return _begin == _end;
|
||||
}
|
||||
constexpr std::size_t size() const noexcept
|
||||
{
|
||||
return static_cast<std::size_t>(_end - _begin);
|
||||
}
|
||||
constexpr std::size_t length() const noexcept
|
||||
{
|
||||
return static_cast<std::size_t>(_end - _begin);
|
||||
}
|
||||
|
||||
constexpr char_type operator[](std::size_t i) const noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(i <= size());
|
||||
return _begin[i];
|
||||
}
|
||||
constexpr char_type front() const noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(!empty());
|
||||
return *_begin;
|
||||
}
|
||||
constexpr char_type back() const noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(!empty());
|
||||
return *(_end - 1);
|
||||
}
|
||||
|
||||
constexpr const char_type* data() const noexcept
|
||||
{
|
||||
return _begin;
|
||||
}
|
||||
|
||||
//=== operations ===//
|
||||
static constexpr std::size_t npos = std::size_t(-1);
|
||||
|
||||
constexpr void remove_prefix(std::size_t n) noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(n <= size());
|
||||
_begin += n;
|
||||
}
|
||||
constexpr void remove_suffix(std::size_t n) noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(n <= size());
|
||||
_end -= n;
|
||||
}
|
||||
|
||||
constexpr basic_string_view substr(std::size_t pos, std::size_t length = npos) const noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(pos < size());
|
||||
if (length >= size() - pos)
|
||||
return basic_string_view(_begin + pos, _end);
|
||||
else
|
||||
return basic_string_view(_begin + pos, _begin + pos + length);
|
||||
}
|
||||
constexpr std::size_t find(basic_string_view str, std::size_t pos = 0) const noexcept
|
||||
{
|
||||
for (auto i = pos; i < length(); ++i)
|
||||
{
|
||||
if (substr(i, str.length()) == str)
|
||||
return i;
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
constexpr std::size_t find(CharT c, std::size_t pos = 0) const noexcept
|
||||
{
|
||||
return find(basic_string_view(&c, 1), pos);
|
||||
}
|
||||
|
||||
//=== comparison ===//
|
||||
friend constexpr bool operator==(basic_string_view<CharT> lhs,
|
||||
basic_string_view<CharT> rhs) noexcept
|
||||
{
|
||||
if (lhs.size() != rhs.size())
|
||||
return false;
|
||||
|
||||
for (auto a = lhs.begin(), b = rhs.begin(); a != lhs.end(); ++a, ++b)
|
||||
if (*a != *b)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend constexpr bool operator!=(basic_string_view<CharT> lhs,
|
||||
basic_string_view<CharT> rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
protected:
|
||||
const CharT* _begin;
|
||||
const CharT* _end;
|
||||
};
|
||||
using string_view = basic_string_view<char>;
|
||||
} // namespace lexy::_detail
|
||||
|
||||
#endif // LEXY_DETAIL_STRING_VIEW_HPP_INCLUDED
|
||||
@@ -1,88 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DETAIL_TYPE_NAME_HPP_INCLUDED
|
||||
#define LEXY_DETAIL_TYPE_NAME_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/config.hpp>
|
||||
#include <lexy/_detail/detect.hpp>
|
||||
#include <lexy/_detail/string_view.hpp>
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <typename T>
|
||||
using _detect_name_f = decltype(T::name());
|
||||
template <typename T>
|
||||
using _detect_name_v = decltype(T::name);
|
||||
|
||||
template <typename T>
|
||||
constexpr auto _type_name_impl()
|
||||
{
|
||||
#if defined(__clang__)
|
||||
|
||||
constexpr auto prefix = string_view("auto lexy::_detail::_type_name_impl() [T = ");
|
||||
constexpr auto suffix = string_view("]");
|
||||
|
||||
auto function = string_view(__PRETTY_FUNCTION__);
|
||||
function.remove_prefix(prefix.length());
|
||||
function.remove_suffix(suffix.length());
|
||||
return function;
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
constexpr auto prefix
|
||||
= string_view("constexpr auto lexy::_detail::_type_name_impl() [with T = ");
|
||||
constexpr auto suffix = string_view("]");
|
||||
|
||||
auto function = string_view(__PRETTY_FUNCTION__);
|
||||
function.remove_prefix(prefix.length());
|
||||
function.remove_suffix(suffix.length());
|
||||
return function;
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
constexpr auto prefix = string_view("auto __cdecl lexy::_detail::_type_name_impl<");
|
||||
constexpr auto suffix = string_view(">(void)");
|
||||
|
||||
auto function = string_view(__FUNCSIG__);
|
||||
function.remove_prefix(prefix.length());
|
||||
function.remove_suffix(suffix.length());
|
||||
return function;
|
||||
|
||||
#else
|
||||
|
||||
static_assert(_detail::error<T>,
|
||||
"require T::name() or T::name on this compiler to get the name of a type");
|
||||
return "";
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
LEXY_CONSTEVAL string_view type_name(int namespace_count = 1)
|
||||
{
|
||||
if constexpr (_detail::is_detected<_detect_name_f, T>)
|
||||
return string_view(T::name());
|
||||
else if constexpr (_detail::is_detected<_detect_name_v, T>)
|
||||
return string_view(T::name);
|
||||
else
|
||||
{
|
||||
auto name = _type_name_impl<T>();
|
||||
LEXY_ASSERT(name.find('<') == string_view::npos || namespace_count == 0,
|
||||
"cannot strip namespaces from template instantiations");
|
||||
|
||||
for (; namespace_count > 0; --namespace_count)
|
||||
{
|
||||
auto pos = name.find("::");
|
||||
if (pos == string_view::npos)
|
||||
break;
|
||||
name.remove_prefix(pos + 2);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
} // namespace lexy::_detail
|
||||
|
||||
#endif // LEXY_DETAIL_TYPE_NAME_HPP_INCLUDED
|
||||
|
||||
@@ -1,593 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_CALLBACK_HPP_INCLUDED
|
||||
#define LEXY_CALLBACK_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/config.hpp>
|
||||
#include <lexy/_detail/invoke.hpp>
|
||||
#include <lexy/dsl/member.hpp>
|
||||
#include <lexy/encoding.hpp>
|
||||
#include <lexy/lexeme.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename Fn>
|
||||
struct _fn_holder
|
||||
{
|
||||
Fn fn;
|
||||
|
||||
constexpr explicit _fn_holder(Fn fn) : fn(fn) {}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr auto operator()(Args&&... args) const
|
||||
-> decltype(_detail::invoke(fn, LEXY_FWD(args)...))
|
||||
{
|
||||
return _detail::invoke(fn, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
using _fn_as_base = std::conditional_t<std::is_class_v<Fn>, Fn, _fn_holder<Fn>>;
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename ReturnType, typename... Fns>
|
||||
struct _callback : _fn_as_base<Fns>...
|
||||
{
|
||||
using return_type = ReturnType;
|
||||
|
||||
LEXY_CONSTEVAL explicit _callback(Fns... fns) : _fn_as_base<Fns>(fns)... {}
|
||||
|
||||
using _fn_as_base<Fns>::operator()...;
|
||||
};
|
||||
|
||||
/// Creates a callback.
|
||||
template <typename ReturnType = void, typename... Fns>
|
||||
LEXY_CONSTEVAL auto callback(Fns&&... fns)
|
||||
{
|
||||
static_assert(((std::is_pointer_v<std::decay_t<Fns>> //
|
||||
|| std::is_member_pointer_v<std::decay_t<Fns>> //
|
||||
|| std::is_empty_v<std::decay_t<Fns>>)&&...),
|
||||
"only capture-less lambdas are allowed in a callback");
|
||||
return _callback<ReturnType, std::decay_t<Fns>...>(LEXY_FWD(fns)...);
|
||||
}
|
||||
|
||||
/// Invokes a callback into a result.
|
||||
template <typename Result, typename ErrorOrValue, typename Callback, typename... Args>
|
||||
constexpr Result invoke_as_result(ErrorOrValue tag, Callback&& callback, Args&&... args)
|
||||
{
|
||||
using callback_t = std::decay_t<Callback>;
|
||||
using return_type = typename callback_t::return_type;
|
||||
|
||||
if constexpr (std::is_same_v<return_type, void>)
|
||||
{
|
||||
LEXY_FWD(callback)(LEXY_FWD(args)...);
|
||||
return Result(tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result(tag, LEXY_FWD(callback)(LEXY_FWD(args)...));
|
||||
}
|
||||
}
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename T, typename Callback>
|
||||
class _sink
|
||||
{
|
||||
public:
|
||||
using return_type = T;
|
||||
|
||||
constexpr explicit _sink(Callback cb) : _value(), _cb(cb) {}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr void operator()(Args&&... args)
|
||||
{
|
||||
// We pass the value and other arguments to the internal callback.
|
||||
_cb(_value, LEXY_FWD(args)...);
|
||||
}
|
||||
|
||||
constexpr T&& finish() &&
|
||||
{
|
||||
return LEXY_MOV(_value);
|
||||
}
|
||||
|
||||
private:
|
||||
T _value;
|
||||
LEXY_EMPTY_MEMBER Callback _cb;
|
||||
};
|
||||
|
||||
template <typename T, typename... Fns>
|
||||
class _sink_callback
|
||||
{
|
||||
public:
|
||||
LEXY_CONSTEVAL explicit _sink_callback(Fns... fns) : _cb(fns...) {}
|
||||
|
||||
constexpr auto sink() const
|
||||
{
|
||||
return _sink<T, _callback<void, Fns...>>(_cb);
|
||||
}
|
||||
|
||||
private:
|
||||
LEXY_EMPTY_MEMBER _callback<void, Fns...> _cb;
|
||||
};
|
||||
|
||||
/// Creates a sink callback.
|
||||
template <typename T, typename... Fns>
|
||||
LEXY_CONSTEVAL auto sink(Fns&&... fns)
|
||||
{
|
||||
static_assert(((std::is_pointer_v<
|
||||
std::decay_t<Fns>> || std::is_empty_v<std::decay_t<Fns>>)&&...),
|
||||
"only capture-less lambdas are allowed in a callback");
|
||||
return _sink_callback<T, std::decay_t<Fns>...>(LEXY_FWD(fns)...);
|
||||
}
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename First, typename Second>
|
||||
struct _compose
|
||||
{
|
||||
LEXY_EMPTY_MEMBER First first;
|
||||
LEXY_EMPTY_MEMBER Second second;
|
||||
|
||||
using return_type = typename Second::return_type;
|
||||
|
||||
template <typename... Args>
|
||||
constexpr auto operator()(Args&&... args) const
|
||||
-> std::decay_t<decltype(first(LEXY_FWD(args)...), LEXY_DECLVAL(return_type))>
|
||||
{
|
||||
return second(first(LEXY_FWD(args)...));
|
||||
}
|
||||
};
|
||||
|
||||
/// Composes two callbacks.
|
||||
template <typename First, typename Second, typename = typename First::return_type,
|
||||
typename = typename Second::return_type>
|
||||
LEXY_CONSTEVAL auto operator|(First first, Second second)
|
||||
{
|
||||
return _compose<First, Second>{LEXY_MOV(first), LEXY_MOV(second)};
|
||||
}
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct _noop
|
||||
{
|
||||
using return_type = void;
|
||||
|
||||
constexpr auto sink() const
|
||||
{
|
||||
// We don't need a separate type, noop itself can have the required functions.
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr void operator()(const Args&...) const
|
||||
{}
|
||||
|
||||
constexpr void finish() && {}
|
||||
};
|
||||
|
||||
/// A callback with sink that does nothing.
|
||||
inline constexpr auto noop = _noop{};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename T>
|
||||
struct _fwd
|
||||
{
|
||||
using return_type = T;
|
||||
|
||||
constexpr T operator()(T&& t) const
|
||||
{
|
||||
return LEXY_MOV(t);
|
||||
}
|
||||
constexpr T operator()(const T& t) const
|
||||
{
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
/// A callback that just forwards an existing object.
|
||||
template <typename T>
|
||||
constexpr auto forward = _fwd<T>{};
|
||||
|
||||
template <typename T>
|
||||
struct _construct
|
||||
{
|
||||
using return_type = T;
|
||||
|
||||
constexpr T operator()(T&& t) const
|
||||
{
|
||||
return LEXY_MOV(t);
|
||||
}
|
||||
constexpr T operator()(const T& t) const
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr T operator()(Args&&... args) const
|
||||
{
|
||||
if constexpr (std::is_constructible_v<T, Args&&...>)
|
||||
return T(LEXY_FWD(args)...);
|
||||
else
|
||||
return T{LEXY_FWD(args)...};
|
||||
}
|
||||
};
|
||||
|
||||
/// A callback that constructs an object of type T by forwarding the arguments.
|
||||
template <typename T>
|
||||
constexpr auto construct = _construct<T>{};
|
||||
|
||||
template <typename T, typename PtrT>
|
||||
struct _new
|
||||
{
|
||||
using return_type = PtrT;
|
||||
|
||||
constexpr PtrT operator()(T&& t) const
|
||||
{
|
||||
auto ptr = new T(LEXY_MOV(t));
|
||||
return PtrT(ptr);
|
||||
}
|
||||
constexpr PtrT operator()(const T& t) const
|
||||
{
|
||||
auto ptr = new T(t);
|
||||
return PtrT(ptr);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr PtrT operator()(Args&&... args) const
|
||||
{
|
||||
if constexpr (std::is_constructible_v<T, Args&&...>)
|
||||
{
|
||||
auto ptr = new T(LEXY_FWD(args)...);
|
||||
return PtrT(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ptr = new T{LEXY_FWD(args)...};
|
||||
return PtrT(ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// A callback that constructs an object of type T on the heap by forwarding the arguments.
|
||||
template <typename T, typename PtrT = T*>
|
||||
constexpr auto new_ = _new<T, PtrT>{};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename T>
|
||||
struct _list
|
||||
{
|
||||
using return_type = T;
|
||||
|
||||
template <typename... Args>
|
||||
constexpr T operator()(Args&&... args) const
|
||||
{
|
||||
// Use the initializer_list constructor.
|
||||
return T{LEXY_FWD(args)...};
|
||||
}
|
||||
|
||||
struct _sink
|
||||
{
|
||||
T _result;
|
||||
|
||||
using return_type = T;
|
||||
|
||||
void operator()(const typename T::value_type& obj)
|
||||
{
|
||||
_result.push_back(obj);
|
||||
}
|
||||
void operator()(typename T::value_type&& obj)
|
||||
{
|
||||
_result.push_back(LEXY_MOV(obj));
|
||||
}
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args) -> std::enable_if_t<(sizeof...(Args) > 1)>
|
||||
{
|
||||
_result.emplace_back(LEXY_FWD(args)...);
|
||||
}
|
||||
|
||||
T&& finish() &&
|
||||
{
|
||||
return LEXY_MOV(_result);
|
||||
}
|
||||
};
|
||||
constexpr auto sink() const
|
||||
{
|
||||
return _sink{};
|
||||
}
|
||||
};
|
||||
|
||||
/// A callback with sink that creates a list of things (e.g. a `std::vector`, `std::list`, etc.).
|
||||
/// As a callback, it forwards the arguments to the initializer list constructor.
|
||||
/// As a sink, it repeatedly calls `push_back()` and `emplace_back()`.
|
||||
template <typename T>
|
||||
constexpr auto as_list = _list<T>{};
|
||||
|
||||
template <typename T>
|
||||
struct _collection
|
||||
{
|
||||
using return_type = T;
|
||||
|
||||
template <typename... Args>
|
||||
constexpr T operator()(Args&&... args) const
|
||||
{
|
||||
// Use the initializer_list constructor.
|
||||
return T{LEXY_FWD(args)...};
|
||||
}
|
||||
|
||||
struct _sink
|
||||
{
|
||||
T _result;
|
||||
|
||||
using return_type = T;
|
||||
|
||||
void operator()(const typename T::value_type& obj)
|
||||
{
|
||||
_result.insert(obj);
|
||||
}
|
||||
void operator()(typename T::value_type&& obj)
|
||||
{
|
||||
_result.insert(LEXY_MOV(obj));
|
||||
}
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args) -> std::enable_if_t<(sizeof...(Args) > 1)>
|
||||
{
|
||||
_result.emplace(LEXY_FWD(args)...);
|
||||
}
|
||||
|
||||
T&& finish() &&
|
||||
{
|
||||
return LEXY_MOV(_result);
|
||||
}
|
||||
};
|
||||
constexpr auto sink() const
|
||||
{
|
||||
return _sink{};
|
||||
}
|
||||
};
|
||||
|
||||
/// A callback with sink that creates an unordered collection of things (e.g. a `std::set`,
|
||||
/// `std::unordered_map`, etc.). As a callback, it forwards the arguments to the initializer list
|
||||
/// constructor. As a sink, it repeatedly calls `insert()` and `emplace()`.
|
||||
template <typename T>
|
||||
constexpr auto as_collection = _collection<T>{};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename MemPtr>
|
||||
struct _mem_ptr_type_impl;
|
||||
template <typename T, typename ClassT>
|
||||
struct _mem_ptr_type_impl<T ClassT::*>
|
||||
{
|
||||
using class_type = ClassT;
|
||||
using member_type = T;
|
||||
};
|
||||
|
||||
template <auto MemPtr>
|
||||
using _mem_ptr_class_type = typename _mem_ptr_type_impl<decltype(MemPtr)>::class_type;
|
||||
template <auto MemPtr>
|
||||
using _mem_ptr_member_type = typename _mem_ptr_type_impl<decltype(MemPtr)>::member_type;
|
||||
|
||||
template <typename T>
|
||||
struct _as_aggregate
|
||||
{
|
||||
using return_type = T;
|
||||
static_assert(std::is_aggregate_v<return_type>);
|
||||
|
||||
template <typename Fn, typename H, typename... Tail>
|
||||
constexpr void _set(T& result, lexy::member<Fn>, H&& value, Tail&&... tail) const
|
||||
{
|
||||
Fn()(result, LEXY_FWD(value));
|
||||
if constexpr (sizeof...(Tail) > 0)
|
||||
_set(result, LEXY_FWD(tail)...);
|
||||
}
|
||||
|
||||
template <typename Fn, typename... Args>
|
||||
constexpr auto operator()(lexy::member<Fn> member, Args&&... args) const
|
||||
{
|
||||
static_assert(sizeof...(Args) % 2 == 1, "missing dsl::member rules");
|
||||
|
||||
T result{};
|
||||
_set(result, member, LEXY_FWD(args)...);
|
||||
return result;
|
||||
}
|
||||
template <typename... Args>
|
||||
constexpr auto operator()(return_type&& result, Args&&... args) const
|
||||
{
|
||||
static_assert(sizeof...(Args) % 2 == 0, "missing dsl::member rules");
|
||||
|
||||
_set(result, LEXY_FWD(args)...);
|
||||
return LEXY_MOV(result);
|
||||
}
|
||||
|
||||
struct _sink
|
||||
{
|
||||
T _result{};
|
||||
|
||||
using return_type = T;
|
||||
|
||||
template <typename Fn, typename Value>
|
||||
constexpr void operator()(lexy::member<Fn>, Value&& value)
|
||||
{
|
||||
Fn()(_result, LEXY_FWD(value));
|
||||
}
|
||||
|
||||
constexpr auto&& finish() &&
|
||||
{
|
||||
return LEXY_MOV(_result);
|
||||
}
|
||||
};
|
||||
constexpr auto sink() const
|
||||
{
|
||||
return _sink{};
|
||||
}
|
||||
};
|
||||
|
||||
/// A callback with sink that creates an aggregate.
|
||||
template <typename T>
|
||||
constexpr auto as_aggregate = _as_aggregate<T>{};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename String>
|
||||
using _string_char_type = std::decay_t<decltype(LEXY_DECLVAL(String)[0])>;
|
||||
|
||||
template <typename String, typename Encoding>
|
||||
struct _as_string
|
||||
{
|
||||
using return_type = String;
|
||||
using _char_type = _string_char_type<String>;
|
||||
|
||||
constexpr String operator()(String&& str) const
|
||||
{
|
||||
return LEXY_MOV(str);
|
||||
}
|
||||
constexpr String operator()(const String& str) const
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
constexpr auto operator()(const CharT* str, std::size_t length) const
|
||||
-> decltype(String(str, length))
|
||||
{
|
||||
return String(str, length);
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
constexpr String operator()(lexeme<Reader> lex) const
|
||||
{
|
||||
using iterator = typename lexeme<Reader>::iterator;
|
||||
if constexpr (std::is_pointer_v<iterator>)
|
||||
{
|
||||
static_assert(lexy::char_type_compatible_with_reader<Reader, _char_type>,
|
||||
"cannot convert lexeme to this string type");
|
||||
|
||||
if constexpr (std::is_same_v<_char_type, typename Reader::encoding::char_type>)
|
||||
return String(lex.data(), lex.size());
|
||||
else
|
||||
return String(reinterpret_cast<const _char_type*>(lex.data()), lex.size());
|
||||
}
|
||||
else
|
||||
return String(lex.begin(), lex.end());
|
||||
}
|
||||
|
||||
constexpr String operator()(code_point cp) const
|
||||
{
|
||||
typename Encoding::char_type buffer[4] = {};
|
||||
auto size = Encoding::encode_code_point(cp, buffer, 4);
|
||||
|
||||
if constexpr (std::is_same_v<_char_type, typename Encoding::char_type>)
|
||||
return (*this)(buffer, size);
|
||||
else
|
||||
return (*this)(reinterpret_cast<const _char_type*>(buffer), size);
|
||||
}
|
||||
|
||||
struct _sink
|
||||
{
|
||||
String _result;
|
||||
|
||||
using return_type = String;
|
||||
|
||||
template <typename CharT>
|
||||
auto operator()(CharT c) -> decltype(_result.push_back(c))
|
||||
{
|
||||
return _result.push_back(c);
|
||||
}
|
||||
|
||||
void operator()(const String& str)
|
||||
{
|
||||
_result.append(str);
|
||||
}
|
||||
void operator()(String&& str)
|
||||
{
|
||||
_result.append(LEXY_MOV(str));
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
auto operator()(const CharT* str, std::size_t length)
|
||||
-> decltype(_result.append(str, length))
|
||||
{
|
||||
return _result.append(str, length);
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
void operator()(lexeme<Reader> lex)
|
||||
{
|
||||
using iterator = typename lexeme<Reader>::iterator;
|
||||
if constexpr (std::is_pointer_v<iterator>)
|
||||
{
|
||||
static_assert(lexy::char_type_compatible_with_reader<Reader, _char_type>,
|
||||
"cannot convert lexeme to this string type");
|
||||
_result.append(reinterpret_cast<const _char_type*>(lex.data()), lex.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
_result.append(lex.begin(), lex.end());
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(code_point cp)
|
||||
{
|
||||
typename Encoding::char_type buffer[4] = {};
|
||||
auto size = Encoding::encode_code_point(cp, buffer, 4);
|
||||
(*this)(reinterpret_cast<const _char_type*>(buffer), size);
|
||||
}
|
||||
|
||||
String&& finish() &&
|
||||
{
|
||||
return LEXY_MOV(_result);
|
||||
}
|
||||
};
|
||||
constexpr auto sink() const
|
||||
{
|
||||
return _sink{};
|
||||
}
|
||||
};
|
||||
|
||||
/// A callback with sink that creates a string (e.g. `std::string`).
|
||||
/// As a callback, it converts a lexeme into the string.
|
||||
/// As a sink, it repeatedly calls `.push_back()` for individual characters,
|
||||
/// or `.append()` for lexemes or other strings.
|
||||
template <typename String, typename Encoding = deduce_encoding<_string_char_type<String>>>
|
||||
constexpr auto as_string = _as_string<String, Encoding>{};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename T>
|
||||
struct _int
|
||||
{
|
||||
using return_type = T;
|
||||
|
||||
template <typename Integer>
|
||||
constexpr T operator()(const Integer& value) const
|
||||
{
|
||||
return T(value);
|
||||
}
|
||||
template <typename Integer>
|
||||
constexpr T operator()(int sign, const Integer& value) const
|
||||
{
|
||||
return T(sign * value);
|
||||
}
|
||||
};
|
||||
|
||||
// A callback that takes an optional sign and an integer and produces the signed integer.
|
||||
template <typename T>
|
||||
constexpr auto as_integer = _int<T>{};
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_CALLBACK_HPP_INCLUDED
|
||||
@@ -1,53 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_HPP_INCLUDED
|
||||
#define LEXY_DSL_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/alternative.hpp>
|
||||
#include <lexy/dsl/any.hpp>
|
||||
#include <lexy/dsl/ascii.hpp>
|
||||
#include <lexy/dsl/bom.hpp>
|
||||
#include <lexy/dsl/brackets.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
#include <lexy/dsl/capture.hpp>
|
||||
#include <lexy/dsl/choice.hpp>
|
||||
#include <lexy/dsl/code_point.hpp>
|
||||
#include <lexy/dsl/combination.hpp>
|
||||
#include <lexy/dsl/context.hpp>
|
||||
#include <lexy/dsl/delimited.hpp>
|
||||
#include <lexy/dsl/digit.hpp>
|
||||
#include <lexy/dsl/encode.hpp>
|
||||
#include <lexy/dsl/eof.hpp>
|
||||
#include <lexy/dsl/error.hpp>
|
||||
#include <lexy/dsl/if.hpp>
|
||||
#include <lexy/dsl/integer.hpp>
|
||||
#include <lexy/dsl/label.hpp>
|
||||
#include <lexy/dsl/list.hpp>
|
||||
#include <lexy/dsl/literal.hpp>
|
||||
#include <lexy/dsl/lookahead.hpp>
|
||||
#include <lexy/dsl/loop.hpp>
|
||||
#include <lexy/dsl/match.hpp>
|
||||
#include <lexy/dsl/member.hpp>
|
||||
#include <lexy/dsl/minus.hpp>
|
||||
#include <lexy/dsl/newline.hpp>
|
||||
#include <lexy/dsl/not.hpp>
|
||||
#include <lexy/dsl/option.hpp>
|
||||
#include <lexy/dsl/peek.hpp>
|
||||
#include <lexy/dsl/position.hpp>
|
||||
#include <lexy/dsl/production.hpp>
|
||||
#include <lexy/dsl/punctuator.hpp>
|
||||
#include <lexy/dsl/return.hpp>
|
||||
#include <lexy/dsl/separator.hpp>
|
||||
#include <lexy/dsl/sequence.hpp>
|
||||
#include <lexy/dsl/sign.hpp>
|
||||
#include <lexy/dsl/switch.hpp>
|
||||
#include <lexy/dsl/times.hpp>
|
||||
#include <lexy/dsl/until.hpp>
|
||||
#include <lexy/dsl/value.hpp>
|
||||
#include <lexy/dsl/while.hpp>
|
||||
#include <lexy/dsl/whitespace.hpp>
|
||||
|
||||
#endif // LEXY_DSL_HPP_INCLUDED
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_ALTERNATIVE_HPP_INCLUDED
|
||||
#define LEXY_DSL_ALTERNATIVE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct exhausted_alternatives
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "exhausted alternatives";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename... P>
|
||||
struct _alt : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = true;
|
||||
|
||||
struct matcher
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
return (P::matcher::match(reader) || ...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if (matcher::match(reader))
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
{
|
||||
auto e = lexy::make_error<Reader, lexy::exhausted_alternatives>(reader.cur());
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename R, typename S>
|
||||
LEXY_CONSTEVAL auto operator/(R, S)
|
||||
{
|
||||
static_assert(lexy::is_pattern<R>);
|
||||
static_assert(lexy::is_pattern<S>);
|
||||
return _alt<R, S>{};
|
||||
}
|
||||
template <typename... R, typename S>
|
||||
LEXY_CONSTEVAL auto operator/(_alt<R...>, S)
|
||||
{
|
||||
static_assert(lexy::is_pattern<S>);
|
||||
return _alt<R..., S>{};
|
||||
}
|
||||
template <typename R, typename... S>
|
||||
LEXY_CONSTEVAL auto operator/(R, _alt<S...>)
|
||||
{
|
||||
static_assert(lexy::is_pattern<R>);
|
||||
return _alt<R, S...>{};
|
||||
}
|
||||
template <typename... R, typename... S>
|
||||
LEXY_CONSTEVAL auto operator/(_alt<R...>, _alt<S...>)
|
||||
{
|
||||
return _alt<R..., S...>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_ALTERNATIVE_HPP_INCLUDED
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_ANY_HPP_INCLUDED
|
||||
#define LEXY_DSL_ANY_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/assert.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
struct _any : atom_base<_any>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
while (!reader.eof())
|
||||
reader.bump();
|
||||
return true;
|
||||
}
|
||||
template <typename Encoding, typename Iterator>
|
||||
LEXY_DSL_FUNC bool match(lexy::_detail::range_reader<Encoding, Iterator, Iterator>& reader)
|
||||
{
|
||||
reader._make_eof();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC void error(const Reader&, typename Reader::iterator);
|
||||
};
|
||||
|
||||
/// Matches anything and consumes all remaining characters.
|
||||
constexpr auto any = _any{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_ANY_HPP_INCLUDED
|
||||
@@ -1,274 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_ASCII_HPP_INCLUDED
|
||||
#define LEXY_DSL_ASCII_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexyd::ascii
|
||||
{
|
||||
template <typename Predicate>
|
||||
struct _ascii : atom_base<_ascii<Predicate>>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
if (!Predicate::template match<typename Reader::encoding>(reader.peek()))
|
||||
return false;
|
||||
reader.bump();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader&, typename Reader::iterator pos)
|
||||
{
|
||||
return lexy::make_error<Reader, lexy::expected_char_class>(pos, Predicate::name());
|
||||
}
|
||||
|
||||
constexpr bool operator()(char c) const
|
||||
{
|
||||
return Predicate::template match<lexy::ascii_encoding>(int(c));
|
||||
}
|
||||
};
|
||||
|
||||
//=== control ===//
|
||||
struct _control
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.control";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return (Encoding::to_int_type(0x0) <= c && c <= Encoding::to_int_type(0x1F))
|
||||
|| c == Encoding::to_int_type(0x7F);
|
||||
}
|
||||
};
|
||||
inline constexpr auto control = _ascii<_control>{};
|
||||
|
||||
//=== whitespace ===//
|
||||
struct _blank
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.blank";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return c == Encoding::to_int_type(' ') || c == Encoding::to_int_type('\t');
|
||||
}
|
||||
};
|
||||
inline constexpr auto blank = _ascii<_blank>{};
|
||||
|
||||
struct _newline
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.newline";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return c == Encoding::to_int_type('\n') || c == Encoding::to_int_type('\r');
|
||||
}
|
||||
};
|
||||
inline constexpr auto newline = _ascii<_newline>{};
|
||||
|
||||
struct _other_space
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.other-space";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return c == Encoding::to_int_type('\f') || c == Encoding::to_int_type('\v');
|
||||
}
|
||||
};
|
||||
inline constexpr auto other_space = _ascii<_other_space>{};
|
||||
|
||||
struct _space
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.space";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return _blank::template match<Encoding>(c) || _newline::template match<Encoding>(c)
|
||||
|| _other_space::template match<Encoding>(c);
|
||||
}
|
||||
};
|
||||
inline constexpr auto space = _ascii<_space>{};
|
||||
|
||||
//=== alpha ===//
|
||||
struct _lower
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.lower";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return c >= Encoding::to_int_type('a') && c <= Encoding::to_int_type('z');
|
||||
}
|
||||
};
|
||||
inline constexpr auto lower = _ascii<_lower>{};
|
||||
|
||||
struct _upper
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.upper";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return c >= Encoding::to_int_type('A') && c <= Encoding::to_int_type('Z');
|
||||
}
|
||||
};
|
||||
inline constexpr auto upper = _ascii<_upper>{};
|
||||
|
||||
struct _alpha
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.alpha";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return _lower::template match<Encoding>(c) || _upper::template match<Encoding>(c);
|
||||
}
|
||||
};
|
||||
inline constexpr auto alpha = _ascii<_alpha>{};
|
||||
|
||||
//=== digit ===//
|
||||
struct _digit
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.digit";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return c >= Encoding::to_int_type('0') && c <= Encoding::to_int_type('9');
|
||||
}
|
||||
};
|
||||
inline constexpr auto digit = _ascii<_digit>{};
|
||||
|
||||
struct _alnum
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.alnum";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return _lower::template match<Encoding>(c) || _upper::template match<Encoding>(c)
|
||||
|| _digit::template match<Encoding>(c);
|
||||
}
|
||||
};
|
||||
inline constexpr auto alnum = _ascii<_alnum>{};
|
||||
|
||||
//=== punct ===//
|
||||
struct _punct
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.punct";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return c == Encoding::to_int_type('!') || c == Encoding::to_int_type('"')
|
||||
|| c == Encoding::to_int_type('#') || c == Encoding::to_int_type('$')
|
||||
|| c == Encoding::to_int_type('%') || c == Encoding::to_int_type('&')
|
||||
|| c == Encoding::to_int_type('\'') || c == Encoding::to_int_type('(')
|
||||
|| c == Encoding::to_int_type(')') || c == Encoding::to_int_type('*')
|
||||
|| c == Encoding::to_int_type('+') || c == Encoding::to_int_type(',')
|
||||
|| c == Encoding::to_int_type('-') || c == Encoding::to_int_type('.')
|
||||
|| c == Encoding::to_int_type('/') || c == Encoding::to_int_type(':')
|
||||
|| c == Encoding::to_int_type(';') || c == Encoding::to_int_type('<')
|
||||
|| c == Encoding::to_int_type('=') || c == Encoding::to_int_type('>')
|
||||
|| c == Encoding::to_int_type('?') || c == Encoding::to_int_type('@')
|
||||
|| c == Encoding::to_int_type('[') || c == Encoding::to_int_type('\\')
|
||||
|| c == Encoding::to_int_type(']') || c == Encoding::to_int_type('^')
|
||||
|| c == Encoding::to_int_type('_') || c == Encoding::to_int_type('`')
|
||||
|| c == Encoding::to_int_type('{') || c == Encoding::to_int_type('|')
|
||||
|| c == Encoding::to_int_type('}') || c == Encoding::to_int_type('~');
|
||||
}
|
||||
};
|
||||
inline constexpr auto punct = _ascii<_punct>{};
|
||||
|
||||
//=== categories ===//
|
||||
struct _graph
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.graph";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return _lower::template match<Encoding>(c) || _upper::template match<Encoding>(c)
|
||||
|| _digit::template match<Encoding>(c) || _punct::template match<Encoding>(c);
|
||||
}
|
||||
};
|
||||
inline constexpr auto graph = _ascii<_graph>{};
|
||||
|
||||
struct _print
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII.print";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return c == Encoding::to_int_type(' ') || _lower::template match<Encoding>(c)
|
||||
|| _upper::template match<Encoding>(c) || _digit::template match<Encoding>(c)
|
||||
|| _punct::template match<Encoding>(c);
|
||||
}
|
||||
};
|
||||
inline constexpr auto print = _ascii<_print>{};
|
||||
|
||||
struct _char
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "ASCII";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return Encoding::to_int_type(0) <= c && c <= Encoding::to_int_type(0x7F);
|
||||
}
|
||||
};
|
||||
inline constexpr auto character = _ascii<_char>{};
|
||||
} // namespace lexyd::ascii
|
||||
|
||||
#endif // LEXY_DSL_ASCII_HPP_INCLUDED
|
||||
@@ -1,168 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_BASE_HPP_INCLUDED
|
||||
#define LEXY_DSL_BASE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/assert.hpp>
|
||||
#include <lexy/_detail/config.hpp>
|
||||
#include <lexy/error.hpp>
|
||||
#include <lexy/input/base.hpp>
|
||||
|
||||
#define LEXY_DSL_FUNC LEXY_FORCE_INLINE static constexpr
|
||||
|
||||
//=== rule ===//
|
||||
#if 0
|
||||
struct Handler
|
||||
{
|
||||
using result_type = ...;
|
||||
|
||||
/// Returns a sub-handler for an inner production.
|
||||
template <typename Production, typename Reader>
|
||||
ParseHandler sub_handler(const Reader& reader);
|
||||
|
||||
/// Returns a sink to construct a list.
|
||||
Sink list_sink();
|
||||
|
||||
/// Called when an error ocurred.
|
||||
template <typename Reader, typename Error>
|
||||
result_type error(const Reader& reader, Error&& error) &&;
|
||||
|
||||
/// Called when parsing was succesful.
|
||||
template <typename ... Args>
|
||||
result_type value(Args&&... args) &&;
|
||||
};
|
||||
|
||||
struct Rule : rule_base
|
||||
{
|
||||
// Whether or not the rule has a matcher.
|
||||
// A rule with matcher is a pattern.
|
||||
static constexpr bool has_matcher;
|
||||
|
||||
struct matcher
|
||||
{
|
||||
// If matches, consumes characters from reader and return true.
|
||||
// If it doesn't match, leave reader as-is and return false.
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader);
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename ... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args)
|
||||
-> typename Handler::result_type
|
||||
{
|
||||
if (/* match reader */)
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., /* rule arguments */);
|
||||
else
|
||||
return LEXY_MOV(handler).error(reader, /* error */);
|
||||
}
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
||||
// We use a shorthand namespace to decrease symbol size.
|
||||
namespace lexyd
|
||||
{
|
||||
struct rule_base
|
||||
{};
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
namespace dsl = lexyd;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_rule = std::is_base_of_v<dsl::rule_base, T>;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_pattern = [] {
|
||||
if constexpr (is_rule<T>)
|
||||
return T::has_matcher;
|
||||
else
|
||||
return false;
|
||||
}();
|
||||
} // namespace lexy
|
||||
|
||||
//=== atom ===//
|
||||
#if 0
|
||||
class Atom : atom_base<Atom>
|
||||
{
|
||||
// Try to match and consume characters.
|
||||
// Returns true, if match succesful and leave reader after the consumed characters.
|
||||
// Otherwise, return false and leaves reader at the error position.
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader);
|
||||
|
||||
// Returns an error object describing the error.
|
||||
// The reader is in the state the match function left it, `pos` is the position before calling match.
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader& reader, typename Reader::iterator pos);
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Atom>
|
||||
struct atom_base : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = true;
|
||||
|
||||
struct matcher
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
auto reset = reader;
|
||||
if (Atom::match(reader))
|
||||
return true;
|
||||
|
||||
reader = LEXY_MOV(reset);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if constexpr (std::is_same_v<decltype(Atom::error(reader, reader.cur())), void>)
|
||||
{
|
||||
Atom::match(reader);
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto pos = reader.cur(); Atom::match(reader))
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
return LEXY_MOV(handler).error(reader, Atom::error(reader, pos));
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
} // namespace lexyd
|
||||
|
||||
//=== infrastructure ===//
|
||||
namespace lexy
|
||||
{
|
||||
/// The final parser in the chain of NextParsers, forwarding everything to the handler.
|
||||
struct final_parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader&, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
return LEXY_MOV(handler).value(LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_DSL_BASE_HPP_INCLUDED
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_BOM_HPP_INCLUDED
|
||||
#define LEXY_DSL_BOM_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Encoding, lexy::encoding_endianness Endianness>
|
||||
struct _bom_impl
|
||||
{
|
||||
static_assert(Endianness != lexy::encoding_endianness::bom,
|
||||
"bom with BOM-endianness doesn't make sense");
|
||||
|
||||
static constexpr auto name = "";
|
||||
|
||||
static constexpr const unsigned char* value = nullptr;
|
||||
static constexpr std::size_t length = 0u;
|
||||
};
|
||||
template <lexy::encoding_endianness DontCare>
|
||||
struct _bom_impl<lexy::utf8_encoding, DontCare>
|
||||
{
|
||||
static constexpr auto name = "BOM.UTF-8";
|
||||
|
||||
static constexpr unsigned char value[] = {0xEF, 0xBB, 0xBF};
|
||||
static constexpr auto length = 3u;
|
||||
};
|
||||
template <>
|
||||
struct _bom_impl<lexy::utf16_encoding, lexy::encoding_endianness::little>
|
||||
{
|
||||
static constexpr auto name = "BOM.UTF-16-LE";
|
||||
|
||||
static constexpr unsigned char value[] = {0xFF, 0xFE};
|
||||
static constexpr auto length = 2u;
|
||||
};
|
||||
template <>
|
||||
struct _bom_impl<lexy::utf16_encoding, lexy::encoding_endianness::big>
|
||||
{
|
||||
static constexpr auto name = "BOM.UTF-16-BE";
|
||||
|
||||
static constexpr unsigned char value[] = {0xFE, 0xFF};
|
||||
static constexpr auto length = 2u;
|
||||
};
|
||||
template <>
|
||||
struct _bom_impl<lexy::utf32_encoding, lexy::encoding_endianness::little>
|
||||
{
|
||||
static constexpr auto name = "BOM.UTF-32-LE";
|
||||
|
||||
static constexpr unsigned char value[] = {0xFF, 0xFE, 0x00, 0x00};
|
||||
static constexpr auto length = 4u;
|
||||
};
|
||||
template <>
|
||||
struct _bom_impl<lexy::utf32_encoding, lexy::encoding_endianness::big>
|
||||
{
|
||||
static constexpr auto name = "BOM.UTF-32-BE";
|
||||
|
||||
static constexpr unsigned char value[] = {0x00, 0x00, 0xFE, 0xFF};
|
||||
static constexpr auto length = 4u;
|
||||
};
|
||||
|
||||
template <typename Encoding, lexy::encoding_endianness Endianness>
|
||||
struct _bom : atom_base<_bom<Encoding, Endianness>>
|
||||
{
|
||||
using _impl = _bom_impl<Encoding, Endianness>;
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
constexpr auto string
|
||||
= lexy::_detail::basic_string_view<unsigned char>(_impl::value, _impl::length);
|
||||
for (auto c : string)
|
||||
{
|
||||
if (reader.peek() != Reader::encoding::to_int_type(typename Reader::char_type(c)))
|
||||
return false;
|
||||
reader.bump();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader&, typename Reader::iterator pos)
|
||||
{
|
||||
return lexy::make_error<Reader, lexy::expected_char_class>(pos, _impl::name);
|
||||
}
|
||||
};
|
||||
|
||||
/// The BOM for that particular encoding.
|
||||
template <typename Encoding, lexy::encoding_endianness Endianness>
|
||||
inline constexpr auto bom = _bom<Encoding, Endianness>{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_BOM_HPP_INCLUDED
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_BRACKETS_HPP_INCLUDED
|
||||
#define LEXY_DSL_BRACKETS_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
#include <lexy/dsl/choice.hpp>
|
||||
#include <lexy/dsl/list.hpp>
|
||||
#include <lexy/dsl/literal.hpp>
|
||||
#include <lexy/dsl/not.hpp>
|
||||
#include <lexy/dsl/option.hpp>
|
||||
#include <lexy/dsl/peek.hpp>
|
||||
#include <lexy/dsl/sequence.hpp>
|
||||
#include <lexy/dsl/whitespace.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Open, typename Close, typename Whitespace>
|
||||
struct _brackets
|
||||
{
|
||||
/// Sets the whitespace.
|
||||
template <typename Ws>
|
||||
LEXY_CONSTEVAL auto operator[](Ws) const
|
||||
{
|
||||
return _brackets<Open, Close, Ws>{};
|
||||
}
|
||||
|
||||
/// Matches the rule surrounded by brackets.
|
||||
template <typename R>
|
||||
LEXY_CONSTEVAL auto operator()(R r) const
|
||||
{
|
||||
auto o = open();
|
||||
auto c = close();
|
||||
return o >> r + c;
|
||||
}
|
||||
|
||||
/// Matches `opt(r)` surrounded by brackets.
|
||||
/// The rule does not require a condition.
|
||||
template <typename R>
|
||||
LEXY_CONSTEVAL auto opt(R r) const
|
||||
{
|
||||
auto o = open();
|
||||
auto c = close();
|
||||
|
||||
if constexpr (lexy::is_pattern<decltype(c)>)
|
||||
return o >> lexyd::opt(!c >> r + c);
|
||||
else
|
||||
return o >> (!c.condition() >> r + c | else_ >> nullopt + c.then());
|
||||
}
|
||||
|
||||
/// Matches `list(r, sep)` surrounded by brackets.
|
||||
/// The rule does not require a condition.
|
||||
template <typename R>
|
||||
LEXY_CONSTEVAL auto list(R r) const
|
||||
{
|
||||
auto o = open();
|
||||
auto c = branch(close());
|
||||
|
||||
// !c.condition() matches until we've consumed the branch condition.
|
||||
// Then the list exits and we still need c.then().
|
||||
return o >> lexyd::list(!c.condition() >> r) + c.then();
|
||||
}
|
||||
template <typename R, typename S>
|
||||
LEXY_CONSTEVAL auto list(R r, S sep) const
|
||||
{
|
||||
auto o = open();
|
||||
auto c = branch(close());
|
||||
|
||||
// We can't use ! alone, as we might decide the list ends based on separator alone.
|
||||
// As such we reset after the ! anyway and parse the entire condition again.
|
||||
return o >> lexyd::list(peek(!c.condition()) >> r, sep) + c;
|
||||
}
|
||||
|
||||
/// Matches `opt(list(r, sep))` surrounded by brackets.
|
||||
/// The rule does not require a condition.
|
||||
template <typename R>
|
||||
LEXY_CONSTEVAL auto opt_list(R r) const
|
||||
{
|
||||
auto o = open();
|
||||
auto c = branch(close());
|
||||
|
||||
// Same as above, we're just allowing the list to exit before doing one item.
|
||||
// As !c.condition() is also the condition for taking the optional, we still need c.then().
|
||||
return o >> lexyd::opt(lexyd::list(!c.condition() >> r)) + c.then();
|
||||
}
|
||||
template <typename R, typename S>
|
||||
LEXY_CONSTEVAL auto opt_list(R r, S sep) const
|
||||
{
|
||||
auto o = open();
|
||||
auto c = branch(close());
|
||||
|
||||
// As above, we can't use !c and reuse it as the condition for opt().
|
||||
return o >> lexyd::opt(lexyd::list(peek(!c.condition()) >> r, sep)) + c;
|
||||
}
|
||||
|
||||
/// Matches the open bracket.
|
||||
LEXY_CONSTEVAL auto open() const
|
||||
{
|
||||
if constexpr (std::is_same_v<Whitespace, void>)
|
||||
return Open{};
|
||||
else
|
||||
return whitespaced(Open{}, Whitespace{});
|
||||
}
|
||||
/// Matches the closing bracket.
|
||||
LEXY_CONSTEVAL auto close() const
|
||||
{
|
||||
if constexpr (std::is_same_v<Whitespace, void>)
|
||||
return Close{};
|
||||
else
|
||||
return whitespaced(Close{}, Whitespace{});
|
||||
}
|
||||
};
|
||||
|
||||
/// Defines open and close brackets.
|
||||
template <typename Open, typename Close>
|
||||
LEXY_CONSTEVAL auto brackets(Open, Close)
|
||||
{
|
||||
static_assert(lexy::is_branch_rule<Open> && lexy::is_branch_rule<Close>);
|
||||
return _brackets<Open, Close, void>{};
|
||||
}
|
||||
|
||||
constexpr auto round_bracketed = brackets(lit_c<'('>, lit_c<')'>);
|
||||
constexpr auto square_bracketed = brackets(lit_c<'['>, lit_c<']'>);
|
||||
constexpr auto curly_bracketed = brackets(lit_c<'{'>, lit_c<'}'>);
|
||||
constexpr auto angle_bracketed = brackets(lit_c<'<'>, lit_c<'>'>);
|
||||
|
||||
constexpr auto parenthesized = round_bracketed;
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_BRACKETS_HPP_INCLUDED
|
||||
@@ -1,153 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_BRANCH_HPP_INCLUDED
|
||||
#define LEXY_DSL_BRANCH_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/detect.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/sequence.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Condition, typename Then>
|
||||
struct _br : rule_base
|
||||
{
|
||||
static constexpr auto is_unconditional = std::is_same_v<const Condition, decltype(success)>;
|
||||
|
||||
using condition_matcher = typename Condition::matcher;
|
||||
struct then_matcher : Then::matcher
|
||||
{};
|
||||
|
||||
template <typename NextParser>
|
||||
using then_parser = typename Then::template parser<NextParser>;
|
||||
|
||||
//=== rule ===//
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
using parser = typename Condition::template parser<typename Then::template parser<NextParser>>;
|
||||
|
||||
//=== dsl ===//
|
||||
/// Returns the condition of the branch.
|
||||
static LEXY_CONSTEVAL auto condition()
|
||||
{
|
||||
return Condition{};
|
||||
}
|
||||
|
||||
/// Returns the then of the branch.
|
||||
static LEXY_CONSTEVAL auto then()
|
||||
{
|
||||
return Then{};
|
||||
}
|
||||
|
||||
// A branch is already a branch.
|
||||
friend LEXY_CONSTEVAL auto branch(_br)
|
||||
{
|
||||
return _br{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Turns a pattern into a branch.
|
||||
template <typename Pattern, typename = std::enable_if_t<lexy::is_pattern<Pattern>>>
|
||||
LEXY_CONSTEVAL auto branch(Pattern pattern)
|
||||
{
|
||||
return pattern >> success;
|
||||
}
|
||||
|
||||
/// Parses `Then` only after `Condition` has matched.
|
||||
template <typename Condition, typename Then>
|
||||
LEXY_CONSTEVAL auto operator>>(Condition, Then)
|
||||
{
|
||||
static_assert(lexy::is_pattern<Condition>, "branch condition must be a pattern");
|
||||
return _br<Condition, Then>{};
|
||||
}
|
||||
|
||||
// A condition on the left extends the condition.
|
||||
template <typename Pattern, typename Condition, typename Then>
|
||||
LEXY_CONSTEVAL auto operator>>(Pattern pattern, _br<Condition, Then>)
|
||||
{
|
||||
static_assert(lexy::is_pattern<Pattern>, "branch condition must be a pattern");
|
||||
return _br<decltype(pattern + Condition{}), Then>{};
|
||||
}
|
||||
|
||||
// If a branch is used as condition, only its condition is the condition.
|
||||
template <typename Condition, typename Then, typename Rule>
|
||||
LEXY_CONSTEVAL auto operator>>(_br<Condition, Then>, Rule rule)
|
||||
{
|
||||
return _br<Condition, decltype(Then{} + rule)>{};
|
||||
}
|
||||
// Disambiguation.
|
||||
template <typename C1, typename T1, typename C2, typename T2>
|
||||
LEXY_CONSTEVAL auto operator>>(_br<C1, T1>, _br<C2, T2>)
|
||||
{
|
||||
return _br<C1, decltype(T1{} + C2{} + T2{})>{};
|
||||
}
|
||||
|
||||
// If we add something on the left to a branch, we loose the branchy-ness.
|
||||
template <typename Rule, typename Condition, typename Then>
|
||||
LEXY_CONSTEVAL auto operator+(Rule rule, _br<Condition, Then>)
|
||||
{
|
||||
return rule + Condition{} + Then{};
|
||||
}
|
||||
// Disambiguation.
|
||||
template <typename... R, typename Condition, typename Then>
|
||||
LEXY_CONSTEVAL auto operator+(_seq<R...>, _br<Condition, Then>)
|
||||
{
|
||||
return _seq<R...>{} + Condition{} + Then{};
|
||||
}
|
||||
|
||||
// If we add something on the right to a branch, we extend the then.
|
||||
template <typename Condition, typename Then, typename Rule>
|
||||
LEXY_CONSTEVAL auto operator+(_br<Condition, Then>, Rule rule)
|
||||
{
|
||||
return _br<Condition, decltype(Then{} + rule)>{};
|
||||
}
|
||||
// Disambiguation.
|
||||
template <typename Condition, typename Then, typename... R>
|
||||
LEXY_CONSTEVAL auto operator+(_br<Condition, Then>, _seq<R...>)
|
||||
{
|
||||
return _br<Condition, decltype(Then{} + _seq<R...>{})>{};
|
||||
}
|
||||
|
||||
// If we add two branches, we extend the then of the first one.
|
||||
template <typename C1, typename T1, typename C2, typename T2>
|
||||
LEXY_CONSTEVAL auto operator+(_br<C1, T1>, _br<C2, T2>)
|
||||
{
|
||||
return _br<C1, decltype(T1{} + C2{} + T2{})>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
struct _else
|
||||
{
|
||||
template <typename Then>
|
||||
friend LEXY_CONSTEVAL auto operator>>(_else, Then then)
|
||||
{
|
||||
return success >> then;
|
||||
}
|
||||
template <typename Condition, typename Then>
|
||||
friend LEXY_CONSTEVAL auto operator>>(_else, _br<Condition, Then>)
|
||||
{
|
||||
return success >> Condition{} + Then{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Takes the branch unconditionally.
|
||||
inline constexpr auto else_ = _else{};
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename Rule>
|
||||
using _detect_branch = decltype(branch(Rule{}));
|
||||
|
||||
/// Whether or not the type is a branch rule.
|
||||
template <typename T>
|
||||
constexpr auto is_branch_rule = is_rule<T>&& _detail::is_detected<_detect_branch, T>;
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_DSL_BRANCH_HPP_INCLUDED
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_CAPTURE_HPP_INCLUDED
|
||||
#define LEXY_DSL_CAPTURE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/whitespace.hpp>
|
||||
#include <lexy/lexeme.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <template <typename Reader> typename Lexeme, typename Rule, typename NextParser>
|
||||
struct _cap_parser
|
||||
{
|
||||
template <typename... PrevArgs>
|
||||
struct _continuation
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, typename Reader::iterator begin,
|
||||
PrevArgs&&... prev_args, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
auto end = reader.cur();
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(prev_args)...,
|
||||
Lexeme<typename Reader::canonical_reader>(begin, end),
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
using continuation = _continuation<Args...>;
|
||||
return Rule::template parser<continuation>::parse(handler, reader, reader.cur(),
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Rule>
|
||||
struct _cap : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
using parser = _cap_parser<lexy::lexeme, Rule, NextParser>;
|
||||
|
||||
template <typename Whitespace>
|
||||
LEXY_CONSTEVAL auto operator[](Whitespace ws) const
|
||||
{
|
||||
return whitespaced(*this, ws);
|
||||
}
|
||||
};
|
||||
|
||||
/// Captures whatever the rule matches as a lexeme.
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto capture(Rule)
|
||||
{
|
||||
return _cap<Rule>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_CAPTURE_HPP_INCLUDED
|
||||
@@ -1,129 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_CHOICE_HPP_INCLUDED
|
||||
#define LEXY_DSL_CHOICE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct exhausted_choice
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "exhausted choice";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename... R>
|
||||
struct _chc_matcher
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
template <typename H, typename... T>
|
||||
struct _chc_matcher<H, T...>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
using as_branch = decltype(branch(H{}));
|
||||
|
||||
if (auto save = reader; as_branch::condition_matcher::match(reader))
|
||||
{
|
||||
if (as_branch::then_matcher::match(reader))
|
||||
return true;
|
||||
else
|
||||
{
|
||||
reader = LEXY_MOV(save);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return _chc_matcher<T...>::match(reader);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NextParser, typename... R>
|
||||
struct _chc_parser;
|
||||
template <typename NextParser>
|
||||
struct _chc_parser<NextParser>
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&...) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
auto e = lexy::make_error<Reader, lexy::exhausted_choice>(reader.cur());
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
};
|
||||
template <typename NextParser, typename H, typename... T>
|
||||
struct _chc_parser<NextParser, H, T...>
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
using as_branch = decltype(branch(H{}));
|
||||
if constexpr (as_branch::is_unconditional)
|
||||
return as_branch::template then_parser<NextParser>::parse(handler, reader,
|
||||
LEXY_FWD(args)...);
|
||||
else
|
||||
{
|
||||
if (as_branch::condition_matcher::match(reader))
|
||||
return as_branch::template then_parser<NextParser>::parse(handler, reader,
|
||||
LEXY_FWD(args)...);
|
||||
else
|
||||
return _chc_parser<NextParser, T...>::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... R>
|
||||
struct _chc : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = (decltype(branch(R()).then())::has_matcher&&...);
|
||||
|
||||
using matcher = _chc_matcher<R...>;
|
||||
|
||||
template <typename NextParser>
|
||||
using parser = _chc_parser<NextParser, R...>;
|
||||
};
|
||||
|
||||
template <typename R, typename S>
|
||||
LEXY_CONSTEVAL auto operator|(R, S)
|
||||
{
|
||||
static_assert(lexy::is_branch_rule<R>, "choice requires a branch condition");
|
||||
static_assert(lexy::is_branch_rule<S>, "choice requires a branch condition");
|
||||
return _chc<R, S>{};
|
||||
}
|
||||
template <typename... R, typename S>
|
||||
LEXY_CONSTEVAL auto operator|(_chc<R...>, S)
|
||||
{
|
||||
static_assert(lexy::is_branch_rule<S>, "choice requires a branch condition");
|
||||
return _chc<R..., S>{};
|
||||
}
|
||||
template <typename R, typename... S>
|
||||
LEXY_CONSTEVAL auto operator|(R, _chc<S...>)
|
||||
{
|
||||
static_assert(lexy::is_branch_rule<R>, "choice requires a branch condition");
|
||||
return _chc<R, S...>{};
|
||||
}
|
||||
template <typename... R, typename... S>
|
||||
LEXY_CONSTEVAL auto operator|(_chc<R...>, _chc<S...>)
|
||||
{
|
||||
return _chc<R..., S...>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_CHOICE_HPP_INCLUDED
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_CODE_POINT_HPP_INCLUDED
|
||||
#define LEXY_DSL_CODE_POINT_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Encoding>
|
||||
constexpr auto _cp_name()
|
||||
{
|
||||
if constexpr (std::is_same_v<Encoding, lexy::ascii_encoding>)
|
||||
return "ASCII.code_point";
|
||||
else if constexpr (std::is_same_v<Encoding, lexy::utf8_encoding>)
|
||||
return "UTF-8.code_point";
|
||||
else if constexpr (std::is_same_v<Encoding, lexy::utf16_encoding>)
|
||||
return "UTF-16.code_point";
|
||||
else if constexpr (std::is_same_v<Encoding, lexy::utf32_encoding>)
|
||||
return "UTF-32.code_point";
|
||||
else
|
||||
return "code_point";
|
||||
}
|
||||
|
||||
struct _cp_cap : rule_base
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC lexy::code_point _parse(Reader& reader)
|
||||
{
|
||||
using decoder_t = typename Reader::encoding::code_point_decoder;
|
||||
decoder_t decoder;
|
||||
|
||||
switch (decoder.init(reader.peek()))
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 3:
|
||||
reader.bump();
|
||||
if (!decoder.next(reader.peek()))
|
||||
return {};
|
||||
// fallthrough
|
||||
case 2:
|
||||
reader.bump();
|
||||
if (!decoder.next(reader.peek()))
|
||||
return {};
|
||||
// fallthrough
|
||||
case 1:
|
||||
reader.bump();
|
||||
if (!decoder.next(reader.peek()))
|
||||
return {};
|
||||
break;
|
||||
|
||||
case -1:
|
||||
return {};
|
||||
default:
|
||||
LEXY_PRECONDITION(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
// We only accept valid code points.
|
||||
auto cp = LEXY_MOV(decoder).finish();
|
||||
if (!cp.is_valid())
|
||||
return {};
|
||||
|
||||
// Consume final code unit.
|
||||
// This has to be done after the validity check,
|
||||
// because for UTF-32, eof creates an invalid code point.
|
||||
reader.bump();
|
||||
return cp;
|
||||
}
|
||||
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
auto save = reader;
|
||||
if (auto result = _parse(reader); result.is_valid())
|
||||
{
|
||||
LEXY_PRECONDITION(result.is_scalar());
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., result);
|
||||
}
|
||||
else
|
||||
{
|
||||
reader = LEXY_MOV(save);
|
||||
|
||||
auto name = _cp_name<typename Reader::encoding>();
|
||||
auto e = lexy::make_error<Reader, lexy::expected_char_class>(reader.cur(), name);
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct _cp : atom_base<_cp>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
auto cp = _cp_cap::_parse(reader);
|
||||
LEXY_PRECONDITION(!cp.is_valid() || cp.is_scalar());
|
||||
return cp.is_valid();
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader&, typename Reader::iterator pos)
|
||||
{
|
||||
auto name = _cp_name<typename Reader::encoding>();
|
||||
return lexy::make_error<Reader, lexy::expected_char_class>(pos, name);
|
||||
}
|
||||
|
||||
LEXY_CONSTEVAL auto capture() const
|
||||
{
|
||||
return _cp_cap{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches a single unicode code point in the current unicode encoding.
|
||||
constexpr auto code_point = _cp{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_CODE_POINT_HPP_INCLUDED
|
||||
@@ -1,154 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_COMBINATION_HPP_INCLUDED
|
||||
#define LEXY_DSL_COMBINATION_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/integer_sequence.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/choice.hpp>
|
||||
#include <lexy/dsl/loop.hpp>
|
||||
#include <lexy/dsl/sequence.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct combination_duplicate
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "combination duplicate";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Reader, std::size_t N, typename Sink>
|
||||
struct _comb_state
|
||||
{
|
||||
static constexpr auto max_count = N;
|
||||
|
||||
// Whether or not the rule with the specified index was already matched.
|
||||
bool handled[N];
|
||||
// How many we already did.
|
||||
std::size_t count;
|
||||
// Beginning of the current repetition.
|
||||
typename Reader::iterator pos;
|
||||
// The sink to store values.
|
||||
Sink sink;
|
||||
};
|
||||
|
||||
// Added at the end of each branch to update the combination state.
|
||||
template <typename E, std::size_t Idx, typename... PrevArgs>
|
||||
struct _comb_it : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, std::size_t N, typename Sink, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(_loop_handler<Handler>& handler, Reader& reader,
|
||||
_comb_state<Reader, N, Sink>& state, PrevArgs&&... pargs,
|
||||
Args&&... args) -> typename _loop_handler<Handler>::result_type
|
||||
{
|
||||
if (!state.handled[Idx])
|
||||
{
|
||||
state.handled[Idx] = true;
|
||||
state.count++;
|
||||
state.pos = reader.cur();
|
||||
if constexpr (sizeof...(Args) > 0)
|
||||
state.sink(LEXY_FWD(args)...);
|
||||
|
||||
if (state.count == state.max_count)
|
||||
return LEXY_MOV(handler).break_();
|
||||
else
|
||||
return NextParser::parse(handler, reader, state, LEXY_FWD(pargs)...);
|
||||
}
|
||||
else
|
||||
{
|
||||
using tag = std::conditional_t<std::is_void_v<E>, lexy::combination_duplicate, E>;
|
||||
auto e = lexy::make_error<Reader, tag>(state.pos, reader.cur());
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <bool Partial, typename E, typename... R>
|
||||
struct _comb : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename... Args, std::size_t... Idx>
|
||||
static LEXY_CONSTEVAL auto _comb_choice(lexy::_detail::index_sequence<Idx...>)
|
||||
{
|
||||
if constexpr (Partial)
|
||||
return ((R{} >> _comb_it<E, Idx, Args...>{}) | ... | (else_ >> break_));
|
||||
else
|
||||
return ((R{} >> _comb_it<E, Idx, Args...>{}) | ...);
|
||||
}
|
||||
|
||||
struct _continuation
|
||||
{
|
||||
template <typename Handler, typename Reader, typename Sink, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader,
|
||||
_comb_state<Reader, sizeof...(R), Sink>& state, Args&&... args)
|
||||
-> typename Handler::result_type
|
||||
{
|
||||
if constexpr (std::is_same_v<typename Sink::return_type, void>)
|
||||
{
|
||||
LEXY_MOV(state.sink).finish();
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...,
|
||||
LEXY_MOV(state.sink).finish());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
constexpr auto N = sizeof...(R);
|
||||
|
||||
using state_t = _comb_state<Reader, N, decltype(handler.list_sink())>;
|
||||
state_t state{{}, 0, reader.cur(), handler.list_sink()};
|
||||
|
||||
constexpr auto comb_choice
|
||||
= _comb_choice<Args...>(lexy::_detail::make_index_sequence<N>{});
|
||||
constexpr auto l = loop(comb_choice);
|
||||
return decltype(l)::template parser<_continuation>::parse(handler, reader, state,
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Matches each of the rules in an arbitrary order.
|
||||
/// Only matches each rule exactly once.
|
||||
template <typename Error = void, typename... R>
|
||||
LEXY_CONSTEVAL auto combination(R...)
|
||||
{
|
||||
static_assert((lexy::is_branch_rule<R> && ...), "combination() requires a branch rule");
|
||||
return _comb<false, Error, R...>{};
|
||||
}
|
||||
|
||||
/// Matches some of the rules in an arbitrary order.
|
||||
/// Only matches a rule at most once.
|
||||
template <typename Error = void, typename... R>
|
||||
LEXY_CONSTEVAL auto partial_combination(R...)
|
||||
{
|
||||
static_assert((lexy::is_branch_rule<R> && ...), "partial_combination() requires a branch rule");
|
||||
return _comb<true, Error, R...>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_COMBINATION_HPP_INCLUDED
|
||||
|
||||
@@ -1,240 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_CONTEXT_HPP_INCLUDED
|
||||
#define LEXY_DSL_CONTEXT_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/capture.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Reader>
|
||||
struct _context
|
||||
{
|
||||
lexy::lexeme<Reader> lexeme;
|
||||
|
||||
constexpr _context() noexcept = default;
|
||||
constexpr _context(typename Reader::iterator begin, typename Reader::iterator end) noexcept
|
||||
: lexeme(begin, end)
|
||||
{}
|
||||
|
||||
template <typename U>
|
||||
static constexpr bool is = std::is_same_v<std::decay_t<U>, _context>;
|
||||
|
||||
// Returns the last context argument.
|
||||
template <typename... Args>
|
||||
static constexpr auto get(Args&&... args)
|
||||
{
|
||||
_context<Reader> result;
|
||||
// We're immediately invoking a lambda that sets result if the type matches for each
|
||||
// argument.
|
||||
auto lambda = [&](auto&& arg) {
|
||||
if constexpr (is<decltype(arg)>)
|
||||
result = arg;
|
||||
};
|
||||
(lambda(args), ...);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Rule>
|
||||
struct _ctx_push : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
using parser = _cap_parser<_context, Rule, NextParser>;
|
||||
};
|
||||
|
||||
/// Pushes whatever the rule captures onto the context stack.
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto context_push(Rule)
|
||||
{
|
||||
return _ctx_push<Rule>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
struct _ctx_drop : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename... Args>
|
||||
struct _continuation
|
||||
{
|
||||
template <typename Handler, typename Reader, typename Head, typename... Tail>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args, Head&& head,
|
||||
Tail&&... tail) -> typename Handler::result_type
|
||||
{
|
||||
using context_t = _context<typename Reader::canonical_reader>;
|
||||
constexpr auto head_is_context = context_t::template is<Head>;
|
||||
constexpr auto tail_is_context = (context_t::template is<Tail> || ...);
|
||||
|
||||
if constexpr (head_is_context && !tail_is_context)
|
||||
{
|
||||
// Head is a context argument and it is the last one; remove it and we're done.
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., LEXY_FWD(tail)...);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Either Head is a context, but there is a later one, or Head isn't a context.
|
||||
// In either case, we're keeping Head.
|
||||
static_assert(sizeof...(Tail) > 0, "missing previous context_push()");
|
||||
return _continuation<Args..., Head>::parse(handler, reader, LEXY_FWD(args)...,
|
||||
LEXY_FWD(head), LEXY_FWD(tail)...);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
// Initially no argument is kept.
|
||||
return _continuation<>::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Removes the last pushed context from the stack.
|
||||
constexpr auto context_drop = _ctx_drop{};
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
/// Error when the previously pushed context does not match the popped one.
|
||||
struct context_mismatch
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "context mismatch";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
/// Checks that the pushed and popped context are exactly equal.
|
||||
struct context_eq
|
||||
{
|
||||
template <typename Reader>
|
||||
constexpr bool operator()(lexy::lexeme<Reader> lhs, lexy::lexeme<Reader> rhs) const
|
||||
{
|
||||
using iterator = typename Reader::iterator;
|
||||
if constexpr (std::is_pointer_v<iterator>)
|
||||
if (lhs.size() != rhs.size())
|
||||
return false;
|
||||
|
||||
auto lhs_cur = lhs.begin();
|
||||
auto rhs_cur = rhs.begin();
|
||||
while (lhs_cur != lhs.end() && rhs_cur != rhs.end())
|
||||
{
|
||||
if (*lhs_cur != *rhs_cur)
|
||||
return false;
|
||||
++lhs_cur;
|
||||
++rhs_cur;
|
||||
}
|
||||
return lhs_cur == lhs.end() && rhs_cur == rhs.end();
|
||||
}
|
||||
};
|
||||
|
||||
/// Checks that the pushed and popped contexts have the same length.
|
||||
struct context_eq_length
|
||||
{
|
||||
template <typename Reader>
|
||||
constexpr bool operator()(lexy::lexeme<Reader> lhs, lexy::lexeme<Reader> rhs) const
|
||||
{
|
||||
return lexy::_detail::range_size(lhs.begin(), lhs.end())
|
||||
== lexy::_detail::range_size(rhs.begin(), rhs.end());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Rule, typename Eq, typename Error>
|
||||
struct _ctx_top
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename... Args>
|
||||
struct _continuation
|
||||
{
|
||||
// We're ignoring any values created by the rule.
|
||||
//
|
||||
template <typename Handler, typename Reader, typename... RuleArgs>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args,
|
||||
const _context<typename Reader::canonical_reader>& popped,
|
||||
RuleArgs&&...) -> typename Handler::result_type
|
||||
{
|
||||
auto pushed = _context<typename Reader::canonical_reader>::get(LEXY_FWD(args)...);
|
||||
if (Eq{}(pushed.lexeme, popped.lexeme))
|
||||
// We've parsed the same argument that we've pushed, continue.
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
{
|
||||
auto e = lexy::make_error<Reader, Error>(popped.lexeme.begin(),
|
||||
popped.lexeme.end());
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
// We capture the rule and pass it to the continuation to process.
|
||||
return _cap_parser<_context, Rule, _continuation<Args...>>::parse(handler, reader,
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
/// Sets the error if the context doesn't match.
|
||||
template <typename E>
|
||||
LEXY_CONSTEVAL auto error()
|
||||
{
|
||||
return _ctx_top<Rule, Eq, E>{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Captures what the Rule matches and checks that it is equal to the last context pushed onto the
|
||||
/// stack.
|
||||
/// The context is kept on the stack.
|
||||
template <typename Eq = context_eq, typename Rule>
|
||||
LEXY_CONSTEVAL auto context_top(Rule)
|
||||
{
|
||||
return _ctx_top<Rule, Eq, lexy::context_mismatch>{};
|
||||
}
|
||||
|
||||
template <typename Rule, typename Eq, typename Error>
|
||||
struct _ctx_pop : decltype(_ctx_top<Rule, Eq, Error>{} + context_drop)
|
||||
{
|
||||
/// Sets the error if the context doesn't match.
|
||||
template <typename E>
|
||||
LEXY_CONSTEVAL auto error()
|
||||
{
|
||||
return _ctx_pop<Rule, Eq, E>{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Captures what the Rule matches and checks that it is equal to the last context pushed onto the
|
||||
/// stack.
|
||||
/// The context is removed on the stack.
|
||||
template <typename Eq = context_eq, typename Rule>
|
||||
LEXY_CONSTEVAL auto context_pop(Rule)
|
||||
{
|
||||
return _ctx_pop<Rule, Eq, lexy::context_mismatch>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_CONTEXT_HPP_INCLUDED
|
||||
@@ -1,299 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_DELIMITED_HPP_INCLUDED
|
||||
#define LEXY_DSL_DELIMITED_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/capture.hpp>
|
||||
#include <lexy/dsl/choice.hpp>
|
||||
#include <lexy/dsl/error.hpp>
|
||||
#include <lexy/dsl/list.hpp>
|
||||
#include <lexy/dsl/literal.hpp>
|
||||
#include <lexy/dsl/not.hpp>
|
||||
#include <lexy/dsl/option.hpp>
|
||||
#include <lexy/dsl/peek.hpp>
|
||||
#include <lexy/dsl/value.hpp>
|
||||
#include <lexy/dsl/whitespace.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
/// The reader ends before the closing delimiter was found.
|
||||
struct missing_delimiter
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "missing delimiter";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
struct _delb : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
// Remember the beginning of the delimited for the error message.
|
||||
return NextParser::parse(handler, reader, reader.cur(), LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Content>
|
||||
struct _delc : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if (reader.eof())
|
||||
{
|
||||
// Find the beginning of the delimited; it was added somwhere in the arguments.
|
||||
typename Reader::iterator begin = {};
|
||||
auto lambda = [&](const auto& arg) {
|
||||
using arg_type = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<arg_type, typename Reader::iterator>)
|
||||
{
|
||||
begin = arg;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
};
|
||||
(lambda(args) || ...);
|
||||
|
||||
// If we've reached EOF, it means we're missing the closing delimiter.
|
||||
auto e = lexy::make_error<Reader, lexy::missing_delimiter>(begin, reader.cur());
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Content::template parser<NextParser>::parse(handler, reader,
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct _dele : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, typename Reader::iterator,
|
||||
Args&&... args) -> typename Handler::result_type
|
||||
{
|
||||
// Remove the saved beginning again.
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Open, typename Close, typename Whitespace>
|
||||
struct _delim_dsl
|
||||
{
|
||||
/// Sets the whitespace.
|
||||
template <typename Ws>
|
||||
LEXY_CONSTEVAL auto operator[](Ws) const
|
||||
{
|
||||
return _delim_dsl<Open, Close, Ws>{};
|
||||
}
|
||||
|
||||
template <typename Content>
|
||||
LEXY_CONSTEVAL auto _get(Content) const
|
||||
{
|
||||
auto c = branch(close());
|
||||
|
||||
// We put the content in a list until the closing condition of the list matches.
|
||||
auto l = list(!c.condition() >> _delc<Content>{});
|
||||
// We surround the list with the logic that handles the positional stuff.
|
||||
return open() >> _delb{} + opt(l) + _dele{} + c.then();
|
||||
}
|
||||
|
||||
/// Sets the content.
|
||||
template <typename Content>
|
||||
LEXY_CONSTEVAL auto operator()(Content content) const
|
||||
{
|
||||
if constexpr (lexy::is_pattern<Content>)
|
||||
return _get(capture(content));
|
||||
else
|
||||
return _get(content);
|
||||
}
|
||||
template <typename Content, typename Escape>
|
||||
LEXY_CONSTEVAL auto operator()(Content content, Escape escape) const
|
||||
{
|
||||
if constexpr (lexy::is_pattern<Content>)
|
||||
return _get(escape | else_ >> capture(content));
|
||||
else
|
||||
return _get(escape | else_ >> content);
|
||||
}
|
||||
|
||||
/// Matches the open delimiter.
|
||||
LEXY_CONSTEVAL auto open() const
|
||||
{
|
||||
if constexpr (std::is_same_v<Whitespace, void>)
|
||||
return Open{};
|
||||
else
|
||||
return whitespaced(Open{}, Whitespace{});
|
||||
}
|
||||
/// Matches the closing delimiter.
|
||||
LEXY_CONSTEVAL auto close() const
|
||||
{
|
||||
// Close never has any whitespace.
|
||||
return Close{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Parses everything between the two delimiters and captures it.
|
||||
template <typename Open, typename Close>
|
||||
LEXY_CONSTEVAL auto delimited(Open, Close)
|
||||
{
|
||||
static_assert(lexy::is_branch_rule<Open> && lexy::is_branch_rule<Close>);
|
||||
return _delim_dsl<Open, Close, void>{};
|
||||
}
|
||||
|
||||
/// Parses everything between a paired delimiter.
|
||||
template <typename Delim>
|
||||
LEXY_CONSTEVAL auto delimited(Delim)
|
||||
{
|
||||
static_assert(lexy::is_pattern<Delim>);
|
||||
return _delim_dsl<Delim, Delim, void>{};
|
||||
}
|
||||
|
||||
constexpr auto quoted = delimited(LEXY_LIT("\""));
|
||||
constexpr auto triple_quoted = delimited(LEXY_LIT("\"\"\""));
|
||||
|
||||
constexpr auto single_quoted = delimited(LEXY_LIT("'"));
|
||||
|
||||
constexpr auto backticked = delimited(LEXY_LIT("`"));
|
||||
constexpr auto double_backticked = delimited(LEXY_LIT("``"));
|
||||
constexpr auto triple_backticked = delimited(LEXY_LIT("```"));
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct invalid_escape_sequence
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "invalid escape sequence";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Pattern, typename... Branches>
|
||||
LEXY_CONSTEVAL auto _escape_rule(Branches... branches)
|
||||
{
|
||||
if constexpr (sizeof...(Branches) == 0)
|
||||
return Pattern{};
|
||||
else if constexpr ((decltype(branch(branches))::is_unconditional || ...))
|
||||
return Pattern{} >> (branches | ...);
|
||||
else
|
||||
return Pattern{} >> (branches | ... | (else_ >> error<lexy::invalid_escape_sequence>));
|
||||
}
|
||||
|
||||
template <typename Pattern>
|
||||
struct _escape_cap
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
// We can be sure that the pattern matches here.
|
||||
auto begin = reader.cur();
|
||||
auto result = Pattern::matcher::match(reader);
|
||||
LEXY_PRECONDITION(result);
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...,
|
||||
lexy::lexeme(reader, begin));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename EscapePattern, typename... Branches>
|
||||
struct _escape : decltype(_escape_rule<EscapePattern>(Branches{}...))
|
||||
{
|
||||
/// Adds a generic escape rule.
|
||||
template <typename Branch>
|
||||
LEXY_CONSTEVAL auto rule(Branch) const
|
||||
{
|
||||
static_assert(lexy::is_branch_rule<Branch>);
|
||||
return _escape<EscapePattern, Branches..., Branch>{};
|
||||
}
|
||||
|
||||
/// Adds an escape rule that captures the pattern.
|
||||
template <typename Pattern>
|
||||
LEXY_CONSTEVAL auto capture(Pattern pattern) const
|
||||
{
|
||||
static_assert(lexy::is_pattern<Pattern>);
|
||||
return rule(peek(pattern) >> _escape_cap<Pattern>{});
|
||||
}
|
||||
|
||||
#if LEXY_HAS_NTTP
|
||||
/// Adds an escape rule that replaces the escaped string with the replacement.
|
||||
template <lexy::_detail::string_literal Str, typename Value>
|
||||
LEXY_CONSTEVAL auto lit(Value value)
|
||||
{
|
||||
return rule(lexyd::lit<Str> >> value);
|
||||
}
|
||||
/// Adds an escape rule that replaces the escaped string with itself.
|
||||
template <lexy::_detail::string_literal Str>
|
||||
LEXY_CONSTEVAL auto lit()
|
||||
{
|
||||
return lit<Str>(value_str<Str>);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Adds an escape rule that replaces the escaped character with the replacement.
|
||||
template <auto C, typename Value>
|
||||
LEXY_CONSTEVAL auto lit_c(Value value) const
|
||||
{
|
||||
return rule(lexyd::lit_c<C> >> value);
|
||||
}
|
||||
/// Adds an escape rule that replaces the escape character with itself.
|
||||
template <auto C>
|
||||
LEXY_CONSTEVAL auto lit_c() const
|
||||
{
|
||||
return lit_c<C>(value_c<C>);
|
||||
}
|
||||
};
|
||||
|
||||
/// Creates an escape rule.
|
||||
/// The pattern is the initial pattern to begin,
|
||||
/// and then you can add rules that match after it.
|
||||
template <typename EscapePattern>
|
||||
LEXY_CONSTEVAL auto escape(EscapePattern)
|
||||
{
|
||||
static_assert(lexy::is_pattern<EscapePattern>);
|
||||
return _escape<EscapePattern>{};
|
||||
}
|
||||
|
||||
constexpr auto backslash_escape = escape(lit_c<'\\'>);
|
||||
constexpr auto dollar_escape = escape(lit_c<'$'>);
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_DELIMITED_HPP_INCLUDED
|
||||
|
||||
@@ -1,409 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_DIGIT_HPP_INCLUDED
|
||||
#define LEXY_DSL_DIGIT_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/alternative.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/error.hpp>
|
||||
#include <lexy/dsl/literal.hpp>
|
||||
#include <lexy/dsl/option.hpp>
|
||||
#include <lexy/dsl/sequence.hpp>
|
||||
#include <lexy/dsl/while.hpp>
|
||||
|
||||
//=== bases ===//
|
||||
namespace lexyd
|
||||
{
|
||||
struct binary
|
||||
{
|
||||
static constexpr unsigned radix = 2;
|
||||
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "digit.binary";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return c == Encoding::to_int_type('0') || c == Encoding::to_int_type('1');
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match_zero(IntType c)
|
||||
{
|
||||
return c == Encoding::to_int_type('0');
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
LEXY_DSL_FUNC unsigned value(CharT c)
|
||||
{
|
||||
return static_cast<unsigned>(c) - '0';
|
||||
}
|
||||
};
|
||||
|
||||
struct octal
|
||||
{
|
||||
static constexpr unsigned radix = 8;
|
||||
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "digit.octal";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return c >= Encoding::to_int_type('0') && c <= Encoding::to_int_type('7');
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match_zero(IntType c)
|
||||
{
|
||||
return c == Encoding::to_int_type('0');
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
LEXY_DSL_FUNC unsigned value(CharT c)
|
||||
{
|
||||
return static_cast<unsigned>(c) - '0';
|
||||
}
|
||||
};
|
||||
|
||||
struct decimal
|
||||
{
|
||||
static constexpr unsigned radix = 10;
|
||||
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "digit.decimal";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return c >= Encoding::to_int_type('0') && c <= Encoding::to_int_type('9');
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match_zero(IntType c)
|
||||
{
|
||||
return c == Encoding::to_int_type('0');
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
LEXY_DSL_FUNC unsigned value(CharT c)
|
||||
{
|
||||
return static_cast<unsigned>(c) - '0';
|
||||
}
|
||||
};
|
||||
|
||||
struct hex_lower
|
||||
{
|
||||
static constexpr unsigned radix = 16;
|
||||
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "digit.hex-lower";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return (c >= Encoding::to_int_type('0') && c <= Encoding::to_int_type('9'))
|
||||
|| (c >= Encoding::to_int_type('a') && c <= Encoding::to_int_type('f'));
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match_zero(IntType c)
|
||||
{
|
||||
return c == Encoding::to_int_type('0');
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
LEXY_DSL_FUNC unsigned value(CharT c)
|
||||
{
|
||||
if (c >= 'a')
|
||||
return static_cast<unsigned>(c) - 'a' + 10;
|
||||
else if (c <= '9')
|
||||
return static_cast<unsigned>(c) - '0';
|
||||
else
|
||||
return unsigned(-1);
|
||||
}
|
||||
};
|
||||
|
||||
struct hex_upper
|
||||
{
|
||||
static constexpr unsigned radix = 16;
|
||||
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "digit.hex-upper";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return (c >= Encoding::to_int_type('0') && c <= Encoding::to_int_type('9'))
|
||||
|| (c >= Encoding::to_int_type('A') && c <= Encoding::to_int_type('F'));
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match_zero(IntType c)
|
||||
{
|
||||
return c == Encoding::to_int_type('0');
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
LEXY_DSL_FUNC unsigned value(CharT c)
|
||||
{
|
||||
if (c >= 'A')
|
||||
return static_cast<unsigned>(c) - 'A' + 10;
|
||||
else if (c <= '9')
|
||||
return static_cast<unsigned>(c) - '0';
|
||||
else
|
||||
return unsigned(-1);
|
||||
}
|
||||
};
|
||||
|
||||
struct hex
|
||||
{
|
||||
static constexpr unsigned radix = 16;
|
||||
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "digit.hex";
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match(IntType c)
|
||||
{
|
||||
return (c >= Encoding::to_int_type('0') && c <= Encoding::to_int_type('9'))
|
||||
|| (c >= Encoding::to_int_type('A') && c <= Encoding::to_int_type('F'))
|
||||
|| (c >= Encoding::to_int_type('a') && c <= Encoding::to_int_type('f'));
|
||||
}
|
||||
|
||||
template <typename Encoding, typename IntType>
|
||||
LEXY_DSL_FUNC bool match_zero(IntType c)
|
||||
{
|
||||
return c == Encoding::to_int_type('0');
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
LEXY_DSL_FUNC unsigned value(CharT c)
|
||||
{
|
||||
if (c >= 'a')
|
||||
return static_cast<unsigned>(c) - 'a' + 10;
|
||||
else if (c >= 'A')
|
||||
return static_cast<unsigned>(c) - 'A' + 10;
|
||||
else if (c <= '9')
|
||||
return static_cast<unsigned>(c) - '0';
|
||||
else
|
||||
return unsigned(-1);
|
||||
}
|
||||
};
|
||||
} // namespace lexyd
|
||||
|
||||
//=== digit ===//
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Base>
|
||||
struct _zero : atom_base<_zero<Base>>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
if (!Base::template match<typename Reader::encoding>(reader.peek()))
|
||||
return false;
|
||||
else if (!Base::template match_zero<typename Reader::encoding>(reader.peek()))
|
||||
return false;
|
||||
|
||||
reader.bump();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader&, typename Reader::iterator pos)
|
||||
{
|
||||
return lexy::make_error<Reader, lexy::expected_char_class>(pos, "digit.zero");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Base>
|
||||
struct _nzero : atom_base<_nzero<Base>>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
if (!Base::template match<typename Reader::encoding>(reader.peek()))
|
||||
return false;
|
||||
else if (Base::template match_zero<typename Reader::encoding>(reader.peek()))
|
||||
return false;
|
||||
|
||||
reader.bump();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader& reader, typename Reader::iterator pos)
|
||||
{
|
||||
if (Base::template match_zero<typename Reader::encoding>(reader.peek()))
|
||||
return lexy::make_error<Reader, lexy::expected_char_class>(pos, "digit.non-zero");
|
||||
else
|
||||
return lexy::make_error<Reader, lexy::expected_char_class>(pos, Base::name());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Base>
|
||||
struct _digit : atom_base<_digit<Base>>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
if (!Base::template match<typename Reader::encoding>(reader.peek()))
|
||||
return false;
|
||||
|
||||
reader.bump();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader&, typename Reader::iterator pos)
|
||||
{
|
||||
return lexy::make_error<Reader, lexy::expected_char_class>(pos, Base::name());
|
||||
}
|
||||
|
||||
//=== dsl ===//
|
||||
LEXY_CONSTEVAL auto zero() const
|
||||
{
|
||||
return _zero<Base>{};
|
||||
}
|
||||
|
||||
LEXY_CONSTEVAL auto non_zero() const
|
||||
{
|
||||
return _nzero<Base>{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches a single digit.
|
||||
template <typename Base = decimal>
|
||||
constexpr auto digit = _digit<Base>{};
|
||||
} // namespace lexyd
|
||||
|
||||
//=== digits ===//
|
||||
namespace lexy
|
||||
{
|
||||
struct forbidden_leading_zero
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "forbidden leading zero";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Base, typename Sep, bool LeadingZero>
|
||||
LEXY_CONSTEVAL auto _make_digits()
|
||||
{
|
||||
constexpr auto d = digit<Base>;
|
||||
|
||||
auto tail = [d] {
|
||||
if constexpr (std::is_same_v<Sep, void>)
|
||||
return while_(d);
|
||||
else
|
||||
return while_(if_(Sep{}) + d);
|
||||
}();
|
||||
|
||||
if constexpr (LeadingZero)
|
||||
{
|
||||
return d + tail;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto zero = d.zero() + prevent<lexy::forbidden_leading_zero>(d);
|
||||
auto non_zero = d.non_zero() + tail;
|
||||
return zero / non_zero;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Base, typename Sep, bool LeadingZero>
|
||||
struct _digits : decltype(_make_digits<Base, Sep, LeadingZero>())
|
||||
{
|
||||
template <typename Pattern>
|
||||
LEXY_CONSTEVAL auto sep(Pattern) const
|
||||
{
|
||||
static_assert(lexy::is_pattern<Pattern>);
|
||||
return _digits<Base, Pattern, LeadingZero>{};
|
||||
}
|
||||
|
||||
LEXY_CONSTEVAL auto no_leading_zero() const
|
||||
{
|
||||
return _digits<Base, Sep, false>{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches a non-empty list of digits.
|
||||
template <typename Base = decimal>
|
||||
constexpr auto digits = _digits<Base, void, true>{};
|
||||
|
||||
constexpr auto digit_sep_underscore = LEXY_LIT("_");
|
||||
constexpr auto digit_sep_tick = LEXY_LIT("'");
|
||||
} // namespace lexyd
|
||||
|
||||
//=== n_digits ===//
|
||||
namespace lexyd
|
||||
{
|
||||
template <std::size_t N, typename Base, typename Sep>
|
||||
LEXY_CONSTEVAL auto _make_digits()
|
||||
{
|
||||
if constexpr (N == 0)
|
||||
return success;
|
||||
else
|
||||
{
|
||||
auto d = digit<Base>;
|
||||
if constexpr (std::is_same_v<Sep, void>)
|
||||
return d + _make_digits<N - 1, Base, Sep>();
|
||||
else
|
||||
return if_(Sep{}) + d + _make_digits<N - 1, Base, Sep>();
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t N, typename Base, typename Sep, bool LeadingZero>
|
||||
LEXY_CONSTEVAL auto _make_digits()
|
||||
{
|
||||
auto d = digit<Base>;
|
||||
if constexpr (LeadingZero)
|
||||
return d + _make_digits<N - 1, Base, Sep>();
|
||||
else
|
||||
return d.non_zero() + _make_digits<N - 1, Base, Sep>();
|
||||
}
|
||||
|
||||
template <std::size_t N, typename Base, typename Sep, bool LeadingZero>
|
||||
struct _ndigits : decltype(_make_digits<N, Base, Sep, LeadingZero>())
|
||||
{
|
||||
static_assert(N > 1);
|
||||
|
||||
template <typename Pattern>
|
||||
LEXY_CONSTEVAL auto sep(Pattern) const
|
||||
{
|
||||
static_assert(lexy::is_pattern<Pattern>);
|
||||
return _ndigits<N, Base, Pattern, LeadingZero>{};
|
||||
}
|
||||
|
||||
LEXY_CONSTEVAL auto no_leading_zero() const
|
||||
{
|
||||
return _ndigits<N, Base, Sep, false>{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches exactly N digits.
|
||||
template <std::size_t N, typename Base = decimal>
|
||||
constexpr auto n_digits = _ndigits<N, Base, void, true>{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_DIGIT_HPP_INCLUDED
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_ENCODE_HPP_INCLUDED
|
||||
#define LEXY_DSL_ENCODE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/bom.hpp>
|
||||
#include <lexy/dsl/choice.hpp>
|
||||
#include <lexy/dsl/if.hpp>
|
||||
#include <lexy/dsl/sequence.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Reader, typename Encoding, lexy::encoding_endianness Endianness>
|
||||
struct _encoded_reader // Single byte encodings
|
||||
{
|
||||
static_assert(sizeof(typename Encoding::char_type) == 1);
|
||||
|
||||
using encoding = Encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
using iterator = typename Reader::iterator;
|
||||
using canonical_reader = _encoded_reader<Reader, Encoding, Endianness>;
|
||||
|
||||
constexpr bool eof() const
|
||||
{
|
||||
return _reader.eof();
|
||||
}
|
||||
|
||||
constexpr auto peek() const
|
||||
{
|
||||
if (_reader.eof())
|
||||
return encoding::eof();
|
||||
else
|
||||
return encoding::to_int_type(static_cast<char_type>(*_reader.cur()));
|
||||
}
|
||||
|
||||
constexpr void bump()
|
||||
{
|
||||
_reader.bump();
|
||||
}
|
||||
|
||||
constexpr iterator cur() const
|
||||
{
|
||||
return _reader.cur();
|
||||
}
|
||||
|
||||
Reader& _reader;
|
||||
};
|
||||
template <typename Reader, lexy::encoding_endianness Endianness>
|
||||
struct _encoded_reader<Reader, lexy::utf16_encoding, Endianness>
|
||||
{
|
||||
using encoding = lexy::utf16_encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
using iterator = typename Reader::iterator;
|
||||
using canonical_reader = _encoded_reader<Reader, lexy::utf16_encoding, Endianness>;
|
||||
|
||||
constexpr bool eof() const
|
||||
{
|
||||
return peek() == eof();
|
||||
}
|
||||
|
||||
constexpr auto peek() const
|
||||
{
|
||||
auto copy = _reader;
|
||||
if (copy.eof())
|
||||
return encoding::eof();
|
||||
auto first = static_cast<char_type>(*copy.cur());
|
||||
|
||||
copy.bump();
|
||||
if (copy.eof())
|
||||
return encoding::eof();
|
||||
auto second = static_cast<char_type>(*copy.cur());
|
||||
|
||||
if constexpr (Endianness == lexy::encoding_endianness::little)
|
||||
return encoding::to_int_type(static_cast<char_type>((second << 8) | first));
|
||||
else
|
||||
return encoding::to_int_type(static_cast<char_type>((first << 8) | second));
|
||||
}
|
||||
|
||||
constexpr void bump()
|
||||
{
|
||||
_reader.bump();
|
||||
_reader.bump();
|
||||
}
|
||||
|
||||
constexpr iterator cur() const
|
||||
{
|
||||
return _reader.cur();
|
||||
}
|
||||
|
||||
Reader& _reader;
|
||||
};
|
||||
template <typename Reader, lexy::encoding_endianness Endianness>
|
||||
struct _encoded_reader<Reader, lexy::utf32_encoding, Endianness>
|
||||
{
|
||||
using encoding = lexy::utf32_encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
using iterator = typename Reader::iterator;
|
||||
using canonical_reader = _encoded_reader<Reader, lexy::utf32_encoding, Endianness>;
|
||||
|
||||
constexpr bool eof() const
|
||||
{
|
||||
return peek() == eof();
|
||||
}
|
||||
|
||||
constexpr auto peek() const
|
||||
{
|
||||
auto copy = _reader;
|
||||
if (copy.eof())
|
||||
return encoding::eof();
|
||||
auto first = static_cast<char_type>(*copy.cur());
|
||||
|
||||
copy.bump();
|
||||
if (copy.eof())
|
||||
return encoding::eof();
|
||||
auto second = static_cast<char_type>(*copy.cur());
|
||||
|
||||
copy.bump();
|
||||
if (copy.eof())
|
||||
return encoding::eof();
|
||||
auto third = static_cast<char_type>(*copy.cur());
|
||||
|
||||
copy.bump();
|
||||
if (copy.eof())
|
||||
return encoding::eof();
|
||||
auto fourth = static_cast<char_type>(*copy.cur());
|
||||
|
||||
if constexpr (Endianness == lexy::encoding_endianness::little)
|
||||
{
|
||||
auto c = (fourth << 24) | (third << 16) | (second << 8) | first;
|
||||
return encoding::to_int_type(static_cast<char_type>(c));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto c = (first << 24) | (second << 16) | (third << 8) | fourth;
|
||||
return encoding::to_int_type(static_cast<char_type>(c));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void bump()
|
||||
{
|
||||
_reader.bump();
|
||||
_reader.bump();
|
||||
_reader.bump();
|
||||
_reader.bump();
|
||||
}
|
||||
|
||||
constexpr iterator cur() const
|
||||
{
|
||||
return _reader.cur();
|
||||
}
|
||||
|
||||
Reader& _reader;
|
||||
};
|
||||
|
||||
template <typename Encoding, lexy::encoding_endianness Endianness>
|
||||
struct _encode_begin : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
using old_encoding = typename Reader::encoding;
|
||||
static_assert((std::is_same_v<old_encoding, lexy::default_encoding>)
|
||||
|| (std::is_same_v<old_encoding, lexy::raw_encoding>),
|
||||
"cannot re-encode input");
|
||||
|
||||
auto encoded_reader = _encoded_reader<Reader, Encoding, Endianness>{reader};
|
||||
return NextParser::parse(handler, encoded_reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct _encode_end : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename Encoding,
|
||||
lexy::encoding_endianness Endianness, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler,
|
||||
_encoded_reader<Reader, Encoding, Endianness>& reader,
|
||||
Args&&... args) -> typename Handler::result_type
|
||||
{
|
||||
return NextParser::parse(handler, reader._reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Encoding, lexy::encoding_endianness Endianness>
|
||||
struct _encode
|
||||
{
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto operator()(Rule rule) const
|
||||
{
|
||||
if constexpr (Endianness == lexy::encoding_endianness::bom)
|
||||
{
|
||||
if constexpr (sizeof(typename Encoding::char_type) == 1)
|
||||
{
|
||||
// The Endianness doesn't matter, just parse a BOM.
|
||||
return if_(bom<Encoding, lexy::encoding_endianness::little>)
|
||||
+ _encode<Encoding, lexy::encoding_endianness::little>{}(rule);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto encode_little = _encode<Encoding, lexy::encoding_endianness::little>{}(rule);
|
||||
auto encode_big = _encode<Encoding, lexy::encoding_endianness::big>{}(rule);
|
||||
|
||||
auto little = bom<Encoding, lexy::encoding_endianness::little> >> encode_little;
|
||||
auto big = bom<Encoding, lexy::encoding_endianness::big> >> encode_big;
|
||||
auto fallback = else_ >> encode_big;
|
||||
|
||||
return little | big | fallback;
|
||||
}
|
||||
}
|
||||
else
|
||||
return _encode_begin<Encoding, Endianness>{} + rule + _encode_end{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches the rule using the specified encoding.
|
||||
template <typename Encoding, lexy::encoding_endianness Endianness = lexy::encoding_endianness::bom>
|
||||
constexpr auto encode = _encode<Encoding, Endianness>{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_ENCODE_HPP_INCLUDED
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_EOF_HPP_INCLUDED
|
||||
#define LEXY_DSL_EOF_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/whitespace.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
struct _eof : atom_base<_eof>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
return reader.eof();
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader&, typename Reader::iterator pos)
|
||||
{
|
||||
return lexy::make_error<Reader, lexy::expected_char_class>(pos, "EOF");
|
||||
}
|
||||
|
||||
template <typename Whitespace>
|
||||
LEXY_CONSTEVAL auto operator[](Whitespace ws) const
|
||||
{
|
||||
return whitespaced(*this, ws);
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches EOF.
|
||||
constexpr auto eof = _eof{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_EOF_HPP_INCLUDED
|
||||
@@ -1,123 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_ERROR_HPP_INCLUDED
|
||||
#define LEXY_DSL_ERROR_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/type_name.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/if.hpp>
|
||||
#include <lexy/dsl/not.hpp>
|
||||
#include <lexy/dsl/peek.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Tag, typename Pattern>
|
||||
struct _err : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = true;
|
||||
|
||||
struct matcher
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&...) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if constexpr (!std::is_same_v<Pattern, void>)
|
||||
{
|
||||
if (auto begin = reader.cur(); Pattern::matcher::match(reader))
|
||||
{
|
||||
auto e = lexy::make_error<Reader, Tag>(begin, reader.cur());
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Pattern didn't match or we don't have one.
|
||||
auto e = lexy::make_error<Reader, Tag>(reader.cur());
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
};
|
||||
|
||||
/// Adds a pattern whose match will be part of the error location.
|
||||
template <typename P>
|
||||
LEXY_CONSTEVAL auto operator()(P) const
|
||||
{
|
||||
static_assert(lexy::is_pattern<P>);
|
||||
return _err<Tag, P>{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches nothing, produces an error with the given tag.
|
||||
template <typename Tag>
|
||||
constexpr auto error = _err<Tag, void>{};
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
/// Requires that lookahead will match a pattern at a location.
|
||||
template <typename Tag, typename Pattern>
|
||||
LEXY_CONSTEVAL auto require(Pattern pattern)
|
||||
{
|
||||
// If we don't get the pattern, we create a failure.
|
||||
// Otherwise, we match the empty string.
|
||||
return if_(peek(!pattern) >> error<Tag>);
|
||||
}
|
||||
|
||||
/// Requires that lookahead does not match a pattern at a location.
|
||||
template <typename Tag, typename Pattern>
|
||||
LEXY_CONSTEVAL auto prevent(Pattern pattern)
|
||||
{
|
||||
// Same as above, but we don't want to match the pattern.
|
||||
return if_(peek(pattern) >> error<Tag>);
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Tag, typename Pattern>
|
||||
struct _try : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = true;
|
||||
|
||||
using matcher = typename Pattern::matcher;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if (auto pos = reader.cur(); Pattern::matcher::match(reader))
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
{
|
||||
auto e = lexy::make_error<Reader, Tag>(pos, reader.cur());
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Tries to match the pattern, report a tagged failure if it doesn't match.
|
||||
template <typename Tag, typename Pattern>
|
||||
LEXY_CONSTEVAL auto try_(Pattern)
|
||||
{
|
||||
static_assert(lexy::is_pattern<Pattern>);
|
||||
return _try<Tag, Pattern>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_ERROR_HPP_INCLUDED
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_IF_HPP_INCLUDED
|
||||
#define LEXY_DSL_IF_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Condition, typename Then>
|
||||
struct _if : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = Then::has_matcher;
|
||||
|
||||
struct matcher
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
if (auto reset = reader; Condition::matcher::match(reader))
|
||||
{
|
||||
if (Then::matcher::match(reader))
|
||||
return true;
|
||||
else
|
||||
{
|
||||
reader = LEXY_MOV(reset);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader = LEXY_MOV(reset);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if (auto result = Condition::matcher::match(reader))
|
||||
return Then::template parser<NextParser>::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// If the branch condition matches, matches the branch then.
|
||||
template <typename Branch>
|
||||
LEXY_CONSTEVAL auto if_(Branch b)
|
||||
{
|
||||
static_assert(lexy::is_branch_rule<Branch>, "if_() requires a branch condition");
|
||||
|
||||
auto as_branch = branch(b);
|
||||
return _if<decltype(as_branch.condition()), decltype(as_branch.then())>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_IF_HPP_INCLUDED
|
||||
|
||||
@@ -1,345 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_INTEGER_HPP_INCLUDED
|
||||
#define LEXY_DSL_INTEGER_HPP_INCLUDED
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <lexy/_detail/assert.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/digit.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
// Number of digits to express the given value.
|
||||
template <typename Integer>
|
||||
constexpr std::size_t _digit_count(int radix, Integer value)
|
||||
{
|
||||
LEXY_PRECONDITION(value >= Integer(0));
|
||||
|
||||
if (value == 0)
|
||||
return 1;
|
||||
|
||||
std::size_t result = 0;
|
||||
while (value > 0)
|
||||
{
|
||||
value = Integer(value / Integer(radix));
|
||||
++result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct integer_traits
|
||||
{
|
||||
using _limits = std::numeric_limits<T>;
|
||||
static_assert(_limits::is_integer);
|
||||
|
||||
using type = T;
|
||||
|
||||
static constexpr auto is_bounded = _limits::is_bounded;
|
||||
|
||||
template <int Radix>
|
||||
static constexpr std::size_t max_digit_count = _digit_count(Radix, _limits::max());
|
||||
|
||||
template <int Radix>
|
||||
static constexpr void add_digit_unchecked(T& result, unsigned digit)
|
||||
{
|
||||
result = T(result * T(Radix) + T(digit));
|
||||
}
|
||||
|
||||
template <int Radix>
|
||||
static constexpr bool add_digit_checked(T& result, unsigned digit)
|
||||
{
|
||||
constexpr auto can_use_unsigned = [] {
|
||||
if constexpr (!std::is_integral_v<T>)
|
||||
// If it's not a built-in integer, be careful and don't do it.
|
||||
return false;
|
||||
else if constexpr (sizeof(T) >= sizeof(unsigned))
|
||||
// If it's bigger than unsigned, we can't use unsigned.
|
||||
return false;
|
||||
else
|
||||
{
|
||||
// We can do it if the worst-case does not overflow unsigned.
|
||||
auto worst_case = static_cast<unsigned>(_limits::max());
|
||||
return integer_traits<unsigned>::add_digit_checked<Radix>(worst_case, Radix - 1);
|
||||
}
|
||||
}();
|
||||
|
||||
// Check whether we can do an optimization for small integers,
|
||||
// where we do the operation on unsigned and check later.
|
||||
if constexpr (can_use_unsigned)
|
||||
{
|
||||
// This can't overflow, we've checked it above.
|
||||
auto value = static_cast<unsigned>(result) * Radix + digit;
|
||||
|
||||
// Check whether the final value can fit.
|
||||
if (value > static_cast<unsigned>(_limits::max()))
|
||||
return false;
|
||||
else
|
||||
{
|
||||
result = static_cast<T>(value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// result *= Radix
|
||||
constexpr auto max_per_radix = _limits::max() / Radix;
|
||||
if (result > max_per_radix)
|
||||
return false;
|
||||
result = T(result * Radix);
|
||||
|
||||
// result += digit
|
||||
if (result > T(_limits::max() - digit))
|
||||
return false;
|
||||
result = T(result + T(digit));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct integer_traits<code_point>
|
||||
{
|
||||
using type = code_point;
|
||||
|
||||
static constexpr auto is_bounded = true;
|
||||
|
||||
template <int Radix>
|
||||
static constexpr std::size_t max_digit_count = _digit_count(Radix, 0x10'FFFF);
|
||||
|
||||
template <int Radix>
|
||||
static constexpr void add_digit_unchecked(type& result, unsigned digit)
|
||||
{
|
||||
std::uint_least32_t value = result.value();
|
||||
integer_traits<std::uint_least32_t>::add_digit_unchecked<Radix>(value, digit);
|
||||
result = code_point(value);
|
||||
}
|
||||
template <int Radix>
|
||||
static constexpr bool add_digit_checked(type& result, unsigned digit)
|
||||
{
|
||||
std::uint_least32_t value = result.value();
|
||||
if (!integer_traits<std::uint_least32_t>::add_digit_checked<Radix>(value, digit))
|
||||
return false;
|
||||
result = code_point(value);
|
||||
return result.is_valid();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct unbounded
|
||||
{};
|
||||
template <typename T>
|
||||
struct integer_traits<unbounded<T>>
|
||||
{
|
||||
using type = typename integer_traits<T>::type;
|
||||
static constexpr auto is_bounded = false;
|
||||
|
||||
template <int Radix>
|
||||
static constexpr void add_digit_unchecked(type& result, unsigned digit)
|
||||
{
|
||||
integer_traits<T>::template add_digit_unchecked<Radix>(result, digit);
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct integer_overflow
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "integer overflow";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename T>
|
||||
constexpr bool _is_bounded = lexy::integer_traits<T>::is_bounded;
|
||||
|
||||
// Parses T in the Base while checking for overflow.
|
||||
template <typename T, typename Base>
|
||||
struct _unbounded_integer_parser
|
||||
{
|
||||
using traits = lexy::integer_traits<T>;
|
||||
using result_type = typename traits::type;
|
||||
|
||||
static constexpr auto radix = Base::radix;
|
||||
|
||||
template <typename Iterator>
|
||||
static constexpr bool parse(result_type& result, Iterator cur, Iterator end)
|
||||
{
|
||||
// Just parse digits until we've run out of digits.
|
||||
while (cur != end)
|
||||
{
|
||||
auto digit = Base::value(*cur++);
|
||||
if (digit >= Base::radix)
|
||||
// Skip digit separator.
|
||||
continue;
|
||||
|
||||
traits::template add_digit_unchecked<radix>(result, digit);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Parses T in the Base without checking for overflow.
|
||||
template <typename T, typename Base, bool AssumeOnlyDigits>
|
||||
struct _bounded_integer_parser
|
||||
{
|
||||
using traits = lexy::integer_traits<T>;
|
||||
using result_type = typename traits::type;
|
||||
|
||||
static constexpr auto radix = Base::radix;
|
||||
|
||||
template <typename Iterator>
|
||||
static constexpr unsigned find_digit(Iterator& cur, Iterator end)
|
||||
{
|
||||
auto digit = 0u;
|
||||
while (true)
|
||||
{
|
||||
if (cur == end)
|
||||
// No more digits.
|
||||
return unsigned(-1);
|
||||
|
||||
digit = Base::value(*cur++);
|
||||
if constexpr (AssumeOnlyDigits)
|
||||
break;
|
||||
else if (digit < Base::radix)
|
||||
break;
|
||||
}
|
||||
return digit;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static constexpr bool parse(result_type& result, Iterator cur, Iterator end)
|
||||
{
|
||||
constexpr auto max_digit_count = traits::template max_digit_count<radix>;
|
||||
static_assert(max_digit_count > 1,
|
||||
"integer must be able to store all possible digit values");
|
||||
|
||||
// Skip leading zeroes.
|
||||
while (true)
|
||||
{
|
||||
if (cur == end)
|
||||
return true; // We only had zeroes.
|
||||
|
||||
const auto digit = Base::value(*cur++);
|
||||
if (digit == 0 || digit >= radix)
|
||||
continue; // Zero or digit separator.
|
||||
|
||||
// First non-zero digit, so we can assign it instead of adding.
|
||||
result = result_type(digit);
|
||||
break;
|
||||
}
|
||||
// At this point, we've parsed exactly one non-zero digit.
|
||||
|
||||
// Handle max_digit_count - 1 digits without checking for overflow.
|
||||
// We cannot overflow, as the maximal value has one digit more.
|
||||
for (std::size_t digit_count = 1; digit_count < max_digit_count - 1; ++digit_count)
|
||||
{
|
||||
auto digit = find_digit(cur, end);
|
||||
if (digit == unsigned(-1))
|
||||
return true;
|
||||
|
||||
traits::template add_digit_unchecked<radix>(result, digit);
|
||||
}
|
||||
|
||||
// Handle the final digit, if there is any, while checking for overflow.
|
||||
{
|
||||
auto digit = find_digit(cur, end);
|
||||
if (digit == unsigned(-1))
|
||||
return true;
|
||||
|
||||
if (!traits::template add_digit_checked<radix>(result, digit))
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we've reached this point, we've parsed the maximal number of digits allowed.
|
||||
// Now we can only fail if there are still digits left.
|
||||
return cur == end;
|
||||
}
|
||||
};
|
||||
|
||||
// Continuation of integer that assumes the pattern is already dealt with.
|
||||
template <typename T, typename Base, bool AssumeOnlyDigits>
|
||||
struct _int_p : rule_base
|
||||
{
|
||||
using integer_parser
|
||||
= std::conditional_t<_is_bounded<T>, _bounded_integer_parser<T, Base, AssumeOnlyDigits>,
|
||||
_unbounded_integer_parser<T, Base>>;
|
||||
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename Iterator, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Iterator begin, Args&&... args)
|
||||
-> typename Handler::result_type
|
||||
{
|
||||
using error_type
|
||||
= lexy::error<typename Reader::canonical_reader, lexy::integer_overflow>;
|
||||
|
||||
auto result = typename integer_parser::result_type(0);
|
||||
if (integer_parser::parse(result, begin, reader.cur()))
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., result);
|
||||
else
|
||||
return LEXY_MOV(handler).error(reader, error_type(begin, reader.cur()));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Captures the pattern which is then parsed.
|
||||
// Must be followed by _int_p.
|
||||
template <typename Pattern>
|
||||
struct _int_c : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
return Pattern::template parser<NextParser>::parse(handler, reader, reader.cur(),
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Parses the digits matched by the pattern into an integer type.
|
||||
template <typename T, typename Base, typename Pattern>
|
||||
LEXY_CONSTEVAL auto integer(Pattern)
|
||||
{
|
||||
return _int_c<Pattern>{} + _int_p<T, Base, false>{};
|
||||
}
|
||||
template <typename T, typename Base, typename Sep, bool LeadingZero>
|
||||
LEXY_CONSTEVAL auto integer(_digits<Base, Sep, LeadingZero>)
|
||||
{
|
||||
return _int_c<_digits<Base, Sep, LeadingZero>>{} + _int_p<T, Base, std::is_void_v<Sep>>{};
|
||||
}
|
||||
template <typename T, typename Base, std::size_t N, typename Sep, bool LeadingZero>
|
||||
LEXY_CONSTEVAL auto integer(_ndigits<N, Base, Sep, LeadingZero>)
|
||||
{
|
||||
return _int_c<_ndigits<N, Base, Sep, LeadingZero>>{} + _int_p<T, Base, std::is_void_v<Sep>>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
/// Matches the number of a code point.
|
||||
template <std::size_t N, typename Base = hex>
|
||||
constexpr auto code_point_id = integer<lexy::code_point>(n_digits<N, Base>);
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_INTEGER_HPP_INCLUDED
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_LABEL_HPP_INCLUDED
|
||||
#define LEXY_DSL_LABEL_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/stateless_lambda.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename T, typename = void>
|
||||
struct label
|
||||
{};
|
||||
template <typename T>
|
||||
struct label<T, decltype(void(T::value))>
|
||||
{
|
||||
LEXY_CONSTEVAL operator decltype(T::value)() const
|
||||
{
|
||||
return T::value;
|
||||
}
|
||||
};
|
||||
|
||||
template <auto Id>
|
||||
using id = label<std::integral_constant<int, Id>>;
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Label>
|
||||
struct _lab : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., lexy::label<Label>{});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto operator()(Rule rule) const
|
||||
{
|
||||
if constexpr (lexy::is_branch_rule<Rule>)
|
||||
{
|
||||
auto as_branch = branch(rule);
|
||||
return as_branch.condition() >> *this + as_branch.then();
|
||||
}
|
||||
else
|
||||
{
|
||||
return *this + rule;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches with the specified label.
|
||||
template <typename Label>
|
||||
constexpr auto label = _lab<Label>{};
|
||||
|
||||
/// Matches with the specified id.
|
||||
template <auto Id>
|
||||
constexpr auto id = _lab<std::integral_constant<int, Id>>{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_LABEL_HPP_INCLUDED
|
||||
@@ -1,244 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_LIST_HPP_INCLUDED
|
||||
#define LEXY_DSL_LIST_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
#include <lexy/dsl/choice.hpp>
|
||||
#include <lexy/dsl/loop.hpp>
|
||||
#include <lexy/dsl/separator.hpp>
|
||||
#include <lexy/lexeme.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Rule>
|
||||
struct _it : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename... ParentArgs>
|
||||
struct _continuation
|
||||
{
|
||||
template <typename Handler, typename Reader, typename Sink, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Sink& sink,
|
||||
ParentArgs&&... pargs, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if constexpr (sizeof...(Args) > 0)
|
||||
sink(LEXY_FWD(args)...); // Forward item args to the sink.
|
||||
return NextParser::parse(handler, reader, sink, LEXY_FWD(pargs)...); // Continue.
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Handler, typename Reader, typename Sink, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Sink& sink, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
return Rule::template parser<_continuation<Args...>>::parse(handler, reader, sink,
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Parses the rule as a list item.
|
||||
/// Most be used inside of a `make_list()` rule.
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto item(Rule)
|
||||
{
|
||||
if constexpr (lexy::is_branch_rule<Rule>)
|
||||
{
|
||||
auto b = branch(Rule{});
|
||||
return b.condition() >> _it<decltype(b.then())>{};
|
||||
}
|
||||
else
|
||||
{
|
||||
return _it<Rule>{};
|
||||
}
|
||||
}
|
||||
template <typename... Rs>
|
||||
LEXY_CONSTEVAL auto item(_chc<Rs...>)
|
||||
{
|
||||
return (item(Rs{}) | ...);
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Rule>
|
||||
struct _lst : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
struct _continuation
|
||||
{
|
||||
template <typename Handler, typename Reader, typename Sink, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Sink& sink, Args&&... args)
|
||||
-> typename Handler::result_type
|
||||
{
|
||||
if constexpr (std::is_same_v<typename Sink::return_type, void>)
|
||||
{
|
||||
LEXY_MOV(sink).finish();
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...,
|
||||
LEXY_MOV(sink).finish());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
auto sink = handler.list_sink();
|
||||
return Rule::template parser<_continuation>::parse(handler, reader, sink,
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Matches the rule and stores its values using the list interface.
|
||||
/// Subrules marked as `item()` will be forwarded to the list sink.
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto build_list(Rule)
|
||||
{
|
||||
return _lst<Rule>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
// Takes care of the separator logic in a list.
|
||||
template <typename Pattern, bool Capture>
|
||||
struct _lsts : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename Sink, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(_loop_handler<Handler>& handler, Reader& reader, Sink& sink,
|
||||
Args&&... args) -> typename _loop_handler<Handler>::result_type
|
||||
{
|
||||
auto begin = reader.cur();
|
||||
if (!Pattern::matcher::match(reader))
|
||||
// No separator, done with the list.
|
||||
return LEXY_MOV(handler).break_();
|
||||
|
||||
if constexpr (Capture)
|
||||
sink(lexy::lexeme(reader, begin));
|
||||
else
|
||||
(void)begin;
|
||||
|
||||
return NextParser::parse(handler, reader, sink, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Takes care of the item in a list if it's a branch.
|
||||
template <typename Item>
|
||||
struct _lsti : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename Sink, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(_loop_handler<Handler>& handler, Reader& reader, Sink& sink,
|
||||
Args&&... args) -> typename _loop_handler<Handler>::result_type
|
||||
{
|
||||
using branch = decltype(branch(Item{}));
|
||||
|
||||
if (!branch::condition_matcher::match(reader))
|
||||
// Item doesn't match, done with the list.
|
||||
return LEXY_MOV(handler).break_();
|
||||
|
||||
using rule = decltype(item(branch::then()));
|
||||
return rule::template parser<NextParser>::parse(handler, reader, sink,
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Creates a list of items without a separator.
|
||||
template <typename Item>
|
||||
LEXY_CONSTEVAL auto list(Item it)
|
||||
{
|
||||
static_assert(lexy::is_branch_rule<Item>,
|
||||
"list() without a separator requires a branch condition");
|
||||
auto b = branch(it);
|
||||
|
||||
auto head = item(b.then());
|
||||
auto tail = loop(_lsti<Item>{});
|
||||
return b.condition() >> build_list(head + tail);
|
||||
}
|
||||
template <typename... Items>
|
||||
LEXY_CONSTEVAL auto list(_chc<Items...> choice)
|
||||
{
|
||||
auto head = item(choice);
|
||||
auto tail = loop(item(choice | else_ >> break_));
|
||||
return build_list(head + tail);
|
||||
}
|
||||
|
||||
/// Creates a list of items with the specified separator.
|
||||
template <typename Item, typename Pattern, bool Capture>
|
||||
LEXY_CONSTEVAL auto list(Item it, _sep<Pattern, Capture>)
|
||||
{
|
||||
if constexpr (lexy::is_branch_rule<Item>)
|
||||
{
|
||||
auto b = branch(it);
|
||||
|
||||
auto head = item(b.then());
|
||||
auto tail = loop(_lsts<Pattern, Capture>{} + item(it));
|
||||
return b.condition() >> build_list(head + tail);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto head = item(it);
|
||||
auto tail = loop(_lsts<Pattern, Capture>{} + item(it));
|
||||
return build_list(head + tail);
|
||||
}
|
||||
}
|
||||
template <typename... Items, typename Pattern, bool Capture>
|
||||
LEXY_CONSTEVAL auto list(_chc<Items...> choice, _sep<Pattern, Capture>)
|
||||
{
|
||||
auto head = item(choice);
|
||||
auto tail = loop(_lsts<Pattern, Capture>{} + item(choice));
|
||||
return build_list(head + tail);
|
||||
}
|
||||
|
||||
/// Creates a list of items with the specified separator that can be trailing.
|
||||
template <typename Item, typename Pattern, bool Capture>
|
||||
LEXY_CONSTEVAL auto list(Item it, _tsep<Pattern, Capture>)
|
||||
{
|
||||
static_assert(lexy::is_branch_rule<Item>,
|
||||
"list() without a trailing separator requires a branch condition");
|
||||
auto b = branch(it);
|
||||
|
||||
auto head = item(b.then());
|
||||
auto tail = loop(_lsts<Pattern, Capture>{} + _lsti<Item>{});
|
||||
return b.condition() >> build_list(head + tail);
|
||||
}
|
||||
template <typename... Items, typename Pattern, bool Capture>
|
||||
LEXY_CONSTEVAL auto list(_chc<Items...> choice, _tsep<Pattern, Capture>)
|
||||
{
|
||||
auto head = item(choice);
|
||||
auto tail = loop(_lsts<Pattern, Capture>{} + item(choice | else_ >> break_));
|
||||
return build_list(head + tail);
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_LIST_HPP_INCLUDED
|
||||
@@ -1,85 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_LITERAL_HPP_INCLUDED
|
||||
#define LEXY_DSL_LITERAL_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/nttp_string.hpp>
|
||||
#include <lexy/_detail/string_view.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/whitespace.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename String>
|
||||
struct _lit : atom_base<_lit<String>>
|
||||
{
|
||||
template <typename Reader>
|
||||
static LEXY_CONSTEVAL bool _string_compatible()
|
||||
{
|
||||
using encoding = typename Reader::encoding;
|
||||
if (lexy::char_type_compatible_with_reader<Reader, typename String::char_type>)
|
||||
return true;
|
||||
|
||||
// The string and the input have incompatible character types.
|
||||
// We then only allow ASCII characters in the string literal.
|
||||
for (auto c : String::get())
|
||||
{
|
||||
auto value = encoding::to_int_type(typename encoding::char_type(c));
|
||||
if (value < encoding::to_int_type(0) || value > encoding::to_int_type(0x7F))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
static_assert(_string_compatible<Reader>(),
|
||||
"literal contains characters not compatible with input encoding");
|
||||
|
||||
for (auto c : String::get())
|
||||
{
|
||||
if (reader.peek() != Reader::encoding::to_int_type(typename Reader::char_type(c)))
|
||||
return false;
|
||||
reader.bump();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader& reader, typename Reader::iterator pos)
|
||||
{
|
||||
using error = lexy::error<typename Reader::canonical_reader, lexy::expected_literal>;
|
||||
auto idx = lexy::_detail::range_size(pos, reader.cur());
|
||||
|
||||
using reader_char_type = typename Reader::encoding::char_type;
|
||||
return error(pos, String::template get<reader_char_type>(), idx);
|
||||
}
|
||||
|
||||
//=== dsl ===//
|
||||
template <typename Whitespace>
|
||||
LEXY_CONSTEVAL auto operator[](Whitespace ws) const
|
||||
{
|
||||
return whitespaced(*this, ws);
|
||||
}
|
||||
};
|
||||
|
||||
template <auto C>
|
||||
constexpr auto lit_c = _lit<lexy::_detail::type_char<C>>{};
|
||||
|
||||
#if LEXY_HAS_NTTP
|
||||
/// Matches the literal string.
|
||||
template <lexy::_detail::string_literal Str>
|
||||
constexpr auto lit = _lit<lexy::_detail::type_string<Str>>{};
|
||||
#endif
|
||||
|
||||
#define LEXY_LIT(Str) \
|
||||
::lexyd::_lit<LEXY_NTTP_STRING(Str)> {}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_LITERAL_HPP_INCLUDED
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_LOOKAHEAD_HPP_INCLUDED
|
||||
#define LEXY_DSL_LOOKAHEAD_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/whitespace.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct lookahead_failure
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "lookahead failure";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Needle, typename End>
|
||||
struct _look : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = true;
|
||||
|
||||
struct matcher
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool _match(Reader& reader)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (Needle::matcher::match(reader))
|
||||
// We found the lookahead condition.
|
||||
break;
|
||||
else if (End::matcher::match(reader))
|
||||
// We haven't found the lookahead condition and need to cancel.
|
||||
return false;
|
||||
else if (reader.eof())
|
||||
// Dito.
|
||||
return false;
|
||||
else
|
||||
reader.bump();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
// We're matching on a copy of the reader so we don't change the input.
|
||||
auto copy = reader;
|
||||
return _match(copy);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if (auto copy = reader; matcher::_match(copy))
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
{
|
||||
auto e
|
||||
= lexy::make_error<Reader, lexy::lookahead_failure>(reader.cur(), copy.cur());
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Whitespace>
|
||||
LEXY_CONSTEVAL auto operator[](Whitespace ws) const
|
||||
{
|
||||
return whitespaced(*this, ws);
|
||||
}
|
||||
};
|
||||
|
||||
/// Looks for the Needle pattern before End pattern.
|
||||
/// Used as condition to implement arbitrary lookahead.
|
||||
template <typename Needle, typename End>
|
||||
LEXY_CONSTEVAL auto lookahead(Needle, End)
|
||||
{
|
||||
static_assert(lexy::is_pattern<Needle> && lexy::is_pattern<End>);
|
||||
return _look<Needle, End>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_LOOKAHEAD_HPP_INCLUDED
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_LOOP_HPP_INCLUDED
|
||||
#define LEXY_DSL_LOOP_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/result.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
// The handler used for parsing the rules inside a loop.
|
||||
// It will package any error nicely and return it to us.
|
||||
template <typename Handler>
|
||||
struct _loop_handler
|
||||
{
|
||||
Handler& _handler;
|
||||
bool _break;
|
||||
|
||||
using result_type = lexy::result<void, typename Handler::result_type>;
|
||||
|
||||
template <typename SubProduction, typename Reader>
|
||||
constexpr auto sub_handler(const Reader& reader)
|
||||
{
|
||||
return _handler.template sub_handler<SubProduction>(reader);
|
||||
}
|
||||
|
||||
constexpr auto list_sink()
|
||||
{
|
||||
return _handler.sink();
|
||||
}
|
||||
|
||||
template <typename Reader, typename Error>
|
||||
constexpr auto error(const Reader& reader, Error&& error) &&
|
||||
{
|
||||
// We report errors to the normal handler.
|
||||
return result_type(lexy::result_error, LEXY_MOV(_handler).error(reader, LEXY_FWD(error)));
|
||||
}
|
||||
|
||||
constexpr auto value() &&
|
||||
{
|
||||
return result_type(lexy::result_value);
|
||||
}
|
||||
|
||||
constexpr auto break_() &&
|
||||
{
|
||||
_break = true;
|
||||
return result_type(lexy::result_value);
|
||||
}
|
||||
};
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
struct _break : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(_loop_handler<Handler>& handler, Reader&, Args&&...) ->
|
||||
typename _loop_handler<Handler>::result_type
|
||||
{
|
||||
return LEXY_MOV(handler).break_();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Exits a loop().
|
||||
constexpr auto break_ = _break{};
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename... PrevArgs>
|
||||
struct _loop_iter_parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(_loop_handler<Handler>& handler, Reader&, PrevArgs&&..., Args&&...) ->
|
||||
typename _loop_handler<Handler>::result_type
|
||||
{
|
||||
static_assert(sizeof...(Args) == 0, "looped rule must not add any values");
|
||||
return LEXY_MOV(handler).value();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Rule>
|
||||
struct _loop : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
_loop_handler<Handler> loop_handler{handler, false};
|
||||
while (!loop_handler._break)
|
||||
{
|
||||
using continuation = _loop_iter_parser<Args...>;
|
||||
auto result = Rule::template parser<continuation>::parse(loop_handler, reader,
|
||||
LEXY_FWD(args)...);
|
||||
if (!result)
|
||||
return LEXY_MOV(result).error();
|
||||
}
|
||||
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Repeatedly matches the rule until a break rule matches.
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto loop(Rule)
|
||||
{
|
||||
return _loop<Rule>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_LOOP_HPP_INCLUDED
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_MATCH_HPP_INCLUDED
|
||||
#define LEXY_DSL_MATCH_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/whitespace.hpp>
|
||||
#include <lexy/match.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct no_match
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "no match";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Rule>
|
||||
struct _match : atom_base<_match<Rule>>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
lexy::_match_handler handler{};
|
||||
return Rule::template parser<lexy::final_parser>::parse(handler, reader).has_value();
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader&, typename Reader::iterator pos)
|
||||
{
|
||||
return lexy::make_error<Reader, lexy::no_match>(pos);
|
||||
}
|
||||
|
||||
template <typename Whitespace>
|
||||
LEXY_CONSTEVAL auto operator[](Whitespace ws) const
|
||||
{
|
||||
return whitespaced(*this, ws);
|
||||
}
|
||||
};
|
||||
|
||||
/// Turns the arbitrary rule into a pattern by matching it without producing any values.
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto match(Rule)
|
||||
{
|
||||
return _match<Rule>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_MATCH_HPP_INCLUDED
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_MEMBER_HPP_INCLUDED
|
||||
#define LEXY_DSL_MEMBER_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/stateless_lambda.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <auto Ptr>
|
||||
struct _mem_ptr_fn
|
||||
{
|
||||
template <typename Object, typename Value>
|
||||
constexpr void operator()(Object& object, Value&& value) const
|
||||
{
|
||||
object.*Ptr = LEXY_FWD(value);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
struct member
|
||||
{};
|
||||
|
||||
template <auto Ptr>
|
||||
using make_member_ptr = member<_mem_ptr_fn<Ptr>>;
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Fn>
|
||||
struct _mem : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., lexy::member<Fn>{});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
struct _mem_dsl
|
||||
{
|
||||
LEXY_CONSTEVAL _mem_dsl(Fn = {}) {}
|
||||
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto operator=(Rule rule) const
|
||||
{
|
||||
using lambda = std::conditional_t<std::is_default_constructible_v<Fn>, Fn,
|
||||
lexy::_detail::stateless_lambda<Fn>>;
|
||||
using mem_rule = _mem<lambda>;
|
||||
|
||||
if constexpr (lexy::is_branch_rule<Rule>)
|
||||
{
|
||||
auto as_branch = branch(rule);
|
||||
return as_branch.condition() >> mem_rule{} + as_branch.then();
|
||||
}
|
||||
else
|
||||
{
|
||||
return mem_rule{} + rule;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Specifies that the output of the associated rule should be stored in the member pointer. Used
|
||||
/// with `lexy::as_aggregate`.
|
||||
template <auto MemPtr>
|
||||
constexpr auto member = _mem_dsl<lexy::_mem_ptr_fn<MemPtr>>{};
|
||||
|
||||
#define LEXY_MEM(Name) \
|
||||
::lexyd::_mem_dsl([](auto& obj, auto&& value) { obj.Name = LEXY_FWD(value); })
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_MEMBER_HPP_INCLUDED
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_MINUS_HPP_INCLUDED
|
||||
#define LEXY_DSL_MINUS_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
/// We've matched the Except of a minus.
|
||||
struct minus_failure
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "minus failure";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Rule, typename Except>
|
||||
struct _minus : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = Rule::has_matcher;
|
||||
|
||||
struct matcher
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
auto save = reader;
|
||||
|
||||
// First check whether the original rule matched.
|
||||
if (!Rule::matcher::match(reader))
|
||||
return false;
|
||||
|
||||
// Then match Except on the same input.
|
||||
if (auto partial = lexy::partial_reader(save, reader.cur());
|
||||
Except::matcher::match(partial) && partial.eof())
|
||||
{
|
||||
// It did, so we don't match after all.
|
||||
reader = LEXY_MOV(save);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
struct _continuation
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Reader save, Args&&... args)
|
||||
-> typename Handler::result_type
|
||||
{
|
||||
// At this point, we've matched the rule.
|
||||
// Check, whether Except matches as well on the same input.
|
||||
if (auto partial = lexy::partial_reader(save, reader.cur());
|
||||
Except::matcher::match(partial) && partial.eof())
|
||||
{
|
||||
// It did, so we don't match after all.
|
||||
auto e
|
||||
= lexy::make_error<Reader, lexy::minus_failure>(save.cur(), reader.cur());
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
// Parse the Rule, but remember the current input by copying the reader.
|
||||
return Rule::template parser<_continuation>::parse(handler, reader, Reader(reader),
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Matches Rule unless Except matches on the input Rule matched.
|
||||
template <typename Rule, typename Except>
|
||||
LEXY_CONSTEVAL auto operator-(Rule, Except)
|
||||
{
|
||||
static_assert(lexy::is_pattern<Except>);
|
||||
return _minus<Rule, Except>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_MINUS_HPP_INCLUDED
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_NEWLINE_HPP_INCLUDED
|
||||
#define LEXY_DSL_NEWLINE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/whitespace.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
struct _nl : atom_base<_nl>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
if (auto cur = reader.peek(); cur == Reader::encoding::to_int_type('\n'))
|
||||
{
|
||||
reader.bump();
|
||||
return true;
|
||||
}
|
||||
else if (cur == Reader::encoding::to_int_type('\r'))
|
||||
{
|
||||
reader.bump();
|
||||
if (reader.peek() == Reader::encoding::to_int_type('\n'))
|
||||
{
|
||||
reader.bump();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // Single '\r' not allowed.
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader&, typename Reader::iterator pos)
|
||||
{
|
||||
return lexy::make_error<Reader, lexy::expected_char_class>(pos, "newline");
|
||||
}
|
||||
|
||||
template <typename Whitespace>
|
||||
LEXY_CONSTEVAL auto operator[](Whitespace ws) const
|
||||
{
|
||||
return whitespaced(*this, ws);
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches a newline character.
|
||||
constexpr auto newline = _nl{};
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
struct _eol : atom_base<_eol>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
return reader.eof() || _nl::match(reader);
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader&, typename Reader::iterator pos)
|
||||
{
|
||||
return lexy::make_error<Reader, lexy::expected_char_class>(pos, "EOL");
|
||||
}
|
||||
|
||||
template <typename Whitespace>
|
||||
LEXY_CONSTEVAL auto operator[](Whitespace ws) const
|
||||
{
|
||||
return whitespaced(*this, ws);
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches the end of line (EOF or newline).
|
||||
constexpr auto eol = _eol{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_NEWLINE_HPP_INCLUDED
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_NOT_HPP_INCLUDED
|
||||
#define LEXY_DSL_NOT_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct unexpected
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "unexpected";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Pattern>
|
||||
struct _not : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = true;
|
||||
|
||||
struct matcher
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
return !Pattern::matcher::match(reader);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if (auto pos = reader.cur(); Pattern::matcher::match(reader))
|
||||
{
|
||||
auto e = lexy::make_error<Reader, lexy::unexpected>(pos, reader.cur());
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
else
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Only matches if the pattern didn't match.
|
||||
/// If this fails, it still has consumed the pattern.
|
||||
/// It is designed for something like `opt(!pattern >> then)`,
|
||||
/// which matches the pattern if we don't match then.
|
||||
template <typename Pattern, typename = std::enable_if_t<lexy::is_rule<Pattern>>>
|
||||
LEXY_CONSTEVAL auto operator!(Pattern)
|
||||
{
|
||||
static_assert(lexy::is_pattern<Pattern>);
|
||||
return _not<Pattern>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_NOT_HPP_INCLUDED
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_OPTION_HPP_INCLUDED
|
||||
#define LEXY_DSL_OPTION_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct nullopt
|
||||
{
|
||||
template <typename T, typename = std::enable_if_t<std::is_default_constructible_v<T>>>
|
||||
constexpr operator T() const
|
||||
{
|
||||
return T();
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
struct _nullopt : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., lexy::nullopt{});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
constexpr auto nullopt = _nullopt{};
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Condition, typename Then>
|
||||
struct _opt : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if (auto result = Condition::matcher::match(reader))
|
||||
return Then::template parser<NextParser>::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., lexy::nullopt{});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Matches the rule or nothing.
|
||||
/// In the latter case, produces a `nullopt` value.
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto opt(Rule rule)
|
||||
{
|
||||
static_assert(lexy::is_branch_rule<Rule>, "opt() requires a branch condition");
|
||||
|
||||
auto as_branch = branch(rule);
|
||||
return _opt<decltype(as_branch.condition()), decltype(as_branch.then())>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_OPTION_HPP_INCLUDED
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_PEEK_HPP_INCLUDED
|
||||
#define LEXY_DSL_PEEK_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/whitespace.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Pattern>
|
||||
struct _peek : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = true;
|
||||
|
||||
struct matcher
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
auto copy = reader;
|
||||
return Pattern::matcher::match(copy);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
struct _continuation
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Reader save, Args&&... args)
|
||||
-> typename Handler::result_type
|
||||
{
|
||||
// We continue with the reader as it was before we parsed the pattern.
|
||||
reader = LEXY_MOV(save);
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
// We parse the Pattern but remember the previous reader to reset it.
|
||||
return Pattern::template parser<_continuation>::parse(handler, reader, Reader(reader),
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Whitespace>
|
||||
LEXY_CONSTEVAL auto operator[](Whitespace ws) const
|
||||
{
|
||||
return whitespaced(*this, ws);
|
||||
}
|
||||
};
|
||||
|
||||
/// Check if at this reader position, Pattern would match, but don't actually consume any characters
|
||||
/// if it does.
|
||||
template <typename Pattern>
|
||||
LEXY_CONSTEVAL auto peek(Pattern)
|
||||
{
|
||||
static_assert(lexy::is_pattern<Pattern>);
|
||||
return _peek<Pattern>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_PEEK_HPP_INCLUDED
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_POSITION_HPP_INCLUDED
|
||||
#define LEXY_DSL_POSITION_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
struct _pos : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., reader.cur());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Produces an iterator to the current reader position without parsing anything.
|
||||
constexpr auto position = _pos{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_POSITION_HPP_INCLUDED
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_PRODUCTION_HPP_INCLUDED
|
||||
#define LEXY_DSL_PRODUCTION_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
#include <lexy/dsl/whitespace.hpp>
|
||||
#include <lexy/result.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Production>
|
||||
using _production_rule = std::remove_const_t<decltype(Production::rule)>;
|
||||
|
||||
// Not inline: one function per production.
|
||||
template <typename Rule, typename Handler, typename Reader>
|
||||
constexpr auto _parse(Handler& handler, Reader& reader) -> typename Handler::result_type
|
||||
{
|
||||
return Rule::template parser<lexy::final_parser>::parse(handler, reader);
|
||||
}
|
||||
|
||||
template <typename Production, typename Rule, typename NextParser>
|
||||
struct _prd_parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
auto&& sub_handler = handler.template sub_handler<Production>(reader);
|
||||
|
||||
if (auto result = _parse<Rule>(sub_handler, reader))
|
||||
{
|
||||
if constexpr (result.has_void_value())
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...,
|
||||
LEXY_MOV(result).value());
|
||||
}
|
||||
else
|
||||
return typename Handler::result_type(LEXY_MOV(result));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Production, typename Rule = void>
|
||||
struct _prd : rule_base
|
||||
{
|
||||
using _rule = std::conditional_t<std::is_void_v<Rule>, _production_rule<Production>, Rule>;
|
||||
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
using parser = _prd_parser<Production, _rule, NextParser>;
|
||||
|
||||
// Allow using the production as a branch, if its rule is a branch rule.
|
||||
template <typename R = _rule, typename = std::enable_if_t<lexy::is_branch_rule<R>>>
|
||||
friend LEXY_CONSTEVAL auto branch(_prd)
|
||||
{
|
||||
using branch_rule = decltype(branch(_rule()));
|
||||
return branch_rule::condition() >> _prd<Production, decltype(branch_rule::then())>{};
|
||||
}
|
||||
|
||||
template <typename Whitespace>
|
||||
LEXY_CONSTEVAL auto operator[](Whitespace ws) const
|
||||
{
|
||||
return whitespaced(*this, ws);
|
||||
}
|
||||
};
|
||||
|
||||
/// Parses the production.
|
||||
template <typename Production>
|
||||
constexpr auto p = _prd<Production>{};
|
||||
|
||||
template <typename Production>
|
||||
struct _rec : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser : _prd_parser<Production, _production_rule<Production>, NextParser>
|
||||
{};
|
||||
|
||||
template <typename Whitespace>
|
||||
LEXY_CONSTEVAL auto operator[](Whitespace ws) const
|
||||
{
|
||||
return whitespaced(*this, ws);
|
||||
}
|
||||
};
|
||||
|
||||
/// Parses the production, recursively.
|
||||
/// `dsl::p` requires that the production is already defined in order to propagate a branch
|
||||
/// condition outwards.
|
||||
template <typename Production>
|
||||
constexpr auto recurse = _rec<Production>{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_PRODUCTION_HPP_INCLUDED
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_PUNCTUATOR_HPP_INCLUDED
|
||||
#define LEXY_DSL_PUNCTUATOR_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/literal.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
constexpr auto period = LEXY_LIT(".");
|
||||
constexpr auto comma = LEXY_LIT(",");
|
||||
constexpr auto colon = LEXY_LIT(":");
|
||||
constexpr auto semicolon = LEXY_LIT(";");
|
||||
|
||||
constexpr auto hyphen = LEXY_LIT("-");
|
||||
constexpr auto slash = LEXY_LIT("/");
|
||||
constexpr auto backslash = LEXY_LIT("\\");
|
||||
constexpr auto apostrophe = LEXY_LIT("'");
|
||||
|
||||
constexpr auto hash_sign = LEXY_LIT("#");
|
||||
constexpr auto dollar_sign = LEXY_LIT("$");
|
||||
constexpr auto at_sign = LEXY_LIT("@");
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_PUNCTUATOR_HPP_INCLUDED
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_RETURN_HPP_INCLUDED
|
||||
#define LEXY_DSL_RETURN_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
struct _ret : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
// We unconditionally jump to the final parser.
|
||||
template <typename NextParser>
|
||||
using parser = lexy::final_parser;
|
||||
};
|
||||
|
||||
/// Finishes parsing a production without considering subsequent rules.
|
||||
constexpr auto return_ = _ret{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_RETURN_HPP_INCLUDED
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_SEPARATOR_HPP_INCLUDED
|
||||
#define LEXY_DSL_SEPARATOR_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Pattern, bool Capture>
|
||||
struct _sep
|
||||
{
|
||||
LEXY_CONSTEVAL auto capture() const
|
||||
{
|
||||
return _sep<Pattern, true>{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Defines a separator pattern for a list.
|
||||
template <typename Pattern>
|
||||
LEXY_CONSTEVAL auto sep(Pattern)
|
||||
{
|
||||
static_assert(lexy::is_pattern<Pattern>);
|
||||
return _sep<Pattern, false>{};
|
||||
}
|
||||
|
||||
template <typename Pattern, bool Capture>
|
||||
struct _tsep
|
||||
{
|
||||
LEXY_CONSTEVAL auto capture() const
|
||||
{
|
||||
return _tsep<Pattern, true>{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Defines a separator pattern for a list that can be trailing.
|
||||
template <typename Pattern>
|
||||
LEXY_CONSTEVAL auto trailing_sep(Pattern)
|
||||
{
|
||||
static_assert(lexy::is_pattern<Pattern>);
|
||||
return _tsep<Pattern, false>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_SEPARATOR_HPP_INCLUDED
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_SEQUENCE_HPP_INCLUDED
|
||||
#define LEXY_DSL_SEQUENCE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename NextParser, typename... R>
|
||||
struct _seq_parser;
|
||||
template <typename NextParser>
|
||||
struct _seq_parser<NextParser>
|
||||
{
|
||||
using type = NextParser;
|
||||
};
|
||||
template <typename NextParser, typename H, typename... T>
|
||||
struct _seq_parser<NextParser, H, T...>
|
||||
{
|
||||
using tail = typename _seq_parser<NextParser, T...>::type;
|
||||
using type = typename H::template parser<tail>;
|
||||
};
|
||||
|
||||
template <typename... R>
|
||||
struct _seq : rule_base
|
||||
{
|
||||
static_assert(sizeof...(R) > 1);
|
||||
static constexpr auto has_matcher = (R::has_matcher && ...);
|
||||
|
||||
struct matcher
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
auto reset = reader;
|
||||
if ((R::matcher::match(reader) && ...))
|
||||
return true;
|
||||
|
||||
reader = LEXY_MOV(reset);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
using parser = typename _seq_parser<NextParser, R...>::type;
|
||||
};
|
||||
template <>
|
||||
struct _seq<> : atom_base<_seq<>>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC void error(const Reader&, typename Reader::iterator);
|
||||
};
|
||||
|
||||
/// Matches the empty string, always succeeds.
|
||||
constexpr auto success = _seq<>{};
|
||||
|
||||
template <typename R, typename S>
|
||||
LEXY_CONSTEVAL auto operator+(R, S)
|
||||
{
|
||||
return _seq<R, S>{};
|
||||
}
|
||||
template <typename... R, typename S>
|
||||
LEXY_CONSTEVAL auto operator+(_seq<R...>, S)
|
||||
{
|
||||
if constexpr (sizeof...(R) == 0)
|
||||
return S{};
|
||||
else
|
||||
return _seq<R..., S>{};
|
||||
}
|
||||
template <typename R, typename... S>
|
||||
LEXY_CONSTEVAL auto operator+(R, _seq<S...>)
|
||||
{
|
||||
if constexpr (sizeof...(S) == 0)
|
||||
return R{};
|
||||
else
|
||||
return _seq<R, S...>{};
|
||||
}
|
||||
template <typename... R, typename... S>
|
||||
LEXY_CONSTEVAL auto operator+(_seq<R...>, _seq<S...>)
|
||||
{
|
||||
if constexpr (sizeof...(R) == 0)
|
||||
return _seq<S...>{};
|
||||
else if constexpr (sizeof...(S) == 0)
|
||||
return _seq<R...>{};
|
||||
else
|
||||
return _seq<R..., S...>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_SEQUENCE_HPP_INCLUDED
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_SIGN_HPP_INCLUDED
|
||||
#define LEXY_DSL_SIGN_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
#include <lexy/dsl/choice.hpp>
|
||||
#include <lexy/dsl/literal.hpp>
|
||||
#include <lexy/dsl/value.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
/// Matches a plus sign or nothing, producing +1.
|
||||
constexpr auto plus_sign = LEXY_LIT("+") >> value_c<+1> | else_ >> value_c<+1>;
|
||||
/// Matches a minus sign or nothing, producing +1 or -1.
|
||||
constexpr auto minus_sign = LEXY_LIT("-") >> value_c<-1> | else_ >> value_c<+1>;
|
||||
|
||||
/// Matches a plus or minus sign or nothing, producing +1 or -1.
|
||||
constexpr auto sign
|
||||
= LEXY_LIT("+") >> value_c<+1> | LEXY_LIT("-") >> value_c<-1> | else_ >> value_c<+1>;
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_SIGN_HPP_INCLUDED
|
||||
@@ -1,153 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is partialject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_SWITCH_HPP_INCLUDED
|
||||
#define LEXY_DSL_SWITCH_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/any.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
#include <lexy/dsl/error.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct exhausted_switch
|
||||
{
|
||||
static LEXY_CONSTEVAL auto name()
|
||||
{
|
||||
return "exhausted switch";
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
// Continues with the rest of the input after we've handled the switch.
|
||||
template <typename NextParser>
|
||||
struct _switch_continue
|
||||
{
|
||||
template <typename Handler, typename PartialReader, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, PartialReader&, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
// We now continue with the regular reader again.
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
// Selects the appropriate case after the switch rule has been matched.
|
||||
template <typename NextParser, typename... Cases>
|
||||
struct _switch_select;
|
||||
template <typename NextParser>
|
||||
struct _switch_select<NextParser>
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Reader save, Args&&...) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
// We didn't match any of the switch cases, report an error.
|
||||
// save.cur() is the beginning of the switched value, reader.cur() at the end.
|
||||
auto e = lexy::make_error<Reader, lexy::exhausted_switch>(save.cur(), reader.cur());
|
||||
return LEXY_MOV(handler).error(reader, e);
|
||||
}
|
||||
};
|
||||
template <typename NextParser, typename H, typename... T>
|
||||
struct _switch_select<NextParser, H, T...>
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Reader save, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
using cont = _switch_continue<NextParser>;
|
||||
using as_branch = decltype(branch(H{}));
|
||||
|
||||
// We only want to read what the value has matched.
|
||||
auto partial = lexy::partial_reader(save, reader.cur());
|
||||
|
||||
if constexpr (lexy::is_pattern<H>)
|
||||
{
|
||||
// We need this to catch `dsl::success` on its own, otherwise, it will be treated as an
|
||||
// unconditional branch `else_ >> success`. This is not true, as it needs to consume the
|
||||
// entire pattern, but the branch logic doesn't know it.
|
||||
|
||||
if (H::matcher::match(partial) && partial.eof())
|
||||
// We can continue directly, it's a pattern.
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
return _switch_select<NextParser, T...>::parse(handler, reader, save,
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
else if constexpr (as_branch::is_unconditional)
|
||||
{
|
||||
// We take it directly.
|
||||
return as_branch::template then_parser<cont>::parse(handler, partial, reader,
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (as_branch::condition_matcher::match(partial) && partial.eof())
|
||||
// We have matched the entire value, continue with normal parsing.
|
||||
return as_branch::template then_parser<cont>::parse(handler, partial, reader,
|
||||
LEXY_FWD(args)...);
|
||||
else
|
||||
return _switch_select<NextParser, T...>::parse(handler, reader, save,
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Rule, typename... Cases>
|
||||
struct _switch : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
// We parse the rule using our special continuation.
|
||||
// To recover the old reader position, we create a copy.
|
||||
using cont = _switch_select<NextParser, Cases...>;
|
||||
return Rule::template parser<cont>::parse(handler, reader, Reader(reader),
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
//=== dsl ===//
|
||||
/// Adds a case to the switch.
|
||||
template <typename Branch>
|
||||
LEXY_CONSTEVAL auto case_(Branch) const
|
||||
{
|
||||
static_assert(lexy::is_branch_rule<Branch>, "switch case must be a branch");
|
||||
return _switch<Rule, Cases..., Branch>{};
|
||||
}
|
||||
|
||||
/// Adds a default value to the switch.
|
||||
template <typename Default>
|
||||
LEXY_CONSTEVAL auto default_(Default def) const
|
||||
{
|
||||
return case_(else_ >> def);
|
||||
}
|
||||
|
||||
/// Adds an error on the default case.
|
||||
template <typename Error>
|
||||
LEXY_CONSTEVAL auto error()
|
||||
{
|
||||
return default_(lexyd::error<Error>(any));
|
||||
}
|
||||
};
|
||||
|
||||
/// Switches on the lexeme matched by the rule.
|
||||
/// The first case that will match the entire pattern will be taken.
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto switch_(Rule)
|
||||
{
|
||||
return _switch<Rule>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_SWITCH_HPP_INCLUDED
|
||||
@@ -1,141 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_TIMES_HPP_INCLUDED
|
||||
#define LEXY_DSL_TIMES_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/if.hpp>
|
||||
#include <lexy/dsl/separator.hpp>
|
||||
#include <lexy/dsl/sequence.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <std::size_t N, typename T>
|
||||
using times = T (&)[N];
|
||||
|
||||
template <typename T>
|
||||
using twice = times<2, T>;
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <std::size_t N, typename Rule>
|
||||
LEXY_CONSTEVAL auto _gen_times(Rule rule)
|
||||
{
|
||||
if constexpr (N == 1)
|
||||
return rule;
|
||||
else
|
||||
return rule + _gen_times<N - 1>(rule);
|
||||
}
|
||||
|
||||
template <std::size_t N, typename Rule, typename Pattern>
|
||||
LEXY_CONSTEVAL auto _gen_times(Rule rule, _sep<Pattern, false> sep)
|
||||
{
|
||||
if constexpr (N == 1)
|
||||
{
|
||||
(void)sep;
|
||||
return rule;
|
||||
}
|
||||
else
|
||||
{
|
||||
return rule + Pattern{} + _gen_times<N - 1>(rule, sep);
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t N, typename Rule, typename Pattern>
|
||||
LEXY_CONSTEVAL auto _gen_times(Rule rule, _tsep<Pattern, false> sep)
|
||||
{
|
||||
if constexpr (N == 1)
|
||||
{
|
||||
(void)sep;
|
||||
return rule + if_(Pattern{});
|
||||
}
|
||||
else
|
||||
{
|
||||
return rule + Pattern{} + _gen_times<N - 1>(rule, sep);
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t N, typename Rule, typename Sep>
|
||||
struct _times : rule_base
|
||||
{
|
||||
static LEXY_CONSTEVAL auto _repeated_rule()
|
||||
{
|
||||
if constexpr (std::is_same_v<Sep, void>)
|
||||
return _gen_times<N>(Rule{});
|
||||
else
|
||||
return _gen_times<N>(Rule{}, Sep{});
|
||||
}
|
||||
|
||||
// We only use this template if our rule does not have a matcher.
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename... Args>
|
||||
struct _continuation
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... RuleArgs>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args,
|
||||
RuleArgs&&... rule_args) -> typename Handler::result_type
|
||||
{
|
||||
// Create an array containing the rule arguments.
|
||||
static_assert(N == sizeof...(RuleArgs), "rule must create exactly one value");
|
||||
using array_type = std::common_type_t<std::decay_t<RuleArgs>...>;
|
||||
array_type array[N] = {LEXY_FWD(rule_args)...};
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., array);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
// Parse the rule with the special continuation that converts the value into an array
|
||||
// afterwards.
|
||||
using rule = decltype(_repeated_rule());
|
||||
using continuation = _continuation<Args...>;
|
||||
return rule::template parser<continuation>::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Repeats the rule N times and collects the values into an array.
|
||||
template <std::size_t N, typename Rule>
|
||||
LEXY_CONSTEVAL auto times(Rule)
|
||||
{
|
||||
static_assert(N > 0);
|
||||
if constexpr (lexy::is_pattern<Rule>)
|
||||
return _gen_times<N>(Rule{});
|
||||
else
|
||||
return _times<N, Rule, void>{};
|
||||
}
|
||||
|
||||
/// Repeates the rule N times separated by the separator and collects the values into an array.
|
||||
template <std::size_t N, typename Rule, typename Sep>
|
||||
LEXY_CONSTEVAL auto times(Rule, Sep)
|
||||
{
|
||||
static_assert(N > 0);
|
||||
if constexpr (lexy::is_pattern<Rule>)
|
||||
return _gen_times<N>(Rule{}, Sep{});
|
||||
else
|
||||
return _times<N, Rule, Sep>{};
|
||||
}
|
||||
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto twice(Rule rule)
|
||||
{
|
||||
return times<2>(rule);
|
||||
}
|
||||
template <typename Rule, typename Sep>
|
||||
LEXY_CONSTEVAL auto twice(Rule rule, Sep sep)
|
||||
{
|
||||
return times<2>(rule, sep);
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_TIMES_HPP_INCLUDED
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_UNTIL_HPP_INCLUDED
|
||||
#define LEXY_DSL_UNTIL_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Condition>
|
||||
struct _until_eof : atom_base<_until_eof<Condition>>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
while (!Condition::matcher::match(reader))
|
||||
{
|
||||
if (reader.eof())
|
||||
break;
|
||||
|
||||
reader.bump();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC void error(const Reader&, typename Reader::iterator);
|
||||
};
|
||||
|
||||
template <typename Condition>
|
||||
struct _until : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = true;
|
||||
|
||||
struct matcher
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
auto save = reader;
|
||||
|
||||
while (!Condition::matcher::match(reader))
|
||||
{
|
||||
if (reader.eof())
|
||||
{
|
||||
reader = LEXY_MOV(save);
|
||||
return false;
|
||||
}
|
||||
|
||||
reader.bump();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
while (!Condition::matcher::match(reader))
|
||||
{
|
||||
if (reader.eof())
|
||||
{
|
||||
// We're trying to parse the condition now.
|
||||
// This will fail, but it will create an appropriate error.
|
||||
return Condition::template parser<NextParser>::parse(handler, reader,
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
|
||||
reader.bump();
|
||||
}
|
||||
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
/// Also accepts EOF as the closing condition.
|
||||
LEXY_CONSTEVAL auto or_eof() const
|
||||
{
|
||||
return _until_eof<Condition>{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches anything until Condition matches.
|
||||
/// Then matches Condition.
|
||||
template <typename Condition>
|
||||
LEXY_CONSTEVAL auto until(Condition)
|
||||
{
|
||||
static_assert(lexy::is_pattern<Condition>);
|
||||
return _until<Condition>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_UNTIL_HPP_INCLUDED
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_VALUE_HPP_INCLUDED
|
||||
#define LEXY_DSL_VALUE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/nttp_string.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct _match_handler;
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <auto Value>
|
||||
struct _valc : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if constexpr (std::is_same_v<Handler, lexy::_match_handler>)
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., Value);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Produces the specified value without parsing anything.
|
||||
template <auto Value>
|
||||
constexpr auto value_c = _valc<Value>{};
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <auto F>
|
||||
struct _valf : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if constexpr (std::is_same_v<Handler, lexy::_match_handler>)
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., F());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Produces the value returned by the function without parsing anything.
|
||||
template <auto F>
|
||||
constexpr auto value_f = _valf<F>{};
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename T>
|
||||
struct _valt : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if constexpr (std::is_same_v<Handler, lexy::_match_handler>)
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., T());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Produces a default constructed value of the specified type without parsing anything.
|
||||
template <typename T>
|
||||
constexpr auto value_t = _valt<T>{};
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename String>
|
||||
struct _vals : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
if constexpr (std::is_same_v<Handler, lexy::_match_handler>)
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
else
|
||||
{
|
||||
constexpr auto str = String::get();
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., str.data(),
|
||||
str.size());
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#if LEXY_HAS_NTTP
|
||||
/// Produces the string value.
|
||||
template <lexy::_detail::string_literal Str>
|
||||
constexpr auto value_str = _vals<lexy::_detail::type_string<Str>>{};
|
||||
#endif
|
||||
|
||||
#define LEXY_VALUE_STR(Str) \
|
||||
::lexyd::_vals<LEXY_NTTP_STRING(Str)> {}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_VALUE_HPP_INCLUDED
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_WHILE_HPP_INCLUDED
|
||||
#define LEXY_DSL_WHILE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
#include <lexy/dsl/choice.hpp>
|
||||
#include <lexy/dsl/loop.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Condition, typename Then>
|
||||
struct _whl : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = Then::has_matcher;
|
||||
|
||||
struct matcher
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
auto save = reader;
|
||||
while (Condition::matcher::match(reader))
|
||||
{
|
||||
if (!Then::matcher::match(reader))
|
||||
{
|
||||
reader = LEXY_MOV(save);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct _rule : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(_loop_handler<Handler>& handler, Reader& reader,
|
||||
Args&&... args) -> typename _loop_handler<Handler>::result_type
|
||||
{
|
||||
if (!Condition::matcher::match(reader))
|
||||
return LEXY_MOV(handler).break_();
|
||||
|
||||
return Then::template parser<NextParser>::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
using parser = typename _loop<_rule>::template parser<NextParser>;
|
||||
};
|
||||
|
||||
template <typename Pattern>
|
||||
struct _whl<Pattern, void> : atom_base<_whl<Pattern, void>>
|
||||
{
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
while (Pattern::matcher::match(reader))
|
||||
{
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC void error(const Reader&, typename Reader::iterator);
|
||||
};
|
||||
|
||||
/// Matches the pattern branch rule as often as possible.
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto while_(Rule rule)
|
||||
{
|
||||
static_assert(lexy::is_branch_rule<Rule>, "while() requires a branch condition");
|
||||
|
||||
if constexpr (lexy::is_pattern<Rule>)
|
||||
{
|
||||
(void)rule;
|
||||
return _whl<Rule, void>{};
|
||||
}
|
||||
else
|
||||
{
|
||||
auto b = branch(rule);
|
||||
return _whl<decltype(b.condition()), decltype(b.then())>{};
|
||||
}
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename... R>
|
||||
struct _whlc : rule_base
|
||||
{
|
||||
using _choice = _chc<R...>;
|
||||
static constexpr auto has_matcher = _choice::has_matcher;
|
||||
|
||||
struct matcher
|
||||
{
|
||||
template <typename Rule, typename Reader>
|
||||
LEXY_DSL_FUNC int _match(Reader& reader)
|
||||
{
|
||||
using as_branch = decltype(branch(Rule{}));
|
||||
|
||||
if (as_branch::condition_matcher::match(reader))
|
||||
return as_branch::then_matcher::match(reader) ? 1 : 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader& reader)
|
||||
{
|
||||
auto save = reader;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int result = -1;
|
||||
// Try to match all branches in turn, until we found one where the result isn't
|
||||
// unmatched.
|
||||
(void)((result = _match<R>(reader), result == -1 ? false : true) || ...);
|
||||
|
||||
if (result == -1)
|
||||
// We no longer matched any condition, we are done.
|
||||
break;
|
||||
else if (result == 0)
|
||||
{
|
||||
// We had a match failure.
|
||||
reader = LEXY_MOV(save);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NextParser>
|
||||
using parser =
|
||||
typename decltype(loop(_choice{} | else_ >> break_))::template parser<NextParser>;
|
||||
};
|
||||
|
||||
/// Matches the choice as often as possible.
|
||||
template <typename... R>
|
||||
LEXY_CONSTEVAL auto while_(_chc<R...>)
|
||||
{
|
||||
return _whlc<R...>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
/// Matches the rule at least once, then as often as possible.
|
||||
template <typename Rule>
|
||||
LEXY_CONSTEVAL auto while_one(Rule rule)
|
||||
{
|
||||
if constexpr (lexy::is_branch_rule<Rule>)
|
||||
return rule >> while_(rule);
|
||||
else
|
||||
return rule + while_(rule);
|
||||
}
|
||||
|
||||
/// Matches then once, then `while_(condition >> then)`.
|
||||
template <typename Then, typename Condition>
|
||||
LEXY_CONSTEVAL auto do_while(Then then, Condition condition)
|
||||
{
|
||||
if constexpr (lexy::is_branch_rule<Then>)
|
||||
return then >> while_(condition >> then);
|
||||
else
|
||||
return then + while_(condition >> then);
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_WHILE_HPP_INCLUDED
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_DSL_WHITESPACE_HPP_INCLUDED
|
||||
#define LEXY_DSL_WHITESPACE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/branch.hpp>
|
||||
#include <lexy/dsl/while.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <typename Rule, typename Whitespace>
|
||||
struct _ws : decltype(while_(Whitespace{}) + Rule{})
|
||||
{
|
||||
/// Make it a branch rule, if the rule is a branch rule.
|
||||
/// If the rule isn't a branch rule, we could make the whitespace the condition, but this is
|
||||
/// probably insufficient to identify the rule.
|
||||
template <typename R = Rule, typename = std::enable_if_t<lexy::is_branch_rule<R>>>
|
||||
friend LEXY_CONSTEVAL auto branch(_ws)
|
||||
{
|
||||
// We just add another condition to the left of the branch rule.
|
||||
return while_(Whitespace{}) >> branch(Rule{});
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches whitespace before parsing rule.
|
||||
template <typename Rule, typename Whitespace>
|
||||
LEXY_CONSTEVAL auto whitespaced(Rule, Whitespace)
|
||||
{
|
||||
return _ws<Rule, Whitespace>{};
|
||||
}
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_DSL_WHITESPACE_HPP_INCLUDED
|
||||
|
||||
@@ -1,589 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_ENCODING_HPP_INCLUDED
|
||||
#define LEXY_ENCODING_HPP_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
#include <lexy/_detail/assert.hpp>
|
||||
#include <lexy/_detail/config.hpp>
|
||||
|
||||
//=== code_point ===//
|
||||
namespace lexy
|
||||
{
|
||||
/// A unicode code point.
|
||||
class code_point
|
||||
{
|
||||
public:
|
||||
constexpr code_point() noexcept : _value(0xFFFF'FFFF) {}
|
||||
constexpr explicit code_point(char32_t value) noexcept : _value(value) {}
|
||||
|
||||
constexpr auto value() const noexcept
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
//=== classification ===//
|
||||
constexpr bool is_valid() const noexcept
|
||||
{
|
||||
return _value <= 0x10'FFFF;
|
||||
}
|
||||
constexpr bool is_surrogate() const noexcept
|
||||
{
|
||||
return 0xD800 <= _value && _value <= 0xDFFF;
|
||||
}
|
||||
constexpr bool is_scalar() const noexcept
|
||||
{
|
||||
return is_valid() && !is_surrogate();
|
||||
}
|
||||
|
||||
constexpr bool is_ascii() const noexcept
|
||||
{
|
||||
return _value <= 0x7F;
|
||||
}
|
||||
constexpr bool is_bmp() const noexcept
|
||||
{
|
||||
return _value <= 0xFFFF;
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(code_point lhs, code_point rhs) noexcept
|
||||
{
|
||||
return lhs._value == rhs._value;
|
||||
}
|
||||
friend constexpr bool operator!=(code_point lhs, code_point rhs) noexcept
|
||||
{
|
||||
return lhs._value != rhs._value;
|
||||
}
|
||||
|
||||
private:
|
||||
char32_t _value;
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
//=== encoding ===//
|
||||
namespace lexy
|
||||
{
|
||||
/// The endianness used by an encoding.
|
||||
enum class encoding_endianness
|
||||
{
|
||||
/// Little endian.
|
||||
little,
|
||||
/// Big endian.
|
||||
big,
|
||||
/// Checks for a BOM and uses its endianness.
|
||||
/// If there is no BOM, assumes big endian.
|
||||
bom,
|
||||
};
|
||||
|
||||
/// An encoding where the input is some 8bit encoding (ASCII, UTF-8, extended ASCII etc.).
|
||||
struct default_encoding
|
||||
{
|
||||
using char_type = char;
|
||||
using int_type = int;
|
||||
|
||||
template <typename OtherCharType>
|
||||
static constexpr bool is_secondary_char_type = false;
|
||||
|
||||
static LEXY_CONSTEVAL int_type eof()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static LEXY_CONSTEVAL int_type to_int_type(char_type c)
|
||||
{
|
||||
if constexpr (std::is_unsigned_v<char_type>)
|
||||
// We can just convert it to int directly.
|
||||
return static_cast<int_type>(c);
|
||||
else
|
||||
{
|
||||
// We first need to prevent negative values, by making it unsigned.
|
||||
auto value = static_cast<unsigned char>(c);
|
||||
return static_cast<int_type>(value);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr std::size_t encode_code_point(code_point cp, char_type* buffer,
|
||||
std::size_t size)
|
||||
= delete;
|
||||
class code_point_decoder;
|
||||
};
|
||||
|
||||
// An encoding where the input is assumed to be valid ASCII.
|
||||
struct ascii_encoding
|
||||
{
|
||||
using char_type = char;
|
||||
using int_type = char;
|
||||
|
||||
template <typename OtherCharType>
|
||||
static constexpr bool is_secondary_char_type = false;
|
||||
|
||||
static LEXY_CONSTEVAL int_type eof()
|
||||
{
|
||||
if constexpr (std::is_signed_v<char_type>)
|
||||
return int_type(-1);
|
||||
else
|
||||
return int_type(0xFFu);
|
||||
}
|
||||
|
||||
static LEXY_CONSTEVAL int_type to_int_type(char_type c)
|
||||
{
|
||||
return int_type(c);
|
||||
}
|
||||
|
||||
static constexpr std::size_t encode_code_point(code_point cp, char_type* buffer,
|
||||
std::size_t size)
|
||||
{
|
||||
LEXY_PRECONDITION(cp.is_ascii());
|
||||
LEXY_PRECONDITION(size >= 1);
|
||||
|
||||
*buffer = char_type(cp.value());
|
||||
return 1;
|
||||
}
|
||||
|
||||
class code_point_decoder
|
||||
{
|
||||
public:
|
||||
int init(int_type c)
|
||||
{
|
||||
if (c == eof())
|
||||
return -1;
|
||||
|
||||
_result = char32_t(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool next(int_type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto finish() &&
|
||||
{
|
||||
auto cp = code_point(_result);
|
||||
if (!cp.is_ascii())
|
||||
return code_point();
|
||||
return cp;
|
||||
}
|
||||
|
||||
private:
|
||||
char32_t _result = {};
|
||||
};
|
||||
};
|
||||
|
||||
/// An encoding where the input is assumed to be valid UTF-8.
|
||||
struct utf8_encoding
|
||||
{
|
||||
using char_type = LEXY_CHAR8_T;
|
||||
using int_type = LEXY_CHAR8_T;
|
||||
|
||||
template <typename OtherCharType>
|
||||
static constexpr bool is_secondary_char_type = false;
|
||||
|
||||
static LEXY_CONSTEVAL int_type eof()
|
||||
{
|
||||
// 0xFF is not part of valid UTF-8.
|
||||
return int_type(0xFF);
|
||||
}
|
||||
|
||||
static LEXY_CONSTEVAL int_type to_int_type(char_type c)
|
||||
{
|
||||
return int_type(c);
|
||||
}
|
||||
|
||||
static constexpr std::size_t encode_code_point(code_point cp, char_type* buffer,
|
||||
std::size_t size)
|
||||
{
|
||||
LEXY_PRECONDITION(cp.is_valid());
|
||||
|
||||
// Taken from http://www.herongyang.com/Unicode/UTF-8-UTF-8-Encoding-Algorithm.html.
|
||||
if (cp.is_ascii())
|
||||
{
|
||||
LEXY_PRECONDITION(size >= 1);
|
||||
|
||||
buffer[0] = char_type(cp.value());
|
||||
return 1;
|
||||
}
|
||||
else if (cp.value() <= 0x07'FF)
|
||||
{
|
||||
LEXY_PRECONDITION(size >= 2);
|
||||
|
||||
auto first = (cp.value() >> 6) & 0x1F;
|
||||
auto second = (cp.value() >> 0) & 0x3F;
|
||||
|
||||
buffer[0] = char_type(0xC0 | first);
|
||||
buffer[1] = char_type(0x80 | second);
|
||||
return 2;
|
||||
}
|
||||
else if (cp.value() <= 0xFF'FF)
|
||||
{
|
||||
LEXY_PRECONDITION(size >= 3);
|
||||
|
||||
auto first = (cp.value() >> 12) & 0x0F;
|
||||
auto second = (cp.value() >> 6) & 0x3F;
|
||||
auto third = (cp.value() >> 0) & 0x3F;
|
||||
|
||||
buffer[0] = char_type(0xE0 | first);
|
||||
buffer[1] = char_type(0x80 | second);
|
||||
buffer[2] = char_type(0x80 | third);
|
||||
return 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
LEXY_PRECONDITION(size >= 4);
|
||||
|
||||
auto first = (cp.value() >> 18) & 0x07;
|
||||
auto second = (cp.value() >> 12) & 0x3F;
|
||||
auto third = (cp.value() >> 6) & 0x3F;
|
||||
auto fourth = (cp.value() >> 0) & 0x3F;
|
||||
|
||||
buffer[0] = char_type(0xF0 | first);
|
||||
buffer[1] = char_type(0x80 | second);
|
||||
buffer[2] = char_type(0x80 | third);
|
||||
buffer[3] = char_type(0x80 | fourth);
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
class code_point_decoder
|
||||
{
|
||||
static constexpr auto payload_lead1 = 0b0111'1111;
|
||||
static constexpr auto payload_lead2 = 0b0001'1111;
|
||||
static constexpr auto payload_lead3 = 0b0000'1111;
|
||||
static constexpr auto payload_lead4 = 0b0000'0111;
|
||||
static constexpr auto payload_cont = 0b0011'1111;
|
||||
|
||||
static constexpr auto pattern_lead1 = 0b0 << 7;
|
||||
static constexpr auto pattern_lead2 = 0b110 << 5;
|
||||
static constexpr auto pattern_lead3 = 0b1110 << 4;
|
||||
static constexpr auto pattern_lead4 = 0b11110 << 3;
|
||||
static constexpr auto pattern_cont = 0b10 << 6;
|
||||
|
||||
public:
|
||||
int init(int_type c)
|
||||
{
|
||||
if ((c & ~payload_lead1) == pattern_lead1)
|
||||
{
|
||||
_result = char32_t(c & payload_lead1);
|
||||
return 0;
|
||||
}
|
||||
else if (c == 0xC0 || c == 0xC1)
|
||||
{
|
||||
// These leading characters can only used for overlong ASCII.
|
||||
return -1;
|
||||
}
|
||||
else if ((c & ~payload_lead2) == pattern_lead2)
|
||||
{
|
||||
_result = char32_t(c & payload_lead2);
|
||||
return 1;
|
||||
}
|
||||
else if ((c & ~payload_lead3) == pattern_lead3)
|
||||
{
|
||||
_result = char32_t(c & payload_lead3);
|
||||
if (c == 0xE0)
|
||||
_min_cont_value = 0xA0;
|
||||
return 2;
|
||||
}
|
||||
else if ((c & ~payload_lead4) == pattern_lead4)
|
||||
{
|
||||
_result = char32_t(c & payload_lead4);
|
||||
if (c == 0xF0)
|
||||
_min_cont_value = 0x90;
|
||||
return 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool next(int_type c)
|
||||
{
|
||||
if ((c & ~payload_cont) != pattern_cont)
|
||||
return false; // Not a continuation byte.
|
||||
else if (c < _min_cont_value)
|
||||
return false; // Overlong sequence.
|
||||
|
||||
_result <<= 6;
|
||||
_result |= char32_t(c & payload_cont);
|
||||
|
||||
// We're only having overlong sequences in the second byte, so overwrite.
|
||||
_min_cont_value = 0x80;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto finish() &&
|
||||
{
|
||||
auto cp = code_point(_result);
|
||||
if (cp.is_surrogate())
|
||||
// Surrogates are not allowed.
|
||||
return code_point();
|
||||
return cp;
|
||||
}
|
||||
|
||||
private:
|
||||
char32_t _result = {};
|
||||
// The minimal continuation value to prevent overlong sequences.
|
||||
int_type _min_cont_value = 0x80;
|
||||
};
|
||||
};
|
||||
template <>
|
||||
constexpr bool utf8_encoding::is_secondary_char_type<char> = true;
|
||||
|
||||
/// An encoding where the input is assumed to be valid UTF-16.
|
||||
struct utf16_encoding
|
||||
{
|
||||
using char_type = char16_t;
|
||||
using int_type = std::int_least32_t;
|
||||
|
||||
template <typename OtherCharType>
|
||||
static constexpr bool is_secondary_char_type = false;
|
||||
|
||||
static LEXY_CONSTEVAL int_type eof()
|
||||
{
|
||||
// Every value of char16_t is valid UTF16.
|
||||
return int_type(-1);
|
||||
}
|
||||
|
||||
static LEXY_CONSTEVAL int_type to_int_type(char_type c)
|
||||
{
|
||||
return int_type(c);
|
||||
}
|
||||
|
||||
static constexpr std::size_t encode_code_point(code_point cp, char_type* buffer,
|
||||
std::size_t size)
|
||||
{
|
||||
LEXY_PRECONDITION(cp.is_valid());
|
||||
|
||||
if (cp.is_bmp())
|
||||
{
|
||||
LEXY_PRECONDITION(size >= 1);
|
||||
|
||||
buffer[0] = char_type(cp.value());
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Algorithm implemented from
|
||||
// https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF.
|
||||
LEXY_PRECONDITION(size >= 2);
|
||||
|
||||
auto u_prime = cp.value() - 0x1'0000;
|
||||
auto high_ten_bits = u_prime >> 10;
|
||||
auto low_ten_bits = u_prime & 0b0000'0011'1111'1111;
|
||||
|
||||
buffer[0] = char_type(0xD800 + high_ten_bits);
|
||||
buffer[1] = char_type(0xDC00 + low_ten_bits);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
class code_point_decoder
|
||||
{
|
||||
static constexpr auto payload_lead1 = 0b0000'0011'1111'1111;
|
||||
static constexpr auto payload_lead2 = payload_lead1;
|
||||
|
||||
static constexpr auto pattern_lead1 = 0b110110 << 10;
|
||||
static constexpr auto pattern_lead2 = 0b110111 << 10;
|
||||
|
||||
public:
|
||||
int init(int_type c)
|
||||
{
|
||||
// We need to handle EOF separately and then convert it to a uint16_t.
|
||||
if (c == eof())
|
||||
return -1;
|
||||
auto value = char_type(c);
|
||||
|
||||
if ((value & ~payload_lead1) == pattern_lead1)
|
||||
{
|
||||
_result = char32_t(value & payload_lead1);
|
||||
return 1;
|
||||
}
|
||||
else if ((value & ~payload_lead2) == pattern_lead2)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_result = char32_t(value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool next(int_type c)
|
||||
{
|
||||
if (c == eof())
|
||||
return false;
|
||||
auto value = char_type(c);
|
||||
|
||||
if ((value & ~payload_lead2) != pattern_lead2)
|
||||
return false;
|
||||
|
||||
_result <<= 10;
|
||||
_result |= char32_t(value & payload_lead2);
|
||||
_result |= 0x10000;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto finish() &&
|
||||
{
|
||||
auto cp = code_point(_result);
|
||||
LEXY_PRECONDITION(!cp.is_surrogate());
|
||||
return cp;
|
||||
}
|
||||
|
||||
private:
|
||||
char32_t _result = {};
|
||||
};
|
||||
};
|
||||
template <>
|
||||
constexpr bool utf16_encoding::is_secondary_char_type<wchar_t> = sizeof(wchar_t)
|
||||
== sizeof(char16_t);
|
||||
|
||||
/// An encoding where the input is assumed to be valid UTF-32.
|
||||
struct utf32_encoding
|
||||
{
|
||||
using char_type = char32_t;
|
||||
using int_type = char32_t;
|
||||
|
||||
template <typename OtherCharType>
|
||||
static constexpr bool is_secondary_char_type = false;
|
||||
|
||||
static LEXY_CONSTEVAL int_type eof()
|
||||
{
|
||||
// The highest unicode code point is U+10'FFFF, so this is never a valid code point.
|
||||
return int_type(0xFFFF'FFFF);
|
||||
}
|
||||
|
||||
static LEXY_CONSTEVAL int_type to_int_type(char_type c)
|
||||
{
|
||||
return c;
|
||||
}
|
||||
|
||||
static constexpr std::size_t encode_code_point(code_point cp, char_type* buffer,
|
||||
std::size_t size)
|
||||
{
|
||||
LEXY_PRECONDITION(cp.is_valid());
|
||||
LEXY_PRECONDITION(size >= 1);
|
||||
|
||||
*buffer = char_type(cp.value());
|
||||
return 1;
|
||||
}
|
||||
|
||||
class code_point_decoder
|
||||
{
|
||||
public:
|
||||
int init(int_type c)
|
||||
{
|
||||
// No need to handle EOF, the code point validation will take care of that one.
|
||||
_result = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool next(int_type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto finish() &&
|
||||
{
|
||||
auto cp = code_point(_result);
|
||||
if (cp.is_surrogate())
|
||||
// Surrogates are not allowed.
|
||||
return code_point();
|
||||
return cp;
|
||||
}
|
||||
|
||||
private:
|
||||
char32_t _result = {};
|
||||
};
|
||||
};
|
||||
template <>
|
||||
constexpr bool utf32_encoding::is_secondary_char_type<wchar_t> = sizeof(wchar_t)
|
||||
== sizeof(char32_t);
|
||||
|
||||
/// An encoding where the input is just raw bytes, not characters.
|
||||
struct raw_encoding
|
||||
{
|
||||
using char_type = unsigned char;
|
||||
using int_type = int;
|
||||
|
||||
template <typename OtherCharType>
|
||||
static constexpr bool is_secondary_char_type = false;
|
||||
|
||||
static LEXY_CONSTEVAL int_type eof()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static LEXY_CONSTEVAL int_type to_int_type(char_type c)
|
||||
{
|
||||
return int_type(c);
|
||||
}
|
||||
|
||||
static constexpr std::size_t encode_code_point(code_point cp, char_type* buffer,
|
||||
std::size_t size)
|
||||
= delete;
|
||||
|
||||
class code_point_decoder;
|
||||
};
|
||||
template <>
|
||||
constexpr bool raw_encoding::is_secondary_char_type<char> = true;
|
||||
} // namespace lexy
|
||||
|
||||
//=== deduce_encoding ===//
|
||||
namespace lexy
|
||||
{
|
||||
template <typename CharT>
|
||||
struct _deduce_encoding;
|
||||
template <typename CharT>
|
||||
using deduce_encoding = typename _deduce_encoding<CharT>::type;
|
||||
|
||||
template <>
|
||||
struct _deduce_encoding<char>
|
||||
{
|
||||
#if defined(LEXY_ENCODING_OF_CHAR)
|
||||
using type = LEXY_ENCODING_OF_CHAR;
|
||||
static_assert(std::is_same_v<type, default_encoding> //
|
||||
|| std::is_same_v<type, ascii_encoding> //
|
||||
|| std::is_same_v<type, utf8_encoding>,
|
||||
"invalid value for LEXY_ENCODING_OF_CHAR");
|
||||
#else
|
||||
using type = default_encoding; // Don't know the exact encoding.
|
||||
#endif
|
||||
};
|
||||
|
||||
#if LEXY_HAS_CHAR8_T
|
||||
template <>
|
||||
struct _deduce_encoding<LEXY_CHAR8_T>
|
||||
{
|
||||
using type = utf8_encoding;
|
||||
};
|
||||
#endif
|
||||
template <>
|
||||
struct _deduce_encoding<char16_t>
|
||||
{
|
||||
using type = utf16_encoding;
|
||||
};
|
||||
template <>
|
||||
struct _deduce_encoding<char32_t>
|
||||
{
|
||||
using type = utf32_encoding;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct _deduce_encoding<unsigned char>
|
||||
{
|
||||
using type = raw_encoding;
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
//=== impls ===//
|
||||
namespace lexy
|
||||
{
|
||||
template <typename Encoding, typename CharT>
|
||||
using _require_secondary_char_type
|
||||
= std::enable_if_t<Encoding::template is_secondary_char_type<CharT>>;
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_ENCODING_HPP_INCLUDED
|
||||
@@ -1,170 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_ERROR_HPP_INCLUDED
|
||||
#define LEXY_ERROR_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/config.hpp>
|
||||
#include <lexy/_detail/type_name.hpp>
|
||||
#include <lexy/input/base.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
/// Generic failure.
|
||||
template <typename Reader, typename Tag>
|
||||
class error
|
||||
{
|
||||
static_assert(is_canonical_reader<Reader>);
|
||||
|
||||
public:
|
||||
constexpr explicit error(typename Reader::iterator pos) noexcept : _pos(pos), _end(pos) {}
|
||||
constexpr explicit error(typename Reader::iterator begin,
|
||||
typename Reader::iterator end) noexcept
|
||||
: _pos(begin), _end(end)
|
||||
{}
|
||||
|
||||
constexpr auto position() const noexcept
|
||||
{
|
||||
return _pos;
|
||||
}
|
||||
|
||||
constexpr _detail::string_view message() const noexcept
|
||||
{
|
||||
return _detail::type_name<Tag>();
|
||||
}
|
||||
|
||||
constexpr auto begin() const noexcept
|
||||
{
|
||||
return _pos;
|
||||
}
|
||||
constexpr auto end() const noexcept
|
||||
{
|
||||
return _end;
|
||||
}
|
||||
|
||||
private:
|
||||
typename Reader::iterator _pos;
|
||||
typename Reader::iterator _end;
|
||||
};
|
||||
|
||||
/// Expected the literal character sequence.
|
||||
struct expected_literal
|
||||
{};
|
||||
template <typename Reader>
|
||||
class error<Reader, expected_literal>
|
||||
{
|
||||
static_assert(is_canonical_reader<Reader>);
|
||||
|
||||
public:
|
||||
constexpr explicit error(typename Reader::iterator pos,
|
||||
_detail::basic_string_view<typename Reader::char_type> str,
|
||||
std::size_t index) noexcept
|
||||
: _pos(pos), _str(str), _idx(index)
|
||||
{}
|
||||
|
||||
constexpr auto position() const noexcept
|
||||
{
|
||||
return _pos;
|
||||
}
|
||||
|
||||
constexpr auto string() const noexcept
|
||||
{
|
||||
return _str;
|
||||
}
|
||||
|
||||
constexpr std::size_t index() const noexcept
|
||||
{
|
||||
return _idx;
|
||||
}
|
||||
|
||||
constexpr auto character() const noexcept
|
||||
{
|
||||
return _str[_idx];
|
||||
}
|
||||
|
||||
private:
|
||||
typename Reader::iterator _pos;
|
||||
_detail::basic_string_view<typename Reader::char_type> _str;
|
||||
std::size_t _idx;
|
||||
};
|
||||
|
||||
/// Expected a character of the specified character class.
|
||||
struct expected_char_class
|
||||
{};
|
||||
template <typename Reader>
|
||||
class error<Reader, expected_char_class>
|
||||
{
|
||||
static_assert(is_canonical_reader<Reader>);
|
||||
|
||||
public:
|
||||
constexpr explicit error(typename Reader::iterator pos, const char* name) noexcept
|
||||
: _pos(pos), _name(name)
|
||||
{}
|
||||
|
||||
constexpr auto position() const noexcept
|
||||
{
|
||||
return _pos;
|
||||
}
|
||||
|
||||
constexpr _detail::string_view character_class() const noexcept
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
private:
|
||||
typename Reader::iterator _pos;
|
||||
const char* _name;
|
||||
};
|
||||
|
||||
template <typename Input, typename Tag>
|
||||
using error_for = error<input_reader<Input>, Tag>;
|
||||
|
||||
template <typename Reader, typename Tag, typename... Args>
|
||||
constexpr auto make_error(Args&&... args)
|
||||
{
|
||||
return error<typename Reader::canonical_reader, Tag>(LEXY_FWD(args)...);
|
||||
}
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
// Contains information about the context of an error.
|
||||
template <typename Production, typename Input>
|
||||
class error_context
|
||||
{
|
||||
public:
|
||||
constexpr explicit error_context(const Input& input,
|
||||
typename input_reader<Input>::iterator pos) noexcept
|
||||
: _input(&input), _pos(pos)
|
||||
{}
|
||||
constexpr explicit error_context(Production, const Input& input,
|
||||
typename input_reader<Input>::iterator pos) noexcept
|
||||
: error_context(input, pos)
|
||||
{}
|
||||
|
||||
// The input.
|
||||
constexpr const Input& input() const noexcept
|
||||
{
|
||||
return *_input;
|
||||
}
|
||||
|
||||
// The name of the production where the error occurred.
|
||||
static LEXY_CONSTEVAL _detail::string_view production()
|
||||
{
|
||||
return _detail::type_name<Production>();
|
||||
}
|
||||
|
||||
// The starting position of the production.
|
||||
constexpr auto position() const noexcept
|
||||
{
|
||||
return _pos;
|
||||
}
|
||||
|
||||
private:
|
||||
const Input* _input;
|
||||
typename input_reader<Input>::iterator _pos;
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_ERROR_HPP_INCLUDED
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_ERROR_LOCATION_HPP_INCLUDED
|
||||
#define LEXY_ERROR_LOCATION_HPP_INCLUDED
|
||||
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/input/base.hpp>
|
||||
#include <lexy/lexeme.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename Reader>
|
||||
struct error_location
|
||||
{
|
||||
std::size_t line, column;
|
||||
/// The entire line where the error occurred.
|
||||
lexeme<Reader> context;
|
||||
};
|
||||
|
||||
template <typename Input>
|
||||
using error_location_for = error_location<input_reader<Input>>;
|
||||
|
||||
template <typename Input, typename PatternCP, typename PatternNL>
|
||||
constexpr auto make_error_location(const Input& input, typename input_reader<Input>::iterator pos,
|
||||
PatternCP, PatternNL) -> error_location_for<Input>
|
||||
{
|
||||
static_assert(is_pattern<PatternCP> && is_pattern<PatternNL>);
|
||||
|
||||
auto reader = input.reader();
|
||||
|
||||
// We start at the first line and first column.
|
||||
std::size_t cur_line = 1;
|
||||
std::size_t cur_column = 1;
|
||||
auto line_start = reader.cur();
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (reader.cur() == pos)
|
||||
{
|
||||
// We found the position of the error.
|
||||
break;
|
||||
}
|
||||
else if (PatternNL::matcher::match(reader))
|
||||
{
|
||||
// We're at a new line.
|
||||
++cur_line;
|
||||
cur_column = 1;
|
||||
line_start = reader.cur();
|
||||
}
|
||||
else if (PatternCP::matcher::match(reader))
|
||||
{
|
||||
// Next column.
|
||||
++cur_column;
|
||||
}
|
||||
else if (reader.eof())
|
||||
{
|
||||
// We have an OOB error position, return what the current position (i.e. the end of the
|
||||
// file).
|
||||
return error_location_for<Input>{cur_line, cur_column, lexeme(reader, line_start)};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalid code unit, just ignore it in the column count.
|
||||
reader.bump();
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, we've found the right line and column number and the reader is at the error
|
||||
// position.
|
||||
// We now need to find the end of the current line to compute context.
|
||||
while (true)
|
||||
{
|
||||
auto save = reader;
|
||||
if (PatternNL::match(reader))
|
||||
{
|
||||
// We've found the final newline, reset to previous position.
|
||||
reader = LEXY_MOV(save);
|
||||
break;
|
||||
}
|
||||
else if (reader.eof())
|
||||
{
|
||||
// Last line of the input.
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try again.
|
||||
reader.bump();
|
||||
}
|
||||
}
|
||||
|
||||
// We've found the right line and column number and the reader is at the end of the line.
|
||||
return error_location_for<Input>{cur_line, cur_column, lexeme(reader, line_start)};
|
||||
}
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_ERROR_LOCATION_HPP_INCLUDED
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#include <lexy/input/file.hpp>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <lexy/_detail/buffer_builder.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
class file_handle
|
||||
{
|
||||
public:
|
||||
explicit file_handle(std::FILE* file) noexcept : _file(file) {}
|
||||
|
||||
file_handle(const file_handle&) = delete;
|
||||
file_handle& operator=(const file_handle&) = delete;
|
||||
|
||||
~file_handle() noexcept
|
||||
{
|
||||
if (_file)
|
||||
std::fclose(_file);
|
||||
}
|
||||
|
||||
operator std::FILE*() const noexcept
|
||||
{
|
||||
return _file;
|
||||
}
|
||||
|
||||
private:
|
||||
std::FILE* _file;
|
||||
};
|
||||
|
||||
lexy::file_error get_file_error() noexcept
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case ENOENT:
|
||||
case ENOTDIR:
|
||||
case ELOOP:
|
||||
return lexy::file_error::file_not_found;
|
||||
|
||||
case EACCES:
|
||||
case EPERM:
|
||||
return lexy::file_error::permission_denied;
|
||||
|
||||
default:
|
||||
return lexy::file_error::os_error;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
lexy::file_error lexy::_detail::read_file(const char* path, file_callback cb, void* user_data)
|
||||
{
|
||||
file_handle file(std::fopen(path, "rb"));
|
||||
if (!file)
|
||||
return get_file_error();
|
||||
|
||||
_detail::buffer_builder<char> buffer;
|
||||
while (true)
|
||||
{
|
||||
const auto buffer_size = buffer.write_size();
|
||||
LEXY_ASSERT(buffer_size > 0, "buffer empty?!");
|
||||
|
||||
// Read into the entire write area of the buffer from the file,
|
||||
// commiting what we've just read.
|
||||
const auto read = std::fread(buffer.write_data(), sizeof(char), buffer_size, file);
|
||||
buffer.commit(read);
|
||||
|
||||
// Check whether we have exhausted the file.
|
||||
if (read < buffer_size)
|
||||
{
|
||||
if (std::ferror(file))
|
||||
// We have a read error.
|
||||
return file_error::os_error;
|
||||
|
||||
// We should have reached the end of the file.
|
||||
LEXY_ASSERT(std::feof(file), "why did fread() not read enough?");
|
||||
break;
|
||||
}
|
||||
|
||||
// We've filled the entire buffer and need more space.
|
||||
// This grow might be unnecessary if we're just so happen to reach EOF with the next
|
||||
// input, but checking this requires reading more input.
|
||||
buffer.grow();
|
||||
}
|
||||
|
||||
cb(user_data, buffer.read_data(), buffer.read_size());
|
||||
return file_error::_success;
|
||||
}
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_INPUT_ARGV_INPUT_HPP_INCLUDED
|
||||
#define LEXY_INPUT_ARGV_INPUT_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/assert.hpp>
|
||||
#include <lexy/_detail/std.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/dsl/whitespace.hpp>
|
||||
#include <lexy/input/base.hpp>
|
||||
#include <lexy/lexeme.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
/// A sentinel for the command-line arguments.
|
||||
class argv_sentinel
|
||||
{};
|
||||
|
||||
/// An iterator over the command-line arguments.
|
||||
class argv_iterator
|
||||
{
|
||||
public:
|
||||
using value_type = char;
|
||||
using reference = const char&;
|
||||
using pointer = const char*;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
|
||||
constexpr argv_iterator() noexcept : _arg(nullptr), _c(nullptr) {}
|
||||
|
||||
//=== dereference ===//
|
||||
constexpr reference operator*() const noexcept
|
||||
{
|
||||
return *_c;
|
||||
}
|
||||
constexpr pointer operator->() const noexcept
|
||||
{
|
||||
return _c;
|
||||
}
|
||||
|
||||
//=== positioning ===//
|
||||
constexpr argv_iterator& operator++() noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(*this != argv_sentinel{});
|
||||
if (*_c == '\0')
|
||||
{
|
||||
// Go to next argument.
|
||||
// We will have one, otherwise the precondition above would have fired.
|
||||
++_arg;
|
||||
// Update c to the beginning of next argument.
|
||||
_c = *_arg;
|
||||
}
|
||||
else
|
||||
++_c;
|
||||
|
||||
return *this;
|
||||
}
|
||||
constexpr argv_iterator operator++(int) noexcept
|
||||
{
|
||||
argv_iterator tmp(*this);
|
||||
++*this;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
constexpr argv_iterator& operator--() noexcept
|
||||
{
|
||||
// Check whether we point to the first character of the argument.
|
||||
if (_c == *_arg)
|
||||
{
|
||||
// Go to end of previous argument.
|
||||
--_arg;
|
||||
_c = *_arg;
|
||||
while (*_c != '\0')
|
||||
++_c;
|
||||
}
|
||||
else
|
||||
--_c;
|
||||
return *this;
|
||||
}
|
||||
constexpr argv_iterator operator--(int) noexcept
|
||||
{
|
||||
argv_iterator tmp(*this);
|
||||
--*this;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
//=== comparison ===//
|
||||
friend constexpr bool operator==(argv_iterator lhs, argv_sentinel) noexcept
|
||||
{
|
||||
// We're at the end if we're at the last character of the last argument,
|
||||
// or have an empty argv range.
|
||||
return lhs._c == nullptr || (*lhs._c == '\0' && lhs._arg[1] == nullptr);
|
||||
}
|
||||
friend constexpr bool operator!=(argv_iterator lhs, argv_sentinel) noexcept
|
||||
{
|
||||
return !(lhs == argv_sentinel{});
|
||||
}
|
||||
friend constexpr bool operator==(argv_sentinel, argv_iterator rhs) noexcept
|
||||
{
|
||||
return rhs == argv_sentinel{};
|
||||
}
|
||||
friend constexpr bool operator!=(argv_sentinel, argv_iterator rhs) noexcept
|
||||
{
|
||||
return !(rhs == argv_sentinel{});
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(argv_iterator lhs, argv_iterator rhs) noexcept
|
||||
{
|
||||
return lhs._arg == rhs._arg && lhs._c == rhs._c;
|
||||
}
|
||||
friend constexpr bool operator!=(argv_iterator lhs, argv_iterator rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr explicit argv_iterator(char** argument, char* c) noexcept : _arg(argument), _c(c) {}
|
||||
|
||||
char** _arg;
|
||||
char* _c;
|
||||
|
||||
friend constexpr argv_iterator argv_begin(int argc, char* argv[]) noexcept;
|
||||
friend constexpr argv_iterator argv_end(int argc, char* argv[]) noexcept;
|
||||
};
|
||||
|
||||
/// Returns an iterator to the beginning of the command-line arguments.
|
||||
constexpr argv_iterator argv_begin(int argc, char* argv[]) noexcept
|
||||
{
|
||||
if (argc <= 1)
|
||||
// Create an iterator where *arg_ == nullptr, *c_ == nullptr.
|
||||
return argv_iterator(&argv[argc], nullptr);
|
||||
else
|
||||
return argv_iterator(&argv[1], &argv[1][0]);
|
||||
}
|
||||
|
||||
/// Returns an iterator one past the end of the command-line arguments.
|
||||
constexpr argv_iterator argv_end(int argc, char* argv[]) noexcept
|
||||
{
|
||||
if (argc <= 1)
|
||||
// Create an iterator where *arg_ == nullptr, *c_ == nullptr.
|
||||
return argv_iterator(&argv[argc], nullptr);
|
||||
else
|
||||
{
|
||||
// Construct an iterator pointing to the nullptr arg.
|
||||
// Then decrement it to get the null terminator of the last argument,
|
||||
// which is the actual end.
|
||||
argv_iterator one_past_end(&argv[argc], nullptr);
|
||||
--one_past_end;
|
||||
return one_past_end;
|
||||
}
|
||||
}
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename Encoding = default_encoding>
|
||||
class argv_input
|
||||
{
|
||||
public:
|
||||
using encoding = Encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
static_assert(
|
||||
std::is_same_v<char_type, char> || Encoding::template is_secondary_char_type<char>,
|
||||
"invalid encoding for argv");
|
||||
|
||||
using iterator = argv_iterator;
|
||||
|
||||
//=== constructors ===//
|
||||
constexpr argv_input() = default;
|
||||
|
||||
/// Iterate over the range of command-line arguments.
|
||||
constexpr argv_input(argv_iterator begin, argv_iterator end) noexcept : _begin(begin), _end(end)
|
||||
{}
|
||||
|
||||
/// Iterate over the command-line arguments.
|
||||
constexpr argv_input(int argc, char* argv[]) noexcept
|
||||
: _begin(argv_begin(argc, argv)), _end(argv_end(argc, argv))
|
||||
{}
|
||||
|
||||
//=== reader ===//
|
||||
constexpr auto reader() const& noexcept
|
||||
{
|
||||
return _detail::range_reader<encoding, iterator>(_begin, _end);
|
||||
}
|
||||
|
||||
private:
|
||||
argv_iterator _begin, _end;
|
||||
};
|
||||
|
||||
argv_input(int argc, char* argv[])->argv_input<>;
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename Encoding = default_encoding>
|
||||
using argv_lexeme = lexeme_for<argv_input<Encoding>>;
|
||||
|
||||
template <typename Tag, typename Encoding = default_encoding>
|
||||
using argv_error = error_for<argv_input<Encoding>, Tag>;
|
||||
|
||||
template <typename Production, typename Encoding = default_encoding>
|
||||
using argv_error_context = error_context<Production, argv_input<Encoding>>;
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
struct _argvsep : atom_base<_argvsep>
|
||||
{
|
||||
template <typename Encoding>
|
||||
using _argv_reader = lexy::_detail::range_reader<Encoding, lexy::argv_iterator>;
|
||||
|
||||
template <typename Encoding>
|
||||
LEXY_DSL_FUNC bool match(_argv_reader<Encoding>& reader)
|
||||
{
|
||||
if (reader.peek() != Encoding::to_int_type('\0'))
|
||||
return false;
|
||||
reader.bump();
|
||||
return true;
|
||||
}
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC bool match(Reader&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
LEXY_DSL_FUNC auto error(const Reader&, typename Reader::iterator pos)
|
||||
{
|
||||
return lexy::make_error<Reader, lexy::expected_char_class>(pos, "argv-separator");
|
||||
}
|
||||
|
||||
template <typename Whitespace>
|
||||
LEXY_CONSTEVAL auto operator[](Whitespace ws) const
|
||||
{
|
||||
return whitespaced(*this, ws);
|
||||
}
|
||||
};
|
||||
|
||||
/// Matches the separator between arguments of an argv_input.
|
||||
constexpr auto argv_separator = _argvsep{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_INPUT_ARGV_INPUT_HPP_INCLUDED
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_INPUT_BASE_HPP_INCLUDED
|
||||
#define LEXY_INPUT_BASE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/config.hpp>
|
||||
#include <lexy/encoding.hpp>
|
||||
|
||||
#if 0
|
||||
/// Readers are non-owning, cheaply copyable types.
|
||||
class Reader
|
||||
{
|
||||
public:
|
||||
/// The encoding the input uses.
|
||||
using encoding = XXX_encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
|
||||
/// An iterator of char_type, not int_type.
|
||||
using iterator = ForwardIterator;
|
||||
|
||||
/// A reader with the same encoding and iterator that is used for reporting errors/lexemes.
|
||||
/// If you're writing an Input, the Input's Reader is always canonical.
|
||||
using canonical_reader = Reader;
|
||||
|
||||
/// Checks whether the reader is at EOF.
|
||||
bool eof() const;
|
||||
|
||||
/// If the reader is at eof, returns Encoding::eof().
|
||||
/// Otherwise, returns Encoding::to_int_type(/* current character */).
|
||||
typename Encoding::int_type peek() const;
|
||||
|
||||
/// Advances to the next character in the input.
|
||||
void bump();
|
||||
|
||||
/// Returns an iterator to the current character.
|
||||
/// The following code must produce a valid range:
|
||||
/// ```
|
||||
/// auto begin = reader.cur();
|
||||
/// reader.bump();
|
||||
/// ... // more bumps
|
||||
/// auto end = reader.cur();
|
||||
/// ```
|
||||
iterator cur() const;
|
||||
};
|
||||
|
||||
/// An Input produces a reader.
|
||||
class Input
|
||||
{
|
||||
public:
|
||||
/// Returns a reader to the beginning of the input.
|
||||
Reader reader() const &;
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
template <typename I>
|
||||
constexpr auto range_size(I begin, I end) -> decltype(std::size_t(end - begin))
|
||||
{
|
||||
return std::size_t(end - begin);
|
||||
}
|
||||
template <typename I, typename I2> // always worse match because two different params
|
||||
constexpr auto range_size(I begin, I2 end)
|
||||
{
|
||||
std::size_t result = 0;
|
||||
for (auto cur = begin; cur != end; ++cur)
|
||||
++result;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Encoding, typename Iterator, typename Sentinel = Iterator>
|
||||
class range_reader
|
||||
{
|
||||
public:
|
||||
using encoding = Encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
using iterator = Iterator;
|
||||
using canonical_reader = range_reader<Encoding, Iterator, Sentinel>;
|
||||
|
||||
constexpr explicit range_reader(Iterator begin, Sentinel end) noexcept : _cur(begin), _end(end)
|
||||
{}
|
||||
|
||||
constexpr bool eof() const noexcept
|
||||
{
|
||||
return _cur == _end;
|
||||
}
|
||||
|
||||
constexpr auto peek() const noexcept
|
||||
{
|
||||
if (_cur == _end)
|
||||
return encoding::eof();
|
||||
else
|
||||
return encoding::to_int_type(*_cur);
|
||||
}
|
||||
|
||||
constexpr void bump() noexcept
|
||||
{
|
||||
++_cur;
|
||||
}
|
||||
|
||||
constexpr iterator cur() const noexcept
|
||||
{
|
||||
return _cur;
|
||||
}
|
||||
|
||||
constexpr void _make_eof() noexcept
|
||||
{
|
||||
static_assert(std::is_same_v<Iterator, Sentinel>);
|
||||
_cur = _end;
|
||||
}
|
||||
|
||||
private:
|
||||
Iterator _cur;
|
||||
LEXY_EMPTY_MEMBER Sentinel _end;
|
||||
};
|
||||
} // namespace lexy::_detail
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename Input>
|
||||
using input_reader = decltype(LEXY_DECLVAL(Input).reader());
|
||||
|
||||
template <typename Reader, typename CharT>
|
||||
constexpr bool char_type_compatible_with_reader
|
||||
= (std::is_same_v<CharT, typename Reader::char_type>)
|
||||
|| Reader::encoding::template is_secondary_char_type<CharT>;
|
||||
|
||||
template <typename Reader>
|
||||
constexpr bool is_canonical_reader = std::is_same_v<typename Reader::canonical_reader, Reader>;
|
||||
|
||||
/// Creates a reader that only reads until the given end.
|
||||
template <typename Reader>
|
||||
constexpr auto partial_reader(Reader reader, typename Reader::iterator end)
|
||||
{
|
||||
struct partial_reader_t
|
||||
: _detail::range_reader<typename Reader::encoding, typename Reader::iterator>
|
||||
{
|
||||
using canonical_reader = Reader;
|
||||
using _detail::range_reader<typename Reader::encoding,
|
||||
typename Reader::iterator>::range_reader;
|
||||
};
|
||||
return partial_reader_t(reader.cur(), end);
|
||||
}
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_INPUT_BASE_HPP_INCLUDED
|
||||
@@ -1,396 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_INPUT_BUFFER_HPP_INCLUDED
|
||||
#define LEXY_INPUT_BUFFER_HPP_INCLUDED
|
||||
|
||||
#include <cstring>
|
||||
#include <lexy/_detail/memory_resource.hpp>
|
||||
#include <lexy/error.hpp>
|
||||
#include <lexy/input/base.hpp>
|
||||
#include <lexy/lexeme.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
/// Stores the input that will be parsed.
|
||||
/// For encodings with spare code points, it can append an EOF sentinel.
|
||||
/// This allows branch-less detection of EOF.
|
||||
template <typename Encoding = default_encoding,
|
||||
typename MemoryResource = _detail::default_memory_resource>
|
||||
class buffer
|
||||
{
|
||||
static constexpr auto _has_sentinel
|
||||
= std::is_same_v<typename Encoding::char_type, typename Encoding::int_type>;
|
||||
|
||||
public:
|
||||
using encoding = Encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
|
||||
//=== constructors ===//
|
||||
/// Allows the creation of an uninitialized buffer that is then filled by the user.
|
||||
class builder
|
||||
{
|
||||
public:
|
||||
explicit builder(std::size_t size,
|
||||
MemoryResource* resource = _detail::get_memory_resource<MemoryResource>())
|
||||
: _buffer(resource)
|
||||
{
|
||||
_buffer._data = _buffer.allocate(size);
|
||||
_buffer._size = size;
|
||||
}
|
||||
|
||||
char_type* data() const noexcept
|
||||
{
|
||||
return _buffer._data;
|
||||
}
|
||||
std::size_t size() const noexcept
|
||||
{
|
||||
return _buffer._size;
|
||||
}
|
||||
|
||||
buffer finish() && noexcept
|
||||
{
|
||||
return LEXY_MOV(_buffer);
|
||||
}
|
||||
|
||||
private:
|
||||
buffer _buffer;
|
||||
};
|
||||
|
||||
constexpr buffer() noexcept : buffer(_detail::get_memory_resource<MemoryResource>()) {}
|
||||
|
||||
constexpr explicit buffer(MemoryResource* resource) noexcept
|
||||
: _resource(resource), _data(nullptr), _size(0)
|
||||
{}
|
||||
|
||||
explicit buffer(const char_type* data, std::size_t size,
|
||||
MemoryResource* resource = _detail::get_memory_resource<MemoryResource>())
|
||||
: _resource(resource), _size(size)
|
||||
{
|
||||
_data = allocate(size);
|
||||
std::memcpy(_data, data, size * sizeof(char_type));
|
||||
}
|
||||
explicit buffer(const char_type* begin, const char_type* end,
|
||||
MemoryResource* resource = _detail::get_memory_resource<MemoryResource>())
|
||||
: buffer(begin, std::size_t(end - begin), resource)
|
||||
{}
|
||||
|
||||
template <typename CharT, typename = _require_secondary_char_type<encoding, CharT>>
|
||||
explicit buffer(const CharT* data, std::size_t size,
|
||||
MemoryResource* resource = _detail::get_memory_resource<MemoryResource>())
|
||||
: buffer(reinterpret_cast<const char_type*>(data), size, resource)
|
||||
{
|
||||
static_assert(sizeof(CharT) == sizeof(char_type));
|
||||
}
|
||||
template <typename CharT, typename = _require_secondary_char_type<encoding, CharT>>
|
||||
explicit buffer(const CharT* begin, const CharT* end,
|
||||
MemoryResource* resource = _detail::get_memory_resource<MemoryResource>())
|
||||
: buffer(reinterpret_cast<const char_type*>(begin), reinterpret_cast<const char_type*>(end),
|
||||
resource)
|
||||
{
|
||||
static_assert(sizeof(CharT) == sizeof(char_type));
|
||||
}
|
||||
|
||||
template <typename View, typename = decltype(LEXY_DECLVAL(View).data())>
|
||||
explicit buffer(const View& view,
|
||||
MemoryResource* resource = _detail::get_memory_resource<MemoryResource>())
|
||||
: buffer(view.data(), view.size(), resource)
|
||||
{}
|
||||
|
||||
buffer(const buffer& other) : buffer(other.data(), other.size(), other._resource.get()) {}
|
||||
buffer(const buffer& other, MemoryResource* resource)
|
||||
: buffer(other.data(), other.size(), resource)
|
||||
{}
|
||||
|
||||
buffer(buffer&& other) noexcept
|
||||
: _resource(other._resource), _data(other._data), _size(other._size)
|
||||
{
|
||||
other._data = nullptr;
|
||||
other._size = 0;
|
||||
}
|
||||
|
||||
~buffer() noexcept
|
||||
{
|
||||
if (!_data)
|
||||
return;
|
||||
|
||||
if constexpr (_has_sentinel)
|
||||
_resource->deallocate(_data, (_size + 1) * sizeof(char_type), alignof(char_type));
|
||||
else
|
||||
_resource->deallocate(_data, _size * sizeof(char_type), alignof(char_type));
|
||||
}
|
||||
|
||||
buffer& operator=(const buffer& other)
|
||||
{
|
||||
// Create a temporary buffer that owns the same memory as other but with our resource.
|
||||
// We then move assign it to *this.
|
||||
return *this = buffer(other, _resource.get());
|
||||
}
|
||||
|
||||
buffer& operator=(buffer&& other) noexcept(std::is_empty_v<MemoryResource>)
|
||||
{
|
||||
if (*_resource == *other._resource)
|
||||
{
|
||||
// We have the same resource; we can just steal other's memory.
|
||||
// We do that by swapping - when other is destroyed it will free our memory.
|
||||
_detail::swap(_data, other._data);
|
||||
_detail::swap(_size, other._size);
|
||||
return *this;
|
||||
}
|
||||
else
|
||||
{
|
||||
LEXY_PRECONDITION(!std::is_empty_v<MemoryResource>);
|
||||
|
||||
// We create a copy using the right resource and swap the ownership.
|
||||
buffer copy(other, _resource.get());
|
||||
_detail::swap(_data, copy._data);
|
||||
_detail::swap(_size, copy._size);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
//=== access ===//
|
||||
const char_type* begin() const noexcept
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
const char_type* end() const noexcept
|
||||
{
|
||||
return _data + _size;
|
||||
}
|
||||
|
||||
const char_type* data() const noexcept
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
bool empty() const noexcept
|
||||
{
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
std::size_t size() const noexcept
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
std::size_t length() const noexcept
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
//=== input ===//
|
||||
auto reader() const& noexcept
|
||||
{
|
||||
if constexpr (_has_sentinel)
|
||||
return _sentinel_reader(_data);
|
||||
else
|
||||
return _detail::range_reader<encoding, const char_type*>(_data, _data + _size);
|
||||
}
|
||||
|
||||
private:
|
||||
class _sentinel_reader
|
||||
{
|
||||
public:
|
||||
using encoding = Encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
using iterator = const char_type*;
|
||||
using canonical_reader = _sentinel_reader;
|
||||
|
||||
bool eof() const noexcept
|
||||
{
|
||||
return *_cur == encoding::eof();
|
||||
}
|
||||
|
||||
auto peek() const noexcept
|
||||
{
|
||||
// The last one will be EOF.
|
||||
return *_cur;
|
||||
}
|
||||
|
||||
void bump() noexcept
|
||||
{
|
||||
++_cur;
|
||||
}
|
||||
|
||||
iterator cur() const noexcept
|
||||
{
|
||||
return _cur;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit _sentinel_reader(iterator begin) noexcept : _cur(begin) {}
|
||||
|
||||
iterator _cur;
|
||||
friend buffer;
|
||||
};
|
||||
|
||||
char_type* allocate(std::size_t size) const
|
||||
{
|
||||
if constexpr (_has_sentinel)
|
||||
++size;
|
||||
|
||||
auto memory = static_cast<char_type*>(
|
||||
_resource->allocate(size * sizeof(char_type), alignof(char_type)));
|
||||
if constexpr (_has_sentinel)
|
||||
memory[size - 1] = encoding::eof();
|
||||
return memory;
|
||||
}
|
||||
|
||||
LEXY_EMPTY_MEMBER _detail::memory_resource_ptr<MemoryResource> _resource;
|
||||
char_type* _data;
|
||||
std::size_t _size;
|
||||
};
|
||||
|
||||
template <typename CharT>
|
||||
buffer(const CharT*, const CharT*) -> buffer<deduce_encoding<CharT>>;
|
||||
template <typename CharT>
|
||||
buffer(const CharT*, std::size_t) -> buffer<deduce_encoding<CharT>>;
|
||||
template <typename View>
|
||||
buffer(const View&) -> buffer<deduce_encoding<std::decay_t<decltype(*LEXY_DECLVAL(View).data())>>>;
|
||||
|
||||
template <typename CharT, typename MemoryResource>
|
||||
buffer(const CharT*, const CharT*, MemoryResource*)
|
||||
-> buffer<deduce_encoding<CharT>, MemoryResource>;
|
||||
template <typename CharT, typename MemoryResource>
|
||||
buffer(const CharT*, std::size_t, MemoryResource*)
|
||||
-> buffer<deduce_encoding<CharT>, MemoryResource>;
|
||||
template <typename View, typename MemoryResource>
|
||||
buffer(const View&, MemoryResource*)
|
||||
-> buffer<deduce_encoding<std::decay_t<decltype(*LEXY_DECLVAL(View).data())>>, MemoryResource>;
|
||||
|
||||
//=== make_buffer ===//
|
||||
template <typename Encoding, encoding_endianness Endian>
|
||||
struct _make_buffer
|
||||
{
|
||||
template <typename MemoryResource = _detail::default_memory_resource>
|
||||
auto operator()(const void* _memory, std::size_t size,
|
||||
MemoryResource* resource = _detail::get_memory_resource<MemoryResource>()) const
|
||||
{
|
||||
constexpr auto native_endianness
|
||||
= LEXY_IS_LITTLE_ENDIAN ? encoding_endianness::little : encoding_endianness::big;
|
||||
|
||||
using char_type = typename Encoding::char_type;
|
||||
LEXY_PRECONDITION(size % sizeof(char_type) == 0);
|
||||
auto memory = static_cast<const unsigned char*>(_memory);
|
||||
|
||||
if constexpr (sizeof(char_type) == 1 || Endian == native_endianness)
|
||||
{
|
||||
// No need to deal with endianness at all.
|
||||
// The reinterpret_cast is technically UB, as we didn't create objects in memory,
|
||||
// but until std::start_lifetime_as is added, there is nothing we can do.
|
||||
return buffer<Encoding, MemoryResource>(reinterpret_cast<const char_type*>(memory),
|
||||
size / sizeof(char_type), resource);
|
||||
}
|
||||
else
|
||||
{
|
||||
typename buffer<Encoding, MemoryResource>::builder builder(size / sizeof(char_type),
|
||||
resource);
|
||||
|
||||
const auto end = memory + size;
|
||||
for (auto dest = builder.data(); memory != end; memory += sizeof(char_type))
|
||||
{
|
||||
constexpr auto is_char16 = std::is_same_v<char_type, char16_t>;
|
||||
constexpr auto is_char32 = std::is_same_v<char_type, char32_t>;
|
||||
|
||||
// We convert each group of bytes to the appropriate value.
|
||||
if constexpr (is_char16 && Endian == encoding_endianness::little)
|
||||
*dest++ = static_cast<char_type>((memory[0] << 0) | (memory[1] << 8));
|
||||
else if constexpr (is_char32 && Endian == encoding_endianness::little)
|
||||
*dest++ = static_cast<char_type>((memory[0] << 0) | (memory[1] << 8)
|
||||
| (memory[2] << 16) | (memory[3] << 24));
|
||||
else if constexpr (is_char16 && Endian == encoding_endianness::big)
|
||||
*dest++ = static_cast<char_type>((memory[0] << 8) | (memory[1] << 0));
|
||||
else if constexpr (is_char32 && Endian == encoding_endianness::big)
|
||||
*dest++ = static_cast<char_type>((memory[0] << 24) | (memory[1] << 16)
|
||||
| (memory[2] << 8) | (memory[3] << 0));
|
||||
else
|
||||
static_assert(_detail::error<Encoding>, "unhandled encoding/endianness");
|
||||
}
|
||||
|
||||
return LEXY_MOV(builder).finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct _make_buffer<utf8_encoding, encoding_endianness::bom>
|
||||
{
|
||||
template <typename MemoryResource = _detail::default_memory_resource>
|
||||
auto operator()(const void* _memory, std::size_t size,
|
||||
MemoryResource* resource = _detail::get_memory_resource<MemoryResource>()) const
|
||||
{
|
||||
auto memory = static_cast<const unsigned char*>(_memory);
|
||||
|
||||
// We just skip over the BOM if there is one, it doesn't matter.
|
||||
if (size >= 3 && memory[0] == 0xEF && memory[1] == 0xBB && memory[2] == 0xBF)
|
||||
{
|
||||
memory += 3;
|
||||
size -= 3;
|
||||
}
|
||||
|
||||
return _make_buffer<utf8_encoding, encoding_endianness::big>{}(memory, size, resource);
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct _make_buffer<utf16_encoding, encoding_endianness::bom>
|
||||
{
|
||||
template <typename MemoryResource = _detail::default_memory_resource>
|
||||
auto operator()(const void* _memory, std::size_t size,
|
||||
MemoryResource* resource = _detail::get_memory_resource<MemoryResource>()) const
|
||||
{
|
||||
constexpr auto utf16_big = _make_buffer<utf16_encoding, encoding_endianness::big>{};
|
||||
constexpr auto utf16_little = _make_buffer<utf16_encoding, encoding_endianness::little>{};
|
||||
auto memory = static_cast<const unsigned char*>(_memory);
|
||||
|
||||
if (size < 2)
|
||||
return utf16_big(memory, size, resource);
|
||||
if (memory[0] == 0xFF && memory[1] == 0xFE)
|
||||
return utf16_little(memory + 2, size - 2, resource);
|
||||
else if (memory[0] == 0xFE && memory[1] == 0xFF)
|
||||
return utf16_big(memory + 2, size - 2, resource);
|
||||
else
|
||||
return utf16_big(memory, size, resource);
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct _make_buffer<utf32_encoding, encoding_endianness::bom>
|
||||
{
|
||||
template <typename MemoryResource = _detail::default_memory_resource>
|
||||
auto operator()(const void* _memory, std::size_t size,
|
||||
MemoryResource* resource = _detail::get_memory_resource<MemoryResource>()) const
|
||||
{
|
||||
constexpr auto utf32_big = _make_buffer<utf32_encoding, encoding_endianness::big>{};
|
||||
constexpr auto utf32_little = _make_buffer<utf32_encoding, encoding_endianness::little>{};
|
||||
auto memory = static_cast<const unsigned char*>(_memory);
|
||||
|
||||
if (size < 4)
|
||||
return utf32_big(memory, size, resource);
|
||||
else if (memory[0] == 0xFF && memory[1] == 0xFE && memory[2] == 0x00 && memory[3] == 0x00)
|
||||
return utf32_little(memory + 4, size - 4, resource);
|
||||
else if (memory[0] == 0x00 && memory[1] == 0x00 && memory[2] == 0xFE && memory[3])
|
||||
return utf32_big(memory + 4, size - 4, resource);
|
||||
else
|
||||
return utf32_big(memory, size, resource);
|
||||
}
|
||||
};
|
||||
|
||||
/// Creates a buffer with the specified encoding/endianness from raw memory.
|
||||
template <typename Encoding, encoding_endianness Endianness>
|
||||
constexpr auto make_buffer = _make_buffer<Encoding, Endianness>{};
|
||||
|
||||
//=== convenience typedefs ===//
|
||||
template <typename Encoding = default_encoding,
|
||||
typename MemoryResource = _detail::default_memory_resource>
|
||||
using buffer_lexeme = lexeme_for<buffer<Encoding, MemoryResource>>;
|
||||
|
||||
template <typename Tag, typename Encoding = default_encoding,
|
||||
typename MemoryResource = _detail::default_memory_resource>
|
||||
using buffer_error = error_for<buffer<Encoding, MemoryResource>, Tag>;
|
||||
|
||||
template <typename Production, typename Encoding = default_encoding,
|
||||
typename MemoryResource = _detail::default_memory_resource>
|
||||
using buffer_error_context = error_context<Production, buffer<Encoding, MemoryResource>>;
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_INPUT_BUFFER_HPP_INCLUDED
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_INPUT_FILE_HPP_INCLUDED
|
||||
#define LEXY_INPUT_FILE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/std.hpp>
|
||||
#include <lexy/input/base.hpp>
|
||||
#include <lexy/input/buffer.hpp>
|
||||
#include <lexy/result.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
/// Errors that might occur while reading the file.
|
||||
enum class file_error
|
||||
{
|
||||
_success,
|
||||
/// An internal OS error, such as failure to read from the file.
|
||||
os_error,
|
||||
/// The file was not found.
|
||||
file_not_found,
|
||||
/// The file cannot be opened.
|
||||
permission_denied,
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy::_detail
|
||||
{
|
||||
using file_callback = void (*)(void* user_data, const char* memory, std::size_t size);
|
||||
|
||||
// Reads the entire contents of the specified file into memory.
|
||||
// On success, invokes the callback before freeing the memory.
|
||||
// On error, returns the error without invoking the callback.
|
||||
//
|
||||
// Do not change ABI, especially with different build configurations!
|
||||
file_error read_file(const char* path, file_callback cb, void* user_data);
|
||||
} // namespace lexy::_detail
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
/// Reads the file at the specified path into a buffer.
|
||||
template <typename Encoding = default_encoding,
|
||||
encoding_endianness Endian = encoding_endianness::bom,
|
||||
typename MemoryResource = _detail::default_memory_resource>
|
||||
auto read_file(const char* path,
|
||||
MemoryResource* resource = _detail::get_memory_resource<MemoryResource>())
|
||||
-> result<buffer<Encoding, MemoryResource>, file_error>
|
||||
{
|
||||
using buffer_type = buffer<Encoding, MemoryResource>;
|
||||
|
||||
struct user_data_t
|
||||
{
|
||||
buffer_type buffer;
|
||||
MemoryResource* resource;
|
||||
} user_data{buffer_type(resource), resource};
|
||||
|
||||
auto error = _detail::read_file(
|
||||
path,
|
||||
[](void* _user_data, const char* memory, std::size_t size) {
|
||||
auto user_data = static_cast<user_data_t*>(_user_data);
|
||||
|
||||
user_data->buffer
|
||||
= lexy::make_buffer<Encoding, Endian>(memory, size, user_data->resource);
|
||||
},
|
||||
&user_data);
|
||||
|
||||
if (error == file_error::_success)
|
||||
return {lexy::result_value, LEXY_MOV(user_data.buffer)};
|
||||
else
|
||||
return {lexy::result_error, error};
|
||||
}
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_INPUT_FILE_HPP_INCLUDED
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_INPUT_NULL_INPUT_HPP_INCLUDED
|
||||
#define LEXY_INPUT_NULL_INPUT_HPP_INCLUDED
|
||||
|
||||
#include <lexy/error.hpp>
|
||||
#include <lexy/input/base.hpp>
|
||||
#include <lexy/lexeme.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename Encoding = default_encoding>
|
||||
class null_input
|
||||
{
|
||||
public:
|
||||
constexpr auto reader() const& noexcept
|
||||
{
|
||||
class reader_type
|
||||
{
|
||||
public:
|
||||
using encoding = Encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
using iterator = const char_type*;
|
||||
using canonical_reader = reader_type;
|
||||
|
||||
constexpr bool eof() const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr auto peek() const noexcept
|
||||
{
|
||||
return Encoding::eof();
|
||||
}
|
||||
|
||||
constexpr void bump() noexcept {}
|
||||
|
||||
constexpr iterator cur() const noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
return reader_type();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Encoding = default_encoding>
|
||||
using null_lexeme = lexeme_for<null_input<Encoding>>;
|
||||
|
||||
template <typename Tag, typename Encoding = default_encoding>
|
||||
using null_error = error_for<null_input<Encoding>, Tag>;
|
||||
|
||||
template <typename Production, typename Encoding = default_encoding>
|
||||
using null_error_context = error_context<Production, null_input<Encoding>>;
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_INPUT_NULL_INPUT_HPP_INCLUDED
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_INPUT_RANGE_INPUT_HPP_INCLUDED
|
||||
#define LEXY_INPUT_RANGE_INPUT_HPP_INCLUDED
|
||||
|
||||
#include <lexy/error.hpp>
|
||||
#include <lexy/input/base.hpp>
|
||||
#include <lexy/lexeme.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename Encoding, typename Iterator, typename Sentinel = Iterator>
|
||||
class range_input
|
||||
{
|
||||
public:
|
||||
using encoding = Encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
|
||||
using iterator = Iterator;
|
||||
|
||||
//=== constructors ===//
|
||||
constexpr range_input() noexcept : _begin(), _end() {}
|
||||
|
||||
constexpr range_input(Iterator begin, Sentinel end) noexcept : _begin(begin), _end(end) {}
|
||||
|
||||
//=== access ===//
|
||||
constexpr iterator begin() const noexcept
|
||||
{
|
||||
return _begin;
|
||||
}
|
||||
|
||||
constexpr iterator end() const noexcept
|
||||
{
|
||||
return _end;
|
||||
}
|
||||
|
||||
//=== reader ===//
|
||||
constexpr auto reader() const& noexcept
|
||||
{
|
||||
return _detail::range_reader<Encoding, Iterator, Sentinel>(_begin, _end);
|
||||
}
|
||||
|
||||
private:
|
||||
Iterator _begin;
|
||||
LEXY_EMPTY_MEMBER Sentinel _end;
|
||||
};
|
||||
|
||||
template <typename Iterator, typename Sentinel>
|
||||
range_input(Iterator begin, Sentinel end)
|
||||
-> range_input<deduce_encoding<std::decay_t<decltype(*begin)>>, Iterator, Sentinel>;
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_INPUT_RANGE_INPUT_HPP_INCLUDED
|
||||
@@ -1,372 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_INPUT_SHELL_HPP_INCLUDED
|
||||
#define LEXY_INPUT_SHELL_HPP_INCLUDED
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <lexy/_detail/assert.hpp>
|
||||
#include <lexy/_detail/buffer_builder.hpp>
|
||||
#include <lexy/error.hpp>
|
||||
#include <lexy/input/base.hpp>
|
||||
#include <lexy/lexeme.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
#if 0
|
||||
/// Controls how the shell performs I/O.
|
||||
class Prompt
|
||||
{
|
||||
using encoding = ...;
|
||||
|
||||
/// Called to display the primary prompt.
|
||||
void primary_prompt();
|
||||
|
||||
/// Called to display the continuation prompt.
|
||||
void continuation_prompt();
|
||||
|
||||
/// Called to display EOF.
|
||||
void eof_prompt();
|
||||
|
||||
/// Whether or not the user has closed the input.
|
||||
bool is_open() const;
|
||||
|
||||
struct read_line_callback
|
||||
{
|
||||
/// Reads at most `size` characters into the `buffer` until and including a newline.
|
||||
/// Returns the number of characters read.
|
||||
/// If the number of characters read is less than the size,
|
||||
/// the entire line has been read or a read error occurs.
|
||||
std::size_t operator()(char_type* buffer, std::size_t size);
|
||||
|
||||
/// Called after the shell has finished reading.
|
||||
void done() &&;
|
||||
};
|
||||
|
||||
/// Returns a callback object for reading the next line.
|
||||
auto read_line()
|
||||
{
|
||||
return read_line_callback{...};
|
||||
}
|
||||
|
||||
struct write_message_callback
|
||||
{
|
||||
/// Writes the buffer.
|
||||
void operator()(const char_type* buffer, std::size_t size);
|
||||
|
||||
/// Called to finish writing.
|
||||
void done() &&;
|
||||
};
|
||||
|
||||
/// Writes a message out.
|
||||
/// The arguments are passed by the user to indicate kinds of messages.
|
||||
auto write_message(Args&&... config_args)
|
||||
{
|
||||
return write_message_callback{...};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename Encoding = default_encoding>
|
||||
struct default_prompt
|
||||
{
|
||||
using encoding = Encoding;
|
||||
using char_type = typename Encoding::char_type;
|
||||
static_assert(sizeof(char_type) == sizeof(char), "only support single-byte encodings");
|
||||
|
||||
void primary_prompt() noexcept
|
||||
{
|
||||
std::fputs("> ", stdout);
|
||||
}
|
||||
|
||||
void continuation_prompt() noexcept
|
||||
{
|
||||
std::fputs(". ", stdout);
|
||||
}
|
||||
|
||||
void eof_prompt() noexcept
|
||||
{
|
||||
// We write an additional newline to prevent output on the same line.
|
||||
std::fputs("\n", stdout);
|
||||
}
|
||||
|
||||
bool is_open() const noexcept
|
||||
{
|
||||
return !std::feof(stdin) && !std::ferror(stdin);
|
||||
}
|
||||
|
||||
struct read_line_callback
|
||||
{
|
||||
std::size_t operator()(char_type* buffer, std::size_t size)
|
||||
{
|
||||
LEXY_PRECONDITION(size > 1);
|
||||
|
||||
auto memory = reinterpret_cast<char*>(buffer);
|
||||
if (auto str = std::fgets(memory, int(size), stdin))
|
||||
return std::strlen(str);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void done() && {}
|
||||
};
|
||||
auto read_line()
|
||||
{
|
||||
return read_line_callback{};
|
||||
}
|
||||
|
||||
struct write_message_callback
|
||||
{
|
||||
void operator()(const char_type* buffer, std::size_t size)
|
||||
{
|
||||
std::fprintf(stdout, "%.*s", int(size), reinterpret_cast<const char*>(buffer));
|
||||
}
|
||||
|
||||
void done() &&
|
||||
{
|
||||
std::putchar('\n');
|
||||
}
|
||||
};
|
||||
auto write_message()
|
||||
{
|
||||
return write_message_callback{};
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
/// Reads input from an interactive shell.
|
||||
template <typename Prompt = default_prompt<>>
|
||||
class shell
|
||||
{
|
||||
public:
|
||||
using encoding = typename Prompt::encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
using prompt_type = Prompt;
|
||||
|
||||
shell() = default;
|
||||
explicit shell(Prompt prompt) : _prompt(LEXY_MOV(prompt)) {}
|
||||
|
||||
/// Whether or not the shell is still open.
|
||||
bool is_open() const noexcept
|
||||
{
|
||||
return _prompt.is_open();
|
||||
}
|
||||
|
||||
// This is both Reader and Input.
|
||||
class input
|
||||
{
|
||||
public:
|
||||
using encoding = typename Prompt::encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
using iterator = typename _detail::buffer_builder<char_type>::stable_iterator;
|
||||
using canonical_reader = input;
|
||||
|
||||
auto reader() const&
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool eof() const
|
||||
{
|
||||
if (_idx != _shell->_buffer.read_size())
|
||||
// We're still having characters in the read buffer.
|
||||
return false;
|
||||
else if (!_shell->_prompt.is_open())
|
||||
// The prompt has been closed by the user.
|
||||
return true;
|
||||
else
|
||||
{
|
||||
// We've reached the end of the buffer, but the user might be willing to type
|
||||
// another line.
|
||||
_shell->_prompt.continuation_prompt();
|
||||
auto did_append = _shell->append_next_line();
|
||||
if (!did_append)
|
||||
_shell->_prompt.eof_prompt();
|
||||
return !did_append;
|
||||
}
|
||||
}
|
||||
|
||||
auto peek() const
|
||||
{
|
||||
if (eof())
|
||||
return encoding::eof();
|
||||
else
|
||||
return encoding::to_int_type(_shell->_buffer.read_data()[_idx]);
|
||||
}
|
||||
|
||||
void bump() noexcept
|
||||
{
|
||||
++_idx;
|
||||
}
|
||||
|
||||
auto cur() const noexcept
|
||||
{
|
||||
return iterator(_shell->_buffer, _idx);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit input(shell* s) : _shell(s), _idx(0)
|
||||
{
|
||||
_shell->_buffer.clear();
|
||||
_shell->_prompt.primary_prompt();
|
||||
if (!_shell->append_next_line())
|
||||
_shell->_prompt.eof_prompt();
|
||||
}
|
||||
|
||||
shell* _shell;
|
||||
std::size_t _idx;
|
||||
|
||||
friend shell;
|
||||
};
|
||||
|
||||
/// Asks the user to enter input.
|
||||
/// This will invalidate the previous buffer.
|
||||
/// Returns an Input for that input.
|
||||
auto prompt_for_input()
|
||||
{
|
||||
return input(this);
|
||||
}
|
||||
|
||||
class writer
|
||||
{
|
||||
public:
|
||||
writer(const writer&) = delete;
|
||||
writer& operator=(const writer&) = delete;
|
||||
|
||||
~writer() noexcept
|
||||
{
|
||||
LEXY_MOV(_writer).done();
|
||||
}
|
||||
|
||||
writer& operator()(const char_type* str, std::size_t length)
|
||||
{
|
||||
_writer(str, length);
|
||||
return *this;
|
||||
}
|
||||
writer& operator()(const char_type* str)
|
||||
{
|
||||
auto length = std::size_t(0);
|
||||
for (auto ptr = str; *ptr; ++ptr)
|
||||
++length;
|
||||
_writer(str, length);
|
||||
return *this;
|
||||
}
|
||||
writer& operator()(char_type c)
|
||||
{
|
||||
_writer(&c, 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename CharT,
|
||||
typename = std::enable_if_t<encoding::template is_secondary_char_type<CharT>>>
|
||||
writer& operator()(const CharT* str, std::size_t length)
|
||||
{
|
||||
return operator()(reinterpret_cast<const char_type*>(str), length);
|
||||
}
|
||||
template <typename CharT,
|
||||
typename = std::enable_if_t<encoding::template is_secondary_char_type<CharT>>>
|
||||
writer& operator()(const CharT* str)
|
||||
{
|
||||
return operator()(reinterpret_cast<const char_type*>(str));
|
||||
}
|
||||
template <typename CharT,
|
||||
typename = std::enable_if_t<encoding::template is_secondary_char_type<CharT>>>
|
||||
writer& operator()(CharT c)
|
||||
{
|
||||
return operator()(char_type(c));
|
||||
}
|
||||
|
||||
writer& operator()(lexy::lexeme_for<input> lexeme)
|
||||
{
|
||||
// We know that the iterator is contiguous.
|
||||
auto data = &*lexeme.begin();
|
||||
_writer(data, lexeme.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit writer(typename Prompt::write_message_callback&& writer)
|
||||
: _writer(LEXY_MOV(writer))
|
||||
{}
|
||||
|
||||
LEXY_EMPTY_MEMBER typename Prompt::write_message_callback _writer;
|
||||
|
||||
friend shell;
|
||||
};
|
||||
|
||||
/// Writes a message out to the shell.
|
||||
template <typename... Args>
|
||||
auto write_message(Args&&... args)
|
||||
{
|
||||
return writer{_prompt.write_message(LEXY_FWD(args)...)};
|
||||
}
|
||||
|
||||
Prompt& get_prompt() noexcept
|
||||
{
|
||||
return _prompt;
|
||||
}
|
||||
const Prompt& get_prompt() const noexcept
|
||||
{
|
||||
return _prompt;
|
||||
}
|
||||
|
||||
private:
|
||||
// Returns whether or not we've read anything.
|
||||
bool append_next_line()
|
||||
{
|
||||
// Grwo buffer if necessary.
|
||||
constexpr auto min_capacity = 128;
|
||||
if (_buffer.write_size() < min_capacity)
|
||||
_buffer.grow();
|
||||
|
||||
for (auto reader = _prompt.read_line(); true;)
|
||||
{
|
||||
const auto buffer_size = _buffer.write_size();
|
||||
|
||||
// Read into the entire write area of the buffer from the file,
|
||||
// commiting what we've just read.
|
||||
const auto read = reader(_buffer.write_data(), buffer_size);
|
||||
_buffer.commit(read);
|
||||
|
||||
// Check whether we've read the entire line.
|
||||
if (_buffer.write_data()[-1] == '\n')
|
||||
{
|
||||
LEXY_MOV(reader).done();
|
||||
return true;
|
||||
}
|
||||
else if (read < buffer_size)
|
||||
{
|
||||
LEXY_ASSERT(!_prompt.is_open(), "read error but prompt still open?!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We've filled the entire buffer and need more space.
|
||||
// This grow might be unnecessary if we're just so happen to reach the newline with the
|
||||
// next character, but checking this requires reading more input.
|
||||
_buffer.grow();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_detail::buffer_builder<char_type> _buffer;
|
||||
LEXY_EMPTY_MEMBER Prompt _prompt;
|
||||
};
|
||||
|
||||
//=== convenience typedefs ===//
|
||||
template <typename Prompt = default_prompt<>>
|
||||
using shell_lexeme = lexeme_for<shell<Prompt>>;
|
||||
|
||||
template <typename Tag, typename Prompt = default_prompt<>>
|
||||
using shell_error = error_for<shell<Prompt>, Tag>;
|
||||
|
||||
template <typename Production, typename Prompt = default_prompt<>>
|
||||
using shell_error_context = error_context<Production, shell<Prompt>>;
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_INPUT_SHELL_HPP_INCLUDED
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_INPUT_STRING_INPUT_HPP_INCLUDED
|
||||
#define LEXY_INPUT_STRING_INPUT_HPP_INCLUDED
|
||||
|
||||
#include <lexy/error.hpp>
|
||||
#include <lexy/input/base.hpp>
|
||||
#include <lexy/lexeme.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename View>
|
||||
using _string_view_char_type = std::decay_t<decltype(*LEXY_DECLVAL(View).data())>;
|
||||
|
||||
/// An input that refers to a string.
|
||||
template <typename Encoding = default_encoding>
|
||||
class string_input
|
||||
{
|
||||
public:
|
||||
using encoding = Encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
|
||||
using iterator = const char_type*;
|
||||
|
||||
//=== constructors ===//
|
||||
constexpr string_input() noexcept : _begin(nullptr), _end(nullptr) {}
|
||||
|
||||
constexpr string_input(const char_type* begin, const char_type* end) noexcept
|
||||
: _begin(begin), _end(end)
|
||||
{}
|
||||
constexpr string_input(const char_type* data, std::size_t size) noexcept
|
||||
: string_input(data, data + size)
|
||||
{}
|
||||
|
||||
template <typename CharT, typename = _require_secondary_char_type<Encoding, CharT>>
|
||||
string_input(const CharT* begin, const CharT* end) noexcept
|
||||
: _begin(reinterpret_cast<iterator>(begin)), _end(reinterpret_cast<iterator>(end))
|
||||
{}
|
||||
template <typename CharT, typename = _require_secondary_char_type<Encoding, CharT>>
|
||||
string_input(const CharT* data, std::size_t size) noexcept : string_input(data, data + size)
|
||||
{}
|
||||
|
||||
template <typename View, typename CharT = _string_view_char_type<View>>
|
||||
constexpr explicit string_input(const View& view) noexcept
|
||||
{
|
||||
if constexpr (std::is_same_v<CharT, char_type>)
|
||||
{
|
||||
_begin = view.data();
|
||||
_end = _begin + view.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(Encoding::template is_secondary_char_type<CharT>);
|
||||
_begin = reinterpret_cast<iterator>(view.data());
|
||||
_end = _begin + view.size();
|
||||
}
|
||||
}
|
||||
|
||||
//=== access ===//
|
||||
constexpr iterator begin() const noexcept
|
||||
{
|
||||
return _begin;
|
||||
}
|
||||
|
||||
constexpr iterator end() const noexcept
|
||||
{
|
||||
return _end;
|
||||
}
|
||||
|
||||
//=== reader ===//
|
||||
constexpr auto reader() const& noexcept
|
||||
{
|
||||
return _detail::range_reader<encoding, iterator>(_begin, _end);
|
||||
}
|
||||
|
||||
private:
|
||||
iterator _begin, _end;
|
||||
};
|
||||
|
||||
template <typename CharT>
|
||||
string_input(const CharT* begin, const CharT* end) -> string_input<deduce_encoding<CharT>>;
|
||||
template <typename CharT>
|
||||
string_input(const CharT* data, std::size_t size) -> string_input<deduce_encoding<CharT>>;
|
||||
template <typename View>
|
||||
string_input(const View&) -> string_input<deduce_encoding<_string_view_char_type<View>>>;
|
||||
|
||||
template <typename Encoding, typename CharT>
|
||||
constexpr string_input<Encoding> zstring_input(const CharT* str) noexcept
|
||||
{
|
||||
auto end = str;
|
||||
while (*end)
|
||||
++end;
|
||||
|
||||
return string_input<Encoding>(str, end);
|
||||
}
|
||||
template <typename CharT>
|
||||
constexpr auto zstring_input(const CharT* str) noexcept
|
||||
{
|
||||
return zstring_input<deduce_encoding<CharT>>(str);
|
||||
}
|
||||
|
||||
//=== convenience typedefs ===//
|
||||
template <typename Encoding = default_encoding>
|
||||
using string_lexeme = lexeme_for<string_input<Encoding>>;
|
||||
|
||||
template <typename Tag, typename Encoding = default_encoding>
|
||||
using string_error = error_for<string_input<Encoding>, Tag>;
|
||||
|
||||
template <typename Production, typename Encoding = default_encoding>
|
||||
using string_error_context = error_context<Production, string_input<Encoding>>;
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_INPUT_STRING_INPUT_HPP_INCLUDED
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_LEXEME_HPP_INCLUDED
|
||||
#define LEXY_LEXEME_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/assert.hpp>
|
||||
#include <lexy/_detail/config.hpp>
|
||||
#include <lexy/encoding.hpp>
|
||||
#include <lexy/input/base.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename Reader>
|
||||
class lexeme
|
||||
{
|
||||
static_assert(is_canonical_reader<Reader>, "lexeme must take the canonical reader");
|
||||
|
||||
public:
|
||||
using encoding = typename Reader::encoding;
|
||||
using char_type = typename encoding::char_type;
|
||||
using iterator = typename Reader::iterator;
|
||||
|
||||
constexpr lexeme() noexcept : _begin(), _end() {}
|
||||
constexpr lexeme(iterator begin, iterator end) noexcept : _begin(begin), _end(end) {}
|
||||
|
||||
constexpr explicit lexeme(const Reader& reader, iterator begin) noexcept
|
||||
: _begin(begin), _end(reader.cur())
|
||||
{}
|
||||
|
||||
constexpr bool empty() const noexcept
|
||||
{
|
||||
return _begin == _end;
|
||||
}
|
||||
|
||||
constexpr iterator begin() const noexcept
|
||||
{
|
||||
return _begin;
|
||||
}
|
||||
constexpr iterator end() const noexcept
|
||||
{
|
||||
return _end;
|
||||
}
|
||||
|
||||
constexpr const char_type* data() const noexcept
|
||||
{
|
||||
static_assert(std::is_pointer_v<iterator>);
|
||||
return _begin;
|
||||
}
|
||||
|
||||
constexpr std::size_t size() const noexcept
|
||||
{
|
||||
return static_cast<std::size_t>(_end - _begin);
|
||||
}
|
||||
|
||||
constexpr char_type operator[](std::size_t idx) const noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(idx < size());
|
||||
return _begin[idx];
|
||||
}
|
||||
|
||||
private:
|
||||
iterator _begin, _end;
|
||||
};
|
||||
|
||||
template <typename Reader>
|
||||
lexeme(const Reader&, typename Reader::iterator) -> lexeme<typename Reader::canonical_reader>;
|
||||
|
||||
template <typename Input>
|
||||
using lexeme_for = lexeme<input_reader<Input>>;
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_LEXEME_HPP_INCLUDED
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_MATCH_HPP_INCLUDED
|
||||
#define LEXY_MATCH_HPP_INCLUDED
|
||||
|
||||
#include <lexy/callback.hpp>
|
||||
#include <lexy/result.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct _match_handler
|
||||
{
|
||||
using result_type = lexy::result<void, void>;
|
||||
|
||||
template <typename SubProduction, typename Reader>
|
||||
constexpr auto sub_handler(const Reader&)
|
||||
{
|
||||
return _match_handler();
|
||||
}
|
||||
|
||||
constexpr auto list_sink()
|
||||
{
|
||||
return noop.sink();
|
||||
}
|
||||
|
||||
template <typename Reader, typename Error>
|
||||
constexpr auto error(const Reader&, Error&&) &&
|
||||
{
|
||||
return result_type();
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr auto value(Args&&...) &&
|
||||
{
|
||||
return result_type(result_value);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Input, typename Rule, typename = std::enable_if_t<is_rule<Rule>>>
|
||||
LEXY_FORCE_INLINE constexpr bool match(const Input& input, Rule)
|
||||
{
|
||||
auto reader = input.reader();
|
||||
|
||||
_match_handler handler;
|
||||
return !!Rule::template parser<final_parser>::parse(handler, reader);
|
||||
}
|
||||
|
||||
template <typename Production, typename Input>
|
||||
constexpr bool match(const Input& input)
|
||||
{
|
||||
return match(input, Production::rule);
|
||||
}
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_MATCH_HPP_INCLUDED
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_PARSE_HPP_INCLUDED
|
||||
#define LEXY_PARSE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/detect.hpp>
|
||||
#include <lexy/_detail/invoke.hpp>
|
||||
#include <lexy/callback.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/result.hpp>
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <auto Fn>
|
||||
struct _state;
|
||||
} // namespace lexyd
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename Production>
|
||||
using _production_value = decltype(Production::value);
|
||||
|
||||
struct _no_parse_state
|
||||
{};
|
||||
|
||||
template <typename Production, typename State, typename Input, typename Callback>
|
||||
class _parse_handler
|
||||
{
|
||||
static_assert(std::is_lvalue_reference_v<State>);
|
||||
|
||||
public:
|
||||
constexpr explicit _parse_handler(State state, const Input& input,
|
||||
const input_reader<Input>& reader, Callback callback) noexcept
|
||||
: _err_ctx(input, reader.cur()), _state(state), _callback(callback)
|
||||
{}
|
||||
|
||||
_parse_handler(const _parse_handler&) = delete;
|
||||
_parse_handler& operator=(const _parse_handler&) = delete;
|
||||
|
||||
static auto _result_value_cb()
|
||||
{
|
||||
if constexpr (_detail::is_detected<_production_value, Production>)
|
||||
return Production::value;
|
||||
else
|
||||
// If we don't have a Production::value callback, we must have only the list.
|
||||
// Then the list return type determines value.
|
||||
return Production::list.sink();
|
||||
}
|
||||
using result_type = result<typename decltype(_result_value_cb())::return_type,
|
||||
typename Callback::return_type>;
|
||||
|
||||
template <typename SubProduction>
|
||||
constexpr auto sub_handler(const input_reader<Input>& reader)
|
||||
{
|
||||
return _parse_handler<SubProduction, State, Input, Callback>(_state, _err_ctx.input(),
|
||||
reader, _callback);
|
||||
}
|
||||
|
||||
constexpr auto list_sink()
|
||||
{
|
||||
return Production::list.sink();
|
||||
}
|
||||
|
||||
template <typename Reader, typename Error>
|
||||
constexpr auto error(const Reader&, Error&& error) &&
|
||||
{
|
||||
return lexy::invoke_as_result<result_type>(lexy::result_error, _callback, _err_ctx,
|
||||
LEXY_FWD(error));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr auto value(Args&&... args) &&
|
||||
{
|
||||
if constexpr (!_detail::is_detected<_production_value, Production>)
|
||||
{
|
||||
// We don't have a value callback, which means we must have a list callback.
|
||||
// Use it to handle the arguments.
|
||||
|
||||
if constexpr (sizeof...(Args) == 0)
|
||||
// No arguments, build an empty list.
|
||||
return result_type(lexy::result_value, Production::list.sink().finish());
|
||||
else if constexpr (sizeof...(Args) == 1)
|
||||
// Single argument, return that one.
|
||||
return result_type(lexy::result_value, LEXY_FWD(args)...);
|
||||
else
|
||||
static_assert(_detail::error<Production, Args...>,
|
||||
"missing value callback for Production");
|
||||
}
|
||||
else
|
||||
// Pass the arguments to the value callback.
|
||||
return lexy::invoke_as_result<result_type>(lexy::result_value, Production::value,
|
||||
LEXY_FWD(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
// If we don't have a state, don't store a reference.
|
||||
using _state_storage_type
|
||||
= std::conditional_t<std::is_same_v<State, _no_parse_state&>, _no_parse_state, State>;
|
||||
|
||||
error_context<Production, Input> _err_ctx;
|
||||
LEXY_EMPTY_MEMBER _state_storage_type _state;
|
||||
LEXY_EMPTY_MEMBER Callback _callback;
|
||||
|
||||
template <auto Fn>
|
||||
friend struct lexyd::_state;
|
||||
};
|
||||
|
||||
/// Parses the production into a value, invoking the callback on error.
|
||||
template <typename Production, typename Input, typename State, typename Callback>
|
||||
constexpr auto parse(const Input& input, State&& state, Callback callback)
|
||||
{
|
||||
using rule = std::remove_const_t<decltype(Production::rule)>;
|
||||
// We make state an lvalue reference.
|
||||
using handler_t = _parse_handler<Production, State&, Input, Callback>;
|
||||
|
||||
auto reader = input.reader();
|
||||
|
||||
handler_t handler(state, input, reader, callback);
|
||||
return rule::template parser<final_parser>::parse(handler, reader);
|
||||
}
|
||||
|
||||
template <typename Production, typename Input, typename Callback>
|
||||
constexpr auto parse(const Input& input, Callback callback)
|
||||
{
|
||||
return parse<Production>(input, _no_parse_state{}, callback);
|
||||
}
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexyd
|
||||
{
|
||||
template <auto Fn>
|
||||
struct _state : rule_base
|
||||
{
|
||||
static constexpr auto has_matcher = false;
|
||||
|
||||
template <typename NextParser>
|
||||
struct parser
|
||||
{
|
||||
template <typename Production, typename State, typename Input, typename Callback,
|
||||
typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(lexy::_parse_handler<Production, State, Input, Callback>& handler,
|
||||
lexy::input_reader<Input>& reader, Args&&... args) ->
|
||||
typename lexy::_parse_handler<Production, State, Input, Callback>::result_type
|
||||
{
|
||||
static_assert(!std::is_same_v<State, lexy::_no_parse_state&>,
|
||||
"lexy::dsl::state requires passing a state to lexy::parse()");
|
||||
|
||||
if constexpr (std::is_same_v<decltype(Fn), decltype(nullptr)>)
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)..., handler._state);
|
||||
else
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...,
|
||||
lexy::_detail::invoke(Fn, handler._state));
|
||||
}
|
||||
|
||||
template <typename Handler, typename Reader, typename... Args>
|
||||
LEXY_DSL_FUNC auto parse(Handler& handler, Reader& reader, Args&&... args) ->
|
||||
typename Handler::result_type
|
||||
{
|
||||
// Not used with parse, ignore.
|
||||
return NextParser::parse(handler, reader, LEXY_FWD(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// Produces the parsing state passed to `parse()` as a value.
|
||||
constexpr auto parse_state = _state<nullptr>{};
|
||||
|
||||
/// Produces a member of the parsing state as a value.
|
||||
template <auto Fn>
|
||||
constexpr auto parse_state_member = _state<Fn>{};
|
||||
} // namespace lexyd
|
||||
|
||||
#endif // LEXY_PARSE_HPP_INCLUDED
|
||||
@@ -1,256 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_RESULT_HPP_INCLUDED
|
||||
#define LEXY_RESULT_HPP_INCLUDED
|
||||
|
||||
#include <lexy/_detail/assert.hpp>
|
||||
#include <lexy/_detail/config.hpp>
|
||||
#include <new>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct result_value_t
|
||||
{};
|
||||
|
||||
/// Tag value to indicate result success.
|
||||
constexpr auto result_value = result_value_t{};
|
||||
|
||||
struct result_error_t
|
||||
{};
|
||||
|
||||
/// Tag value to indiciate result error.
|
||||
constexpr auto result_error = result_error_t{};
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
struct nullopt;
|
||||
|
||||
template <typename T, typename E>
|
||||
struct _result_storage_trivial
|
||||
{
|
||||
using value_type = T;
|
||||
using error_type = E;
|
||||
|
||||
bool _has_value;
|
||||
union
|
||||
{
|
||||
T _value;
|
||||
E _error;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
constexpr _result_storage_trivial(result_value_t, Args&&... args)
|
||||
: _has_value(true), _value(LEXY_FWD(args)...)
|
||||
{}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr _result_storage_trivial(result_error_t, Args&&... args)
|
||||
: _has_value(false), _error(LEXY_FWD(args)...)
|
||||
{}
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
struct _result_storage_non_trivial
|
||||
{
|
||||
using value_type = T;
|
||||
using error_type = E;
|
||||
|
||||
bool _has_value;
|
||||
union
|
||||
{
|
||||
T _value;
|
||||
E _error;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
_result_storage_non_trivial(result_value_t, Args&&... args)
|
||||
: _has_value(true), _value(LEXY_FWD(args)...)
|
||||
{}
|
||||
template <typename... Args>
|
||||
_result_storage_non_trivial(result_error_t, Args&&... args)
|
||||
: _has_value(false), _error(LEXY_FWD(args)...)
|
||||
{}
|
||||
|
||||
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ == 9
|
||||
// GCC 9 crashes when trying to convert nullopt to a value here.
|
||||
|
||||
template <typename Nullopt,
|
||||
typename = std::enable_if_t<std::is_same_v<std::decay_t<Nullopt>, nullopt>>>
|
||||
_result_storage_non_trivial(result_value_t, Nullopt&&) : _has_value(true), _value()
|
||||
{}
|
||||
template <typename Nullopt,
|
||||
typename = std::enable_if_t<std::is_same_v<std::decay_t<Nullopt>, nullopt>>>
|
||||
_result_storage_non_trivial(result_error_t, Nullopt&&) : _has_value(false), _error()
|
||||
{}
|
||||
#endif
|
||||
|
||||
_result_storage_non_trivial(_result_storage_non_trivial&& other) noexcept
|
||||
: _has_value(other._has_value)
|
||||
{
|
||||
if (_has_value)
|
||||
::new (static_cast<void*>(&_value)) T(LEXY_MOV(other._value));
|
||||
else
|
||||
::new (static_cast<void*>(&_error)) E(LEXY_MOV(other._error));
|
||||
}
|
||||
|
||||
~_result_storage_non_trivial() noexcept
|
||||
{
|
||||
if (_has_value)
|
||||
_value.~T();
|
||||
else
|
||||
_error.~E();
|
||||
}
|
||||
|
||||
_result_storage_non_trivial& operator=(_result_storage_non_trivial&& other) noexcept
|
||||
{
|
||||
if (_has_value && other._has_value)
|
||||
{
|
||||
_value = LEXY_MOV(other._value);
|
||||
}
|
||||
else if (_has_value && !other._has_value)
|
||||
{
|
||||
_value.~T();
|
||||
::new (static_cast<void*>(&_error)) E(LEXY_MOV(other._error));
|
||||
_has_value = false;
|
||||
}
|
||||
else if (!_has_value && other._has_value)
|
||||
{
|
||||
_error.~E();
|
||||
::new (static_cast<void*>(&_value)) T(LEXY_MOV(other._value));
|
||||
_has_value = true;
|
||||
}
|
||||
else // !_has_value && !other._has_value
|
||||
{
|
||||
_error = LEXY_MOV(other._error);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
using _result_storage_impl
|
||||
= std::conditional_t<std::is_trivially_copyable_v<T> && std::is_trivially_copyable_v<E>,
|
||||
_result_storage_trivial<T, E>, _result_storage_non_trivial<T, E>>;
|
||||
template <typename T, typename E>
|
||||
using _result_storage
|
||||
= _result_storage_impl<std::conditional_t<std::is_void_v<T>, result_value_t, T>,
|
||||
std::conditional_t<std::is_void_v<E>, result_error_t, E>>;
|
||||
} // namespace lexy
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
/// Stores a T or an E.
|
||||
/// Supports `void` for either one of them meaning "none".
|
||||
template <typename T, typename E>
|
||||
class result : _result_storage<T, E>
|
||||
{
|
||||
static constexpr auto optional_tag = [] {
|
||||
if constexpr (std::is_void_v<T>)
|
||||
return result_error;
|
||||
else
|
||||
return result_value;
|
||||
}();
|
||||
|
||||
public:
|
||||
using value_type = typename _result_storage<T, E>::value_type;
|
||||
using error_type = typename _result_storage<T, E>::error_type;
|
||||
|
||||
//=== constructor ===//
|
||||
constexpr result() : _result_storage<T, E>(result_error) {}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr result(result_value_t, Args&&... args)
|
||||
: _result_storage<T, E>(result_value, LEXY_FWD(args)...)
|
||||
{}
|
||||
template <typename... Args>
|
||||
constexpr result(result_error_t, Args&&... args)
|
||||
: _result_storage<T, E>(result_error, LEXY_FWD(args)...)
|
||||
{}
|
||||
|
||||
/// Conversion from an errored result with a different value type.
|
||||
template <typename U>
|
||||
constexpr explicit result(const result<U, E>& other) : result(result_error, other.error())
|
||||
{}
|
||||
template <typename U>
|
||||
constexpr explicit result(result<U, E>&& other) : result(result_error, LEXY_MOV(other).error())
|
||||
{}
|
||||
|
||||
/// Construct a value without tag if we don't have an error.
|
||||
template <typename Arg, typename = std::enable_if_t<
|
||||
(std::is_constructible_v<T, Arg> || std::is_constructible_v<E, Arg>)
|
||||
|| (std::is_void_v<T> || std::is_void_v<E>)>>
|
||||
constexpr explicit result(Arg&& arg) : result(optional_tag, LEXY_FWD(arg))
|
||||
{}
|
||||
|
||||
//=== access ===//
|
||||
constexpr explicit operator bool() const noexcept
|
||||
{
|
||||
return this->_has_value;
|
||||
}
|
||||
constexpr bool has_value() const noexcept
|
||||
{
|
||||
return this->_has_value;
|
||||
}
|
||||
constexpr bool has_error() const noexcept
|
||||
{
|
||||
return !this->_has_value;
|
||||
}
|
||||
|
||||
static constexpr bool has_void_value() noexcept
|
||||
{
|
||||
return std::is_same_v<T, void>;
|
||||
}
|
||||
static constexpr bool has_void_error() noexcept
|
||||
{
|
||||
return std::is_same_v<E, void>;
|
||||
}
|
||||
|
||||
constexpr value_type& value() & noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(has_value());
|
||||
return this->_value;
|
||||
}
|
||||
constexpr const value_type& value() const& noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(has_value());
|
||||
return this->_value;
|
||||
}
|
||||
constexpr value_type&& value() && noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(has_value());
|
||||
return LEXY_MOV(this->_value);
|
||||
}
|
||||
constexpr const value_type&& value() const&& noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(has_value());
|
||||
return LEXY_MOV(this->_value);
|
||||
}
|
||||
|
||||
constexpr error_type& error() & noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(has_error());
|
||||
return this->_error;
|
||||
}
|
||||
constexpr const error_type& error() const& noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(has_error());
|
||||
return this->_error;
|
||||
}
|
||||
constexpr error_type&& error() && noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(has_error());
|
||||
return LEXY_MOV(this->_error);
|
||||
}
|
||||
constexpr const error_type&& error() const&& noexcept
|
||||
{
|
||||
LEXY_PRECONDITION(has_error());
|
||||
return LEXY_MOV(this->_error);
|
||||
}
|
||||
};
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_RESULT_HPP_INCLUDED
|
||||
@@ -1,69 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef LEXY_VALIDATE_HPP_INCLUDED
|
||||
#define LEXY_VALIDATE_HPP_INCLUDED
|
||||
|
||||
#include <lexy/callback.hpp>
|
||||
#include <lexy/dsl/base.hpp>
|
||||
#include <lexy/result.hpp>
|
||||
|
||||
namespace lexy
|
||||
{
|
||||
template <typename Production, typename Input, typename Callback>
|
||||
class _validate_handler
|
||||
{
|
||||
public:
|
||||
constexpr explicit _validate_handler(const Input& input, const input_reader<Input>& reader,
|
||||
Callback cb)
|
||||
: _err_ctx(input, reader.cur()), _callback(cb)
|
||||
{}
|
||||
|
||||
using result_type = result<void, typename Callback::return_type>;
|
||||
|
||||
template <typename SubProduction>
|
||||
constexpr auto sub_handler(const input_reader<Input>& reader)
|
||||
{
|
||||
return _validate_handler<SubProduction, Input, Callback>(_err_ctx.input(), reader,
|
||||
_callback);
|
||||
}
|
||||
|
||||
constexpr auto list_sink()
|
||||
{
|
||||
return noop.sink();
|
||||
}
|
||||
|
||||
template <typename Reader, typename Error>
|
||||
constexpr auto error(const Reader&, Error&& error) &&
|
||||
{
|
||||
return lexy::invoke_as_result<result_type>(lexy::result_error, _callback, _err_ctx,
|
||||
LEXY_FWD(error));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr auto value(Args&&...) &&
|
||||
{
|
||||
return result_type(lexy::result_value);
|
||||
}
|
||||
|
||||
private:
|
||||
error_context<Production, Input> _err_ctx;
|
||||
LEXY_EMPTY_MEMBER Callback _callback;
|
||||
};
|
||||
|
||||
template <typename Production, typename Input, typename Callback>
|
||||
constexpr auto validate(const Input& input, Callback callback)
|
||||
{
|
||||
using rule = std::remove_const_t<decltype(Production::rule)>;
|
||||
using handler_t = _validate_handler<Production, Input, Callback>;
|
||||
|
||||
auto reader = input.reader();
|
||||
|
||||
handler_t handler(input, reader, callback);
|
||||
return rule::template parser<final_parser>::parse(handler, reader);
|
||||
}
|
||||
} // namespace lexy
|
||||
|
||||
#endif // LEXY_VALIDATE_HPP_INCLUDED
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,111 +0,0 @@
|
||||
// Copyright (C) 2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef EXAMPLE_REPORT_ERROR_HPP_INCLUDED
|
||||
#define EXAMPLE_REPORT_ERROR_HPP_INCLUDED
|
||||
|
||||
#include <cstdio>
|
||||
#include <lexy/callback.hpp>
|
||||
#include <lexy/dsl/ascii.hpp>
|
||||
#include <lexy/dsl/newline.hpp>
|
||||
#include <lexy/error.hpp>
|
||||
#include <lexy/error_location.hpp>
|
||||
|
||||
namespace lexy_ex
|
||||
{
|
||||
template <typename Reader>
|
||||
void _print_location(const lexy::error_location<Reader>& location)
|
||||
{
|
||||
std::fputs(" | \n", stderr);
|
||||
|
||||
std::fprintf(stderr, "%2zd:%2zd| ", location.line, location.column);
|
||||
for (auto c : location.context)
|
||||
std::fputc(c, stderr);
|
||||
std::fputc('\n', stderr);
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
void _print_message_indent(const lexy::error_location<Reader>& location)
|
||||
{
|
||||
std::fputs(" | ", stderr);
|
||||
for (auto i = 0u; i != location.column - 1; ++i)
|
||||
std::fputc(' ', stderr);
|
||||
|
||||
// The next character printed will be under the error location of the column above.
|
||||
}
|
||||
|
||||
// Print a generic error.
|
||||
template <typename Reader, typename Tag>
|
||||
void _print_message(const lexy::error_location<Reader>& location, const lexy::error<Reader, Tag>& e)
|
||||
{
|
||||
if (e.begin() == e.end())
|
||||
std::fputc('^', stderr);
|
||||
else
|
||||
{
|
||||
for (auto cur = e.begin(); cur != e.end(); ++cur)
|
||||
{
|
||||
if (cur == location.context.end())
|
||||
break; // More than one line.
|
||||
std::fputc('^', stderr);
|
||||
}
|
||||
}
|
||||
|
||||
std::fprintf(stderr, " %.*s", int(e.message().size()), e.message().data());
|
||||
}
|
||||
|
||||
// Print an expected_literal error.
|
||||
template <typename Reader>
|
||||
void _print_message(const lexy::error_location<Reader>&,
|
||||
const lexy::error<Reader, lexy::expected_literal>& e)
|
||||
{
|
||||
std::fprintf(stderr, "^ expected '%.*s'", int(e.string().size()),
|
||||
reinterpret_cast<const char*>(e.string().data()));
|
||||
}
|
||||
|
||||
// Print an expected_char_class error.
|
||||
template <typename Reader>
|
||||
void _print_message(const lexy::error_location<Reader>&,
|
||||
const lexy::error<Reader, lexy::expected_char_class>& e)
|
||||
{
|
||||
std::fprintf(stderr, "^ expected '%.*s' character", int(e.character_class().size()),
|
||||
e.character_class().data());
|
||||
}
|
||||
|
||||
// The error callback that prints to stderr.
|
||||
constexpr auto report_error = lexy::callback([](const auto& context, const auto& error) {
|
||||
// Convert the context location and error location into line/column information.
|
||||
auto context_location
|
||||
= lexy::make_error_location(context.input(), context.position(),
|
||||
lexy::dsl::ascii::character, lexy::dsl::newline);
|
||||
auto location = lexy::make_error_location(context.input(), error.position(),
|
||||
lexy::dsl::ascii::character, lexy::dsl::newline);
|
||||
|
||||
// Print the main error headline.
|
||||
auto prod_name = context.production();
|
||||
std::fflush(stdout);
|
||||
std::fprintf(stderr, "error: while parsing %.*s\n", int(prod_name.size()), prod_name.data());
|
||||
|
||||
if (location.line != context_location.line)
|
||||
{
|
||||
_print_location(context_location);
|
||||
_print_message_indent(context_location);
|
||||
std::fputs("^ beginning here\n", stderr);
|
||||
_print_location(location);
|
||||
_print_message_indent(location);
|
||||
}
|
||||
else
|
||||
{
|
||||
_print_location(location);
|
||||
_print_message_indent(context_location);
|
||||
for (auto i = context_location.column; i != location.column; ++i)
|
||||
std::fputc('~', stderr);
|
||||
}
|
||||
|
||||
_print_message(location, error);
|
||||
std::putc('\n', stderr);
|
||||
});
|
||||
} // namespace lexy_ex
|
||||
|
||||
#endif // EXAMPLE_REPORT_ERROR_HPP_INCLUDED
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#usda 1.0
|
||||
(
|
||||
upAxis = "Z"
|
||||
"This is note"
|
||||
)
|
||||
|
||||
def Xform "Light" {
|
||||
uniform token bas:is = "bspline"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
```
|
||||
color3f[] primvars:displayColor = [(1, 1, 1)] (
|
||||
customData = {
|
||||
dictionary Maya = {
|
||||
bool generated = 1
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
* [ ] Multi-threaded parsing of array values(e.g. `normal3f` array)
|
||||
@@ -3781,6 +3781,7 @@ bool CrateReader::ReadTOC() {
|
||||
}
|
||||
}
|
||||
|
||||
DCOUT("TOC read success");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -601,11 +601,21 @@ bool LoadUSDAFromMemory(const uint8_t *addr, const size_t length, const std::str
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// TODO: Reconstruct Scene
|
||||
if (err) {
|
||||
(*err) += "USDA parsing success, but reconstructing Scene is TODO.\n";
|
||||
|
||||
{
|
||||
bool ret = reader.ReconstructStage();
|
||||
if (!ret) {
|
||||
if (err) {
|
||||
(*err) += "Failed to reconstruct Stage from USDA:\n";
|
||||
(*err) += reader.GetError() + "\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
(*stage) = reader.GetStage();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadUSDAFromFile(const std::string &_filename, Stage *stage,
|
||||
|
||||
@@ -1811,9 +1811,11 @@ bool USDCReader::Impl::ReadUSDC() {
|
||||
|
||||
// TODO(syoyo): Read unknown sections
|
||||
|
||||
|
||||
///
|
||||
/// Reconstruct C++ representation of USD scene graph.
|
||||
///
|
||||
DCOUT("BuildLiveFieldSets\n");
|
||||
if (!crate_reader->BuildLiveFieldSets()) {
|
||||
_warn = crate_reader->GetWarning();
|
||||
_err = crate_reader->GetError();
|
||||
@@ -1821,8 +1823,10 @@ bool USDCReader::Impl::ReadUSDC() {
|
||||
return false;
|
||||
}
|
||||
|
||||
DCOUT("Read Crate.\n");
|
||||
|
||||
// TODO
|
||||
return false;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
BIN
tests/usdc/usdskel-001.usdc
Normal file
BIN
tests/usdc/usdskel-001.usdc
Normal file
Binary file not shown.
Reference in New Issue
Block a user