// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com) // This file is part of the "Nazara Engine - Core module" // For conditions of distribution and use, see copyright notice in Export.hpp #include #include #include #include #include #include #include namespace Nz { inline ParameterFile::ParameterFile(Stream& stream) : m_bufferOffset(0), m_stream(stream), m_currentLine(1) { } template void ParameterFile::Parse(Args&&... args) { HandleInner(std::forward(args)...); } template void ParameterFile::HandleInner(Args&&... args) { using Types = TypeListTransform, std::decay>; constexpr bool IsArray = TypeListHas; if constexpr (IsArray) { ValueHandler handler = GetSingleHandler(std::forward(args)...); for (;;) { if (std::holds_alternative(PeekToken())) break; handler(*this); } } else { FixedVector keys; BuildKeyValues(&keys, std::forward(args)...); for (;;) { if (std::holds_alternative(PeekToken())) break; Identifier nextIdentifier = Read(); auto it = std::find_if(keys.begin(), keys.end(), [&](const KeyValue& keyValue) { return keyValue.key == nextIdentifier.value; }); if (it == keys.end()) throw std::runtime_error(Format("unexpected keyword \"{}\"", nextIdentifier.value)); const ValueHandler& handler = it->handler; handler(*this); } } } template auto ParameterFile::Peek() -> T& { Token& token = PeekToken(); if (!std::holds_alternative(token)) throw std::runtime_error(Format("expected {} on line {}", TypeName(), m_currentLine)); return std::get(token); } template auto ParameterFile::Read() -> T { Token token = std::move(m_nextToken); if (!std::holds_alternative(token)) throw std::runtime_error(Format("expected {} on line {}", TypeName(), m_currentLine)); Advance(); return std::get(token); } template T ParameterFile::ReadValue() { if constexpr (std::is_same_v) return Read().value; else if constexpr (std::is_same_v) return Read(); else if constexpr (std::is_same_v) return ParameterFileSection{ *this }; else static_assert(AlwaysFalse(), "unsupported type"); } template void ParameterFile::BuildKeyValues(FixedVector* keyValues, K&& key, Rest&&... rest) { if constexpr (ShouldIgnore) return BuildKeyValues(keyValues, std::forward(rest)...); else { assert(keyValues); auto& keyValue = keyValues->emplace_back(); keyValue.key = BuildBlockKey(std::forward(key)); keyValue.handler = BuildBlockHandler(keyValues, std::forward(rest)...); } } template auto ParameterFile::GetSingleHandler(V&& value, Rest&&... rest) -> ValueHandler { if constexpr (ShouldIgnore) { static_assert(sizeof...(Rest) > 0, "expected a handler"); return GetSingleHandler(std::forward(rest)...); } else { static_assert(sizeof...(Rest) == 0, "expected a single handler"); return BuildBlockHandler(static_cast*>(nullptr), std::forward(value)); } } template auto ParameterFile::BuildBlockHandler(FixedVector* keyValues, T* value, Rest&&... rest) -> ValueHandler { ValueHandler valueHandler = [value](ParameterFile& file) { *value = file.ReadValue(); }; if constexpr (sizeof...(Rest) > 0) BuildKeyValues(keyValues, std::forward(rest)...); return valueHandler; } template auto ParameterFile::BuildBlockHandler(FixedVector* keyValues, T&& handler, Rest&&... rest) -> ValueHandler { using FunctionType = typename FunctionTraits::FuncType; return BuildBlockHandler(keyValues, FunctionRef(handler), std::forward(rest)...); } template auto ParameterFile::BuildBlockHandler(FixedVector* keyValues, void(O::* method)(Args...), O* object, Rest&&... rest) -> ValueHandler { ValueHandler valueHandler = [object, method](ParameterFile& file) { std::tuple args{ object, file.ReadValue()... }; std::apply(method, std::move(args)); }; if constexpr (sizeof...(Rest) > 0) BuildKeyValues(keyValues, std::forward(rest)...); return valueHandler; } template auto ParameterFile::BuildBlockHandler(FixedVector* keyValues, FunctionRef handler, Rest&&... rest) -> ValueHandler { ValueHandler valueHandler = [handler](ParameterFile& file) { std::tuple args{ file.ReadValue()... }; std::apply(handler, std::move(args)); }; if constexpr (sizeof...(Rest) > 0) BuildKeyValues(keyValues, std::forward(rest)...); return valueHandler; } template std::string_view ParameterFile::BuildBlockKey(T&& key) { static_assert(std::is_constructible_v, "parameter key must be convertible to a std::string_view"); return std::string_view(std::forward(key)); } inline ParameterFileSection::ParameterFileSection(ParameterFile& file) : m_file(file) { } template void ParameterFileSection::Block(Args&&... args) { using Types = TypeListTransform, std::decay>; constexpr bool IsOptionalBlock = TypeListHas; if constexpr (IsOptionalBlock) { if (!std::holds_alternative(m_file.PeekToken())) return; m_file.Advance(); } else { if (!std::holds_alternative(m_file.Advance())) throw std::runtime_error(Format("expected OpenCurlyBracket on line {}", m_file.m_currentLine)); } m_file.HandleInner(std::forward(args)...); if (!std::holds_alternative(m_file.Advance())) throw std::runtime_error(Format("expected ClosingCurlyBracket on line {}", m_file.m_currentLine)); } }