sandbox/usda -> examples/usda_parser

This commit is contained in:
Syoyo Fujita
2022-08-29 19:03:08 +09:00
parent b7b530d081
commit 18b4242317
88 changed files with 21 additions and 13005 deletions

View File

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

View File

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

View File

@@ -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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,9 +0,0 @@
#usda 1.0
(
upAxis = "Z"
"This is note"
)
def Xform "Light" {
uniform token bas:is = "bspline"
}

View File

@@ -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)

View File

@@ -3781,6 +3781,7 @@ bool CrateReader::ReadTOC() {
}
}
DCOUT("TOC read success");
return true;
}

View File

@@ -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,

View File

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

Binary file not shown.