Compare commits
10 Commits
f29e2ba235
...
bf29cc40e3
| Author | SHA1 | Date | |
|---|---|---|---|
| bf29cc40e3 | |||
| c70b5c8f08 | |||
| f2d912b4eb | |||
| ffe69f08b5 | |||
| 41fd2a83ee | |||
| ebf6cea353 | |||
| f5a999d20a | |||
| 8b740dfe8e | |||
| 1e1f2d7816 | |||
| e3e188a24c |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build/
|
||||
65
CMakeLists.txt
Normal file
65
CMakeLists.txt
Normal file
@@ -0,0 +1,65 @@
|
||||
cmake_minimum_required(VERSION 3.14.4)
|
||||
|
||||
message(STATUS "Using CMake ${CMAKE_VERSION}")
|
||||
|
||||
include(cmake/add_files.cmake)
|
||||
include(cmake/create_source_groups.cmake)
|
||||
|
||||
# Project ----------------------------------------------------------------------
|
||||
|
||||
project(DotaFactory)
|
||||
|
||||
set(CMAKE_BUILD_TYPE_INIT "Release")
|
||||
|
||||
|
||||
# Qt ---------------------------------------------------------------------------
|
||||
|
||||
find_package(Qt5 COMPONENTS Widgets Network Multimedia Charts REQUIRED)
|
||||
|
||||
if(Qt5Widgets_FOUND)
|
||||
message(STATUS "Found Qt ${Qt5Widgets_VERSION_STRING}")
|
||||
|
||||
# FIX: Qt was built with -reduce-relocations
|
||||
if (Qt5_POSITION_INDEPENDENT_CODE)
|
||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Setup ---------------------------------------------------------------------------
|
||||
|
||||
get_filename_component(QT_BINARY_DIR "${QT_MOC_EXECUTABLE}" PATH)
|
||||
set(QT_PLUGINS_DIR "${QT_BINARY_DIR}/../plugins")
|
||||
|
||||
function(COPY_QT_BINARIES TARGET_DIR IS_DEBUG)
|
||||
set(SUFFIX "")
|
||||
if (IS_DEBUG)
|
||||
set(SUFFIX "d")
|
||||
endif()
|
||||
|
||||
set(CONFIGURATION "Release")
|
||||
if (IS_DEBUG)
|
||||
set(CONFIGURATION "Debug")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CL_64)
|
||||
set(BITS "64")
|
||||
else()
|
||||
set(BITS "32")
|
||||
endif()
|
||||
|
||||
configure_file("${QT_BINARY_DIR}/Qt5Charts${SUFFIX}.dll" "${TARGET_DIR}/Qt5Charts${SUFFIX}.dll" COPYONLY)
|
||||
configure_file("${QT_BINARY_DIR}/Qt5Core${SUFFIX}.dll" "${TARGET_DIR}/Qt5Core${SUFFIX}.dll" COPYONLY)
|
||||
configure_file("${QT_BINARY_DIR}/Qt5Gui${SUFFIX}.dll" "${TARGET_DIR}/Qt5Gui${SUFFIX}.dll" COPYONLY)
|
||||
configure_file("${QT_BINARY_DIR}/Qt5Network${SUFFIX}.dll" "${TARGET_DIR}/Qt5Network${SUFFIX}.dll" COPYONLY)
|
||||
configure_file("${QT_BINARY_DIR}/Qt5Widgets${SUFFIX}.dll" "${TARGET_DIR}/Qt5Widgets${SUFFIX}.dll" COPYONLY)
|
||||
configure_file("${QT_BINARY_DIR}/Qt5Multimedia${SUFFIX}.dll" "${TARGET_DIR}/Qt5Multimedia${SUFFIX}.dll" COPYONLY)
|
||||
endfunction(COPY_QT_BINARIES)
|
||||
|
||||
|
||||
# Src ----------------------------------------------------------------------
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
@@ -19,6 +19,13 @@ inputs = [{item = "iron_ore", amount = 2}]
|
||||
outputs = [{item = "iron_ingot", amount = 1}]
|
||||
duration_seconds = 2.0
|
||||
|
||||
[[recipe]]
|
||||
id = "copper_ingot"
|
||||
building = "smelter"
|
||||
inputs = [{item = "copper_ore", amount = 2}]
|
||||
outputs = [{item = "copper_ingot", amount = 1}]
|
||||
duration_seconds = 2.5
|
||||
|
||||
[[recipe]]
|
||||
id = "circuit_board"
|
||||
building = "assembler"
|
||||
|
||||
184
bin/config/visuals.toml
Normal file
184
bin/config/visuals.toml
Normal file
@@ -0,0 +1,184 @@
|
||||
# visuals.toml
|
||||
#
|
||||
# Rendering-only configuration, loaded once at startup by the UI.
|
||||
# The simulation does not read this file.
|
||||
#
|
||||
# Every BuildingType, ItemType, ship role, and station type must have an
|
||||
# entry here; missing or malformed entries abort startup (see architecture.md
|
||||
# "Visual Parameters").
|
||||
#
|
||||
# Colors are "#rrggbb" or "#rrggbbaa" (alpha optional, last byte).
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Tile backgrounds
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[tiles.asteroid]
|
||||
fill = "#4a4038"
|
||||
|
||||
[tiles.space]
|
||||
fill = "#0a0a15"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Buildings
|
||||
#
|
||||
# One section per BuildingType enum value. `fill` colors every footprint tile
|
||||
# of the building; `outline` is drawn around the building's bounding box;
|
||||
# `glyph` is a short label drawn centered on the building.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[buildings.hq]
|
||||
fill = "#2e5fb8"
|
||||
outline = "#ffffff"
|
||||
glyph = "HQ"
|
||||
|
||||
[buildings.miner]
|
||||
fill = "#6b4a2c"
|
||||
outline = "#ffffff"
|
||||
glyph = "M"
|
||||
|
||||
[buildings.smelter]
|
||||
fill = "#b85a1e"
|
||||
outline = "#ffffff"
|
||||
glyph = "Sm"
|
||||
|
||||
[buildings.assembler]
|
||||
fill = "#3a6fa8"
|
||||
outline = "#ffffff"
|
||||
glyph = "A"
|
||||
|
||||
[buildings.reprocessing_plant]
|
||||
fill = "#6a3a8a"
|
||||
outline = "#ffffff"
|
||||
glyph = "R"
|
||||
|
||||
[buildings.shipyard]
|
||||
fill = "#385870"
|
||||
outline = "#ffffff"
|
||||
glyph = "Y"
|
||||
|
||||
[buildings.salvage_bay]
|
||||
fill = "#b8a23a"
|
||||
outline = "#ffffff"
|
||||
glyph = "Sb"
|
||||
|
||||
[buildings.belt]
|
||||
fill = "#5a5a5a"
|
||||
outline = "#7a7a7a"
|
||||
glyph = ""
|
||||
|
||||
[buildings.splitter]
|
||||
fill = "#7a7a5a"
|
||||
outline = "#9a9a7a"
|
||||
glyph = ""
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Stations
|
||||
#
|
||||
# Player and enemy defence stations have their own entries rather than going
|
||||
# through [buildings.*] so the two sides can be color-coded at a glance.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[stations.player]
|
||||
fill = "#2e5fb8"
|
||||
outline = "#ffffff"
|
||||
glyph = "P"
|
||||
|
||||
[stations.enemy]
|
||||
fill = "#b82e2e"
|
||||
outline = "#ffffff"
|
||||
glyph = "E"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Items
|
||||
#
|
||||
# Items on belts are 10x10 squares. `fill` is the square color; `outline` is
|
||||
# drawn around it. One section per ItemType.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[items.iron_ore]
|
||||
fill = "#8a5a4a"
|
||||
outline = "#201010"
|
||||
|
||||
[items.copper_ore]
|
||||
fill = "#c47a3a"
|
||||
outline = "#3a1a0a"
|
||||
|
||||
[items.iron_ingot]
|
||||
fill = "#b0b0b8"
|
||||
outline = "#202028"
|
||||
|
||||
[items.copper_ingot]
|
||||
fill = "#d48a4a"
|
||||
outline = "#402010"
|
||||
|
||||
[items.circuit_board]
|
||||
fill = "#2ea35a"
|
||||
outline = "#0a2a14"
|
||||
|
||||
[items.advanced_alloy]
|
||||
fill = "#a06acc"
|
||||
outline = "#201030"
|
||||
|
||||
[items.building_block]
|
||||
fill = "#c8b070"
|
||||
outline = "#302810"
|
||||
|
||||
[items.scrap]
|
||||
fill = "#7a7268"
|
||||
outline = "#201a14"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Ships
|
||||
#
|
||||
# Ships are drawn as oriented triangles/arrows. Color is keyed to role, not
|
||||
# blueprint (architecture.md, "Layer Order").
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[ships.player_combat]
|
||||
fill = "#3366ff"
|
||||
outline = "#ffffff"
|
||||
|
||||
[ships.salvage]
|
||||
fill = "#33cc66"
|
||||
outline = "#ffffff"
|
||||
|
||||
[ships.repair]
|
||||
fill = "#66ccff"
|
||||
outline = "#ffffff"
|
||||
|
||||
[ships.enemy]
|
||||
fill = "#cc3333"
|
||||
outline = "#ffffff"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Laser beams (REQ-SHP-FIRING-BEAM)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[beams]
|
||||
color = "#ff6600"
|
||||
width_px = 2
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build / demolish / selection overlays
|
||||
#
|
||||
# All overlay colors carry an alpha channel so they composite over the
|
||||
# underlying scene.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[overlays]
|
||||
ghost_valid = "#ffffff44" # builder-mode ghost, placement allowed (REQ-BLD-GHOST)
|
||||
ghost_invalid = "#ff000044" # builder-mode ghost, placement invalid (REQ-BLD-PLACE-VALID)
|
||||
demolish_tint = "#ff000033" # demolish-mode hover tint
|
||||
selection_rect = "#00ff00" # box-drag selection rectangle (REQ-UI-MULTI-SELECT)
|
||||
tile_highlight = "#ffffff22" # tile under cursor
|
||||
selected_outline = "#ffff00" # outline drawn around currently-selected building(s)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Blueprint-drop toasts (REQ-UI-BLUEPRINT-TOAST)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[toast]
|
||||
bg = "#000000cc"
|
||||
fg = "#ffffff"
|
||||
font_size = 14
|
||||
@@ -1,7 +1,9 @@
|
||||
[world]
|
||||
height_tiles = 60
|
||||
refund_percentage = 75
|
||||
starting_building_blocks = 100
|
||||
scrap_despawn_seconds = 30
|
||||
belt_speed_tiles_per_second = 2
|
||||
|
||||
[regions]
|
||||
asteroid_width = 40
|
||||
|
||||
33
cmake/add_files.cmake
Normal file
33
cmake/add_files.cmake
Normal file
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# Defines a list of files with a given name, prepends the relative path to the
|
||||
# source dir to each file if available and passes the list to PARENT_SCOPE.
|
||||
#
|
||||
# Usage:
|
||||
# add_files(
|
||||
# NAME_OF_FILES
|
||||
# file1.cpp
|
||||
# file2.cpp
|
||||
# ...
|
||||
# )
|
||||
#
|
||||
macro(add_files var_name)
|
||||
if(DEFINED SRC_DIR)
|
||||
file (RELATIVE_PATH _relPath "${SRC_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
else()
|
||||
file (RELATIVE_PATH _relPath "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
endif()
|
||||
|
||||
foreach (_src ${ARGN})
|
||||
if (_relPath)
|
||||
list (APPEND ${var_name} "${_relPath}/${_src}")
|
||||
else()
|
||||
list (APPEND ${var_name} "${_src}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (_relPath)
|
||||
set(${var_name} ${${var_name}} PARENT_SCOPE)
|
||||
else()
|
||||
set(${var_name} ${${var_name}})
|
||||
endif()
|
||||
endmacro()
|
||||
12
cmake/create_source_groups.cmake
Normal file
12
cmake/create_source_groups.cmake
Normal file
@@ -0,0 +1,12 @@
|
||||
macro(create_source_groups)
|
||||
foreach(FILE ${ARGN})
|
||||
get_filename_component(GROUP "${FILE}" PATH)
|
||||
# changes /'s to \\'s
|
||||
string(REPLACE "/" "\\" GROUP "${GROUP}")
|
||||
|
||||
string(LENGTH "${GROUP}" GROUP_NAME_LENGTH)
|
||||
if (${GROUP_NAME_LENGTH} GREATER 0)
|
||||
source_group("${GROUP}" FILES "${FILE}")
|
||||
endif()
|
||||
endforeach()
|
||||
endmacro()
|
||||
@@ -36,6 +36,7 @@ Config files (`world.toml`, `buildings.toml`, `recipes.toml`, `ships.toml`, `sta
|
||||
- `toml++` parses the files into strongly-typed config structs.
|
||||
- Formula strings (e.g., threat accumulation, enemy ship level as a function of `t`, per-ship stats as functions of `level`, station stats as functions of generation) are compiled once via `tinyexpr` at load time and stored as callable objects. They are never re-parsed during simulation.
|
||||
- Configs are immutable after load. Any formula that fails to parse, or any required field that is missing or malformed, aborts startup with a clear error message — never mid-game.
|
||||
- The UI layer loads its own TOML file, `visuals.toml`, using the same toml++-based pattern and the same immutability / fail-fast rule (see Rendering → Visual Parameters). The simulation never reads it.
|
||||
|
||||
## Coordinate System
|
||||
|
||||
@@ -53,7 +54,7 @@ Simulation types shared across subsystems:
|
||||
|
||||
- `EntityId` — strictly increasing integer handle, allocated centrally by the simulation. Assigned to every targetable entity: ships, scrap drops, **and** buildings (including HQ and defence stations). Buildings additionally retain their anchor tile for spatial lookups and placement; the `EntityId` is the canonical reference used by ship-component target fields (`Weapon.currentTarget`, `RepairTool.currentTarget`, `ThreatResponse.currentTarget`, etc.), so a combat ship can target either another ship or a defence station uniformly.
|
||||
- `Rotation` — enum `{ North, East, South, West }`. The rotation applied to a building's surface_mask when placed.
|
||||
- `BuildingType` — enum covering every building type in requirements.md (Miner, Smelter, Assembler, ReprocessingPlant, Shipyard, SalvageBay, Belt, Splitter, Hq, PlayerDefenceStation, EnemyDefenceStation).
|
||||
- `BuildingType` — enum covering every building type in requirements.md (Miner, Smelter, Assembler, ReprocessingPlant, Shipyard, SalvageBay, Belt, Splitter, Hq, PlayerDefenceStation, EnemyDefenceStation). `Belt` and `Splitter` share the enum for cost, construction, placement, and `visuals.toml` lookup, but their runtime data lives inside the belt subsystem rather than in `Building` instances (see Belt Subsystem).
|
||||
- `ItemType` — tagged id of every transportable material (ores, ingots, intermediates, building_blocks, scrap).
|
||||
- `Item` — `struct Item { ItemType type; }`. Items on belts have no persistent identity across ticks.
|
||||
- `Port` — `struct Port { QPoint tile; Rotation direction; }`. Identifies a belt-adjacent cell and the direction of flow across that cell.
|
||||
|
||||
410
docs/plan.md
Normal file
410
docs/plan.md
Normal file
@@ -0,0 +1,410 @@
|
||||
# Implementation Plan — Steps 4 through 8
|
||||
|
||||
Cross-references: `architecture.md` (design), `requirements.md` (REQ-* citations).
|
||||
|
||||
## Status
|
||||
|
||||
| Step | Scope | State |
|
||||
|------|-------|-------|
|
||||
| 1 | Config loading (Formula, ConfigLoader, all config structs) | ✅ done |
|
||||
| 2 | Simulation shell + TickDriver + entity id allocator + event queues | ✅ done |
|
||||
| 3 | Belt subsystem (placement, port interface, per-tile v1, splitter routing, clearTiles, visual iteration) | ✅ done |
|
||||
| 4 | Buildings + placement + belt↔building transport | ✅ done |
|
||||
| 5 | Scrap + ships skeleton (data + spawning, no AI) | ⬜ next |
|
||||
| 6 | Ship behavior systems + movement arbitration | ⬜ |
|
||||
| 7 | Waves, threat accumulation, combat resolution, deaths & loot | ⬜ |
|
||||
| 8 | UI layer (GameWorldView, visuals.toml, panels, build/demolish, speed controls) | ⬜ |
|
||||
|
||||
Tick order reference (architecture.md §Tick Order):
|
||||
1. Wave scheduler — step 7
|
||||
2. Threat accumulation — step 7
|
||||
3. Belt→building pull — step 4
|
||||
4. Building production — step 4
|
||||
5. Building→belt push — step 4
|
||||
6. Belt tick — step 3 ✅
|
||||
7. Ship behavior systems — step 6
|
||||
8. Combat resolution — step 7
|
||||
9. Deaths & loot — step 7
|
||||
10. `tickMovement` — step 6
|
||||
11. Scrap despawn — step 5
|
||||
|
||||
Each new subsystem slots into `Simulation::tick()` in this exact order.
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Buildings + placement + belt↔building transport
|
||||
|
||||
Covers REQ-BLD-*, REQ-MAT-*. Introduces the first stateful gameplay loop: miners pull nothing, produce ore, push onto belts; smelters pull ore, produce ingots; etc.
|
||||
|
||||
### New types (`src/lib/sim/`)
|
||||
|
||||
```cpp
|
||||
struct InputBuffer {
|
||||
std::map<ItemType, int> counts;
|
||||
std::map<ItemType, int> caps; // per-material; = 2× per-cycle requirement
|
||||
};
|
||||
|
||||
struct OutputBuffer {
|
||||
std::vector<Item> items;
|
||||
int capacity; // 2× per-cycle output; 1× for ReprocessingPlant
|
||||
};
|
||||
|
||||
struct Production {
|
||||
std::string recipeId;
|
||||
Tick completesAt;
|
||||
std::vector<Item> chosenOutputs; // resolved at cycle start for reprocessing
|
||||
};
|
||||
|
||||
struct Building {
|
||||
EntityId id;
|
||||
QPoint tile; // origin of footprint (top-left)
|
||||
QSize footprint;
|
||||
Rotation rotation;
|
||||
BuildingType type;
|
||||
float hp;
|
||||
float maxHp;
|
||||
std::string recipeId; // current recipe; empty = none selected
|
||||
InputBuffer inputBuffer;
|
||||
OutputBuffer outputBuffer;
|
||||
std::optional<Production> production;
|
||||
};
|
||||
```
|
||||
|
||||
### Surface-mask parsing (new utility in `src/lib/config/`)
|
||||
|
||||
```cpp
|
||||
struct ParsedSurfaceMask {
|
||||
QSize footprint;
|
||||
std::vector<QPoint> bodyCells; // relative to tile origin
|
||||
std::vector<Port> outputPorts; // tile = adjacent cell OUTSIDE footprint
|
||||
// direction = away from building
|
||||
std::vector<QPoint> shipDockCells; // 'S' cells — for salvage bay / shipyard
|
||||
};
|
||||
|
||||
ParsedSurfaceMask parseSurfaceMask(const std::vector<std::string>& rows,
|
||||
Rotation rotation);
|
||||
```
|
||||
|
||||
Conventions (inferred from `buildings.toml`):
|
||||
- `A` = body cell
|
||||
- `S` = ship dock cell (part of footprint; shipyard/salvage bay)
|
||||
- `>`, `<`, `^`, `v` = direction marker on cell ADJACENT to body, NOT part of footprint
|
||||
- space = empty within bounding box
|
||||
- Rotation transforms the grid 90°/180°/270° around the mask origin
|
||||
|
||||
### Placement + BuildingSystem
|
||||
|
||||
Either a new `BuildingSystem` class in `src/lib/sim/` or methods on `Simulation`. Recommended: a `BuildingSystem` owned by `Simulation`, mirroring `BeltSystem`'s pattern.
|
||||
|
||||
```cpp
|
||||
class BuildingSystem {
|
||||
public:
|
||||
BuildingSystem(const GameConfig& config, BeltSystem& belts,
|
||||
std::function<EntityId()> allocateId,
|
||||
std::mt19937& rng);
|
||||
|
||||
// Placement (called by UI commands in Step 8)
|
||||
EntityId place(BuildingType type, QPoint tile, Rotation rotation);
|
||||
void demolish(EntityId id);
|
||||
void setRecipe(EntityId id, const std::string& recipeId);
|
||||
|
||||
// Tick hooks — called from Simulation::tick() in the correct order
|
||||
void tickBeltPull(); // step 3
|
||||
void tickProduction(); // step 4
|
||||
void tickBeltPush(); // step 5
|
||||
|
||||
// Queries (for UI)
|
||||
const Building* find(EntityId id) const;
|
||||
std::vector<Building> all() const; // for rendering
|
||||
};
|
||||
```
|
||||
|
||||
Belts and splitters are registered with `BeltSystem` directly from `BuildingSystem::place` when type == Belt or Splitter — these don't get `Building` instances (architecture.md §Buildings).
|
||||
|
||||
### Production cycle (REQ-MAT-CYCLE)
|
||||
|
||||
In `tickProduction`:
|
||||
```
|
||||
for each building with recipeId set:
|
||||
if building has active production:
|
||||
if currentTick >= production.completesAt:
|
||||
deposit chosenOutputs into outputBuffer
|
||||
clear production
|
||||
continue
|
||||
|
||||
// idle: try to start a new cycle
|
||||
recipe = config.findRecipe(recipeId)
|
||||
if inputs available in buffers AND outputs fit in outputBuffer:
|
||||
consume inputs
|
||||
if reprocessing: roll chosenOutputs via discrete_distribution on probabilities
|
||||
else: chosenOutputs = recipe.outputs (expanded by amounts)
|
||||
// re-check fit for reprocessing (chosen output must fit)
|
||||
production = {recipeId, currentTick + secondsToTicks(recipe.durationSeconds), chosenOutputs}
|
||||
```
|
||||
|
||||
Reprocessing uses `Simulation`'s `std::mt19937` + `std::discrete_distribution<>`. Do NOT use the legacy `WeightedRandomGenerator` (uses `auto` and float precision).
|
||||
|
||||
### Belt↔building interaction
|
||||
|
||||
`tickBeltPull` (step 3): for each building with `recipeId`, walk its footprint's edges; for each adjacent tile, construct `Port{adjTile, directionFromBeltToBuilding}` and call `belts.tryTakeItem(port)`. Accept if the item matches a required input AND the per-material buffer has space.
|
||||
|
||||
`tickBeltPush` (step 5): for each output port on each building with items in outputBuffer, call `belts.tryPutItem(port, item)`. On success, remove from buffer.
|
||||
|
||||
### Files
|
||||
|
||||
**New:**
|
||||
- `src/lib/sim/Building.h`
|
||||
- `src/lib/sim/BuildingSystem.h` / `.cpp`
|
||||
- `src/lib/config/SurfaceMask.h` / `.cpp`
|
||||
- `src/test/BuildingTest.cpp`
|
||||
- `src/test/SurfaceMaskTest.cpp`
|
||||
|
||||
**Modified:**
|
||||
- `src/lib/sim/Simulation.h` / `.cpp` — own `BeltSystem` + `BuildingSystem`; call their tick hooks in order
|
||||
- `src/lib/sim/CMakeLists.txt`
|
||||
- `src/lib/config/CMakeLists.txt`
|
||||
- `src/test/CMakeLists.txt`
|
||||
|
||||
### Tests
|
||||
|
||||
- **Surface mask:** all four rotations of miner, smelter, splitter; output ports land on correct adjacent cells
|
||||
- **Placement:** place miner, verify footprint occupies expected tiles; demolish removes it
|
||||
- **Belt registration:** placing a Belt calls `BeltSystem::placeBelt`; demolishing calls `removeTile`
|
||||
- **Miner cycle:** miner with `mine_iron_ore` recipe deposits iron_ore into outputBuffer after recipe duration ticks
|
||||
- **Smelter cycle:** feed iron_ore into input buffer, 2 ore → 1 ingot in output after duration
|
||||
- **Output buffer cap:** buffer fills to 2×, production stalls
|
||||
- **Reprocessing cap:** buffer holds exactly 1× (REQ-MAT-OUTPUT-BUFFER-REPROCESSING)
|
||||
- **Reprocessing RNG:** seed-deterministic weighted output pick; N trials match expected distribution within tolerance
|
||||
- **Belt pull:** belt adjacent to smelter input edge delivers ore; smelter input buffer increments
|
||||
- **Belt push:** miner outputBuffer drains onto adjacent belt each tick when space available
|
||||
- **Recipe change:** `setRecipe` clears input + output buffers (REQ-MAT-INPUT-BUFFER, REQ-MAT-OUTPUT-BUFFER)
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Scrap + ships skeleton
|
||||
|
||||
Data structures + spawning only. No AI yet. Covers REQ-RES-SCRAP-DROP, REQ-SHP-STATS, REQ-BLD-SHIPYARD scaffolding.
|
||||
|
||||
### New types
|
||||
|
||||
```cpp
|
||||
struct Scrap {
|
||||
EntityId id;
|
||||
QVector2D position; // tile units; ship-center convention
|
||||
int amount;
|
||||
Tick despawnAt;
|
||||
};
|
||||
|
||||
struct Weapon { float damage; float range; float fireRateHz;
|
||||
float cooldownTicks; std::optional<EntityId> currentTarget; };
|
||||
struct SalvageCargo { int capacity; int current; };
|
||||
struct RepairTool { float ratePerTick; std::optional<EntityId> currentTarget; };
|
||||
|
||||
struct ThreatResponse { float engagementRange; /* CombatStance, CombatTargetPriority */
|
||||
std::optional<EntityId> currentTarget; };
|
||||
struct ScrapCollector { std::optional<QVector2D> scrapTarget; EntityId deliveryBay; };
|
||||
struct RepairBehavior { /* RepairTargetPriority */ std::optional<EntityId> currentTarget; };
|
||||
struct HomeReturn { float retreatHpFraction; QVector2D homePos; };
|
||||
|
||||
struct Ship {
|
||||
EntityId id;
|
||||
QVector2D position;
|
||||
QVector2D velocity;
|
||||
float hp;
|
||||
float maxHp;
|
||||
int level;
|
||||
std::string blueprintId; // matches ShipDef::id
|
||||
|
||||
std::optional<Weapon> weapon;
|
||||
std::optional<SalvageCargo> cargo;
|
||||
std::optional<RepairTool> repairTool;
|
||||
std::optional<ThreatResponse> threatResponse;
|
||||
std::optional<ScrapCollector> scrapCollector;
|
||||
std::optional<RepairBehavior> repairBehavior;
|
||||
std::optional<HomeReturn> homeReturn;
|
||||
|
||||
MovementIntent intent;
|
||||
};
|
||||
```
|
||||
|
||||
### `ShipSystem` / `ScrapSystem`
|
||||
|
||||
Small classes owned by `Simulation`:
|
||||
- `ShipSystem::spawn(ShipDef, level, QVector2D position)` — builds a Ship from the config by evaluating per-role formulas at `level`; components present iff corresponding `ShipDef` sections are present
|
||||
- `ShipSystem::forEach(…)` — for Step 6 behavior systems to iterate
|
||||
- `ScrapSystem::spawn(QVector2D position, int amount)` — tick step 9 caller
|
||||
- `ScrapSystem::tickDespawn()` — step 11
|
||||
|
||||
Still no AI tick hooks; `Simulation::tick()` gains step 11 only.
|
||||
|
||||
### Tests
|
||||
|
||||
- **Ship spawn:** combat ship has Weapon + ThreatResponse; salvage ship has SalvageCargo + ScrapCollector; stats evaluated from formulas at given level
|
||||
- **Component absence:** salvage ship has no Weapon; combat ship has no SalvageCargo
|
||||
- **Scrap spawn + despawn:** scrap created with `despawnAt = currentTick + secondsToTicks(world.scrapDespawnSeconds)`; after that many ticks `tickDespawn` removes it
|
||||
- **Entity ids:** spawned ships/scrap receive strictly increasing ids from `Simulation::allocateId` (needs to be exposed to `ShipSystem`/`ScrapSystem` via constructor callback)
|
||||
|
||||
### Files
|
||||
|
||||
New: `Scrap.h`, `Ship.h`, `ShipSystem.h/.cpp`, `ScrapSystem.h/.cpp`, `ShipTest.cpp`, `ScrapTest.cpp`.
|
||||
Modified: `Simulation.*`, `src/lib/sim/CMakeLists.txt`, `src/test/CMakeLists.txt`.
|
||||
|
||||
---
|
||||
|
||||
## Step 6 — Ship behavior systems + movement arbitration
|
||||
|
||||
All four behaviors + `tickMovement`, one at a time with focused tests. Movement intent priority (architecture.md §Movement Arbitration):
|
||||
|
||||
```
|
||||
HomeReturn > ThreatResponse > RepairBehavior > ScrapCollector
|
||||
priorities: 4 3 2 1
|
||||
```
|
||||
|
||||
Behaviors write `MovementIntent{priority, target}` on the ship; higher priority overwrites lower. `MovementIntent` is cleared at the start of the ship behavior step.
|
||||
|
||||
### Sub-steps (independent commits recommended)
|
||||
|
||||
**6a. `tickHomeReturn`** — if `hp/maxHp < retreatHpFraction`, write intent toward `homePos` with priority 4.
|
||||
|
||||
**6b. `tickThreatResponse`** — acquire enemy target within `engagementRange` if none; hold existing target if still valid. If target in weapon range, fire (emit FireEvent, apply damage to target's hp, start cooldown — stays in Step 7 combat resolution if we want to centralize damage; for modularity, fire here). Else write intent toward target, priority 3.
|
||||
|
||||
**6c. `tickRepairBehavior`** — find damaged friendly target; move toward if out of repair range, repair if in range. Priority 2.
|
||||
|
||||
**6d. `tickScrapCollector`** — if cargo full, intent = `deliveryBay.tile`; else find nearest scrap, intent = scrap.position. On arrival, consume scrap (calls into `ScrapSystem`), increment cargo. Priority 1.
|
||||
|
||||
**6e. `tickMovement`** — for each ship with an intent, advance position toward `intent.target` by `speedPerTick` (from ShipDef speed formula). No pathfinding v1 — straight line.
|
||||
|
||||
### Design decision: combat resolution split
|
||||
|
||||
Two options for where fire/damage happens:
|
||||
- (A) Inside `tickThreatResponse` — simpler, atomic
|
||||
- (B) In a separate `tickCombatResolution` step 8 — matches architecture.md exactly
|
||||
|
||||
Recommend (B) for fidelity to architecture.md. `tickThreatResponse` only sets target + writes movement intent. Step 7 runs combat resolution across ships + stations uniformly.
|
||||
|
||||
### Tests
|
||||
|
||||
- Intent priority: ship with low hp + weapon + enemy in range routes to homePos, not enemy
|
||||
- Target acquisition: closest enemy within engagementRange; unchanged while still valid
|
||||
- Repair ship finds damaged ally, moves in, repairs
|
||||
- Salvage ship picks up scrap, returns when cargo full, cargo empties at delivery bay
|
||||
- Movement: ship travels exactly `speed × secondsToTicks(duration)` tiles over N ticks
|
||||
|
||||
---
|
||||
|
||||
## Step 7 — Waves + threat + combat + deaths & loot
|
||||
|
||||
Fills tick steps 1, 2, 8, 9. Covers REQ-WAV-*, REQ-SHP-FIRING-*, REQ-DEF-*, REQ-PSH-*, REQ-RES-SCRAP-DROP.
|
||||
|
||||
### Tick step 1 — Wave scheduler
|
||||
|
||||
```
|
||||
- advance m_waveTimer by 1 tick
|
||||
- if between waves: at wave trigger (random gap within world.waves.gap_min/max_seconds),
|
||||
compute wave composition by drawing ship picks up to threat budget
|
||||
(REQ-WAV-TRIGGER, REQ-WAV-THREAT-COST) using world.waves.threat_rate_formula
|
||||
- schedule spawn times across spawn_duration_seconds
|
||||
- spawn any enemy ships whose scheduled tick has arrived
|
||||
```
|
||||
|
||||
Ships eligible for waves: those with `threat.costFormula(elapsedSeconds) > 0`.
|
||||
|
||||
### Tick step 2 — Threat accumulation
|
||||
|
||||
`m_threatLevel += max(0.0, world.waves.threatRateFormula.evaluate(elapsedSeconds)) × kTickDurationSeconds`.
|
||||
|
||||
### Tick step 8 — Combat resolution
|
||||
|
||||
Unified across ships + defence stations (player + enemy). Each shooter has {damage, range, fireRateHz, cooldown, currentTarget}. If target in range and cooldown ≤ 0:
|
||||
- apply damage to target's hp
|
||||
- emit `FireEvent{shooter.id, target.id, currentTick}` into `Simulation::m_fireEvents`
|
||||
- set cooldown = `kTickRateHz / fireRateHz`
|
||||
|
||||
Stations fire per REQ-DEF-PLAYER-FIRE and REQ-PSH-STATION-FIRE; stats from config formulas at their level / generation.
|
||||
|
||||
### Tick step 9 — Deaths & loot
|
||||
|
||||
- For each entity with hp ≤ 0: drop scrap at position (REQ-RES-SCRAP-DROP); amount from ShipDef.loot.scrapDrop or station scrap formula
|
||||
- Track enemy defence station "sets": if a full set destroyed this tick, award player one blueprint (REQ-DEF-BLUEPRINT-DROP); emit `BlueprintDropEvent`
|
||||
- Remove dead entities (ships, scrap, buildings)
|
||||
|
||||
### Push mechanic (REQ-PSH-*)
|
||||
|
||||
When enemy wave progresses beyond contest zone: `world.push` expansion triggers, enemy defence station set spawns at new front, scaling_factor applied to formulas. This may belong in a dedicated `PushSystem` or fold into the wave scheduler. Decide at implementation time.
|
||||
|
||||
### Files
|
||||
|
||||
New: `WaveSystem.h/.cpp`, `CombatSystem.h/.cpp`, maybe `PushSystem.h/.cpp`, corresponding `*Test.cpp`.
|
||||
Modified: `Simulation.*` to wire in tick steps 1, 2, 8, 9; `ShipSystem` to expose iteration; `BuildingSystem` to expose defence stations for combat.
|
||||
|
||||
### Tests
|
||||
|
||||
- Threat accumulates per second from the formula
|
||||
- Wave spawn count matches threat budget / ship cost
|
||||
- Fire event emitted + drainable + cleared
|
||||
- Shooter on cooldown does not fire
|
||||
- Ship at hp ≤ 0 drops scrap; scrap amount matches ShipDef
|
||||
- Full enemy station set destroyed → BlueprintDropEvent with correct newLevel / wasNewUnlock
|
||||
- Damage to HQ decrements HQ hp — game-over condition emitted when hp ≤ 0 (if we model it that way)
|
||||
|
||||
---
|
||||
|
||||
## Step 8 — UI layer
|
||||
|
||||
Big step. Break into sub-phases to keep each commit reviewable.
|
||||
|
||||
### 8a. Visuals config + window scaffolding
|
||||
|
||||
- New `visuals.toml` (REQ-UI, architecture.md §Rendering → Visual Parameters) — per-type fill/outline/glyph entries
|
||||
- `src/ui/VisualsConfig.h/.cpp`, `src/ui/VisualsLoader.h/.cpp` — fail-fast on missing entries for any known sim id
|
||||
- Main window widget: header bar + central game view + right-hand selected-building panel (QDockWidget or split layout)
|
||||
- Wire `QApplication` + `Simulation + TickDriver` into `main.cpp` replacing the current stub
|
||||
- Sim + UI share one thread; paintEvent reads sim state directly (no locks — architecture.md §Threading)
|
||||
|
||||
### 8b. GameWorldView (render only, no input)
|
||||
|
||||
- `QOpenGLWidget` subclass with `QPainter` drawing
|
||||
- `QTimer` @ 60 Hz → `update()` + advances sim via `TickDriver::advance(elapsedMs, gameSpeedMultiplier)` → calls `sim.tick()` N times
|
||||
- Layer order per architecture.md §Layer Order (tiles → buildings → belt items → scrap → ships → beams → overlays → screen-space)
|
||||
- Scroll via `scrollXTiles` float, A/D keyboard input, clamped per REQ-GW-SCROLL-LIMIT
|
||||
- Mouse→world conversion: `worldX = mouseX / 20 + scrollXTiles`
|
||||
- Beam renderer: keeps `FireEvent`s for 0.3 s wall time (9 ticks @ 30 Hz), drops if either end entity is gone
|
||||
- Blueprint toasts: keeps `BlueprintDropEvent`s for configured toast duration
|
||||
|
||||
### 8c. Input → sim commands
|
||||
|
||||
- Tile click: select building / select belt tiles (box drag)
|
||||
- Builder mode: open from build button grid; shows ghost on cursor; click places construction site (REQ-BLD-PLACE); drag-to-place for belts (REQ-BLD-DRAG)
|
||||
- Demolish mode: click building → demolish (confirm), returns refund (REQ-BLD-DEMOLISH)
|
||||
- Selected-building panel: recipe picker, clear-belt button (REQ-UI-BELT-CLEAR), splitter filter config, demolish button
|
||||
- Speed controls: 0 / 0.5× / 1× / 2× / 4× (REQ-UI-SPEED) — bound to spacebar pause + number keys
|
||||
|
||||
### 8d. Header bar + polish
|
||||
|
||||
- Resource counters (building blocks, blueprint collection)
|
||||
- Threat meter
|
||||
- Wave countdown
|
||||
- FPS / speed indicator
|
||||
- Minor polish: hover highlights, keyboard shortcuts, tooltip on build buttons
|
||||
|
||||
### Files
|
||||
|
||||
New: `src/ui/` populated — `MainWindow.*`, `GameWorldView.*`, `HeaderBar.*`, `BuildButtonGrid.*`, `SelectedBuildingPanel.*`, `VisualsConfig.*`, `VisualsLoader.*`, `Toast.*`, etc.
|
||||
Modified: `src/ui/CMakeLists.txt` — flip from INTERFACE library to regular static library; enable AUTOMOC; add `Q_OBJECT` macros where needed. `src/app/main.cpp` — construct sim + main window.
|
||||
|
||||
### Tests
|
||||
|
||||
UI code is largely visual; prioritize:
|
||||
- Visuals loader fail-fast on missing entries
|
||||
- Simulation + TickDriver integration test: at 1×, 60 render frames produce ~30 sim ticks (approximately — tolerate ±1 for accumulator residue)
|
||||
- Manual smoke test checklist (in-repo markdown) for builder mode, demolish, recipe change, clear belt, speed toggling
|
||||
|
||||
---
|
||||
|
||||
## Things to revisit as needed
|
||||
|
||||
- **Pathfinding for ships:** straight-line in v1 is fine given open space; only revisit if enemy defence stations create obstacles
|
||||
- **Belt segment compression (v2):** only if v1 per-tile profiling is bad
|
||||
- **Worker thread for sim:** only if paint stalls become visible; `drain*` APIs already support it
|
||||
- **ECS migration for ships:** only if component iteration becomes a bottleneck
|
||||
- **Belt curves rendering:** derive from consecutive belt tile directions; sim logic is unaffected
|
||||
@@ -9,7 +9,7 @@ Config files use the TOML format. The following config files drive game paramete
|
||||
- **recipes.toml** — crafting recipes: inputs, outputs, quantities, durations, and reprocessing plant probabilities.
|
||||
- **ships.toml** — per blueprint: a human-readable display name (used in toasts and UI), ship stats (HP, speed, damage, attack range, attack rate, sensor range) as formulas of ship level, required build materials, threat cost formula, player production level, and whether the blueprint is available from game start.
|
||||
- **stations.toml** — HP, damage, range, fire rate, and scrap drop for player and enemy defence stations, defined as formulas of station level.
|
||||
- **visuals.toml** — rendering-only config (not game parameters): fill and outline colors, glyphs, and tile tints for every building type, item type, ship role, and station type; beam color and width; overlay and toast colors. Loaded by the UI at startup; the simulation does not read it.
|
||||
- **visuals.toml** — rendering-only config (not game parameters): fill and outline colors and glyphs for every building type, item type, ship role, and station type; beam color and width; overlay and toast colors. Loaded by the UI at startup; the simulation does not read it.
|
||||
|
||||
### Surface Mask Format
|
||||
|
||||
@@ -95,7 +95,6 @@ Output port indicators are not building tiles themselves. A building may have mo
|
||||
- REQ-RES-SCRAP-DROP: Destroyed ships (both player and enemy) and destroyed defence stations (both player and enemy) drop scrap at their location. The scrap amount per ship is defined in `ships.toml [ship.loot].scrap_drop`; for stations it is defined as `stations.toml [player_station].scrap_drop_formula` and `[enemy_station].scrap_drop_formula`. Scrap despawns after `world.toml [world].scrap_despawn_seconds` seconds if not collected.
|
||||
- REQ-RES-SCRAP-COLLECT: Scrap is collected by salvage ships and delivered to a Salvage Bay on the asteroid. From there it can be fed via belt into a smelter (same output as ore) or a Reprocessing Plant.
|
||||
- REQ-RES-BUILDING-BLOCKS: Building blocks are produced by an assembler recipe and are the only globally pooled resource. They are added to the global stock when delivered to the HQ via belt.
|
||||
- REQ-RES-CONFIG: All recipes, production rates, and reprocessing probabilities are defined in recipes.toml.
|
||||
|
||||
## Ships
|
||||
|
||||
@@ -108,13 +107,13 @@ Output port indicators are not building tiles themselves. A building may have mo
|
||||
- REQ-SHP-SENSOR: A ship perceives only entities within its sensor range. Behavior is driven by what is in sensor range; entities outside sensor range are ignored.
|
||||
- REQ-SHP-FIRING: All weapons — on ships and on defence stations — are hitscan lasers. When a weapon is off cooldown and its target is within attack range, it fires: damage is applied instantly to the target with no projectile entity, no travel time, and no intervening collision. The weapon's cooldown then begins, derived from its fire rate formula.
|
||||
- REQ-SHP-FIRING-BEAM: Each fire event produces a visual laser beam drawn from the shooter's position to the target's position for 0.3 seconds. The beam is a pure rendering effect and has no simulation state (does not block movement, does not re-apply damage over its lifetime). Beams follow the shooter and target positions if either moves during the 0.3-second window.
|
||||
- REQ-SHP-ENEMY-AI: Enemy ships engage the closest valid target (player defence station, HQ, or player ship) within their sensor range. If no target is in sensor range, they move toward the asteroid (leftward in world coordinates).
|
||||
- REQ-SHP-COMBAT: **Combat ships** — move right through space and engage enemy ships. The player can configure the following per shipyard (applied to all ships produced by that shipyard):
|
||||
- REQ-SHP-COMBAT: **Combat ships** (player) — engage enemy ships within sensor range. The player can configure the following per shipyard (applied to all ships produced by that shipyard):
|
||||
- Stance: aggressive (advance toward enemies) / defensive (hold position near asteroid).
|
||||
- Target priority: closest / highest HP / structures first.
|
||||
- REQ-SHP-SALVAGE: **Salvage ships** — patrol by moving forward (rightward, away from the asteroid) while searching sensor range. If scrap enters sensor range, move to it, collect, and deliver it to a Salvage Bay on the asteroid; after delivery, resume patrol. If an enemy ship enters sensor range while not currently targeting or carrying scrap, turn back (move toward the asteroid) until the enemy is no longer in sensor range, then resume patrol. Salvage ships are vulnerable to enemy ships while operating.
|
||||
- REQ-SHP-REPAIR: **Repair ships** — patrol by moving forward (rightward, away from the asteroid) while searching sensor range. If a damaged player defence station or player ship enters sensor range, move to it and repair. If an enemy ship enters sensor range while not currently repairing, turn back (move toward the asteroid) until the enemy is no longer in sensor range, then resume patrol. The player can configure the target priority per shipyard:
|
||||
- REQ-SHP-SALVAGE: **Salvage ships** (player) — patrol by moving forward (rightward, away from the asteroid) while searching sensor range. If scrap enters sensor range, move to it, collect, and deliver it to a Salvage Bay on the asteroid; after delivery, resume patrol. If an enemy ship enters sensor range while not currently targeting or carrying scrap, turn back (move toward the asteroid) until the enemy is no longer in sensor range, then resume patrol. Salvage ships are vulnerable to enemy ships while operating.
|
||||
- REQ-SHP-REPAIR: **Repair ships** (player) — patrol by moving forward (rightward, away from the asteroid) while searching sensor range. If a damaged player defence station or player ship enters sensor range, move to it and repair. If an enemy ship enters sensor range while not currently repairing, turn back (move toward the asteroid) until the enemy is no longer in sensor range, then resume patrol. The player can configure the target priority per shipyard:
|
||||
- Defence stations first / ships first / nearest target.
|
||||
- REQ-SHP-ENEMY-AI: **Enemy ships** — engage the closest valid target (player defence station, HQ, or player ship) within their sensor range. If no target is in sensor range, they move toward the asteroid (leftward in world coordinates).
|
||||
- REQ-SHP-BLUEPRINTS: The player selects a blueprint per shipyard by clicking it. New blueprints are unlocked automatically when an enemy defence station set is destroyed (REQ-DEF-BLUEPRINT-DROP) — there is no physical loot to collect.
|
||||
|
||||
## Defence Stations
|
||||
@@ -133,10 +132,9 @@ Output port indicators are not building tiles themselves. A building may have mo
|
||||
- REQ-WAV-THREAT-RATE: A global **threat level** accumulates continuously over time. The rate of increase per second is determined by `world.toml [waves].threat_rate_formula` where x is elapsed game time in seconds, clamped to a minimum of 0 (negative formula values are treated as 0). Example: `1*x - 30` yields 0 threat/s for x ≤ 30s and increases linearly after that.
|
||||
- REQ-WAV-GAP: At game start and immediately after each wave is triggered, a random inter-wave gap is drawn uniformly from [`world.toml [waves].gap_min_seconds`, `gap_max_seconds`].
|
||||
- REQ-WAV-TRIGGER: When the gap expires, a wave is triggered. Ships are selected one at a time: from all blueprints whose `threat.cost_formula` evaluates to > 0 at the current enemy ship level, uniformly randomly pick one whose cost fits the remaining threat budget. Repeat until no eligible blueprint fits. Any remaining threat carries over to the next wave. A longer gap results in a larger wave.
|
||||
- REQ-WAV-SHIP-LEVEL: Each wave's enemy ships are assigned a level determined by `world.toml [waves].ship_level_formula` where x is elapsed game time in seconds. This is the sole mechanism by which enemy waves grow stronger over time.
|
||||
- REQ-WAV-SHIP-LEVEL: Each wave's enemy ships are assigned a level determined by `world.toml [waves].ship_level_formula` where x is elapsed game time in seconds. This is the sole mechanism by which individual enemy ships become stronger over time. Wave *size* grows separately via threat accumulation (REQ-WAV-THREAT-RATE) and push scaling (REQ-PSH-ACCUMULATION). Per-ship stats and threat cost are computed from the ship level via the formulas in `ships.toml` (see REQ-SHP-STATS).
|
||||
- REQ-WAV-THREAT-COST: Each ship type has a threat cost defined as `ships.toml [ship.threat].cost_formula`. Ships are selected one at a time per REQ-WAV-TRIGGER until no eligible blueprint's cost fits the remaining threat budget. Because enemy ship level increases with time, threat cost per ship rises naturally over the course of the game.
|
||||
- REQ-WAV-SPAWN-DURATION: Ships in a wave are spawned one at a time over `world.toml [waves].spawn_duration_seconds`.
|
||||
- REQ-WAV-SHIP-STATS: Per-ship stats (damage: `[ship.combat].damage_formula`, attack rate: `[ship.combat].attack_rate_formula`, range: `[ship.combat].attack_range_formula`, speed: `[ship.movement].speed_formula`) and threat cost (`[ship.threat].cost_formula`) are each defined as formulas of ship level in `ships.toml`.
|
||||
- REQ-WAV-GRACE-PERIOD: The grace period before the first wave is implicit: threat accumulates from t=0 but the first wave does not trigger until the initial gap (drawn at game start) has elapsed.
|
||||
|
||||
## Push Scaling
|
||||
|
||||
186
src/CMakeLists.txt
Normal file
186
src/CMakeLists.txt
Normal file
@@ -0,0 +1,186 @@
|
||||
set(TARGET_BASE_NAME "DotaFactory")
|
||||
|
||||
set(TARGET_APP_NAME "${TARGET_BASE_NAME}")
|
||||
set(TARGET_LIB_NAME "${TARGET_BASE_NAME}_lib")
|
||||
set(TARGET_UI_NAME "${TARGET_BASE_NAME}_ui")
|
||||
set(TARGET_TEST_NAME "${TARGET_BASE_NAME}_test")
|
||||
|
||||
set(TARGET_LIB_INCLUDE_DIRS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lib"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/external"
|
||||
)
|
||||
set(TARGET_UI_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/ui")
|
||||
set(TARGET_TEST_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/test")
|
||||
|
||||
set(SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# lib — simulation + config
|
||||
# Depends on Qt Core, toml++, tinyexpr only (no QtWidgets).
|
||||
# ============================================================
|
||||
|
||||
set(HDRS)
|
||||
set(SRCS)
|
||||
set(LIB_INCLUDE_PATH)
|
||||
|
||||
add_subdirectory(external)
|
||||
add_subdirectory(lib)
|
||||
|
||||
set_source_files_properties(${EXTERNAL_FILES} PROPERTIES COMPILE_FLAGS "-w")
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
set(RELATIVE_HDRS)
|
||||
foreach (_file ${HDRS})
|
||||
file(RELATIVE_PATH _relPath "${SRC_DIR}" "${_file}")
|
||||
list(APPEND RELATIVE_HDRS "${_relPath}")
|
||||
endforeach()
|
||||
|
||||
set(RELATIVE_SRCS)
|
||||
foreach (_file ${SRCS})
|
||||
file(RELATIVE_PATH _relPath "${SRC_DIR}" "${_file}")
|
||||
list(APPEND RELATIVE_SRCS "${_relPath}")
|
||||
endforeach()
|
||||
|
||||
add_files(LIB_FILES ${RELATIVE_HDRS} ${RELATIVE_SRCS})
|
||||
|
||||
add_library(${TARGET_LIB_NAME} ${LIB_FILES} ${EXTERNAL_FILES})
|
||||
create_source_groups(${LIB_FILES})
|
||||
create_source_groups(${EXTERNAL_FILES})
|
||||
|
||||
set_property(TARGET ${TARGET_LIB_NAME} PROPERTY CXX_STANDARD 17)
|
||||
set_property(TARGET ${TARGET_LIB_NAME} PROPERTY INCLUDE_DIRECTORIES
|
||||
"${TARGET_LIB_INCLUDE_DIRS}"
|
||||
"${LIB_INCLUDE_PATH}"
|
||||
)
|
||||
target_link_libraries(${TARGET_LIB_NAME} Qt5::Core)
|
||||
target_compile_definitions(${TARGET_LIB_NAME} PRIVATE TOML_FLOAT_CHARCONV=0)
|
||||
|
||||
set(CMAKE_AUTOMOC OFF)
|
||||
|
||||
unset(LIB_FILES)
|
||||
unset(RELATIVE_HDRS)
|
||||
unset(RELATIVE_SRCS)
|
||||
unset(HDRS)
|
||||
unset(SRCS)
|
||||
# LIB_INCLUDE_PATH (populated by lib/ and external/ subdirectories) is kept
|
||||
# in scope so the test target below can also reach those headers.
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ui — QtWidgets + QOpenGLWidget
|
||||
# Depends on lib. No sources yet; declared as INTERFACE library.
|
||||
# When UI source files are added under src/ui/, change this to
|
||||
# a regular static library and enable AUTOMOC on the target.
|
||||
# ============================================================
|
||||
|
||||
add_subdirectory(ui)
|
||||
|
||||
add_library(${TARGET_UI_NAME} INTERFACE)
|
||||
|
||||
target_link_libraries(${TARGET_UI_NAME} INTERFACE
|
||||
${TARGET_LIB_NAME}
|
||||
Qt5::Widgets
|
||||
${OPENGL_LIBRARIES}
|
||||
Qt5::Network
|
||||
Qt5::Multimedia
|
||||
Qt5::Charts
|
||||
)
|
||||
|
||||
target_include_directories(${TARGET_UI_NAME} INTERFACE
|
||||
"${TARGET_UI_INCLUDE_DIRS}"
|
||||
"${TARGET_LIB_INCLUDE_DIRS}"
|
||||
"${LIB_INCLUDE_PATH}"
|
||||
)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# app — thin main(); depends on ui
|
||||
# ============================================================
|
||||
|
||||
set(OUTPUT_ROOT_PATH "${CMAKE_BINARY_DIR}/${TARGET_BASE_NAME}")
|
||||
|
||||
if (WIN32)
|
||||
COPY_QT_BINARIES("${OUTPUT_ROOT_PATH}/Debug/app/" True)
|
||||
COPY_QT_BINARIES("${OUTPUT_ROOT_PATH}/Release/app/" False)
|
||||
|
||||
string(REGEX REPLACE "/" "\\\\" BACKSLASHED_CMAKE_SOURCE_DIR ${CMAKE_SOURCE_DIR})
|
||||
string(REGEX REPLACE "/" "\\\\" BACKSLASHED_OUTPUT_ROOT_PATH ${OUTPUT_ROOT_PATH})
|
||||
execute_process(
|
||||
COMMAND "cmd.exe" "/k" "rmdir" "${BACKSLASHED_OUTPUT_ROOT_PATH}\\Debug\\app\\data" & "mklink" "/d" "/j" "${BACKSLASHED_OUTPUT_ROOT_PATH}\\Debug\\app\\data" "${BACKSLASHED_CMAKE_SOURCE_DIR}\\bin\\app\\data" & exit
|
||||
COMMAND "cmd.exe" "/k" "rmdir" "${BACKSLASHED_OUTPUT_ROOT_PATH}\\Release\\app\\data" & "mklink" "/d" "/j" "${BACKSLASHED_OUTPUT_ROOT_PATH}\\Release\\app\\data" "${BACKSLASHED_CMAKE_SOURCE_DIR}\\bin\\app\\data" & exit
|
||||
)
|
||||
endif ()
|
||||
|
||||
foreach (OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES})
|
||||
string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} "${OUTPUT_ROOT_PATH}/${OUTPUTCONFIG}/app/")
|
||||
endforeach ()
|
||||
|
||||
set(HDRS)
|
||||
set(SRCS)
|
||||
|
||||
add_subdirectory(app)
|
||||
|
||||
set(RELATIVE_HDRS)
|
||||
foreach (_file ${HDRS})
|
||||
file(RELATIVE_PATH _relPath "${SRC_DIR}" "${_file}")
|
||||
list(APPEND RELATIVE_HDRS "${_relPath}")
|
||||
endforeach()
|
||||
|
||||
set(RELATIVE_SRCS)
|
||||
foreach (_file ${SRCS})
|
||||
file(RELATIVE_PATH _relPath "${SRC_DIR}" "${_file}")
|
||||
list(APPEND RELATIVE_SRCS "${_relPath}")
|
||||
endforeach()
|
||||
|
||||
add_files(APP_FILES ${RELATIVE_HDRS} ${RELATIVE_SRCS})
|
||||
|
||||
add_executable(${TARGET_APP_NAME} ${APP_FILES})
|
||||
create_source_groups(${APP_FILES})
|
||||
|
||||
set_property(TARGET ${TARGET_APP_NAME} PROPERTY CXX_STANDARD 17)
|
||||
set_target_properties(${TARGET_APP_NAME} PROPERTIES
|
||||
VS_DEBUGGER_WORKING_DIRECTORY "${OUTPUT_ROOT_PATH}/$(Configuration)/app/"
|
||||
)
|
||||
target_link_libraries(${TARGET_APP_NAME} ${TARGET_UI_NAME})
|
||||
|
||||
unset(APP_FILES)
|
||||
unset(RELATIVE_HDRS)
|
||||
unset(RELATIVE_SRCS)
|
||||
unset(HDRS)
|
||||
unset(SRCS)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# tests — Catch2 tests; links against lib only (no QtWidgets)
|
||||
# ============================================================
|
||||
|
||||
add_subdirectory(test)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
add_executable(${TARGET_TEST_NAME} ${TEST_FILES})
|
||||
create_source_groups(${TEST_FILES})
|
||||
|
||||
set_property(TARGET ${TARGET_TEST_NAME} PROPERTY CXX_STANDARD 17)
|
||||
set_property(TARGET ${TARGET_TEST_NAME} PROPERTY INCLUDE_DIRECTORIES
|
||||
"${TARGET_TEST_INCLUDE_DIRS}"
|
||||
"${TARGET_LIB_INCLUDE_DIRS}"
|
||||
"${LIB_INCLUDE_PATH}"
|
||||
)
|
||||
target_compile_definitions(${TARGET_TEST_NAME} PRIVATE
|
||||
DOTA_FACTORY_CONFIG_DIR="${CMAKE_SOURCE_DIR}/bin/config"
|
||||
)
|
||||
target_link_libraries(${TARGET_TEST_NAME} ${TARGET_LIB_NAME})
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Export
|
||||
# ============================================================
|
||||
|
||||
set(TARGET_LIB_NAME "${TARGET_LIB_NAME}" PARENT_SCOPE)
|
||||
set(TARGET_UI_NAME "${TARGET_UI_NAME}" PARENT_SCOPE)
|
||||
set(TARGET_APP_NAME "${TARGET_APP_NAME}" PARENT_SCOPE)
|
||||
16
src/app/CMakeLists.txt
Normal file
16
src/app/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
SET(HDRS)
|
||||
SET(SRCS)
|
||||
|
||||
SET(HDRS
|
||||
${HDRS}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
SET(SRCS
|
||||
${SRCS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
|
||||
|
||||
34
src/app/main.cpp
Normal file
34
src/app/main.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <memory>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
|
||||
#include "ConsoleLogger.h"
|
||||
#include "logging.h"
|
||||
#include "LogManager.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
LogManager::getInstance()->addLogger(std::make_shared<ConsoleLogger>());
|
||||
LogManager::getInstance()->setLoggingEnabled(true);
|
||||
LOG_INFO("Logging enabled");
|
||||
|
||||
QApplication::setApplicationName("DotaFactory");
|
||||
|
||||
if (QSysInfo::windowsVersion() != QSysInfo::WV_None)
|
||||
{
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
|
||||
}
|
||||
|
||||
QApplication application(argc, argv);
|
||||
|
||||
const QDir dataDir("data");
|
||||
if (!dataDir.exists())
|
||||
{
|
||||
QDir().mkdir(dataDir.dirName());
|
||||
}
|
||||
|
||||
const int ret = application.exec();
|
||||
|
||||
return ret;
|
||||
}
|
||||
22
src/external/CMakeLists.txt
vendored
Normal file
22
src/external/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
add_files(
|
||||
EXTERNAL_FILES
|
||||
|
||||
catch/catch.hpp
|
||||
tinyexpr/tinyexpr.c
|
||||
tinyexpr/tinyexpr.h
|
||||
toml++/toml.hpp
|
||||
toml++/toml.h
|
||||
)
|
||||
|
||||
# Expose each external library's own directory on the include path so that
|
||||
# source files can reference its headers without a subdirectory prefix:
|
||||
# #include "catch.hpp" // instead of "catch/catch.hpp"
|
||||
# #include "tinyexpr.h" // instead of "tinyexpr/tinyexpr.h"
|
||||
# #include "toml.hpp" // instead of "toml++/toml.hpp"
|
||||
set(LIB_INCLUDE_PATH
|
||||
${LIB_INCLUDE_PATH}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/catch
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tinyexpr
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/toml++
|
||||
PARENT_SCOPE
|
||||
)
|
||||
14361
src/external/catch/catch.hpp
vendored
Normal file
14361
src/external/catch/catch.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
734
src/external/tinyexpr/tinyexpr.c
vendored
Normal file
734
src/external/tinyexpr/tinyexpr.c
vendored
Normal file
@@ -0,0 +1,734 @@
|
||||
// SPDX-License-Identifier: Zlib
|
||||
/*
|
||||
* TINYEXPR - Tiny recursive descent parser and evaluation engine in C
|
||||
*
|
||||
* Copyright (c) 2015-2020 Lewis Van Winkle
|
||||
*
|
||||
* http://CodePlea.com
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgement in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/* COMPILE TIME OPTIONS */
|
||||
|
||||
/* Exponentiation associativity:
|
||||
For a^b^c = (a^b)^c and -a^b = (-a)^b do nothing.
|
||||
For a^b^c = a^(b^c) and -a^b = -(a^b) uncomment the next line.*/
|
||||
/* #define TE_POW_FROM_RIGHT */
|
||||
|
||||
/* Logarithms
|
||||
For log = base 10 log do nothing
|
||||
For log = natural log uncomment the next line. */
|
||||
/* #define TE_NAT_LOG */
|
||||
|
||||
#include "tinyexpr.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifndef NAN
|
||||
#define NAN (0.0/0.0)
|
||||
#endif
|
||||
|
||||
#ifndef INFINITY
|
||||
#define INFINITY (1.0/0.0)
|
||||
#endif
|
||||
|
||||
|
||||
typedef double (*te_fun2)(double, double);
|
||||
|
||||
enum {
|
||||
TOK_NULL = TE_CLOSURE7+1, TOK_ERROR, TOK_END, TOK_SEP,
|
||||
TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_VARIABLE, TOK_INFIX
|
||||
};
|
||||
|
||||
|
||||
enum {TE_CONSTANT = 1};
|
||||
|
||||
|
||||
typedef struct state {
|
||||
const char *start;
|
||||
const char *next;
|
||||
int type;
|
||||
union {double value; const double *bound; const void *function;};
|
||||
void *context;
|
||||
|
||||
const te_variable *lookup;
|
||||
int lookup_len;
|
||||
} state;
|
||||
|
||||
|
||||
#define TYPE_MASK(TYPE) ((TYPE)&0x0000001F)
|
||||
|
||||
#define IS_PURE(TYPE) (((TYPE) & TE_FLAG_PURE) != 0)
|
||||
#define IS_FUNCTION(TYPE) (((TYPE) & TE_FUNCTION0) != 0)
|
||||
#define IS_CLOSURE(TYPE) (((TYPE) & TE_CLOSURE0) != 0)
|
||||
#define ARITY(TYPE) ( ((TYPE) & (TE_FUNCTION0 | TE_CLOSURE0)) ? ((TYPE) & 0x00000007) : 0 )
|
||||
#define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){__VA_ARGS__})
|
||||
#define CHECK_NULL(ptr, ...) if ((ptr) == NULL) { __VA_ARGS__; return NULL; }
|
||||
|
||||
static te_expr *new_expr(const int type, const te_expr *parameters[]) {
|
||||
const int arity = ARITY(type);
|
||||
const int psize = sizeof(void*) * arity;
|
||||
const int size = (sizeof(te_expr) - sizeof(void*)) + psize + (IS_CLOSURE(type) ? sizeof(void*) : 0);
|
||||
te_expr *ret = malloc(size);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
memset(ret, 0, size);
|
||||
if (arity && parameters) {
|
||||
memcpy(ret->parameters, parameters, psize);
|
||||
}
|
||||
ret->type = type;
|
||||
ret->bound = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void te_free_parameters(te_expr *n) {
|
||||
if (!n) return;
|
||||
switch (TYPE_MASK(n->type)) {
|
||||
case TE_FUNCTION7: case TE_CLOSURE7: te_free(n->parameters[6]); /* Falls through. */
|
||||
case TE_FUNCTION6: case TE_CLOSURE6: te_free(n->parameters[5]); /* Falls through. */
|
||||
case TE_FUNCTION5: case TE_CLOSURE5: te_free(n->parameters[4]); /* Falls through. */
|
||||
case TE_FUNCTION4: case TE_CLOSURE4: te_free(n->parameters[3]); /* Falls through. */
|
||||
case TE_FUNCTION3: case TE_CLOSURE3: te_free(n->parameters[2]); /* Falls through. */
|
||||
case TE_FUNCTION2: case TE_CLOSURE2: te_free(n->parameters[1]); /* Falls through. */
|
||||
case TE_FUNCTION1: case TE_CLOSURE1: te_free(n->parameters[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void te_free(te_expr *n) {
|
||||
if (!n) return;
|
||||
te_free_parameters(n);
|
||||
free(n);
|
||||
}
|
||||
|
||||
|
||||
static double pi(void) {return 3.14159265358979323846;}
|
||||
static double e(void) {return 2.71828182845904523536;}
|
||||
static double fac(double a) {/* simplest version of fac */
|
||||
if (a < 0.0)
|
||||
return NAN;
|
||||
if (a > UINT_MAX)
|
||||
return INFINITY;
|
||||
unsigned int ua = (unsigned int)(a);
|
||||
unsigned long int result = 1, i;
|
||||
for (i = 1; i <= ua; i++) {
|
||||
if (i > ULONG_MAX / result)
|
||||
return INFINITY;
|
||||
result *= i;
|
||||
}
|
||||
return (double)result;
|
||||
}
|
||||
static double ncr(double n, double r) {
|
||||
if (n < 0.0 || r < 0.0 || n < r) return NAN;
|
||||
if (n > UINT_MAX || r > UINT_MAX) return INFINITY;
|
||||
unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i;
|
||||
unsigned long int result = 1;
|
||||
if (ur > un / 2) ur = un - ur;
|
||||
for (i = 1; i <= ur; i++) {
|
||||
if (result > ULONG_MAX / (un - ur + i))
|
||||
return INFINITY;
|
||||
result *= un - ur + i;
|
||||
result /= i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static double npr(double n, double r) {return ncr(n, r) * fac(r);}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma function (ceil)
|
||||
#pragma function (floor)
|
||||
#endif
|
||||
|
||||
static const te_variable functions[] = {
|
||||
/* must be in alphabetical order */
|
||||
{"abs", fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"acos", acos, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"asin", asin, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"atan", atan, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"atan2", atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0},
|
||||
{"ceil", ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"cos", cos, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"cosh", cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"e", e, TE_FUNCTION0 | TE_FLAG_PURE, 0},
|
||||
{"exp", exp, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"fac", fac, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"ln", log, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
#ifdef TE_NAT_LOG
|
||||
{"log", log, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
#else
|
||||
{"log", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
#endif
|
||||
{"log10", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"ncr", ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0},
|
||||
{"npr", npr, TE_FUNCTION2 | TE_FLAG_PURE, 0},
|
||||
{"pi", pi, TE_FUNCTION0 | TE_FLAG_PURE, 0},
|
||||
{"pow", pow, TE_FUNCTION2 | TE_FLAG_PURE, 0},
|
||||
{"sin", sin, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"sinh", sinh, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"sqrt", sqrt, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"tan", tan, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{"tanh", tanh, TE_FUNCTION1 | TE_FLAG_PURE, 0},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static const te_variable *find_builtin(const char *name, int len) {
|
||||
int imin = 0;
|
||||
int imax = sizeof(functions) / sizeof(te_variable) - 2;
|
||||
|
||||
/*Binary search.*/
|
||||
while (imax >= imin) {
|
||||
const int i = (imin + ((imax-imin)/2));
|
||||
int c = strncmp(name, functions[i].name, len);
|
||||
if (!c) c = '\0' - functions[i].name[len];
|
||||
if (c == 0) {
|
||||
return functions + i;
|
||||
} else if (c > 0) {
|
||||
imin = i + 1;
|
||||
} else {
|
||||
imax = i - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const te_variable *find_lookup(const state *s, const char *name, int len) {
|
||||
int iters;
|
||||
const te_variable *var;
|
||||
if (!s->lookup) return 0;
|
||||
|
||||
for (var = s->lookup, iters = s->lookup_len; iters; ++var, --iters) {
|
||||
if (strncmp(name, var->name, len) == 0 && var->name[len] == '\0') {
|
||||
return var;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static double add(double a, double b) {return a + b;}
|
||||
static double sub(double a, double b) {return a - b;}
|
||||
static double mul(double a, double b) {return a * b;}
|
||||
static double divide(double a, double b) {return a / b;}
|
||||
static double negate(double a) {return -a;}
|
||||
static double comma(double a, double b) {(void)a; return b;}
|
||||
|
||||
|
||||
void next_token(state *s) {
|
||||
s->type = TOK_NULL;
|
||||
|
||||
do {
|
||||
|
||||
if (!*s->next){
|
||||
s->type = TOK_END;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try reading a number. */
|
||||
if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') {
|
||||
s->value = strtod(s->next, (char**)&s->next);
|
||||
s->type = TOK_NUMBER;
|
||||
} else {
|
||||
/* Look for a variable or builtin function call. */
|
||||
if (isalpha(s->next[0])) {
|
||||
const char *start;
|
||||
start = s->next;
|
||||
while (isalpha(s->next[0]) || isdigit(s->next[0]) || (s->next[0] == '_')) s->next++;
|
||||
|
||||
const te_variable *var = find_lookup(s, start, s->next - start);
|
||||
if (!var) var = find_builtin(start, s->next - start);
|
||||
|
||||
if (!var) {
|
||||
s->type = TOK_ERROR;
|
||||
} else {
|
||||
switch(TYPE_MASK(var->type))
|
||||
{
|
||||
case TE_VARIABLE:
|
||||
s->type = TOK_VARIABLE;
|
||||
s->bound = var->address;
|
||||
break;
|
||||
|
||||
case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: /* Falls through. */
|
||||
case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: /* Falls through. */
|
||||
s->context = var->context; /* Falls through. */
|
||||
|
||||
case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: /* Falls through. */
|
||||
case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: /* Falls through. */
|
||||
s->type = var->type;
|
||||
s->function = var->address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Look for an operator or special character. */
|
||||
switch (s->next++[0]) {
|
||||
case '+': s->type = TOK_INFIX; s->function = add; break;
|
||||
case '-': s->type = TOK_INFIX; s->function = sub; break;
|
||||
case '*': s->type = TOK_INFIX; s->function = mul; break;
|
||||
case '/': s->type = TOK_INFIX; s->function = divide; break;
|
||||
case '^': s->type = TOK_INFIX; s->function = pow; break;
|
||||
case '%': s->type = TOK_INFIX; s->function = fmod; break;
|
||||
case '(': s->type = TOK_OPEN; break;
|
||||
case ')': s->type = TOK_CLOSE; break;
|
||||
case ',': s->type = TOK_SEP; break;
|
||||
case ' ': case '\t': case '\n': case '\r': break;
|
||||
default: s->type = TOK_ERROR; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (s->type == TOK_NULL);
|
||||
}
|
||||
|
||||
|
||||
static te_expr *list(state *s);
|
||||
static te_expr *expr(state *s);
|
||||
static te_expr *power(state *s);
|
||||
|
||||
static te_expr *base(state *s) {
|
||||
/* <base> = <constant> | <variable> | <function-0> {"(" ")"} | <function-1> <power> | <function-X> "(" <expr> {"," <expr>} ")" | "(" <list> ")" */
|
||||
te_expr *ret;
|
||||
int arity;
|
||||
|
||||
switch (TYPE_MASK(s->type)) {
|
||||
case TOK_NUMBER:
|
||||
ret = new_expr(TE_CONSTANT, 0);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
ret->value = s->value;
|
||||
next_token(s);
|
||||
break;
|
||||
|
||||
case TOK_VARIABLE:
|
||||
ret = new_expr(TE_VARIABLE, 0);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
ret->bound = s->bound;
|
||||
next_token(s);
|
||||
break;
|
||||
|
||||
case TE_FUNCTION0:
|
||||
case TE_CLOSURE0:
|
||||
ret = new_expr(s->type, 0);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
ret->function = s->function;
|
||||
if (IS_CLOSURE(s->type)) ret->parameters[0] = s->context;
|
||||
next_token(s);
|
||||
if (s->type == TOK_OPEN) {
|
||||
next_token(s);
|
||||
if (s->type != TOK_CLOSE) {
|
||||
s->type = TOK_ERROR;
|
||||
} else {
|
||||
next_token(s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TE_FUNCTION1:
|
||||
case TE_CLOSURE1:
|
||||
ret = new_expr(s->type, 0);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
ret->function = s->function;
|
||||
if (IS_CLOSURE(s->type)) ret->parameters[1] = s->context;
|
||||
next_token(s);
|
||||
ret->parameters[0] = power(s);
|
||||
CHECK_NULL(ret->parameters[0], te_free(ret));
|
||||
break;
|
||||
|
||||
case TE_FUNCTION2: case TE_FUNCTION3: case TE_FUNCTION4:
|
||||
case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7:
|
||||
case TE_CLOSURE2: case TE_CLOSURE3: case TE_CLOSURE4:
|
||||
case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7:
|
||||
arity = ARITY(s->type);
|
||||
|
||||
ret = new_expr(s->type, 0);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
ret->function = s->function;
|
||||
if (IS_CLOSURE(s->type)) ret->parameters[arity] = s->context;
|
||||
next_token(s);
|
||||
|
||||
if (s->type != TOK_OPEN) {
|
||||
s->type = TOK_ERROR;
|
||||
} else {
|
||||
int i;
|
||||
for(i = 0; i < arity; i++) {
|
||||
next_token(s);
|
||||
ret->parameters[i] = expr(s);
|
||||
CHECK_NULL(ret->parameters[i], te_free(ret));
|
||||
|
||||
if(s->type != TOK_SEP) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(s->type != TOK_CLOSE || i != arity - 1) {
|
||||
s->type = TOK_ERROR;
|
||||
} else {
|
||||
next_token(s);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TOK_OPEN:
|
||||
next_token(s);
|
||||
ret = list(s);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
if (s->type != TOK_CLOSE) {
|
||||
s->type = TOK_ERROR;
|
||||
} else {
|
||||
next_token(s);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = new_expr(0, 0);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
s->type = TOK_ERROR;
|
||||
ret->value = NAN;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static te_expr *power(state *s) {
|
||||
/* <power> = {("-" | "+")} <base> */
|
||||
int sign = 1;
|
||||
while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) {
|
||||
if (s->function == sub) sign = -sign;
|
||||
next_token(s);
|
||||
}
|
||||
|
||||
te_expr *ret;
|
||||
|
||||
if (sign == 1) {
|
||||
ret = base(s);
|
||||
} else {
|
||||
te_expr *b = base(s);
|
||||
CHECK_NULL(b);
|
||||
|
||||
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, b);
|
||||
CHECK_NULL(ret, te_free(b));
|
||||
|
||||
ret->function = negate;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef TE_POW_FROM_RIGHT
|
||||
static te_expr *factor(state *s) {
|
||||
/* <factor> = <power> {"^" <power>} */
|
||||
te_expr *ret = power(s);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
int neg = 0;
|
||||
|
||||
if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) {
|
||||
te_expr *se = ret->parameters[0];
|
||||
free(ret);
|
||||
ret = se;
|
||||
neg = 1;
|
||||
}
|
||||
|
||||
te_expr *insertion = 0;
|
||||
|
||||
while (s->type == TOK_INFIX && (s->function == pow)) {
|
||||
te_fun2 t = s->function;
|
||||
next_token(s);
|
||||
|
||||
if (insertion) {
|
||||
/* Make exponentiation go right-to-left. */
|
||||
te_expr *p = power(s);
|
||||
CHECK_NULL(p, te_free(ret));
|
||||
|
||||
te_expr *insert = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], p);
|
||||
CHECK_NULL(insert, te_free(p), te_free(ret));
|
||||
|
||||
insert->function = t;
|
||||
insertion->parameters[1] = insert;
|
||||
insertion = insert;
|
||||
} else {
|
||||
te_expr *p = power(s);
|
||||
CHECK_NULL(p, te_free(ret));
|
||||
|
||||
te_expr *prev = ret;
|
||||
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, p);
|
||||
CHECK_NULL(ret, te_free(p), te_free(prev));
|
||||
|
||||
ret->function = t;
|
||||
insertion = ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (neg) {
|
||||
te_expr *prev = ret;
|
||||
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret);
|
||||
CHECK_NULL(ret, te_free(prev));
|
||||
|
||||
ret->function = negate;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static te_expr *factor(state *s) {
|
||||
/* <factor> = <power> {"^" <power>} */
|
||||
te_expr *ret = power(s);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
while (s->type == TOK_INFIX && (s->function == pow)) {
|
||||
te_fun2 t = s->function;
|
||||
next_token(s);
|
||||
te_expr *p = power(s);
|
||||
CHECK_NULL(p, te_free(ret));
|
||||
|
||||
te_expr *prev = ret;
|
||||
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, p);
|
||||
CHECK_NULL(ret, te_free(p), te_free(prev));
|
||||
|
||||
ret->function = t;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static te_expr *term(state *s) {
|
||||
/* <term> = <factor> {("*" | "/" | "%") <factor>} */
|
||||
te_expr *ret = factor(s);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
while (s->type == TOK_INFIX && (s->function == mul || s->function == divide || s->function == fmod)) {
|
||||
te_fun2 t = s->function;
|
||||
next_token(s);
|
||||
te_expr *f = factor(s);
|
||||
CHECK_NULL(f, te_free(ret));
|
||||
|
||||
te_expr *prev = ret;
|
||||
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, f);
|
||||
CHECK_NULL(ret, te_free(f), te_free(prev));
|
||||
|
||||
ret->function = t;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static te_expr *expr(state *s) {
|
||||
/* <expr> = <term> {("+" | "-") <term>} */
|
||||
te_expr *ret = term(s);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) {
|
||||
te_fun2 t = s->function;
|
||||
next_token(s);
|
||||
te_expr *te = term(s);
|
||||
CHECK_NULL(te, te_free(ret));
|
||||
|
||||
te_expr *prev = ret;
|
||||
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, te);
|
||||
CHECK_NULL(ret, te_free(te), te_free(prev));
|
||||
|
||||
ret->function = t;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static te_expr *list(state *s) {
|
||||
/* <list> = <expr> {"," <expr>} */
|
||||
te_expr *ret = expr(s);
|
||||
CHECK_NULL(ret);
|
||||
|
||||
while (s->type == TOK_SEP) {
|
||||
next_token(s);
|
||||
te_expr *e = expr(s);
|
||||
CHECK_NULL(e, te_free(ret));
|
||||
|
||||
te_expr *prev = ret;
|
||||
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, e);
|
||||
CHECK_NULL(ret, te_free(e), te_free(prev));
|
||||
|
||||
ret->function = comma;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#define TE_FUN(...) ((double(*)(__VA_ARGS__))n->function)
|
||||
#define M(e) te_eval(n->parameters[e])
|
||||
|
||||
|
||||
double te_eval(const te_expr *n) {
|
||||
if (!n) return NAN;
|
||||
|
||||
switch(TYPE_MASK(n->type)) {
|
||||
case TE_CONSTANT: return n->value;
|
||||
case TE_VARIABLE: return *n->bound;
|
||||
|
||||
case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3:
|
||||
case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7:
|
||||
switch(ARITY(n->type)) {
|
||||
case 0: return TE_FUN(void)();
|
||||
case 1: return TE_FUN(double)(M(0));
|
||||
case 2: return TE_FUN(double, double)(M(0), M(1));
|
||||
case 3: return TE_FUN(double, double, double)(M(0), M(1), M(2));
|
||||
case 4: return TE_FUN(double, double, double, double)(M(0), M(1), M(2), M(3));
|
||||
case 5: return TE_FUN(double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4));
|
||||
case 6: return TE_FUN(double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5));
|
||||
case 7: return TE_FUN(double, double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5), M(6));
|
||||
default: return NAN;
|
||||
}
|
||||
|
||||
case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3:
|
||||
case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7:
|
||||
switch(ARITY(n->type)) {
|
||||
case 0: return TE_FUN(void*)(n->parameters[0]);
|
||||
case 1: return TE_FUN(void*, double)(n->parameters[1], M(0));
|
||||
case 2: return TE_FUN(void*, double, double)(n->parameters[2], M(0), M(1));
|
||||
case 3: return TE_FUN(void*, double, double, double)(n->parameters[3], M(0), M(1), M(2));
|
||||
case 4: return TE_FUN(void*, double, double, double, double)(n->parameters[4], M(0), M(1), M(2), M(3));
|
||||
case 5: return TE_FUN(void*, double, double, double, double, double)(n->parameters[5], M(0), M(1), M(2), M(3), M(4));
|
||||
case 6: return TE_FUN(void*, double, double, double, double, double, double)(n->parameters[6], M(0), M(1), M(2), M(3), M(4), M(5));
|
||||
case 7: return TE_FUN(void*, double, double, double, double, double, double, double)(n->parameters[7], M(0), M(1), M(2), M(3), M(4), M(5), M(6));
|
||||
default: return NAN;
|
||||
}
|
||||
|
||||
default: return NAN;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#undef TE_FUN
|
||||
#undef M
|
||||
|
||||
static void optimize(te_expr *n) {
|
||||
/* Evaluates as much as possible. */
|
||||
if (n->type == TE_CONSTANT) return;
|
||||
if (n->type == TE_VARIABLE) return;
|
||||
|
||||
/* Only optimize out functions flagged as pure. */
|
||||
if (IS_PURE(n->type)) {
|
||||
const int arity = ARITY(n->type);
|
||||
int known = 1;
|
||||
int i;
|
||||
for (i = 0; i < arity; ++i) {
|
||||
optimize(n->parameters[i]);
|
||||
if (((te_expr*)(n->parameters[i]))->type != TE_CONSTANT) {
|
||||
known = 0;
|
||||
}
|
||||
}
|
||||
if (known) {
|
||||
const double value = te_eval(n);
|
||||
te_free_parameters(n);
|
||||
n->type = TE_CONSTANT;
|
||||
n->value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error) {
|
||||
state s;
|
||||
s.start = s.next = expression;
|
||||
s.lookup = variables;
|
||||
s.lookup_len = var_count;
|
||||
|
||||
next_token(&s);
|
||||
te_expr *root = list(&s);
|
||||
if (root == NULL) {
|
||||
if (error) *error = -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (s.type != TOK_END) {
|
||||
te_free(root);
|
||||
if (error) {
|
||||
*error = (s.next - s.start);
|
||||
if (*error == 0) *error = 1;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
optimize(root);
|
||||
if (error) *error = 0;
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double te_interp(const char *expression, int *error) {
|
||||
te_expr *n = te_compile(expression, 0, 0, error);
|
||||
|
||||
double ret;
|
||||
if (n) {
|
||||
ret = te_eval(n);
|
||||
te_free(n);
|
||||
} else {
|
||||
ret = NAN;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pn (const te_expr *n, int depth) {
|
||||
int i, arity;
|
||||
printf("%*s", depth, "");
|
||||
|
||||
switch(TYPE_MASK(n->type)) {
|
||||
case TE_CONSTANT: printf("%f\n", n->value); break;
|
||||
case TE_VARIABLE: printf("bound %p\n", n->bound); break;
|
||||
|
||||
case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3:
|
||||
case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7:
|
||||
case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3:
|
||||
case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7:
|
||||
arity = ARITY(n->type);
|
||||
printf("f%d", arity);
|
||||
for(i = 0; i < arity; i++) {
|
||||
printf(" %p", n->parameters[i]);
|
||||
}
|
||||
printf("\n");
|
||||
for(i = 0; i < arity; i++) {
|
||||
pn(n->parameters[i], depth + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void te_print(const te_expr *n) {
|
||||
pn(n, 0);
|
||||
}
|
||||
87
src/external/tinyexpr/tinyexpr.h
vendored
Normal file
87
src/external/tinyexpr/tinyexpr.h
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: Zlib
|
||||
/*
|
||||
* TINYEXPR - Tiny recursive descent parser and evaluation engine in C
|
||||
*
|
||||
* Copyright (c) 2015-2020 Lewis Van Winkle
|
||||
*
|
||||
* http://CodePlea.com
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgement in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef TINYEXPR_H
|
||||
#define TINYEXPR_H
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
typedef struct te_expr {
|
||||
int type;
|
||||
union {double value; const double *bound; const void *function;};
|
||||
void *parameters[1];
|
||||
} te_expr;
|
||||
|
||||
|
||||
enum {
|
||||
TE_VARIABLE = 0,
|
||||
|
||||
TE_FUNCTION0 = 8, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3,
|
||||
TE_FUNCTION4, TE_FUNCTION5, TE_FUNCTION6, TE_FUNCTION7,
|
||||
|
||||
TE_CLOSURE0 = 16, TE_CLOSURE1, TE_CLOSURE2, TE_CLOSURE3,
|
||||
TE_CLOSURE4, TE_CLOSURE5, TE_CLOSURE6, TE_CLOSURE7,
|
||||
|
||||
TE_FLAG_PURE = 32
|
||||
};
|
||||
|
||||
typedef struct te_variable {
|
||||
const char *name;
|
||||
const void *address;
|
||||
int type;
|
||||
void *context;
|
||||
} te_variable;
|
||||
|
||||
|
||||
|
||||
/* Parses the input expression, evaluates it, and frees it. */
|
||||
/* Returns NaN on error. */
|
||||
double te_interp(const char *expression, int *error);
|
||||
|
||||
/* Parses the input expression and binds variables. */
|
||||
/* Returns NULL on error. */
|
||||
te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error);
|
||||
|
||||
/* Evaluates the expression. */
|
||||
double te_eval(const te_expr *n);
|
||||
|
||||
/* Prints debugging information on the syntax tree. */
|
||||
void te_print(const te_expr *n);
|
||||
|
||||
/* Frees the expression. */
|
||||
/* This is safe to call on NULL pointers. */
|
||||
void te_free(te_expr *n);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*TINYEXPR_H*/
|
||||
16
src/external/toml++/LICENSE
vendored
Normal file
16
src/external/toml++/LICENSE
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
1793
src/external/toml++/impl/array.hpp
vendored
Normal file
1793
src/external/toml++/impl/array.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
382
src/external/toml++/impl/array.inl
vendored
Normal file
382
src/external/toml++/impl/array.inl
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
//# {{
|
||||
#include "preprocessor.hpp"
|
||||
#if !TOML_IMPLEMENTATION
|
||||
#error This is an implementation-only header.
|
||||
#endif
|
||||
//# }}
|
||||
|
||||
#include "array.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
array::array() noexcept
|
||||
{
|
||||
#if TOML_LIFETIME_HOOKS
|
||||
TOML_ARRAY_CREATED;
|
||||
#endif
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
array::~array() noexcept
|
||||
{
|
||||
#if TOML_LIFETIME_HOOKS
|
||||
TOML_ARRAY_DESTROYED;
|
||||
#endif
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
array::array(const impl::array_init_elem* b, const impl::array_init_elem* e)
|
||||
{
|
||||
#if TOML_LIFETIME_HOOKS
|
||||
TOML_ARRAY_CREATED;
|
||||
#endif
|
||||
|
||||
TOML_ASSERT_ASSUME(b);
|
||||
TOML_ASSERT_ASSUME(e);
|
||||
TOML_ASSERT_ASSUME(b <= e);
|
||||
|
||||
if TOML_UNLIKELY(b == e)
|
||||
return;
|
||||
|
||||
size_t cap{};
|
||||
for (auto it = b; it != e; it++)
|
||||
{
|
||||
if (it->value)
|
||||
cap++;
|
||||
}
|
||||
if TOML_UNLIKELY(!cap)
|
||||
return;
|
||||
|
||||
elems_.reserve(cap);
|
||||
for (; b != e; b++)
|
||||
{
|
||||
if (b->value)
|
||||
elems_.push_back(std::move(b->value));
|
||||
}
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
array::array(const array& other) //
|
||||
: node(other)
|
||||
{
|
||||
elems_.reserve(other.elems_.size());
|
||||
for (const auto& elem : other)
|
||||
elems_.emplace_back(impl::make_node(elem));
|
||||
|
||||
#if TOML_LIFETIME_HOOKS
|
||||
TOML_ARRAY_CREATED;
|
||||
#endif
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
array::array(array && other) noexcept //
|
||||
: node(std::move(other)),
|
||||
elems_(std::move(other.elems_))
|
||||
{
|
||||
#if TOML_LIFETIME_HOOKS
|
||||
TOML_ARRAY_CREATED;
|
||||
#endif
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
array& array::operator=(const array& rhs)
|
||||
{
|
||||
if (&rhs != this)
|
||||
{
|
||||
node::operator=(rhs);
|
||||
elems_.clear();
|
||||
elems_.reserve(rhs.elems_.size());
|
||||
for (const auto& elem : rhs)
|
||||
elems_.emplace_back(impl::make_node(elem));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
array& array::operator=(array&& rhs) noexcept
|
||||
{
|
||||
if (&rhs != this)
|
||||
{
|
||||
node::operator=(std::move(rhs));
|
||||
elems_ = std::move(rhs.elems_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void array::preinsertion_resize(size_t idx, size_t count)
|
||||
{
|
||||
TOML_ASSERT(idx <= elems_.size());
|
||||
TOML_ASSERT_ASSUME(count >= 1u);
|
||||
const auto old_size = elems_.size();
|
||||
const auto new_size = old_size + count;
|
||||
const auto inserting_at_end = idx == old_size;
|
||||
elems_.resize(new_size);
|
||||
if (!inserting_at_end)
|
||||
{
|
||||
for (size_t left = old_size, right = new_size - 1u; left-- > idx; right--)
|
||||
elems_[right] = std::move(elems_[left]);
|
||||
}
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void array::insert_at_back(impl::node_ptr && elem)
|
||||
{
|
||||
TOML_ASSERT(elem);
|
||||
elems_.push_back(std::move(elem));
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
array::vector_iterator array::insert_at(const_vector_iterator pos, impl::node_ptr && elem)
|
||||
{
|
||||
return elems_.insert(pos, std::move(elem));
|
||||
}
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool array::is_homogeneous(node_type ntype) const noexcept
|
||||
{
|
||||
if (elems_.empty())
|
||||
return false;
|
||||
|
||||
if (ntype == node_type::none)
|
||||
ntype = elems_[0]->type();
|
||||
|
||||
for (const auto& val : elems_)
|
||||
if (val->type() != ntype)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool array::is_homogeneous(node_type ntype, node * &first_nonmatch) noexcept
|
||||
{
|
||||
if (elems_.empty())
|
||||
{
|
||||
first_nonmatch = {};
|
||||
return false;
|
||||
}
|
||||
if (ntype == node_type::none)
|
||||
ntype = elems_[0]->type();
|
||||
for (const auto& val : elems_)
|
||||
{
|
||||
if (val->type() != ntype)
|
||||
{
|
||||
first_nonmatch = val.get();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool array::is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept
|
||||
{
|
||||
node* fnm = nullptr;
|
||||
const auto result = const_cast<array&>(*this).is_homogeneous(ntype, fnm);
|
||||
first_nonmatch = fnm;
|
||||
return result;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node& array::at(size_t index)
|
||||
{
|
||||
#if TOML_COMPILER_HAS_EXCEPTIONS
|
||||
|
||||
return *elems_.at(index);
|
||||
|
||||
#else
|
||||
|
||||
auto n = get(index);
|
||||
TOML_ASSERT_ASSUME(n && "element index not found in array!");
|
||||
return *n;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void array::reserve(size_t new_capacity)
|
||||
{
|
||||
elems_.reserve(new_capacity);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void array::shrink_to_fit()
|
||||
{
|
||||
elems_.shrink_to_fit();
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void array::truncate(size_t new_size)
|
||||
{
|
||||
if (new_size < elems_.size())
|
||||
elems_.resize(new_size);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
array::iterator array::erase(const_iterator pos) noexcept
|
||||
{
|
||||
return iterator{ elems_.erase(const_vector_iterator{ pos }) };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
array::iterator array::erase(const_iterator first, const_iterator last) noexcept
|
||||
{
|
||||
return iterator{ elems_.erase(const_vector_iterator{ first }, const_vector_iterator{ last }) };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
size_t array::total_leaf_count() const noexcept
|
||||
{
|
||||
size_t leaves{};
|
||||
for (size_t i = 0, e = elems_.size(); i < e; i++)
|
||||
{
|
||||
auto arr = elems_[i]->as_array();
|
||||
leaves += arr ? arr->total_leaf_count() : size_t{ 1 };
|
||||
}
|
||||
return leaves;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void array::flatten_child(array && child, size_t & dest_index) noexcept
|
||||
{
|
||||
for (size_t i = 0, e = child.size(); i < e; i++)
|
||||
{
|
||||
auto type = child.elems_[i]->type();
|
||||
if (type == node_type::array)
|
||||
{
|
||||
array& arr = *reinterpret_cast<array*>(child.elems_[i].get());
|
||||
if (!arr.empty())
|
||||
flatten_child(std::move(arr), dest_index);
|
||||
}
|
||||
else
|
||||
elems_[dest_index++] = std::move(child.elems_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
array& array::flatten()&
|
||||
{
|
||||
if (elems_.empty())
|
||||
return *this;
|
||||
|
||||
bool requires_flattening = false;
|
||||
size_t size_after_flattening = elems_.size();
|
||||
for (size_t i = elems_.size(); i-- > 0u;)
|
||||
{
|
||||
auto arr = elems_[i]->as_array();
|
||||
if (!arr)
|
||||
continue;
|
||||
size_after_flattening--; // discount the array itself
|
||||
const auto leaf_count = arr->total_leaf_count();
|
||||
if (leaf_count > 0u)
|
||||
{
|
||||
requires_flattening = true;
|
||||
size_after_flattening += leaf_count;
|
||||
}
|
||||
else
|
||||
elems_.erase(elems_.cbegin() + static_cast<ptrdiff_t>(i));
|
||||
}
|
||||
|
||||
if (!requires_flattening)
|
||||
return *this;
|
||||
|
||||
elems_.reserve(size_after_flattening);
|
||||
|
||||
size_t i = 0;
|
||||
while (i < elems_.size())
|
||||
{
|
||||
auto arr = elems_[i]->as_array();
|
||||
if (!arr)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
impl::node_ptr arr_storage = std::move(elems_[i]);
|
||||
const auto leaf_count = arr->total_leaf_count();
|
||||
if (leaf_count > 1u)
|
||||
preinsertion_resize(i + 1u, leaf_count - 1u);
|
||||
flatten_child(std::move(*arr), i); // increments i
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
array& array::prune(bool recursive)& noexcept
|
||||
{
|
||||
if (elems_.empty())
|
||||
return *this;
|
||||
|
||||
for (size_t i = elems_.size(); i-- > 0u;)
|
||||
{
|
||||
if (auto arr = elems_[i]->as_array())
|
||||
{
|
||||
if (recursive)
|
||||
arr->prune(true);
|
||||
if (arr->empty())
|
||||
elems_.erase(elems_.cbegin() + static_cast<ptrdiff_t>(i));
|
||||
}
|
||||
else if (auto tbl = elems_[i]->as_table())
|
||||
{
|
||||
if (recursive)
|
||||
tbl->prune(true);
|
||||
if (tbl->empty())
|
||||
elems_.erase(elems_.cbegin() + static_cast<ptrdiff_t>(i));
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void array::pop_back() noexcept
|
||||
{
|
||||
elems_.pop_back();
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void array::clear() noexcept
|
||||
{
|
||||
elems_.clear();
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool TOML_CALLCONV array::equal(const array& lhs, const array& rhs) noexcept
|
||||
{
|
||||
if (&lhs == &rhs)
|
||||
return true;
|
||||
if (lhs.elems_.size() != rhs.elems_.size())
|
||||
return false;
|
||||
for (size_t i = 0, e = lhs.elems_.size(); i < e; i++)
|
||||
{
|
||||
const auto lhs_type = lhs.elems_[i]->type();
|
||||
const node& rhs_ = *rhs.elems_[i];
|
||||
const auto rhs_type = rhs_.type();
|
||||
if (lhs_type != rhs_type)
|
||||
return false;
|
||||
|
||||
const bool equal = lhs.elems_[i]->visit(
|
||||
[&](const auto& lhs_) noexcept
|
||||
{ return lhs_ == *reinterpret_cast<std::remove_reference_t<decltype(lhs_)>*>(&rhs_); });
|
||||
if (!equal)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
97
src/external/toml++/impl/at_path.hpp
vendored
Normal file
97
src/external/toml++/impl/at_path.hpp
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "forward_declarations.hpp"
|
||||
|
||||
/// \cond
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
template <typename T>
|
||||
using parse_path_callback = bool(TOML_CALLCONV*)(void*, T);
|
||||
|
||||
TOML_NODISCARD
|
||||
bool TOML_CALLCONV parse_path(std::string_view,
|
||||
void*,
|
||||
parse_path_callback<std::string_view>,
|
||||
parse_path_callback<size_t>);
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
/// \endcond
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
/// \brief Returns a view of the node matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto config = toml::parse(R"(
|
||||
///
|
||||
/// [foo]
|
||||
/// bar = [ 0, 1, 2, [ 3 ], { kek = 4 } ]
|
||||
///
|
||||
/// )"sv);
|
||||
///
|
||||
/// std::cout << toml::at_path(config, "foo.bar[2]") << "\n";
|
||||
/// std::cout << toml::at_path(config, "foo.bar[3][0]") << "\n";
|
||||
/// std::cout << toml::at_path(config, "foo.bar[4].kek") << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 2
|
||||
/// 3
|
||||
/// 4
|
||||
/// \eout
|
||||
///
|
||||
///
|
||||
/// \note Keys in paths are interpreted literally, so whitespace (or lack thereof) matters:
|
||||
/// \cpp
|
||||
/// toml::at_path(config, "foo.bar") // same as config["foo"]["bar"]
|
||||
/// toml::at_path(config, "foo. bar") // same as config["foo"][" bar"]
|
||||
/// toml::at_path(config, "foo..bar") // same as config["foo"][""]["bar"]
|
||||
/// toml::at_path(config, ".foo.bar") // same as config[""]["foo"]["bar"]
|
||||
/// \ecpp
|
||||
/// <br>
|
||||
/// Additionally, TOML allows '.' (period) characters to appear in keys if they are quoted strings.
|
||||
/// This function makes no allowance for this, instead treating all period characters as sub-table delimiters.
|
||||
/// If you have periods in your table keys, first consider:
|
||||
/// 1. Not doing that
|
||||
/// 2. Using node_view::operator[] instead.
|
||||
///
|
||||
/// \param root The root node from which the path will be traversed.
|
||||
/// \param path The "TOML path" to traverse.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
node_view<node> TOML_CALLCONV at_path(node & root, std::string_view path) noexcept;
|
||||
|
||||
/// \brief Returns a const view of the node matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \see #toml::at_path(node&, std::string_view)
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
node_view<const node> TOML_CALLCONV at_path(const node& root, std::string_view path) noexcept;
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Returns a view of the node matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \see #toml::at_path(node&, std::string_view)
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
node_view<node> TOML_CALLCONV at_path(node & root, std::wstring_view path);
|
||||
|
||||
/// \brief Returns a const view of the node matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \see #toml::at_path(node&, std::string_view)
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
node_view<const node> TOML_CALLCONV at_path(const node& root, std::wstring_view path);
|
||||
|
||||
#endif
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
290
src/external/toml++/impl/at_path.inl
vendored
Normal file
290
src/external/toml++/impl/at_path.inl
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
//# {{
|
||||
#include "preprocessor.hpp"
|
||||
#if !TOML_IMPLEMENTATION
|
||||
#error This is an implementation-only header.
|
||||
#endif
|
||||
//# }}
|
||||
|
||||
#include "at_path.hpp"
|
||||
#include "array.hpp"
|
||||
#include "table.hpp"
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#if TOML_INT_CHARCONV
|
||||
#include <charconv>
|
||||
#else
|
||||
#include <sstream>
|
||||
#endif
|
||||
TOML_ENABLE_WARNINGS;
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool TOML_CALLCONV parse_path(const std::string_view path,
|
||||
void* const data,
|
||||
const parse_path_callback<std::string_view> on_key,
|
||||
const parse_path_callback<size_t> on_index)
|
||||
{
|
||||
// a blank string is a valid path; it's just one component representing the "" key
|
||||
if (path.empty())
|
||||
return on_key(data, ""sv);
|
||||
|
||||
size_t pos = 0;
|
||||
const auto end = path.length();
|
||||
bool prev_was_array_indexer = false;
|
||||
bool prev_was_dot = true; // invisible root 'dot'
|
||||
|
||||
while (pos < end)
|
||||
{
|
||||
// start of an array indexer
|
||||
if (path[pos] == '[')
|
||||
{
|
||||
// find first digit in index
|
||||
size_t index_start = pos + 1u;
|
||||
while (true)
|
||||
{
|
||||
if TOML_UNLIKELY(index_start >= path.length())
|
||||
return false;
|
||||
|
||||
const auto c = path[index_start];
|
||||
if TOML_LIKELY(c >= '0' && c <= '9')
|
||||
break;
|
||||
else if (c == ' ' || c == '\t')
|
||||
index_start++;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
TOML_ASSERT(path[index_start] >= '0');
|
||||
TOML_ASSERT(path[index_start] <= '9');
|
||||
|
||||
// find end of index (first non-digit character)
|
||||
size_t index_end = index_start + 1u;
|
||||
while (true)
|
||||
{
|
||||
// if an array indexer is missing the trailing ']' at the end of the string, permissively accept it
|
||||
if TOML_UNLIKELY(index_end >= path.length())
|
||||
break;
|
||||
|
||||
const auto c = path[index_end];
|
||||
if (c >= '0' && c <= '9')
|
||||
index_end++;
|
||||
else if (c == ']' || c == ' ' || c == '\t' || c == '.' || c == '[')
|
||||
break;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
TOML_ASSERT(path[index_end - 1u] >= '0');
|
||||
TOML_ASSERT(path[index_end - 1u] <= '9');
|
||||
|
||||
// move pos to after indexer (char after closing ']' or permissively EOL/subkey '.'/next opening '[')
|
||||
pos = index_end;
|
||||
while (true)
|
||||
{
|
||||
if TOML_UNLIKELY(pos >= path.length())
|
||||
break;
|
||||
|
||||
const auto c = path[pos];
|
||||
if (c == ']')
|
||||
{
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
else if TOML_UNLIKELY(c == '.' || c == '[')
|
||||
break;
|
||||
else if (c == '\t' || c == ' ')
|
||||
pos++;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// get array index substring
|
||||
auto index_str = path.substr(index_start, index_end - index_start);
|
||||
|
||||
// parse the actual array index to an integer type
|
||||
size_t index;
|
||||
if (index_str.length() == 1u)
|
||||
index = static_cast<size_t>(index_str[0] - '0');
|
||||
else
|
||||
{
|
||||
#if TOML_INT_CHARCONV
|
||||
|
||||
auto fc_result = std::from_chars(index_str.data(), index_str.data() + index_str.length(), index);
|
||||
if (fc_result.ec != std::errc{})
|
||||
return false;
|
||||
|
||||
#else
|
||||
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss.write(index_str.data(), static_cast<std::streamsize>(index_str.length()));
|
||||
if (!(ss >> index))
|
||||
return false;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
prev_was_dot = false;
|
||||
prev_was_array_indexer = true;
|
||||
|
||||
if (!on_index(data, index))
|
||||
return false;
|
||||
}
|
||||
|
||||
// start of a new table child
|
||||
else if (path[pos] == '.')
|
||||
{
|
||||
// a dot immediately following another dot (or at the beginning of the string) is as if we'd asked
|
||||
// for an empty child in between, e.g.
|
||||
//
|
||||
// foo..bar
|
||||
//
|
||||
// is equivalent to
|
||||
//
|
||||
// "foo".""."bar"
|
||||
//
|
||||
if (prev_was_dot && !on_key(data, ""sv))
|
||||
return false;
|
||||
|
||||
pos++;
|
||||
prev_was_dot = true;
|
||||
prev_was_array_indexer = false;
|
||||
}
|
||||
|
||||
// an errant closing ']'
|
||||
else if TOML_UNLIKELY(path[pos] == ']')
|
||||
return false;
|
||||
|
||||
// some regular subkey
|
||||
else
|
||||
{
|
||||
const auto subkey_start = pos;
|
||||
const auto subkey_len =
|
||||
impl::min(path.find_first_of(".[]"sv, subkey_start + 1u), path.length()) - subkey_start;
|
||||
const auto subkey = path.substr(subkey_start, subkey_len);
|
||||
|
||||
// a regular subkey segment immediately after an array indexer is OK if it was all whitespace, e.g.:
|
||||
//
|
||||
// "foo[0] .bar"
|
||||
// ^^ skip this
|
||||
//
|
||||
// otherwise its an error (since it would have to be preceeded by a dot)
|
||||
if (prev_was_array_indexer)
|
||||
{
|
||||
auto non_ws = subkey.find_first_not_of(" \t");
|
||||
if (non_ws == std::string_view::npos)
|
||||
{
|
||||
pos += subkey_len;
|
||||
prev_was_dot = false;
|
||||
prev_was_array_indexer = false;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
pos += subkey_len;
|
||||
prev_was_dot = false;
|
||||
prev_was_array_indexer = false;
|
||||
|
||||
if (!on_key(data, subkey))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Last character was a '.', which implies an empty string key at the end of the path
|
||||
if (prev_was_dot && !on_key(data, ""sv))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<node> TOML_CALLCONV at_path(node & root, std::string_view path) noexcept
|
||||
{
|
||||
// early-exit sanity-checks
|
||||
if (root.is_value())
|
||||
return {};
|
||||
if (auto tbl = root.as_table(); tbl && tbl->empty())
|
||||
return {};
|
||||
if (auto arr = root.as_array(); arr && arr->empty())
|
||||
return {};
|
||||
|
||||
node* current = &root;
|
||||
|
||||
static constexpr auto on_key = [](void* data, std::string_view key) noexcept -> bool
|
||||
{
|
||||
auto& curr = *static_cast<node**>(data);
|
||||
TOML_ASSERT_ASSUME(curr);
|
||||
|
||||
const auto current_table = curr->as<table>();
|
||||
if (!current_table)
|
||||
return false;
|
||||
|
||||
curr = current_table->get(key);
|
||||
return curr != nullptr;
|
||||
};
|
||||
|
||||
static constexpr auto on_index = [](void* data, size_t index) noexcept -> bool
|
||||
{
|
||||
auto& curr = *static_cast<node**>(data);
|
||||
TOML_ASSERT_ASSUME(curr);
|
||||
|
||||
const auto current_array = curr->as<array>();
|
||||
if (!current_array)
|
||||
return false;
|
||||
|
||||
curr = current_array->get(index);
|
||||
return curr != nullptr;
|
||||
};
|
||||
|
||||
if (!impl::parse_path(path, ¤t, on_key, on_index))
|
||||
current = nullptr;
|
||||
|
||||
return node_view{ current };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<const node> TOML_CALLCONV at_path(const node& root, std::string_view path) noexcept
|
||||
{
|
||||
return node_view<const node>{ at_path(const_cast<node&>(root), path).node() };
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<node> TOML_CALLCONV at_path(node & root, std::wstring_view path)
|
||||
{
|
||||
// these are the same top-level checks from the narrow-string version;
|
||||
// they're hoisted up here to avoid doing the wide -> narrow conversion where it would not be necessary
|
||||
// (avoids an allocation)
|
||||
if (root.is_value())
|
||||
return {};
|
||||
if (auto tbl = root.as_table(); tbl && tbl->empty())
|
||||
return {};
|
||||
if (auto arr = root.as_array(); arr && arr->empty())
|
||||
return {};
|
||||
|
||||
return at_path(root, impl::narrow(path));
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<const node> TOML_CALLCONV at_path(const node& root, std::wstring_view path)
|
||||
{
|
||||
return node_view<const node>{ at_path(const_cast<node&>(root), path).node() };
|
||||
}
|
||||
|
||||
#endif // TOML_ENABLE_WINDOWS_COMPAT
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
469
src/external/toml++/impl/date_time.hpp
vendored
Normal file
469
src/external/toml++/impl/date_time.hpp
vendored
Normal file
@@ -0,0 +1,469 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "forward_declarations.hpp"
|
||||
#include "print_to_stream.hpp"
|
||||
#include "std_optional.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
/// \brief A local date.
|
||||
struct TOML_TRIVIAL_ABI date
|
||||
{
|
||||
/// \brief The year component.
|
||||
uint16_t year;
|
||||
|
||||
/// \brief The month component, from 1 - 12.
|
||||
uint8_t month;
|
||||
|
||||
/// \brief The day component, from 1 - 31.
|
||||
uint8_t day;
|
||||
|
||||
/// \brief Default constructor. Does not initialize the members.
|
||||
TOML_NODISCARD_CTOR
|
||||
date() noexcept = default;
|
||||
|
||||
/// \brief Constructs a date from individual date component values.
|
||||
TOML_CONSTRAINED_TEMPLATE((impl::all_integral<Y, M, D>), typename Y, typename M, typename D)
|
||||
TOML_NODISCARD_CTOR
|
||||
constexpr date(Y y, M m, D d) noexcept //
|
||||
: year{ static_cast<uint16_t>(y) },
|
||||
month{ static_cast<uint8_t>(m) },
|
||||
day{ static_cast<uint8_t>(d) }
|
||||
{}
|
||||
|
||||
/// \brief Equality operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator==(const date& lhs, const date& rhs) noexcept
|
||||
{
|
||||
return lhs.year == rhs.year //
|
||||
&& lhs.month == rhs.month //
|
||||
&& lhs.day == rhs.day;
|
||||
}
|
||||
|
||||
/// \brief Inequality operator.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend constexpr bool operator!=(const date& lhs, const date& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
/// \cond
|
||||
|
||||
TOML_PURE_GETTER
|
||||
static constexpr uint32_t pack(const date& d) noexcept
|
||||
{
|
||||
return (static_cast<uint32_t>(d.year) << 16) | (static_cast<uint32_t>(d.month) << 8)
|
||||
| static_cast<uint32_t>(d.day);
|
||||
}
|
||||
|
||||
/// \endcond
|
||||
|
||||
public:
|
||||
/// \brief Less-than operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator<(const date& lhs, const date& rhs) noexcept
|
||||
{
|
||||
return pack(lhs) < pack(rhs);
|
||||
}
|
||||
|
||||
/// \brief Less-than-or-equal-to operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator<=(const date& lhs, const date& rhs) noexcept
|
||||
{
|
||||
return pack(lhs) <= pack(rhs);
|
||||
}
|
||||
|
||||
/// \brief Greater-than operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator>(const date& lhs, const date& rhs) noexcept
|
||||
{
|
||||
return pack(lhs) > pack(rhs);
|
||||
}
|
||||
|
||||
/// \brief Greater-than-or-equal-to operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator>=(const date& lhs, const date& rhs) noexcept
|
||||
{
|
||||
return pack(lhs) >= pack(rhs);
|
||||
}
|
||||
|
||||
/// \brief Prints a date out to a stream as `YYYY-MM-DD` (per RFC 3339).
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// std::cout << toml::date{ 1987, 3, 16 } << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 1987-03-16
|
||||
/// \eout
|
||||
friend std::ostream& operator<<(std::ostream& lhs, const date& rhs)
|
||||
{
|
||||
impl::print_to_stream(lhs, rhs);
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief A local time-of-day.
|
||||
struct TOML_TRIVIAL_ABI time
|
||||
{
|
||||
/// \brief The hour component, from 0 - 23.
|
||||
uint8_t hour;
|
||||
|
||||
/// \brief The minute component, from 0 - 59.
|
||||
uint8_t minute;
|
||||
|
||||
/// \brief The second component, from 0 - 59.
|
||||
uint8_t second;
|
||||
|
||||
/// \brief The fractional nanoseconds component, from 0 - 999999999.
|
||||
uint32_t nanosecond;
|
||||
|
||||
/// \brief Default constructor. Does not initialize the members.
|
||||
TOML_NODISCARD_CTOR
|
||||
time() noexcept = default;
|
||||
|
||||
/// \brief Constructs a time from individual time component values.
|
||||
TOML_CONSTRAINED_TEMPLATE((impl::all_integral<H, M, S, NS>),
|
||||
typename H,
|
||||
typename M,
|
||||
typename S = uint8_t,
|
||||
typename NS = uint32_t)
|
||||
TOML_NODISCARD_CTOR
|
||||
constexpr time(H h, M m, S s = S{}, NS ns = NS{}) noexcept //
|
||||
: hour{ static_cast<uint8_t>(h) },
|
||||
minute{ static_cast<uint8_t>(m) },
|
||||
second{ static_cast<uint8_t>(s) },
|
||||
nanosecond{ static_cast<uint32_t>(ns) }
|
||||
{}
|
||||
|
||||
/// \brief Equality operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator==(const time& lhs, const time& rhs) noexcept
|
||||
{
|
||||
return lhs.hour == rhs.hour //
|
||||
&& lhs.minute == rhs.minute //
|
||||
&& lhs.second == rhs.second //
|
||||
&& lhs.nanosecond == rhs.nanosecond;
|
||||
}
|
||||
|
||||
/// \brief Inequality operator.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend constexpr bool operator!=(const time& lhs, const time& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
/// \cond
|
||||
|
||||
TOML_PURE_GETTER
|
||||
static constexpr uint64_t pack(const time& t) noexcept
|
||||
{
|
||||
return static_cast<uint64_t>(t.hour) << 48 | static_cast<uint64_t>(t.minute) << 40
|
||||
| static_cast<uint64_t>(t.second) << 32 | static_cast<uint64_t>(t.nanosecond);
|
||||
}
|
||||
|
||||
/// \endcond
|
||||
|
||||
public:
|
||||
/// \brief Less-than operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator<(const time& lhs, const time& rhs) noexcept
|
||||
{
|
||||
return pack(lhs) < pack(rhs);
|
||||
}
|
||||
|
||||
/// \brief Less-than-or-equal-to operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator<=(const time& lhs, const time& rhs) noexcept
|
||||
{
|
||||
return pack(lhs) <= pack(rhs);
|
||||
}
|
||||
|
||||
/// \brief Greater-than operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator>(const time& lhs, const time& rhs) noexcept
|
||||
{
|
||||
return pack(lhs) > pack(rhs);
|
||||
}
|
||||
|
||||
/// \brief Greater-than-or-equal-to operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator>=(const time& lhs, const time& rhs) noexcept
|
||||
{
|
||||
return pack(lhs) >= pack(rhs);
|
||||
}
|
||||
|
||||
/// \brief Prints a time out to a stream as `HH:MM:SS.FFFFFF` (per RFC 3339).
|
||||
/// \detail \cpp
|
||||
/// std::cout << toml::time{ 10, 20, 34 } << "\n";
|
||||
/// std::cout << toml::time{ 10, 20, 34, 500000000 } << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 10:20:34
|
||||
/// 10:20:34.5
|
||||
/// \eout
|
||||
friend std::ostream& operator<<(std::ostream& lhs, const time& rhs)
|
||||
{
|
||||
impl::print_to_stream(lhs, rhs);
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief A timezone offset.
|
||||
struct TOML_TRIVIAL_ABI time_offset
|
||||
{
|
||||
/// \brief Offset from UTC+0, in minutes.
|
||||
int16_t minutes;
|
||||
|
||||
/// \brief Default constructor. Does not initialize the members.
|
||||
TOML_NODISCARD_CTOR
|
||||
time_offset() noexcept = default;
|
||||
|
||||
/// \brief Constructs a timezone offset from individual hour and minute totals.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// std::cout << toml::time_offset{ 2, 30 } << "\n";
|
||||
/// std::cout << toml::time_offset{ -2, 30 } << "\n";
|
||||
/// std::cout << toml::time_offset{ -2, -30 } << "\n";
|
||||
/// std::cout << toml::time_offset{ 0, 0 } << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// +02:30
|
||||
/// -01:30
|
||||
/// -02:30
|
||||
/// Z
|
||||
/// \eout
|
||||
///
|
||||
/// \tparam H An integral type.
|
||||
/// \tparam M An integral type.
|
||||
///
|
||||
/// \param h The total hours.
|
||||
/// \param m The total minutes.
|
||||
TOML_CONSTRAINED_TEMPLATE((impl::all_integral<H, M>), typename H, typename M)
|
||||
TOML_NODISCARD_CTOR
|
||||
constexpr time_offset(H h, M m) noexcept //
|
||||
: minutes{ static_cast<int16_t>(static_cast<impl::common_signed_type<H, M>>(h)
|
||||
* impl::common_signed_type<H, M>{ 60 }
|
||||
+ static_cast<impl::common_signed_type<H, M>>(m)) }
|
||||
{}
|
||||
|
||||
/// \brief Equality operator.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend constexpr bool operator==(time_offset lhs, time_offset rhs) noexcept
|
||||
{
|
||||
return lhs.minutes == rhs.minutes;
|
||||
}
|
||||
|
||||
/// \brief Inequality operator.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend constexpr bool operator!=(time_offset lhs, time_offset rhs) noexcept
|
||||
{
|
||||
return lhs.minutes != rhs.minutes;
|
||||
}
|
||||
|
||||
/// \brief Less-than operator.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend constexpr bool operator<(time_offset lhs, time_offset rhs) noexcept
|
||||
{
|
||||
return lhs.minutes < rhs.minutes;
|
||||
}
|
||||
|
||||
/// \brief Less-than-or-equal-to operator.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend constexpr bool operator<=(time_offset lhs, time_offset rhs) noexcept
|
||||
{
|
||||
return lhs.minutes <= rhs.minutes;
|
||||
}
|
||||
|
||||
/// \brief Greater-than operator.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend constexpr bool operator>(time_offset lhs, time_offset rhs) noexcept
|
||||
{
|
||||
return lhs.minutes > rhs.minutes;
|
||||
}
|
||||
|
||||
/// \brief Greater-than-or-equal-to operator.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend constexpr bool operator>=(time_offset lhs, time_offset rhs) noexcept
|
||||
{
|
||||
return lhs.minutes >= rhs.minutes;
|
||||
}
|
||||
|
||||
/// \brief Prints a time_offset out to a stream as `+-HH:MM or Z` (per RFC 3339).
|
||||
/// \detail \cpp
|
||||
/// std::cout << toml::time_offset{ 2, 30 } << "\n";
|
||||
/// std::cout << toml::time_offset{ 2, -30 } << "\n";
|
||||
/// std::cout << toml::time_offset{} << "\n";
|
||||
/// std::cout << toml::time_offset{ -2, 30 } << "\n";
|
||||
/// std::cout << toml::time_offset{ -2, -30 } << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// +02:30
|
||||
/// +01:30
|
||||
/// Z
|
||||
/// -01:30
|
||||
/// -02:30
|
||||
/// \eout
|
||||
friend std::ostream& operator<<(std::ostream& lhs, const time_offset& rhs)
|
||||
{
|
||||
impl::print_to_stream(lhs, rhs);
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt);
|
||||
|
||||
/// \brief A date-time.
|
||||
struct date_time
|
||||
{
|
||||
/// \brief The date component.
|
||||
toml::date date;
|
||||
|
||||
/// \brief The time component.
|
||||
toml::time time;
|
||||
|
||||
/// \brief The timezone offset component.
|
||||
///
|
||||
/// \remarks The date_time is said to be 'local' if the offset is empty.
|
||||
optional<toml::time_offset> offset;
|
||||
|
||||
/// \brief Default constructor. Does not initialize the members.
|
||||
TOML_NODISCARD_CTOR
|
||||
date_time() noexcept = default;
|
||||
|
||||
/// \brief Constructs a local date-time.
|
||||
///
|
||||
/// \param d The date component.
|
||||
/// \param t The time component.
|
||||
TOML_NODISCARD_CTOR
|
||||
constexpr date_time(const toml::date& d, const toml::time& t) noexcept //
|
||||
: date{ d },
|
||||
time{ t },
|
||||
offset{} // TINAE - icc bugfix
|
||||
{}
|
||||
|
||||
/// \brief Constructs a local date-time.
|
||||
///
|
||||
/// \param d The date component.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit constexpr date_time(const toml::date& d) noexcept //
|
||||
: date{ d },
|
||||
time{},
|
||||
offset{} // TINAE - icc bugfix
|
||||
{}
|
||||
|
||||
/// \brief Constructs a local date-time.
|
||||
///
|
||||
/// \param t The time component.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit constexpr date_time(const toml::time& t) noexcept //
|
||||
: date{},
|
||||
time{ t },
|
||||
offset{} // TINAE - icc bugfix
|
||||
{}
|
||||
|
||||
/// \brief Constructs an offset date-time.
|
||||
///
|
||||
/// \param d The date component.
|
||||
/// \param t The time component.
|
||||
/// \param off The timezone offset.
|
||||
TOML_NODISCARD_CTOR
|
||||
constexpr date_time(const toml::date& d, const toml::time& t, const toml::time_offset& off) noexcept
|
||||
: date{ d },
|
||||
time{ t },
|
||||
offset{ off }
|
||||
{}
|
||||
|
||||
/// \brief Returns true if this date_time does not contain timezone offset information.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
constexpr bool is_local() const noexcept
|
||||
{
|
||||
return !offset.has_value();
|
||||
}
|
||||
|
||||
/// \brief Equality operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator==(const date_time& lhs, const date_time& rhs) noexcept
|
||||
{
|
||||
return lhs.date == rhs.date //
|
||||
&& lhs.time == rhs.time //
|
||||
&& lhs.offset == rhs.offset;
|
||||
}
|
||||
|
||||
/// \brief Inequality operator.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend constexpr bool operator!=(const date_time& lhs, const date_time& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/// \brief Less-than operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator<(const date_time& lhs, const date_time& rhs) noexcept
|
||||
{
|
||||
if (lhs.date != rhs.date)
|
||||
return lhs.date < rhs.date;
|
||||
if (lhs.time != rhs.time)
|
||||
return lhs.time < rhs.time;
|
||||
return lhs.offset < rhs.offset;
|
||||
}
|
||||
|
||||
/// \brief Less-than-or-equal-to operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator<=(const date_time& lhs, const date_time& rhs) noexcept
|
||||
{
|
||||
if (lhs.date != rhs.date)
|
||||
return lhs.date < rhs.date;
|
||||
if (lhs.time != rhs.time)
|
||||
return lhs.time < rhs.time;
|
||||
return lhs.offset <= rhs.offset;
|
||||
}
|
||||
|
||||
/// \brief Greater-than operator.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend constexpr bool operator>(const date_time& lhs, const date_time& rhs) noexcept
|
||||
{
|
||||
return !(lhs <= rhs);
|
||||
}
|
||||
|
||||
/// \brief Greater-than-or-equal-to operator.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend constexpr bool operator>=(const date_time& lhs, const date_time& rhs) noexcept
|
||||
{
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
/// \brief Prints a date_time out to a stream in RFC 3339 format.
|
||||
/// \detail \cpp
|
||||
/// std::cout << toml::date_time{ { 1987, 3, 16 }, { 10, 20, 34 } } << "\n";
|
||||
/// std::cout << toml::date_time{ { 1987, 3, 16 }, { 10, 20, 34 }, { -2, -30 } } << "\n";
|
||||
/// std::cout << toml::date_time{ { 1987, 3, 16 }, { 10, 20, 34 }, {} } << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 1987-03-16T10:20:34
|
||||
/// 1987-03-16T10:20:34-02:30
|
||||
/// 1987-03-16T10:20:34Z
|
||||
/// \eout
|
||||
friend std::ostream& operator<<(std::ostream& lhs, const date_time& rhs)
|
||||
{
|
||||
impl::print_to_stream(lhs, rhs);
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
TOML_ABI_NAMESPACE_END; // TOML_HAS_CUSTOM_OPTIONAL_TYPE
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
200
src/external/toml++/impl/formatter.hpp
vendored
Normal file
200
src/external/toml++/impl/formatter.hpp
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
#if TOML_ENABLE_FORMATTERS
|
||||
|
||||
#include "forward_declarations.hpp"
|
||||
#include "print_to_stream.hpp"
|
||||
#include "header_start.hpp"
|
||||
/// \cond
|
||||
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
struct formatter_constants
|
||||
{
|
||||
format_flags mandatory_flags;
|
||||
format_flags ignored_flags;
|
||||
|
||||
std::string_view float_pos_inf;
|
||||
std::string_view float_neg_inf;
|
||||
std::string_view float_nan;
|
||||
|
||||
std::string_view bool_true;
|
||||
std::string_view bool_false;
|
||||
};
|
||||
|
||||
struct formatter_config
|
||||
{
|
||||
format_flags flags;
|
||||
std::string_view indent;
|
||||
};
|
||||
|
||||
class TOML_EXPORTED_CLASS formatter
|
||||
{
|
||||
private:
|
||||
const node* source_;
|
||||
#if TOML_ENABLE_PARSER && !TOML_EXCEPTIONS
|
||||
const parse_result* result_;
|
||||
#endif
|
||||
const formatter_constants* constants_;
|
||||
formatter_config config_;
|
||||
size_t indent_columns_;
|
||||
format_flags int_format_mask_;
|
||||
std::ostream* stream_; //
|
||||
int indent_; // these are set in attach()
|
||||
bool naked_newline_; //
|
||||
|
||||
protected:
|
||||
TOML_PURE_INLINE_GETTER
|
||||
const node& source() const noexcept
|
||||
{
|
||||
return *source_;
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
std::ostream& stream() const noexcept
|
||||
{
|
||||
return *stream_;
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
int indent() const noexcept
|
||||
{
|
||||
return indent_;
|
||||
}
|
||||
|
||||
void indent(int level) noexcept
|
||||
{
|
||||
indent_ = level;
|
||||
}
|
||||
|
||||
void increase_indent() noexcept
|
||||
{
|
||||
indent_++;
|
||||
}
|
||||
|
||||
void decrease_indent() noexcept
|
||||
{
|
||||
indent_--;
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
size_t indent_columns() const noexcept
|
||||
{
|
||||
return indent_columns_;
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
bool indent_array_elements() const noexcept
|
||||
{
|
||||
return !!(config_.flags & format_flags::indent_array_elements);
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
bool indent_sub_tables() const noexcept
|
||||
{
|
||||
return !!(config_.flags & format_flags::indent_sub_tables);
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
bool literal_strings_allowed() const noexcept
|
||||
{
|
||||
return !!(config_.flags & format_flags::allow_literal_strings);
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
bool multi_line_strings_allowed() const noexcept
|
||||
{
|
||||
return !!(config_.flags & format_flags::allow_multi_line_strings);
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
bool real_tabs_in_strings_allowed() const noexcept
|
||||
{
|
||||
return !!(config_.flags & format_flags::allow_real_tabs_in_strings);
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
bool unicode_strings_allowed() const noexcept
|
||||
{
|
||||
return !!(config_.flags & format_flags::allow_unicode_strings);
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
bool terse_kvps() const noexcept
|
||||
{
|
||||
return !!(config_.flags & format_flags::terse_key_value_pairs);
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
bool force_multiline_arrays() const noexcept
|
||||
{
|
||||
return !!(config_.flags & format_flags::force_multiline_arrays);
|
||||
}
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void attach(std::ostream& stream) noexcept;
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void detach() noexcept;
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print_newline(bool force = false);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print_indent();
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print_unformatted(char);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print_unformatted(std::string_view);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print_string(std::string_view str,
|
||||
bool allow_multi_line = true,
|
||||
bool allow_bare = false,
|
||||
bool allow_literal_whitespace = true);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const value<std::string>&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const value<int64_t>&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const value<double>&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const value<bool>&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const value<date>&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const value<time>&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const value<date_time>&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print_value(const node&, node_type);
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
bool dump_failed_parse_result();
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
formatter(const node*, const parse_result*, const formatter_constants&, const formatter_config&) noexcept;
|
||||
};
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
|
||||
/// \endcond
|
||||
#include "header_end.hpp"
|
||||
#endif // TOML_ENABLE_FORMATTERS
|
||||
523
src/external/toml++/impl/formatter.inl
vendored
Normal file
523
src/external/toml++/impl/formatter.inl
vendored
Normal file
@@ -0,0 +1,523 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
//# {{
|
||||
#if !TOML_IMPLEMENTATION
|
||||
#error This is an implementation-only header.
|
||||
#endif
|
||||
//# }}
|
||||
#if TOML_ENABLE_FORMATTERS
|
||||
|
||||
#include "formatter.hpp"
|
||||
#include "print_to_stream.hpp"
|
||||
#include "value.hpp"
|
||||
#include "table.hpp"
|
||||
#include "array.hpp"
|
||||
#include "unicode.hpp"
|
||||
#include "parse_result.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
enum class TOML_CLOSED_FLAGS_ENUM formatted_string_traits : unsigned
|
||||
{
|
||||
none,
|
||||
line_breaks = 1u << 0, // \n
|
||||
tabs = 1u << 1, // \t
|
||||
control_chars = 1u << 2, // also includes non-ascii vertical whitespace
|
||||
single_quotes = 1u << 3,
|
||||
non_bare = 1u << 4, // anything not satisfying "is bare key character"
|
||||
non_ascii = 1u << 5, // any codepoint >= 128
|
||||
|
||||
all = (non_ascii << 1u) - 1u
|
||||
};
|
||||
TOML_MAKE_FLAGS(formatted_string_traits);
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
formatter::formatter(const node* source_node,
|
||||
const parse_result* source_pr,
|
||||
const formatter_constants& constants,
|
||||
const formatter_config& config) noexcept //
|
||||
#if TOML_ENABLE_PARSER && !TOML_EXCEPTIONS
|
||||
: source_{ source_pr && *source_pr ? &source_pr->table() : source_node },
|
||||
result_{ source_pr },
|
||||
#else
|
||||
: source_{ source_pr ? source_pr : source_node },
|
||||
#endif
|
||||
constants_{ &constants },
|
||||
config_{ config }
|
||||
{
|
||||
TOML_ASSERT_ASSUME(source_);
|
||||
|
||||
config_.flags = (config_.flags | constants_->mandatory_flags) & ~constants_->ignored_flags;
|
||||
|
||||
indent_columns_ = {};
|
||||
for (auto c : config_.indent)
|
||||
indent_columns_ += c == '\t' ? 4u : 1u;
|
||||
|
||||
int_format_mask_ = config_.flags
|
||||
& (format_flags::allow_binary_integers | format_flags::allow_octal_integers
|
||||
| format_flags::allow_hexadecimal_integers);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::attach(std::ostream & stream) noexcept
|
||||
{
|
||||
indent_ = {};
|
||||
naked_newline_ = true;
|
||||
stream_ = &stream;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::detach() noexcept
|
||||
{
|
||||
stream_ = nullptr;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print_newline(bool force)
|
||||
{
|
||||
if (!naked_newline_ || force)
|
||||
{
|
||||
print_to_stream(*stream_, '\n');
|
||||
naked_newline_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print_indent()
|
||||
{
|
||||
for (int i = 0; i < indent_; i++)
|
||||
{
|
||||
print_to_stream(*stream_, config_.indent);
|
||||
naked_newline_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print_unformatted(char c)
|
||||
{
|
||||
print_to_stream(*stream_, c);
|
||||
naked_newline_ = false;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print_unformatted(std::string_view str)
|
||||
{
|
||||
print_to_stream(*stream_, str);
|
||||
naked_newline_ = false;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print_string(std::string_view str,
|
||||
bool allow_multi_line,
|
||||
bool allow_bare,
|
||||
bool allow_literal_whitespace)
|
||||
{
|
||||
if (str.empty())
|
||||
{
|
||||
print_unformatted(literal_strings_allowed() ? "''"sv : "\"\""sv);
|
||||
return;
|
||||
}
|
||||
|
||||
// pre-scan the string to determine how we should output it
|
||||
formatted_string_traits traits = {};
|
||||
|
||||
if (!allow_bare)
|
||||
traits |= formatted_string_traits::non_bare;
|
||||
bool unicode_allowed = unicode_strings_allowed();
|
||||
|
||||
// ascii fast path
|
||||
if (is_ascii(str.data(), str.length()))
|
||||
{
|
||||
for (auto c : str)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\n': traits |= formatted_string_traits::line_breaks; break;
|
||||
case '\t': traits |= formatted_string_traits::tabs; break;
|
||||
case '\'': traits |= formatted_string_traits::single_quotes; break;
|
||||
default:
|
||||
{
|
||||
if TOML_UNLIKELY(is_control_character(c))
|
||||
traits |= formatted_string_traits::control_chars;
|
||||
|
||||
if (!is_ascii_bare_key_character(static_cast<char32_t>(c)))
|
||||
traits |= formatted_string_traits::non_bare;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr auto all_ascii_traits =
|
||||
formatted_string_traits::all & ~formatted_string_traits::non_ascii;
|
||||
if (traits == all_ascii_traits)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// unicode slow path
|
||||
else
|
||||
{
|
||||
traits |= formatted_string_traits::non_ascii;
|
||||
utf8_decoder decoder;
|
||||
|
||||
// if the unicode is malformed just treat the string as a single-line non-literal and
|
||||
// escape all non-ascii characters (to ensure round-tripping and help with diagnostics)
|
||||
const auto bad_unicode = [&]() noexcept
|
||||
{
|
||||
traits &= ~formatted_string_traits::line_breaks;
|
||||
traits |= formatted_string_traits::control_chars | formatted_string_traits::non_bare;
|
||||
unicode_allowed = false;
|
||||
};
|
||||
|
||||
for (auto c : str)
|
||||
{
|
||||
decoder(c);
|
||||
|
||||
if TOML_UNLIKELY(decoder.error())
|
||||
{
|
||||
bad_unicode();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!decoder.has_code_point())
|
||||
continue;
|
||||
|
||||
switch (decoder.codepoint)
|
||||
{
|
||||
case U'\n': traits |= formatted_string_traits::line_breaks; break;
|
||||
case U'\t': traits |= formatted_string_traits::tabs; break;
|
||||
case U'\'': traits |= formatted_string_traits::single_quotes; break;
|
||||
default:
|
||||
{
|
||||
if TOML_UNLIKELY(is_control_character(decoder.codepoint)
|
||||
|| is_non_ascii_vertical_whitespace(decoder.codepoint))
|
||||
traits |= formatted_string_traits::control_chars;
|
||||
|
||||
if (!is_bare_key_character(decoder.codepoint))
|
||||
traits |= formatted_string_traits::non_bare;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (decoder.needs_more_input())
|
||||
bad_unicode();
|
||||
}
|
||||
|
||||
// strings with line breaks, tabs, and single-quotes can't be bare
|
||||
if (!!(traits
|
||||
& (formatted_string_traits::line_breaks | formatted_string_traits::tabs
|
||||
| formatted_string_traits::single_quotes)))
|
||||
traits |= formatted_string_traits::non_bare;
|
||||
|
||||
// if the string meets the requirements of being 'bare' we can emit a bare string
|
||||
// (bare strings are composed of letters and numbers; no whitespace, control chars, quotes, etc)
|
||||
if (!(traits & formatted_string_traits::non_bare)
|
||||
&& (!(traits & formatted_string_traits::non_ascii) || unicode_allowed))
|
||||
{
|
||||
print_unformatted(str);
|
||||
return;
|
||||
}
|
||||
const auto real_tabs_allowed = allow_literal_whitespace && real_tabs_in_strings_allowed();
|
||||
|
||||
// determine if this should be a multi-line string (triple-quotes)
|
||||
const auto multi_line = allow_literal_whitespace //
|
||||
&& allow_multi_line //
|
||||
&& multi_line_strings_allowed() //
|
||||
&& !!(traits & formatted_string_traits::line_breaks);
|
||||
|
||||
// determine if this should be a literal string (single-quotes with no escaping)
|
||||
const auto literal = literal_strings_allowed() //
|
||||
&& !(traits & formatted_string_traits::control_chars) //
|
||||
&& (!(traits & formatted_string_traits::single_quotes) || multi_line) //
|
||||
&& (!(traits & formatted_string_traits::tabs) || real_tabs_allowed) //
|
||||
&& (!(traits & formatted_string_traits::line_breaks) || multi_line) //
|
||||
&& (!(traits & formatted_string_traits::non_ascii) || unicode_allowed);
|
||||
|
||||
// literal strings (single quotes, no escape codes)
|
||||
if (literal)
|
||||
{
|
||||
const auto quot = multi_line ? R"(''')"sv : R"(')"sv;
|
||||
print_unformatted(quot);
|
||||
print_unformatted(str);
|
||||
print_unformatted(quot);
|
||||
return;
|
||||
}
|
||||
|
||||
// anything from here down is a non-literal string, so requires iteration and escaping.
|
||||
print_unformatted(multi_line ? R"(""")"sv : R"(")"sv);
|
||||
|
||||
// ascii fast path
|
||||
if (!(traits & formatted_string_traits::non_ascii))
|
||||
{
|
||||
for (auto c : str)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"': print_to_stream(*stream_, R"(\")"sv); break;
|
||||
case '\\': print_to_stream(*stream_, R"(\\)"sv); break;
|
||||
case '\x7F': print_to_stream(*stream_, R"(\u007F)"sv); break;
|
||||
case '\t': print_to_stream(*stream_, real_tabs_allowed ? "\t"sv : R"(\t)"sv); break;
|
||||
case '\n': print_to_stream(*stream_, multi_line ? "\n"sv : R"(\n)"sv); break;
|
||||
default:
|
||||
{
|
||||
// control characters from lookup table
|
||||
if TOML_UNLIKELY(c >= '\x00' && c <= '\x1F')
|
||||
print_to_stream(*stream_, control_char_escapes[c]);
|
||||
|
||||
// regular characters
|
||||
else
|
||||
print_to_stream(*stream_, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unicode slow path
|
||||
else
|
||||
{
|
||||
utf8_decoder decoder;
|
||||
const char* cp_start = str.data();
|
||||
const char* cp_end = cp_start;
|
||||
for (auto c : str)
|
||||
{
|
||||
decoder(c);
|
||||
cp_end++;
|
||||
|
||||
// if the decoder encounters malformed unicode just emit raw bytes and
|
||||
if (decoder.error())
|
||||
{
|
||||
while (cp_start != cp_end)
|
||||
{
|
||||
print_to_stream(*stream_, R"(\u00)"sv);
|
||||
print_to_stream(*stream_,
|
||||
static_cast<uint8_t>(*cp_start),
|
||||
value_flags::format_as_hexadecimal,
|
||||
2);
|
||||
cp_start++;
|
||||
}
|
||||
decoder.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!decoder.has_code_point())
|
||||
continue;
|
||||
|
||||
switch (decoder.codepoint)
|
||||
{
|
||||
case U'"': print_to_stream(*stream_, R"(\")"sv); break;
|
||||
case U'\\': print_to_stream(*stream_, R"(\\)"sv); break;
|
||||
case U'\x7F': print_to_stream(*stream_, R"(\u007F)"sv); break;
|
||||
case U'\t': print_to_stream(*stream_, real_tabs_allowed ? "\t"sv : R"(\t)"sv); break;
|
||||
case U'\n': print_to_stream(*stream_, multi_line ? "\n"sv : R"(\n)"sv); break;
|
||||
default:
|
||||
{
|
||||
// control characters from lookup table
|
||||
if TOML_UNLIKELY(decoder.codepoint <= U'\x1F')
|
||||
print_to_stream(*stream_,
|
||||
control_char_escapes[static_cast<uint_least32_t>(decoder.codepoint)]);
|
||||
|
||||
// escaped unicode characters
|
||||
else if (decoder.codepoint > U'\x7F'
|
||||
&& (!unicode_allowed || is_non_ascii_vertical_whitespace(decoder.codepoint)))
|
||||
{
|
||||
if (static_cast<uint_least32_t>(decoder.codepoint) > 0xFFFFu)
|
||||
{
|
||||
print_to_stream(*stream_, R"(\U)"sv);
|
||||
print_to_stream(*stream_,
|
||||
static_cast<uint_least32_t>(decoder.codepoint),
|
||||
value_flags::format_as_hexadecimal,
|
||||
8);
|
||||
}
|
||||
else
|
||||
{
|
||||
print_to_stream(*stream_, R"(\u)"sv);
|
||||
print_to_stream(*stream_,
|
||||
static_cast<uint_least32_t>(decoder.codepoint),
|
||||
value_flags::format_as_hexadecimal,
|
||||
4);
|
||||
}
|
||||
}
|
||||
|
||||
// regular characters
|
||||
else
|
||||
print_to_stream(*stream_, cp_start, static_cast<size_t>(cp_end - cp_start));
|
||||
}
|
||||
}
|
||||
|
||||
cp_start = cp_end;
|
||||
}
|
||||
}
|
||||
|
||||
print_unformatted(multi_line ? R"(""")"sv : R"(")"sv);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print(const value<std::string>& val)
|
||||
{
|
||||
print_string(val.get());
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print(const value<int64_t>& val)
|
||||
{
|
||||
naked_newline_ = false;
|
||||
|
||||
if (*val >= 0 && !!int_format_mask_)
|
||||
{
|
||||
static constexpr auto value_flags_mask =
|
||||
value_flags::format_as_binary | value_flags::format_as_octal | value_flags::format_as_hexadecimal;
|
||||
|
||||
const auto fmt = val.flags() & value_flags_mask;
|
||||
switch (fmt)
|
||||
{
|
||||
case value_flags::format_as_binary:
|
||||
if (!!(int_format_mask_ & format_flags::allow_binary_integers))
|
||||
{
|
||||
print_to_stream(*stream_, "0b"sv);
|
||||
print_to_stream(*stream_, *val, fmt);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case value_flags::format_as_octal:
|
||||
if (!!(int_format_mask_ & format_flags::allow_octal_integers))
|
||||
{
|
||||
print_to_stream(*stream_, "0o"sv);
|
||||
print_to_stream(*stream_, *val, fmt);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case value_flags::format_as_hexadecimal:
|
||||
if (!!(int_format_mask_ & format_flags::allow_hexadecimal_integers))
|
||||
{
|
||||
print_to_stream(*stream_, "0x"sv);
|
||||
print_to_stream(*stream_, *val, fmt);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to decimal
|
||||
print_to_stream(*stream_, *val);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print(const value<double>& val)
|
||||
{
|
||||
const std::string_view* inf_nan = nullptr;
|
||||
switch (fpclassify(*val))
|
||||
{
|
||||
case fp_class::neg_inf: inf_nan = &constants_->float_neg_inf; break;
|
||||
case fp_class::pos_inf: inf_nan = &constants_->float_pos_inf; break;
|
||||
case fp_class::nan: inf_nan = &constants_->float_nan; break;
|
||||
case fp_class::ok:
|
||||
print_to_stream(*stream_,
|
||||
*val,
|
||||
value_flags::none,
|
||||
!!(config_.flags & format_flags::relaxed_float_precision));
|
||||
break;
|
||||
default: TOML_UNREACHABLE;
|
||||
}
|
||||
|
||||
if (inf_nan)
|
||||
{
|
||||
if (!!(config_.flags & format_flags::quote_infinities_and_nans))
|
||||
print_to_stream_bookended(*stream_, *inf_nan, '"');
|
||||
else
|
||||
print_to_stream(*stream_, *inf_nan);
|
||||
}
|
||||
|
||||
naked_newline_ = false;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print(const value<bool>& val)
|
||||
{
|
||||
print_unformatted(*val ? constants_->bool_true : constants_->bool_false);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print(const value<date>& val)
|
||||
{
|
||||
if (!!(config_.flags & format_flags::quote_dates_and_times))
|
||||
print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"');
|
||||
else
|
||||
print_to_stream(*stream_, *val);
|
||||
naked_newline_ = false;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print(const value<time>& val)
|
||||
{
|
||||
if (!!(config_.flags & format_flags::quote_dates_and_times))
|
||||
print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"');
|
||||
else
|
||||
print_to_stream(*stream_, *val);
|
||||
naked_newline_ = false;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print(const value<date_time>& val)
|
||||
{
|
||||
if (!!(config_.flags & format_flags::quote_dates_and_times))
|
||||
print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"');
|
||||
else
|
||||
print_to_stream(*stream_, *val);
|
||||
naked_newline_ = false;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void formatter::print_value(const node& val_node, node_type type)
|
||||
{
|
||||
TOML_ASSUME(type > node_type::array);
|
||||
switch (type)
|
||||
{
|
||||
case node_type::string: print(*reinterpret_cast<const value<std::string>*>(&val_node)); break;
|
||||
case node_type::integer: print(*reinterpret_cast<const value<int64_t>*>(&val_node)); break;
|
||||
case node_type::floating_point: print(*reinterpret_cast<const value<double>*>(&val_node)); break;
|
||||
case node_type::boolean: print(*reinterpret_cast<const value<bool>*>(&val_node)); break;
|
||||
case node_type::date: print(*reinterpret_cast<const value<date>*>(&val_node)); break;
|
||||
case node_type::time: print(*reinterpret_cast<const value<time>*>(&val_node)); break;
|
||||
case node_type::date_time: print(*reinterpret_cast<const value<date_time>*>(&val_node)); break;
|
||||
default: TOML_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_PARSER && !TOML_EXCEPTIONS
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool formatter::dump_failed_parse_result()
|
||||
{
|
||||
if (result_ && !(*result_))
|
||||
{
|
||||
stream() << result_->error();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
TOML_ATTR(const)
|
||||
bool formatter::dump_failed_parse_result()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
#endif // TOML_ENABLE_FORMATTERS
|
||||
1100
src/external/toml++/impl/forward_declarations.hpp
vendored
Normal file
1100
src/external/toml++/impl/forward_declarations.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
13
src/external/toml++/impl/header_end.hpp
vendored
Normal file
13
src/external/toml++/impl/header_end.hpp
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
//# {{
|
||||
#ifdef __INTELLISENSE__
|
||||
#include "preprocessor.hpp"
|
||||
#endif
|
||||
//# }}
|
||||
#ifdef _MSC_VER
|
||||
#pragma pop_macro("min")
|
||||
#pragma pop_macro("max")
|
||||
#ifndef __clang__
|
||||
#pragma inline_recursion(off)
|
||||
#endif
|
||||
#endif
|
||||
TOML_POP_WARNINGS;
|
||||
15
src/external/toml++/impl/header_start.hpp
vendored
Normal file
15
src/external/toml++/impl/header_start.hpp
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
//# {{
|
||||
#ifdef __INTELLISENSE__
|
||||
#include "preprocessor.hpp"
|
||||
#endif
|
||||
//# }}
|
||||
TOML_PUSH_WARNINGS;
|
||||
#ifdef _MSC_VER
|
||||
#ifndef __clang__
|
||||
#pragma inline_recursion(on)
|
||||
#endif
|
||||
#pragma push_macro("min")
|
||||
#pragma push_macro("max")
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
142
src/external/toml++/impl/json_formatter.hpp
vendored
Normal file
142
src/external/toml++/impl/json_formatter.hpp
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
#if TOML_ENABLE_FORMATTERS
|
||||
|
||||
#include "formatter.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
/// \brief A wrapper for printing TOML objects out to a stream as formatted JSON.
|
||||
///
|
||||
/// \availability This class is only available when #TOML_ENABLE_FORMATTERS is enabled.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto some_toml = toml::parse(R"(
|
||||
/// [fruit]
|
||||
/// apple.color = "red"
|
||||
/// apple.taste.sweet = true
|
||||
///
|
||||
/// [fruit.apple.texture]
|
||||
/// smooth = true
|
||||
/// )"sv);
|
||||
/// std::cout << toml::json_formatter{ some_toml } << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// {
|
||||
/// "fruit" : {
|
||||
/// "apple" : {
|
||||
/// "color" : "red",
|
||||
/// "taste" : {
|
||||
/// "sweet" : true
|
||||
/// },
|
||||
/// "texture" : {
|
||||
/// "smooth" : true
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// \eout
|
||||
class TOML_EXPORTED_CLASS json_formatter : impl::formatter
|
||||
{
|
||||
private:
|
||||
/// \cond
|
||||
|
||||
using base = impl::formatter;
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const toml::table&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const toml::array&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print();
|
||||
|
||||
static constexpr impl::formatter_constants constants = {
|
||||
format_flags::quote_dates_and_times, // mandatory
|
||||
format_flags::allow_literal_strings | format_flags::allow_multi_line_strings, // ignored
|
||||
"Infinity"sv,
|
||||
"-Infinity"sv,
|
||||
"NaN"sv,
|
||||
"true"sv,
|
||||
"false"sv
|
||||
};
|
||||
|
||||
/// \endcond
|
||||
|
||||
public:
|
||||
/// \brief The default flags for a json_formatter.
|
||||
static constexpr format_flags default_flags = constants.mandatory_flags //
|
||||
| format_flags::quote_infinities_and_nans //
|
||||
| format_flags::allow_unicode_strings //
|
||||
| format_flags::indentation;
|
||||
|
||||
/// \brief Constructs a JSON formatter and binds it to a TOML object.
|
||||
///
|
||||
/// \param source The source TOML object.
|
||||
/// \param flags Format option flags.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit json_formatter(const toml::node& source, format_flags flags = default_flags) noexcept
|
||||
: base{ &source, nullptr, constants, { flags, " "sv } }
|
||||
{}
|
||||
|
||||
#if TOML_DOXYGEN || (TOML_ENABLE_PARSER && !TOML_EXCEPTIONS)
|
||||
|
||||
/// \brief Constructs a JSON formatter and binds it to a toml::parse_result.
|
||||
///
|
||||
/// \availability This constructor is only available when exceptions are disabled.
|
||||
///
|
||||
/// \attention Formatting a failed parse result will simply dump the error message out as-is.
|
||||
/// This will not be valid JSON, but at least gives you something to log or show up in diagnostics:
|
||||
/// \cpp
|
||||
/// std::cout << toml::json_formatter{ toml::parse("a = 'b'"sv) } // ok
|
||||
/// << "\n\n"
|
||||
/// << toml::json_formatter{ toml::parse("a = "sv) } // malformed
|
||||
/// << "\n";
|
||||
/// \ecpp
|
||||
/// \out
|
||||
/// {
|
||||
/// "a" : "b"
|
||||
/// }
|
||||
///
|
||||
/// Error while parsing key-value pair: encountered end-of-file
|
||||
/// (error occurred at line 1, column 5)
|
||||
/// \eout
|
||||
/// Use the library with exceptions if you want to avoid this scenario.
|
||||
///
|
||||
/// \param result The parse result.
|
||||
/// \param flags Format option flags.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit json_formatter(const toml::parse_result& result, format_flags flags = default_flags) noexcept
|
||||
: base{ nullptr, &result, constants, { flags, " "sv } }
|
||||
{}
|
||||
|
||||
#endif
|
||||
|
||||
/// \brief Prints the bound TOML object out to the stream as JSON.
|
||||
friend std::ostream& operator<<(std::ostream& lhs, json_formatter& rhs)
|
||||
{
|
||||
rhs.attach(lhs);
|
||||
rhs.print();
|
||||
rhs.detach();
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/// \brief Prints the bound TOML object out to the stream as JSON (rvalue overload).
|
||||
friend std::ostream& operator<<(std::ostream& lhs, json_formatter&& rhs)
|
||||
{
|
||||
return lhs << rhs; // as lvalue
|
||||
}
|
||||
};
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
#endif // TOML_ENABLE_FORMATTERS
|
||||
121
src/external/toml++/impl/json_formatter.inl
vendored
Normal file
121
src/external/toml++/impl/json_formatter.inl
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
//# {{
|
||||
#if !TOML_IMPLEMENTATION
|
||||
#error This is an implementation-only header.
|
||||
#endif
|
||||
//# }}
|
||||
#if TOML_ENABLE_FORMATTERS
|
||||
|
||||
#include "json_formatter.hpp"
|
||||
#include "print_to_stream.hpp"
|
||||
#include "table.hpp"
|
||||
#include "array.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void json_formatter::print(const toml::table& tbl)
|
||||
{
|
||||
if (tbl.empty())
|
||||
{
|
||||
print_unformatted("{}"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
print_unformatted('{');
|
||||
|
||||
if (indent_sub_tables())
|
||||
increase_indent();
|
||||
bool first = false;
|
||||
for (auto&& [k, v] : tbl)
|
||||
{
|
||||
if (first)
|
||||
print_unformatted(',');
|
||||
first = true;
|
||||
print_newline(true);
|
||||
print_indent();
|
||||
|
||||
print_string(k.str(), false);
|
||||
if (terse_kvps())
|
||||
print_unformatted(":"sv);
|
||||
else
|
||||
print_unformatted(" : "sv);
|
||||
|
||||
const auto type = v.type();
|
||||
TOML_ASSUME(type != node_type::none);
|
||||
switch (type)
|
||||
{
|
||||
case node_type::table: print(*reinterpret_cast<const table*>(&v)); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&v)); break;
|
||||
default: print_value(v, type);
|
||||
}
|
||||
}
|
||||
if (indent_sub_tables())
|
||||
decrease_indent();
|
||||
print_newline(true);
|
||||
print_indent();
|
||||
|
||||
print_unformatted('}');
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void json_formatter::print(const toml::array& arr)
|
||||
{
|
||||
if (arr.empty())
|
||||
{
|
||||
print_unformatted("[]"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
print_unformatted('[');
|
||||
if (indent_array_elements())
|
||||
increase_indent();
|
||||
for (size_t i = 0; i < arr.size(); i++)
|
||||
{
|
||||
if (i > 0u)
|
||||
print_unformatted(',');
|
||||
print_newline(true);
|
||||
print_indent();
|
||||
|
||||
auto& v = arr[i];
|
||||
const auto type = v.type();
|
||||
TOML_ASSUME(type != node_type::none);
|
||||
switch (type)
|
||||
{
|
||||
case node_type::table: print(*reinterpret_cast<const table*>(&v)); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&v)); break;
|
||||
default: print_value(v, type);
|
||||
}
|
||||
}
|
||||
if (indent_array_elements())
|
||||
decrease_indent();
|
||||
print_newline(true);
|
||||
print_indent();
|
||||
print_unformatted(']');
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void json_formatter::print()
|
||||
{
|
||||
if (dump_failed_parse_result())
|
||||
return;
|
||||
|
||||
switch (auto source_type = source().type())
|
||||
{
|
||||
case node_type::table: print(*reinterpret_cast<const table*>(&source())); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&source())); break;
|
||||
default: print_value(source(), source_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
#endif // TOML_ENABLE_FORMATTERS
|
||||
335
src/external/toml++/impl/key.hpp
vendored
Normal file
335
src/external/toml++/impl/key.hpp
vendored
Normal file
@@ -0,0 +1,335 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "source_region.hpp"
|
||||
#include "std_utility.hpp"
|
||||
#include "print_to_stream.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
/// \brief A key parsed from a TOML document.
|
||||
///
|
||||
/// \detail These are used as the internal keys for a toml::table: \cpp
|
||||
/// const toml::table tbl = R"(
|
||||
/// a = 1
|
||||
/// b = 2
|
||||
/// c = 3
|
||||
/// )"_toml;
|
||||
///
|
||||
/// for (auto&& [k, v] : tbl)
|
||||
/// std::cout << "key '"sv << k << "' defined at "sv << k.source() << "\n";
|
||||
/// \ecpp
|
||||
/// \out
|
||||
/// key 'a' defined at line 2, column 5
|
||||
/// key 'b' defined at line 3, column 7
|
||||
/// key 'c' defined at line 4, column 9
|
||||
/// \eout
|
||||
class key
|
||||
{
|
||||
private:
|
||||
std::string key_;
|
||||
source_region source_;
|
||||
|
||||
public:
|
||||
/// \brief Default constructor.
|
||||
TOML_NODISCARD_CTOR
|
||||
key() noexcept = default;
|
||||
|
||||
/// \brief Constructs a key from a string view and source region.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit key(std::string_view k, source_region&& src = {}) //
|
||||
: key_{ k },
|
||||
source_{ std::move(src) }
|
||||
{}
|
||||
|
||||
/// \brief Constructs a key from a string view and source region.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit key(std::string_view k, const source_region& src) //
|
||||
: key_{ k },
|
||||
source_{ src }
|
||||
{}
|
||||
|
||||
/// \brief Constructs a key from a string and source region.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit key(std::string&& k, source_region&& src = {}) noexcept //
|
||||
: key_{ std::move(k) },
|
||||
source_{ std::move(src) }
|
||||
{}
|
||||
|
||||
/// \brief Constructs a key from a string and source region.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit key(std::string&& k, const source_region& src) noexcept //
|
||||
: key_{ std::move(k) },
|
||||
source_{ src }
|
||||
{}
|
||||
|
||||
/// \brief Constructs a key from a c-string and source region.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit key(const char* k, source_region&& src = {}) //
|
||||
: key_{ k },
|
||||
source_{ std::move(src) }
|
||||
{}
|
||||
|
||||
/// \brief Constructs a key from a c-string view and source region.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit key(const char* k, const source_region& src) //
|
||||
: key_{ k },
|
||||
source_{ src }
|
||||
{}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Constructs a key from a wide string view and source region.
|
||||
///
|
||||
/// \availability This constructor is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit key(std::wstring_view k, source_region&& src = {}) //
|
||||
: key_{ impl::narrow(k) },
|
||||
source_{ std::move(src) }
|
||||
{}
|
||||
|
||||
/// \brief Constructs a key from a wide string and source region.
|
||||
///
|
||||
/// \availability This constructor is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit key(std::wstring_view k, const source_region& src) //
|
||||
: key_{ impl::narrow(k) },
|
||||
source_{ src }
|
||||
{}
|
||||
|
||||
#endif
|
||||
|
||||
/// \name String operations
|
||||
/// @{
|
||||
|
||||
/// \brief Returns a view of the key's underlying string.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
std::string_view str() const noexcept
|
||||
{
|
||||
return std::string_view{ key_ };
|
||||
}
|
||||
|
||||
/// \brief Returns a view of the key's underlying string.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
/*implicit*/ operator std::string_view() const noexcept
|
||||
{
|
||||
return str();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the key's underlying string is empty.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
bool empty() const noexcept
|
||||
{
|
||||
return key_.empty();
|
||||
}
|
||||
|
||||
/// \brief Returns a pointer to the start of the key's underlying string.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
const char* data() const noexcept
|
||||
{
|
||||
return key_.data();
|
||||
}
|
||||
|
||||
/// \brief Returns the length of the key's underlying string.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
size_t length() const noexcept
|
||||
{
|
||||
return key_.length();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Metadata
|
||||
/// @{
|
||||
|
||||
/// \brief Returns the source region responsible for specifying this key during parsing.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
const source_region& source() const noexcept
|
||||
{
|
||||
return source_;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Equality and Comparison
|
||||
/// \attention These operations only compare the underlying strings; source regions are ignored for the purposes of all comparison!
|
||||
/// @{
|
||||
|
||||
/// \brief Returns true if `lhs.str() == rhs.str()`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator==(const key& lhs, const key& rhs) noexcept
|
||||
{
|
||||
return lhs.key_ == rhs.key_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs.str() != rhs.str()`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator!=(const key& lhs, const key& rhs) noexcept
|
||||
{
|
||||
return lhs.key_ != rhs.key_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs.str() < rhs.str()`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator<(const key& lhs, const key& rhs) noexcept
|
||||
{
|
||||
return lhs.key_ < rhs.key_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs.str() <= rhs.str()`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator<=(const key& lhs, const key& rhs) noexcept
|
||||
{
|
||||
return lhs.key_ <= rhs.key_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs.str() > rhs.str()`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator>(const key& lhs, const key& rhs) noexcept
|
||||
{
|
||||
return lhs.key_ > rhs.key_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs.str() >= rhs.str()`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator>=(const key& lhs, const key& rhs) noexcept
|
||||
{
|
||||
return lhs.key_ >= rhs.key_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs.str() == rhs`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator==(const key& lhs, std::string_view rhs) noexcept
|
||||
{
|
||||
return lhs.key_ == rhs;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs.str() != rhs`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator!=(const key& lhs, std::string_view rhs) noexcept
|
||||
{
|
||||
return lhs.key_ != rhs;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs.str() < rhs`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator<(const key& lhs, std::string_view rhs) noexcept
|
||||
{
|
||||
return lhs.key_ < rhs;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs.str() <= rhs`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator<=(const key& lhs, std::string_view rhs) noexcept
|
||||
{
|
||||
return lhs.key_ <= rhs;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs.str() > rhs`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator>(const key& lhs, std::string_view rhs) noexcept
|
||||
{
|
||||
return lhs.key_ > rhs;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs.str() >= rhs`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator>=(const key& lhs, std::string_view rhs) noexcept
|
||||
{
|
||||
return lhs.key_ >= rhs;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs == rhs.str()`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator==(std::string_view lhs, const key& rhs) noexcept
|
||||
{
|
||||
return lhs == rhs.key_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs != rhs.str()`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator!=(std::string_view lhs, const key& rhs) noexcept
|
||||
{
|
||||
return lhs != rhs.key_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs < rhs.str()`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator<(std::string_view lhs, const key& rhs) noexcept
|
||||
{
|
||||
return lhs < rhs.key_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs <= rhs.str()`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator<=(std::string_view lhs, const key& rhs) noexcept
|
||||
{
|
||||
return lhs <= rhs.key_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs > rhs.str()`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator>(std::string_view lhs, const key& rhs) noexcept
|
||||
{
|
||||
return lhs > rhs.key_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if `lhs >= rhs.str()`.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator>=(std::string_view lhs, const key& rhs) noexcept
|
||||
{
|
||||
return lhs >= rhs.key_;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
/// \brief A const iterator for iterating over the characters in the key.
|
||||
using const_iterator = const char*;
|
||||
|
||||
/// \brief A const iterator for iterating over the characters in the key.
|
||||
using iterator = const_iterator;
|
||||
|
||||
/// \brief Returns an iterator to the first character in the key's backing string.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
const_iterator begin() const noexcept
|
||||
{
|
||||
return key_.data();
|
||||
}
|
||||
|
||||
/// \brief Returns an iterator to one-past-the-last character in the key's backing string.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
const_iterator end() const noexcept
|
||||
{
|
||||
return key_.data() + key_.length();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \brief Prints the key's underlying string out to the stream.
|
||||
friend std::ostream& operator<<(std::ostream& lhs, const key& rhs)
|
||||
{
|
||||
impl::print_to_stream(lhs, rhs.key_);
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Metafunction for determining if a type is, or is a reference to, a toml::key.
|
||||
template <typename T>
|
||||
inline constexpr bool is_key = std::is_same_v<impl::remove_cvref<T>, toml::key>;
|
||||
|
||||
/// \brief Metafunction for determining if a type is, or is a reference to, a toml::key,
|
||||
/// or is implicitly or explicitly convertible to one.
|
||||
template <typename T>
|
||||
inline constexpr bool is_key_or_convertible = is_key<T> //
|
||||
|| impl::is_constructible_or_convertible<toml::key, T>;
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
182
src/external/toml++/impl/make_node.hpp
vendored
Normal file
182
src/external/toml++/impl/make_node.hpp
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "forward_declarations.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
/// \cond
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
template <typename T>
|
||||
TOML_NODISCARD
|
||||
TOML_ATTR(returns_nonnull)
|
||||
auto* make_node_impl_specialized(T && val, [[maybe_unused]] value_flags flags)
|
||||
{
|
||||
using unwrapped_type = unwrap_node<remove_cvref<T>>;
|
||||
static_assert(!std::is_same_v<unwrapped_type, node>);
|
||||
static_assert(!is_node_view<unwrapped_type>);
|
||||
|
||||
// arrays + tables - invoke copy/move ctor
|
||||
if constexpr (is_one_of<unwrapped_type, array, table>)
|
||||
{
|
||||
return new unwrapped_type(static_cast<T&&>(val));
|
||||
}
|
||||
|
||||
// values
|
||||
else
|
||||
{
|
||||
using native_type = native_type_of<unwrapped_type>;
|
||||
using value_type = value<native_type>;
|
||||
|
||||
value_type* out;
|
||||
|
||||
// copy/move ctor
|
||||
if constexpr (std::is_same_v<remove_cvref<T>, value_type>)
|
||||
{
|
||||
out = new value_type{ static_cast<T&&>(val), flags };
|
||||
}
|
||||
|
||||
// creating from raw value
|
||||
else
|
||||
{
|
||||
static_assert(!is_wide_string<T> || TOML_ENABLE_WINDOWS_COMPAT,
|
||||
"Instantiating values from wide-character strings is only "
|
||||
"supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled.");
|
||||
|
||||
if constexpr (!is_losslessly_convertible_to_native<unwrapped_type>)
|
||||
{
|
||||
if constexpr (std::is_same_v<native_type, int64_t>)
|
||||
static_assert(always_false<T>,
|
||||
"Integral value initializers must be losslessly convertible to int64_t");
|
||||
else if constexpr (std::is_same_v<native_type, double>)
|
||||
static_assert(always_false<T>,
|
||||
"Floating-point value initializers must be losslessly convertible to double");
|
||||
else
|
||||
static_assert(
|
||||
always_false<T>,
|
||||
"Value initializers must be losslessly convertible to one of the TOML value types");
|
||||
}
|
||||
|
||||
if constexpr (is_wide_string<T>)
|
||||
{
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
out = new value_type{ narrow(static_cast<T&&>(val)) };
|
||||
#else
|
||||
static_assert(always_false<T>, "Evaluated unreachable branch!");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
out = new value_type{ static_cast<T&&>(val) };
|
||||
|
||||
if (flags != preserve_source_value_flags)
|
||||
out->flags(flags);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TOML_NODISCARD
|
||||
auto* make_node_impl(T && val, value_flags flags = preserve_source_value_flags)
|
||||
{
|
||||
using unwrapped_type = unwrap_node<remove_cvref<T>>;
|
||||
if constexpr (std::is_same_v<unwrapped_type, node> || is_node_view<unwrapped_type>)
|
||||
{
|
||||
if constexpr (is_node_view<unwrapped_type>)
|
||||
{
|
||||
if (!val)
|
||||
return static_cast<toml::node*>(nullptr);
|
||||
}
|
||||
|
||||
return static_cast<T&&>(val).visit(
|
||||
[flags](auto&& concrete) {
|
||||
return static_cast<toml::node*>(
|
||||
make_node_impl_specialized(static_cast<decltype(concrete)&&>(concrete), flags));
|
||||
});
|
||||
}
|
||||
else
|
||||
return make_node_impl_specialized(static_cast<T&&>(val), flags);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TOML_NODISCARD
|
||||
auto* make_node_impl(inserter<T> && val, value_flags flags = preserve_source_value_flags)
|
||||
{
|
||||
return make_node_impl(static_cast<T&&>(val.value), flags);
|
||||
}
|
||||
|
||||
template <typename T, bool = (is_node<T> || is_node_view<T> || is_value<T> || can_partially_represent_native<T>)>
|
||||
struct inserted_type_of_
|
||||
{
|
||||
using type = std::remove_pointer_t<decltype(make_node_impl(std::declval<T>()))>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct inserted_type_of_<inserter<T>, false>
|
||||
{
|
||||
using type = typename inserted_type_of_<remove_cvref<T>>::type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct inserted_type_of_<T, false>
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
TOML_NODISCARD
|
||||
node_ptr make_node(T && val, value_flags flags = preserve_source_value_flags)
|
||||
{
|
||||
return node_ptr{ make_node_impl(static_cast<T&&>(val), flags) };
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
struct emplaced_type_of_
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct emplaced_type_of_<T>
|
||||
{
|
||||
using type = std::conditional_t<is_one_of<T, node, node_view<node>, node_view<const node>>,
|
||||
void,
|
||||
typename inserted_type_of_<T>::type>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct emplaced_type_of_<inserter<T>>
|
||||
{
|
||||
using type = typename emplaced_type_of_<remove_cvref<T>>::type;
|
||||
};
|
||||
|
||||
template <typename... T>
|
||||
using emplaced_type_of = typename emplaced_type_of_<remove_cvref<T>...>::type;
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
/// \endcond
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
/// \brief Metafunction for determining which node type would be constructed
|
||||
/// if an object of this type was inserted into a toml::table or toml::array.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// static_assert(std::is_same_v<toml::inserted_type_of<const char*>, toml::value<std::string>);
|
||||
/// static_assert(std::is_same_v<toml::inserted_type_of<int>, toml::value<int64_t>);
|
||||
/// static_assert(std::is_same_v<toml::inserted_type_of<float>, toml::value<double>);
|
||||
/// static_assert(std::is_same_v<toml::inserted_type_of<bool>, toml::value<bool>);
|
||||
/// \ecpp
|
||||
///
|
||||
/// \note This will return toml::node for nodes and node_views, even though a more specific node subclass
|
||||
/// would actually be inserted. There is no way around this in a compile-time metafunction.
|
||||
template <typename T>
|
||||
using inserted_type_of = POXY_IMPLEMENTATION_DETAIL(typename impl::inserted_type_of_<impl::remove_cvref<T>>::type);
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
1132
src/external/toml++/impl/node.hpp
vendored
Normal file
1132
src/external/toml++/impl/node.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
141
src/external/toml++/impl/node.inl
vendored
Normal file
141
src/external/toml++/impl/node.inl
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
//# {{
|
||||
#include "preprocessor.hpp"
|
||||
#if !TOML_IMPLEMENTATION
|
||||
#error This is an implementation-only header.
|
||||
#endif
|
||||
//# }}
|
||||
|
||||
#include "node.hpp"
|
||||
#include "node_view.hpp"
|
||||
#include "at_path.hpp"
|
||||
#include "table.hpp"
|
||||
#include "array.hpp"
|
||||
#include "value.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node::node() noexcept = default;
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node::~node() noexcept = default;
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node::node(node && other) noexcept //
|
||||
: source_{ std::exchange(other.source_, {}) }
|
||||
{}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node::node(const node& /*other*/) noexcept
|
||||
{
|
||||
// does not copy source information - this is not an error
|
||||
//
|
||||
// see https://github.com/marzer/tomlplusplus/issues/49#issuecomment-665089577
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node& node::operator=(const node& /*rhs*/) noexcept
|
||||
{
|
||||
// does not copy source information - this is not an error
|
||||
//
|
||||
// see https://github.com/marzer/tomlplusplus/issues/49#issuecomment-665089577
|
||||
|
||||
source_ = {};
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node& node::operator=(node&& rhs) noexcept
|
||||
{
|
||||
if (&rhs != this)
|
||||
source_ = std::exchange(rhs.source_, {});
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<node> node::at_path(std::string_view path) noexcept
|
||||
{
|
||||
return toml::at_path(*this, path);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<const node> node::at_path(std::string_view path) const noexcept
|
||||
{
|
||||
return toml::at_path(*this, path);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<node> node::at_path(const path& p) noexcept
|
||||
{
|
||||
return toml::at_path(*this, p);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<const node> node::at_path(const path& p) const noexcept
|
||||
{
|
||||
return toml::at_path(*this, p);
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<node> node::at_path(std::wstring_view path)
|
||||
{
|
||||
return toml::at_path(*this, path);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<const node> node::at_path(std::wstring_view path) const
|
||||
{
|
||||
return toml::at_path(*this, path);
|
||||
}
|
||||
|
||||
#endif // TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<node> node::operator[](const path& p) noexcept
|
||||
{
|
||||
return toml::at_path(*this, p);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<const node> node::operator[](const path& p) const noexcept
|
||||
{
|
||||
return toml::at_path(*this, p);
|
||||
}
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool TOML_CALLCONV node_deep_equality(const node* lhs, const node* rhs) noexcept
|
||||
{
|
||||
// both same or both null
|
||||
if (lhs == rhs)
|
||||
return true;
|
||||
|
||||
// lhs null != rhs null or different types
|
||||
if ((!lhs != !rhs) || lhs->type() != rhs->type())
|
||||
return false;
|
||||
|
||||
return lhs->visit(
|
||||
[=](auto& l) noexcept
|
||||
{
|
||||
using concrete_type = remove_cvref<decltype(l)>;
|
||||
|
||||
return l == *(rhs->as<concrete_type>());
|
||||
});
|
||||
}
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
839
src/external/toml++/impl/node_view.hpp
vendored
Normal file
839
src/external/toml++/impl/node_view.hpp
vendored
Normal file
@@ -0,0 +1,839 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "std_vector.hpp"
|
||||
#include "std_initializer_list.hpp"
|
||||
#include "print_to_stream.hpp"
|
||||
#include "node.hpp"
|
||||
#include "header_start.hpp"
|
||||
TOML_DISABLE_ARITHMETIC_WARNINGS;
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
/// \brief A view of a node.
|
||||
///
|
||||
/// \detail A node_view is like a std::optional<toml::node&> (if such a construct were legal), with lots of
|
||||
/// toml-specific stuff built-in. It _may_ represent a node, and allows you to do many of the
|
||||
/// same operations that you'd do on nodes directly, as well as easily traversing the node tree by creating
|
||||
/// subviews (via node_view::operator[]). \cpp
|
||||
///
|
||||
/// auto tbl = toml::parse(R"(
|
||||
///
|
||||
/// title = "my hardware store"
|
||||
///
|
||||
/// [[products]]
|
||||
/// name = "Hammer"
|
||||
/// sku = 738594937
|
||||
/// keywords = [ "hammer", "construction", "build" ]
|
||||
///
|
||||
/// [[products]]
|
||||
/// name = "Nail"
|
||||
/// sku = 284758393
|
||||
/// color = "gray"
|
||||
///
|
||||
/// )"sv);
|
||||
///
|
||||
/// std::cout << tbl["title"] << "\n";
|
||||
/// std::cout << tbl["products"][0]["name"] << "\n";
|
||||
/// std::cout << tbl["products"][0]["keywords"] << "\n";
|
||||
/// std::cout << tbl["products"][0]["keywords"][2] << "\n";
|
||||
///
|
||||
/// tbl["products"][0]["keywords"].as_array()->push_back("heavy");
|
||||
/// std::cout << tbl["products"][0]["keywords"] << "\n";
|
||||
/// std::cout << "has product[2]: "sv << !!tbl["products"][2] << "\n";
|
||||
/// std::cout << "product[2]: "sv << tbl["products"][2] << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// "my hardware store"
|
||||
/// "Hammer"
|
||||
/// [ "hammer", "construction", "build" ]
|
||||
/// "build"
|
||||
/// [ "hammer", "construction", "build", "heavy" ]
|
||||
/// has product[2]: false
|
||||
/// product[2]:
|
||||
/// \eout
|
||||
template <typename ViewedType>
|
||||
class TOML_TRIVIAL_ABI node_view
|
||||
{
|
||||
static_assert(impl::is_one_of<ViewedType, toml::node, const toml::node>,
|
||||
"A toml::node_view<> must wrap toml::node or const toml::node.");
|
||||
|
||||
public:
|
||||
/// \brief The node type being viewed - either `node` or `const node`.
|
||||
using viewed_type = ViewedType;
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
friend class node_view;
|
||||
|
||||
mutable viewed_type* node_ = nullptr;
|
||||
|
||||
public:
|
||||
/// \brief Constructs an empty node view.
|
||||
TOML_NODISCARD_CTOR
|
||||
node_view() noexcept = default;
|
||||
|
||||
/// \brief Constructs node_view of a specific node.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit node_view(viewed_type* node) noexcept //
|
||||
: node_{ node }
|
||||
{}
|
||||
|
||||
/// \brief Constructs node_view of a specific node.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit node_view(viewed_type& node) noexcept //
|
||||
: node_{ &node }
|
||||
{}
|
||||
|
||||
/// \brief Copy constructor.
|
||||
TOML_NODISCARD_CTOR
|
||||
node_view(const node_view&) noexcept = default;
|
||||
|
||||
/// \brief Move constructor.
|
||||
TOML_NODISCARD_CTOR
|
||||
node_view(node_view&&) noexcept = default;
|
||||
|
||||
/// \brief Copy-assignment operator.
|
||||
node_view& operator=(const node_view&) & noexcept = default;
|
||||
|
||||
/// \brief Move-assignment operator.
|
||||
node_view& operator=(node_view&&) & noexcept = default;
|
||||
|
||||
/// \brief Returns true if the view references a node.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return node_ != nullptr;
|
||||
}
|
||||
|
||||
/// \brief Returns the node that's being referenced by the view.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
viewed_type* node() const noexcept
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
|
||||
/// \name Type checks
|
||||
/// @{
|
||||
|
||||
/// \brief Returns the type identifier for the viewed node.
|
||||
TOML_PURE_GETTER
|
||||
node_type type() const noexcept
|
||||
{
|
||||
return node_ ? node_->type() : node_type::none;
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a toml::table.
|
||||
TOML_PURE_GETTER
|
||||
bool is_table() const noexcept
|
||||
{
|
||||
return node_ && node_->is_table();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a toml::array.
|
||||
TOML_PURE_GETTER
|
||||
bool is_array() const noexcept
|
||||
{
|
||||
return node_ && node_->is_array();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a toml::value<>.
|
||||
TOML_PURE_GETTER
|
||||
bool is_value() const noexcept
|
||||
{
|
||||
return node_ && node_->is_value();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a toml::value<string>.
|
||||
TOML_PURE_GETTER
|
||||
bool is_string() const noexcept
|
||||
{
|
||||
return node_ && node_->is_string();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a toml::value<int64_t>.
|
||||
TOML_PURE_GETTER
|
||||
bool is_integer() const noexcept
|
||||
{
|
||||
return node_ && node_->is_integer();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a toml::value<double>.
|
||||
TOML_PURE_GETTER
|
||||
bool is_floating_point() const noexcept
|
||||
{
|
||||
return node_ && node_->is_floating_point();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a toml::value<int64_t> or toml::value<double>.
|
||||
TOML_PURE_GETTER
|
||||
bool is_number() const noexcept
|
||||
{
|
||||
return node_ && node_->is_number();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a toml::value<bool>.
|
||||
TOML_PURE_GETTER
|
||||
bool is_boolean() const noexcept
|
||||
{
|
||||
return node_ && node_->is_boolean();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a toml::value<date>.
|
||||
TOML_PURE_GETTER
|
||||
bool is_date() const noexcept
|
||||
{
|
||||
return node_ && node_->is_date();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a toml::value<time>.
|
||||
TOML_PURE_GETTER
|
||||
bool is_time() const noexcept
|
||||
{
|
||||
return node_ && node_->is_time();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a toml::value<date_time>.
|
||||
TOML_PURE_GETTER
|
||||
bool is_date_time() const noexcept
|
||||
{
|
||||
return node_ && node_->is_date_time();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a toml::array that contains only tables.
|
||||
TOML_PURE_GETTER
|
||||
bool is_array_of_tables() const noexcept
|
||||
{
|
||||
return node_ && node_->is_array_of_tables();
|
||||
}
|
||||
|
||||
/// \brief Checks if this view references a node of a specific type.
|
||||
///
|
||||
/// \tparam T A TOML node or value type.
|
||||
///
|
||||
/// \returns Returns true if the viewed node is an instance of the specified type.
|
||||
///
|
||||
/// \see toml::node::is()
|
||||
template <typename T>
|
||||
TOML_PURE_GETTER
|
||||
bool is() const noexcept
|
||||
{
|
||||
return node_ ? node_->template is<impl::unwrap_node<impl::remove_cvref<T>>>() : false;
|
||||
}
|
||||
|
||||
/// \brief Checks if the viewed node contains values/elements of only one type.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto cfg = toml::parse("arr = [ 1, 2, 3, 4.0 ]");
|
||||
///
|
||||
/// toml::node* nonmatch{};
|
||||
/// if (cfg["arr"].is_homogeneous(toml::node_type::integer, nonmatch))
|
||||
/// std::cout << "array was homogeneous"sv << "\n";
|
||||
/// else
|
||||
/// std::cout << "array was not homogeneous!\n"
|
||||
/// << "first non-match was a "sv << nonmatch->type() << " at " << nonmatch->source() << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// array was not homogeneous!
|
||||
/// first non-match was a floating-point at line 1, column 18
|
||||
/// \eout
|
||||
///
|
||||
/// \param ntype A TOML node type. <br>
|
||||
/// \conditional_return{toml::node_type::none} "is every element the same type?"
|
||||
/// \conditional_return{Anything else} "is every element one of these?"
|
||||
///
|
||||
/// \param first_nonmatch Reference to a pointer in which the address of the first non-matching element
|
||||
/// will be stored if the return value is false.
|
||||
///
|
||||
/// \returns True if the viewed node was homogeneous.
|
||||
///
|
||||
/// \remarks Always returns `false` if the view does not reference a node, or if the viewed node is
|
||||
/// an empty table or array.
|
||||
TOML_NODISCARD
|
||||
bool is_homogeneous(node_type ntype, viewed_type*& first_nonmatch) const noexcept
|
||||
{
|
||||
if (!node_)
|
||||
{
|
||||
first_nonmatch = {};
|
||||
return false;
|
||||
}
|
||||
return node_->is_homogeneous(ntype, first_nonmatch);
|
||||
}
|
||||
|
||||
/// \brief Checks if the viewed node contains values/elements of only one type.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto cfg = toml::parse("arr = [ 1, 2, 3 ]");
|
||||
/// std::cout << "homogenous: "sv << cfg["arr"].is_homogeneous(toml::node_type::none) << "\n";
|
||||
/// std::cout << "all floats: "sv << cfg["arr"].is_homogeneous(toml::node_type::floating_point) << "\n";
|
||||
/// std::cout << "all arrays: "sv << cfg["arr"].is_homogeneous(toml::node_type::array) << "\n";
|
||||
/// std::cout << "all ints: "sv << cfg["arr"].is_homogeneous(toml::node_type::integer) << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// homogeneous: true
|
||||
/// all floats: false
|
||||
/// all arrays: false
|
||||
/// all ints: true
|
||||
/// \eout
|
||||
///
|
||||
/// \param ntype A TOML node type. <br>
|
||||
/// \conditional_return{toml::node_type::none} "is every element the same type?"
|
||||
/// \conditional_return{Anything else} "is every element one of these?"
|
||||
///
|
||||
/// \returns True if the viewed node was homogeneous.
|
||||
///
|
||||
/// \remarks Always returns `false` if the view does not reference a node, or if the viewed node is
|
||||
/// an empty table or array.
|
||||
TOML_PURE_GETTER
|
||||
bool is_homogeneous(node_type ntype) const noexcept
|
||||
{
|
||||
return node_ ? node_->is_homogeneous(ntype) : false;
|
||||
}
|
||||
|
||||
/// \brief Checks if the viewed node contains values/elements of only one type.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto cfg = toml::parse("arr = [ 1, 2, 3 ]");
|
||||
/// std::cout << "homogenous: "sv << cfg["arr"].is_homogeneous() << "\n";
|
||||
/// std::cout << "all doubles: "sv << cfg["arr"].is_homogeneous<double>() << "\n";
|
||||
/// std::cout << "all arrays: "sv << cfg["arr"].is_homogeneous<toml::array>() << "\n";
|
||||
/// std::cout << "all integers: "sv << cfg["arr"].is_homogeneous<int64_t>() << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// homogeneous: true
|
||||
/// all floats: false
|
||||
/// all arrays: false
|
||||
/// all ints: true
|
||||
/// \eout
|
||||
///
|
||||
/// \tparam ElemType A TOML node or value type. <br>
|
||||
/// \conditional_return{Left as `void`} "is every element the same type?" <br>
|
||||
/// \conditional_return{Explicitly specified} "is every element a T?"
|
||||
///
|
||||
/// \returns True if the viewed node was homogeneous.
|
||||
///
|
||||
/// \remarks Always returns `false` if the view does not reference a node, or if the viewed node is
|
||||
/// an empty table or array.
|
||||
template <typename ElemType = void>
|
||||
TOML_PURE_GETTER
|
||||
bool is_homogeneous() const noexcept
|
||||
{
|
||||
return node_ ? node_->template is_homogeneous<impl::unwrap_node<impl::remove_cvref<ElemType>>>() : false;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Type casts
|
||||
/// @{
|
||||
|
||||
/// \brief Gets a pointer to the viewed node as a more specific node type.
|
||||
///
|
||||
/// \tparam T The node type or TOML value type to cast to.
|
||||
///
|
||||
/// \returns A pointer to the node as the given type, or nullptr if it was a different type.
|
||||
///
|
||||
/// \see toml::node::as()
|
||||
template <typename T>
|
||||
TOML_PURE_GETTER
|
||||
auto* as() const noexcept
|
||||
{
|
||||
return node_ ? node_->template as<T>() : nullptr;
|
||||
}
|
||||
|
||||
/// \brief Returns a pointer to the viewed node as a toml::table, if it is one.
|
||||
TOML_PURE_GETTER
|
||||
auto* as_table() const noexcept
|
||||
{
|
||||
return as<table>();
|
||||
}
|
||||
|
||||
/// \brief Returns a pointer to the viewed node as a toml::array, if it is one.
|
||||
TOML_PURE_GETTER
|
||||
auto* as_array() const noexcept
|
||||
{
|
||||
return as<array>();
|
||||
}
|
||||
|
||||
/// \brief Returns a pointer to the viewed node as a toml::value<string>, if it is one.
|
||||
TOML_PURE_GETTER
|
||||
auto* as_string() const noexcept
|
||||
{
|
||||
return as<std::string>();
|
||||
}
|
||||
|
||||
/// \brief Returns a pointer to the viewed node as a toml::value<int64_t>, if it is one.
|
||||
TOML_PURE_GETTER
|
||||
auto* as_integer() const noexcept
|
||||
{
|
||||
return as<int64_t>();
|
||||
}
|
||||
|
||||
/// \brief Returns a pointer to the viewed node as a toml::value<double>, if it is one.
|
||||
TOML_PURE_GETTER
|
||||
auto* as_floating_point() const noexcept
|
||||
{
|
||||
return as<double>();
|
||||
}
|
||||
|
||||
/// \brief Returns a pointer to the viewed node as a toml::value<bool>, if it is one.
|
||||
TOML_PURE_GETTER
|
||||
auto* as_boolean() const noexcept
|
||||
{
|
||||
return as<bool>();
|
||||
}
|
||||
|
||||
/// \brief Returns a pointer to the viewed node as a toml::value<date>, if it is one.
|
||||
TOML_PURE_GETTER
|
||||
auto* as_date() const noexcept
|
||||
{
|
||||
return as<date>();
|
||||
}
|
||||
|
||||
/// \brief Returns a pointer to the viewed node as a toml::value<time>, if it is one.
|
||||
TOML_PURE_GETTER
|
||||
auto* as_time() const noexcept
|
||||
{
|
||||
return as<time>();
|
||||
}
|
||||
|
||||
/// \brief Returns a pointer to the viewed node as a toml::value<date_time>, if it is one.
|
||||
TOML_PURE_GETTER
|
||||
auto* as_date_time() const noexcept
|
||||
{
|
||||
return as<date_time>();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Value retrieval
|
||||
/// @{
|
||||
|
||||
/// \brief Gets the value contained by the referenced node.
|
||||
///
|
||||
/// \detail This function has 'exact' retrieval semantics; the only return value types allowed are the
|
||||
/// TOML native value types, or types that can losslessly represent a native value type (e.g.
|
||||
/// std::wstring on Windows).
|
||||
///
|
||||
/// \tparam T One of the native TOML value types, or a type capable of losslessly representing one.
|
||||
///
|
||||
/// \returns The underlying value if the node was a value of the
|
||||
/// matching type (or losslessly convertible to it), or an empty optional.
|
||||
///
|
||||
/// \see node_view::value()
|
||||
template <typename T>
|
||||
TOML_NODISCARD
|
||||
optional<T> value_exact() const noexcept(impl::value_retrieval_is_nothrow<T>)
|
||||
{
|
||||
if (node_)
|
||||
return node_->template value_exact<T>();
|
||||
return {};
|
||||
}
|
||||
|
||||
/// \brief Gets the value contained by the referenced node.
|
||||
///
|
||||
/// \detail This function has 'permissive' retrieval semantics; some value types are allowed
|
||||
/// to convert to others (e.g. retrieving a boolean as an integer), and the specified return value
|
||||
/// type can be any type where a reasonable conversion from a native TOML value exists
|
||||
/// (e.g. std::wstring on Windows). If the source value cannot be represented by
|
||||
/// the destination type, an empty optional is returned. See node::value() for examples.
|
||||
///
|
||||
/// \tparam T One of the native TOML value types, or a type capable of convertible to one.
|
||||
///
|
||||
/// \returns The underlying value if the node was a value of the matching type (or convertible to it)
|
||||
/// and within the range of the output type, or an empty optional.
|
||||
///
|
||||
/// \note If you want strict value retrieval semantics that do not allow for any type conversions,
|
||||
/// use node_view::value_exact() instead.
|
||||
///
|
||||
/// \see
|
||||
/// - node_view::value()
|
||||
/// - node_view::value_exact()
|
||||
template <typename T>
|
||||
TOML_NODISCARD
|
||||
optional<T> value() const noexcept(impl::value_retrieval_is_nothrow<T>)
|
||||
{
|
||||
if (node_)
|
||||
return node_->template value<T>();
|
||||
return {};
|
||||
}
|
||||
|
||||
/// \brief Gets the raw value contained by the referenced node, or a default.
|
||||
///
|
||||
/// \tparam T Default value type. Must be one of the native TOML value types,
|
||||
/// or convertible to it.
|
||||
/// \param default_value The default value to return if the node wasn't a value, wasn't the
|
||||
/// correct type, or no conversion was possible.
|
||||
///
|
||||
/// \returns The underlying value if the node was a value of the matching type (or convertible to it)
|
||||
/// and within the range of the output type, or the provided default.
|
||||
///
|
||||
/// \note This function has the same permissive retrieval semantics as node::value(). If you want strict
|
||||
/// value retrieval semantics that do not allow for any type conversions, use node_view::value_exact()
|
||||
/// instead.
|
||||
///
|
||||
/// \see
|
||||
/// - node_view::value()
|
||||
/// - node_view::value_exact()
|
||||
template <typename T>
|
||||
TOML_NODISCARD
|
||||
auto value_or(T&& default_value) const noexcept(impl::value_retrieval_is_nothrow<T>)
|
||||
{
|
||||
using namespace ::toml::impl;
|
||||
|
||||
static_assert(!is_wide_string<T> || TOML_ENABLE_WINDOWS_COMPAT,
|
||||
"Retrieving values as wide-character strings is only "
|
||||
"supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled.");
|
||||
|
||||
if constexpr (is_wide_string<T>)
|
||||
{
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
if (node_)
|
||||
return node_->value_or(static_cast<T&&>(default_value));
|
||||
return std::wstring{ static_cast<T&&>(default_value) };
|
||||
|
||||
#else
|
||||
|
||||
static_assert(impl::always_false<T>, "Evaluated unreachable branch!");
|
||||
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
using value_type =
|
||||
std::conditional_t<std::is_pointer_v<std::decay_t<T>>,
|
||||
std::add_pointer_t<std::add_const_t<std::remove_pointer_t<std::decay_t<T>>>>,
|
||||
std::decay_t<T>>;
|
||||
|
||||
if (node_)
|
||||
return node_->value_or(static_cast<T&&>(default_value));
|
||||
if constexpr (std::is_pointer_v<value_type>)
|
||||
return value_type{ default_value };
|
||||
else
|
||||
return static_cast<T&&>(default_value);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Gets a raw reference to the viewed node's underlying data.
|
||||
///
|
||||
/// \warning This function is dangerous if used carelessly and **WILL** break your code if the
|
||||
/// node_view didn't reference a node, or the chosen value type doesn't match the node's
|
||||
/// actual type. In debug builds an assertion will fire when invalid accesses are attempted: \cpp
|
||||
///
|
||||
/// auto tbl = toml::parse(R"(
|
||||
/// min = 32
|
||||
/// max = 45
|
||||
/// )"sv);
|
||||
///
|
||||
/// int64_t& min_ref = tbl["min"].ref<int64_t>(); // matching type
|
||||
/// double& max_ref = tbl["max"].ref<double>(); // mismatched type, hits assert()
|
||||
/// int64_t& foo_ref = tbl["foo"].ref<int64_t>(); // nonexistent key, hits assert()
|
||||
/// \ecpp
|
||||
///
|
||||
/// \note Specifying explicit ref qualifiers acts as an explicit ref-category cast,
|
||||
/// whereas specifying explicit cv-ref qualifiers merges them with whatever
|
||||
/// the cv qualification of the viewed node is (to ensure cv-correctness is propagated), e.g.:
|
||||
/// | node_view | T | return type |
|
||||
/// |-----------------------|------------------------|------------------------------|
|
||||
/// | node_view<node> | std::string | std::string& |
|
||||
/// | node_view<node> | std::string&& | std::string&& |
|
||||
/// | node_view<const node> | volatile std::string | const volatile std::string& |
|
||||
/// | node_view<const node> | volatile std::string&& | const volatile std::string&& |
|
||||
///
|
||||
///
|
||||
/// \tparam T One of the TOML value types.
|
||||
///
|
||||
/// \returns A reference to the underlying data.
|
||||
template <typename T>
|
||||
TOML_PURE_INLINE_GETTER
|
||||
decltype(auto) ref() const noexcept
|
||||
{
|
||||
TOML_ASSERT_ASSUME(node_ && "toml::node_view::ref() called on a node_view that did not reference a node");
|
||||
return node_->template ref<T>();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Visitation
|
||||
/// @{
|
||||
|
||||
private:
|
||||
/// \cond
|
||||
template <typename Func>
|
||||
static constexpr bool visit_is_nothrow = noexcept(std::declval<viewed_type*>()->visit(std::declval<Func>()));
|
||||
/// \endcond
|
||||
|
||||
public:
|
||||
/// \brief Invokes a visitor on the viewed node based on its concrete type.
|
||||
///
|
||||
/// \remarks Has no effect if the view does not reference a node.
|
||||
///
|
||||
/// \see node::visit()
|
||||
template <typename Func>
|
||||
decltype(auto) visit(Func&& visitor) const noexcept(visit_is_nothrow<Func&&>)
|
||||
{
|
||||
using return_type = decltype(node_->visit(static_cast<Func&&>(visitor)));
|
||||
if (node_)
|
||||
return node_->visit(static_cast<Func&&>(visitor));
|
||||
if constexpr (!std::is_void_v<return_type>)
|
||||
return return_type{};
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Equality
|
||||
/// @{
|
||||
|
||||
public:
|
||||
/// \brief Returns true if the two views refer to nodes of the same type and value.
|
||||
template <typename T>
|
||||
TOML_PURE_GETTER
|
||||
friend bool operator==(const node_view& lhs, const node_view<T>& rhs) noexcept
|
||||
{
|
||||
return impl::node_deep_equality(lhs.node_, rhs.node_);
|
||||
}
|
||||
|
||||
/// \brief Returns true if the two views do not refer to nodes of the same type and value.
|
||||
template <typename T>
|
||||
TOML_PURE_GETTER
|
||||
friend bool operator!=(const node_view& lhs, const node_view<T>& rhs) noexcept
|
||||
{
|
||||
return !impl::node_deep_equality(lhs.node_, rhs.node_);
|
||||
}
|
||||
|
||||
/// \brief Returns true if the viewed node is a table with the same contents as RHS.
|
||||
TOML_NODISCARD
|
||||
friend bool operator==(const node_view& lhs, const table& rhs) noexcept
|
||||
{
|
||||
if (lhs.node_ == &rhs)
|
||||
return true;
|
||||
const auto tbl = lhs.as<table>();
|
||||
return tbl && *tbl == rhs;
|
||||
}
|
||||
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const table&, );
|
||||
|
||||
/// \brief Returns true if the viewed node is an array with the same contents as RHS.
|
||||
TOML_NODISCARD
|
||||
friend bool operator==(const node_view& lhs, const array& rhs) noexcept
|
||||
{
|
||||
if (lhs.node_ == &rhs)
|
||||
return true;
|
||||
const auto arr = lhs.as<array>();
|
||||
return arr && *arr == rhs;
|
||||
}
|
||||
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const array&, );
|
||||
|
||||
/// \brief Returns true if the viewed node is a value with the same value as RHS.
|
||||
template <typename T>
|
||||
TOML_NODISCARD
|
||||
friend bool operator==(const node_view& lhs, const toml::value<T>& rhs) noexcept
|
||||
{
|
||||
if (lhs.node_ == &rhs)
|
||||
return true;
|
||||
const auto val = lhs.as<T>();
|
||||
return val && *val == rhs;
|
||||
}
|
||||
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const toml::value<T>&, template <typename T>);
|
||||
|
||||
/// \brief Returns true if the viewed node is a value with the same value as RHS.
|
||||
TOML_CONSTRAINED_TEMPLATE(impl::is_losslessly_convertible_to_native<T>, typename T)
|
||||
TOML_NODISCARD
|
||||
friend bool operator==(const node_view& lhs, const T& rhs) noexcept(!impl::is_wide_string<T>)
|
||||
{
|
||||
static_assert(!impl::is_wide_string<T> || TOML_ENABLE_WINDOWS_COMPAT,
|
||||
"Comparison with wide-character strings is only "
|
||||
"supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled.");
|
||||
|
||||
if constexpr (impl::is_wide_string<T>)
|
||||
{
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
return lhs == impl::narrow(rhs);
|
||||
#else
|
||||
static_assert(impl::always_false<T>, "Evaluated unreachable branch!");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto val = lhs.as<impl::native_type_of<T>>();
|
||||
return val && *val == rhs;
|
||||
}
|
||||
}
|
||||
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&,
|
||||
const T&,
|
||||
TOML_CONSTRAINED_TEMPLATE(impl::is_losslessly_convertible_to_native<T>,
|
||||
typename T));
|
||||
|
||||
/// \brief Returns true if the viewed node is an array with the same contents as the RHS initializer list.
|
||||
template <typename T>
|
||||
TOML_NODISCARD
|
||||
friend bool operator==(const node_view& lhs,
|
||||
const std::initializer_list<T>& rhs) noexcept(!impl::is_wide_string<T>)
|
||||
{
|
||||
const auto arr = lhs.as<array>();
|
||||
return arr && *arr == rhs;
|
||||
}
|
||||
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const std::initializer_list<T>&, template <typename T>);
|
||||
|
||||
/// \brief Returns true if the viewed node is an array with the same contents as the RHS vector.
|
||||
template <typename T>
|
||||
TOML_NODISCARD
|
||||
friend bool operator==(const node_view& lhs, const std::vector<T>& rhs) noexcept(!impl::is_wide_string<T>)
|
||||
{
|
||||
const auto arr = lhs.as<array>();
|
||||
return arr && *arr == rhs;
|
||||
}
|
||||
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const std::vector<T>&, template <typename T>);
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Subviews
|
||||
/// @{
|
||||
|
||||
/// \brief Returns a view of the selected subnode.
|
||||
///
|
||||
/// \param key The key of the node to retrieve
|
||||
///
|
||||
/// \returns A view of the selected node if this node represented a table and it contained a
|
||||
/// value at the given key, or an empty view.
|
||||
TOML_NODISCARD
|
||||
node_view operator[](std::string_view key) const noexcept
|
||||
{
|
||||
if (auto tbl = this->as_table())
|
||||
return node_view{ tbl->get(key) };
|
||||
return {};
|
||||
}
|
||||
|
||||
/// \brief Returns a view of the selected subnode.
|
||||
///
|
||||
/// \param path A "TOML path" to the desired subnode
|
||||
///
|
||||
/// \returns A view of the selected node if this node represented a table and it contained a
|
||||
/// value at the given key, or an empty view.
|
||||
TOML_NODISCARD
|
||||
node_view operator[](const toml::path& path) const noexcept
|
||||
{
|
||||
return node_ ? node_->at_path(path) : node_view{};
|
||||
}
|
||||
|
||||
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \see #toml::node::at_path(std::string_view)
|
||||
TOML_NODISCARD
|
||||
node_view at_path(std::string_view path) const noexcept
|
||||
{
|
||||
return node_ ? node_->at_path(path) : node_view{};
|
||||
}
|
||||
|
||||
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \see #toml::node::at_path(const toml::path&)
|
||||
TOML_NODISCARD
|
||||
node_view at_path(const toml::path& path) const noexcept
|
||||
{
|
||||
return node_ ? node_->at_path(path) : node_view{};
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Returns a view of the selected subnode.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \param key The key of the node to retrieve
|
||||
///
|
||||
/// \returns A view of the selected node if this node represented a table and it contained a
|
||||
/// value at the given key, or an empty view.
|
||||
TOML_NODISCARD
|
||||
node_view operator[](std::wstring_view key) const
|
||||
{
|
||||
if (auto tbl = this->as_table())
|
||||
return node_view{ tbl->get(key) };
|
||||
return {};
|
||||
}
|
||||
|
||||
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \see #toml::node::at_path(std::string_view)
|
||||
TOML_NODISCARD
|
||||
node_view at_path(std::wstring_view path) const
|
||||
{
|
||||
return node_ ? node_->at_path(path) : node_view{};
|
||||
}
|
||||
|
||||
#endif // TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Returns a view of the selected subnode.
|
||||
///
|
||||
/// \param index The index of the node to retrieve
|
||||
///
|
||||
/// \returns A view of the selected node if this node represented an array and it contained a
|
||||
/// value at the given index, or an empty view.
|
||||
TOML_NODISCARD
|
||||
node_view operator[](size_t index) const noexcept
|
||||
{
|
||||
if (auto arr = this->as_array())
|
||||
return node_view{ arr->get(index) };
|
||||
return {};
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
#if TOML_ENABLE_FORMATTERS
|
||||
|
||||
/// \brief Prints the viewed node out to a stream.
|
||||
///
|
||||
/// \availability This operator is only available when #TOML_ENABLE_FORMATTERS is enabled.
|
||||
friend std::ostream& operator<<(std::ostream& os, const node_view& nv)
|
||||
{
|
||||
if (nv.node_)
|
||||
nv.node_->visit([&os](const auto& n) { os << n; });
|
||||
return os;
|
||||
}
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
/// \cond
|
||||
|
||||
template <typename T>
|
||||
node_view(const T&) -> node_view<const node>;
|
||||
|
||||
template <typename T>
|
||||
node_view(const T*) -> node_view<const node>;
|
||||
|
||||
template <typename T>
|
||||
node_view(T&) -> node_view<node>;
|
||||
|
||||
template <typename T>
|
||||
node_view(T*) -> node_view<node>;
|
||||
|
||||
/// \endcond
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
/// \cond
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
inline node::operator node_view<node>() noexcept
|
||||
{
|
||||
return node_view<node>{ this };
|
||||
}
|
||||
|
||||
inline node::operator node_view<const node>() const noexcept
|
||||
{
|
||||
return node_view<const node>{ this };
|
||||
}
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
/// \endcond
|
||||
|
||||
#include "header_end.hpp"
|
||||
139
src/external/toml++/impl/parse_error.hpp
vendored
Normal file
139
src/external/toml++/impl/parse_error.hpp
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
#if TOML_ENABLE_PARSER
|
||||
|
||||
#include "std_except.hpp"
|
||||
#include "source_region.hpp"
|
||||
#include "print_to_stream.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
#if TOML_DOXYGEN || !TOML_EXCEPTIONS
|
||||
#define TOML_PARSE_ERROR_BASE
|
||||
#else
|
||||
#define TOML_PARSE_ERROR_BASE : public std::runtime_error
|
||||
#endif
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex);
|
||||
|
||||
/// \brief An error generated when parsing fails.
|
||||
///
|
||||
/// \remarks This class inherits from std::runtime_error when exceptions are enabled.
|
||||
/// The public interface is the same regardless of exception mode.
|
||||
class parse_error TOML_PARSE_ERROR_BASE
|
||||
{
|
||||
private:
|
||||
#if !TOML_EXCEPTIONS
|
||||
std::string description_;
|
||||
#endif
|
||||
source_region source_;
|
||||
|
||||
public:
|
||||
#if TOML_EXCEPTIONS
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
TOML_ATTR(nonnull)
|
||||
parse_error(const char* desc, source_region&& src) noexcept //
|
||||
: std::runtime_error{ desc },
|
||||
source_{ std::move(src) }
|
||||
{}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
TOML_ATTR(nonnull)
|
||||
parse_error(const char* desc, const source_region& src) noexcept //
|
||||
: parse_error{ desc, source_region{ src } }
|
||||
{}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
TOML_ATTR(nonnull)
|
||||
parse_error(const char* desc, const source_position& position, const source_path_ptr& path = {}) noexcept
|
||||
: parse_error{ desc, source_region{ position, position, path } }
|
||||
{}
|
||||
|
||||
#else
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(std::string&& desc, source_region&& src) noexcept //
|
||||
: description_{ std::move(desc) },
|
||||
source_{ std::move(src) }
|
||||
{}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(std::string&& desc, const source_region& src) noexcept //
|
||||
: parse_error{ std::move(desc), source_region{ src } }
|
||||
{}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(std::string&& desc, const source_position& position, const source_path_ptr& path = {}) noexcept
|
||||
: parse_error{ std::move(desc), source_region{ position, position, path } }
|
||||
{}
|
||||
|
||||
#endif
|
||||
|
||||
/// \brief Returns a textual description of the error.
|
||||
/// \remark The backing string is guaranteed to be null-terminated.
|
||||
TOML_NODISCARD
|
||||
std::string_view description() const noexcept
|
||||
{
|
||||
#if TOML_EXCEPTIONS
|
||||
return std::string_view{ what() };
|
||||
#else
|
||||
return description_;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \brief Returns the region of the source document responsible for the error.
|
||||
TOML_NODISCARD
|
||||
const source_region& source() const noexcept
|
||||
{
|
||||
return source_;
|
||||
}
|
||||
|
||||
/// \brief Prints a parse_error to a stream.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// try
|
||||
/// {
|
||||
/// auto tbl = toml::parse("enabled = trUe"sv);
|
||||
/// }
|
||||
/// catch (const toml::parse_error & err)
|
||||
/// {
|
||||
/// std::cerr << "Parsing failed:\n"sv << err << "\n";
|
||||
/// }
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// Parsing failed:
|
||||
/// Encountered unexpected character while parsing boolean; expected 'true', saw 'trU'
|
||||
/// (error occurred at line 1, column 13)
|
||||
/// \eout
|
||||
///
|
||||
/// \tparam Char The output stream's underlying character type. Must be 1 byte in size.
|
||||
/// \param lhs The stream.
|
||||
/// \param rhs The parse_error.
|
||||
///
|
||||
/// \returns The input stream.
|
||||
friend std::ostream& operator<<(std::ostream& lhs, const parse_error& rhs)
|
||||
{
|
||||
impl::print_to_stream(lhs, rhs.description());
|
||||
impl::print_to_stream(lhs, "\n\t(error occurred at "sv);
|
||||
impl::print_to_stream(lhs, rhs.source());
|
||||
impl::print_to_stream(lhs, ")"sv);
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#undef TOML_PARSE_ERROR_BASE
|
||||
|
||||
#include "header_end.hpp"
|
||||
#endif // TOML_ENABLE_PARSER
|
||||
499
src/external/toml++/impl/parse_result.hpp
vendored
Normal file
499
src/external/toml++/impl/parse_result.hpp
vendored
Normal file
@@ -0,0 +1,499 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
#if TOML_DOXYGEN || (TOML_ENABLE_PARSER && !TOML_EXCEPTIONS)
|
||||
|
||||
#include "table.hpp"
|
||||
#include "parse_error.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_ABI_NAMESPACE_START(noex);
|
||||
|
||||
/// \brief The result of a parsing operation.
|
||||
///
|
||||
/// \availability <strong>This type only exists when exceptions are disabled.</strong>
|
||||
/// Otherwise parse_result is just an alias for toml::table: \cpp
|
||||
/// #if TOML_EXCEPTIONS
|
||||
/// using parse_result = table;
|
||||
/// #else
|
||||
/// class parse_result { // ...
|
||||
/// #endif
|
||||
/// \ecpp
|
||||
///
|
||||
/// \detail A parse_result is effectively a discriminated union containing either a toml::table
|
||||
/// or a toml::parse_error. Most member functions assume a particular one of these two states,
|
||||
/// and calling them when in the wrong state will cause errors (e.g. attempting to access the
|
||||
/// error object when parsing was successful). \cpp
|
||||
/// toml::parse_result result = toml::parse_file("config.toml");
|
||||
/// if (result)
|
||||
/// do_stuff_with_a_table(result); //implicitly converts to table&
|
||||
/// else
|
||||
/// std::cerr << "Parse failed:\n"sv << result.error() << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// example output:
|
||||
///
|
||||
/// Parse failed:
|
||||
/// Encountered unexpected character while parsing boolean; expected 'true', saw 'trU'
|
||||
/// (error occurred at line 1, column 13 of 'config.toml')
|
||||
/// \eout
|
||||
///
|
||||
/// Getting node_views (`operator[]`, `at_path()`) and using the iterator accessor functions (`begin()`, `end()` etc.) are
|
||||
/// unconditionally safe; when parsing fails these just return 'empty' values. A ranged-for loop on a failed
|
||||
/// parse_result is also safe since `begin()` and `end()` return the same iterator and will not lead to any
|
||||
/// dereferences and iterations.
|
||||
class parse_result
|
||||
{
|
||||
private:
|
||||
struct storage_t
|
||||
{
|
||||
static constexpr size_t size =
|
||||
(sizeof(toml::table) < sizeof(parse_error) ? sizeof(parse_error) : sizeof(toml::table));
|
||||
static constexpr size_t align =
|
||||
(alignof(toml::table) < alignof(parse_error) ? alignof(parse_error) : alignof(toml::table));
|
||||
|
||||
alignas(align) unsigned char bytes[size];
|
||||
};
|
||||
|
||||
alignas(storage_t::align) mutable storage_t storage_;
|
||||
bool err_;
|
||||
|
||||
template <typename Type>
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
static Type* get_as(storage_t& s) noexcept
|
||||
{
|
||||
return TOML_LAUNDER(reinterpret_cast<Type*>(s.bytes));
|
||||
}
|
||||
|
||||
void destroy() noexcept
|
||||
{
|
||||
if (err_)
|
||||
get_as<parse_error>(storage_)->~parse_error();
|
||||
else
|
||||
get_as<toml::table>(storage_)->~table();
|
||||
}
|
||||
|
||||
public:
|
||||
/// \brief Default constructs an 'error' result.
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_result() noexcept //
|
||||
: err_{ true }
|
||||
{
|
||||
::new (static_cast<void*>(storage_.bytes)) parse_error{ std::string{}, source_region{} };
|
||||
}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit parse_result(toml::table&& tbl) noexcept //
|
||||
: err_{ false }
|
||||
{
|
||||
::new (static_cast<void*>(storage_.bytes)) toml::table{ std::move(tbl) };
|
||||
}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit parse_result(parse_error&& err) noexcept //
|
||||
: err_{ true }
|
||||
{
|
||||
::new (static_cast<void*>(storage_.bytes)) parse_error{ std::move(err) };
|
||||
}
|
||||
|
||||
/// \brief Move constructor.
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_result(parse_result&& res) noexcept //
|
||||
: err_{ res.err_ }
|
||||
{
|
||||
if (err_)
|
||||
::new (static_cast<void*>(storage_.bytes)) parse_error{ std::move(res).error() };
|
||||
else
|
||||
::new (static_cast<void*>(storage_.bytes)) toml::table{ std::move(res).table() };
|
||||
}
|
||||
|
||||
/// \brief Move-assignment operator.
|
||||
parse_result& operator=(parse_result&& rhs) noexcept
|
||||
{
|
||||
if (err_ != rhs.err_)
|
||||
{
|
||||
destroy();
|
||||
err_ = rhs.err_;
|
||||
if (err_)
|
||||
::new (static_cast<void*>(storage_.bytes)) parse_error{ std::move(rhs).error() };
|
||||
else
|
||||
::new (static_cast<void*>(storage_.bytes)) toml::table{ std::move(rhs).table() };
|
||||
}
|
||||
else
|
||||
{
|
||||
if (err_)
|
||||
error() = std::move(rhs).error();
|
||||
else
|
||||
table() = std::move(rhs).table();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \brief Destructor.
|
||||
~parse_result() noexcept
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
/// \name Result state
|
||||
/// @{
|
||||
|
||||
/// \brief Returns true if parsing succeeeded.
|
||||
TOML_NODISCARD
|
||||
bool succeeded() const noexcept
|
||||
{
|
||||
return !err_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if parsing failed.
|
||||
TOML_NODISCARD
|
||||
bool failed() const noexcept
|
||||
{
|
||||
return err_;
|
||||
}
|
||||
|
||||
/// \brief Returns true if parsing succeeded.
|
||||
TOML_NODISCARD
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return !err_;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Successful parses
|
||||
/// \warning It is undefined behaviour to call these functions when the result respresents a failed parse.
|
||||
/// Check #failed(), #succeeded or #operator bool() to determine the result's state.
|
||||
/// @{
|
||||
|
||||
/// \brief Returns the internal toml::table.
|
||||
TOML_NODISCARD
|
||||
toml::table& table() & noexcept
|
||||
{
|
||||
TOML_ASSERT_ASSUME(!err_);
|
||||
return *get_as<toml::table>(storage_);
|
||||
}
|
||||
|
||||
/// \brief Returns the internal toml::table (rvalue overload).
|
||||
TOML_NODISCARD
|
||||
toml::table&& table() && noexcept
|
||||
{
|
||||
TOML_ASSERT_ASSUME(!err_);
|
||||
return static_cast<toml::table&&>(*get_as<toml::table>(storage_));
|
||||
}
|
||||
|
||||
/// \brief Returns the internal toml::table (const lvalue overload).
|
||||
TOML_NODISCARD
|
||||
const toml::table& table() const& noexcept
|
||||
{
|
||||
TOML_ASSERT_ASSUME(!err_);
|
||||
return *get_as<const toml::table>(storage_);
|
||||
}
|
||||
|
||||
/// \brief Returns the internal toml::table.
|
||||
TOML_NODISCARD
|
||||
/* implicit */ operator toml::table&() noexcept
|
||||
{
|
||||
return table();
|
||||
}
|
||||
|
||||
/// \brief Returns the internal toml::table (rvalue overload).
|
||||
TOML_NODISCARD
|
||||
/* implicit */ operator toml::table&&() noexcept
|
||||
{
|
||||
return std::move(table());
|
||||
}
|
||||
|
||||
/// \brief Returns the internal toml::table (const lvalue overload).
|
||||
TOML_NODISCARD
|
||||
/* implicit */ operator const toml::table&() const noexcept
|
||||
{
|
||||
return table();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Failed parses
|
||||
/// \warning It is undefined behaviour to call these functions when the result respresents a successful parse.
|
||||
/// Check #failed(), #succeeded or #operator bool() to determine the result's state.
|
||||
/// @{
|
||||
|
||||
/// \brief Returns the internal toml::parse_error.
|
||||
TOML_NODISCARD
|
||||
parse_error& error() & noexcept
|
||||
{
|
||||
TOML_ASSERT_ASSUME(err_);
|
||||
return *get_as<parse_error>(storage_);
|
||||
}
|
||||
|
||||
/// \brief Returns the internal toml::parse_error (rvalue overload).
|
||||
TOML_NODISCARD
|
||||
parse_error&& error() && noexcept
|
||||
{
|
||||
TOML_ASSERT_ASSUME(err_);
|
||||
return static_cast<parse_error&&>(*get_as<parse_error>(storage_));
|
||||
}
|
||||
|
||||
/// \brief Returns the internal toml::parse_error (const lvalue overload).
|
||||
TOML_NODISCARD
|
||||
const parse_error& error() const& noexcept
|
||||
{
|
||||
TOML_ASSERT_ASSUME(err_);
|
||||
return *get_as<const parse_error>(storage_);
|
||||
}
|
||||
|
||||
/// \brief Returns the internal toml::parse_error.
|
||||
TOML_NODISCARD
|
||||
explicit operator parse_error&() noexcept
|
||||
{
|
||||
return error();
|
||||
}
|
||||
|
||||
/// \brief Returns the internal toml::parse_error (rvalue overload).
|
||||
TOML_NODISCARD
|
||||
explicit operator parse_error&&() noexcept
|
||||
{
|
||||
return std::move(error());
|
||||
}
|
||||
|
||||
/// \brief Returns the internal toml::parse_error (const lvalue overload).
|
||||
TOML_NODISCARD
|
||||
explicit operator const parse_error&() const noexcept
|
||||
{
|
||||
return error();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
/// \brief A BidirectionalIterator for iterating over key-value pairs in a wrapped toml::table.
|
||||
using iterator = table_iterator;
|
||||
|
||||
/// \brief A BidirectionalIterator for iterating over const key-value pairs in a wrapped toml::table.
|
||||
using const_iterator = const_table_iterator;
|
||||
|
||||
/// \brief Returns an iterator to the first key-value pair in the wrapped table.
|
||||
/// \remarks Always returns the same value as #end() if parsing failed.
|
||||
TOML_NODISCARD
|
||||
table_iterator begin() noexcept
|
||||
{
|
||||
return err_ ? table_iterator{} : table().begin();
|
||||
}
|
||||
|
||||
/// \brief Returns an iterator to the first key-value pair in the wrapped table.
|
||||
/// \remarks Always returns the same value as #end() if parsing failed.
|
||||
TOML_NODISCARD
|
||||
const_table_iterator begin() const noexcept
|
||||
{
|
||||
return err_ ? const_table_iterator{} : table().begin();
|
||||
}
|
||||
|
||||
/// \brief Returns an iterator to the first key-value pair in the wrapped table.
|
||||
/// \remarks Always returns the same value as #cend() if parsing failed.
|
||||
TOML_NODISCARD
|
||||
const_table_iterator cbegin() const noexcept
|
||||
{
|
||||
return err_ ? const_table_iterator{} : table().cbegin();
|
||||
}
|
||||
|
||||
/// \brief Returns an iterator to one-past-the-last key-value pair in the wrapped table.
|
||||
TOML_NODISCARD
|
||||
table_iterator end() noexcept
|
||||
{
|
||||
return err_ ? table_iterator{} : table().end();
|
||||
}
|
||||
|
||||
/// \brief Returns an iterator to one-past-the-last key-value pair in the wrapped table.
|
||||
TOML_NODISCARD
|
||||
const_table_iterator end() const noexcept
|
||||
{
|
||||
return err_ ? const_table_iterator{} : table().end();
|
||||
}
|
||||
|
||||
/// \brief Returns an iterator to one-past-the-last key-value pair in the wrapped table.
|
||||
TOML_NODISCARD
|
||||
const_table_iterator cend() const noexcept
|
||||
{
|
||||
return err_ ? const_table_iterator{} : table().cend();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Node views
|
||||
/// @{
|
||||
|
||||
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \see #toml::node::at_path(std::string_view)
|
||||
TOML_NODISCARD
|
||||
node_view<node> at_path(std::string_view path) noexcept
|
||||
{
|
||||
return err_ ? node_view<node>{} : table().at_path(path);
|
||||
}
|
||||
|
||||
/// \brief Returns a const view of the subnode matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \see #toml::node::at_path(std::string_view)
|
||||
TOML_NODISCARD
|
||||
node_view<const node> at_path(std::string_view path) const noexcept
|
||||
{
|
||||
return err_ ? node_view<const node>{} : table().at_path(path);
|
||||
}
|
||||
|
||||
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \see #toml::node::at_path(const toml::path&)
|
||||
TOML_NODISCARD
|
||||
node_view<node> at_path(const toml::path& path) noexcept
|
||||
{
|
||||
return err_ ? node_view<node>{} : table().at_path(path);
|
||||
}
|
||||
|
||||
/// \brief Returns a const view of the subnode matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \see #toml::node::at_path(const toml::path&)
|
||||
TOML_NODISCARD
|
||||
node_view<const node> at_path(const toml::path& path) const noexcept
|
||||
{
|
||||
return err_ ? node_view<const node>{} : table().at_path(path);
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \see #toml::node::at_path(std::string_view)
|
||||
TOML_NODISCARD
|
||||
node_view<node> at_path(std::wstring_view path)
|
||||
{
|
||||
return err_ ? node_view<node>{} : table().at_path(path);
|
||||
}
|
||||
|
||||
/// \brief Returns a const view of the subnode matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \see #toml::node::at_path(std::string_view)
|
||||
TOML_NODISCARD
|
||||
node_view<const node> at_path(std::wstring_view path) const
|
||||
{
|
||||
return err_ ? node_view<const node>{} : table().at_path(path);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \see #toml::node::operator[](const toml::path&)
|
||||
TOML_NODISCARD
|
||||
node_view<node> operator[](const toml::path& path) noexcept
|
||||
{
|
||||
return err_ ? node_view<node>{} : table()[path];
|
||||
}
|
||||
|
||||
/// \brief Returns a const view of the subnode matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \see #toml::node::operator[](const toml::path&)
|
||||
TOML_NODISCARD
|
||||
node_view<const node> operator[](const toml::path& path) const noexcept
|
||||
{
|
||||
return err_ ? node_view<const node>{} : table()[path];
|
||||
}
|
||||
|
||||
/// \brief Gets a node_view for the selected key-value pair in the wrapped table.
|
||||
///
|
||||
/// \param key The key used for the lookup.
|
||||
///
|
||||
/// \returns A view of the value at the given key if parsing was successful and a matching key existed,
|
||||
/// or an empty node view.
|
||||
///
|
||||
/// \see toml::node_view
|
||||
TOML_NODISCARD
|
||||
node_view<node> operator[](std::string_view key) noexcept
|
||||
{
|
||||
return err_ ? node_view<node>{} : table()[key];
|
||||
}
|
||||
|
||||
/// \brief Gets a node_view for the selected key-value pair in the wrapped table (const overload).
|
||||
///
|
||||
/// \param key The key used for the lookup.
|
||||
///
|
||||
/// \returns A view of the value at the given key if parsing was successful and a matching key existed,
|
||||
/// or an empty node view.
|
||||
///
|
||||
/// \see toml::node_view
|
||||
TOML_NODISCARD
|
||||
node_view<const node> operator[](std::string_view key) const noexcept
|
||||
{
|
||||
return err_ ? node_view<const node>{} : table()[key];
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Gets a node_view for the selected key-value pair in the wrapped table.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \param key The key used for the lookup.
|
||||
///
|
||||
/// \returns A view of the value at the given key if parsing was successful and a matching key existed,
|
||||
/// or an empty node view.
|
||||
///
|
||||
/// \see toml::node_view
|
||||
TOML_NODISCARD
|
||||
node_view<node> operator[](std::wstring_view key)
|
||||
{
|
||||
return err_ ? node_view<node>{} : table()[key];
|
||||
}
|
||||
|
||||
/// \brief Gets a node_view for the selected key-value pair in the wrapped table (const overload).
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \param key The key used for the lookup.
|
||||
///
|
||||
/// \returns A view of the value at the given key if parsing was successful and a matching key existed,
|
||||
/// or an empty node view.
|
||||
///
|
||||
/// \see toml::node_view
|
||||
TOML_NODISCARD
|
||||
node_view<const node> operator[](std::wstring_view key) const
|
||||
{
|
||||
return err_ ? node_view<const node>{} : table()[key];
|
||||
}
|
||||
|
||||
#endif // TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// @}
|
||||
|
||||
#if TOML_ENABLE_FORMATTERS
|
||||
|
||||
/// \brief Prints the held error or table object out to a text stream.
|
||||
///
|
||||
/// \availability This operator is only available when #TOML_ENABLE_FORMATTERS is enabled.
|
||||
friend std::ostream& operator<<(std::ostream& os, const parse_result& result)
|
||||
{
|
||||
return result.err_ ? (os << result.error()) : (os << result.table());
|
||||
}
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
TOML_ABI_NAMESPACE_END;
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
#endif // TOML_ENABLE_PARSER && !TOML_EXCEPTIONS
|
||||
390
src/external/toml++/impl/parser.hpp
vendored
Normal file
390
src/external/toml++/impl/parser.hpp
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
#if TOML_ENABLE_PARSER
|
||||
|
||||
#include "table.hpp"
|
||||
#include "parse_result.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex);
|
||||
|
||||
/// \brief Parses a TOML document from a string view.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto tbl = toml::parse("a = 3"sv);
|
||||
/// std::cout << tbl["a"] << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 3
|
||||
/// \eout
|
||||
///
|
||||
/// \param doc The TOML document to parse. Must be valid UTF-8.
|
||||
/// \param source_path The path used to initialize each node's `source().path`.
|
||||
/// If you don't have a path (or you have no intention of using paths in diagnostics)
|
||||
/// then this parameter can safely be left blank.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
parse_result TOML_CALLCONV parse(std::string_view doc, std::string_view source_path = {});
|
||||
|
||||
/// \brief Parses a TOML document from a string view.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto tbl = toml::parse("a = 3"sv, "foo.toml");
|
||||
/// std::cout << tbl["a"] << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 3
|
||||
/// \eout
|
||||
///
|
||||
/// \param doc The TOML document to parse. Must be valid UTF-8.
|
||||
/// \param source_path The path used to initialize each node's `source().path`.
|
||||
/// If you don't have a path (or you have no intention of using paths in diagnostics)
|
||||
/// then this parameter can safely be left blank.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
parse_result TOML_CALLCONV parse(std::string_view doc, std::string && source_path);
|
||||
|
||||
/// \brief Parses a TOML document from a file.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// toml::parse_result get_foo_toml()
|
||||
/// {
|
||||
/// return toml::parse_file("foo.toml");
|
||||
/// }
|
||||
/// \ecpp
|
||||
///
|
||||
/// \param file_path The TOML document to parse. Must be valid UTF-8.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
parse_result TOML_CALLCONV parse_file(std::string_view file_path);
|
||||
|
||||
#if TOML_HAS_CHAR8
|
||||
|
||||
/// \brief Parses a TOML document from a char8_t string view.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto tbl = toml::parse(u8"a = 3"sv);
|
||||
/// std::cout << tbl["a"] << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 3
|
||||
/// \eout
|
||||
///
|
||||
/// \param doc The TOML document to parse. Must be valid UTF-8.
|
||||
/// \param source_path The path used to initialize each node's `source().path`.
|
||||
/// If you don't have a path (or you have no intention of using paths in diagnostics)
|
||||
/// then this parameter can safely be left blank.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
parse_result TOML_CALLCONV parse(std::u8string_view doc, std::string_view source_path = {});
|
||||
|
||||
/// \brief Parses a TOML document from a char8_t string view.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto tbl = toml::parse(u8"a = 3"sv, "foo.toml");
|
||||
/// std::cout << tbl["a"] << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 3
|
||||
/// \eout
|
||||
///
|
||||
/// \param doc The TOML document to parse. Must be valid UTF-8.
|
||||
/// \param source_path The path used to initialize each node's `source().path`.
|
||||
/// If you don't have a path (or you have no intention of using paths in diagnostics)
|
||||
/// then this parameter can safely be left blank.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
parse_result TOML_CALLCONV parse(std::u8string_view doc, std::string && source_path);
|
||||
|
||||
/// \brief Parses a TOML document from a file.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// toml::parse_result get_foo_toml()
|
||||
/// {
|
||||
/// return toml::parse_file(u8"foo.toml");
|
||||
/// }
|
||||
/// \ecpp
|
||||
///
|
||||
/// \param file_path The TOML document to parse. Must be valid UTF-8.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
parse_result TOML_CALLCONV parse_file(std::u8string_view file_path);
|
||||
|
||||
#endif // TOML_HAS_CHAR8
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Parses a TOML document from a string view.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto tbl = toml::parse("a = 3"sv, L"foo.toml");
|
||||
/// std::cout << tbl["a"] << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 3
|
||||
/// \eout
|
||||
///
|
||||
/// \param doc The TOML document to parse. Must be valid UTF-8.
|
||||
/// \param source_path The path used to initialize each node's `source().path`.
|
||||
/// If you don't have a path (or you have no intention of using paths in diagnostics)
|
||||
/// then this parameter can safely be left blank.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
parse_result TOML_CALLCONV parse(std::string_view doc, std::wstring_view source_path);
|
||||
|
||||
/// \brief Parses a TOML document from a stream.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// std::stringstream ss;
|
||||
/// ss << "a = 3"sv;
|
||||
///
|
||||
/// auto tbl = toml::parse(ss);
|
||||
/// std::cout << tbl["a"] << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 3
|
||||
/// \eout
|
||||
///
|
||||
/// \param doc The TOML document to parse. Must be valid UTF-8.
|
||||
/// \param source_path The path used to initialize each node's `source().path`.
|
||||
/// If you don't have a path (or you have no intention of using paths in diagnostics)
|
||||
/// then this parameter can safely be left blank.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
parse_result TOML_CALLCONV parse(std::istream & doc, std::wstring_view source_path);
|
||||
|
||||
/// \brief Parses a TOML document from a file.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// toml::parse_result get_foo_toml()
|
||||
/// {
|
||||
/// return toml::parse_file(L"foo.toml");
|
||||
/// }
|
||||
/// \ecpp
|
||||
///
|
||||
/// \param file_path The TOML document to parse. Must be valid UTF-8.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
parse_result TOML_CALLCONV parse_file(std::wstring_view file_path);
|
||||
|
||||
#endif // TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
#if TOML_HAS_CHAR8 && TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Parses a TOML document from a char8_t string view.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto tbl = toml::parse(u8"a = 3"sv, L"foo.toml");
|
||||
/// std::cout << tbl["a"] << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 3
|
||||
/// \eout
|
||||
///
|
||||
/// \param doc The TOML document to parse. Must be valid UTF-8.
|
||||
/// \param source_path The path used to initialize each node's `source().path`.
|
||||
/// If you don't have a path (or you have no intention of using paths in diagnostics)
|
||||
/// then this parameter can safely be left blank.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
parse_result TOML_CALLCONV parse(std::u8string_view doc, std::wstring_view source_path);
|
||||
|
||||
#endif // TOML_HAS_CHAR8 && TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Parses a TOML document from a stream.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// std::stringstream ss;
|
||||
/// ss << "a = 3"sv;
|
||||
///
|
||||
/// auto tbl = toml::parse(ss);
|
||||
/// std::cout << tbl["a"] << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 3
|
||||
/// \eout
|
||||
///
|
||||
/// \param doc The TOML document to parse. Must be valid UTF-8.
|
||||
/// \param source_path The path used to initialize each node's `source().path`.
|
||||
/// If you don't have a path (or you have no intention of using paths in diagnostics)
|
||||
/// then this parameter can safely be left blank.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
parse_result TOML_CALLCONV parse(std::istream & doc, std::string_view source_path = {});
|
||||
|
||||
/// \brief Parses a TOML document from a stream.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// std::stringstream ss;
|
||||
/// ss << "a = 3"sv;
|
||||
///
|
||||
/// auto tbl = toml::parse(ss, "foo.toml");
|
||||
/// std::cout << tbl["a"] << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 3
|
||||
/// \eout
|
||||
///
|
||||
/// \param doc The TOML document to parse. Must be valid UTF-8.
|
||||
/// \param source_path The path used to initialize each node's `source().path`.
|
||||
/// If you don't have a path (or you have no intention of using paths in diagnostics)
|
||||
/// then this parameter can safely be left blank.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
parse_result TOML_CALLCONV parse(std::istream & doc, std::string && source_path);
|
||||
|
||||
TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
|
||||
|
||||
inline namespace literals
|
||||
{
|
||||
TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, lit_ex, lit_noex);
|
||||
|
||||
/// \brief Parses TOML data from a string literal.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// using namespace toml::literals;
|
||||
///
|
||||
/// auto tbl = "a = 3"_toml;
|
||||
/// std::cout << tbl["a"] << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 3
|
||||
/// \eout
|
||||
///
|
||||
/// \param str The string data. Must be valid UTF-8.
|
||||
/// \param len The string length.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
parse_result operator""_toml(const char* str, size_t len)
|
||||
{
|
||||
return parse(std::string_view{ str, len });
|
||||
}
|
||||
|
||||
#if TOML_HAS_CHAR8
|
||||
|
||||
/// \brief Parses TOML data from a UTF-8 string literal.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// using namespace toml::literals;
|
||||
///
|
||||
/// auto tbl = u8"a = 3"_toml;
|
||||
/// std::cout << tbl["a"] << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 3
|
||||
/// \eout
|
||||
///
|
||||
/// \param str The string data. Must be valid UTF-8.
|
||||
/// \param len The string length.
|
||||
///
|
||||
/// \returns \conditional_return{With exceptions}
|
||||
/// A toml::table.
|
||||
/// \conditional_return{Without exceptions}
|
||||
/// A toml::parse_result.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
parse_result operator""_toml(const char8_t* str, size_t len)
|
||||
{
|
||||
return parse(std::u8string_view{ str, len });
|
||||
}
|
||||
|
||||
#endif // TOML_HAS_CHAR8
|
||||
|
||||
TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
|
||||
}
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
#endif // TOML_ENABLE_PARSER
|
||||
3917
src/external/toml++/impl/parser.inl
vendored
Normal file
3917
src/external/toml++/impl/parser.inl
vendored
Normal file
File diff suppressed because it is too large
Load Diff
851
src/external/toml++/impl/path.hpp
vendored
Normal file
851
src/external/toml++/impl/path.hpp
vendored
Normal file
@@ -0,0 +1,851 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "forward_declarations.hpp"
|
||||
#include "std_vector.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
/// \brief Indicates type of path component, either a key, an index in an array, or invalid
|
||||
enum class TOML_CLOSED_ENUM path_component_type : uint8_t
|
||||
{
|
||||
key = 0x1,
|
||||
array_index = 0x2
|
||||
};
|
||||
|
||||
/// \brief Represents a single component of a complete 'TOML-path': either a key or an array index
|
||||
class TOML_EXPORTED_CLASS path_component
|
||||
{
|
||||
/// \cond
|
||||
struct storage_t
|
||||
{
|
||||
static constexpr size_t size =
|
||||
(sizeof(size_t) < sizeof(std::string) ? sizeof(std::string) : sizeof(size_t));
|
||||
static constexpr size_t align =
|
||||
(alignof(size_t) < alignof(std::string) ? alignof(std::string) : alignof(size_t));
|
||||
|
||||
alignas(align) unsigned char bytes[size];
|
||||
};
|
||||
alignas(storage_t::align) mutable storage_t value_storage_;
|
||||
|
||||
path_component_type type_;
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXPORTED_STATIC_FUNCTION
|
||||
static bool TOML_CALLCONV equal(const path_component&, const path_component&) noexcept;
|
||||
|
||||
template <typename Type>
|
||||
TOML_PURE_INLINE_GETTER
|
||||
static Type* get_as(storage_t& s) noexcept
|
||||
{
|
||||
return TOML_LAUNDER(reinterpret_cast<Type*>(s.bytes));
|
||||
}
|
||||
|
||||
static void store_key(std::string_view key, storage_t& storage_)
|
||||
{
|
||||
::new (static_cast<void*>(storage_.bytes)) std::string{ key };
|
||||
}
|
||||
|
||||
static void store_index(size_t index, storage_t& storage_) noexcept
|
||||
{
|
||||
::new (static_cast<void*>(storage_.bytes)) std::size_t{ index };
|
||||
}
|
||||
|
||||
void destroy() noexcept
|
||||
{
|
||||
if (type_ == path_component_type::key)
|
||||
get_as<std::string>(value_storage_)->~basic_string();
|
||||
}
|
||||
|
||||
TOML_NODISCARD
|
||||
size_t& index_ref() noexcept
|
||||
{
|
||||
TOML_ASSERT_ASSUME(type_ == path_component_type::array_index);
|
||||
return *get_as<size_t>(value_storage_);
|
||||
}
|
||||
|
||||
TOML_NODISCARD
|
||||
std::string& key_ref() noexcept
|
||||
{
|
||||
TOML_ASSERT_ASSUME(type_ == path_component_type::key);
|
||||
return *get_as<std::string>(value_storage_);
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
public:
|
||||
/// \brief Default constructor (creates an empty key).
|
||||
TOML_NODISCARD_CTOR
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path_component();
|
||||
|
||||
/// \brief Constructor for a path component that is an array index
|
||||
TOML_NODISCARD_CTOR
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path_component(size_t index) noexcept;
|
||||
|
||||
/// \brief Constructor for a path component that is a key string
|
||||
TOML_NODISCARD_CTOR
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path_component(std::string_view key);
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Constructor for a path component that is a key string
|
||||
///
|
||||
/// \availability This constructor is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_NODISCARD_CTOR
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path_component(std::wstring_view key);
|
||||
|
||||
#endif
|
||||
|
||||
/// \brief Copy constructor.
|
||||
TOML_NODISCARD_CTOR
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path_component(const path_component& pc);
|
||||
|
||||
/// \brief Move constructor.
|
||||
TOML_NODISCARD_CTOR
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path_component(path_component&& pc) noexcept;
|
||||
|
||||
/// \brief Copy-assignment operator.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path_component& operator=(const path_component& rhs);
|
||||
|
||||
/// \brief Move-assignment operator.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path_component& operator=(path_component&& rhs) noexcept;
|
||||
|
||||
/// \brief Assigns an array index to this path component.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path_component& operator=(size_t new_index) noexcept;
|
||||
|
||||
/// \brief Assigns a path key to this path component.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path_component& operator=(std::string_view new_key);
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Assigns a path key to this path component.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path_component& operator=(std::wstring_view new_key);
|
||||
|
||||
#endif
|
||||
|
||||
/// \brief Destructor.
|
||||
~path_component() noexcept
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
/// \name Array index accessors
|
||||
/// \warning It is undefined behaviour to call these functions when the path component does not represent an array index.
|
||||
/// Check #type() to determine the component's value type.
|
||||
/// @{
|
||||
|
||||
/// \brief Returns the array index (const lvalue overload).
|
||||
TOML_PURE_GETTER
|
||||
size_t index() const noexcept
|
||||
{
|
||||
TOML_ASSERT_ASSUME(type_ == path_component_type::array_index);
|
||||
return *get_as<const size_t>(value_storage_);
|
||||
}
|
||||
|
||||
/// \brief Returns the array index (const lvalue).
|
||||
TOML_PURE_INLINE_GETTER
|
||||
explicit operator size_t() const noexcept
|
||||
{
|
||||
return index();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Key accessors
|
||||
/// \warning It is undefined behaviour to call these functions when the path component does not represent a key.
|
||||
/// Check #type() to determine the component's value type.
|
||||
/// @{
|
||||
|
||||
/// \brief Returns the key string.
|
||||
TOML_PURE_GETTER
|
||||
const std::string& key() const noexcept
|
||||
{
|
||||
TOML_ASSERT_ASSUME(type_ == path_component_type::key);
|
||||
return *get_as<const std::string>(value_storage_);
|
||||
}
|
||||
|
||||
/// \brief Returns the key string.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
explicit operator const std::string&() const noexcept
|
||||
{
|
||||
return key();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \brief Retrieve the type of this path component, either path_component::key or path_component::array_index
|
||||
TOML_PURE_INLINE_GETTER
|
||||
path_component_type type() const noexcept
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
/// \name Equality
|
||||
/// @{
|
||||
|
||||
/// \brief Returns true if two path components represent the same key or array index.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator==(const path_component& lhs, const path_component& rhs) noexcept
|
||||
{
|
||||
return equal(lhs, rhs);
|
||||
}
|
||||
|
||||
/// \brief Returns true if two path components do not represent the same key or array index.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator!=(const path_component& lhs, const path_component& rhs) noexcept
|
||||
{
|
||||
return !equal(lhs, rhs);
|
||||
}
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
/// \brief A TOML path.
|
||||
///
|
||||
/// \detail This type parses and represents a path to a TOML node. It validates
|
||||
/// the syntax of the path but does not ensure that the path refers to
|
||||
/// a valid node in any particular TOML document. If parsing fails,
|
||||
/// the object will evaluate as 'falsy', and will be empty.
|
||||
///
|
||||
/// \cpp
|
||||
/// toml::path the_path("animals.cats[1]");
|
||||
///
|
||||
/// // can use with tbl.at_path or operator[]
|
||||
/// std::cout << "second cat: " << tbl[the_path] << "\n";
|
||||
/// std::cout << "cats: " << tbl.at_path(the_path.parent_path()) << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// second cat: lion
|
||||
/// cats: ['tiger', 'lion', 'puma']
|
||||
/// \eout
|
||||
class TOML_EXPORTED_CLASS path
|
||||
{
|
||||
private:
|
||||
/// \cond
|
||||
|
||||
std::vector<path_component> components_;
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print_to(std::ostream&) const;
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXPORTED_STATIC_FUNCTION
|
||||
static bool TOML_CALLCONV equal(const path&, const path&) noexcept;
|
||||
|
||||
/// \endcond
|
||||
|
||||
public:
|
||||
/// \brief Default constructor.
|
||||
TOML_NODISCARD_CTOR
|
||||
path() noexcept = default;
|
||||
|
||||
/// \brief Construct a path by parsing from a string.
|
||||
TOML_NODISCARD_CTOR
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
explicit path(std::string_view);
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Construct a path by parsing from a string.
|
||||
///
|
||||
/// \availability This constructor is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_NODISCARD_CTOR
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
explicit path(std::wstring_view);
|
||||
|
||||
#endif
|
||||
|
||||
/// \brief Default destructor.
|
||||
~path() noexcept = default;
|
||||
|
||||
/// \brief Copy constructor.
|
||||
TOML_NODISCARD_CTOR
|
||||
path(const path&) = default;
|
||||
|
||||
/// \brief Move constructor.
|
||||
TOML_NODISCARD_CTOR
|
||||
path(path&&) noexcept = default;
|
||||
|
||||
/// \brief Returns the number of components in the path.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
size_t size() const noexcept
|
||||
{
|
||||
return components_.size();
|
||||
}
|
||||
|
||||
/// \brief Returns true if the path has one or more components.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return !components_.empty();
|
||||
}
|
||||
|
||||
/// \brief Whether (true) or not (false) the path is empty
|
||||
TOML_PURE_INLINE_GETTER
|
||||
bool empty() const noexcept
|
||||
{
|
||||
return components_.empty();
|
||||
}
|
||||
|
||||
/// \brief Fetch a path component by index.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
path_component& operator[](size_t index) noexcept
|
||||
{
|
||||
TOML_ASSERT(index < size());
|
||||
return components_[index];
|
||||
}
|
||||
|
||||
/// \brief Fetch a path component by index (const overload).
|
||||
TOML_PURE_INLINE_GETTER
|
||||
const path_component& operator[](size_t index) const noexcept
|
||||
{
|
||||
TOML_ASSERT(index < size());
|
||||
return components_[index];
|
||||
}
|
||||
|
||||
/// \name Assignment
|
||||
/// @{
|
||||
|
||||
/// \brief Copy-assignment operator.
|
||||
path& operator=(const path&) = default;
|
||||
|
||||
/// \brief Move-assignment operator.
|
||||
path& operator=(path&&) noexcept = default;
|
||||
|
||||
/// \brief Replaces the contents of the path by parsing from a string.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path& operator=(std::string_view);
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Replaces the contents of the path by parsing from a string.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path& operator=(std::wstring_view);
|
||||
|
||||
#endif
|
||||
|
||||
/// \brief Replaces the contents of the path with that of another.
|
||||
TOML_ALWAYS_INLINE
|
||||
path& assign(const path& p)
|
||||
{
|
||||
return *this = p;
|
||||
}
|
||||
|
||||
/// \brief Replaces the contents of the path with that of another.
|
||||
TOML_ALWAYS_INLINE
|
||||
path& assign(path&& p) noexcept
|
||||
{
|
||||
return *this = std::move(p);
|
||||
}
|
||||
|
||||
/// \brief Replaces the contents of the path object by a new path
|
||||
TOML_ALWAYS_INLINE
|
||||
path& assign(std::string_view str)
|
||||
{
|
||||
return *this = str;
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Replaces the contents of the path object by a new path
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_ALWAYS_INLINE
|
||||
path& assign(std::wstring_view str)
|
||||
{
|
||||
return *this = str;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Appending
|
||||
/// @{
|
||||
|
||||
/// \brief Appends another path onto the end of this one.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path& operator+=(const path&);
|
||||
|
||||
/// \brief Appends another path onto the end of this one.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path& operator+=(path&&);
|
||||
|
||||
/// \brief Parses a path and appends it onto the end of this one.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path& operator+=(std::string_view);
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Parses a path and appends it onto the end of this one.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path& operator+=(std::wstring_view);
|
||||
|
||||
#endif
|
||||
|
||||
/// \brief Appends another path onto the end of this one.
|
||||
TOML_ALWAYS_INLINE
|
||||
path& append(const path& p)
|
||||
{
|
||||
return *this += p;
|
||||
}
|
||||
|
||||
/// \brief Appends another path onto the end of this one.
|
||||
TOML_ALWAYS_INLINE
|
||||
path& append(path&& p)
|
||||
{
|
||||
return *this += std::move(p);
|
||||
}
|
||||
|
||||
/// \brief Parses a path and appends it onto the end of this one.
|
||||
TOML_ALWAYS_INLINE
|
||||
path& append(std::string_view str)
|
||||
{
|
||||
return *this += str;
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Parses a path and appends it onto the end of this one.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_ALWAYS_INLINE
|
||||
path& append(std::wstring_view str)
|
||||
{
|
||||
return *this += str;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Prepending
|
||||
/// @{
|
||||
|
||||
/// \brief Prepends another path onto the beginning of this one.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path& prepend(const path&);
|
||||
|
||||
/// \brief Prepends another path onto the beginning of this one.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path& prepend(path&&);
|
||||
|
||||
/// \brief Parses a path and prepends it onto the beginning of this one.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path& prepend(std::string_view);
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Parses a path and prepends it onto the beginning of this one.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path& prepend(std::wstring_view);
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Concatenation
|
||||
/// @{
|
||||
|
||||
/// \brief Concatenates two paths.
|
||||
TOML_NODISCARD
|
||||
friend path operator+(const path& lhs, const path& rhs)
|
||||
{
|
||||
path result = lhs;
|
||||
result += rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// \brief Concatenates two paths.
|
||||
TOML_NODISCARD
|
||||
friend path operator+(const path& lhs, std::string_view rhs)
|
||||
{
|
||||
path result = lhs;
|
||||
result += rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// \brief Concatenates two paths.
|
||||
TOML_NODISCARD
|
||||
friend path operator+(std::string_view lhs, const path& rhs)
|
||||
{
|
||||
path result = rhs;
|
||||
result.prepend(lhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Concatenates two paths.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_NODISCARD
|
||||
friend path operator+(const path& lhs, std::wstring_view rhs)
|
||||
{
|
||||
path result = lhs;
|
||||
result += rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// \brief Concatenates two paths.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_NODISCARD
|
||||
friend path operator+(std::wstring_view lhs, const path& rhs)
|
||||
{
|
||||
path result = rhs;
|
||||
result.prepend(lhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name String conversion
|
||||
/// @{
|
||||
|
||||
/// \brief Prints the string representation of a #toml::path out to a stream.
|
||||
TOML_ALWAYS_INLINE
|
||||
friend std::ostream& operator<<(std::ostream& os, const path& rhs)
|
||||
{
|
||||
rhs.print_to(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
/// \brief Returns a string representation of this path.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
std::string str() const;
|
||||
|
||||
/// \brief Returns a string representation of this path.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
explicit operator std::string() const
|
||||
{
|
||||
return str();
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Returns a string representation of this path.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
std::wstring wide_str() const;
|
||||
|
||||
/// \brief Returns a string representation of this path.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
explicit operator std::wstring() const
|
||||
{
|
||||
return wide_str();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Equality
|
||||
/// @{
|
||||
|
||||
/// \brief Returns whether two paths are the same.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator==(const path& lhs, const path& rhs) noexcept
|
||||
{
|
||||
return equal(lhs, rhs);
|
||||
}
|
||||
|
||||
/// \brief Returns whether two paths are not the same.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend bool operator!=(const path& lhs, const path& rhs) noexcept
|
||||
{
|
||||
return !equal(lhs, rhs);
|
||||
}
|
||||
|
||||
/// \brief Returns whether two paths are the same.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
friend bool operator==(const path& lhs, std::string_view rhs)
|
||||
{
|
||||
return lhs == path{ rhs };
|
||||
}
|
||||
|
||||
/// \brief Returns whether two paths are the same.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
friend bool operator==(std::string_view lhs, const path& rhs)
|
||||
{
|
||||
return rhs == lhs;
|
||||
}
|
||||
|
||||
/// \brief Returns whether two paths are not the same.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
friend bool operator!=(const path& lhs, std::string_view rhs)
|
||||
{
|
||||
return lhs != path{ rhs };
|
||||
}
|
||||
|
||||
/// \brief Returns whether two paths are not the same.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
friend bool operator!=(std::string_view lhs, const path& rhs)
|
||||
{
|
||||
return rhs != lhs;
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief Returns whether two paths are the same.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
friend bool operator==(const path& lhs, std::wstring_view rhs)
|
||||
{
|
||||
return lhs == path{ rhs };
|
||||
}
|
||||
|
||||
/// \brief Returns whether two paths are the same.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
friend bool operator==(std::wstring_view lhs, const path& rhs)
|
||||
{
|
||||
return rhs == lhs;
|
||||
}
|
||||
|
||||
/// \brief Returns whether two paths are not the same.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
friend bool operator!=(const path& lhs, std::wstring_view rhs)
|
||||
{
|
||||
return lhs != path{ rhs };
|
||||
}
|
||||
|
||||
/// \brief Returns whether two paths are not the same.
|
||||
///
|
||||
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
friend bool operator!=(std::wstring_view lhs, const path& rhs)
|
||||
{
|
||||
return rhs != lhs;
|
||||
}
|
||||
|
||||
#endif // TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
/// An iterator for iterating over the components in the path.
|
||||
/// \see #toml::path_component
|
||||
using iterator = std::vector<path_component>::iterator;
|
||||
|
||||
/// A const iterator for iterating over the components in the path.
|
||||
/// \see #toml::path_component
|
||||
using const_iterator = std::vector<path_component>::const_iterator;
|
||||
|
||||
/// \brief Returns an iterator to the first component in the path.
|
||||
/// \see #toml::path_component
|
||||
TOML_PURE_INLINE_GETTER
|
||||
iterator begin() noexcept
|
||||
{
|
||||
return components_.begin();
|
||||
}
|
||||
|
||||
/// \brief Returns an iterator to one-past-the-last component in the path.
|
||||
/// \see #toml::path_component
|
||||
TOML_PURE_INLINE_GETTER
|
||||
iterator end() noexcept
|
||||
{
|
||||
return components_.end();
|
||||
}
|
||||
|
||||
/// \brief Returns a const iterator to the first component in the path.
|
||||
/// \see #toml::path_component
|
||||
TOML_PURE_INLINE_GETTER
|
||||
const_iterator begin() const noexcept
|
||||
{
|
||||
return components_.begin();
|
||||
}
|
||||
|
||||
/// \brief Returns a const iterator to one-past-the-last component in the path.
|
||||
/// \see #toml::path_component
|
||||
TOML_PURE_INLINE_GETTER
|
||||
const_iterator end() const noexcept
|
||||
{
|
||||
return components_.end();
|
||||
}
|
||||
|
||||
/// \brief Returns a const iterator to the first component in the path.
|
||||
/// \see #toml::path_component
|
||||
TOML_PURE_INLINE_GETTER
|
||||
const_iterator cbegin() const noexcept
|
||||
{
|
||||
return components_.begin();
|
||||
}
|
||||
|
||||
/// \brief Returns a const iterator to one-past-the-last component in the path.
|
||||
/// \see #toml::path_component
|
||||
TOML_PURE_INLINE_GETTER
|
||||
const_iterator cend() const noexcept
|
||||
{
|
||||
return components_.end();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Subpaths and Truncation
|
||||
/// @{
|
||||
|
||||
/// \brief Erases the contents of the path.
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void clear() noexcept;
|
||||
|
||||
/// \brief Removes the number of terminal path components specified by n
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path& truncate(size_t n);
|
||||
|
||||
/// \brief Returns a toml::path object which has had n terminal path components removed
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path truncated(size_t n) const;
|
||||
|
||||
/// \brief Returns a toml::path object representing the path of the parent node
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path parent() const;
|
||||
|
||||
/// \brief Returns a toml::path object representing terminal n-parts of a TOML path
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path leaf(size_t n = 1) const;
|
||||
|
||||
/// \brief Returns a toml::path object that is a specified subpath of the current path, representing the
|
||||
/// range of path components from [start, end).
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path subpath(const_iterator start, const_iterator end) const;
|
||||
|
||||
/// \brief Returns a toml::path object that is a specified subpath of the current path, representing the
|
||||
/// range of path components with indexes from [start, start + length].
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
path subpath(size_t start, size_t length) const;
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
inline namespace literals
|
||||
{
|
||||
/// \brief Parses a TOML path from a string literal.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// using namespace toml::literals;
|
||||
///
|
||||
/// auto path = "main.settings.devices[2]"_tpath;
|
||||
/// std::cout << path.parent_path() << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// main.settings.devices
|
||||
/// \eout
|
||||
///
|
||||
/// \param str The string data.
|
||||
/// \param len The string length.
|
||||
///
|
||||
/// \returns A #toml::path generated from the string literal.
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
path operator""_tpath(const char* str, size_t len)
|
||||
{
|
||||
return path(std::string_view{ str, len });
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Returns a view of the node matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto config = toml::parse(R"(
|
||||
///
|
||||
/// [foo]
|
||||
/// bar = [ 0, 1, 2, [ 3 ], { kek = 4 } ]
|
||||
///
|
||||
/// )"sv);
|
||||
///
|
||||
/// toml::path path1("foo.bar[2]");
|
||||
/// toml::path path2("foo.bar[4].kek");
|
||||
/// std::cout << toml::at_path(config, path1) << "\n";
|
||||
/// std::cout << toml::at_path(config, path1.parent_path()) << "\n";
|
||||
/// std::cout << toml::at_path(config, path2) << "\n";
|
||||
/// std::cout << toml::at_path(config, path2.parent_path()) << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// 2
|
||||
/// [ 0, 1, 2, [ 3 ], { kek = 4 } ]
|
||||
/// 4
|
||||
/// { kek = 4 }
|
||||
/// \eout
|
||||
///
|
||||
///
|
||||
/// \note Keys in paths are interpreted literally, so whitespace (or lack thereof) matters:
|
||||
/// \cpp
|
||||
/// toml::at_path(config, toml::path("foo.bar")) // same as config["foo"]["bar"]
|
||||
/// toml::at_path(config, toml::path("foo. bar")) // same as config["foo"][" bar"]
|
||||
/// toml::at_path(config, toml::path("foo..bar")) // same as config["foo"][""]["bar"]
|
||||
/// toml::at_path(config, toml::path(".foo.bar")) // same as config[""]["foo"]["bar"]
|
||||
/// \ecpp
|
||||
/// <br>
|
||||
/// Additionally, TOML allows '.' (period) characters to appear in keys if they are quoted strings.
|
||||
/// This function makes no allowance for this, instead treating all period characters as sub-table delimiters.
|
||||
///
|
||||
/// \param root The root node from which the path will be traversed.
|
||||
/// \param path The "TOML path" to traverse.
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
node_view<node> TOML_CALLCONV at_path(node & root, const toml::path& path) noexcept;
|
||||
|
||||
/// \brief Returns a const view of the node matching a fully-qualified "TOML path".
|
||||
///
|
||||
/// \see #toml::at_path(node&, const toml::path& path)
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
node_view<const node> TOML_CALLCONV at_path(const node& root, const toml::path& path) noexcept;
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
523
src/external/toml++/impl/path.inl
vendored
Normal file
523
src/external/toml++/impl/path.inl
vendored
Normal file
@@ -0,0 +1,523 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
//# {{
|
||||
#include "preprocessor.hpp"
|
||||
#if !TOML_IMPLEMENTATION
|
||||
#error This is an implementation-only header.
|
||||
#endif
|
||||
//# }}
|
||||
|
||||
#include "path.hpp"
|
||||
#include "at_path.hpp"
|
||||
#include "print_to_stream.hpp"
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#if TOML_INT_CHARCONV
|
||||
#include <charconv>
|
||||
#endif
|
||||
#include <sstream>
|
||||
TOML_ENABLE_WARNINGS;
|
||||
#include "header_start.hpp"
|
||||
|
||||
//#=====================================================================================================================
|
||||
//# toml::path_component
|
||||
//#=====================================================================================================================
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path_component::path_component() //
|
||||
: type_{ path_component_type::key }
|
||||
{
|
||||
store_key("", value_storage_);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path_component::path_component(size_t index) noexcept //
|
||||
: type_(path_component_type::array_index)
|
||||
{
|
||||
store_index(index, value_storage_);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path_component::path_component(std::string_view key) //
|
||||
: type_(path_component_type::key)
|
||||
{
|
||||
store_key(key, value_storage_);
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path_component::path_component(std::wstring_view key) //
|
||||
: path_component(impl::narrow(key))
|
||||
{}
|
||||
|
||||
#endif
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path_component::path_component(const path_component& pc) //
|
||||
: type_{ pc.type_ }
|
||||
{
|
||||
if (type_ == path_component_type::array_index)
|
||||
store_index(pc.index(), value_storage_);
|
||||
else
|
||||
store_key(pc.key(), value_storage_);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path_component::path_component(path_component && pc) noexcept //
|
||||
: type_{ pc.type_ }
|
||||
{
|
||||
if (type_ == path_component_type::array_index)
|
||||
store_index(pc.index_ref(), value_storage_);
|
||||
else
|
||||
store_key(std::move(pc.key_ref()), value_storage_);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path_component& path_component::operator=(const path_component& rhs)
|
||||
{
|
||||
if (type_ != rhs.type_)
|
||||
{
|
||||
destroy();
|
||||
|
||||
type_ = rhs.type_;
|
||||
if (type_ == path_component_type::array_index)
|
||||
store_index(rhs.index(), value_storage_);
|
||||
else
|
||||
store_key(rhs.key(), value_storage_);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type_ == path_component_type::array_index)
|
||||
index_ref() = rhs.index();
|
||||
else
|
||||
key_ref() = rhs.key();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path_component& path_component::operator=(path_component&& rhs) noexcept
|
||||
{
|
||||
if (type_ != rhs.type_)
|
||||
{
|
||||
destroy();
|
||||
|
||||
type_ = rhs.type_;
|
||||
if (type_ == path_component_type::array_index)
|
||||
store_index(rhs.index(), value_storage_);
|
||||
else
|
||||
store_key(std::move(rhs.key_ref()), value_storage_);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type_ == path_component_type::array_index)
|
||||
index_ref() = rhs.index();
|
||||
else
|
||||
key_ref() = std::move(rhs.key_ref());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool TOML_CALLCONV path_component::equal(const path_component& lhs, const path_component& rhs) noexcept
|
||||
{
|
||||
// Different comparison depending on contents
|
||||
if (lhs.type_ != rhs.type_)
|
||||
return false;
|
||||
|
||||
if (lhs.type_ == path_component_type::array_index)
|
||||
return lhs.index() == rhs.index();
|
||||
else // path_component_type::key
|
||||
return lhs.key() == rhs.key();
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path_component& path_component::operator=(size_t new_index) noexcept
|
||||
{
|
||||
// If currently a key, string will need to be destroyed regardless
|
||||
destroy();
|
||||
|
||||
type_ = path_component_type::array_index;
|
||||
store_index(new_index, value_storage_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path_component& path_component::operator=(std::string_view new_key)
|
||||
{
|
||||
if (type_ == path_component_type::key)
|
||||
key_ref() = new_key;
|
||||
else
|
||||
{
|
||||
type_ = path_component_type::key;
|
||||
store_key(new_key, value_storage_);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path_component& path_component::operator=(std::wstring_view new_key)
|
||||
{
|
||||
if (type_ == path_component_type::key)
|
||||
key_ref() = impl::narrow(new_key);
|
||||
else
|
||||
{
|
||||
type_ = path_component_type::key;
|
||||
store_key(impl::narrow(new_key), value_storage_);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
//#=====================================================================================================================
|
||||
//# toml::path
|
||||
//#=====================================================================================================================
|
||||
|
||||
TOML_ANON_NAMESPACE_START
|
||||
{
|
||||
TOML_INTERNAL_LINKAGE
|
||||
bool parse_path_into(std::string_view path_str, std::vector<path_component> & components)
|
||||
{
|
||||
using components_type = std::remove_reference_t<decltype(components)>;
|
||||
|
||||
const auto original_size = components.size();
|
||||
|
||||
static constexpr auto on_key = [](void* data, std::string_view key) -> bool
|
||||
{
|
||||
auto& comps = *static_cast<components_type*>(data);
|
||||
comps.emplace_back(key);
|
||||
return true;
|
||||
};
|
||||
|
||||
static constexpr auto on_index = [](void* data, size_t index) -> bool
|
||||
{
|
||||
auto& comps = *static_cast<components_type*>(data);
|
||||
comps.emplace_back(index);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!impl::parse_path(path_str, &components, on_key, on_index))
|
||||
{
|
||||
components.resize(original_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
TOML_ANON_NAMESPACE_END;
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void path::print_to(std::ostream & os) const
|
||||
{
|
||||
bool root = true;
|
||||
for (const auto& component : components_)
|
||||
{
|
||||
if (component.type() == path_component_type::key) // key
|
||||
{
|
||||
if (!root)
|
||||
impl::print_to_stream(os, '.');
|
||||
impl::print_to_stream(os, component.key());
|
||||
}
|
||||
else if (component.type() == path_component_type::array_index) // array
|
||||
{
|
||||
impl::print_to_stream(os, '[');
|
||||
impl::print_to_stream(os, component.index());
|
||||
impl::print_to_stream(os, ']');
|
||||
}
|
||||
root = false;
|
||||
}
|
||||
}
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool TOML_CALLCONV path::equal(const path& lhs, const path& rhs) noexcept
|
||||
{
|
||||
return lhs.components_ == rhs.components_;
|
||||
}
|
||||
|
||||
//#=== constructors =================================================
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path::path(std::string_view str) //
|
||||
{
|
||||
TOML_ANON_NAMESPACE::parse_path_into(str, components_);
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path::path(std::wstring_view str) //
|
||||
: path(impl::narrow(str))
|
||||
{}
|
||||
|
||||
#endif
|
||||
|
||||
//#=== assignment =================================================
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path& path::operator=(std::string_view rhs)
|
||||
{
|
||||
components_.clear();
|
||||
TOML_ANON_NAMESPACE::parse_path_into(rhs, components_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path& path::operator=(std::wstring_view rhs)
|
||||
{
|
||||
return assign(impl::narrow(rhs));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//#=== appending =================================================
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path& path::operator+=(const path& rhs)
|
||||
{
|
||||
components_.insert(components_.cend(), rhs.begin(), rhs.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path& path::operator+=(path&& rhs)
|
||||
{
|
||||
components_.insert(components_.end(),
|
||||
std::make_move_iterator(rhs.components_.begin()),
|
||||
std::make_move_iterator(rhs.components_.end()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path& path::operator+=(std::string_view str)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::parse_path_into(str, components_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path& path::operator+=(std::wstring_view str)
|
||||
{
|
||||
return *this += impl::narrow(str);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//#=== prepending =================================================
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path& path::prepend(const path& source)
|
||||
{
|
||||
components_.insert(components_.begin(), source.components_.begin(), source.components_.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path& path::prepend(path && source)
|
||||
{
|
||||
components_.insert(components_.begin(),
|
||||
std::make_move_iterator(source.components_.begin()),
|
||||
std::make_move_iterator(source.components_.end()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path& path::prepend(std::string_view source)
|
||||
{
|
||||
return prepend(path{ source });
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path& path::prepend(std::wstring_view source)
|
||||
{
|
||||
return prepend(impl::narrow(source));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//#=== string conversion =================================================
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
std::string path::str() const
|
||||
{
|
||||
if (empty())
|
||||
return "";
|
||||
|
||||
std::ostringstream ss;
|
||||
print_to(ss);
|
||||
return std::move(ss).str();
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
std::wstring path::wide_str() const
|
||||
{
|
||||
return impl::widen(str());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//#=== equality and comparison =================================================
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void path::clear() noexcept
|
||||
{
|
||||
components_.clear();
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path& path::truncate(size_t n)
|
||||
{
|
||||
n = n > components_.size() ? components_.size() : n;
|
||||
|
||||
auto it_end = components_.end();
|
||||
components_.erase(it_end - static_cast<int>(n), it_end);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path path::truncated(size_t n) const
|
||||
{
|
||||
path truncated_path{};
|
||||
|
||||
n = n > components_.size() ? components_.size() : n;
|
||||
|
||||
// Copy all components except one
|
||||
// Need at least two path components to have a parent, since if there is
|
||||
// only one path component, the parent is the root/null path ""
|
||||
truncated_path.components_.insert(truncated_path.components_.begin(),
|
||||
components_.begin(),
|
||||
components_.end() - static_cast<int>(n));
|
||||
|
||||
return truncated_path;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path path::parent() const
|
||||
{
|
||||
return truncated(1);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path path::leaf(size_t n) const
|
||||
{
|
||||
path leaf_path{};
|
||||
|
||||
n = n > components_.size() ? components_.size() : n;
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
leaf_path.components_.insert(leaf_path.components_.begin(),
|
||||
components_.end() - static_cast<int>(n),
|
||||
components_.end());
|
||||
}
|
||||
|
||||
return leaf_path;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path path::subpath(std::vector<path_component>::const_iterator start,
|
||||
std::vector<path_component>::const_iterator end) const
|
||||
{
|
||||
if (start >= end)
|
||||
return {};
|
||||
|
||||
path subpath;
|
||||
subpath.components_.insert(subpath.components_.begin(), start, end);
|
||||
return subpath;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
path path::subpath(size_t start, size_t length) const
|
||||
{
|
||||
return subpath(begin() + static_cast<int>(start), begin() + static_cast<int>(start + length));
|
||||
}
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
//#=====================================================================================================================
|
||||
//# at_path() overloads for toml::path
|
||||
//#=====================================================================================================================
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<node> TOML_CALLCONV at_path(node & root, const toml::path& path) noexcept
|
||||
{
|
||||
// early-exit sanity-checks
|
||||
if (root.is_value())
|
||||
return {};
|
||||
if (auto tbl = root.as_table(); tbl && tbl->empty())
|
||||
return {};
|
||||
if (auto arr = root.as_array(); arr && arr->empty())
|
||||
return {};
|
||||
|
||||
node* current = &root;
|
||||
|
||||
for (const auto& component : path)
|
||||
{
|
||||
auto type = component.type();
|
||||
if (type == path_component_type::array_index)
|
||||
{
|
||||
const auto current_array = current->as<array>();
|
||||
if (!current_array)
|
||||
return {}; // not an array, using array index doesn't work
|
||||
|
||||
current = current_array->get(component.index());
|
||||
}
|
||||
else if (type == path_component_type::key)
|
||||
{
|
||||
const auto current_table = current->as<table>();
|
||||
if (!current_table)
|
||||
return {};
|
||||
|
||||
current = current_table->get(component.key());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Error: invalid component
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!current)
|
||||
return {}; // not found
|
||||
}
|
||||
|
||||
return node_view{ current };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node_view<const node> TOML_CALLCONV at_path(const node& root, const toml::path& path) noexcept
|
||||
{
|
||||
return node_view<const node>{ at_path(const_cast<node&>(root), path).node() };
|
||||
}
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
1471
src/external/toml++/impl/preprocessor.hpp
vendored
Normal file
1471
src/external/toml++/impl/preprocessor.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
129
src/external/toml++/impl/print_to_stream.hpp
vendored
Normal file
129
src/external/toml++/impl/print_to_stream.hpp
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "std_string.hpp"
|
||||
#include "forward_declarations.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
// Q: "why does print_to_stream() exist? why not just use ostream::write(), ostream::put() etc?"
|
||||
// A: - I'm using <charconv> to format numerics. Faster and locale-independent.
|
||||
// - I can (potentially) avoid forcing users to drag in <sstream> and <iomanip>.
|
||||
// - Strings in C++. Honestly.
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
TOML_ATTR(nonnull)
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const char*, size_t);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, std::string_view);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const std::string&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, char);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, signed char, value_flags = {}, size_t min_digits = 0);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, signed short, value_flags = {}, size_t min_digits = 0);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, signed int, value_flags = {}, size_t min_digits = 0);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, signed long, value_flags = {}, size_t min_digits = 0);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, signed long long, value_flags = {}, size_t min_digits = 0);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, unsigned char, value_flags = {}, size_t min_digits = 0);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, unsigned short, value_flags = {}, size_t min_digits = 0);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, unsigned int, value_flags = {}, size_t min_digits = 0);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, unsigned long, value_flags = {}, size_t min_digits = 0);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, unsigned long long, value_flags = {}, size_t min_digits = 0);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, float, value_flags = {}, bool relaxed_precision = false);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, double, value_flags = {}, bool relaxed_precision = false);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, bool);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const toml::date&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const toml::time&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const toml::time_offset&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const toml::date_time&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const source_position&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const source_region&);
|
||||
|
||||
#if TOML_ENABLE_FORMATTERS
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const array&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const table&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const value<std::string>&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const value<int64_t>&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const value<double>&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const value<bool>&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const value<date>&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const value<time>&);
|
||||
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
void TOML_CALLCONV print_to_stream(std::ostream&, const value<date_time>&);
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T, typename U>
|
||||
inline void print_to_stream_bookended(std::ostream & stream, const T& val, const U& bookend)
|
||||
{
|
||||
print_to_stream(stream, bookend);
|
||||
print_to_stream(stream, val);
|
||||
print_to_stream(stream, bookend);
|
||||
}
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
495
src/external/toml++/impl/print_to_stream.inl
vendored
Normal file
495
src/external/toml++/impl/print_to_stream.inl
vendored
Normal file
@@ -0,0 +1,495 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
//# {{
|
||||
#include "preprocessor.hpp"
|
||||
#if !TOML_IMPLEMENTATION
|
||||
#error This is an implementation-only header.
|
||||
#endif
|
||||
//# }}
|
||||
|
||||
#include "print_to_stream.hpp"
|
||||
#include "source_region.hpp"
|
||||
#include "date_time.hpp"
|
||||
#include "toml_formatter.hpp"
|
||||
#include "value.hpp"
|
||||
#include "array.hpp"
|
||||
#include "table.hpp"
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#include <ostream>
|
||||
#if TOML_INT_CHARCONV || TOML_FLOAT_CHARCONV
|
||||
#include <charconv>
|
||||
#endif
|
||||
#if !TOML_INT_CHARCONV || !TOML_FLOAT_CHARCONV
|
||||
#include <sstream>
|
||||
#endif
|
||||
#if !TOML_INT_CHARCONV
|
||||
#include <iomanip>
|
||||
#endif
|
||||
TOML_ENABLE_WARNINGS;
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_ANON_NAMESPACE_START
|
||||
{
|
||||
template <typename T>
|
||||
inline constexpr size_t charconv_buffer_length = 0;
|
||||
|
||||
template <>
|
||||
inline constexpr size_t charconv_buffer_length<int8_t> = 4; // strlen("-128")
|
||||
|
||||
template <>
|
||||
inline constexpr size_t charconv_buffer_length<int16_t> = 6; // strlen("-32768")
|
||||
|
||||
template <>
|
||||
inline constexpr size_t charconv_buffer_length<int32_t> = 11; // strlen("-2147483648")
|
||||
|
||||
template <>
|
||||
inline constexpr size_t charconv_buffer_length<int64_t> = 20; // strlen("-9223372036854775808")
|
||||
|
||||
template <>
|
||||
inline constexpr size_t charconv_buffer_length<uint8_t> = 3; // strlen("255")
|
||||
|
||||
template <>
|
||||
inline constexpr size_t charconv_buffer_length<uint16_t> = 5; // strlen("65535")
|
||||
|
||||
template <>
|
||||
inline constexpr size_t charconv_buffer_length<uint32_t> = 10; // strlen("4294967295")
|
||||
|
||||
template <>
|
||||
inline constexpr size_t charconv_buffer_length<uint64_t> = 20; // strlen("18446744073709551615")
|
||||
|
||||
template <>
|
||||
inline constexpr size_t charconv_buffer_length<float> = 64;
|
||||
|
||||
template <>
|
||||
inline constexpr size_t charconv_buffer_length<double> = 64;
|
||||
|
||||
template <typename T>
|
||||
TOML_INTERNAL_LINKAGE
|
||||
void print_integer_to_stream(std::ostream & stream, T val, value_flags format = {}, size_t min_digits = 0)
|
||||
{
|
||||
if (!val)
|
||||
{
|
||||
if (!min_digits)
|
||||
min_digits = 1;
|
||||
|
||||
for (size_t i = 0; i < min_digits; i++)
|
||||
stream.put('0');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr auto value_flags_mask =
|
||||
value_flags::format_as_binary | value_flags::format_as_octal | value_flags::format_as_hexadecimal;
|
||||
format &= value_flags_mask;
|
||||
|
||||
int base = 10;
|
||||
if (format != value_flags::none && val > T{})
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case value_flags::format_as_binary: base = 2; break;
|
||||
case value_flags::format_as_octal: base = 8; break;
|
||||
case value_flags::format_as_hexadecimal: base = 16; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
#if TOML_INT_CHARCONV
|
||||
|
||||
char buf[(sizeof(T) * CHAR_BIT)];
|
||||
const auto res = std::to_chars(buf, buf + sizeof(buf), val, base);
|
||||
const auto len = static_cast<size_t>(res.ptr - buf);
|
||||
for (size_t i = len; i < min_digits; i++)
|
||||
stream.put('0');
|
||||
if (base == 16)
|
||||
{
|
||||
for (size_t i = 0; i < len; i++)
|
||||
if (buf[i] >= 'a')
|
||||
buf[i] -= 32;
|
||||
}
|
||||
impl::print_to_stream(stream, buf, len);
|
||||
|
||||
#else
|
||||
|
||||
using unsigned_type = std::conditional_t<(sizeof(T) > sizeof(unsigned)), std::make_unsigned_t<T>, unsigned>;
|
||||
using cast_type = std::conditional_t<std::is_signed_v<T>, std::make_signed_t<unsigned_type>, unsigned_type>;
|
||||
|
||||
if (base == 2)
|
||||
{
|
||||
const auto len = sizeof(T) * CHAR_BIT;
|
||||
for (size_t i = len; i < min_digits; i++)
|
||||
stream.put('0');
|
||||
|
||||
bool found_one = false;
|
||||
const auto v = static_cast<unsigned_type>(val);
|
||||
unsigned_type mask = unsigned_type{ 1 } << (len - 1u);
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
if ((v & mask))
|
||||
{
|
||||
stream.put('1');
|
||||
found_one = true;
|
||||
}
|
||||
else if (found_one)
|
||||
stream.put('0');
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << std::uppercase << std::setbase(base);
|
||||
if (min_digits)
|
||||
ss << std::setfill('0') << std::setw(static_cast<int>(min_digits));
|
||||
ss << static_cast<cast_type>(val);
|
||||
const auto str = std::move(ss).str();
|
||||
impl::print_to_stream(stream, str);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TOML_INTERNAL_LINKAGE
|
||||
void print_floating_point_to_stream(std::ostream & stream,
|
||||
T val,
|
||||
value_flags format,
|
||||
[[maybe_unused]] bool relaxed_precision)
|
||||
{
|
||||
switch (impl::fpclassify(val))
|
||||
{
|
||||
case impl::fp_class::neg_inf: impl::print_to_stream(stream, "-inf"sv); break;
|
||||
|
||||
case impl::fp_class::pos_inf: impl::print_to_stream(stream, "inf"sv); break;
|
||||
|
||||
case impl::fp_class::nan: impl::print_to_stream(stream, "nan"sv); break;
|
||||
|
||||
case impl::fp_class::ok:
|
||||
{
|
||||
static constexpr auto needs_decimal_point = [](auto&& s) noexcept
|
||||
{
|
||||
for (auto c : s)
|
||||
if (c == '.' || c == 'E' || c == 'e')
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
#if TOML_FLOAT_CHARCONV
|
||||
|
||||
const auto hex = !!(format & value_flags::format_as_hexadecimal);
|
||||
char buf[charconv_buffer_length<T>];
|
||||
auto res = hex ? std::to_chars(buf, buf + sizeof(buf), val, std::chars_format::hex)
|
||||
: std::to_chars(buf, buf + sizeof(buf), val);
|
||||
auto str = std::string_view{ buf, static_cast<size_t>(res.ptr - buf) };
|
||||
|
||||
char buf2[charconv_buffer_length<T>];
|
||||
if (!hex && relaxed_precision)
|
||||
{
|
||||
res = std::to_chars(buf2, buf2 + sizeof(buf2), val, std::chars_format::general, 6);
|
||||
const auto str2 = std::string_view{ buf2, static_cast<size_t>(res.ptr - buf2) };
|
||||
if (str2.length() < str.length())
|
||||
str = str2;
|
||||
}
|
||||
|
||||
impl::print_to_stream(stream, str);
|
||||
if (!hex && needs_decimal_point(str))
|
||||
toml::impl::print_to_stream(stream, ".0"sv);
|
||||
|
||||
#else
|
||||
|
||||
std::ostringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
if (!relaxed_precision)
|
||||
ss.precision(std::numeric_limits<T>::max_digits10);
|
||||
if (!!(format & value_flags::format_as_hexadecimal))
|
||||
ss << std::hexfloat;
|
||||
ss << val;
|
||||
const auto str = std::move(ss).str();
|
||||
impl::print_to_stream(stream, str);
|
||||
if (!(format & value_flags::format_as_hexadecimal) && needs_decimal_point(str))
|
||||
impl::print_to_stream(stream, ".0"sv);
|
||||
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
default: TOML_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
TOML_ANON_NAMESPACE_END;
|
||||
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
TOML_ATTR(nonnull)
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const char* val, size_t len)
|
||||
{
|
||||
stream.write(val, static_cast<std::streamsize>(len));
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, std::string_view val)
|
||||
{
|
||||
stream.write(val.data(), static_cast<std::streamsize>(val.length()));
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const std::string& val)
|
||||
{
|
||||
stream.write(val.data(), static_cast<std::streamsize>(val.length()));
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, char val)
|
||||
{
|
||||
stream.put(val);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, signed char val, value_flags format, size_t min_digits)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, signed short val, value_flags format, size_t min_digits)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, signed int val, value_flags format, size_t min_digits)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, signed long val, value_flags format, size_t min_digits)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream,
|
||||
signed long long val,
|
||||
value_flags format,
|
||||
size_t min_digits)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned char val, value_flags format, size_t min_digits)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned short val, value_flags format, size_t min_digits)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned int val, value_flags format, size_t min_digits)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned long val, value_flags format, size_t min_digits)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream,
|
||||
unsigned long long val,
|
||||
value_flags format,
|
||||
size_t min_digits)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, float val, value_flags format, bool relaxed_precision)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format, relaxed_precision);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, double val, value_flags format, bool relaxed_precision)
|
||||
{
|
||||
TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format, relaxed_precision);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, bool val)
|
||||
{
|
||||
print_to_stream(stream, val ? "true"sv : "false"sv);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const toml::date& val)
|
||||
{
|
||||
print_to_stream(stream, val.year, {}, 4);
|
||||
stream.put('-');
|
||||
print_to_stream(stream, val.month, {}, 2);
|
||||
stream.put('-');
|
||||
print_to_stream(stream, val.day, {}, 2);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const toml::time& val)
|
||||
{
|
||||
print_to_stream(stream, val.hour, {}, 2);
|
||||
stream.put(':');
|
||||
print_to_stream(stream, val.minute, {}, 2);
|
||||
stream.put(':');
|
||||
print_to_stream(stream, val.second, {}, 2);
|
||||
if (val.nanosecond && val.nanosecond <= 999999999u)
|
||||
{
|
||||
stream.put('.');
|
||||
auto ns = val.nanosecond;
|
||||
size_t digits = 9u;
|
||||
while (ns % 10u == 0u)
|
||||
{
|
||||
ns /= 10u;
|
||||
digits--;
|
||||
}
|
||||
print_to_stream(stream, ns, {}, digits);
|
||||
}
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const toml::time_offset& val)
|
||||
{
|
||||
if (!val.minutes)
|
||||
{
|
||||
stream.put('Z');
|
||||
return;
|
||||
}
|
||||
|
||||
auto mins = static_cast<int>(val.minutes);
|
||||
if (mins < 0)
|
||||
{
|
||||
stream.put('-');
|
||||
mins = -mins;
|
||||
}
|
||||
else
|
||||
stream.put('+');
|
||||
const auto hours = mins / 60;
|
||||
if (hours)
|
||||
{
|
||||
print_to_stream(stream, static_cast<unsigned int>(hours), {}, 2);
|
||||
mins -= hours * 60;
|
||||
}
|
||||
else
|
||||
print_to_stream(stream, "00"sv);
|
||||
stream.put(':');
|
||||
print_to_stream(stream, static_cast<unsigned int>(mins), {}, 2);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const toml::date_time& val)
|
||||
{
|
||||
print_to_stream(stream, val.date);
|
||||
stream.put('T');
|
||||
print_to_stream(stream, val.time);
|
||||
if (val.offset)
|
||||
print_to_stream(stream, *val.offset);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const source_position& val)
|
||||
{
|
||||
print_to_stream(stream, "line "sv);
|
||||
print_to_stream(stream, val.line);
|
||||
print_to_stream(stream, ", column "sv);
|
||||
print_to_stream(stream, val.column);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const source_region& val)
|
||||
{
|
||||
print_to_stream(stream, val.begin);
|
||||
if (val.begin != val.end)
|
||||
{
|
||||
print_to_stream(stream, " to "sv);
|
||||
print_to_stream(stream, val.end);
|
||||
}
|
||||
if (val.path)
|
||||
{
|
||||
print_to_stream(stream, " of '"sv);
|
||||
print_to_stream(stream, *val.path);
|
||||
stream.put('\'');
|
||||
}
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_FORMATTERS
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const array& arr)
|
||||
{
|
||||
stream << toml_formatter{ arr };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const table& tbl)
|
||||
{
|
||||
stream << toml_formatter{ tbl };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<std::string>& val)
|
||||
{
|
||||
stream << toml_formatter{ val };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<int64_t>& val)
|
||||
{
|
||||
stream << toml_formatter{ val };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<double>& val)
|
||||
{
|
||||
stream << toml_formatter{ val };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<bool>& val)
|
||||
{
|
||||
stream << toml_formatter{ val };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<date>& val)
|
||||
{
|
||||
stream << toml_formatter{ val };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<time>& val)
|
||||
{
|
||||
stream << toml_formatter{ val };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<date_time>& val)
|
||||
{
|
||||
stream << toml_formatter{ val };
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
35
src/external/toml++/impl/simd.hpp
vendored
Normal file
35
src/external/toml++/impl/simd.hpp
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
#if TOML_ENABLE_SIMD
|
||||
|
||||
#if defined(__SSE2__) \
|
||||
|| (defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)))
|
||||
#define TOML_HAS_SSE2 1
|
||||
#endif
|
||||
|
||||
#if defined(__SSE4_1__) || (defined(_MSC_VER) && (defined(__AVX__) || defined(__AVX2__)))
|
||||
#define TOML_HAS_SSE4_1 1
|
||||
#endif
|
||||
|
||||
#endif // TOML_ENABLE_SIMD
|
||||
|
||||
#ifndef TOML_HAS_SSE2
|
||||
#define TOML_HAS_SSE2 0
|
||||
#endif
|
||||
#ifndef TOML_HAS_SSE4_1
|
||||
#define TOML_HAS_SSE4_1 0
|
||||
#endif
|
||||
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#if TOML_HAS_SSE4_1
|
||||
#include <smmintrin.h>
|
||||
#endif
|
||||
#if TOML_HAS_SSE2
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
TOML_ENABLE_WARNINGS;
|
||||
296
src/external/toml++/impl/source_region.hpp
vendored
Normal file
296
src/external/toml++/impl/source_region.hpp
vendored
Normal file
@@ -0,0 +1,296 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "std_optional.hpp"
|
||||
#include "std_string.hpp"
|
||||
#include "forward_declarations.hpp"
|
||||
#include "print_to_stream.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
/// \brief The integer type used to tally line numbers and columns.
|
||||
using source_index = uint32_t;
|
||||
|
||||
/// \brief A pointer to a shared string resource containing a source path.
|
||||
using source_path_ptr = std::shared_ptr<const std::string>;
|
||||
|
||||
/// \brief A source document line-and-column pair.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto table = toml::parse_file("config.toml"sv);
|
||||
/// std::cout << "The node 'description' was defined at "sv
|
||||
/// << table.get("description")->source().begin()
|
||||
/// << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// The value 'description' was defined at line 7, column 15
|
||||
/// \eout
|
||||
///
|
||||
/// \remarks toml++'s parser is unicode-aware insofar as it knows how to handle
|
||||
/// non-ASCII whitespace and newline characters, but it doesn't give much thought
|
||||
/// to combining marks, grapheme clusters vs. characters, et cetera.
|
||||
/// If a TOML document contains lots of codepoints outside of the ASCII range
|
||||
/// you may find that your source_positions don't match those given by a text editor
|
||||
/// (typically the line numbers will be accurate but column numbers will be too high).
|
||||
/// <strong>This is not an error.</strong> I've chosen this behaviour as a deliberate trade-off
|
||||
/// between parser complexity and correctness.
|
||||
struct TOML_TRIVIAL_ABI source_position
|
||||
{
|
||||
/// \brief The line number.
|
||||
/// \remarks Valid line numbers start at 1.
|
||||
source_index line;
|
||||
|
||||
/// \brief The column number.
|
||||
/// \remarks Valid column numbers start at 1.
|
||||
source_index column;
|
||||
|
||||
/// \brief Returns true if both line and column numbers are non-zero.
|
||||
TOML_PURE_GETTER
|
||||
explicit constexpr operator bool() const noexcept
|
||||
{
|
||||
return line > source_index{} //
|
||||
&& column > source_index{};
|
||||
}
|
||||
|
||||
/// \brief Equality operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator==(const source_position& lhs, const source_position& rhs) noexcept
|
||||
{
|
||||
return lhs.line == rhs.line //
|
||||
&& lhs.column == rhs.column;
|
||||
}
|
||||
|
||||
/// \brief Inequality operator.
|
||||
TOML_PURE_INLINE_GETTER
|
||||
friend constexpr bool operator!=(const source_position& lhs, const source_position& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
/// \cond
|
||||
|
||||
TOML_PURE_GETTER
|
||||
static constexpr uint64_t pack(const source_position& pos) noexcept
|
||||
{
|
||||
return static_cast<uint64_t>(pos.line) << 32 | static_cast<uint64_t>(pos.column);
|
||||
}
|
||||
|
||||
/// \endcond
|
||||
|
||||
public:
|
||||
/// \brief Less-than operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator<(const source_position& lhs, const source_position& rhs) noexcept
|
||||
{
|
||||
return pack(lhs) < pack(rhs);
|
||||
}
|
||||
|
||||
/// \brief Less-than-or-equal-to operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator<=(const source_position& lhs, const source_position& rhs) noexcept
|
||||
{
|
||||
return pack(lhs) <= pack(rhs);
|
||||
}
|
||||
|
||||
/// \brief Greater-than operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator>(const source_position& lhs, const source_position& rhs) noexcept
|
||||
{
|
||||
return pack(lhs) > pack(rhs);
|
||||
}
|
||||
|
||||
/// \brief Greater-than-or-equal-to operator.
|
||||
TOML_PURE_GETTER
|
||||
friend constexpr bool operator>=(const source_position& lhs, const source_position& rhs) noexcept
|
||||
{
|
||||
return pack(lhs) >= pack(rhs);
|
||||
}
|
||||
|
||||
/// \brief Prints a source_position to a stream.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto tbl = toml::parse("bar = 42"sv);
|
||||
///
|
||||
/// std::cout << "The value for 'bar' was found on "sv
|
||||
/// << tbl.get("bar")->source().begin()
|
||||
/// << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// The value for 'bar' was found on line 1, column 7
|
||||
/// \eout
|
||||
///
|
||||
/// \param lhs The stream.
|
||||
/// \param rhs The source_position.
|
||||
///
|
||||
/// \returns The input stream.
|
||||
friend std::ostream& operator<<(std::ostream& lhs, const source_position& rhs)
|
||||
{
|
||||
impl::print_to_stream(lhs, rhs);
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief A source document region.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto tbl = toml::parse_file("config.toml"sv);
|
||||
/// if (auto server = tbl.get("server"))
|
||||
/// {
|
||||
/// std::cout << "begin: "sv << server->source().begin << "\n";
|
||||
/// std::cout << "end: "sv << server->source().end << "\n";
|
||||
/// std::cout << "path: "sv << *server->source().path << "\n";
|
||||
/// }
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// begin: line 3, column 1
|
||||
/// end: line 3, column 22
|
||||
/// path: config.toml
|
||||
/// \eout
|
||||
///
|
||||
/// \remarks toml++'s parser is unicode-aware insofar as it knows how to handle
|
||||
/// non-ASCII whitespace and newline characters, but it doesn't give much thought
|
||||
/// to combining marks, grapheme clusters vs. characters, et cetera.
|
||||
/// If a TOML document contains lots of codepoints outside of the ASCII range
|
||||
/// you may find that your source_positions don't match those given by a text editor
|
||||
/// (typically the line numbers will be accurate but column numbers will be too high).
|
||||
/// <strong>This is not an error.</strong> I've chosen this behaviour as a deliberate trade-off
|
||||
/// between parser complexity and correctness.
|
||||
struct source_region
|
||||
{
|
||||
/// \brief The beginning of the region (inclusive).
|
||||
source_position begin;
|
||||
|
||||
/// \brief The end of the region (exclusive).
|
||||
source_position end;
|
||||
|
||||
/// \brief The path to the corresponding source document.
|
||||
///
|
||||
/// \remarks This will be `nullptr` if no path was provided to toml::parse().
|
||||
source_path_ptr path;
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \brief The path to the corresponding source document as a wide-string.
|
||||
///
|
||||
/// \availability This function is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
|
||||
///
|
||||
/// \remarks This will return an empty optional if no path was provided to toml::parse().
|
||||
TOML_NODISCARD
|
||||
optional<std::wstring> wide_path() const
|
||||
{
|
||||
if (!path || path->empty())
|
||||
return {};
|
||||
return { impl::widen(*path) };
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// \brief Prints a source_region to a stream.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto tbl = toml::parse("bar = 42", "config.toml");
|
||||
///
|
||||
/// std::cout << "The value for 'bar' was found on "sv
|
||||
/// << tbl.get("bar")->source()
|
||||
/// << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// The value for 'bar' was found on line 1, column 7 of 'config.toml'
|
||||
/// \eout
|
||||
///
|
||||
/// \param lhs The stream.
|
||||
/// \param rhs The source_position.
|
||||
///
|
||||
/// \returns The input stream.
|
||||
friend std::ostream& operator<<(std::ostream& lhs, const source_region& rhs)
|
||||
{
|
||||
impl::print_to_stream(lhs, rhs);
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Returns the line at the specified line number, from the specified document.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto doc = "alpha = 1\nbeta = 2"sv;
|
||||
/// auto second_line = toml::get_line(doc, 2);
|
||||
///
|
||||
/// std::cout << "The second line says \"" << second_line.value() << "\"\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// The second line says "beta = 2"
|
||||
/// \eout
|
||||
///
|
||||
/// \param doc The document.
|
||||
/// \param line_num The line number (1-based).
|
||||
///
|
||||
/// \returns The specified line, excluding any possible trailing carriage return or line feed character.
|
||||
/// \remarks Returns an empty `optional` when the specified line number is out of range, i.e., when
|
||||
/// the line number is zero or greater than the total number of lines of the specified document.
|
||||
TOML_NODISCARD
|
||||
constexpr optional<std::string_view> get_line(std::string_view doc, source_index line_num) noexcept
|
||||
{
|
||||
if (line_num == 0)
|
||||
{
|
||||
// Invalid line number. Should be greater than zero.
|
||||
return {};
|
||||
}
|
||||
|
||||
// The position of the first character of the specified line.
|
||||
const auto begin_of_line = [doc, line_num]() -> std::size_t
|
||||
{
|
||||
if (line_num == 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto num_chars_of_doc = doc.size();
|
||||
std::size_t current_line_num{ 1 };
|
||||
|
||||
for (std::size_t i{}; i < num_chars_of_doc; ++i)
|
||||
{
|
||||
if (doc[i] == '\n')
|
||||
{
|
||||
++current_line_num;
|
||||
|
||||
if (current_line_num == line_num)
|
||||
{
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::string_view::npos;
|
||||
}();
|
||||
|
||||
if (begin_of_line >= doc.size())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto end_of_line = doc.find('\n', begin_of_line); end_of_line != std::string_view::npos)
|
||||
{
|
||||
const auto num_chars_of_line = end_of_line - begin_of_line;
|
||||
|
||||
// Trim an optional trailing carriage return.
|
||||
return doc.substr(begin_of_line,
|
||||
((num_chars_of_line > 0) && (doc[end_of_line - 1] == '\r')) ? num_chars_of_line - 1
|
||||
: num_chars_of_line);
|
||||
}
|
||||
|
||||
// Return the last line. Apparently this doc has no trailing line break character at the end.
|
||||
return doc.substr(begin_of_line);
|
||||
}
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
12
src/external/toml++/impl/std_except.hpp
vendored
Normal file
12
src/external/toml++/impl/std_except.hpp
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#if TOML_EXCEPTIONS
|
||||
#include <stdexcept>
|
||||
#endif
|
||||
TOML_ENABLE_WARNINGS;
|
||||
10
src/external/toml++/impl/std_initializer_list.hpp
vendored
Normal file
10
src/external/toml++/impl/std_initializer_list.hpp
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#include <initializer_list>
|
||||
TOML_ENABLE_WARNINGS;
|
||||
11
src/external/toml++/impl/std_map.hpp
vendored
Normal file
11
src/external/toml++/impl/std_map.hpp
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#include <map>
|
||||
#include <iterator>
|
||||
TOML_ENABLE_WARNINGS;
|
||||
18
src/external/toml++/impl/std_new.hpp
vendored
Normal file
18
src/external/toml++/impl/std_new.hpp
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#include <new>
|
||||
TOML_ENABLE_WARNINGS;
|
||||
|
||||
#if (!defined(__apple_build_version__) && TOML_CLANG >= 8) || TOML_GCC >= 7 || TOML_ICC >= 1910 || TOML_MSVC >= 1914
|
||||
#define TOML_LAUNDER(x) __builtin_launder(x)
|
||||
#elif defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
|
||||
#define TOML_LAUNDER(x) std::launder(x)
|
||||
#else
|
||||
#define TOML_LAUNDER(x) x
|
||||
#endif
|
||||
32
src/external/toml++/impl/std_optional.hpp
vendored
Normal file
32
src/external/toml++/impl/std_optional.hpp
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#if !TOML_HAS_CUSTOM_OPTIONAL_TYPE
|
||||
#include <optional>
|
||||
#endif
|
||||
TOML_ENABLE_WARNINGS;
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
#if TOML_HAS_CUSTOM_OPTIONAL_TYPE
|
||||
|
||||
template <typename T>
|
||||
using optional = TOML_OPTIONAL_TYPE<T>;
|
||||
|
||||
#else
|
||||
|
||||
/// \brief The 'optional' type used throughout the library.
|
||||
///
|
||||
/// \remarks By default this will be an alias for std::optional, but you can change the optional type
|
||||
/// used by the library by defining #TOML_OPTIONAL_TYPE.
|
||||
template <typename T>
|
||||
using optional = std::optional<T>;
|
||||
|
||||
#endif
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
53
src/external/toml++/impl/std_string.hpp
vendored
Normal file
53
src/external/toml++/impl/std_string.hpp
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
TOML_ENABLE_WARNINGS;
|
||||
|
||||
#if TOML_DOXYGEN \
|
||||
|| (defined(__cpp_char8_t) && __cpp_char8_t >= 201811 && defined(__cpp_lib_char8_t) \
|
||||
&& __cpp_lib_char8_t >= 201907)
|
||||
#define TOML_HAS_CHAR8 1
|
||||
#else
|
||||
#define TOML_HAS_CHAR8 0
|
||||
#endif
|
||||
|
||||
/// \cond
|
||||
|
||||
namespace toml // non-abi namespace; this is not an error
|
||||
{
|
||||
using namespace std::string_literals;
|
||||
using namespace std::string_view_literals;
|
||||
}
|
||||
|
||||
#if TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
std::string narrow(std::wstring_view);
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
std::wstring widen(std::string_view);
|
||||
|
||||
#if TOML_HAS_CHAR8
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_EXPORTED_FREE_FUNCTION
|
||||
std::wstring widen(std::u8string_view);
|
||||
|
||||
#endif
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
|
||||
#endif // TOML_ENABLE_WINDOWS_COMPAT
|
||||
|
||||
/// \endcond
|
||||
99
src/external/toml++/impl/std_string.inl
vendored
Normal file
99
src/external/toml++/impl/std_string.inl
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
//# {{
|
||||
#include "preprocessor.hpp"
|
||||
#if !TOML_IMPLEMENTATION
|
||||
#error This is an implementation-only header.
|
||||
#endif
|
||||
//# }}
|
||||
|
||||
#if TOML_WINDOWS
|
||||
#include "std_string.hpp"
|
||||
#ifndef _WINDOWS_
|
||||
#if TOML_INCLUDE_WINDOWS_H
|
||||
#include <Windows.h>
|
||||
#else
|
||||
|
||||
extern "C" __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int CodePage,
|
||||
unsigned long dwFlags,
|
||||
const wchar_t* lpWideCharStr,
|
||||
int cchWideChar,
|
||||
char* lpMultiByteStr,
|
||||
int cbMultiByte,
|
||||
const char* lpDefaultChar,
|
||||
int* lpUsedDefaultChar);
|
||||
|
||||
extern "C" __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage,
|
||||
unsigned long dwFlags,
|
||||
const char* lpMultiByteStr,
|
||||
int cbMultiByte,
|
||||
wchar_t* lpWideCharStr,
|
||||
int cchWideChar);
|
||||
|
||||
#endif // TOML_INCLUDE_WINDOWS_H
|
||||
#endif // _WINDOWS_
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
std::string narrow(std::wstring_view str)
|
||||
{
|
||||
if (str.empty())
|
||||
return {};
|
||||
|
||||
std::string s;
|
||||
const auto len =
|
||||
::WideCharToMultiByte(65001, 0, str.data(), static_cast<int>(str.length()), nullptr, 0, nullptr, nullptr);
|
||||
if (len)
|
||||
{
|
||||
s.resize(static_cast<size_t>(len));
|
||||
::WideCharToMultiByte(65001,
|
||||
0,
|
||||
str.data(),
|
||||
static_cast<int>(str.length()),
|
||||
s.data(),
|
||||
len,
|
||||
nullptr,
|
||||
nullptr);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
std::wstring widen(std::string_view str)
|
||||
{
|
||||
if (str.empty())
|
||||
return {};
|
||||
|
||||
std::wstring s;
|
||||
const auto len = ::MultiByteToWideChar(65001, 0, str.data(), static_cast<int>(str.length()), nullptr, 0);
|
||||
if (len)
|
||||
{
|
||||
s.resize(static_cast<size_t>(len));
|
||||
::MultiByteToWideChar(65001, 0, str.data(), static_cast<int>(str.length()), s.data(), len);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#if TOML_HAS_CHAR8
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
std::wstring widen(std::u8string_view str)
|
||||
{
|
||||
if (str.empty())
|
||||
return {};
|
||||
|
||||
return widen(std::string_view{ reinterpret_cast<const char*>(str.data()), str.length() });
|
||||
}
|
||||
|
||||
#endif // TOML_HAS_CHAR8
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
#endif // TOML_WINDOWS
|
||||
10
src/external/toml++/impl/std_utility.hpp
vendored
Normal file
10
src/external/toml++/impl/std_utility.hpp
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#include <utility>
|
||||
TOML_ENABLE_WARNINGS;
|
||||
10
src/external/toml++/impl/std_variant.hpp
vendored
Normal file
10
src/external/toml++/impl/std_variant.hpp
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#include <variant>
|
||||
TOML_ENABLE_WARNINGS;
|
||||
11
src/external/toml++/impl/std_vector.hpp
vendored
Normal file
11
src/external/toml++/impl/std_vector.hpp
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
TOML_DISABLE_WARNINGS;
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
TOML_ENABLE_WARNINGS;
|
||||
2001
src/external/toml++/impl/table.hpp
vendored
Normal file
2001
src/external/toml++/impl/table.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
318
src/external/toml++/impl/table.inl
vendored
Normal file
318
src/external/toml++/impl/table.inl
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
//# {{
|
||||
#include "preprocessor.hpp"
|
||||
#if !TOML_IMPLEMENTATION
|
||||
#error This is an implementation-only header.
|
||||
#endif
|
||||
//# }}
|
||||
|
||||
#include "table.hpp"
|
||||
#include "node_view.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table::table() noexcept
|
||||
{
|
||||
#if TOML_LIFETIME_HOOKS
|
||||
TOML_TABLE_CREATED;
|
||||
#endif
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table::~table() noexcept
|
||||
{
|
||||
#if TOML_LIFETIME_HOOKS
|
||||
TOML_TABLE_DESTROYED;
|
||||
#endif
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table::table(const impl::table_init_pair* b, const impl::table_init_pair* e)
|
||||
{
|
||||
#if TOML_LIFETIME_HOOKS
|
||||
TOML_TABLE_CREATED;
|
||||
#endif
|
||||
|
||||
TOML_ASSERT_ASSUME(b);
|
||||
TOML_ASSERT_ASSUME(e);
|
||||
TOML_ASSERT_ASSUME(b <= e);
|
||||
|
||||
if TOML_UNLIKELY(b == e)
|
||||
return;
|
||||
|
||||
for (; b != e; b++)
|
||||
{
|
||||
if (!b->value) // empty node_views
|
||||
continue;
|
||||
|
||||
map_.insert_or_assign(std::move(b->key), std::move(b->value));
|
||||
}
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table::table(const table& other) //
|
||||
: node(other),
|
||||
inline_{ other.inline_ }
|
||||
{
|
||||
for (auto&& [k, v] : other.map_)
|
||||
map_.emplace_hint(map_.end(), k, impl::make_node(*v));
|
||||
|
||||
#if TOML_LIFETIME_HOOKS
|
||||
TOML_TABLE_CREATED;
|
||||
#endif
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table::table(table && other) noexcept //
|
||||
: node(std::move(other)),
|
||||
map_{ std::move(other.map_) },
|
||||
inline_{ other.inline_ }
|
||||
{
|
||||
#if TOML_LIFETIME_HOOKS
|
||||
TOML_TABLE_CREATED;
|
||||
#endif
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table& table::operator=(const table& rhs)
|
||||
{
|
||||
if (&rhs != this)
|
||||
{
|
||||
node::operator=(rhs);
|
||||
map_.clear();
|
||||
for (auto&& [k, v] : rhs.map_)
|
||||
map_.emplace_hint(map_.end(), k, impl::make_node(*v));
|
||||
inline_ = rhs.inline_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table& table::operator=(table&& rhs) noexcept
|
||||
{
|
||||
if (&rhs != this)
|
||||
{
|
||||
node::operator=(std::move(rhs));
|
||||
map_ = std::move(rhs.map_);
|
||||
inline_ = rhs.inline_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool table::is_homogeneous(node_type ntype) const noexcept
|
||||
{
|
||||
if (map_.empty())
|
||||
return false;
|
||||
|
||||
if (ntype == node_type::none)
|
||||
ntype = map_.cbegin()->second->type();
|
||||
|
||||
for (auto&& [k, v] : map_)
|
||||
{
|
||||
TOML_UNUSED(k);
|
||||
if (v->type() != ntype)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool table::is_homogeneous(node_type ntype, node * &first_nonmatch) noexcept
|
||||
{
|
||||
if (map_.empty())
|
||||
{
|
||||
first_nonmatch = {};
|
||||
return false;
|
||||
}
|
||||
if (ntype == node_type::none)
|
||||
ntype = map_.cbegin()->second->type();
|
||||
for (const auto& [k, v] : map_)
|
||||
{
|
||||
TOML_UNUSED(k);
|
||||
if (v->type() != ntype)
|
||||
{
|
||||
first_nonmatch = v.get();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool table::is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept
|
||||
{
|
||||
node* fnm = nullptr;
|
||||
const auto result = const_cast<table&>(*this).is_homogeneous(ntype, fnm);
|
||||
first_nonmatch = fnm;
|
||||
return result;
|
||||
}
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node* table::get(std::string_view key) noexcept
|
||||
{
|
||||
if (auto it = map_.find(key); it != map_.end())
|
||||
return it->second.get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
node& table::at(std::string_view key)
|
||||
{
|
||||
auto n = get(key);
|
||||
|
||||
#if TOML_COMPILER_HAS_EXCEPTIONS
|
||||
|
||||
if (!n)
|
||||
{
|
||||
auto err = "key '"s;
|
||||
err.append(key);
|
||||
err.append("' not found in table"sv);
|
||||
throw std::out_of_range{ err };
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
TOML_ASSERT_ASSUME(n && "key not found in table!");
|
||||
|
||||
#endif
|
||||
|
||||
return *n;
|
||||
}
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table::map_iterator table::get_lower_bound(std::string_view key) noexcept
|
||||
{
|
||||
return map_.lower_bound(key);
|
||||
}
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table::iterator table::find(std::string_view key) noexcept
|
||||
{
|
||||
return iterator{ map_.find(key) };
|
||||
}
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table::const_iterator table::find(std::string_view key) const noexcept
|
||||
{
|
||||
return const_iterator{ map_.find(key) };
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table::map_iterator table::erase(const_map_iterator pos) noexcept
|
||||
{
|
||||
return map_.erase(pos);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table::map_iterator table::erase(const_map_iterator begin, const_map_iterator end) noexcept
|
||||
{
|
||||
return map_.erase(begin, end);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
size_t table::erase(std::string_view key) noexcept
|
||||
{
|
||||
if (auto it = map_.find(key); it != map_.end())
|
||||
{
|
||||
map_.erase(it);
|
||||
return size_t{ 1 };
|
||||
}
|
||||
return size_t{};
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table& table::prune(bool recursive)& noexcept
|
||||
{
|
||||
if (map_.empty())
|
||||
return *this;
|
||||
|
||||
for (auto it = map_.begin(); it != map_.end();)
|
||||
{
|
||||
if (auto arr = it->second->as_array())
|
||||
{
|
||||
if (recursive)
|
||||
arr->prune(true);
|
||||
|
||||
if (arr->empty())
|
||||
{
|
||||
it = map_.erase(it);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (auto tbl = it->second->as_table())
|
||||
{
|
||||
if (recursive)
|
||||
tbl->prune(true);
|
||||
|
||||
if (tbl->empty())
|
||||
{
|
||||
it = map_.erase(it);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void table::clear() noexcept
|
||||
{
|
||||
map_.clear();
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
table::map_iterator table::insert_with_hint(const_iterator hint, key && k, impl::node_ptr && v)
|
||||
{
|
||||
return map_.emplace_hint(const_map_iterator{ hint }, std::move(k), std::move(v));
|
||||
}
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool TOML_CALLCONV table::equal(const table& lhs, const table& rhs) noexcept
|
||||
{
|
||||
if (&lhs == &rhs)
|
||||
return true;
|
||||
if (lhs.map_.size() != rhs.map_.size())
|
||||
return false;
|
||||
|
||||
for (auto l = lhs.map_.begin(), r = rhs.map_.begin(), e = lhs.map_.end(); l != e; l++, r++)
|
||||
{
|
||||
if (l->first != r->first)
|
||||
return false;
|
||||
|
||||
const auto lhs_type = l->second->type();
|
||||
const node& rhs_ = *r->second;
|
||||
const auto rhs_type = rhs_.type();
|
||||
if (lhs_type != rhs_type)
|
||||
return false;
|
||||
|
||||
const bool equal = l->second->visit(
|
||||
[&](const auto& lhs_) noexcept
|
||||
{ return lhs_ == *reinterpret_cast<std::remove_reference_t<decltype(lhs_)>*>(&rhs_); });
|
||||
if (!equal)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
153
src/external/toml++/impl/toml_formatter.hpp
vendored
Normal file
153
src/external/toml++/impl/toml_formatter.hpp
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
#if TOML_ENABLE_FORMATTERS
|
||||
|
||||
#include "std_vector.hpp"
|
||||
#include "formatter.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
/// \brief A wrapper for printing TOML objects out to a stream as formatted TOML.
|
||||
///
|
||||
/// \availability This class is only available when #TOML_ENABLE_FORMATTERS is enabled.
|
||||
///
|
||||
/// \remarks You generally don't need to create an instance of this class explicitly; the stream
|
||||
/// operators of the TOML node types already print themselves out using this formatter.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto tbl = toml::table{
|
||||
/// { "description", "This is some TOML, yo." },
|
||||
/// { "fruit", toml::array{ "apple", "orange", "pear" } },
|
||||
/// { "numbers", toml::array{ 1, 2, 3, 4, 5 } },
|
||||
/// { "table", toml::table{ { "foo", "bar" } } }
|
||||
/// };
|
||||
///
|
||||
/// // these two lines are equivalent:
|
||||
/// std::cout << toml::toml_formatter{ tbl } << "\n";
|
||||
/// std::cout << tbl << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// description = "This is some TOML, yo."
|
||||
/// fruit = ["apple", "orange", "pear"]
|
||||
/// numbers = [1, 2, 3, 4, 5]
|
||||
///
|
||||
/// [table]
|
||||
/// foo = "bar"
|
||||
/// \eout
|
||||
class TOML_EXPORTED_CLASS toml_formatter : impl::formatter
|
||||
{
|
||||
private:
|
||||
/// \cond
|
||||
|
||||
using base = impl::formatter;
|
||||
std::vector<const key*> key_path_;
|
||||
bool pending_table_separator_ = false;
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print_pending_table_separator();
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const key&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print_inline(const toml::table&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const toml::array&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const toml::table&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print();
|
||||
|
||||
static constexpr impl::formatter_constants constants = { format_flags::none, // mandatory
|
||||
format_flags::none, // ignored
|
||||
"inf"sv,
|
||||
"-inf"sv,
|
||||
"nan"sv,
|
||||
"true"sv,
|
||||
"false"sv };
|
||||
|
||||
/// \endcond
|
||||
|
||||
public:
|
||||
/// \brief The default flags for a toml_formatter.
|
||||
static constexpr format_flags default_flags = constants.mandatory_flags //
|
||||
| format_flags::allow_literal_strings //
|
||||
| format_flags::allow_multi_line_strings //
|
||||
| format_flags::allow_unicode_strings //
|
||||
| format_flags::allow_real_tabs_in_strings //
|
||||
| format_flags::allow_binary_integers //
|
||||
| format_flags::allow_octal_integers //
|
||||
| format_flags::allow_hexadecimal_integers //
|
||||
| format_flags::indentation;
|
||||
|
||||
/// \brief Constructs a TOML formatter and binds it to a TOML object.
|
||||
///
|
||||
/// \param source The source TOML object.
|
||||
/// \param flags Format option flags.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit toml_formatter(const toml::node& source, format_flags flags = default_flags) noexcept
|
||||
: base{ &source, nullptr, constants, { flags, " "sv } }
|
||||
{}
|
||||
|
||||
#if TOML_DOXYGEN || (TOML_ENABLE_PARSER && !TOML_EXCEPTIONS)
|
||||
|
||||
/// \brief Constructs a TOML formatter and binds it to a toml::parse_result.
|
||||
///
|
||||
/// \availability This constructor is only available when exceptions are disabled.
|
||||
///
|
||||
/// \attention Formatting a failed parse result will simply dump the error message out as-is.
|
||||
/// This will not be valid TOML, but at least gives you something to log or show up in diagnostics:
|
||||
/// \cpp
|
||||
/// std::cout << toml::toml_formatter{ toml::parse("a = 'b'"sv) } // ok
|
||||
/// << "\n\n"
|
||||
/// << toml::toml_formatter{ toml::parse("a = "sv) } // malformed
|
||||
/// << "\n";
|
||||
/// \ecpp
|
||||
/// \out
|
||||
/// a = 'b'
|
||||
///
|
||||
/// Error while parsing key-value pair: encountered end-of-file
|
||||
/// (error occurred at line 1, column 5)
|
||||
/// \eout
|
||||
/// Use the library with exceptions if you want to avoid this scenario.
|
||||
///
|
||||
/// \param result The parse result.
|
||||
/// \param flags Format option flags.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit toml_formatter(const toml::parse_result& result, format_flags flags = default_flags) noexcept
|
||||
: base{ nullptr, &result, constants, { flags, " "sv } }
|
||||
{}
|
||||
|
||||
#endif
|
||||
|
||||
/// \brief Prints the bound TOML object out to the stream as formatted TOML.
|
||||
friend std::ostream& operator<<(std::ostream& lhs, toml_formatter& rhs)
|
||||
{
|
||||
rhs.attach(lhs);
|
||||
rhs.key_path_.clear();
|
||||
rhs.print();
|
||||
rhs.detach();
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/// \brief Prints the bound TOML object out to the stream as formatted TOML (rvalue overload).
|
||||
friend std::ostream& operator<<(std::ostream& lhs, toml_formatter&& rhs)
|
||||
{
|
||||
return lhs << rhs; // as lvalue
|
||||
}
|
||||
};
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
#endif // TOML_ENABLE_FORMATTERS
|
||||
405
src/external/toml++/impl/toml_formatter.inl
vendored
Normal file
405
src/external/toml++/impl/toml_formatter.inl
vendored
Normal file
@@ -0,0 +1,405 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
//# {{
|
||||
#if !TOML_IMPLEMENTATION
|
||||
#error This is an implementation-only header.
|
||||
#endif
|
||||
//# }}
|
||||
#if TOML_ENABLE_FORMATTERS
|
||||
|
||||
#include "toml_formatter.hpp"
|
||||
#include "print_to_stream.hpp"
|
||||
#include "value.hpp"
|
||||
#include "table.hpp"
|
||||
#include "array.hpp"
|
||||
#include "unicode.hpp"
|
||||
#include "header_start.hpp"
|
||||
TOML_DISABLE_ARITHMETIC_WARNINGS;
|
||||
|
||||
TOML_ANON_NAMESPACE_START
|
||||
{
|
||||
TOML_INTERNAL_LINKAGE
|
||||
size_t toml_formatter_count_inline_columns(const node& node, size_t line_wrap_cols) noexcept
|
||||
{
|
||||
switch (node.type())
|
||||
{
|
||||
case node_type::table:
|
||||
{
|
||||
auto& tbl = *reinterpret_cast<const table*>(&node);
|
||||
if (tbl.empty())
|
||||
return 2u; // "{}"
|
||||
size_t weight = 3u; // "{ }"
|
||||
for (auto&& [k, v] : tbl)
|
||||
{
|
||||
weight += k.length() + toml_formatter_count_inline_columns(v, line_wrap_cols) + 2u; // + ", "
|
||||
if (weight >= line_wrap_cols)
|
||||
break;
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
case node_type::array:
|
||||
{
|
||||
auto& arr = *reinterpret_cast<const array*>(&node);
|
||||
if (arr.empty())
|
||||
return 2u; // "[]"
|
||||
size_t weight = 3u; // "[ ]"
|
||||
for (auto& elem : arr)
|
||||
{
|
||||
weight += toml_formatter_count_inline_columns(elem, line_wrap_cols) + 2u; // + ", "
|
||||
if (weight >= line_wrap_cols)
|
||||
break;
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
case node_type::string:
|
||||
{
|
||||
// todo: proper utf8 decoding?
|
||||
// todo: tab awareness?
|
||||
auto& str = (*reinterpret_cast<const value<std::string>*>(&node)).get();
|
||||
return str.length() + 2u; // + ""
|
||||
}
|
||||
|
||||
case node_type::integer:
|
||||
{
|
||||
auto val = (*reinterpret_cast<const value<int64_t>*>(&node)).get();
|
||||
if (!val)
|
||||
return 1u;
|
||||
size_t weight = {};
|
||||
if (val < 0)
|
||||
{
|
||||
weight += 1u;
|
||||
val *= -1;
|
||||
}
|
||||
return weight + static_cast<size_t>(log10(static_cast<double>(val))) + 1u;
|
||||
}
|
||||
|
||||
case node_type::floating_point:
|
||||
{
|
||||
auto val = (*reinterpret_cast<const value<double>*>(&node)).get();
|
||||
if (val == 0.0)
|
||||
return 3u; // "0.0"
|
||||
size_t weight = 2u; // ".0"
|
||||
if (val < 0.0)
|
||||
{
|
||||
weight += 1u;
|
||||
val *= -1.0;
|
||||
}
|
||||
return weight + static_cast<size_t>(abs(log10(val))) + 1u;
|
||||
}
|
||||
|
||||
case node_type::boolean: return 5u;
|
||||
case node_type::date: [[fallthrough]];
|
||||
case node_type::time: return 10u;
|
||||
case node_type::date_time: return 30u;
|
||||
case node_type::none: TOML_UNREACHABLE;
|
||||
default: TOML_UNREACHABLE;
|
||||
}
|
||||
|
||||
TOML_UNREACHABLE;
|
||||
}
|
||||
|
||||
TOML_INTERNAL_LINKAGE
|
||||
bool toml_formatter_forces_multiline(const node& node, size_t line_wrap_cols, size_t starting_column_bias) noexcept
|
||||
{
|
||||
return (toml_formatter_count_inline_columns(node, line_wrap_cols) + starting_column_bias) >= line_wrap_cols;
|
||||
}
|
||||
}
|
||||
TOML_ANON_NAMESPACE_END;
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void toml_formatter::print_pending_table_separator()
|
||||
{
|
||||
if (pending_table_separator_)
|
||||
{
|
||||
print_newline(true);
|
||||
print_newline(true);
|
||||
pending_table_separator_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void toml_formatter::print(const key& k)
|
||||
{
|
||||
print_string(k.str(), false, true, false);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void toml_formatter::print_inline(const table& tbl)
|
||||
{
|
||||
if (tbl.empty())
|
||||
{
|
||||
print_unformatted("{}"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
print_unformatted("{ "sv);
|
||||
|
||||
bool first = false;
|
||||
for (auto&& [k, v] : tbl)
|
||||
{
|
||||
if (first)
|
||||
print_unformatted(", "sv);
|
||||
first = true;
|
||||
|
||||
print(k);
|
||||
if (terse_kvps())
|
||||
print_unformatted("="sv);
|
||||
else
|
||||
print_unformatted(" = "sv);
|
||||
|
||||
const auto type = v.type();
|
||||
TOML_ASSUME(type != node_type::none);
|
||||
switch (type)
|
||||
{
|
||||
case node_type::table: print_inline(*reinterpret_cast<const table*>(&v)); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&v)); break;
|
||||
default: print_value(v, type);
|
||||
}
|
||||
}
|
||||
|
||||
print_unformatted(" }"sv);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void toml_formatter::print(const array& arr)
|
||||
{
|
||||
if (arr.empty())
|
||||
{
|
||||
print_unformatted("[]"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto original_indent = indent();
|
||||
const auto multiline = force_multiline_arrays()
|
||||
|| TOML_ANON_NAMESPACE::toml_formatter_forces_multiline(
|
||||
arr,
|
||||
120u,
|
||||
indent_columns() * static_cast<size_t>(original_indent < 0 ? 0 : original_indent));
|
||||
|
||||
print_unformatted("["sv);
|
||||
|
||||
if (multiline)
|
||||
{
|
||||
if (original_indent < 0)
|
||||
indent(0);
|
||||
if (indent_array_elements())
|
||||
increase_indent();
|
||||
}
|
||||
else
|
||||
print_unformatted(' ');
|
||||
|
||||
for (size_t i = 0; i < arr.size(); i++)
|
||||
{
|
||||
if (i > 0u)
|
||||
{
|
||||
print_unformatted(',');
|
||||
if (!multiline)
|
||||
print_unformatted(' ');
|
||||
}
|
||||
|
||||
if (multiline)
|
||||
{
|
||||
print_newline(true);
|
||||
print_indent();
|
||||
}
|
||||
|
||||
auto& v = arr[i];
|
||||
const auto type = v.type();
|
||||
TOML_ASSUME(type != node_type::none);
|
||||
switch (type)
|
||||
{
|
||||
case node_type::table: print_inline(*reinterpret_cast<const table*>(&v)); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&v)); break;
|
||||
default: print_value(v, type);
|
||||
}
|
||||
}
|
||||
if (multiline)
|
||||
{
|
||||
indent(original_indent);
|
||||
print_newline(true);
|
||||
print_indent();
|
||||
}
|
||||
else
|
||||
print_unformatted(' ');
|
||||
|
||||
print_unformatted("]"sv);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void toml_formatter::print(const table& tbl)
|
||||
{
|
||||
static constexpr auto is_non_inline_array_of_tables = [](const node& n) noexcept
|
||||
{
|
||||
const auto arr = n.as_array();
|
||||
if (!arr || !arr->is_array_of_tables())
|
||||
return false;
|
||||
|
||||
return !reinterpret_cast<const table*>(&(*arr)[0])->is_inline();
|
||||
};
|
||||
|
||||
// values, arrays, and inline tables/table arrays
|
||||
for (auto&& [k, v] : tbl)
|
||||
{
|
||||
const auto type = v.type();
|
||||
if ((type == node_type::table && !reinterpret_cast<const table*>(&v)->is_inline())
|
||||
|| (type == node_type::array && is_non_inline_array_of_tables(v)))
|
||||
continue;
|
||||
|
||||
pending_table_separator_ = true;
|
||||
print_newline();
|
||||
print_indent();
|
||||
print(k);
|
||||
if (terse_kvps())
|
||||
print_unformatted("="sv);
|
||||
else
|
||||
print_unformatted(" = "sv);
|
||||
TOML_ASSUME(type != node_type::none);
|
||||
switch (type)
|
||||
{
|
||||
case node_type::table: print_inline(*reinterpret_cast<const table*>(&v)); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&v)); break;
|
||||
default: print_value(v, type);
|
||||
}
|
||||
}
|
||||
|
||||
const auto print_key_path = [&]()
|
||||
{
|
||||
size_t i{};
|
||||
for (const auto k : key_path_)
|
||||
{
|
||||
if (i++)
|
||||
print_unformatted('.');
|
||||
print(*k);
|
||||
}
|
||||
};
|
||||
|
||||
// non-inline tables
|
||||
for (auto&& [k, v] : tbl)
|
||||
{
|
||||
const auto type = v.type();
|
||||
if (type != node_type::table || reinterpret_cast<const table*>(&v)->is_inline())
|
||||
continue;
|
||||
auto& child_tbl = *reinterpret_cast<const table*>(&v);
|
||||
|
||||
// we can skip indenting and emitting the headers for tables that only contain other tables
|
||||
// (so we don't over-nest)
|
||||
size_t child_value_count{}; // includes inline tables and non-table arrays
|
||||
size_t child_table_count{};
|
||||
size_t child_table_array_count{};
|
||||
for (auto&& [child_k, child_v] : child_tbl)
|
||||
{
|
||||
TOML_UNUSED(child_k);
|
||||
const auto child_type = child_v.type();
|
||||
TOML_ASSUME(child_type != node_type::none);
|
||||
switch (child_type)
|
||||
{
|
||||
case node_type::table:
|
||||
if (reinterpret_cast<const table*>(&child_v)->is_inline())
|
||||
child_value_count++;
|
||||
else
|
||||
child_table_count++;
|
||||
break;
|
||||
|
||||
case node_type::array:
|
||||
if (is_non_inline_array_of_tables(child_v))
|
||||
child_table_array_count++;
|
||||
else
|
||||
child_value_count++;
|
||||
break;
|
||||
|
||||
default: child_value_count++;
|
||||
}
|
||||
}
|
||||
bool skip_self = false;
|
||||
if (child_value_count == 0u && (child_table_count > 0u || child_table_array_count > 0u))
|
||||
skip_self = true;
|
||||
|
||||
key_path_.push_back(&k);
|
||||
|
||||
if (!skip_self)
|
||||
{
|
||||
print_pending_table_separator();
|
||||
if (indent_sub_tables())
|
||||
increase_indent();
|
||||
print_indent();
|
||||
print_unformatted("["sv);
|
||||
print_key_path();
|
||||
print_unformatted("]"sv);
|
||||
pending_table_separator_ = true;
|
||||
}
|
||||
|
||||
print(child_tbl);
|
||||
|
||||
key_path_.pop_back();
|
||||
if (!skip_self && indent_sub_tables())
|
||||
decrease_indent();
|
||||
}
|
||||
|
||||
// table arrays
|
||||
for (auto&& [k, v] : tbl)
|
||||
{
|
||||
if (!is_non_inline_array_of_tables(v))
|
||||
continue;
|
||||
auto& arr = *reinterpret_cast<const array*>(&v);
|
||||
|
||||
if (indent_sub_tables())
|
||||
increase_indent();
|
||||
key_path_.push_back(&k);
|
||||
|
||||
for (size_t i = 0; i < arr.size(); i++)
|
||||
{
|
||||
print_pending_table_separator();
|
||||
print_indent();
|
||||
print_unformatted("[["sv);
|
||||
print_key_path();
|
||||
print_unformatted("]]"sv);
|
||||
pending_table_separator_ = true;
|
||||
print(*reinterpret_cast<const table*>(&arr[i]));
|
||||
}
|
||||
|
||||
key_path_.pop_back();
|
||||
if (indent_sub_tables())
|
||||
decrease_indent();
|
||||
}
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void toml_formatter::print()
|
||||
{
|
||||
if (dump_failed_parse_result())
|
||||
return;
|
||||
|
||||
switch (auto source_type = source().type())
|
||||
{
|
||||
case node_type::table:
|
||||
{
|
||||
auto& tbl = *reinterpret_cast<const table*>(&source());
|
||||
if (tbl.is_inline())
|
||||
print_inline(tbl);
|
||||
else
|
||||
{
|
||||
decrease_indent(); // so root kvps and tables have the same indent
|
||||
print(tbl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&source())); break;
|
||||
|
||||
default: print_value(source(), source_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
#endif // TOML_ENABLE_FORMATTERS
|
||||
197
src/external/toml++/impl/unicode.hpp
vendored
Normal file
197
src/external/toml++/impl/unicode.hpp
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "unicode_autogenerated.hpp"
|
||||
#include "header_start.hpp"
|
||||
/// \cond
|
||||
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_string_delimiter(char32_t c) noexcept
|
||||
{
|
||||
return c == U'"' || c == U'\'';
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_ascii_letter(char32_t c) noexcept
|
||||
{
|
||||
return (c >= U'a' && c <= U'z') || (c >= U'A' && c <= U'Z');
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_binary_digit(char32_t c) noexcept
|
||||
{
|
||||
return c == U'0' || c == U'1';
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_octal_digit(char32_t c) noexcept
|
||||
{
|
||||
return (c >= U'0' && c <= U'7');
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_decimal_digit(char32_t c) noexcept
|
||||
{
|
||||
return (c >= U'0' && c <= U'9');
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_hexadecimal_digit(char32_t c) noexcept
|
||||
{
|
||||
return U'0' <= c && c <= U'f' && (1ull << (static_cast<uint_least64_t>(c) - 0x30u)) & 0x7E0000007E03FFull;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TOML_CONST_GETTER
|
||||
constexpr uint_least32_t hex_to_dec(const T c) noexcept
|
||||
{
|
||||
if constexpr (std::is_same_v<remove_cvref<T>, uint_least32_t>)
|
||||
return c >= 0x41u // >= 'A'
|
||||
? 10u + (c | 0x20u) - 0x61u // - 'a'
|
||||
: c - 0x30u // - '0'
|
||||
;
|
||||
else
|
||||
return hex_to_dec(static_cast<uint_least32_t>(c));
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_horizontal_whitespace(char32_t c) noexcept
|
||||
{
|
||||
return is_ascii_horizontal_whitespace(c) || is_non_ascii_horizontal_whitespace(c);
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_vertical_whitespace(char32_t c) noexcept
|
||||
{
|
||||
return is_ascii_vertical_whitespace(c) || is_non_ascii_vertical_whitespace(c);
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_whitespace(char32_t c) noexcept
|
||||
{
|
||||
return is_horizontal_whitespace(c) || is_vertical_whitespace(c);
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_bare_key_character(char32_t c) noexcept
|
||||
{
|
||||
return is_ascii_bare_key_character(c)
|
||||
#if TOML_LANG_UNRELEASED // toml/pull/891 (unicode bare keys)
|
||||
|| is_non_ascii_bare_key_character(c)
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_value_terminator(char32_t c) noexcept
|
||||
{
|
||||
return is_whitespace(c) || c == U']' || c == U'}' || c == U',' || c == U'#';
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_control_character(char c) noexcept
|
||||
{
|
||||
return c <= '\u001F' || c == '\u007F';
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_control_character(char32_t c) noexcept
|
||||
{
|
||||
return c <= U'\u001F' || c == U'\u007F';
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_nontab_control_character(char32_t c) noexcept
|
||||
{
|
||||
return c <= U'\u0008' || (c >= U'\u000A' && c <= U'\u001F') || c == U'\u007F';
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_unicode_surrogate(char32_t c) noexcept
|
||||
{
|
||||
return c >= 0xD800u && c <= 0xDFFF;
|
||||
}
|
||||
|
||||
struct utf8_decoder
|
||||
{
|
||||
// utf8_decoder based on this: https://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||
// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||
|
||||
uint_least32_t state{};
|
||||
char32_t codepoint{};
|
||||
|
||||
static constexpr uint8_t state_table[]{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6,
|
||||
6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
|
||||
0, 12, 24, 36, 60, 96, 84, 12, 12, 12, 48, 72, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 12,
|
||||
12, 12, 12, 12, 0, 12, 0, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 24, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||
12, 24, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 12, 12, 36, 12,
|
||||
36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12
|
||||
};
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
constexpr bool error() const noexcept
|
||||
{
|
||||
return state == uint_least32_t{ 12u };
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
constexpr bool has_code_point() const noexcept
|
||||
{
|
||||
return state == uint_least32_t{};
|
||||
}
|
||||
|
||||
TOML_PURE_INLINE_GETTER
|
||||
constexpr bool needs_more_input() const noexcept
|
||||
{
|
||||
return !has_code_point() && !error();
|
||||
}
|
||||
|
||||
constexpr void operator()(uint8_t byte) noexcept
|
||||
{
|
||||
TOML_ASSERT_ASSUME(!error());
|
||||
|
||||
const auto type = state_table[byte];
|
||||
|
||||
codepoint = static_cast<char32_t>(has_code_point() ? (uint_least32_t{ 255u } >> type) & byte
|
||||
: (byte & uint_least32_t{ 63u })
|
||||
| (static_cast<uint_least32_t>(codepoint) << 6));
|
||||
|
||||
state = state_table[state + uint_least32_t{ 256u } + type];
|
||||
}
|
||||
|
||||
TOML_ALWAYS_INLINE
|
||||
constexpr void operator()(char c) noexcept
|
||||
{
|
||||
operator()(static_cast<uint8_t>(c));
|
||||
}
|
||||
|
||||
TOML_ALWAYS_INLINE
|
||||
constexpr void reset() noexcept
|
||||
{
|
||||
state = {};
|
||||
}
|
||||
};
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_ATTR(nonnull)
|
||||
bool is_ascii(const char* str, size_t len) noexcept;
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
|
||||
/// \endcond
|
||||
#include "header_end.hpp"
|
||||
60
src/external/toml++/impl/unicode.inl
vendored
Normal file
60
src/external/toml++/impl/unicode.inl
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
//# {{
|
||||
#include "preprocessor.hpp"
|
||||
#if !TOML_IMPLEMENTATION
|
||||
#error This is an implementation-only header.
|
||||
#endif
|
||||
//# }}
|
||||
|
||||
#include "unicode.hpp"
|
||||
#include "simd.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool is_ascii(const char* str, size_t len) noexcept
|
||||
{
|
||||
const char* const end = str + len;
|
||||
|
||||
#if TOML_HAS_SSE2 && (128 % CHAR_BIT) == 0
|
||||
{
|
||||
constexpr size_t chars_per_vector = 128u / CHAR_BIT;
|
||||
|
||||
if (const size_t simdable = len - (len % chars_per_vector))
|
||||
{
|
||||
__m128i mask = _mm_setzero_si128();
|
||||
for (const char* const e = str + simdable; str < e; str += chars_per_vector)
|
||||
{
|
||||
const __m128i current_bytes = _mm_loadu_si128(reinterpret_cast<const __m128i*>(str));
|
||||
mask = _mm_or_si128(mask, current_bytes);
|
||||
}
|
||||
const __m128i has_error = _mm_cmpgt_epi8(_mm_setzero_si128(), mask);
|
||||
|
||||
#if TOML_HAS_SSE4_1
|
||||
if (!_mm_testz_si128(has_error, has_error))
|
||||
return false;
|
||||
#else
|
||||
if (_mm_movemask_epi8(_mm_cmpeq_epi8(has_error, _mm_setzero_si128())) != 0xFFFF)
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (; str < end; str++)
|
||||
if (static_cast<unsigned char>(*str) > 127u)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
182
src/external/toml++/impl/unicode_autogenerated.hpp
vendored
Normal file
182
src/external/toml++/impl/unicode_autogenerated.hpp
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
#include "header_start.hpp"
|
||||
/// \cond
|
||||
|
||||
#if TOML_GCC && TOML_GCC < 9
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize("O1") // codegen bugs
|
||||
#endif
|
||||
|
||||
// the functions in this namespace block are automatically generated by a tool - they are not meant to be hand-edited
|
||||
|
||||
TOML_IMPL_NAMESPACE_START
|
||||
{
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_ascii_horizontal_whitespace(char32_t c) noexcept
|
||||
{
|
||||
return c == U'\t' || c == U' ';
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_non_ascii_horizontal_whitespace(char32_t c) noexcept
|
||||
{
|
||||
// 20 code units from 8 ranges (spanning a search area of 65120)
|
||||
|
||||
if (c < U'\xA0' || c > U'\uFEFF')
|
||||
return false;
|
||||
|
||||
const auto child_index_0 = (static_cast<uint_least64_t>(c) - 0xA0ull) / 0x3FAull;
|
||||
if ((1ull << child_index_0) & 0x7FFFFFFFFFFFF75Eull)
|
||||
return false;
|
||||
if (c == U'\xA0' || c == U'\u3000' || c == U'\uFEFF')
|
||||
return true;
|
||||
switch (child_index_0)
|
||||
{
|
||||
case 0x05: return c == U'\u1680' || c == U'\u180E';
|
||||
case 0x07:
|
||||
return (U'\u2000' <= c && c <= U'\u200B') || (U'\u205F' <= c && c <= U'\u2060') || c == U'\u202F';
|
||||
default: TOML_UNREACHABLE;
|
||||
}
|
||||
|
||||
TOML_UNREACHABLE;
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_ascii_vertical_whitespace(char32_t c) noexcept
|
||||
{
|
||||
return c >= U'\n' && c <= U'\r';
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_non_ascii_vertical_whitespace(char32_t c) noexcept
|
||||
{
|
||||
return (U'\u2028' <= c && c <= U'\u2029') || c == U'\x85';
|
||||
}
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_ascii_bare_key_character(char32_t c) noexcept
|
||||
{
|
||||
#if TOML_LANG_UNRELEASED // toml/issues/644 ('+' in bare keys)
|
||||
if TOML_UNLIKELY(c == U'+')
|
||||
return true;
|
||||
#endif
|
||||
// 64 code units from 5 ranges (spanning a search area of 78)
|
||||
|
||||
if (c < U'-' || c > U'z')
|
||||
return false;
|
||||
|
||||
return (((static_cast<uint_least64_t>(c) - 0x2Dull) / 0x40ull) != 0ull)
|
||||
|| ((1ull << (static_cast<uint_least64_t>(c) - 0x2Dull)) & 0xFFF43FFFFFF01FF9ull);
|
||||
}
|
||||
|
||||
#if TOML_LANG_UNRELEASED // toml/pull/891 (unicode bare keys)
|
||||
|
||||
TOML_CONST_GETTER
|
||||
constexpr bool is_non_ascii_bare_key_character(char32_t c) noexcept
|
||||
{
|
||||
// 971732 code units from 16 ranges (spanning a search area of 982862)
|
||||
|
||||
if (c < U'\xB2' || c > U'\U000EFFFF')
|
||||
return false;
|
||||
|
||||
const auto child_index_0 = (static_cast<uint_least64_t>(c) - 0xB2ull) / 0x3BFEull;
|
||||
if ((1ull << child_index_0) & 0xFFFFFFFFFFFFFFE6ull)
|
||||
return true;
|
||||
switch (child_index_0)
|
||||
{
|
||||
case 0x00: // [0] 00B2 - 3CAF
|
||||
{
|
||||
// 12710 code units from 13 ranges (spanning a search area of 15358)
|
||||
|
||||
TOML_ASSUME(c >= U'\xB2' && c <= U'\u3CAF');
|
||||
|
||||
constexpr uint_least64_t bitmask_table_1[] = {
|
||||
0xFFFFFFDFFFFFDC83u, 0xFFFFFFFFFFFFFFDFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFEFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0x000000000C003FFFu, 0xC000000000006000u, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0x000000003FFFFFFFu,
|
||||
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
|
||||
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
|
||||
0x0000000000000000u, 0x0000000000000000u, 0xFFFFC00000000000u, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0x0000000000003FFFu, 0x0000000000000000u, 0x0000000000000000u,
|
||||
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
|
||||
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
|
||||
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
|
||||
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
|
||||
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
|
||||
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
|
||||
0x0000000000000000u, 0xFFFFFFFFFFFFC000u, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0x3FFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFF8000u, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
|
||||
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0x3FFFFFFFFFFFFFFFu,
|
||||
};
|
||||
return bitmask_table_1[(static_cast<uint_least64_t>(c) - 0xB2ull) / 0x40ull]
|
||||
& (0x1ull << ((static_cast<uint_least64_t>(c) - 0xB2ull) % 0x40ull));
|
||||
}
|
||||
case 0x03: return c <= U'\uD7FF';
|
||||
case 0x04:
|
||||
return (U'\uF900' <= c && c <= U'\uFDCF') || (U'\uFDF0' <= c && c <= U'\uFFFD') || U'\U00010000' <= c;
|
||||
default: TOML_UNREACHABLE;
|
||||
}
|
||||
|
||||
TOML_UNREACHABLE;
|
||||
}
|
||||
|
||||
#endif // TOML_LANG_UNRELEASED
|
||||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
|
||||
#if TOML_GCC && TOML_GCC < 9
|
||||
#pragma GCC pop_options
|
||||
#endif
|
||||
|
||||
/// \endcond
|
||||
#include "header_end.hpp"
|
||||
1273
src/external/toml++/impl/value.hpp
vendored
Normal file
1273
src/external/toml++/impl/value.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
13
src/external/toml++/impl/version.hpp
vendored
Normal file
13
src/external/toml++/impl/version.hpp
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#define TOML_LIB_MAJOR 3
|
||||
#define TOML_LIB_MINOR 4
|
||||
#define TOML_LIB_PATCH 0
|
||||
|
||||
#define TOML_LANG_MAJOR 1
|
||||
#define TOML_LANG_MINOR 0
|
||||
#define TOML_LANG_PATCH 0
|
||||
139
src/external/toml++/impl/yaml_formatter.hpp
vendored
Normal file
139
src/external/toml++/impl/yaml_formatter.hpp
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
#if TOML_ENABLE_FORMATTERS
|
||||
|
||||
#include "formatter.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
/// \brief A wrapper for printing TOML objects out to a stream as formatted YAML.
|
||||
///
|
||||
/// \availability This class is only available when #TOML_ENABLE_FORMATTERS is enabled.
|
||||
///
|
||||
/// \detail \cpp
|
||||
/// auto some_toml = toml::parse(R"(
|
||||
/// [fruit]
|
||||
/// apple.color = "red"
|
||||
/// apple.taste.sweet = true
|
||||
///
|
||||
/// [fruit.apple.texture]
|
||||
/// smooth = true
|
||||
/// )"sv);
|
||||
/// std::cout << toml::yaml_formatter{ some_toml } << "\n";
|
||||
/// \ecpp
|
||||
///
|
||||
/// \out
|
||||
/// fruit:
|
||||
/// apple:
|
||||
/// color: red
|
||||
/// taste:
|
||||
/// sweet: true
|
||||
/// texture:
|
||||
/// smooth: true
|
||||
/// \eout
|
||||
class TOML_EXPORTED_CLASS yaml_formatter : impl::formatter
|
||||
{
|
||||
private:
|
||||
/// \cond
|
||||
|
||||
using base = impl::formatter;
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print_yaml_string(const value<std::string>&);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const toml::table&, bool = false);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print(const toml::array&, bool = false);
|
||||
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
void print();
|
||||
|
||||
static constexpr impl::formatter_constants constants = {
|
||||
//
|
||||
format_flags::quote_dates_and_times | format_flags::indentation, // mandatory
|
||||
format_flags::allow_multi_line_strings, // ignored
|
||||
".inf"sv,
|
||||
"-.inf"sv,
|
||||
".NAN"sv,
|
||||
"true"sv,
|
||||
"false"sv
|
||||
};
|
||||
|
||||
/// \endcond
|
||||
|
||||
public:
|
||||
/// \brief The default flags for a yaml_formatter.
|
||||
static constexpr format_flags default_flags = constants.mandatory_flags //
|
||||
| format_flags::allow_literal_strings //
|
||||
| format_flags::allow_unicode_strings //
|
||||
| format_flags::allow_octal_integers //
|
||||
| format_flags::allow_hexadecimal_integers;
|
||||
|
||||
/// \brief Constructs a YAML formatter and binds it to a TOML object.
|
||||
///
|
||||
/// \param source The source TOML object.
|
||||
/// \param flags Format option flags.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit yaml_formatter(const toml::node& source, format_flags flags = default_flags) noexcept
|
||||
: base{ &source, nullptr, constants, { flags, " "sv } }
|
||||
{}
|
||||
|
||||
#if TOML_DOXYGEN || (TOML_ENABLE_PARSER && !TOML_EXCEPTIONS)
|
||||
|
||||
/// \brief Constructs a YAML formatter and binds it to a toml::parse_result.
|
||||
///
|
||||
/// \availability This constructor is only available when exceptions are disabled.
|
||||
///
|
||||
/// \attention Formatting a failed parse result will simply dump the error message out as-is.
|
||||
/// This will not be valid YAML, but at least gives you something to log or show up in diagnostics:
|
||||
/// \cpp
|
||||
/// std::cout << toml::yaml_formatter{ toml::parse("a = 'b'"sv) } // ok
|
||||
/// << "\n\n"
|
||||
/// << toml::yaml_formatter{ toml::parse("a = "sv) } // malformed
|
||||
/// << "\n";
|
||||
/// \ecpp
|
||||
/// \out
|
||||
/// a: b
|
||||
///
|
||||
/// Error while parsing key-value pair: encountered end-of-file
|
||||
/// (error occurred at line 1, column 5)
|
||||
/// \eout
|
||||
/// Use the library with exceptions if you want to avoid this scenario.
|
||||
///
|
||||
/// \param result The parse result.
|
||||
/// \param flags Format option flags.
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit yaml_formatter(const toml::parse_result& result, format_flags flags = default_flags) noexcept
|
||||
: base{ nullptr, &result, constants, { flags, " "sv } }
|
||||
{}
|
||||
|
||||
#endif
|
||||
|
||||
/// \brief Prints the bound TOML object out to the stream as YAML.
|
||||
friend std::ostream& TOML_CALLCONV operator<<(std::ostream& lhs, yaml_formatter& rhs)
|
||||
{
|
||||
rhs.attach(lhs);
|
||||
rhs.print();
|
||||
rhs.detach();
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/// \brief Prints the bound TOML object out to the stream as YAML (rvalue overload).
|
||||
friend std::ostream& TOML_CALLCONV operator<<(std::ostream& lhs, yaml_formatter&& rhs)
|
||||
{
|
||||
return lhs << rhs; // as lvalue
|
||||
}
|
||||
};
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
#endif // TOML_ENABLE_FORMATTERS
|
||||
165
src/external/toml++/impl/yaml_formatter.inl
vendored
Normal file
165
src/external/toml++/impl/yaml_formatter.inl
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include "preprocessor.hpp"
|
||||
//# {{
|
||||
#if !TOML_IMPLEMENTATION
|
||||
#error This is an implementation-only header.
|
||||
#endif
|
||||
//# }}
|
||||
#if TOML_ENABLE_FORMATTERS
|
||||
|
||||
#include "yaml_formatter.hpp"
|
||||
#include "print_to_stream.hpp"
|
||||
#include "table.hpp"
|
||||
#include "array.hpp"
|
||||
#include "header_start.hpp"
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void yaml_formatter::print_yaml_string(const value<std::string>& str)
|
||||
{
|
||||
if (str->empty())
|
||||
{
|
||||
base::print(str);
|
||||
return;
|
||||
}
|
||||
|
||||
bool contains_newline = false;
|
||||
for (auto c = str->c_str(), e = str->c_str() + str->length(); c < e && !contains_newline; c++)
|
||||
contains_newline = *c == '\n';
|
||||
|
||||
if (contains_newline)
|
||||
{
|
||||
print_unformatted("|-"sv);
|
||||
|
||||
increase_indent();
|
||||
|
||||
auto line_end = str->c_str() - 1u;
|
||||
const auto end = str->c_str() + str->length();
|
||||
while (line_end != end)
|
||||
{
|
||||
auto line_start = line_end + 1u;
|
||||
line_end = line_start;
|
||||
for (; line_end != end && *line_end != '\n'; line_end++)
|
||||
;
|
||||
|
||||
if TOML_LIKELY(line_start != line_end || line_end != end)
|
||||
{
|
||||
print_newline();
|
||||
print_indent();
|
||||
print_unformatted(std::string_view{ line_start, static_cast<size_t>(line_end - line_start) });
|
||||
}
|
||||
}
|
||||
|
||||
decrease_indent();
|
||||
}
|
||||
else
|
||||
print_string(*str, false, true);
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void yaml_formatter::print(const toml::table& tbl, bool parent_is_array)
|
||||
{
|
||||
if (tbl.empty())
|
||||
{
|
||||
print_unformatted("{}"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
increase_indent();
|
||||
|
||||
for (auto&& [k, v] : tbl)
|
||||
{
|
||||
if (!parent_is_array)
|
||||
{
|
||||
print_newline();
|
||||
print_indent();
|
||||
}
|
||||
parent_is_array = false;
|
||||
|
||||
print_string(k.str(), false, true);
|
||||
if (terse_kvps())
|
||||
print_unformatted(":"sv);
|
||||
else
|
||||
print_unformatted(": "sv);
|
||||
|
||||
const auto type = v.type();
|
||||
TOML_ASSUME(type != node_type::none);
|
||||
switch (type)
|
||||
{
|
||||
case node_type::table: print(*reinterpret_cast<const table*>(&v)); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&v)); break;
|
||||
case node_type::string: print_yaml_string(*reinterpret_cast<const value<std::string>*>(&v)); break;
|
||||
default: print_value(v, type);
|
||||
}
|
||||
}
|
||||
|
||||
decrease_indent();
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void yaml_formatter::print(const toml::array& arr, bool parent_is_array)
|
||||
{
|
||||
if (arr.empty())
|
||||
{
|
||||
print_unformatted("[]"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
increase_indent();
|
||||
|
||||
for (auto&& v : arr)
|
||||
{
|
||||
if (!parent_is_array)
|
||||
{
|
||||
print_newline();
|
||||
print_indent();
|
||||
}
|
||||
parent_is_array = false;
|
||||
|
||||
print_unformatted("- "sv);
|
||||
|
||||
const auto type = v.type();
|
||||
TOML_ASSUME(type != node_type::none);
|
||||
switch (type)
|
||||
{
|
||||
case node_type::table: print(*reinterpret_cast<const table*>(&v), true); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&v), true); break;
|
||||
case node_type::string: print_yaml_string(*reinterpret_cast<const value<std::string>*>(&v)); break;
|
||||
default: print_value(v, type);
|
||||
}
|
||||
}
|
||||
|
||||
decrease_indent();
|
||||
}
|
||||
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
void yaml_formatter::print()
|
||||
{
|
||||
if (dump_failed_parse_result())
|
||||
return;
|
||||
|
||||
switch (auto source_type = source().type())
|
||||
{
|
||||
case node_type::table:
|
||||
decrease_indent(); // so root kvps and tables have the same indent
|
||||
print(*reinterpret_cast<const table*>(&source()));
|
||||
break;
|
||||
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&source())); break;
|
||||
|
||||
case node_type::string: print_yaml_string(*reinterpret_cast<const value<std::string>*>(&source())); break;
|
||||
|
||||
default: print_value(source(), source_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
#include "header_end.hpp"
|
||||
#endif // TOML_ENABLE_FORMATTERS
|
||||
7
src/external/toml++/toml.h
vendored
Normal file
7
src/external/toml++/toml.h
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// this file is just for backwards compatibility.
|
||||
#ifndef TOMLPLUSPLUS_H
|
||||
#define TOMLPLUSPLUS_H
|
||||
|
||||
#include "toml.hpp"
|
||||
|
||||
#endif
|
||||
234
src/external/toml++/toml.hpp
vendored
Normal file
234
src/external/toml++/toml.hpp
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
|
||||
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#ifndef TOMLPLUSPLUS_HPP
|
||||
#define TOMLPLUSPLUS_HPP
|
||||
|
||||
#define INCLUDE_TOMLPLUSPLUS_H // old guard name used pre-v3
|
||||
#define TOMLPLUSPLUS_H // guard name used in the legacy toml.h
|
||||
|
||||
#include "impl/preprocessor.hpp"
|
||||
|
||||
TOML_PUSH_WARNINGS;
|
||||
TOML_DISABLE_SPAM_WARNINGS;
|
||||
TOML_DISABLE_SWITCH_WARNINGS;
|
||||
TOML_DISABLE_SUGGEST_ATTR_WARNINGS;
|
||||
|
||||
// misc warning false-positives
|
||||
#if TOML_MSVC
|
||||
#pragma warning(disable : 5031) // #pragma warning(pop): likely mismatch
|
||||
#if TOML_SHARED_LIB
|
||||
#pragma warning(disable : 4251) // dll exports for std lib types
|
||||
#endif
|
||||
#elif TOML_CLANG
|
||||
TOML_PRAGMA_CLANG(diagnostic ignored "-Wheader-hygiene")
|
||||
#if TOML_CLANG >= 12
|
||||
TOML_PRAGMA_CLANG(diagnostic ignored "-Wc++20-extensions")
|
||||
#endif
|
||||
#if TOML_CLANG == 13
|
||||
TOML_PRAGMA_CLANG(diagnostic ignored "-Wreserved-identifier")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "impl/std_new.hpp"
|
||||
#include "impl/std_string.hpp"
|
||||
#include "impl/std_optional.hpp"
|
||||
#include "impl/forward_declarations.hpp"
|
||||
#include "impl/print_to_stream.hpp"
|
||||
#include "impl/source_region.hpp"
|
||||
#include "impl/date_time.hpp"
|
||||
#include "impl/at_path.hpp"
|
||||
#include "impl/path.hpp"
|
||||
#include "impl/node.hpp"
|
||||
#include "impl/node_view.hpp"
|
||||
#include "impl/value.hpp"
|
||||
#include "impl/make_node.hpp"
|
||||
#include "impl/array.hpp"
|
||||
#include "impl/key.hpp"
|
||||
#include "impl/table.hpp"
|
||||
#include "impl/unicode_autogenerated.hpp"
|
||||
#include "impl/unicode.hpp"
|
||||
#include "impl/parse_error.hpp"
|
||||
#include "impl/parse_result.hpp"
|
||||
#include "impl/parser.hpp"
|
||||
#include "impl/formatter.hpp"
|
||||
#include "impl/toml_formatter.hpp"
|
||||
#include "impl/json_formatter.hpp"
|
||||
#include "impl/yaml_formatter.hpp"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#if TOML_IMPLEMENTATION
|
||||
|
||||
#include "impl/std_string.inl"
|
||||
#include "impl/print_to_stream.inl"
|
||||
#include "impl/node.inl"
|
||||
#include "impl/at_path.inl"
|
||||
#include "impl/path.inl"
|
||||
#include "impl/array.inl"
|
||||
#include "impl/table.inl"
|
||||
#include "impl/unicode.inl"
|
||||
#include "impl/parser.inl"
|
||||
#include "impl/formatter.inl"
|
||||
#include "impl/toml_formatter.inl"
|
||||
#include "impl/json_formatter.inl"
|
||||
#include "impl/yaml_formatter.inl"
|
||||
|
||||
#endif // TOML_IMPLEMENTATION
|
||||
|
||||
TOML_POP_WARNINGS;
|
||||
|
||||
// macro hygiene
|
||||
#if TOML_UNDEF_MACROS
|
||||
#undef TOML_ABI_NAMESPACE_BOOL
|
||||
#undef TOML_ABI_NAMESPACE_END
|
||||
#undef TOML_ABI_NAMESPACE_START
|
||||
#undef TOML_ABI_NAMESPACES
|
||||
#undef TOML_ABSTRACT_INTERFACE
|
||||
#undef TOML_ALWAYS_INLINE
|
||||
#undef TOML_ANON_NAMESPACE
|
||||
#undef TOML_ANON_NAMESPACE_END
|
||||
#undef TOML_ANON_NAMESPACE_START
|
||||
#undef TOML_ARCH_AMD64
|
||||
#undef TOML_ARCH_ARM
|
||||
#undef TOML_ARCH_ARM32
|
||||
#undef TOML_ARCH_ARM64
|
||||
#undef TOML_ARCH_BITNESS
|
||||
#undef TOML_ARCH_ITANIUM
|
||||
#undef TOML_ARCH_X64
|
||||
#undef TOML_ARCH_X86
|
||||
#undef TOML_ASSERT
|
||||
#undef TOML_ASSERT_ASSUME
|
||||
#undef TOML_ASSUME
|
||||
#undef TOML_ASYMMETRICAL_EQUALITY_OPS
|
||||
#undef TOML_ATTR
|
||||
#undef TOML_CLANG
|
||||
#undef TOML_CLANG_VERSION
|
||||
#undef TOML_CLOSED_ENUM
|
||||
#undef TOML_CLOSED_FLAGS_ENUM
|
||||
#undef TOML_COMPILER_HAS_EXCEPTIONS
|
||||
#undef TOML_COMPILER_HAS_RTTI
|
||||
#undef TOML_CONST
|
||||
#undef TOML_CONST_GETTER
|
||||
#undef TOML_CONST_INLINE_GETTER
|
||||
#undef TOML_CONSTRAINED_TEMPLATE
|
||||
#undef TOML_CPP
|
||||
#undef TOML_DECLSPEC
|
||||
#undef TOML_DELETE_DEFAULTS
|
||||
#undef TOML_DISABLE_ARITHMETIC_WARNINGS
|
||||
#undef TOML_DISABLE_CODE_ANALYSIS_WARNINGS
|
||||
#undef TOML_DISABLE_SPAM_WARNINGS
|
||||
#undef TOML_DISABLE_SPAM_WARNINGS_CLANG_10
|
||||
#undef TOML_DISABLE_SPAM_WARNINGS_CLANG_11
|
||||
#undef TOML_DISABLE_SUGGEST_ATTR_WARNINGS
|
||||
#undef TOML_DISABLE_SWITCH_WARNINGS
|
||||
#undef TOML_DISABLE_WARNINGS
|
||||
#undef TOML_DOXYGEN
|
||||
#undef TOML_EMPTY_BASES
|
||||
#undef TOML_ENABLE_IF
|
||||
#undef TOML_ENABLE_WARNINGS
|
||||
#undef TOML_EVAL_BOOL_0
|
||||
#undef TOML_EVAL_BOOL_1
|
||||
#undef TOML_EXTERNAL_LINKAGE
|
||||
#undef TOML_FLAGS_ENUM
|
||||
#undef TOML_FLOAT_CHARCONV
|
||||
#undef TOML_FLOAT128
|
||||
#undef TOML_FLOAT16_DIG
|
||||
#undef TOML_FLOAT16_LIMITS_SET
|
||||
#undef TOML_FLOAT16_MANT_DIG
|
||||
#undef TOML_FLOAT16_MAX_10_EXP
|
||||
#undef TOML_FLOAT16_MAX_EXP
|
||||
#undef TOML_FLOAT16_MIN_10_EXP
|
||||
#undef TOML_FLOAT16_MIN_EXP
|
||||
#undef TOML_GCC
|
||||
#undef TOML_GCC_LIKE
|
||||
#undef TOML_HAS_ATTR
|
||||
#undef TOML_HAS_BUILTIN
|
||||
#undef TOML_HAS_CHAR8
|
||||
#undef TOML_HAS_CPP_ATTR
|
||||
#undef TOML_HAS_CUSTOM_OPTIONAL_TYPE
|
||||
#undef TOML_HAS_FEATURE
|
||||
#undef TOML_HAS_INCLUDE
|
||||
#undef TOML_HAS_SSE2
|
||||
#undef TOML_HAS_SSE4_1
|
||||
#undef TOML_HIDDEN_CONSTRAINT
|
||||
#undef TOML_ICC
|
||||
#undef TOML_ICC_CL
|
||||
#undef TOML_IMPL_NAMESPACE_END
|
||||
#undef TOML_IMPL_NAMESPACE_START
|
||||
#undef TOML_IMPLEMENTATION
|
||||
#undef TOML_INCLUDE_WINDOWS_H
|
||||
#undef TOML_INLINE_GETTER
|
||||
#undef TOML_INT_CHARCONV
|
||||
#undef TOML_INT128
|
||||
#undef TOML_INTELLISENSE
|
||||
#undef TOML_INTERNAL_LINKAGE
|
||||
#undef TOML_LANG_AT_LEAST
|
||||
#undef TOML_LANG_EFFECTIVE_VERSION
|
||||
#undef TOML_LANG_HIGHER_THAN
|
||||
#undef TOML_LANG_UNRELEASED
|
||||
#undef TOML_LAUNDER
|
||||
#undef TOML_LIFETIME_HOOKS
|
||||
#undef TOML_LIKELY
|
||||
#undef TOML_LIKELY_CASE
|
||||
#undef TOML_LINUX
|
||||
#undef TOML_MAKE_FLAGS
|
||||
#undef TOML_MAKE_FLAGS_
|
||||
#undef TOML_MAKE_FLAGS_1
|
||||
#undef TOML_MAKE_FLAGS_2
|
||||
#undef TOML_MAKE_STRING
|
||||
#undef TOML_MAKE_STRING_1
|
||||
#undef TOML_MAKE_VERSION
|
||||
#undef TOML_MSVC
|
||||
#undef TOML_MSVC_LIKE
|
||||
#undef TOML_NAMESPACE
|
||||
#undef TOML_NEVER_INLINE
|
||||
#undef TOML_NODISCARD
|
||||
#undef TOML_NODISCARD_CTOR
|
||||
#undef TOML_NVCC
|
||||
#undef TOML_OPEN_ENUM
|
||||
#undef TOML_OPEN_FLAGS_ENUM
|
||||
#undef TOML_PARSER_TYPENAME
|
||||
#undef TOML_POP_WARNINGS
|
||||
#undef TOML_PRAGMA_CLANG
|
||||
#undef TOML_PRAGMA_CLANG_GE_10
|
||||
#undef TOML_PRAGMA_CLANG_GE_11
|
||||
#undef TOML_PRAGMA_CLANG_GE_8
|
||||
#undef TOML_PRAGMA_CLANG_GE_9
|
||||
#undef TOML_PRAGMA_GCC
|
||||
#undef TOML_PRAGMA_ICC
|
||||
#undef TOML_PRAGMA_MSVC
|
||||
#undef TOML_PURE
|
||||
#undef TOML_PURE_GETTER
|
||||
#undef TOML_PURE_INLINE_GETTER
|
||||
#undef TOML_PUSH_WARNINGS
|
||||
#undef TOML_REQUIRES
|
||||
#undef TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN
|
||||
#undef TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN_MESSAGE
|
||||
#undef TOML_SA_LIST_BEG
|
||||
#undef TOML_SA_LIST_END
|
||||
#undef TOML_SA_LIST_NEW
|
||||
#undef TOML_SA_LIST_NXT
|
||||
#undef TOML_SA_LIST_SEP
|
||||
#undef TOML_SA_NATIVE_VALUE_TYPE_LIST
|
||||
#undef TOML_SA_NEWLINE
|
||||
#undef TOML_SA_NODE_TYPE_LIST
|
||||
#undef TOML_SA_UNWRAPPED_NODE_TYPE_LIST
|
||||
#undef TOML_SA_VALUE_EXACT_FUNC_MESSAGE
|
||||
#undef TOML_SA_VALUE_FUNC_MESSAGE
|
||||
#undef TOML_SA_VALUE_MESSAGE_CONST_CHAR8
|
||||
#undef TOML_SA_VALUE_MESSAGE_U8STRING_VIEW
|
||||
#undef TOML_SA_VALUE_MESSAGE_WSTRING
|
||||
#undef TOML_SIMPLE_STATIC_ASSERT_MESSAGES
|
||||
#undef TOML_TRIVIAL_ABI
|
||||
#undef TOML_UINT128
|
||||
#undef TOML_UNIX
|
||||
#undef TOML_UNLIKELY
|
||||
#undef TOML_UNLIKELY_CASE
|
||||
#undef TOML_UNREACHABLE
|
||||
#undef TOML_UNUSED
|
||||
#undef TOML_WINDOWS
|
||||
#endif
|
||||
|
||||
#endif // TOMLPLUSPLUS_HPP
|
||||
23
src/lib/CMakeLists.txt
Normal file
23
src/lib/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
SET(HDRS)
|
||||
SET(SRCS)
|
||||
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(config)
|
||||
add_subdirectory(utility)
|
||||
add_subdirectory(sim)
|
||||
|
||||
SET(HDRS
|
||||
${HDRS}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
SET(SRCS
|
||||
${SRCS}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(LIB_INCLUDE_PATH
|
||||
${LIB_INCLUDE_PATH}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
26
src/lib/config/BuildingsConfig.h
Normal file
26
src/lib/config/BuildingsConfig.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "BuildingType.h"
|
||||
|
||||
// A single entry from buildings.toml [[building]].
|
||||
struct BuildingDef
|
||||
{
|
||||
std::string id; // Raw id string from TOML, e.g. "miner".
|
||||
BuildingType type; // Parsed from id at load time.
|
||||
int cost; // REQ-BLD-COST
|
||||
bool playerPlaceable; // Shown in the build menu if true.
|
||||
double constructionTimeSeconds; // REQ-BLD-QUEUE
|
||||
|
||||
// Rows of the surface_mask (REQ-BLD requirements, "Surface Mask Format").
|
||||
// Stored as raw strings here; parsing into per-cell tiles + output ports
|
||||
// happens when buildings are placed, not at load time.
|
||||
std::vector<std::string> surfaceMask;
|
||||
};
|
||||
|
||||
struct BuildingsConfig
|
||||
{
|
||||
std::vector<BuildingDef> buildings;
|
||||
};
|
||||
27
src/lib/config/CMakeLists.txt
Normal file
27
src/lib/config/CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
SET(HDRS
|
||||
${HDRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Formula.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/WorldConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildingsConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/RecipesConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipsConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/StationsConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
SET(SRCS
|
||||
${SRCS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Formula.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(LIB_INCLUDE_PATH
|
||||
${LIB_INCLUDE_PATH}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
498
src/lib/config/ConfigLoader.cpp
Normal file
498
src/lib/config/ConfigLoader.cpp
Normal file
@@ -0,0 +1,498 @@
|
||||
#include "ConfigLoader.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "toml.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// --- Error helpers --------------------------------------------------------
|
||||
|
||||
std::runtime_error makeError(const std::string& file,
|
||||
const std::string& path,
|
||||
const std::string& why)
|
||||
{
|
||||
return std::runtime_error("Config: " + file + ": '" + path + "' " + why);
|
||||
}
|
||||
|
||||
// --- Typed accessors (throw on missing or wrong type) ---------------------
|
||||
|
||||
int64_t requireInt(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const std::optional<int64_t> value = node.value<int64_t>();
|
||||
if (!value)
|
||||
{
|
||||
throw makeError(file, path, "missing or not an integer");
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
double requireDouble(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
if (const std::optional<double> v = node.value<double>())
|
||||
{
|
||||
return *v;
|
||||
}
|
||||
if (const std::optional<int64_t> v = node.value<int64_t>())
|
||||
{
|
||||
return static_cast<double>(*v);
|
||||
}
|
||||
throw makeError(file, path, "missing or not a number");
|
||||
}
|
||||
|
||||
std::string requireString(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const std::optional<std::string> value = node.value<std::string>();
|
||||
if (!value)
|
||||
{
|
||||
throw makeError(file, path, "missing or not a string");
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
bool requireBool(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const std::optional<bool> value = node.value<bool>();
|
||||
if (!value)
|
||||
{
|
||||
throw makeError(file, path, "missing or not a boolean");
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
const toml::array& requireArray(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const toml::array* arr = node.as_array();
|
||||
if (arr == nullptr)
|
||||
{
|
||||
throw makeError(file, path, "missing or not an array");
|
||||
}
|
||||
return *arr;
|
||||
}
|
||||
|
||||
const toml::table& requireTable(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const toml::table* tbl = node.as_table();
|
||||
if (tbl == nullptr)
|
||||
{
|
||||
throw makeError(file, path, "missing or not a table");
|
||||
}
|
||||
return *tbl;
|
||||
}
|
||||
|
||||
Formula requireFormula(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const std::string source = requireString(node, file, path);
|
||||
try
|
||||
{
|
||||
return Formula::compile(source);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw makeError(file, path, std::string("formula error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> requireStringArray(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const toml::array& arr = requireArray(node, file, path);
|
||||
std::vector<std::string> result;
|
||||
result.reserve(arr.size());
|
||||
for (std::size_t i = 0; i < arr.size(); ++i)
|
||||
{
|
||||
const std::string elemPath = path + "[" + std::to_string(i) + "]";
|
||||
const std::optional<std::string> s = arr[i].value<std::string>();
|
||||
if (!s)
|
||||
{
|
||||
throw makeError(file, elemPath, "not a string");
|
||||
}
|
||||
result.push_back(*s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<RecipeIngredient> parseIngredients(const toml::array& arr,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
std::vector<RecipeIngredient> result;
|
||||
result.reserve(arr.size());
|
||||
for (std::size_t i = 0; i < arr.size(); ++i)
|
||||
{
|
||||
const std::string elemPath = path + "[" + std::to_string(i) + "]";
|
||||
const toml::table* t = arr[i].as_table();
|
||||
if (t == nullptr)
|
||||
{
|
||||
throw makeError(file, elemPath, "not a table");
|
||||
}
|
||||
|
||||
// We need a mutable node_view to reuse our helpers, which is fine
|
||||
// because the helpers never mutate.
|
||||
toml::table& mt = const_cast<toml::table&>(*t);
|
||||
|
||||
RecipeIngredient ing;
|
||||
ing.item = requireString(mt["item"], file, elemPath + ".item");
|
||||
ing.amount = static_cast<int>(requireInt(mt["amount"], file, elemPath + ".amount"));
|
||||
result.push_back(std::move(ing));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<RecipeOutput> parseRecipeOutputs(const toml::array& arr,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
std::vector<RecipeOutput> result;
|
||||
result.reserve(arr.size());
|
||||
for (std::size_t i = 0; i < arr.size(); ++i)
|
||||
{
|
||||
const std::string elemPath = path + "[" + std::to_string(i) + "]";
|
||||
const toml::table* t = arr[i].as_table();
|
||||
if (t == nullptr)
|
||||
{
|
||||
throw makeError(file, elemPath, "not a table");
|
||||
}
|
||||
toml::table& mt = const_cast<toml::table&>(*t);
|
||||
|
||||
RecipeOutput out;
|
||||
out.item = requireString(mt["item"], file, elemPath + ".item");
|
||||
out.amount = static_cast<int>(requireInt(mt["amount"], file, elemPath + ".amount"));
|
||||
if (const std::optional<double> p = mt["probability"].value<double>())
|
||||
{
|
||||
out.probability = *p;
|
||||
}
|
||||
else if (const std::optional<int64_t> p = mt["probability"].value<int64_t>())
|
||||
{
|
||||
out.probability = static_cast<double>(*p);
|
||||
}
|
||||
result.push_back(std::move(out));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
toml::table parseFile(const std::string& path, const std::string& file)
|
||||
{
|
||||
try
|
||||
{
|
||||
return toml::parse_file(path);
|
||||
}
|
||||
catch (const toml::parse_error& e)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Config: " << file << ": TOML parse error: " << e.description()
|
||||
<< " at " << e.source().begin;
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// --- Per-file loaders -----------------------------------------------------
|
||||
|
||||
WorldConfig ConfigLoader::loadWorld(const std::string& path)
|
||||
{
|
||||
const std::string file = "world.toml";
|
||||
toml::table tbl = parseFile(path, file);
|
||||
|
||||
WorldConfig cfg;
|
||||
|
||||
cfg.heightTiles = static_cast<int>(requireInt(tbl["world"]["height_tiles"], file, "world.height_tiles"));
|
||||
cfg.refundPercentage = static_cast<int>(requireInt(tbl["world"]["refund_percentage"], file, "world.refund_percentage"));
|
||||
cfg.startingBuildingBlocks = static_cast<int>(requireInt(tbl["world"]["starting_building_blocks"], file, "world.starting_building_blocks"));
|
||||
cfg.scrapDespawnSeconds = requireDouble(tbl["world"]["scrap_despawn_seconds"], file, "world.scrap_despawn_seconds");
|
||||
cfg.beltSpeedTilesPerSecond = requireDouble(tbl["world"]["belt_speed_tiles_per_second"], file, "world.belt_speed_tiles_per_second");
|
||||
|
||||
cfg.regions.asteroidWidth = static_cast<int>(requireInt(tbl["regions"]["asteroid_width"], file, "regions.asteroid_width"));
|
||||
cfg.regions.playerBufferWidth = static_cast<int>(requireInt(tbl["regions"]["player_buffer_width"], file, "regions.player_buffer_width"));
|
||||
cfg.regions.contestZoneWidth = static_cast<int>(requireInt(tbl["regions"]["contest_zone_width"], file, "regions.contest_zone_width"));
|
||||
cfg.regions.enemyBufferWidth = static_cast<int>(requireInt(tbl["regions"]["enemy_buffer_width"], file, "regions.enemy_buffer_width"));
|
||||
|
||||
cfg.expansion.columnsPerExpansion = static_cast<int>(requireInt(tbl["expansion"]["columns_per_expansion"], file, "expansion.columns_per_expansion"));
|
||||
cfg.expansion.costBuildingBlocks = static_cast<int>(requireInt(tbl["expansion"]["cost_building_blocks"], file, "expansion.cost_building_blocks"));
|
||||
|
||||
cfg.push.pushExpandColumns = static_cast<int>(requireInt(tbl["push"]["push_expand_columns"], file, "push.push_expand_columns"));
|
||||
cfg.push.scalingFactor = requireDouble(tbl["push"]["scaling_factor"], file, "push.scaling_factor");
|
||||
|
||||
cfg.waves.threatRateFormula = requireFormula(tbl["waves"]["threat_rate_formula"], file, "waves.threat_rate_formula");
|
||||
cfg.waves.shipLevelFormula = requireFormula(tbl["waves"]["ship_level_formula"], file, "waves.ship_level_formula");
|
||||
cfg.waves.gapMinSeconds = requireDouble(tbl["waves"]["gap_min_seconds"], file, "waves.gap_min_seconds");
|
||||
cfg.waves.gapMaxSeconds = requireDouble(tbl["waves"]["gap_max_seconds"], file, "waves.gap_max_seconds");
|
||||
cfg.waves.spawnDurationSeconds = requireDouble(tbl["waves"]["spawn_duration_seconds"], file, "waves.spawn_duration_seconds");
|
||||
|
||||
if (cfg.waves.gapMinSeconds > cfg.waves.gapMaxSeconds)
|
||||
{
|
||||
throw makeError(file, "waves", "gap_min_seconds > gap_max_seconds");
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
BuildingsConfig ConfigLoader::loadBuildings(const std::string& path)
|
||||
{
|
||||
const std::string file = "buildings.toml";
|
||||
toml::table tbl = parseFile(path, file);
|
||||
|
||||
BuildingsConfig cfg;
|
||||
const toml::array& arr = requireArray(tbl["building"], file, "building");
|
||||
|
||||
for (std::size_t i = 0; i < arr.size(); ++i)
|
||||
{
|
||||
const std::string elemPath = "building[" + std::to_string(i) + "]";
|
||||
const toml::table* bt = arr[i].as_table();
|
||||
if (bt == nullptr)
|
||||
{
|
||||
throw makeError(file, elemPath, "not a table");
|
||||
}
|
||||
toml::table& mt = const_cast<toml::table&>(*bt);
|
||||
|
||||
BuildingDef def;
|
||||
def.id = requireString(mt["id"], file, elemPath + ".id");
|
||||
def.cost = static_cast<int>(requireInt(mt["cost"], file, elemPath + ".cost"));
|
||||
def.playerPlaceable = requireBool(mt["player_placeable"], file, elemPath + ".player_placeable");
|
||||
def.constructionTimeSeconds = requireDouble(mt["construction_time_seconds"], file, elemPath + ".construction_time_seconds");
|
||||
def.surfaceMask = requireStringArray(mt["surface_mask"], file, elemPath + ".surface_mask");
|
||||
|
||||
const std::optional<BuildingType> parsedType = parseBuildingType(def.id);
|
||||
if (!parsedType)
|
||||
{
|
||||
throw makeError(file, elemPath + ".id", "unknown building id '" + def.id + "'");
|
||||
}
|
||||
def.type = *parsedType;
|
||||
|
||||
cfg.buildings.push_back(std::move(def));
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
RecipesConfig ConfigLoader::loadRecipes(const std::string& path)
|
||||
{
|
||||
const std::string file = "recipes.toml";
|
||||
toml::table tbl = parseFile(path, file);
|
||||
|
||||
RecipesConfig cfg;
|
||||
const toml::array& arr = requireArray(tbl["recipe"], file, "recipe");
|
||||
|
||||
for (std::size_t i = 0; i < arr.size(); ++i)
|
||||
{
|
||||
const std::string elemPath = "recipe[" + std::to_string(i) + "]";
|
||||
const toml::table* rt = arr[i].as_table();
|
||||
if (rt == nullptr)
|
||||
{
|
||||
throw makeError(file, elemPath, "not a table");
|
||||
}
|
||||
toml::table& mt = const_cast<toml::table&>(*rt);
|
||||
|
||||
RecipeDef def;
|
||||
def.id = requireString(mt["id"], file, elemPath + ".id");
|
||||
def.durationSeconds = requireDouble(mt["duration_seconds"], file, elemPath + ".duration_seconds");
|
||||
|
||||
const std::string buildingId = requireString(mt["building"], file, elemPath + ".building");
|
||||
const std::optional<BuildingType> parsedType = parseBuildingType(buildingId);
|
||||
if (!parsedType)
|
||||
{
|
||||
throw makeError(file, elemPath + ".building",
|
||||
"unknown building id '" + buildingId + "'");
|
||||
}
|
||||
def.building = *parsedType;
|
||||
|
||||
// inputs may be omitted (e.g. miner recipes). An empty array is fine.
|
||||
if (mt.contains("inputs"))
|
||||
{
|
||||
const toml::array& inputs = requireArray(mt["inputs"], file, elemPath + ".inputs");
|
||||
def.inputs = parseIngredients(inputs, file, elemPath + ".inputs");
|
||||
}
|
||||
|
||||
const toml::array& outputs = requireArray(mt["outputs"], file, elemPath + ".outputs");
|
||||
def.outputs = parseRecipeOutputs(outputs, file, elemPath + ".outputs");
|
||||
|
||||
cfg.recipes.push_back(std::move(def));
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
ShipsConfig ConfigLoader::loadShips(const std::string& path)
|
||||
{
|
||||
const std::string file = "ships.toml";
|
||||
toml::table tbl = parseFile(path, file);
|
||||
|
||||
ShipsConfig cfg;
|
||||
const toml::array& arr = requireArray(tbl["ship"], file, "ship");
|
||||
|
||||
for (std::size_t i = 0; i < arr.size(); ++i)
|
||||
{
|
||||
const std::string elemPath = "ship[" + std::to_string(i) + "]";
|
||||
const toml::table* st = arr[i].as_table();
|
||||
if (st == nullptr)
|
||||
{
|
||||
throw makeError(file, elemPath, "not a table");
|
||||
}
|
||||
toml::table& mt = const_cast<toml::table&>(*st);
|
||||
|
||||
ShipDef def;
|
||||
def.id = requireString(mt["id"], file, elemPath + ".id");
|
||||
def.availableFromStart = requireBool(mt["available_from_start"], file, elemPath + ".available_from_start");
|
||||
|
||||
// Blueprint
|
||||
{
|
||||
const std::string bpPath = elemPath + ".blueprint";
|
||||
const toml::table& bpTable = requireTable(mt["blueprint"], file, bpPath);
|
||||
toml::table& bpMt = const_cast<toml::table&>(bpTable);
|
||||
|
||||
const toml::array& materials = requireArray(bpMt["materials"], file, bpPath + ".materials");
|
||||
def.blueprint.materials = parseIngredients(materials, file, bpPath + ".materials");
|
||||
def.blueprint.playerProductionLevel = static_cast<int>(requireInt(
|
||||
bpMt["player_production_level"], file, bpPath + ".player_production_level"));
|
||||
}
|
||||
|
||||
// Threat
|
||||
{
|
||||
const std::string tPath = elemPath + ".threat";
|
||||
const toml::table& tTable = requireTable(mt["threat"], file, tPath);
|
||||
toml::table& tMt = const_cast<toml::table&>(tTable);
|
||||
def.threat.costFormula = requireFormula(tMt["cost_formula"], file, tPath + ".cost_formula");
|
||||
}
|
||||
|
||||
// Health
|
||||
{
|
||||
const std::string hPath = elemPath + ".health";
|
||||
const toml::table& hTable = requireTable(mt["health"], file, hPath);
|
||||
toml::table& hMt = const_cast<toml::table&>(hTable);
|
||||
def.health.hpFormula = requireFormula(hMt["hp_formula"], file, hPath + ".hp_formula");
|
||||
}
|
||||
|
||||
// Movement
|
||||
{
|
||||
const std::string mPath = elemPath + ".movement";
|
||||
const toml::table& mTable = requireTable(mt["movement"], file, mPath);
|
||||
toml::table& mMt = const_cast<toml::table&>(mTable);
|
||||
def.movement.speedFormula = requireFormula(mMt["speed_formula"], file, mPath + ".speed_formula");
|
||||
}
|
||||
|
||||
// Loot
|
||||
{
|
||||
const std::string lPath = elemPath + ".loot";
|
||||
const toml::table& lTable = requireTable(mt["loot"], file, lPath);
|
||||
toml::table& lMt = const_cast<toml::table&>(lTable);
|
||||
def.loot.scrapDrop = static_cast<int>(requireInt(lMt["scrap_drop"], file, lPath + ".scrap_drop"));
|
||||
}
|
||||
|
||||
// Optional: combat
|
||||
if (mt.contains("combat"))
|
||||
{
|
||||
const std::string cPath = elemPath + ".combat";
|
||||
const toml::table& cTable = requireTable(mt["combat"], file, cPath);
|
||||
toml::table& cMt = const_cast<toml::table&>(cTable);
|
||||
ShipCombat combat {
|
||||
requireFormula(cMt["damage_formula"], file, cPath + ".damage_formula"),
|
||||
requireFormula(cMt["attack_range_formula"], file, cPath + ".attack_range_formula"),
|
||||
requireFormula(cMt["attack_rate_formula"], file, cPath + ".attack_rate_formula"),
|
||||
};
|
||||
def.combat = std::move(combat);
|
||||
}
|
||||
|
||||
// Optional: salvage
|
||||
if (mt.contains("salvage"))
|
||||
{
|
||||
const std::string sPath = elemPath + ".salvage";
|
||||
const toml::table& sTable = requireTable(mt["salvage"], file, sPath);
|
||||
toml::table& sMt = const_cast<toml::table&>(sTable);
|
||||
ShipSalvage salvage;
|
||||
salvage.collectionRange = requireDouble(sMt["collection_range"], file, sPath + ".collection_range");
|
||||
salvage.cargoCapacity = static_cast<int>(requireInt(sMt["cargo_capacity"], file, sPath + ".cargo_capacity"));
|
||||
def.salvage = salvage;
|
||||
}
|
||||
|
||||
// Optional: repair
|
||||
if (mt.contains("repair"))
|
||||
{
|
||||
const std::string rPath = elemPath + ".repair";
|
||||
const toml::table& rTable = requireTable(mt["repair"], file, rPath);
|
||||
toml::table& rMt = const_cast<toml::table&>(rTable);
|
||||
ShipRepair repair {
|
||||
requireFormula(rMt["repair_rate_formula"], file, rPath + ".repair_rate_formula"),
|
||||
requireFormula(rMt["repair_range_formula"], file, rPath + ".repair_range_formula"),
|
||||
};
|
||||
def.repair = std::move(repair);
|
||||
}
|
||||
|
||||
cfg.ships.push_back(std::move(def));
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
StationsConfig ConfigLoader::loadStations(const std::string& path)
|
||||
{
|
||||
const std::string file = "stations.toml";
|
||||
toml::table tbl = parseFile(path, file);
|
||||
|
||||
StationsConfig cfg;
|
||||
|
||||
// HQ
|
||||
{
|
||||
const std::string p = "hq";
|
||||
cfg.hq.surfaceMask = requireStringArray(tbl[p]["surface_mask"], file, p + ".surface_mask");
|
||||
cfg.hq.hpFormula = requireFormula(tbl[p]["hp_formula"], file, p + ".hp_formula");
|
||||
}
|
||||
|
||||
// Player station
|
||||
{
|
||||
const std::string p = "player_station";
|
||||
cfg.playerStation.surfaceMask = requireStringArray(tbl[p]["surface_mask"], file, p + ".surface_mask");
|
||||
cfg.playerStation.level = static_cast<int>(requireInt(tbl[p]["level"], file, p + ".level"));
|
||||
cfg.playerStation.hpFormula = requireFormula(tbl[p]["hp_formula"], file, p + ".hp_formula");
|
||||
cfg.playerStation.damageFormula = requireFormula(tbl[p]["damage_formula"], file, p + ".damage_formula");
|
||||
cfg.playerStation.rangeFormula = requireFormula(tbl[p]["range_formula"], file, p + ".range_formula");
|
||||
cfg.playerStation.fireRateFormula = requireFormula(tbl[p]["fire_rate_formula"], file, p + ".fire_rate_formula");
|
||||
cfg.playerStation.scrapDropFormula = requireFormula(tbl[p]["scrap_drop_formula"], file, p + ".scrap_drop_formula");
|
||||
}
|
||||
|
||||
// Enemy station
|
||||
{
|
||||
const std::string p = "enemy_station";
|
||||
cfg.enemyStation.surfaceMask = requireStringArray(tbl[p]["surface_mask"], file, p + ".surface_mask");
|
||||
cfg.enemyStation.hpFormula = requireFormula(tbl[p]["hp_formula"], file, p + ".hp_formula");
|
||||
cfg.enemyStation.damageFormula = requireFormula(tbl[p]["damage_formula"], file, p + ".damage_formula");
|
||||
cfg.enemyStation.rangeFormula = requireFormula(tbl[p]["range_formula"], file, p + ".range_formula");
|
||||
cfg.enemyStation.fireRateFormula = requireFormula(tbl[p]["fire_rate_formula"], file, p + ".fire_rate_formula");
|
||||
cfg.enemyStation.scrapDropFormula = requireFormula(tbl[p]["scrap_drop_formula"], file, p + ".scrap_drop_formula");
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
GameConfig ConfigLoader::loadFromDirectory(const std::string& configDir)
|
||||
{
|
||||
GameConfig cfg;
|
||||
cfg.world = loadWorld(configDir + "/world.toml");
|
||||
cfg.buildings = loadBuildings(configDir + "/buildings.toml");
|
||||
cfg.recipes = loadRecipes(configDir + "/recipes.toml");
|
||||
cfg.ships = loadShips(configDir + "/ships.toml");
|
||||
cfg.stations = loadStations(configDir + "/stations.toml");
|
||||
return cfg;
|
||||
}
|
||||
24
src/lib/config/ConfigLoader.h
Normal file
24
src/lib/config/ConfigLoader.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "GameConfig.h"
|
||||
|
||||
// Parses the five simulation TOML files from a directory and returns a fully
|
||||
// populated, immutable GameConfig. Throws std::runtime_error on any parse or
|
||||
// validation failure; the exception message identifies the offending file,
|
||||
// field, or formula (see architecture.md "Config Loading").
|
||||
//
|
||||
// Per-file helpers are exposed so tests can exercise individual loaders in
|
||||
// isolation.
|
||||
class ConfigLoader
|
||||
{
|
||||
public:
|
||||
static GameConfig loadFromDirectory(const std::string& configDir);
|
||||
|
||||
static WorldConfig loadWorld(const std::string& path);
|
||||
static BuildingsConfig loadBuildings(const std::string& path);
|
||||
static RecipesConfig loadRecipes(const std::string& path);
|
||||
static ShipsConfig loadShips(const std::string& path);
|
||||
static StationsConfig loadStations(const std::string& path);
|
||||
};
|
||||
68
src/lib/config/Formula.cpp
Normal file
68
src/lib/config/Formula.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "Formula.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "tinyexpr.h"
|
||||
|
||||
Formula::Formula(Formula&& other) noexcept
|
||||
: m_source(std::move(other.m_source))
|
||||
, m_x(std::move(other.m_x))
|
||||
, m_expr(other.m_expr)
|
||||
{
|
||||
other.m_expr = nullptr;
|
||||
}
|
||||
|
||||
Formula& Formula::operator=(Formula&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
te_free(m_expr);
|
||||
m_source = std::move(other.m_source);
|
||||
m_x = std::move(other.m_x);
|
||||
m_expr = other.m_expr;
|
||||
other.m_expr = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Formula::~Formula()
|
||||
{
|
||||
te_free(m_expr);
|
||||
}
|
||||
|
||||
Formula Formula::compile(const std::string& source)
|
||||
{
|
||||
Formula result;
|
||||
result.m_source = source;
|
||||
result.m_x = std::make_unique<double>(0.0);
|
||||
|
||||
const te_variable variables[] = {
|
||||
{ "x", result.m_x.get(), 0, nullptr },
|
||||
};
|
||||
|
||||
int errorPos = 0;
|
||||
result.m_expr = te_compile(result.m_source.c_str(), variables, 1, &errorPos);
|
||||
|
||||
if (result.m_expr == nullptr)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Formula parse error at position " + std::to_string(errorPos)
|
||||
+ " in \"" + source + "\"");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
double Formula::evaluate(double x) const
|
||||
{
|
||||
if (m_expr == nullptr)
|
||||
{
|
||||
throw std::runtime_error("Formula::evaluate called on uninitialized formula");
|
||||
}
|
||||
|
||||
// The variable at *m_x is what the compiled tree dereferences during
|
||||
// te_eval. Mutating it here does not affect logical const-ness of the
|
||||
// formula — the formula itself is unchanged.
|
||||
*m_x = x;
|
||||
return te_eval(m_expr);
|
||||
}
|
||||
40
src/lib/config/Formula.h
Normal file
40
src/lib/config/Formula.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// Forward declaration so tinyexpr.h stays out of this header.
|
||||
struct te_expr;
|
||||
|
||||
// Compiled single-variable expression. The bound variable is named x.
|
||||
// Compile once at config load; evaluate many times at simulation time.
|
||||
// tinyexpr bakes the address of the bound variable into the compiled tree, so
|
||||
// we keep x on the heap to preserve that pointer across moves.
|
||||
class Formula
|
||||
{
|
||||
public:
|
||||
Formula() = default;
|
||||
|
||||
Formula(const Formula&) = delete;
|
||||
Formula& operator=(const Formula&) = delete;
|
||||
|
||||
Formula(Formula&& other) noexcept;
|
||||
Formula& operator=(Formula&& other) noexcept;
|
||||
|
||||
~Formula();
|
||||
|
||||
// Parses source and returns a ready-to-evaluate Formula.
|
||||
// Throws std::runtime_error with the source and error position on failure.
|
||||
static Formula compile(const std::string& source);
|
||||
|
||||
// Evaluates the expression at the given x. Requires a compiled formula.
|
||||
double evaluate(double x) const;
|
||||
|
||||
const std::string& source() const { return m_source; }
|
||||
bool isValid() const { return m_expr != nullptr; }
|
||||
|
||||
private:
|
||||
std::string m_source;
|
||||
std::unique_ptr<double> m_x;
|
||||
te_expr* m_expr = nullptr;
|
||||
};
|
||||
18
src/lib/config/GameConfig.h
Normal file
18
src/lib/config/GameConfig.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "WorldConfig.h"
|
||||
#include "BuildingsConfig.h"
|
||||
#include "RecipesConfig.h"
|
||||
#include "ShipsConfig.h"
|
||||
#include "StationsConfig.h"
|
||||
|
||||
// Aggregate of all five simulation config files, loaded once at startup and
|
||||
// immutable for the rest of the game. See architecture.md "Config Loading".
|
||||
struct GameConfig
|
||||
{
|
||||
WorldConfig world;
|
||||
BuildingsConfig buildings;
|
||||
RecipesConfig recipes;
|
||||
ShipsConfig ships;
|
||||
StationsConfig stations;
|
||||
};
|
||||
40
src/lib/config/RecipesConfig.h
Normal file
40
src/lib/config/RecipesConfig.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "BuildingType.h"
|
||||
|
||||
// One entry in [[recipe]].inputs — amount units of a named item consumed per
|
||||
// production cycle (REQ-MAT-CYCLE).
|
||||
struct RecipeIngredient
|
||||
{
|
||||
std::string item;
|
||||
int amount;
|
||||
};
|
||||
|
||||
// One entry in [[recipe]].outputs. For reprocessing_plant recipes, probability
|
||||
// is populated and outputs are rolled with weighted pick at cycle start
|
||||
// (REQ-BLD-REPROCESSING, REQ-MAT-CYCLE). For other buildings, probability is
|
||||
// std::nullopt and all outputs are produced on every cycle.
|
||||
struct RecipeOutput
|
||||
{
|
||||
std::string item;
|
||||
int amount;
|
||||
std::optional<double> probability;
|
||||
};
|
||||
|
||||
struct RecipeDef
|
||||
{
|
||||
std::string id; // Unique recipe id; used by UI for selection.
|
||||
BuildingType building; // Which BuildingType can run this recipe.
|
||||
std::vector<RecipeIngredient> inputs;
|
||||
std::vector<RecipeOutput> outputs;
|
||||
double durationSeconds;
|
||||
};
|
||||
|
||||
struct RecipesConfig
|
||||
{
|
||||
std::vector<RecipeDef> recipes;
|
||||
};
|
||||
84
src/lib/config/ShipsConfig.h
Normal file
84
src/lib/config/ShipsConfig.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Formula.h"
|
||||
#include "RecipesConfig.h" // for RecipeIngredient
|
||||
|
||||
// Build materials and initial per-blueprint production level
|
||||
// (REQ-BLD-SHIPYARD, REQ-DEF-BLUEPRINT-DROP).
|
||||
struct ShipBlueprint
|
||||
{
|
||||
std::vector<RecipeIngredient> materials;
|
||||
int playerProductionLevel;
|
||||
};
|
||||
|
||||
// Wave scheduling cost (REQ-WAV-THREAT-COST). Ships with cost_formula that
|
||||
// always evaluates to 0 are ineligible as wave picks.
|
||||
struct ShipThreat
|
||||
{
|
||||
Formula costFormula;
|
||||
};
|
||||
|
||||
struct ShipHealth
|
||||
{
|
||||
Formula hpFormula; // REQ-SHP-STATS
|
||||
};
|
||||
|
||||
struct ShipMovement
|
||||
{
|
||||
Formula speedFormula; // REQ-SHP-STATS, REQ-SHP-MOVEMENT
|
||||
};
|
||||
|
||||
struct ShipCombat
|
||||
{
|
||||
Formula damageFormula;
|
||||
Formula attackRangeFormula;
|
||||
Formula attackRateFormula; // shots per second
|
||||
};
|
||||
|
||||
// Optional; present only on salvage ships (REQ-SHP-SALVAGE).
|
||||
struct ShipSalvage
|
||||
{
|
||||
double collectionRange;
|
||||
int cargoCapacity;
|
||||
};
|
||||
|
||||
// Optional; present only on repair ships (REQ-SHP-REPAIR).
|
||||
struct ShipRepair
|
||||
{
|
||||
Formula repairRateFormula;
|
||||
Formula repairRangeFormula;
|
||||
};
|
||||
|
||||
// Scrap dropped on destruction (REQ-RES-SCRAP-DROP).
|
||||
struct ShipLoot
|
||||
{
|
||||
int scrapDrop;
|
||||
};
|
||||
|
||||
struct ShipDef
|
||||
{
|
||||
std::string id;
|
||||
bool availableFromStart;
|
||||
|
||||
ShipBlueprint blueprint;
|
||||
ShipThreat threat;
|
||||
ShipHealth health;
|
||||
ShipMovement movement;
|
||||
ShipLoot loot;
|
||||
|
||||
// Role-specific sections. A ship is a combat ship if combat is present,
|
||||
// a salvage ship if salvage is present, etc. A ship may have multiple
|
||||
// of these set (hybrid ships) once the behavior systems support it.
|
||||
std::optional<ShipCombat> combat;
|
||||
std::optional<ShipSalvage> salvage;
|
||||
std::optional<ShipRepair> repair;
|
||||
};
|
||||
|
||||
struct ShipsConfig
|
||||
{
|
||||
std::vector<ShipDef> ships;
|
||||
};
|
||||
45
src/lib/config/StationsConfig.h
Normal file
45
src/lib/config/StationsConfig.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Formula.h"
|
||||
|
||||
// REQ-HQ-STATS. HP may become a formula of player level later; for now the
|
||||
// requirement only lists hp so we store a formula that callers evaluate at 0.
|
||||
struct HqConfig
|
||||
{
|
||||
std::vector<std::string> surfaceMask;
|
||||
Formula hpFormula;
|
||||
};
|
||||
|
||||
// REQ-DEF-PLAYER-FIRE. Stats are formulas of a fixed station level.
|
||||
struct PlayerStationConfig
|
||||
{
|
||||
std::vector<std::string> surfaceMask;
|
||||
int level;
|
||||
Formula hpFormula;
|
||||
Formula damageFormula;
|
||||
Formula rangeFormula;
|
||||
Formula fireRateFormula; // shots per second
|
||||
Formula scrapDropFormula;
|
||||
};
|
||||
|
||||
// REQ-PSH-STATION-STATS. Stats are formulas of the station generation level,
|
||||
// which increments each time a new set is placed.
|
||||
struct EnemyStationConfig
|
||||
{
|
||||
std::vector<std::string> surfaceMask;
|
||||
Formula hpFormula;
|
||||
Formula damageFormula;
|
||||
Formula rangeFormula;
|
||||
Formula fireRateFormula;
|
||||
Formula scrapDropFormula;
|
||||
};
|
||||
|
||||
struct StationsConfig
|
||||
{
|
||||
HqConfig hq;
|
||||
PlayerStationConfig playerStation;
|
||||
EnemyStationConfig enemyStation;
|
||||
};
|
||||
172
src/lib/config/SurfaceMask.cpp
Normal file
172
src/lib/config/SurfaceMask.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#include "SurfaceMask.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Rotate direction character 90° clockwise.
|
||||
char rotateDirCharCW(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '>': return 'v';
|
||||
case 'v': return '<';
|
||||
case '<': return '^';
|
||||
case '^': return '>';
|
||||
default: return c;
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate the character grid 90° clockwise.
|
||||
// Input grid[row][col]. Returns a new grid with swapped dimensions.
|
||||
std::vector<std::string> rotateCW(const std::vector<std::string>& grid)
|
||||
{
|
||||
if (grid.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const int srcH = static_cast<int>(grid.size());
|
||||
// Pad all rows to the same width.
|
||||
int srcW = 0;
|
||||
for (const std::string& row : grid)
|
||||
{
|
||||
const int w = static_cast<int>(row.size());
|
||||
if (w > srcW)
|
||||
{
|
||||
srcW = w;
|
||||
}
|
||||
}
|
||||
|
||||
// After 90° CW: new width = srcH, new height = srcW.
|
||||
const int dstW = srcH;
|
||||
const int dstH = srcW;
|
||||
std::vector<std::string> dst(dstH, std::string(dstW, ' '));
|
||||
|
||||
for (int row = 0; row < srcH; ++row)
|
||||
{
|
||||
for (int col = 0; col < srcW; ++col)
|
||||
{
|
||||
const char ch = (col < static_cast<int>(grid[row].size()))
|
||||
? grid[row][col]
|
||||
: ' ';
|
||||
// 90° CW mapping: (col, row) -> (dstCol = srcH-1-row, dstRow = col)
|
||||
const int dstCol = srcH - 1 - row;
|
||||
const int dstRow = col;
|
||||
dst[dstRow][dstCol] = rotateDirCharCW(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ParsedSurfaceMask parseSurfaceMask(const std::vector<std::string>& rows,
|
||||
Rotation rotation)
|
||||
{
|
||||
// Number of 90° CW steps to apply.
|
||||
int cwSteps = 0;
|
||||
switch (rotation)
|
||||
{
|
||||
case Rotation::East: cwSteps = 0; break;
|
||||
case Rotation::South: cwSteps = 1; break;
|
||||
case Rotation::West: cwSteps = 2; break;
|
||||
case Rotation::North: cwSteps = 3; break;
|
||||
}
|
||||
|
||||
// Apply rotations.
|
||||
std::vector<std::string> grid = rows;
|
||||
for (int i = 0; i < cwSteps; ++i)
|
||||
{
|
||||
grid = rotateCW(grid);
|
||||
}
|
||||
|
||||
// Scan grid: collect body cells, ship dock cells, and output port indicators.
|
||||
std::vector<QPoint> rawBodyCells;
|
||||
std::vector<QPoint> rawShipDockCells;
|
||||
struct RawPort
|
||||
{
|
||||
QPoint tile;
|
||||
Rotation direction;
|
||||
};
|
||||
std::vector<RawPort> rawPorts;
|
||||
|
||||
for (int row = 0; row < static_cast<int>(grid.size()); ++row)
|
||||
{
|
||||
for (int col = 0; col < static_cast<int>(grid[row].size()); ++col)
|
||||
{
|
||||
const char ch = grid[row][col];
|
||||
if (ch == 'A')
|
||||
{
|
||||
rawBodyCells.push_back(QPoint(col, row));
|
||||
}
|
||||
else if (ch == 'S')
|
||||
{
|
||||
rawBodyCells.push_back(QPoint(col, row));
|
||||
rawShipDockCells.push_back(QPoint(col, row));
|
||||
}
|
||||
else if (ch == '>')
|
||||
{
|
||||
rawPorts.push_back({QPoint(col, row), Rotation::East});
|
||||
}
|
||||
else if (ch == '<')
|
||||
{
|
||||
rawPorts.push_back({QPoint(col, row), Rotation::West});
|
||||
}
|
||||
else if (ch == '^')
|
||||
{
|
||||
rawPorts.push_back({QPoint(col, row), Rotation::North});
|
||||
}
|
||||
else if (ch == 'v')
|
||||
{
|
||||
rawPorts.push_back({QPoint(col, row), Rotation::South});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute bounding box of body cells and normalize to (0,0).
|
||||
int minCol = INT_MAX;
|
||||
int minRow = INT_MAX;
|
||||
int maxCol = INT_MIN;
|
||||
int maxRow = INT_MIN;
|
||||
|
||||
for (const QPoint& pt : rawBodyCells)
|
||||
{
|
||||
if (pt.x() < minCol) { minCol = pt.x(); }
|
||||
if (pt.x() > maxCol) { maxCol = pt.x(); }
|
||||
if (pt.y() < minRow) { minRow = pt.y(); }
|
||||
if (pt.y() > maxRow) { maxRow = pt.y(); }
|
||||
}
|
||||
|
||||
// If there are no body cells, return an empty mask.
|
||||
if (rawBodyCells.empty())
|
||||
{
|
||||
return ParsedSurfaceMask{};
|
||||
}
|
||||
|
||||
const QPoint offset(-minCol, -minRow);
|
||||
|
||||
ParsedSurfaceMask result;
|
||||
result.footprint = QSize(maxCol - minCol + 1, maxRow - minRow + 1);
|
||||
|
||||
for (const QPoint& pt : rawBodyCells)
|
||||
{
|
||||
result.bodyCells.push_back(pt + offset);
|
||||
}
|
||||
for (const QPoint& pt : rawShipDockCells)
|
||||
{
|
||||
result.shipDockCells.push_back(pt + offset);
|
||||
}
|
||||
for (const RawPort& rp : rawPorts)
|
||||
{
|
||||
Port port;
|
||||
port.tile = rp.tile + offset;
|
||||
port.direction = rp.direction;
|
||||
result.outputPorts.push_back(port);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
28
src/lib/config/SurfaceMask.h
Normal file
28
src/lib/config/SurfaceMask.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
|
||||
#include "Port.h"
|
||||
#include "Rotation.h"
|
||||
|
||||
// Parsed representation of a building's surface_mask after applying rotation.
|
||||
// All coordinates are relative to the building's anchor tile (the point passed
|
||||
// to BuildingSystem::place), which corresponds to (0,0) in this system.
|
||||
struct ParsedSurfaceMask
|
||||
{
|
||||
QSize footprint; // bounding box of body cells (A + S tiles)
|
||||
std::vector<QPoint> bodyCells; // relative positions of A and S tiles
|
||||
std::vector<Port> outputPorts; // port.tile = cell adjacent to body, outside footprint;
|
||||
// port.direction = flow direction away from building
|
||||
std::vector<QPoint> shipDockCells; // relative positions of S tiles (subset of bodyCells)
|
||||
};
|
||||
|
||||
// Parse a surface_mask definition (as loaded from TOML) and apply the given
|
||||
// rotation. The canonical mask orientation corresponds to Rotation::East.
|
||||
// Rotations are applied clockwise (East=0, South=90°, West=180°, North=270°).
|
||||
ParsedSurfaceMask parseSurfaceMask(const std::vector<std::string>& rows,
|
||||
Rotation rotation);
|
||||
50
src/lib/config/WorldConfig.h
Normal file
50
src/lib/config/WorldConfig.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "Formula.h"
|
||||
|
||||
// Region widths are in tiles (REQ-GW-REGIONS).
|
||||
struct WorldRegions
|
||||
{
|
||||
int asteroidWidth;
|
||||
int playerBufferWidth;
|
||||
int contestZoneWidth;
|
||||
int enemyBufferWidth;
|
||||
};
|
||||
|
||||
// Asteroid expansion (REQ-EXP-UNLOCK, REQ-EXP-COST).
|
||||
struct WorldExpansion
|
||||
{
|
||||
int columnsPerExpansion;
|
||||
int costBuildingBlocks;
|
||||
};
|
||||
|
||||
// Push scaling (REQ-PSH-*).
|
||||
struct WorldPush
|
||||
{
|
||||
int pushExpandColumns;
|
||||
double scalingFactor;
|
||||
};
|
||||
|
||||
// Wave scheduling (REQ-WAV-*).
|
||||
struct WorldWaves
|
||||
{
|
||||
Formula threatRateFormula; // threat/s as a function of elapsed game-time seconds
|
||||
Formula shipLevelFormula; // enemy ship level as a function of elapsed game-time seconds
|
||||
double gapMinSeconds;
|
||||
double gapMaxSeconds;
|
||||
double spawnDurationSeconds;
|
||||
};
|
||||
|
||||
struct WorldConfig
|
||||
{
|
||||
int heightTiles; // REQ-GW-HEIGHT
|
||||
int refundPercentage; // REQ-BLD-DEMOLISH
|
||||
int startingBuildingBlocks; // REQ-HQ-STARTING-BLOCKS
|
||||
double scrapDespawnSeconds; // REQ-RES-SCRAP-DROP
|
||||
double beltSpeedTilesPerSecond; // REQ-GW-BELT-SPEED
|
||||
|
||||
WorldRegions regions;
|
||||
WorldExpansion expansion;
|
||||
WorldPush push;
|
||||
WorldWaves waves;
|
||||
};
|
||||
14
src/lib/core/BlueprintDropEvent.h
Normal file
14
src/lib/core/BlueprintDropEvent.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
// Emitted in tick step 9 (Deaths & loot) when a destroyed enemy-defence-station
|
||||
// set awards a blueprint (REQ-DEF-BLUEPRINT-DROP). The UI renders a toast
|
||||
// (REQ-UI-BLUEPRINT-TOAST); wasNewUnlock chooses between the "unlocked" and
|
||||
// "level -> N" wording.
|
||||
struct BlueprintDropEvent
|
||||
{
|
||||
std::string blueprintId; // matches ShipDef::id in the config.
|
||||
int newLevel;
|
||||
bool wasNewUnlock;
|
||||
};
|
||||
36
src/lib/core/BuildingType.cpp
Normal file
36
src/lib/core/BuildingType.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "BuildingType.h"
|
||||
|
||||
std::optional<BuildingType> parseBuildingType(const std::string& id)
|
||||
{
|
||||
if (id == "miner") { return BuildingType::Miner; }
|
||||
if (id == "smelter") { return BuildingType::Smelter; }
|
||||
if (id == "assembler") { return BuildingType::Assembler; }
|
||||
if (id == "reprocessing_plant") { return BuildingType::ReprocessingPlant; }
|
||||
if (id == "shipyard") { return BuildingType::Shipyard; }
|
||||
if (id == "salvage_bay") { return BuildingType::SalvageBay; }
|
||||
if (id == "belt") { return BuildingType::Belt; }
|
||||
if (id == "splitter") { return BuildingType::Splitter; }
|
||||
if (id == "hq") { return BuildingType::Hq; }
|
||||
if (id == "player_defence_station") { return BuildingType::PlayerDefenceStation; }
|
||||
if (id == "enemy_defence_station") { return BuildingType::EnemyDefenceStation; }
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string buildingTypeId(BuildingType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case BuildingType::Miner: return "miner";
|
||||
case BuildingType::Smelter: return "smelter";
|
||||
case BuildingType::Assembler: return "assembler";
|
||||
case BuildingType::ReprocessingPlant: return "reprocessing_plant";
|
||||
case BuildingType::Shipyard: return "shipyard";
|
||||
case BuildingType::SalvageBay: return "salvage_bay";
|
||||
case BuildingType::Belt: return "belt";
|
||||
case BuildingType::Splitter: return "splitter";
|
||||
case BuildingType::Hq: return "hq";
|
||||
case BuildingType::PlayerDefenceStation: return "player_defence_station";
|
||||
case BuildingType::EnemyDefenceStation: return "enemy_defence_station";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
29
src/lib/core/BuildingType.h
Normal file
29
src/lib/core/BuildingType.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
// All building types defined in requirements.md. Belts and splitters share the
|
||||
// enum for cost/construction/placement/visuals lookup, but their runtime data
|
||||
// lives in the belt subsystem rather than in Building instances.
|
||||
enum class BuildingType
|
||||
{
|
||||
Miner,
|
||||
Smelter,
|
||||
Assembler,
|
||||
ReprocessingPlant,
|
||||
Shipyard,
|
||||
SalvageBay,
|
||||
Belt,
|
||||
Splitter,
|
||||
Hq,
|
||||
PlayerDefenceStation,
|
||||
EnemyDefenceStation,
|
||||
};
|
||||
|
||||
// Maps a config id string (e.g. "miner", "reprocessing_plant") to a
|
||||
// BuildingType. Returns std::nullopt for unknown ids.
|
||||
std::optional<BuildingType> parseBuildingType(const std::string& id);
|
||||
|
||||
// Canonical id string for a BuildingType. The inverse of parseBuildingType.
|
||||
std::string buildingTypeId(BuildingType type);
|
||||
26
src/lib/core/CMakeLists.txt
Normal file
26
src/lib/core/CMakeLists.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
SET(HDRS
|
||||
${HDRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Tick.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/EntityId.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Rotation.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildingType.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ItemType.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Item.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Port.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/MovementIntent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/FireEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintDropEvent.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
SET(SRCS
|
||||
${SRCS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildingType.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(LIB_INCLUDE_PATH
|
||||
${LIB_INCLUDE_PATH}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
9
src/lib/core/EntityId.h
Normal file
9
src/lib/core/EntityId.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
// Canonical reference to every targetable entity in the simulation: ships,
|
||||
// scrap drops, and buildings (including HQ and defence stations).
|
||||
// Ids are allocated centrally by the Simulation, strictly increasing, never
|
||||
// reused. 0 is reserved as the invalid id.
|
||||
using EntityId = long long;
|
||||
|
||||
constexpr EntityId kInvalidEntityId = 0;
|
||||
14
src/lib/core/FireEvent.h
Normal file
14
src/lib/core/FireEvent.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "EntityId.h"
|
||||
#include "Tick.h"
|
||||
|
||||
// Transient record emitted each time a weapon fires (REQ-SHP-FIRING,
|
||||
// REQ-SHP-FIRING-BEAM). Buffered in a sim-owned queue and drained by the
|
||||
// renderer each frame to draw the 0.3-second laser beam.
|
||||
struct FireEvent
|
||||
{
|
||||
EntityId shooter;
|
||||
EntityId target;
|
||||
Tick emittedAt;
|
||||
};
|
||||
10
src/lib/core/Item.h
Normal file
10
src/lib/core/Item.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "ItemType.h"
|
||||
|
||||
// Items on belts have no persistent identity across ticks; see
|
||||
// architecture.md "Belt Subsystem".
|
||||
struct Item
|
||||
{
|
||||
ItemType type;
|
||||
};
|
||||
29
src/lib/core/ItemType.h
Normal file
29
src/lib/core/ItemType.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
// Opaque tagged id of every transportable material (ores, ingots,
|
||||
// intermediates, building_blocks, scrap). Defined in config; the simulation
|
||||
// does not enumerate item types.
|
||||
//
|
||||
// Wrapped in a struct (rather than a bare std::string typedef) so that function
|
||||
// signatures make the semantic intent explicit.
|
||||
struct ItemType
|
||||
{
|
||||
std::string id;
|
||||
};
|
||||
|
||||
inline bool operator==(const ItemType& a, const ItemType& b)
|
||||
{
|
||||
return a.id == b.id;
|
||||
}
|
||||
|
||||
inline bool operator!=(const ItemType& a, const ItemType& b)
|
||||
{
|
||||
return a.id != b.id;
|
||||
}
|
||||
|
||||
inline bool operator<(const ItemType& a, const ItemType& b)
|
||||
{
|
||||
return a.id < b.id;
|
||||
}
|
||||
12
src/lib/core/MovementIntent.h
Normal file
12
src/lib/core/MovementIntent.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <QVector2D>
|
||||
|
||||
// A ship-behavior system writes this each tick before movement runs; the
|
||||
// highest-priority write wins. Priority order is fixed globally — see
|
||||
// architecture.md "Movement Arbitration".
|
||||
struct MovementIntent
|
||||
{
|
||||
int priority;
|
||||
QVector2D target;
|
||||
};
|
||||
13
src/lib/core/Port.h
Normal file
13
src/lib/core/Port.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <QPoint>
|
||||
|
||||
#include "Rotation.h"
|
||||
|
||||
// Identifies a belt-adjacent cell and the direction of flow across it. Used by
|
||||
// the belt subsystem's push/pull interface.
|
||||
struct Port
|
||||
{
|
||||
QPoint tile;
|
||||
Rotation direction;
|
||||
};
|
||||
12
src/lib/core/Rotation.h
Normal file
12
src/lib/core/Rotation.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
// Rotation applied to a building's surface_mask when placed. Also the direction
|
||||
// of an output port or belt flow.
|
||||
// North = -Y (up), East = +X (right), South = +Y (down), West = -X (left).
|
||||
enum class Rotation
|
||||
{
|
||||
North,
|
||||
East,
|
||||
South,
|
||||
West,
|
||||
};
|
||||
23
src/lib/core/Tick.h
Normal file
23
src/lib/core/Tick.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
// Discrete simulation time, measured in ticks since t=0.
|
||||
using Tick = long long;
|
||||
|
||||
// Fixed simulation tick rate. See architecture.md "Fixed-Timestep Tick-Based Simulation".
|
||||
constexpr int kTickRateHz = 30;
|
||||
constexpr double kTickDurationMs = 1000.0 / kTickRateHz;
|
||||
constexpr double kTickDurationSeconds = 1.0 / kTickRateHz;
|
||||
|
||||
// Converts a wall-clock duration (in seconds, as it appears in config TOML) to
|
||||
// an integer tick count. Rounds to nearest to avoid systematic drift from
|
||||
// repeated conversions.
|
||||
constexpr Tick secondsToTicks(double seconds)
|
||||
{
|
||||
return static_cast<Tick>(seconds * kTickRateHz + 0.5);
|
||||
}
|
||||
|
||||
// Inverse of secondsToTicks; useful for logging and UI display.
|
||||
constexpr double ticksToSeconds(Tick ticks)
|
||||
{
|
||||
return static_cast<double>(ticks) / kTickRateHz;
|
||||
}
|
||||
398
src/lib/sim/BeltSystem.cpp
Normal file
398
src/lib/sim/BeltSystem.cpp
Normal file
@@ -0,0 +1,398 @@
|
||||
#include "BeltSystem.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Tick.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
std::pair<int, int> BeltSystem::key(QPoint tile)
|
||||
{
|
||||
return {tile.x(), tile.y()};
|
||||
}
|
||||
|
||||
QPoint BeltSystem::adjacentTile(QPoint tile, Rotation dir)
|
||||
{
|
||||
switch (dir)
|
||||
{
|
||||
case Rotation::North: return {tile.x(), tile.y() - 1};
|
||||
case Rotation::East: return {tile.x() + 1, tile.y() };
|
||||
case Rotation::South: return {tile.x(), tile.y() + 1};
|
||||
case Rotation::West: return {tile.x() - 1, tile.y() };
|
||||
}
|
||||
return tile;
|
||||
}
|
||||
|
||||
QPointF BeltSystem::slotWorldPos(QPoint tile, Rotation dir, double progress)
|
||||
{
|
||||
// Map progress [0, 1] along the belt direction to a fractional tile-unit position.
|
||||
// Progress 0 = entered from opposite side; 1 = at output edge.
|
||||
double baseX = tile.x() + 0.5;
|
||||
double baseY = tile.y() + 0.5;
|
||||
|
||||
switch (dir)
|
||||
{
|
||||
case Rotation::North: return {baseX, baseY - (progress - 0.5)};
|
||||
case Rotation::East: return {baseX + (progress - 0.5), baseY};
|
||||
case Rotation::South: return {baseX, baseY + (progress - 0.5)};
|
||||
case Rotation::West: return {baseX - (progress - 0.5), baseY};
|
||||
}
|
||||
return {baseX, baseY};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Construction / placement
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
BeltSystem::BeltSystem(double beltSpeedTilesPerSecond)
|
||||
: m_progressPerTick(beltSpeedTilesPerSecond * kTickDurationSeconds)
|
||||
{
|
||||
}
|
||||
|
||||
void BeltSystem::placeBelt(QPoint tile, Rotation direction)
|
||||
{
|
||||
m_splitters.erase(key(tile));
|
||||
BeltTile bt;
|
||||
bt.direction = direction;
|
||||
m_belts[key(tile)] = bt;
|
||||
}
|
||||
|
||||
void BeltSystem::placeSplitter(QPoint tile, Rotation outputA, Rotation outputB)
|
||||
{
|
||||
m_belts.erase(key(tile));
|
||||
SplitterTile st;
|
||||
st.outputA = outputA;
|
||||
st.outputB = outputB;
|
||||
st.nextOutputIsA = true;
|
||||
m_splitters[key(tile)] = st;
|
||||
}
|
||||
|
||||
void BeltSystem::removeTile(QPoint tile)
|
||||
{
|
||||
m_belts.erase(key(tile));
|
||||
m_splitters.erase(key(tile));
|
||||
}
|
||||
|
||||
void BeltSystem::setSplitterFilters(QPoint tile,
|
||||
const std::vector<ItemType>& filterA,
|
||||
const std::vector<ItemType>& filterB)
|
||||
{
|
||||
const std::map<std::pair<int, int>, SplitterTile>::iterator it = m_splitters.find(key(tile));
|
||||
if (it == m_splitters.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
it->second.filterA = filterA;
|
||||
it->second.filterB = filterB;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Port interface
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool BeltSystem::tryPutItem(Port port, Item item)
|
||||
{
|
||||
const std::map<std::pair<int, int>, BeltTile>::iterator it = m_belts.find(key(port.tile));
|
||||
if (it == m_belts.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (it->second.direction != port.direction)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return tryPlaceOnBelt(port.tile, item);
|
||||
}
|
||||
|
||||
std::optional<Item> BeltSystem::tryTakeItem(Port port)
|
||||
{
|
||||
const std::map<std::pair<int, int>, BeltTile>::iterator it = m_belts.find(key(port.tile));
|
||||
if (it == m_belts.end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
if (it->second.direction != port.direction)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
BeltTile& bt = it->second;
|
||||
if (bt.front)
|
||||
{
|
||||
const Item taken = bt.front->item;
|
||||
bt.front = bt.back;
|
||||
bt.back = std::nullopt;
|
||||
return taken;
|
||||
}
|
||||
if (bt.back)
|
||||
{
|
||||
const Item taken = bt.back->item;
|
||||
bt.back = std::nullopt;
|
||||
return taken;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<ItemType> BeltSystem::peekItem(Port port) const
|
||||
{
|
||||
const std::map<std::pair<int, int>, BeltTile>::const_iterator it =
|
||||
m_belts.find(key(port.tile));
|
||||
if (it == m_belts.end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
if (it->second.direction != port.direction)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const BeltTile& bt = it->second;
|
||||
if (bt.front)
|
||||
{
|
||||
return bt.front->item.type;
|
||||
}
|
||||
if (bt.back)
|
||||
{
|
||||
return bt.back->item.type;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Maintenance
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void BeltSystem::clearTiles(const std::vector<QPoint>& tiles)
|
||||
{
|
||||
for (const QPoint& tile : tiles)
|
||||
{
|
||||
const std::map<std::pair<int, int>, BeltTile>::iterator bIt = m_belts.find(key(tile));
|
||||
if (bIt != m_belts.end())
|
||||
{
|
||||
bIt->second.front = std::nullopt;
|
||||
bIt->second.back = std::nullopt;
|
||||
}
|
||||
|
||||
const std::map<std::pair<int, int>, SplitterTile>::iterator sIt = m_splitters.find(key(tile));
|
||||
if (sIt != m_splitters.end())
|
||||
{
|
||||
sIt->second.heldItem = std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tick
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void BeltSystem::tick()
|
||||
{
|
||||
advanceProgress();
|
||||
moveItemsToNextTile();
|
||||
routeSplitterItems();
|
||||
}
|
||||
|
||||
void BeltSystem::advanceProgress()
|
||||
{
|
||||
for (std::map<std::pair<int, int>, BeltTile>::iterator it = m_belts.begin();
|
||||
it != m_belts.end(); ++it)
|
||||
{
|
||||
BeltTile& bt = it->second;
|
||||
|
||||
if (bt.front)
|
||||
{
|
||||
bt.front->progress += m_progressPerTick;
|
||||
if (bt.front->progress > 1.0)
|
||||
{
|
||||
bt.front->progress = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (bt.back)
|
||||
{
|
||||
bt.back->progress += m_progressPerTick;
|
||||
|
||||
// Back must not overtake front.
|
||||
if (bt.front && bt.back->progress >= bt.front->progress)
|
||||
{
|
||||
bt.back->progress = bt.front->progress - m_progressPerTick;
|
||||
if (bt.back->progress < 0.0)
|
||||
{
|
||||
bt.back->progress = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (bt.back->progress > 1.0)
|
||||
{
|
||||
bt.back->progress = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BeltSystem::moveItemsToNextTile()
|
||||
{
|
||||
for (std::map<std::pair<int, int>, BeltTile>::iterator it = m_belts.begin();
|
||||
it != m_belts.end(); ++it)
|
||||
{
|
||||
BeltTile& bt = it->second;
|
||||
if (!bt.front || bt.front->progress < 1.0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const QPoint here = QPoint(it->first.first, it->first.second);
|
||||
const QPoint next = adjacentTile(here, bt.direction);
|
||||
|
||||
const std::map<std::pair<int, int>, BeltTile>::iterator nextBelt = m_belts.find(key(next));
|
||||
const std::map<std::pair<int, int>, SplitterTile>::iterator nextSplitter = m_splitters.find(key(next));
|
||||
|
||||
if (nextBelt != m_belts.end())
|
||||
{
|
||||
if (tryPlaceOnBelt(next, bt.front->item))
|
||||
{
|
||||
bt.front = bt.back;
|
||||
bt.back = std::nullopt;
|
||||
}
|
||||
// else: next belt is full — item stays blocked at progress 1.0.
|
||||
}
|
||||
else if (nextSplitter != m_splitters.end())
|
||||
{
|
||||
if (!nextSplitter->second.heldItem)
|
||||
{
|
||||
nextSplitter->second.heldItem = bt.front->item;
|
||||
bt.front = bt.back;
|
||||
bt.back = std::nullopt;
|
||||
}
|
||||
// else: splitter busy — item stays blocked at progress 1.0.
|
||||
}
|
||||
// else: no tile registered (e.g. open space, or building input port).
|
||||
// Items leaving into unregistered tiles are not consumed here — the
|
||||
// building pull step uses tryTakeItem for that.
|
||||
}
|
||||
}
|
||||
|
||||
void BeltSystem::routeSplitterItems()
|
||||
{
|
||||
for (std::map<std::pair<int, int>, SplitterTile>::iterator it = m_splitters.begin();
|
||||
it != m_splitters.end(); ++it)
|
||||
{
|
||||
SplitterTile& st = it->second;
|
||||
if (!st.heldItem)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const Item& item = *st.heldItem;
|
||||
|
||||
const bool matchesA = st.filterA.empty() ||
|
||||
std::find(st.filterA.begin(), st.filterA.end(), item.type) != st.filterA.end();
|
||||
const bool matchesB = st.filterB.empty() ||
|
||||
std::find(st.filterB.begin(), st.filterB.end(), item.type) != st.filterB.end();
|
||||
|
||||
if (matchesA && !matchesB)
|
||||
{
|
||||
const QPoint dest = adjacentTile(QPoint(it->first.first, it->first.second), st.outputA);
|
||||
if (tryPlaceOnBelt(dest, item))
|
||||
{
|
||||
st.heldItem = std::nullopt;
|
||||
}
|
||||
}
|
||||
else if (matchesB && !matchesA)
|
||||
{
|
||||
const QPoint dest = adjacentTile(QPoint(it->first.first, it->first.second), st.outputB);
|
||||
if (tryPlaceOnBelt(dest, item))
|
||||
{
|
||||
st.heldItem = std::nullopt;
|
||||
}
|
||||
}
|
||||
else if (matchesA && matchesB)
|
||||
{
|
||||
// Alternation: try preferred output first, fall back to other.
|
||||
const Rotation preferred = st.nextOutputIsA ? st.outputA : st.outputB;
|
||||
const Rotation fallback = st.nextOutputIsA ? st.outputB : st.outputA;
|
||||
|
||||
const QPoint prefDest = adjacentTile(QPoint(it->first.first, it->first.second), preferred);
|
||||
const QPoint fbDest = adjacentTile(QPoint(it->first.first, it->first.second), fallback);
|
||||
|
||||
if (tryPlaceOnBelt(prefDest, item))
|
||||
{
|
||||
st.heldItem = std::nullopt;
|
||||
st.nextOutputIsA = !st.nextOutputIsA;
|
||||
}
|
||||
else if (tryPlaceOnBelt(fbDest, item))
|
||||
{
|
||||
st.heldItem = std::nullopt;
|
||||
// nextOutputIsA stays: preferred was blocked, so we still owe it next.
|
||||
}
|
||||
// else both blocked — item stays.
|
||||
}
|
||||
// else (!matchesA && !matchesB): stall — item stays in splitter.
|
||||
}
|
||||
}
|
||||
|
||||
bool BeltSystem::tryPlaceOnBelt(QPoint tile, Item item)
|
||||
{
|
||||
const std::map<std::pair<int, int>, BeltTile>::iterator it = m_belts.find(key(tile));
|
||||
if (it == m_belts.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BeltTile& bt = it->second;
|
||||
|
||||
if (!bt.front)
|
||||
{
|
||||
bt.front = BeltItemSlot{item, 0.0};
|
||||
return true;
|
||||
}
|
||||
if (!bt.back)
|
||||
{
|
||||
bt.back = BeltItemSlot{item, 0.0};
|
||||
|
||||
// Ensure ordering invariant: front has higher progress.
|
||||
if (bt.back->progress > bt.front->progress)
|
||||
{
|
||||
std::swap(bt.front, bt.back);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false; // both slots occupied
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Rendering
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void BeltSystem::forEachVisualItem(QRect viewportTiles,
|
||||
std::function<void(VisualItem)> visit) const
|
||||
{
|
||||
for (const std::pair<const std::pair<int, int>, BeltTile>& entry : m_belts)
|
||||
{
|
||||
const QPoint tile(entry.first.first, entry.first.second);
|
||||
if (!viewportTiles.contains(tile))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const BeltTile& bt = entry.second;
|
||||
|
||||
if (bt.front)
|
||||
{
|
||||
VisualItem vi;
|
||||
vi.type = bt.front->item.type;
|
||||
vi.worldPos = slotWorldPos(tile, bt.direction, bt.front->progress);
|
||||
visit(vi);
|
||||
}
|
||||
|
||||
if (bt.back)
|
||||
{
|
||||
VisualItem vi;
|
||||
vi.type = bt.back->item.type;
|
||||
vi.worldPos = slotWorldPos(tile, bt.direction, bt.back->progress);
|
||||
visit(vi);
|
||||
}
|
||||
}
|
||||
}
|
||||
121
src/lib/sim/BeltSystem.h
Normal file
121
src/lib/sim/BeltSystem.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <QPoint>
|
||||
#include <QPointF>
|
||||
#include <QRect>
|
||||
|
||||
#include "Item.h"
|
||||
#include "ItemType.h"
|
||||
#include "Port.h"
|
||||
#include "Rotation.h"
|
||||
|
||||
// Carries item type and fractional world position for the renderer.
|
||||
// worldPos is in tile units (1 tile = 1.0 unit); origin matches tile coords.
|
||||
struct VisualItem
|
||||
{
|
||||
ItemType type;
|
||||
QPointF worldPos;
|
||||
};
|
||||
|
||||
// Isolated belt-and-splitter transport layer. See architecture.md §Belt Subsystem.
|
||||
//
|
||||
// Buildings interact only through tryPutItem / tryTakeItem.
|
||||
// Rendering reads only through forEachVisualItem.
|
||||
// No other system inspects tile contents.
|
||||
class BeltSystem
|
||||
{
|
||||
public:
|
||||
explicit BeltSystem(double beltSpeedTilesPerSecond);
|
||||
|
||||
// -- Placement -----------------------------------------------------------
|
||||
// Register a new belt tile. Any items already on this tile are cleared.
|
||||
void placeBelt(QPoint tile, Rotation direction);
|
||||
|
||||
// Register a new splitter tile. outputA and outputB are the two exit
|
||||
// directions (e.g. West and East for a default-rotation splitter).
|
||||
// Items entering from any adjacent belt whose direction points into this
|
||||
// tile are held and routed to one of the two outputs.
|
||||
void placeSplitter(QPoint tile, Rotation outputA, Rotation outputB);
|
||||
|
||||
// Remove a belt or splitter tile (on demolish). Items are discarded.
|
||||
void removeTile(QPoint tile);
|
||||
|
||||
// -- Splitter filter configuration (REQ-BLD-SPLITTER) -------------------
|
||||
// filterA / filterB: empty means "accept all".
|
||||
void setSplitterFilters(QPoint tile,
|
||||
const std::vector<ItemType>& filterA,
|
||||
const std::vector<ItemType>& filterB);
|
||||
|
||||
// -- Port interface (buildings <-> belts) --------------------------------
|
||||
// port.tile = the belt tile adjacent to the building
|
||||
// port.direction = direction items flow on that tile
|
||||
//
|
||||
// tryPutItem: place item onto port.tile entering from the opposite side.
|
||||
// Returns false if the tile is not a belt, direction mismatches, or tile full.
|
||||
bool tryPutItem(Port port, Item item);
|
||||
|
||||
// tryTakeItem: remove and return the leading item from port.tile.
|
||||
// Returns nullopt if tile is not a belt, direction mismatches, or tile empty.
|
||||
std::optional<Item> tryTakeItem(Port port);
|
||||
|
||||
// peekItem: return the type of the leading item without removing it.
|
||||
// Returns nullopt if tile is not a belt, direction mismatches, or tile empty.
|
||||
std::optional<ItemType> peekItem(Port port) const;
|
||||
|
||||
// -- Maintenance ---------------------------------------------------------
|
||||
void clearTiles(const std::vector<QPoint>& tiles); // REQ-UI-BELT-CLEAR
|
||||
void tick();
|
||||
|
||||
// -- Rendering -----------------------------------------------------------
|
||||
void forEachVisualItem(QRect viewportTiles,
|
||||
std::function<void(VisualItem)> visit) const;
|
||||
|
||||
private:
|
||||
void advanceProgress();
|
||||
void moveItemsToNextTile();
|
||||
void routeSplitterItems();
|
||||
|
||||
// Place item into back slot of an existing belt tile at progress 0.
|
||||
// Returns false if tile is not a belt or is full.
|
||||
bool tryPlaceOnBelt(QPoint tile, Item item);
|
||||
|
||||
static std::pair<int, int> key(QPoint tile);
|
||||
static QPoint adjacentTile(QPoint tile, Rotation dir);
|
||||
|
||||
// Returns the world-space centre of a slot given tile origin and progress.
|
||||
static QPointF slotWorldPos(QPoint tile, Rotation dir, double progress);
|
||||
|
||||
struct BeltItemSlot
|
||||
{
|
||||
Item item;
|
||||
double progress; // [0.0, 1.0]: 0 = just entered, 1 = at output edge
|
||||
};
|
||||
|
||||
struct BeltTile
|
||||
{
|
||||
Rotation direction;
|
||||
std::optional<BeltItemSlot> front; // higher progress; closer to output
|
||||
std::optional<BeltItemSlot> back; // lower progress; closer to input
|
||||
};
|
||||
|
||||
struct SplitterTile
|
||||
{
|
||||
Rotation outputA;
|
||||
Rotation outputB;
|
||||
std::vector<ItemType> filterA; // empty = accept all
|
||||
std::vector<ItemType> filterB;
|
||||
bool nextOutputIsA; // alternation state
|
||||
std::optional<Item> heldItem; // item buffered waiting to exit
|
||||
};
|
||||
|
||||
double m_progressPerTick; // beltSpeedTilesPerSecond / kTickRateHz
|
||||
|
||||
std::map<std::pair<int, int>, BeltTile> m_belts;
|
||||
std::map<std::pair<int, int>, SplitterTile> m_splitters;
|
||||
};
|
||||
76
src/lib/sim/Building.h
Normal file
76
src/lib/sim/Building.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
|
||||
#include "BuildingType.h"
|
||||
#include "EntityId.h"
|
||||
#include "Item.h"
|
||||
#include "ItemType.h"
|
||||
#include "Port.h"
|
||||
#include "Rotation.h"
|
||||
#include "Tick.h"
|
||||
|
||||
// Per-material input buffer for a production building.
|
||||
struct InputBuffer
|
||||
{
|
||||
std::map<ItemType, int> counts; // current item counts per material
|
||||
std::map<ItemType, int> caps; // max items per material (2× per-cycle requirement)
|
||||
};
|
||||
|
||||
// Output buffer shared by all output materials for a production building.
|
||||
struct OutputBuffer
|
||||
{
|
||||
std::vector<Item> items;
|
||||
int capacity = 0; // 2× per-cycle output; 1× for ReprocessingPlant
|
||||
};
|
||||
|
||||
// Active production cycle for a building.
|
||||
struct Production
|
||||
{
|
||||
std::string recipeId;
|
||||
Tick completesAt = 0;
|
||||
std::vector<Item> chosenOutputs; // resolved at cycle start (reprocessing rolls here)
|
||||
};
|
||||
|
||||
// A building placed on the map that is still under construction.
|
||||
// Occupies tiles but does not produce.
|
||||
struct ConstructionSite
|
||||
{
|
||||
EntityId id = kInvalidEntityId;
|
||||
QPoint anchor; // top-left of body bounding box
|
||||
QSize footprint;
|
||||
std::vector<QPoint> bodyCells; // absolute world tile coordinates
|
||||
Rotation rotation = Rotation::East;
|
||||
BuildingType type = BuildingType::Miner;
|
||||
std::string recipeId; // may be configured before completion
|
||||
Tick completesAt = 0; // 0 = queued but not yet started
|
||||
};
|
||||
|
||||
// A fully constructed, operational building.
|
||||
struct Building
|
||||
{
|
||||
EntityId id = kInvalidEntityId;
|
||||
QPoint anchor; // top-left of body bounding box
|
||||
QSize footprint;
|
||||
Rotation rotation = Rotation::East;
|
||||
BuildingType type = BuildingType::Miner;
|
||||
float hp = 0.0f;
|
||||
float maxHp = 0.0f;
|
||||
std::string recipeId; // empty = none selected
|
||||
|
||||
InputBuffer inputBuffer;
|
||||
OutputBuffer outputBuffer;
|
||||
std::optional<Production> production;
|
||||
|
||||
// Pre-computed from surface mask at placement; in absolute world coordinates.
|
||||
std::vector<QPoint> bodyCells;
|
||||
std::vector<Port> outputPorts;
|
||||
std::vector<Port> inputPorts; // perimeter tiles (minus output-port tiles),
|
||||
// direction pointing INTO building
|
||||
};
|
||||
661
src/lib/sim/BuildingSystem.cpp
Normal file
661
src/lib/sim/BuildingSystem.cpp
Normal file
@@ -0,0 +1,661 @@
|
||||
#include "BuildingSystem.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <random>
|
||||
#include <set>
|
||||
|
||||
#include "SurfaceMask.h"
|
||||
|
||||
BuildingSystem::BuildingSystem(const GameConfig& config,
|
||||
BeltSystem& belts,
|
||||
std::function<EntityId()> allocateId,
|
||||
std::function<void(int)> addBuildingBlocks,
|
||||
std::mt19937& rng)
|
||||
: m_config(config)
|
||||
, m_belts(belts)
|
||||
, m_allocateId(std::move(allocateId))
|
||||
, m_addBuildingBlocks(std::move(addBuildingBlocks))
|
||||
, m_rng(rng)
|
||||
{
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Private helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const BuildingDef* BuildingSystem::findBuildingDef(BuildingType type) const
|
||||
{
|
||||
for (const BuildingDef& def : m_config.buildings.buildings)
|
||||
{
|
||||
if (def.type == type)
|
||||
{
|
||||
return &def;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const RecipeDef* BuildingSystem::findRecipe(const std::string& id,
|
||||
BuildingType type) const
|
||||
{
|
||||
for (const RecipeDef& recipe : m_config.recipes.recipes)
|
||||
{
|
||||
if (recipe.id == id && recipe.building == type)
|
||||
{
|
||||
return &recipe;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BuildingSystem::initBuffers(Building& b, const RecipeDef& recipe) const
|
||||
{
|
||||
b.inputBuffer.counts.clear();
|
||||
b.inputBuffer.caps.clear();
|
||||
for (const RecipeIngredient& ing : recipe.inputs)
|
||||
{
|
||||
const ItemType type{ing.item};
|
||||
b.inputBuffer.counts[type] = 0;
|
||||
b.inputBuffer.caps[type] = 2 * ing.amount;
|
||||
}
|
||||
|
||||
b.outputBuffer.items.clear();
|
||||
if (b.type == BuildingType::ReprocessingPlant)
|
||||
{
|
||||
// 1× max-per-roll (REQ-MAT-OUTPUT-BUFFER-REPROCESSING).
|
||||
int maxAmount = 0;
|
||||
for (const RecipeOutput& out : recipe.outputs)
|
||||
{
|
||||
if (out.amount > maxAmount)
|
||||
{
|
||||
maxAmount = out.amount;
|
||||
}
|
||||
}
|
||||
b.outputBuffer.capacity = maxAmount;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 2× per-cycle output.
|
||||
int totalAmount = 0;
|
||||
for (const RecipeOutput& out : recipe.outputs)
|
||||
{
|
||||
totalAmount += out.amount;
|
||||
}
|
||||
b.outputBuffer.capacity = 2 * totalAmount;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Port> BuildingSystem::computeInputPorts(const Building& b) const
|
||||
{
|
||||
// Build lookup sets for quick membership checks.
|
||||
std::set<std::pair<int, int>> bodySet;
|
||||
for (const QPoint& cell : b.bodyCells)
|
||||
{
|
||||
bodySet.insert({cell.x(), cell.y()});
|
||||
}
|
||||
|
||||
std::set<std::pair<int, int>> outputPortTiles;
|
||||
for (const Port& port : b.outputPorts)
|
||||
{
|
||||
outputPortTiles.insert({port.tile.x(), port.tile.y()});
|
||||
}
|
||||
|
||||
// Neighbour deltas and the corresponding "inward" belt direction.
|
||||
const int dx[4] = {-1, 1, 0, 0};
|
||||
const int dy[4] = { 0, 0, -1, 1};
|
||||
const Rotation inward[4] = {
|
||||
Rotation::East, // neighbour is to the West; belt flows East toward building
|
||||
Rotation::West, // neighbour is to the East; belt flows West toward building
|
||||
Rotation::South, // neighbour is above (row-1); belt flows South toward building
|
||||
Rotation::North // neighbour is below (row+1); belt flows North toward building
|
||||
};
|
||||
|
||||
std::set<std::pair<int, int>> seen;
|
||||
std::vector<Port> inputPorts;
|
||||
|
||||
for (const QPoint& cell : b.bodyCells)
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
const int nx = cell.x() + dx[i];
|
||||
const int ny = cell.y() + dy[i];
|
||||
const std::pair<int, int> neighbor = {nx, ny};
|
||||
|
||||
if (bodySet.count(neighbor)) { continue; }
|
||||
if (outputPortTiles.count(neighbor)){ continue; }
|
||||
if (seen.count(neighbor)) { continue; }
|
||||
|
||||
seen.insert(neighbor);
|
||||
Port port;
|
||||
port.tile = QPoint(nx, ny);
|
||||
port.direction = inward[i];
|
||||
inputPorts.push_back(port);
|
||||
}
|
||||
}
|
||||
|
||||
return inputPorts;
|
||||
}
|
||||
|
||||
std::vector<Item> BuildingSystem::rollReprocessingOutput(const RecipeDef& recipe)
|
||||
{
|
||||
std::vector<double> weights;
|
||||
weights.reserve(recipe.outputs.size());
|
||||
for (const RecipeOutput& out : recipe.outputs)
|
||||
{
|
||||
weights.push_back(out.probability.value_or(1.0));
|
||||
}
|
||||
|
||||
std::discrete_distribution<int> dist(weights.begin(), weights.end());
|
||||
const int idx = dist(m_rng);
|
||||
|
||||
const RecipeOutput& chosen = recipe.outputs[static_cast<std::size_t>(idx)];
|
||||
std::vector<Item> result;
|
||||
Item item;
|
||||
item.type.id = chosen.item;
|
||||
for (int i = 0; i < chosen.amount; ++i)
|
||||
{
|
||||
result.push_back(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Placement
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
EntityId BuildingSystem::place(BuildingType type, QPoint anchor,
|
||||
Rotation rotation, Tick currentTick)
|
||||
{
|
||||
const EntityId id = m_allocateId();
|
||||
|
||||
if (type == BuildingType::Belt)
|
||||
{
|
||||
m_belts.placeBelt(anchor, rotation);
|
||||
m_tileOccupancy[{anchor.x(), anchor.y()}] = id;
|
||||
m_beltEntities[id] = BeltEntry{anchor, BuildingType::Belt};
|
||||
return id;
|
||||
}
|
||||
|
||||
if (type == BuildingType::Splitter)
|
||||
{
|
||||
const BuildingDef* def = findBuildingDef(type);
|
||||
assert(def != nullptr);
|
||||
const ParsedSurfaceMask mask = parseSurfaceMask(def->surfaceMask, rotation);
|
||||
assert(mask.outputPorts.size() >= 2);
|
||||
const Rotation outA = mask.outputPorts[0].direction;
|
||||
const Rotation outB = mask.outputPorts[1].direction;
|
||||
m_belts.placeSplitter(anchor, outA, outB);
|
||||
m_tileOccupancy[{anchor.x(), anchor.y()}] = id;
|
||||
m_beltEntities[id] = BeltEntry{anchor, BuildingType::Splitter};
|
||||
return id;
|
||||
}
|
||||
|
||||
const BuildingDef* def = findBuildingDef(type);
|
||||
assert(def != nullptr);
|
||||
const ParsedSurfaceMask mask = parseSurfaceMask(def->surfaceMask, rotation);
|
||||
|
||||
// Record tile occupancy for body cells.
|
||||
for (const QPoint& cell : mask.bodyCells)
|
||||
{
|
||||
const QPoint absCell = anchor + cell;
|
||||
m_tileOccupancy[{absCell.x(), absCell.y()}] = id;
|
||||
}
|
||||
|
||||
// Build construction site.
|
||||
ConstructionSite site;
|
||||
site.id = id;
|
||||
site.anchor = anchor;
|
||||
site.footprint = mask.footprint;
|
||||
site.rotation = rotation;
|
||||
site.type = type;
|
||||
for (const QPoint& cell : mask.bodyCells)
|
||||
{
|
||||
site.bodyCells.push_back(anchor + cell);
|
||||
}
|
||||
|
||||
if (m_constructionQueue.empty())
|
||||
{
|
||||
site.completesAt = currentTick + secondsToTicks(def->constructionTimeSeconds);
|
||||
}
|
||||
// else: completesAt remains 0 (queued, not yet started).
|
||||
|
||||
m_constructionQueue.push_back(std::move(site));
|
||||
return id;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Demolish
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
int BuildingSystem::demolish(EntityId id)
|
||||
{
|
||||
// Belt / splitter?
|
||||
const std::map<EntityId, BeltEntry>::iterator beltIt = m_beltEntities.find(id);
|
||||
if (beltIt != m_beltEntities.end())
|
||||
{
|
||||
const QPoint tile = beltIt->second.tile;
|
||||
const BuildingType btype = beltIt->second.type;
|
||||
m_belts.removeTile(tile);
|
||||
m_tileOccupancy.erase({tile.x(), tile.y()});
|
||||
m_beltEntities.erase(beltIt);
|
||||
|
||||
const BuildingDef* def = findBuildingDef(btype);
|
||||
if (def)
|
||||
{
|
||||
return def->cost * m_config.world.refundPercentage / 100;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Construction queue?
|
||||
for (std::deque<ConstructionSite>::iterator it = m_constructionQueue.begin();
|
||||
it != m_constructionQueue.end();
|
||||
++it)
|
||||
{
|
||||
if (it->id == id)
|
||||
{
|
||||
const BuildingDef* def = findBuildingDef(it->type);
|
||||
for (const QPoint& cell : it->bodyCells)
|
||||
{
|
||||
m_tileOccupancy.erase({cell.x(), cell.y()});
|
||||
}
|
||||
m_constructionQueue.erase(it);
|
||||
if (def)
|
||||
{
|
||||
return def->cost * m_config.world.refundPercentage / 100;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Operational building?
|
||||
for (std::vector<Building>::iterator it = m_buildings.begin();
|
||||
it != m_buildings.end();
|
||||
++it)
|
||||
{
|
||||
if (it->id == id)
|
||||
{
|
||||
const BuildingDef* def = findBuildingDef(it->type);
|
||||
for (const QPoint& cell : it->bodyCells)
|
||||
{
|
||||
m_tileOccupancy.erase({cell.x(), cell.y()});
|
||||
}
|
||||
m_buildings.erase(it);
|
||||
if (def)
|
||||
{
|
||||
return def->cost * m_config.world.refundPercentage / 100;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Set recipe
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void BuildingSystem::setRecipe(EntityId id, const std::string& recipeId)
|
||||
{
|
||||
// Construction site: store recipe for when building completes.
|
||||
for (ConstructionSite& site : m_constructionQueue)
|
||||
{
|
||||
if (site.id == id)
|
||||
{
|
||||
site.recipeId = recipeId;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Operational building: clear buffers and re-init.
|
||||
for (Building& building : m_buildings)
|
||||
{
|
||||
if (building.id == id)
|
||||
{
|
||||
building.recipeId = recipeId;
|
||||
building.inputBuffer.counts.clear();
|
||||
building.inputBuffer.caps.clear();
|
||||
building.outputBuffer.items.clear();
|
||||
building.outputBuffer.capacity = 0;
|
||||
building.production = std::nullopt;
|
||||
|
||||
if (!recipeId.empty())
|
||||
{
|
||||
const RecipeDef* recipe = findRecipe(recipeId, building.type);
|
||||
if (recipe)
|
||||
{
|
||||
initBuffers(building, *recipe);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tick hooks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void BuildingSystem::tickConstruction(Tick currentTick)
|
||||
{
|
||||
if (m_constructionQueue.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ConstructionSite& front = m_constructionQueue.front();
|
||||
|
||||
// Guard: if somehow the front site was never started, start it now.
|
||||
if (front.completesAt == 0)
|
||||
{
|
||||
const BuildingDef* def = findBuildingDef(front.type);
|
||||
if (def)
|
||||
{
|
||||
front.completesAt = currentTick + secondsToTicks(def->constructionTimeSeconds);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentTick < front.completesAt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Promote construction site to operational building.
|
||||
const BuildingDef* def = findBuildingDef(front.type);
|
||||
const ParsedSurfaceMask mask = parseSurfaceMask(
|
||||
def ? def->surfaceMask : std::vector<std::string>{},
|
||||
front.rotation);
|
||||
|
||||
Building building;
|
||||
building.id = front.id;
|
||||
building.anchor = front.anchor;
|
||||
building.footprint = front.footprint;
|
||||
building.rotation = front.rotation;
|
||||
building.type = front.type;
|
||||
building.hp = 100.0f;
|
||||
building.maxHp = 100.0f;
|
||||
building.recipeId = front.recipeId;
|
||||
|
||||
for (const QPoint& cell : mask.bodyCells)
|
||||
{
|
||||
building.bodyCells.push_back(front.anchor + cell);
|
||||
}
|
||||
for (const Port& port : mask.outputPorts)
|
||||
{
|
||||
Port absPort;
|
||||
absPort.tile = front.anchor + port.tile;
|
||||
absPort.direction = port.direction;
|
||||
building.outputPorts.push_back(absPort);
|
||||
}
|
||||
building.inputPorts = computeInputPorts(building);
|
||||
|
||||
if (!building.recipeId.empty())
|
||||
{
|
||||
const RecipeDef* recipe = findRecipe(building.recipeId, building.type);
|
||||
if (recipe)
|
||||
{
|
||||
initBuffers(building, *recipe);
|
||||
}
|
||||
}
|
||||
|
||||
m_buildings.push_back(std::move(building));
|
||||
m_constructionQueue.pop_front();
|
||||
|
||||
// Start next queued site if present.
|
||||
if (!m_constructionQueue.empty() && m_constructionQueue.front().completesAt == 0)
|
||||
{
|
||||
const BuildingDef* nextDef = findBuildingDef(m_constructionQueue.front().type);
|
||||
if (nextDef)
|
||||
{
|
||||
m_constructionQueue.front().completesAt =
|
||||
currentTick + secondsToTicks(nextDef->constructionTimeSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuildingSystem::tickBeltPull()
|
||||
{
|
||||
for (Building& building : m_buildings)
|
||||
{
|
||||
// HQ: pull building_block items and add to global stock.
|
||||
if (building.type == BuildingType::Hq)
|
||||
{
|
||||
for (const Port& port : building.inputPorts)
|
||||
{
|
||||
const std::optional<ItemType> peeked = m_belts.peekItem(port);
|
||||
if (peeked && peeked->id == "building_block")
|
||||
{
|
||||
const std::optional<Item> taken = m_belts.tryTakeItem(port);
|
||||
if (taken)
|
||||
{
|
||||
m_addBuildingBlocks(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (building.recipeId.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const RecipeDef* recipe = findRecipe(building.recipeId, building.type);
|
||||
if (!recipe || recipe->inputs.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const Port& port : building.inputPorts)
|
||||
{
|
||||
const std::optional<ItemType> peeked = m_belts.peekItem(port);
|
||||
if (!peeked)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const ItemType& type = *peeked;
|
||||
|
||||
// Accept only if this type is a required input and buffer has space.
|
||||
const std::map<ItemType, int>::const_iterator capIt =
|
||||
building.inputBuffer.caps.find(type);
|
||||
if (capIt == building.inputBuffer.caps.end() || capIt->second == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const int current = [&]() -> int
|
||||
{
|
||||
const std::map<ItemType, int>::const_iterator it =
|
||||
building.inputBuffer.counts.find(type);
|
||||
return (it != building.inputBuffer.counts.end()) ? it->second : 0;
|
||||
}();
|
||||
|
||||
if (current >= capIt->second)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::optional<Item> taken = m_belts.tryTakeItem(port);
|
||||
if (taken)
|
||||
{
|
||||
building.inputBuffer.counts[taken->type]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuildingSystem::tickProduction(Tick currentTick)
|
||||
{
|
||||
for (Building& building : m_buildings)
|
||||
{
|
||||
// Skip types without a recipe-based production loop.
|
||||
if (building.type == BuildingType::Belt ||
|
||||
building.type == BuildingType::Splitter ||
|
||||
building.type == BuildingType::Shipyard ||
|
||||
building.type == BuildingType::SalvageBay ||
|
||||
building.type == BuildingType::Hq ||
|
||||
building.type == BuildingType::PlayerDefenceStation ||
|
||||
building.type == BuildingType::EnemyDefenceStation)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (building.recipeId.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const RecipeDef* recipe = findRecipe(building.recipeId, building.type);
|
||||
if (!recipe)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If a production cycle is active, check for completion.
|
||||
if (building.production)
|
||||
{
|
||||
if (currentTick >= building.production->completesAt)
|
||||
{
|
||||
for (const Item& item : building.production->chosenOutputs)
|
||||
{
|
||||
building.outputBuffer.items.push_back(item);
|
||||
}
|
||||
building.production = std::nullopt;
|
||||
}
|
||||
// Whether we just completed or are still running, do not start
|
||||
// another cycle in the same tick.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Idle: check if a new cycle can start.
|
||||
|
||||
// 1. All required inputs present?
|
||||
bool inputsOk = true;
|
||||
for (const RecipeIngredient& ing : recipe->inputs)
|
||||
{
|
||||
const ItemType type{ing.item};
|
||||
const std::map<ItemType, int>::const_iterator it =
|
||||
building.inputBuffer.counts.find(type);
|
||||
const int have = (it != building.inputBuffer.counts.end()) ? it->second : 0;
|
||||
if (have < ing.amount)
|
||||
{
|
||||
inputsOk = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inputsOk)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2. Determine chosen outputs (roll for reprocessing).
|
||||
std::vector<Item> chosen;
|
||||
if (building.type == BuildingType::ReprocessingPlant)
|
||||
{
|
||||
chosen = rollReprocessingOutput(*recipe);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const RecipeOutput& out : recipe->outputs)
|
||||
{
|
||||
Item item;
|
||||
item.type.id = out.item;
|
||||
for (int i = 0; i < out.amount; ++i)
|
||||
{
|
||||
chosen.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Output buffer has space for chosen outputs?
|
||||
const int newSize = static_cast<int>(building.outputBuffer.items.size())
|
||||
+ static_cast<int>(chosen.size());
|
||||
if (newSize > building.outputBuffer.capacity)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 4. Consume inputs and start cycle.
|
||||
for (const RecipeIngredient& ing : recipe->inputs)
|
||||
{
|
||||
building.inputBuffer.counts[ItemType{ing.item}] -= ing.amount;
|
||||
}
|
||||
|
||||
Production prod;
|
||||
prod.recipeId = building.recipeId;
|
||||
prod.completesAt = currentTick + secondsToTicks(recipe->durationSeconds);
|
||||
prod.chosenOutputs = std::move(chosen);
|
||||
building.production = std::move(prod);
|
||||
}
|
||||
}
|
||||
|
||||
void BuildingSystem::tickBeltPush()
|
||||
{
|
||||
for (Building& building : m_buildings)
|
||||
{
|
||||
if (building.outputBuffer.items.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const Port& outputPort : building.outputPorts)
|
||||
{
|
||||
if (building.outputBuffer.items.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
const Item item = building.outputBuffer.items.front();
|
||||
if (m_belts.tryPutItem(outputPort, item))
|
||||
{
|
||||
building.outputBuffer.items.erase(building.outputBuffer.items.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Queries
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Building* BuildingSystem::findBuilding(EntityId id) const
|
||||
{
|
||||
for (const Building& building : m_buildings)
|
||||
{
|
||||
if (building.id == id)
|
||||
{
|
||||
return &building;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ConstructionSite* BuildingSystem::findSite(EntityId id) const
|
||||
{
|
||||
for (const ConstructionSite& site : m_constructionQueue)
|
||||
{
|
||||
if (site.id == id)
|
||||
{
|
||||
return &site;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<Building> BuildingSystem::allBuildings() const
|
||||
{
|
||||
return m_buildings;
|
||||
}
|
||||
|
||||
std::vector<ConstructionSite> BuildingSystem::allSites() const
|
||||
{
|
||||
return std::vector<ConstructionSite>(m_constructionQueue.begin(),
|
||||
m_constructionQueue.end());
|
||||
}
|
||||
|
||||
bool BuildingSystem::isTileOccupied(QPoint tile) const
|
||||
{
|
||||
return m_tileOccupancy.count({tile.x(), tile.y()}) > 0;
|
||||
}
|
||||
88
src/lib/sim/BuildingSystem.h
Normal file
88
src/lib/sim/BuildingSystem.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <QPoint>
|
||||
|
||||
#include "BeltSystem.h"
|
||||
#include "Building.h"
|
||||
#include "BuildingType.h"
|
||||
#include "EntityId.h"
|
||||
#include "GameConfig.h"
|
||||
#include "Rotation.h"
|
||||
#include "Tick.h"
|
||||
|
||||
// Manages building placement, construction queuing, and the per-tick
|
||||
// production loop (belt→building pull, production, building→belt push).
|
||||
// Belt and Splitter types are forwarded to BeltSystem rather than stored
|
||||
// as Building instances.
|
||||
class BuildingSystem
|
||||
{
|
||||
public:
|
||||
BuildingSystem(const GameConfig& config,
|
||||
BeltSystem& belts,
|
||||
std::function<EntityId()> allocateId,
|
||||
std::function<void(int)> addBuildingBlocks,
|
||||
std::mt19937& rng);
|
||||
|
||||
// -- Placement / demolish ------------------------------------------------
|
||||
// Returns the new entity id. Belt and Splitter register with BeltSystem
|
||||
// directly; other types enter the construction queue.
|
||||
EntityId place(BuildingType type, QPoint anchor, Rotation rotation,
|
||||
Tick currentTick);
|
||||
|
||||
// Remove a building or construction site by id. Returns the refund in
|
||||
// building blocks (floor(cost * refundPercentage / 100)). Returns 0 for
|
||||
// unknown ids.
|
||||
int demolish(EntityId id);
|
||||
|
||||
// Set the recipe (or blueprint id for shipyard) on a building or queued
|
||||
// construction site. Clears both buffers on an operational building.
|
||||
void setRecipe(EntityId id, const std::string& recipeId);
|
||||
|
||||
// -- Tick hooks (called from Simulation::tick in the documented order) ---
|
||||
void tickConstruction(Tick currentTick);
|
||||
void tickBeltPull();
|
||||
void tickProduction(Tick currentTick);
|
||||
void tickBeltPush();
|
||||
|
||||
// -- Queries -------------------------------------------------------------
|
||||
const Building* findBuilding(EntityId id) const;
|
||||
const ConstructionSite* findSite(EntityId id) const;
|
||||
std::vector<Building> allBuildings() const;
|
||||
std::vector<ConstructionSite> allSites() const;
|
||||
bool isTileOccupied(QPoint tile) const;
|
||||
|
||||
private:
|
||||
struct BeltEntry
|
||||
{
|
||||
QPoint tile;
|
||||
BuildingType type; // Belt or Splitter
|
||||
};
|
||||
|
||||
const BuildingDef* findBuildingDef(BuildingType type) const;
|
||||
const RecipeDef* findRecipe(const std::string& id, BuildingType type) const;
|
||||
void initBuffers(Building& b, const RecipeDef& recipe) const;
|
||||
std::vector<Port> computeInputPorts(const Building& b) const;
|
||||
std::vector<Item> rollReprocessingOutput(const RecipeDef& recipe);
|
||||
|
||||
const GameConfig& m_config;
|
||||
BeltSystem& m_belts;
|
||||
std::function<EntityId()> m_allocateId;
|
||||
std::function<void(int)> m_addBuildingBlocks;
|
||||
std::mt19937& m_rng;
|
||||
|
||||
std::vector<Building> m_buildings;
|
||||
std::deque<ConstructionSite> m_constructionQueue;
|
||||
std::map<EntityId, BeltEntry> m_beltEntities;
|
||||
|
||||
// Maps every occupied body-cell coordinate to the entity that owns it.
|
||||
std::map<std::pair<int, int>, EntityId> m_tileOccupancy;
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user