Compare commits

...

325 Commits

Author SHA1 Message Date
David Given
dcae381973 Add some more GUI for the disk exerciser. 2025-10-24 01:00:11 +02:00
David Given
2142bc7cce Add a Disk menu. 2025-10-23 01:12:40 +02:00
David Given
ae3f82264a Add the boilerplate for the exerciser. 2025-10-22 01:13:36 +02:00
David Given
710e83c098 Try to fix OSX build failure. 2025-10-18 13:03:14 +02:00
David Given
4f46fff3be Remove a bunch of extraneous Providers. 2025-10-18 01:03:39 +02:00
David Given
58ea21a9a2 Add a menu option to allow resetting the workspace. 2025-10-18 00:36:52 +02:00
David Given
0fd1aa82a6 Crudely bodge image writes into working. 2025-10-17 23:41:16 +02:00
David Given
5b7f9d84f9 Fix some rendering issues. 2025-10-16 22:32:20 +02:00
David Given
4b7e8e74a7 Fix NPE. 2025-10-16 22:32:02 +02:00
David Given
5375c72d02 Correctly set the rotational period on Disks even on non-hardware readers. 2025-10-16 21:34:40 +02:00
David Given
5c257be164 Add a message if there's no flux data to draw. 2025-10-16 21:15:38 +02:00
David Given
7fa17322dc Remember to debounce index marks. 2025-10-16 21:00:57 +02:00
David Given
ed3640d945 Add a first draft visualiser. 2025-10-16 00:52:54 +02:00
David Given
87ce3ad61d Fluxmaps can now be queried for a (cached) list of index marks. Tracks
now contain both the raw list of sectors and a deduplicated list,
suitable for the visualiser.
2025-10-16 00:52:37 +02:00
David Given
6d75feb0ce Increase the default number of revolutions to 2.5 to ensure we get at
least one complete revolution.
2025-10-16 00:51:51 +02:00
David Given
168b8b6f6c Turn optimisation back on _again_. 2025-10-15 14:11:30 +02:00
David Given
3d063e932a Rereading disks through the GUI now works. 2025-10-15 00:41:49 +02:00
David Given
157ec569b2 Some more renaming. 2025-10-14 23:16:56 +02:00
David Given
f63c8dadf1 Lots more renaming. 2025-10-14 22:53:26 +02:00
David Given
d17f6116f0 Lots of symbol renaming. 2025-10-14 22:32:42 +02:00
David Given
2d6cb22e3a Looks like we're going to have to rework the reader/writer/source/sink
interfaces, so do fluxsink. This lets us test for overwriting a flux
file on writing in the GUI. HG: Enter commit message.
2025-10-14 21:54:59 +02:00
David Given
2de8b52e56 Fix the Micropolis options. 2025-10-14 00:18:27 +02:00
David Given
171576e538 Rename some stuff. 2025-10-14 00:14:11 +02:00
David Given
2db9f65e8b Move the flux file button into the config area. 2025-10-14 00:06:56 +02:00
David Given
2572b64bd1 You can now load images. 2025-10-14 00:03:44 +02:00
David Given
533aaf85f2 Add a status line. 2025-10-13 23:27:31 +02:00
David Given
f67ddc1f77 Writes now work. 2025-10-13 22:24:00 +02:00
David Given
b1d64f3683 Try and fix the Linux build. 2025-10-13 00:39:26 +02:00
David Given
7e8840e03f Add a rotational speed global option. 2025-10-13 00:38:10 +02:00
David Given
b003297b22 Remove the partially-finished MemoryFluxSink. 2025-10-13 00:33:55 +02:00
David Given
7341cec2c4 Add missing file. 2025-10-13 00:30:24 +02:00
David Given
a98b7f72fd Rearrange some UI. 2025-10-13 00:29:55 +02:00
David Given
2e97579394 Fix optimisation. 2025-10-13 00:20:16 +02:00
David Given
f960c7efd0 Added functionality for faking the necessary data in a DecodedDisk to
make the visualiser work. Blank images can now be created in memory.
2025-10-13 00:19:59 +02:00
David Given
c2e7f32cba Fix various issues to do with shared state and occasional crashes. 2025-10-12 15:51:33 +02:00
David Given
137528fc53 Format. 2025-10-12 15:51:02 +02:00
David Given
cbf4cc35fb Fix some default setting issues. 2025-10-12 15:50:50 +02:00
David Given
cd7b3de1b3 Finally add support for setting the default option. 2025-10-12 15:50:33 +02:00
David Given
fddc2270e5 Ensure that the layout's sector size is honoured. 2025-10-11 18:48:31 +02:00
David Given
2a96d9bd78 Some cleanup. 2025-10-11 18:48:14 +02:00
David Given
fd554f0808 Update imhex module. 2025-10-11 12:25:04 +02:00
David Given
6776c51b23 Add read/write indicators to the summary view. Fix a pile of minor bugs. 2025-10-11 00:49:59 +02:00
David Given
ef58295304 Better and more consistent exception handling. 2025-10-10 21:49:43 +02:00
David Given
2e2c3e3e34 Set a best-guess physical location on missing sectors. 2025-10-10 21:49:14 +02:00
David Given
e87bb44a2d Another OSX fix. 2025-10-10 20:53:11 +02:00
David Given
0ba0a9cce5 Tweak to try and make OSX happy. 2025-10-10 00:54:22 +02:00
David Given
97bb563ba0 Another massive overhaul to rip out the last remaining bits of Layout. 2025-10-10 00:21:47 +02:00
David Given
8f047f842e Massive overhaul to use the new disklayout stuff while
encoding/decoding. Fix lots of bugs, be more consistent with logical and
physical locations.
2025-10-08 22:41:13 +02:00
David Given
9d596ef530 Rename many things for clarity. 2025-10-06 23:29:20 +02:00
David Given
580ffa8cf7 Rename flux.h. 2025-10-06 23:11:50 +02:00
David Given
341e0a320d Rename the stuff in flux.h to actually make sense. 2025-10-06 23:09:26 +02:00
David Given
cff0a9703c Rework the flux data structures to be a bit more sensibly designed and
more amenable to copying.
2025-10-06 22:58:24 +02:00
David Given
38618532c4 Change sandboxing settings again. 2025-10-05 21:17:16 +02:00
David Given
6026dcd86d Try and fix sandboxing. 2025-10-05 21:02:17 +02:00
David Given
3949971546 Add a log viewer view. 2025-10-05 19:55:16 +02:00
David Given
6146f442fb Fix a bunch of minor UI issues. 2025-10-05 13:18:47 +02:00
David Given
7090c1bfdf Rework the way jobs are run so that everything happens in one callback,
which means thrown exceptions propagate properly and will cancel ongoing
jobs. Also, the state machine is much cleaner.
2025-10-05 01:35:16 +02:00
David Given
563babc969 Disable the config settings when the worker thread is busy. 2025-10-03 22:45:19 +02:00
David Given
b649c2b9af Tweak the way the debug menu works. 2025-10-03 22:34:55 +02:00
David Given
f7f887789c Override the debug and feedback menu URLs. 2025-10-03 22:27:44 +02:00
David Given
a8fcdcc528 Add a custom default layout. 2025-10-03 22:27:21 +02:00
David Given
a988578cc7 Oops, turn the sandbox back on! 2025-10-03 22:27:02 +02:00
David Given
ee585b24f0 Hopefully now almost correctly build the OSX package. 2025-10-03 20:49:54 +02:00
David Given
3d6e980990 Remove debugging. 2025-10-03 19:47:00 +02:00
David Given
f5d19416a9 Try something else for OSX. 2025-10-03 02:58:43 +02:00
David Given
4187fa5a09 Adjust Windows dependencies. 2025-10-03 02:55:48 +02:00
David Given
eb7613c03f Debugging. 2025-10-03 02:27:08 +02:00
David Given
7910429037 Debugging. 2025-10-03 02:24:28 +02:00
David Given
cd1cc736a7 Make the new gui canonical. 2025-10-03 02:24:17 +02:00
David Given
e6d6805f25 Switch from pkg-config to pkgconf. 2025-10-03 01:52:58 +02:00
David Given
9733879360 Debugging. 2025-10-03 01:48:48 +02:00
David Given
725712f796 I think boost needs to be linked. 2025-10-02 22:53:11 +02:00
David Given
2122cea5c4 More missing dependencies. 2025-10-02 22:37:13 +02:00
David Given
5466e716a9 Finally figure out the macos dependency problem. 2025-10-02 22:15:28 +02:00
David Given
0dc0e3d9a1 Debugging. 2025-10-02 21:51:08 +02:00
David Given
4bb12b2caa Debugging. 2025-10-02 21:43:48 +02:00
David Given
0d9c5f5150 Try updating homebrew before building. 2025-10-02 21:31:32 +02:00
David Given
4030031a2c Update OSX dependencies. 2025-10-02 21:22:00 +02:00
David Given
3143c87f1c Adjust dependencies... again. 2025-10-02 20:05:26 +02:00
David Given
f16f02c4c7 Adjust dependencies. 2025-10-02 19:57:29 +02:00
David Given
3e13b2461d Adjust dependencies. 2025-10-02 19:52:26 +02:00
David Given
5fd0d1589e Update msys before use. 2025-10-02 19:46:58 +02:00
David Given
23e6d234d0 Change the way we install msys to see if that helps. 2025-10-02 19:44:27 +02:00
David Given
cf2a97f8aa Update splash screen to contain the imhex logo. 2025-10-02 00:23:45 +02:00
David Given
5a815e0cd6 Extend DiskLayout to contain the sector offset and block number data.
Update the gui to use it.
2025-10-01 23:33:21 +02:00
David Given
06a3af2a1d Add filesystem sector offsets to the disk layout structure. 2025-10-01 00:34:09 +02:00
David Given
0558d95fa3 Attach the current layout to DiskFlux objects. 2025-10-01 00:26:19 +02:00
David Given
81f9246ab8 Rework summaryview to use the new DiskLayout. 2025-09-30 00:26:22 +02:00
David Given
6979567429 Add basic tests for DiskLayout. 2025-09-29 20:54:05 +02:00
David Given
348de4165d Initial version of the new DiskLayout class. 2025-09-25 00:04:51 +02:00
David Given
0755d420dd Change gitmodules to use my own fork of imhex. 2025-09-24 23:51:17 +02:00
David Given
dead21bce5 Typo fix. 2025-09-22 18:27:25 +02:00
David Given
4cf451ce60 We can't update msys because it kills the terminal. 2025-09-22 18:26:15 +02:00
David Given
72298ac805 Force msys update before installing dependencies. 2025-09-22 18:22:21 +02:00
David Given
3d1ad81652 Don't try and materialise the symbolic link; it breaks things. 2025-09-21 21:20:40 +02:00
David Given
88c79169b6 Don't log the environment any more. 2025-09-21 20:02:43 +02:00
David Given
d9747b9021 Don't build macos 13 any more. 2025-09-21 19:53:08 +02:00
David Given
256976a5a1 Adjust dependencies. 2025-09-21 19:52:20 +02:00
David Given
0ba4b82e10 Only require libtre on Windows. 2025-09-21 19:49:46 +02:00
David Given
ffd9e28b42 Try and build the Windows package. 2025-09-21 19:49:32 +02:00
David Given
9c919c786d Tweak dependencies. 2025-09-21 19:54:04 +02:00
David Given
47a9a56959 Remember to set -static for source file compilation, not just linking. Use the correct
protoc on mingw.
2025-09-21 17:49:17 +02:00
David Given
6e03bc604a Turns out undefined format_type and drive_type fields are normal. 2025-09-21 17:07:54 +02:00
David Given
feea6a027a Add another ignored variable. 2025-09-21 17:07:21 +02:00
David Given
08fa06b7fe Enable static builds on Windows. 2025-09-21 03:40:25 +02:00
David Given
8a976edef9 Warning fix. 2025-09-20 23:19:22 +02:00
David Given
c71d8d6c74 Another typo fix. 2025-09-20 20:59:39 +02:00
David Given
e809af7426 Typo fix. 2025-09-20 20:54:55 +02:00
David Given
ab05db9040 Switch back to gcc. 2025-09-20 20:49:18 +02:00
David Given
04f916741e Try big-obj instead. 2025-09-20 20:26:42 +02:00
David Given
f6224f3718 Try mcmodel=medium. 2025-09-20 20:09:07 +02:00
David Given
10185bb7a1 Do I need -fPIC for Windows? 2025-09-20 17:36:37 +02:00
David Given
d565960c70 Flag adjustment. 2025-09-20 16:58:24 +02:00
David Given
c21073294f More adjustments. 2025-09-20 16:13:32 +02:00
David Given
3cd95de434 Try using clang instead of gcc. 2025-09-20 16:00:38 +02:00
David Given
6552dba9aa Even more dependencies. 2025-09-20 13:00:15 +02:00
David Given
c8ebe55aa9 Warning fix. 2025-09-20 13:00:04 +02:00
David Given
1eefa2d604 Even even more dependencies. 2025-09-20 12:28:53 +02:00
David Given
a359394eea Even more dependencies. 2025-09-20 12:25:56 +02:00
David Given
9f13026bec Even more dependency changes. 2025-09-20 12:23:14 +02:00
David Given
8fcc99b2a1 More dependencies. 2025-09-20 12:20:26 +02:00
David Given
125a0536ff More dependency tweaks. 2025-09-20 12:17:53 +02:00
David Given
4115947d80 Don't use /dev/stderr as it makes msys sad. 2025-09-20 12:17:09 +02:00
David Given
2f1dcd7c9a More build tweak. 2025-09-20 12:11:34 +02:00
David Given
5e00ffca13 Remove some more WSL stuff. 2025-09-20 12:07:44 +02:00
David Given
ac27095493 Don't need this any more. 2025-09-20 11:54:28 +02:00
David Given
e27ca5cd4c Reformat. 2025-09-20 12:00:52 +02:00
David Given
cc72ac6327 Ignore some more variables. 2025-09-20 12:00:33 +02:00
David Given
5443aa6501 Some work towards making things build on Windows. 2025-09-20 03:24:49 +02:00
David Given
902bf32169 Allow bypassing the sandbox, as it makes msys sad. 2025-09-20 03:24:10 +02:00
David Given
d200633747 Fix windows weirdness. 2025-09-20 03:23:55 +02:00
David Given
a48b749c2e Support Windows protoc command syntax. 2025-09-20 01:22:24 +02:00
David Given
46fab84b95 Progress towards making everything build on Windows. 2025-09-19 02:02:34 +02:00
David Given
b0290f858c Tweak build system. 2025-09-18 22:28:03 +02:00
David Given
fe09c12cd6 Modernise protobuf usage. 2025-09-18 16:37:56 +02:00
David Given
b5ae5a1cea Don't try and use the dbus library on OSX. 2025-09-18 16:26:35 +02:00
David Given
113cb85512 Fix dependencies. 2025-09-18 14:47:43 +02:00
David Given
da276bcb3b Fix dependencies. 2025-09-18 14:45:34 +02:00
David Given
9a78d0f38c Fix OSX build script. 2025-09-18 14:40:23 +02:00
David Given
ec2e1666e7 Make build on Linux again. 2025-09-18 14:39:38 +02:00
David Given
478df40d4b Attempt to make build on OSX. 2025-09-18 00:56:53 +02:00
David Given
a8b9d79cb1 Header fix for OSX. 2025-09-18 00:55:17 +02:00
David Given
23865d1a10 Rip out a lot of imhex stuff we don't want. 2025-09-17 22:46:30 +02:00
David Given
458b3f24fe Make a basic splash screen. 2025-09-17 22:18:54 +02:00
David Given
86fa23e6fa Add missing files. 2025-09-17 21:09:21 +02:00
David Given
dd9d5aaed5 Remember to build all of libpl. 2025-09-17 21:08:50 +02:00
David Given
b22df17bb5 Fix some fmt strings. 2025-09-17 14:28:41 +02:00
David Given
b81e609e66 Hopefully fix the format_to ambiguity. 2025-09-17 14:11:17 +02:00
David Given
d41e57cba6 Fix format errors. 2025-09-17 01:07:37 +02:00
David Given
da7e83e257 Update dependencies. 2025-09-17 00:41:39 +02:00
David Given
83be12fcf1 Update dependencies. 2025-09-17 00:26:25 +02:00
David Given
a999e2d6c9 Adjust dependencies. 2025-09-17 00:14:58 +02:00
David Given
6d6251e757 Adjust dependencies. 2025-09-17 00:12:59 +02:00
David Given
be8b26ef94 Try updating Ubuntu to get a better compiler. 2025-09-17 00:09:40 +02:00
David Given
c6b8bce5d6 Reenable optimisation. 2025-09-17 00:08:29 +02:00
David Given
d8b3452c07 Update dependencies. 2025-09-17 00:05:09 +02:00
David Given
eddbd43cd9 Update dependencies. 2025-09-17 00:03:14 +02:00
David Given
168189180d Update dependencies. 2025-09-16 23:53:58 +02:00
David Given
9e092bab6a Nope, pkg-config for mbedtls doesn't work. 2025-09-16 23:47:28 +02:00
David Given
2c35126b3a mbedcrypto and mbedtls are linked on most systems, and the pkg-config
files seem bad.
2025-09-16 23:42:47 +02:00
David Given
7dc0e4ca31 Rearrange for consistency. 2025-09-16 22:55:58 +02:00
David Given
96257f89d5 Use a local lunasvg. 2025-09-16 22:40:46 +02:00
David Given
09919343b4 Import LunaSVG. 2025-09-16 22:33:20 +02:00
David Given
b070c1068c Add a mostly-functioning logical map in the summary view. 2025-09-16 22:30:53 +02:00
David Given
5628a576db Don't mix up physical and logical units when seeking to a track. 2025-09-16 22:30:09 +02:00
David Given
073c78e25f Be firmer about rebuilding the configuration. 2025-09-16 22:29:29 +02:00
David Given
6a826d6eb5 Move the controls into their own panel. 2025-09-16 00:42:02 +02:00
David Given
11a6143d4c Move all the settings out to a config view again, but designed
differently. Much better.
2025-09-15 22:49:30 +02:00
David Given
6127c9a46d Wire up some more control panel buttons. Create an in-memory sector
interface for doing filesystem stuff.
2025-09-15 20:06:16 +02:00
David Given
98f7febef7 Make two variations on the sector map; one for the physical view and one
for the logical view.
2025-09-13 22:54:48 +02:00
David Given
85afadacf0 Don't make images with holes --- ensure that all sectors in the image
are populated, even if with a stub 'MISSING' sector, with layout
information.
2025-09-13 01:55:01 +02:00
David Given
01cd812162 Use std::optional a bit more right. 2025-09-13 01:54:09 +02:00
David Given
39329acc77 Remove the obsolete configview. 2025-09-12 22:37:27 +02:00
David Given
bdc96038ef Get the format part of the new control panel working. 2025-09-12 22:31:50 +02:00
David Given
93760d989a Get the device config part of the new control panel working. 2025-09-12 22:14:13 +02:00
David Given
b306c7063b Start moving the config view functionality into the control panel. The
current config is now displayed there.
2025-09-12 00:20:46 +02:00
David Given
e3d7fa69d8 Add a --hd global option for setting high density. 2025-09-12 00:20:10 +02:00
David Given
f6c0e5405a Wire up some control panel buttons. Make toasts work. 2025-09-11 01:05:15 +02:00
David Given
fc12a2662c ImGui text entries are very sensitive and don't like skipping frames or
being disabled, so do it less often.
2025-09-10 23:52:23 +02:00
David Given
ab5b16488c The control panel mostly works. 2025-09-10 00:13:07 +02:00
David Given
4d5900268b Create a DiskProvider on startup so we go straight into the GUI. 2025-09-09 00:27:49 +02:00
David Given
b5c5a4335d Wire up the summary and sector map views; although it's shown that the
layout code really needs refactoring. Again.
2025-09-09 00:14:21 +02:00
David Given
e76235541a Add custom out-of-box and welcome screen implementations. 2025-09-08 21:11:26 +02:00
David Given
e75e1a6e27 Make the track/cylinder side/head terminology more consistent. 2025-09-08 20:08:23 +02:00
David Given
aa220ecbcb Sectors and Images now store more unseful information. The DiskProvider
now works.
2025-09-08 19:52:57 +02:00
David Given
edc8d74418 The sector map view is now mostly working. 2025-09-07 00:56:37 +02:00
David Given
2831aa09ae Add CylinderHeadSector in place of some of the 3-tuples I was previously
using; do some refactoring.
2025-09-07 00:56:18 +02:00
David Given
e1b4b0d3a3 Fix kryoflux reading and writing. 2025-09-07 00:55:49 +02:00
David Given
e5df6ca33b Remember to set the correct drive type for 80-track disks. 2025-09-07 00:31:52 +02:00
David Given
68c3cbb020 Sketch out the image view. 2025-09-06 13:22:40 +02:00
David Given
ca3c37d20a Add logic to Image for access filesystem blocks. 2025-09-06 13:22:29 +02:00
David Given
6fcd9233ea Disks can now be read, and incremental progress shows up in the summary view. 2025-09-06 01:21:46 +02:00
David Given
3761c4b1e2 Broadcast a DiskReadLogMessage every time a track is read, with the
entire accumulated disk data (including all the resolved sectors). This
makes it much easier to show partial results in the GUI.
2025-09-06 01:21:07 +02:00
David Given
c89c53b1c7 Try displaying the track status. 2025-09-06 00:04:46 +02:00
David Given
be0f63a133 The summary view now shows a respectable track summary. 2025-09-05 23:14:12 +02:00
David Given
a8216995ad Merge. 2025-09-04 00:57:35 +02:00
David Given
995359ef45 Start preparing to do a read. 2025-09-04 00:55:50 +02:00
David Given
bc84e3c8a0 It doesn't look great, but I think the config view is mostly done. 2025-09-03 23:48:48 +02:00
David Given
af12a25a9d Update dependencies. 2025-09-03 14:47:24 +02:00
David Given
f6b2821221 Update dependencies. 2025-09-03 14:32:20 +02:00
David Given
458601a139 Update dependencies. 2025-09-03 14:29:39 +02:00
David Given
a89130edbd The config viewer now mostly works, and sets mostly correct
configurations in the settings.
2025-09-03 01:14:53 +02:00
David Given
c95cd8a4da Add hints for global options to tell the GUI where to show them. 2025-09-03 01:14:23 +02:00
David Given
4d313a8495 Add in our custom view. 2025-09-01 00:45:36 +02:00
David Given
263eef3442 Try and enable submodules on github. 2025-09-01 00:30:52 +02:00
David Given
2e97996211 Get a minimal FE plugin working. 2025-09-01 00:29:55 +02:00
David Given
7035b9c3c2 Rearrange and clean up. 2025-08-31 23:58:07 +02:00
David Given
5628d2ca06 We need a custom romfs generator. 2025-08-31 23:55:24 +02:00
David Given
61cf7fbccf Get a fully working static imhex. 2025-08-31 23:37:51 +02:00
David Given
ce347c6326 A fair number of assets now load. 2025-08-31 16:43:13 +02:00
David Given
94119b19fe Getting my head around the way imhex works. 2025-08-31 00:35:47 +02:00
David Given
9c7be1268f Build some more bits. 2025-08-29 21:18:14 +02:00
David Given
a9d59f67ba It finally builds! Although does nothing. 2025-08-29 20:37:39 +02:00
David Given
8d2a72228f A reasonable amount of the imhex framework now builds. 2025-08-29 00:35:00 +02:00
David Given
60b95dd3f3 Added throwing_ptr. 2025-08-28 23:41:51 +02:00
David Given
b1094f40dc Add libromfs. 2025-08-28 23:27:40 +02:00
David Given
e40ea80e34 Add xdgpp. 2025-08-28 22:34:27 +02:00
David Given
9e1222d38a Add Native File Dialog. 2025-08-28 22:20:31 +02:00
David Given
4446785729 Put pattern-language in the right place. 2025-08-28 21:40:09 +02:00
David Given
790f015d72 Add the pattern language. 2025-08-28 21:36:42 +02:00
David Given
ccb0dcea3c Finally remember to add the build file! 2025-08-28 21:35:59 +02:00
David Given
15a0632af0 Add imgui. 2025-08-28 21:30:21 +02:00
David Given
3c0da28947 Some more building. 2025-08-28 21:23:22 +02:00
David Given
95227f32ca Start putting together the imhex build. 2025-08-28 21:10:41 +02:00
David Given
edf75b5cda Add libwolv. 2025-08-28 21:09:27 +02:00
David Given
af87c48451 Add an imhex submodule. 2025-08-28 20:18:02 +02:00
David Given
7cde8e3aa6 Merge pull request #824 from davidgiven/ab
Update ab to the new ninja version.
2025-08-27 19:47:09 +01:00
David Given
34fe6f0a5f _Actually_ update ab. 2025-08-27 20:23:46 +02:00
David Given
76c9674f3f Update ab. 2025-08-27 20:21:29 +02:00
David Given
addbabd123 Disable make parallelism (although I'm not sure this will help). 2025-08-27 19:57:24 +02:00
David Given
46b90d9c36 Merge pull request #825 from boamaod/master
Correct Juku E5104 documentation
2025-08-27 13:44:49 +01:00
Märt Põder
7ee67082aa Fix ambigious description about "both sides" and update links 2025-08-27 13:22:09 +03:00
David Given
e8042ed5f3 Adjust parallelism settings again. 2025-08-27 11:40:34 +02:00
David Given
8828874c25 Don't run more than one ninja instance at a time. 2025-08-27 11:22:53 +02:00
David Given
1bdb093319 Update ab. 2025-08-27 02:10:08 +02:00
David Given
a1e2191ad5 mingw is less aggressive about dead code removal that other gccs are, so
we need to add dependencies in places where they're really not used.
2025-08-27 03:06:31 +02:00
David Given
e61fcf1d9b utils.shell now takes a command string rather than an argv list. 2025-08-27 02:28:29 +02:00
David Given
610ef0dc4b Remember to put set -e in front of command scripts. 2025-08-27 02:27:40 +02:00
David Given
273d38f237 Fix command detection when a command string contains multiple words. 2025-08-27 00:11:57 +02:00
David Given
8194a08382 Update ab. 2025-08-26 23:09:47 +02:00
David Given
6170b704b1 Fix escaping of $$ strings; then escape them again before passing to ninja! 2025-08-26 21:30:50 +02:00
David Given
b05f5e7caa Warning fix. 2025-08-26 14:29:27 +02:00
David Given
4b38fc6044 Update ab again. 2025-08-26 02:06:32 +02:00
David Given
cee16a75ca Fix Windows dependencies. 2025-08-26 01:37:28 +02:00
David Given
9fd85a8289 Add missing file. 2025-08-26 01:28:53 +02:00
David Given
2f1eff1474 Update documentation. 2025-08-26 01:27:33 +02:00
David Given
8c582b8d72 Update to the new ninja-fied ab. 2025-08-26 01:23:58 +02:00
David Given
e49673329d Merge pull request #823 from davidgiven/cleanup
Do some cleanup since the last changes.
2025-08-21 19:31:57 +02:00
David Given
07ebed83bf Fix some documentation from the global options change. 2025-08-21 19:14:26 +02:00
David Given
1def87fdc3 Remove the rawReadDiskCommand() function, as it's no longer used. 2025-08-21 19:00:29 +02:00
David Given
d91fed7dd4 Update documentation. 2025-08-21 18:59:02 +02:00
David Given
5f2f7e70ef Remove rawread and merge. 2025-08-21 18:55:46 +02:00
David Given
83432beff6 Merge pull request #822 from davidgiven/config
Overhaul the config file CLI.
2025-08-21 18:42:21 +02:00
David Given
979b550178 Looks like our string_view fix hasn't worked --- tweak. 2025-08-21 01:19:19 +02:00
David Given
9062a531f3 Migrate the 40track etc extension configs to actual options. Add the
ability to have --group=value options to make this cleaner.
2025-08-21 00:53:50 +02:00
David Given
e2a6fbcf3c Update a few places which used -c for other purposes. 2025-08-20 21:30:06 +02:00
David Given
ec16931f3a Update documentation for -c. 2025-08-20 21:23:02 +02:00
David Given
0ec0ca7495 Config files are now specified with -c, rather than via filename
arguments, because otherwise you get really unhelpful error messages
when you get things wrong.
2025-08-20 21:19:34 +02:00
David Given
51fa7c9371 Fix broken link.
Closes: #799
2025-08-20 00:23:21 +02:00
David Given
6c69f10fe7 Merge pull request #821 from davidgiven/protobuf
Expose the .app on OSX (in a zipfile).
2025-08-20 00:17:43 +02:00
David Given
206e85a356 Expose the .app on OSX (in a zipfile).
Closes: #800
2025-08-20 00:02:43 +02:00
David Given
8d7dd4867b Merge pull request #820 from davidgiven/protobuf
Apply the fix from #811 to make everything build against Protobuf 31.
2025-08-19 23:43:26 +02:00
David Given
d1524f78fb Apply the fix from #811 to make everything build against Protobuf 31. 2025-08-19 23:28:19 +02:00
David Given
b26735d520 Merge pull request #819 from davidgiven/fl2
Add some flux file manipulation tools.
2025-08-19 22:58:05 +02:00
David Given
603baee777 Fix a subtle bug that was causing misparsing of indexed fields on OSX. I hate
C++.
2025-08-19 22:43:45 +02:00
David Given
e105b7f498 Merge from master. 2025-08-19 20:13:41 +02:00
David Given
bb3fbccb50 Merge pull request #818 from davidgiven/convert
Add a fe-convert (plus all the necessary backend work).
2025-08-19 19:59:12 +02:00
David Given
c8edcd963d Merge pull request #817 from davidgiven/ab
Update ab.
2025-08-19 01:32:40 +02:00
David Given
3b60cdc707 Remove the -j from the build scripts for OSX. 2025-08-19 01:15:47 +02:00
David Given
ea061d65c9 Update ab. 2025-08-19 01:14:33 +02:00
David Given
da64c0237f Update documentation. 2025-08-19 01:11:40 +02:00
David Given
d2b1602881 Add a working fluxfile cp. 2025-08-19 00:55:53 +02:00
David Given
1afd45068c Merge. 2025-08-19 00:18:59 +02:00
David Given
f01b30e112 Make fluxfile rm work. 2025-08-19 00:18:47 +02:00
David Given
b5f7fbe14e Finally come up with a fluxfile ls I can live with. 2025-08-18 23:59:57 +02:00
David Given
8b6073ccbb Try making the error collector non-constexpr? 2025-08-18 22:14:48 +02:00
David Given
f902c759df Try the suggested workaround in lexy for older compilers. 2025-08-18 22:10:06 +02:00
David Given
996fdbc0f5 More overhauling of the proto layer; fluxfile ls now works. 2025-08-18 00:37:42 +02:00
David Given
9ff3e3b42a Finally make the getters and setters work with repeated fields. 2025-08-17 23:04:14 +02:00
David Given
0a5604521e Merge in fluxfile stuff. 2025-08-17 21:12:27 +02:00
David Given
786636ef5d Don't allow writing Apple 2 flux images to SCP files, because there
isn't space for the quarter-step tracks.
2025-08-17 11:42:34 +02:00
David Given
18bdb27225 fluxengine convert now uses the same syntax as the other tools. 2025-08-17 11:26:16 +02:00
David Given
faca35dec0 Update documentation. 2025-08-17 10:51:50 +02:00
David Given
f8813daae3 Attempt to make work on Windows. 2025-08-17 10:47:54 +02:00
David Given
da5a20390f Fix unhelpful message. 2025-08-17 10:40:34 +02:00
David Given
3ab3db92f5 Add basic support for TI-99 disks. 2025-08-17 10:40:07 +02:00
David Given
a3cd3dd9dc Adjust dependencies. 2025-08-17 09:45:54 +02:00
David Given
918868e9e8 Try updating the Ubuntu version. 2025-08-17 09:43:10 +02:00
David Given
cf05a25445 Does _error_collector need a constexpr constructor and destructor? 2025-08-17 01:01:32 +02:00
David Given
5d5399a267 Add another weirdly missing file. 2025-08-17 00:55:10 +02:00
David Given
2de7af0ba5 Add weirdly missing file. 2025-08-17 00:52:01 +02:00
David Given
0382c304ad Warning fix. 2025-08-17 00:46:50 +02:00
David Given
182d9946fe Add missing file. 2025-08-17 00:40:55 +02:00
David Given
f24e4029b4 Flux sources now add the locations of their data to _extraConfig ---
which is now honoured. Fix a bunch of bugs in some of the flux sources
and sinks. The converter now actually works, maybe.
2025-08-17 00:38:25 +02:00
David Given
4ebda29171 Rename track -> cylinder in lots of places. 2025-08-16 17:39:55 +02:00
David Given
53026f3d02 Rework the way locations are handled to use the new locations
microformat rather than the old RangeProto.
2025-08-16 16:59:44 +02:00
David Given
99c0e95a2f Added a routine for parsing location lists using Lexy. 2025-08-15 23:39:21 +02:00
David Given
dfa56c6b08 Raw import of Lexy. 2025-08-14 23:36:31 +02:00
David Given
0419df4b2d Another archival checkin... 2025-08-13 23:00:08 +02:00
David Given
70bdcd0978 Non-functioning archival checkin. 2025-08-12 20:31:54 +01:00
David Given
022df995aa Update for newer C++. 2025-08-11 16:21:03 +01:00
David Given
dcbe7ec41d Raw import of alphanum. 2025-08-11 16:14:27 +01:00
David Given
df4d27eefe Better support for repeated fields in the config language. Add a helper
for showing all config fields in a proto.
2025-08-10 22:22:58 +01:00
David Given
8f233f55e9 Add fluxfile ls. 2025-07-28 23:20:41 +01:00
David Given
7db49aec21 Merge pull request #814 from davidgiven/build
Update ab.
2025-07-28 13:36:21 +02:00
David Given
b5eaec0778 Try more Windows fix? 2025-07-28 12:23:41 +01:00
David Given
06b126a2e7 Typo fix. 2025-07-27 23:20:32 +01:00
David Given
ed96ebac79 Another Windows fix. 2025-07-27 23:08:37 +01:00
David Given
c6e34d2d88 Alternative Windows fix. 2025-07-27 22:50:43 +01:00
David Given
53ac8bad79 Hopefully fix Windows. 2025-07-27 21:43:26 +01:00
David Given
d2e163bc3b More Windows build debugging. 2025-07-27 21:33:52 +01:00
David Given
1404123281 Windows debugging. 2025-07-27 21:20:28 +01:00
David Given
01a7afd28a Merge from master. 2025-07-27 20:48:27 +01:00
David Given
3a42911e6f Update ab. 2025-07-27 20:48:10 +01:00
David Given
8e5d52f2c7 Update ab. 2025-07-24 23:25:41 +02:00
David Given
dfff5d7230 Merge pull request #796 from davidgiven/layout
Fix and expand the layout support to allow libdsk `altback` layouts.
2025-07-21 00:29:10 +02:00
David Given
19b63786c8 Merge from master. 2025-07-21 00:01:41 +02:00
David Given
5293e1c18b Merge pull request #792 from davidgiven/builds
Make an attempt to switch to WSL 1 for better builds.
2025-04-24 21:07:12 +02:00
Märt Põder
36b120bdbe Add Juku 5104 floppies with a new filesystem_track_order option 2024-11-30 15:31:24 +02:00
David Given
cc169d414f Add experimental support for libdsk 'altback' mode layouts. 2024-11-29 22:39:34 +01:00
David Given
0fcb2075e0 Move filesystem_track_ordering from ImageReaderProto/ImageWriterProto to
ImgInputOutputProto; it now only applies to img files. Make it honour the
appropriate track layout setting too.
2024-11-29 22:30:33 +01:00
David Given
2bda78fb40 Distinguish between filesystem track ordering and image track ordering
(although currently only the filesystem ordering is used).
2024-11-29 22:07:58 +01:00
David Given
e878c6eef6 Remove the unused sector_order field from FilesystemProto. 2024-11-29 21:24:32 +01:00
David Given
9ce405cec5 Remove the broken install rule. 2024-11-24 23:10:48 +01:00
David Given
f064d413b3 Add a docker test for Manjaro Linux. 2024-11-24 22:50:44 +01:00
436 changed files with 40717 additions and 3110 deletions

View File

@@ -8,19 +8,21 @@ concurrency:
jobs:
build-linux:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
repository: 'davidgiven/fluxengine'
path: 'fluxengine'
submodules: 'true'
- uses: actions/checkout@v4
with:
repository: 'davidgiven/fluxengine-testdata'
path: 'fluxengine-testdata'
- name: apt
run: |
sudo apt install libudev-dev libsqlite3-dev protobuf-compiler libwxgtk3.0-gtk3-dev libfmt-dev libprotobuf-dev wx-common
sudo apt update
sudo apt install libudev-dev libsqlite3-dev protobuf-compiler libwxgtk3.2-dev libfmt-dev libprotobuf-dev libmagic-dev libmbedtls-dev libcurl4-openssl-dev libmagic-dev nlohmann-json3-dev libdbus-1-dev libglfw3-dev libmd4c-dev libfreetype-dev libcli11-dev libboost-regex-dev
- name: make
run: CXXFLAGS="-Wp,-D_GLIBCXX_ASSERTIONS" make -j`nproc` -C fluxengine
@@ -50,53 +52,61 @@ jobs:
build-macos-current:
strategy:
matrix:
runs-on: [macos-13, macos-latest]
runs-on: [macos-15, macos-15-intel]
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v4
with:
repository: 'davidgiven/fluxengine'
path: 'fluxengine'
submodules: 'true'
- uses: actions/checkout@v4
with:
repository: 'davidgiven/fluxengine-testdata'
path: 'fluxengine-testdata'
- name: brew
run: |
brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg
brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg libmagic nlohmann-json cli11 boost glfw3 md4c ninja python freetype2 mbedtls
brew upgrade
- name: make
run: gmake -C fluxengine -j2
run: gmake -C fluxengine
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ github.event.repository.name }}.${{ github.sha }}.fluxengine.${{ runner.arch }}.pkg
path: fluxengine/FluxEngine.pkg
path: |
fluxengine/FluxEngine.pkg
fluxengine/FluxEngine.app.zip
build-windows:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- name: setup WSL
run: |
curl -L https://github.com/WhitewaterFoundry/Fedora-Remix-for-WSL/releases/download/41.0.0/Fedora-Remix-for-WSL-SL_41.0.0.0_x64_arm64.msixbundle -o fedora.msixbundle
unzip fedora.msixbundle Fedora-Remix-for-WSL-SL_41.0.0.0_x64.msix
unzip Fedora-Remix-for-WSL-SL_41.0.0.0_x64.msix install.tar.gz
wsl --update
wsl --set-default-version 1
wsl --import fedora fedora install.tar.gz
wsl --set-default fedora
wsl sh -c 'dnf -y install https://github.com/rpmsphere/noarch/raw/master/r/rpmsphere-release-40-1.noarch.rpm'
wsl sh -c 'dnf -y install gcc gcc-c++ protobuf-c-compiler protobuf-devel fmt-devel systemd-devel sqlite-devel wxGTK-devel mingw32-gcc mingw32-gcc-c++ mingw32-zlib-static mingw32-protobuf-static mingw32-sqlite-static mingw32-wxWidgets3-static mingw32-libpng-static mingw32-libjpeg-static mingw32-libtiff-static mingw32-nsis png2ico'
- uses: msys2/setup-msys2@v2
with:
msystem: mingw64
update: true
install: |
python diffutils ninja make zip
pacboy: |
protobuf:p pkgconf:p curl-winssl:p file:p glfw:p mbedtls:p
sqlite:p freetype:p boost:p gcc:p binutils:p nsis:p abseil-cpp:p
- name: fix line endings
- name: debug
run: |
git config --global core.autocrlf false
git config --global core.eol lf
pacboy -Q --info protobuf:p
cat /mingw64/lib/pkgconfig/protobuf.pc
/mingw64/bin/pkg-config.exe protobuf --cflags
/mingw64/bin/pkg-config.exe protobuf --cflags --static
- uses: actions/checkout@v4
with:
repository: 'davidgiven/fluxengine'
path: 'fluxengine'
submodules: 'true'
- uses: actions/checkout@v4
with:
@@ -105,17 +115,18 @@ jobs:
- name: run
run: |
wsl sh -c 'make -C fluxengine BUILDTYPE=windows -j$(nproc)'
make -C fluxengine BUILDTYPE=windows
- name: nsis
run: |
wsl sh -c 'cd fluxengine && strip fluxengine.exe -o fluxengine-stripped.exe'
wsl sh -c 'cd fluxengine && strip fluxengine-gui.exe -o fluxengine-gui-stripped.exe'
wsl sh -c 'cd fluxengine && makensis -v2 -nocd -dOUTFILE=fluxengine-installer.exe extras/windows-installer.nsi'
cd fluxengine
strip fluxengine.exe -o fluxengine-stripped.exe
strip fluxengine-gui.exe -o fluxengine-gui-stripped.exe
makensis -v2 -nocd -dOUTFILE=fluxengine-installer.exe extras/windows-installer.nsi
- name: zip
run: |
wsl sh -c 'cd fluxengine && zip -9 fluxengine-windows.zip fluxengine.exe fluxengine-gui.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex fluxengine-installer.exe'
cd fluxengine && zip -9 fluxengine-windows.zip fluxengine.exe fluxengine-gui.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex fluxengine-installer.exe
- name: Upload build artifacts
uses: actions/upload-artifact@v4

View File

@@ -24,7 +24,7 @@ jobs:
wsl --import fedora fedora install.tar.gz
wsl --set-default fedora
wsl sh -c 'dnf -y install https://github.com/rpmsphere/noarch/raw/master/r/rpmsphere-release-40-1.noarch.rpm'
wsl sh -c 'dnf -y install gcc gcc-c++ protobuf-c-compiler protobuf-devel fmt-devel systemd-devel sqlite-devel wxGTK-devel mingw32-gcc mingw32-gcc-c++ mingw32-zlib-static mingw32-protobuf-static mingw32-sqlite-static mingw32-wxWidgets3-static mingw32-libpng-static mingw32-libjpeg-static mingw32-libtiff-static mingw32-nsis png2ico'
wsl sh -c 'dnf -y install gcc gcc-c++ protobuf-c-compiler protobuf-devel fmt-devel systemd-devel sqlite-devel wxGTK-devel mingw32-gcc mingw32-gcc-c++ mingw32-zlib-static mingw32-protobuf-static mingw32-sqlite-static mingw32-wxWidgets3-static mingw32-libpng-static mingw32-libjpeg-static mingw32-libtiff-static mingw32-nsis png2ico ninja-build'
- name: fix line endings
run: |
@@ -97,8 +97,9 @@ jobs:
- name: make
run: |
gmake -j2
gmake
mv FluxEngine.pkg FluxEngine-${{ runner.arch }}.pkg
mv FluxEngine.app.zip FluxEngine-${{ runner.arch }}.app.zip
- name: tag
uses: EndBug/latest-tag@latest
@@ -115,6 +116,7 @@ jobs:
tag: dev
assets: |
FluxEngine-${{ runner.arch }}.pkg
FluxEngine-${{ runner.arch }}.app.zip
fail-if-no-assets: false
- name: release
@@ -123,6 +125,7 @@ jobs:
name: Development build ${{ env.RELEASE_DATE }}
files: |
FluxEngine-${{ runner.arch }}.pkg
FluxEngine-${{ runner.arch }}.app.zip
tag_name: dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

36
.gitmodules vendored Normal file
View File

@@ -0,0 +1,36 @@
[submodule "dep/imhex"]
path = dep/imhex
url = git@github.com:davidgiven/ImHex.git
[submodule "dep/libwolv"]
path = dep/libwolv
url = https://github.com/WerWolv/libwolv.git
[submodule "dep/imgui"]
path = dep/imgui
url = https://github.com/ocornut/imgui.git
[submodule "dep/pattern-language"]
path = dep/pattern-language
url = https://github.com/WerWolv/PatternLanguage.git
[submodule "dep/native-file-dialog"]
path = dep/native-file-dialog
url = https://github.com/btzy/nativefiledialog-extended.git
[submodule "dep/xdgpp"]
path = dep/xdgpp
url = https://github.com/WerWolv/xdgpp.git
[submodule "dep/libromfs"]
path = dep/libromfs
url = https://github.com/WerWolv/libromfs.git
[submodule "dep/throwing_ptr"]
path = dep/throwing_ptr
url = https://github.com/rockdreamer/throwing_ptr.git
[submodule "dep/lunasvg"]
path = dep/lunasvg
url = https://github.com/sammycage/lunasvg.git
[submodule "dep/md4c"]
path = dep/md4c
url = https://github.com/mity/md4c
[submodule "dep/nlohmann_json"]
path = dep/nlohmann_json
url = https://github.com/nlohmann/json
[submodule "dep/cli11"]
path = dep/cli11
url = https://github.com/CLIUtils/CLI11

110
Makefile
View File

@@ -8,25 +8,54 @@ ifeq ($(BUILDTYPE),)
endif
export BUILDTYPE
OPTFLAGS = -g -O3
ifeq ($(BUILDTYPE),windows)
MINGW = i686-w64-mingw32-
MINGW = x86_64-w64-mingw32-
CC = $(MINGW)gcc
CXX = $(MINGW)g++ -std=c++20
CFLAGS += -g -O3
CXX = $(MINGW)g++
CFLAGS += \
$(OPTFLAGS) \
-ffunction-sections \
-fdata-sections \
-Wno-attributes \
-Wa,-mbig-obj \
-static
CXXFLAGS += \
-fext-numeric-literals \
$(OPTFLAGS) \
-std=c++23 \
-Wno-deprecated-enum-float-conversion \
-Wno-deprecated-enum-enum-conversion
LDFLAGS += -static
AR = $(MINGW)ar
PKG_CONFIG = $(MINGW)pkg-config -static
-Wno-deprecated-enum-enum-conversion \
-Wno-attributes \
-Wa,-mbig-obj \
-static
LDFLAGS += -Wl,--gc-sections -static
AR = $(MINGW)gcc-ar
PKG_CONFIG = $(MINGW)pkg-config --static
WINDRES = $(MINGW)windres
WX_CONFIG = /usr/i686-w64-mingw32/sys-root/mingw/bin/wx-config-3.0 --static=yes
NINJA = /bin/ninja
PROTOC = /mingw64/bin/protoc
PROTOC_SEPARATOR = ;
EXT = .exe
AB_SANDBOX = no
else
CC = gcc
CXX = g++ -std=c++17
CFLAGS = -g -O3
CC = clang
CXX = clang++
CFLAGS = \
$(OPTFLAGS) \
-I/opt/homebrew/include -I/usr/local/include \
-Wno-unknown-warning-option
CXXFLAGS = \
$(OPTFLAGS) \
-std=c++23 \
-fexperimental-library \
-I/opt/homebrew/include -I/usr/local/include \
-Wformat \
-Wformat-security \
-Wno-deprecated-enum-float-conversion \
-Wno-deprecated-enum-enum-conversion
LDFLAGS =
AR = ar
PKG_CONFIG = pkg-config
@@ -34,11 +63,10 @@ else
else
LDFLAGS += -pthread -Wl,--no-as-needed
endif
endif
HOSTCC = gcc
HOSTCXX = g++ -std=c++17
HOSTCXX = g++ -std=c++20
HOSTCFLAGS = -g -O3
HOSTLDFLAGS =
@@ -50,31 +78,33 @@ BINDIR ?= $(PREFIX)/bin
# Special Windows settings.
ifeq ($(OS), Windows_NT)
EXT ?= .exe
MINGWBIN = /mingw32/bin
CCPREFIX = $(MINGWBIN)/
PKG_CONFIG = $(MINGWBIN)/pkg-config
WX_CONFIG = /usr/bin/sh $(MINGWBIN)/wx-config --static=yes
PROTOC = $(MINGWBIN)/protoc
WINDRES = windres
LDFLAGS += \
-static
CXXFLAGS += \
-fext-numeric-literals \
-Wno-deprecated-enum-float-conversion \
-Wno-deprecated-enum-enum-conversion
# Required to get the gcc run - time libraries on the path.
export PATH := $(PATH):$(MINGWBIN)
endif
#ifeq ($(OS), Windows_NT)
# EXT ?= .exe
# MINGWBIN = /mingw32/bin
# CCPREFIX = $(MINGWBIN)/
# PKG_CONFIG = $(MINGWBIN)/pkg-config
# WX_CONFIG = /usr/bin/sh $(MINGWBIN)/wx-config --static=yes
# PROTOC = $(MINGWBIN)/protoc
# WINDRES = windres
# LDFLAGS += \
# -static
# CXXFLAGS += \
# -fext-numeric-literals \
# -Wno-deprecated-enum-float-conversion \
# -Wno-deprecated-enum-enum-conversion
#
# # Required to get the gcc run - time libraries on the path.
# export PATH := $(PATH):$(MINGWBIN)
#endif
# Special OSX settings.
ifeq ($(shell uname),Darwin)
LDFLAGS += \
-framework IOKit \
-framework Foundation
-framework AppKit \
-framework UniformTypeIdentifiers \
-framework UserNotifications
endif
.PHONY: all
@@ -85,33 +115,23 @@ binaries: all
tests: all
README.md: $(OBJ)/scripts/+mkdocindex/mkdocindex$(EXT)
@echo $(PROGRESSINFO) MKDOC $@
@echo $(PROGRESSINFO)MKDOC $@
@csplit -s -f$(OBJ)/README. README.md '/<!-- FORMATSSTART -->/' '%<!-- FORMATSEND -->%'
@(cat $(OBJ)/README.00 && $< && cat $(OBJ)/README.01) > README.md
.PHONY: tests
.PHONY: install install-bin
install:: all install-bin
clean::
$(hide) rm -rf $(REALOBJ)
install-bin:
@echo "INSTALL"
$(hide) install -D -v "$(OBJ)/src+fluxengine/src+fluxengine" "$(DESTDIR)$(BINDIR)/fluxengine"
$(hide) install -D -v "$(OBJ)/src/gui+gui/gui+gui" "$(DESTDIR)$(BINDIR)/fluxengine-gui"
$(hide) install -D -v "$(OBJ)/tools+brother120tool/tools+brother120tool" "$(DESTDIR)$(BINDIR)/brother120tool"
$(hide) install -D -v "$(OBJ)/tools+brother240tool/tools+brother240tool" "$(DESTDIR)$(BINDIR)/brother240tool"
$(hide) install -D -v "$(OBJ)/tools+upgrade-flux-file/tools+upgrade-flux-file" "$(DESTDIR)$(BINDIR)/upgrade-flux-file"
include build/ab.mk
DOCKERFILES = \
debian11 \
debian12 \
fedora40 \
fedora41
fedora41 \
manjaro
docker-%: tests/docker/Dockerfile.%
docker build -t $* -f $< .

View File

@@ -125,6 +125,7 @@ choices because they can store multiple types of file system.
| [`hplif`](doc/disk-hplif.md) | Hewlett-Packard LIF: a variety of disk formats used by HP | 🦄 | 🦄 | LIF |
| [`ibm`](doc/disk-ibm.md) | IBM PC: Generic PC 3.5"/5.25" disks | 🦄 | 🦄 | FATFS |
| [`icl30`](doc/disk-icl30.md) | ICL Model 30: CP/M; 263kB 35-track DSSD | 🦖 | | CPMFS |
| [`juku`](doc/disk-juku.md) | Juku E5104: CP/M | | | CPMFS |
| [`mac`](doc/disk-mac.md) | Macintosh: 400kB/800kB 3.5" GCR | 🦄 | 🦄 | MACHFS |
| [`micropolis`](doc/disk-micropolis.md) | Micropolis: 100tpi MetaFloppy disks | 🦄 | 🦄 | |
| [`ms2000`](doc/disk-ms2000.md) | : MS2000 Microdisk Development System | | | MICRODOS |
@@ -136,6 +137,7 @@ choices because they can store multiple types of file system.
| [`rx50`](doc/disk-rx50.md) | Digital RX50: 400kB 5.25" 80-track 10-sector SSDD | 🦖 | 🦖 | |
| [`smaky6`](doc/disk-smaky6.md) | Smaky 6: 308kB 5.25" 77-track 16-sector SSDD, hard sectored | 🦖 | | SMAKY6 |
| [`tartu`](doc/disk-tartu.md) | Tartu: The Palivere and variations | 🦄 | 🦖 | CPMFS |
| [`ti99`](doc/disk-ti99.md) | TI-99: 90kB 35-track SSSD | 🦖 | | |
| [`tids990`](doc/disk-tids990.md) | Texas Instruments DS990: 1126kB 8" DSSD | 🦖 | 🦖 | |
| [`tiki`](doc/disk-tiki.md) | Tiki 100: CP/M | | | CPMFS |
| [`victor9k`](doc/disk-victor9k.md) | Victor 9000 / Sirius One: 1224kB 5.25" DSDD GCR | 🦖 | 🦖 | |
@@ -257,6 +259,15 @@ package, written by Robert Leslie et al, taken from
https://www.mars.org/home/rob/proj/hfs. It is GPL 2.0 licensed. Please see the
contents of the directory for the full text.
As an exception, `dep/lexy` contains a partial copy of the lexy package, written
by foonathen@github, taken from https://github.com/foonathan/lexy. It is BSL 1.0
licensed. Please see the contents of the directory for the full text.
As an exception, `dep/alphanum` contains a copy of the alphanum package,
written by Dave Koelle, taken from
https://web.archive.org/web/20210207124255/davekoelle.com/alphanum.html. It is
MIT licensed. Please see the source for the full text.
__Important:__ Because of all these exceptions, if you distribute the
FluxEngine package as a whole, you must comply with the terms of _all_ of the
licensing terms. This means that __effectively the FluxEngine package is

View File

@@ -36,8 +36,8 @@ public:
decodeFmMfm(rawbits).slice(0, AESLANIER_RECORD_SIZE);
const auto& reversed = bytes.reverseBits();
_sector->logicalTrack = reversed[1];
_sector->logicalSide = 0;
_sector->logicalCylinder = reversed[1];
_sector->logicalHead = 0;
_sector->logicalSector = reversed[2];
/* Check header 'checksum' (which seems far too simple to mean much). */

View File

@@ -59,9 +59,9 @@ public:
if (bytes[3] != 0x5a)
return;
_sector->logicalTrack = bytes[1] >> 1;
_sector->logicalCylinder = bytes[1] >> 1;
_sector->logicalSector = bytes[2];
_sector->logicalSide = bytes[1] & 1;
_sector->logicalHead = bytes[1] & 1;
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}

View File

@@ -58,13 +58,10 @@ private:
};
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
auto trackLayout = Layout::getLayoutOfTrack(
trackInfo->logicalTrack, trackInfo->logicalSide);
double clockRateUs = _config.target_clock_period_us() / 2.0;
int bitsPerRevolution =
(_config.target_rotational_period_ms() * 1000.0) / clockRateUs;
@@ -80,7 +77,7 @@ public:
writeFillerRawBytes(_config.pre_sector_gap_bytes(), 0xaaaa);
writeRawBits(SECTOR_ID, 64);
writeByte(0x5a);
writeByte((sector->logicalTrack << 1) | sector->logicalSide);
writeByte((sector->logicalCylinder << 1) | sector->logicalHead);
writeByte(sector->logicalSector);
writeByte(0x5a);

View File

@@ -52,8 +52,8 @@ public:
Bytes header = amigaDeinterleave(ptr, 4);
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
_sector->logicalTrack = header[1] >> 1;
_sector->logicalSide = header[1] & 1;
_sector->logicalCylinder = header[1] >> 1;
_sector->logicalHead = header[1] & 1;
_sector->logicalSector = header[2];
uint32_t wantedheaderchecksum =

View File

@@ -84,7 +84,7 @@ static void write_sector(std::vector<bool>& bits,
checksum = 0;
Bytes header = {0xff, /* Amiga 1.0 format byte */
(uint8_t)((sector->logicalTrack << 1) | sector->logicalSide),
(uint8_t)((sector->logicalCylinder << 1) | sector->logicalHead),
(uint8_t)sector->logicalSector,
(uint8_t)(AMIGA_SECTORS_PER_TRACK - sector->logicalSector)};
write_interleaved_bytes(header);
@@ -110,7 +110,7 @@ public:
}
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{

View File

@@ -5,6 +5,7 @@
#include "protocol.h"
#include "lib/decoders/decoders.h"
#include "lib/data/sector.h"
#include "lib/data/layout.h"
#include "arch/apple2/apple2.h"
#include "arch/apple2/apple2.pb.h"
#include "lib/decoders/decoders.pb.h"
@@ -93,24 +94,25 @@ public:
ByteReader br(header);
uint8_t volume = combine(br.read_be16());
_sector->logicalTrack = combine(br.read_be16());
_sector->logicalSide = _sector->physicalSide;
_sector->logicalCylinder = combine(br.read_be16());
_sector->logicalHead = _ltl->logicalHead;
_sector->logicalSector = combine(br.read_be16());
uint8_t checksum = combine(br.read_be16());
// If the checksum is correct, upgrade the sector from MISSING
// to DATA_MISSING in anticipation of its data record
if (checksum ==
(volume ^ _sector->logicalTrack ^ _sector->logicalSector))
(volume ^ _sector->logicalCylinder ^ _sector->logicalSector))
_sector->status =
Sector::DATA_MISSING; /* unintuitive but correct */
if (_sector->logicalSide == 1)
_sector->logicalTrack -= _config.apple2().side_one_track_offset();
if (_sector->logicalHead == 1)
_sector->logicalCylinder -=
_config.apple2().side_one_track_offset();
/* Sanity check. */
if (_sector->logicalTrack > 100)
if (_sector->logicalCylinder > 100)
{
_sector->status = Sector::MISSING;
return;

View File

@@ -36,7 +36,7 @@ private:
const Apple2EncoderProto& _config;
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
@@ -129,8 +129,8 @@ private:
// extra padding.
write_ff40(sector.logicalSector == 0 ? 32 : 8);
int track = sector.logicalTrack;
if (sector.logicalSide == 1)
int track = sector.logicalCylinder;
if (sector.logicalHead == 1)
track += _config.side_one_track_offset();
// Write address field: APPLE2_SECTOR_RECORD + sector identifier +

View File

@@ -75,14 +75,14 @@ public:
const auto& bytes = toBytes(rawbits).slice(0, 4);
ByteReader br(bytes);
_sector->logicalTrack = decode_header_gcr(br.read_be16());
_sector->logicalCylinder = decode_header_gcr(br.read_be16());
_sector->logicalSector = decode_header_gcr(br.read_be16());
/* Sanity check the values read; there's no header checksum and
* occasionally we get garbage due to bit errors. */
if (_sector->logicalSector > 11)
return;
if (_sector->logicalTrack > 79)
if (_sector->logicalCylinder > 79)
return;
_sector->status = Sector::DATA_MISSING;

View File

@@ -107,7 +107,7 @@ public:
}
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
@@ -127,7 +127,7 @@ public:
fillBitmapTo(bits, cursor, headerCursor, {true, false});
write_sector_header(bits,
cursor,
sectorData->logicalTrack,
sectorData->logicalCylinder,
sectorData->logicalSector);
fillBitmapTo(bits, cursor, dataCursor, {true, false});
write_sector_data(bits, cursor, sectorData->data);

View File

@@ -74,8 +74,8 @@ public:
uint8_t checksum = bytes[0];
_sector->logicalSector = bytes[1];
_sector->logicalSide = 0;
_sector->logicalTrack = bytes[2] - 1;
_sector->logicalHead = 0;
_sector->logicalCylinder = bytes[2] - 1;
if (checksum == xorBytes(bytes.slice(1, 4)))
_sector->status =
Sector::DATA_MISSING; /* unintuitive but correct */

View File

@@ -155,7 +155,7 @@ public:
}
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
@@ -178,7 +178,7 @@ public:
else
_formatByte1 = _formatByte2 = 0;
double clockRateUs = clockPeriodForC64Track(trackInfo->logicalTrack);
double clockRateUs = clockPeriodForC64Track(ltl.logicalCylinder);
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
@@ -245,7 +245,7 @@ private:
* 06-07 - $0F ("off" bytes)
*/
uint8_t encodedTrack =
((sector->logicalTrack) +
((sector->logicalCylinder) +
1); // C64 track numbering starts with 1. Fluxengine with 0.
uint8_t encodedSector = sector->logicalSector;
// uint8_t formatByte1 = C64_FORMAT_ID_BYTE1;

View File

@@ -76,8 +76,8 @@ public:
const auto& bytes = decode(readRawBits(6 * 10));
_sector->logicalSector = bytes[2];
_sector->logicalSide = 0;
_sector->logicalTrack = bytes[0];
_sector->logicalHead = 0;
_sector->logicalCylinder = bytes[0];
uint16_t wantChecksum = bytes.reader().seek(4).read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xef21, bytes.slice(0, 4));

View File

@@ -126,8 +126,8 @@ public:
return;
uint8_t abssector = id[2];
_sector->logicalTrack = abssector >> 1;
_sector->logicalSide = 0;
_sector->logicalCylinder = abssector >> 1;
_sector->logicalHead = 0;
_sector->logicalSector = abssector & 1;
_sector->data.writer().append(id.slice(5, 12)).append(payload);

View File

@@ -141,11 +141,10 @@ public:
bw += decodeFmMfm(bits).slice(0, IBM_IDAM_LEN);
IbmDecoderProto::TrackdataProto trackdata;
getTrackFormat(
trackdata, _sector->physicalTrack, _sector->physicalSide);
getTrackFormat(trackdata, _ltl->logicalCylinder, _ltl->logicalHead);
_sector->logicalTrack = br.read_8();
_sector->logicalSide = br.read_8();
_sector->logicalCylinder = br.read_8();
_sector->logicalHead = br.read_8();
_sector->logicalSector = br.read_8();
_currentSectorSize = 1 << (br.read_8() + 7);
@@ -156,11 +155,10 @@ public:
Sector::DATA_MISSING; /* correct but unintuitive */
if (trackdata.ignore_side_byte())
_sector->logicalSide =
Layout::remapSidePhysicalToLogical(_sector->physicalSide);
_sector->logicalSide ^= trackdata.invert_side_byte();
_sector->logicalHead = _ltl->logicalHead;
_sector->logicalHead ^= trackdata.invert_side_byte();
if (trackdata.ignore_track_byte())
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalCylinder = _ltl->logicalCylinder;
for (int sector : trackdata.ignore_sector())
if (_sector->logicalSector == sector)
@@ -209,16 +207,14 @@ public:
_sector->status =
(wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
auto layout = Layout::getLayoutOfTrack(
_sector->logicalTrack, _sector->logicalSide);
if (_currentSectorSize != layout->sectorSize)
if (_currentSectorSize != _ltl->sectorSize)
std::cerr << fmt::format(
"Warning: configured sector size for t{}.h{}.s{} is {} bytes "
"but that seen on disk is {} bytes\n",
_sector->logicalTrack,
_sector->logicalSide,
_sector->logicalCylinder,
_sector->logicalHead,
_sector->logicalSector,
layout->sectorSize,
_ltl->sectorSize,
_currentSectorSize);
}

View File

@@ -107,16 +107,12 @@ private:
}
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
IbmEncoderProto::TrackdataProto trackdata;
getEncoderTrackData(
trackdata, trackInfo->logicalTrack, trackInfo->logicalSide);
auto trackLayout = Layout::getLayoutOfTrack(
trackInfo->logicalTrack, trackInfo->logicalSide);
getEncoderTrackData(trackdata, ltl.logicalCylinder, ltl.logicalHead);
auto writeBytes = [&](const Bytes& bytes)
{
@@ -152,7 +148,7 @@ public:
uint8_t sectorSize = 0;
{
int s = trackLayout->sectorSize >> 7;
int s = ltl.sectorSize >> 7;
while (s > 1)
{
s >>= 1;
@@ -202,9 +198,9 @@ public:
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(idamUnencoded);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorData->logicalCylinder);
bw.write_8(
sectorData->logicalSide ^ trackdata.invert_side_byte());
sectorData->logicalHead ^ trackdata.invert_side_byte());
bw.write_8(sectorData->logicalSector);
bw.write_8(sectorSize);
uint16_t crc = crc16(CCITT_POLY, header);
@@ -237,8 +233,7 @@ public:
}
bw.write_8(damUnencoded);
Bytes truncatedData =
sectorData->data.slice(0, trackLayout->sectorSize);
Bytes truncatedData = sectorData->data.slice(0, ltl.sectorSize);
bw += truncatedData;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);

View File

@@ -5,6 +5,7 @@
#include "protocol.h"
#include "lib/decoders/decoders.h"
#include "lib/data/sector.h"
#include "lib/data/layout.h"
#include "arch/macintosh/macintosh.h"
#include "lib/core/bytes.h"
#include "fmt/format.h"
@@ -146,7 +147,7 @@ public:
auto header = toBytes(readRawBits(7 * 8)).slice(0, 7);
uint8_t encodedTrack = decode_data_gcr(header[0]);
if (encodedTrack != (_sector->physicalTrack & 0x3f))
if (encodedTrack != (_ltl->logicalCylinder & 0x3f))
return;
uint8_t encodedSector = decode_data_gcr(header[1]);
@@ -157,8 +158,8 @@ public:
if (encodedSector > 11)
return;
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = decode_side(encodedSide);
_sector->logicalCylinder = _ltl->logicalCylinder;
_sector->logicalHead = decode_side(encodedSide);
_sector->logicalSector = encodedSector;
uint8_t gotsum =
(encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;

View File

@@ -181,10 +181,10 @@ static void write_sector(std::vector<bool>& bits,
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6 * 8); /* sync */
write_bits(bits, cursor, MAC_SECTOR_RECORD, 3 * 8);
uint8_t encodedTrack = sector->logicalTrack & 0x3f;
uint8_t encodedTrack = sector->logicalCylinder & 0x3f;
uint8_t encodedSector = sector->logicalSector;
uint8_t encodedSide =
encode_side(sector->logicalTrack, sector->logicalSide);
encode_side(sector->logicalCylinder, sector->logicalHead);
uint8_t formatByte = MAC_FORMAT_BYTE;
uint8_t headerChecksum =
(encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
@@ -220,11 +220,11 @@ public:
}
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
double clockRateUs = clockRateUsForTrack(trackInfo->logicalTrack);
double clockRateUs = clockRateUsForTrack(ltl.logicalCylinder);
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;

View File

@@ -4,6 +4,7 @@
#include "lib/data/fluxpattern.h"
#include "lib/decoders/decoders.h"
#include "lib/data/sector.h"
#include "lib/data/layout.h"
#include "arch/micropolis/micropolis.h"
#include "lib/core/bytes.h"
#include "fmt/format.h"
@@ -222,14 +223,14 @@ public:
if (syncByte != 0xFF)
return;
_sector->logicalTrack = br.read_8();
_sector->logicalSide = _sector->physicalSide;
_sector->logicalCylinder = br.read_8();
_sector->logicalHead = _ltl->logicalHead;
_sector->logicalSector = br.read_8();
if (_sector->logicalSector > 15)
return;
if (_sector->logicalTrack > 76)
if (_sector->logicalCylinder > 76)
return;
if (_sector->logicalTrack != _sector->physicalTrack)
if (_sector->logicalCylinder != _ltl->logicalCylinder)
return;
br.read(10); /* OS data or padding */

View File

@@ -40,7 +40,7 @@ static void write_sector(std::vector<bool>& bits,
{
ByteWriter writer(sectorData);
writer.write_8(0xff); /* Sync */
writer.write_8(sector->logicalTrack);
writer.write_8(sector->logicalCylinder);
writer.write_8(sector->logicalSector);
for (int i = 0; i < 10; i++)
writer.write_8(0); /* Padding */
@@ -87,7 +87,7 @@ public:
{
}
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{

View File

@@ -6,6 +6,7 @@
#include "lib/data/fluxmapreader.h"
#include "lib/data/fluxpattern.h"
#include "lib/data/sector.h"
#include "lib/data/layout.h"
#include <string.h>
const int SECTOR_SIZE = 256;
@@ -64,8 +65,8 @@ public:
gotChecksum += br.read_be16();
uint16_t wantChecksum = br.read_be16();
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = _sector->physicalSide;
_sector->logicalCylinder = _ltl->logicalCylinder;
_sector->logicalHead = _ltl->logicalHead;
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE).swab();
_sector->status =

View File

@@ -17,6 +17,7 @@
#include "lib/data/fluxpattern.h"
#include "lib/decoders/decoders.h"
#include "lib/data/sector.h"
#include "lib/data/layout.h"
#include "arch/northstar/northstar.h"
#include "lib/core/bytes.h"
#include "lib/decoders/decoders.pb.h"
@@ -159,9 +160,9 @@ public:
auto bytes = decodeFmMfm(rawbits).slice(0, recordSize);
ByteReader br(bytes);
_sector->logicalSide = _sector->physicalSide;
_sector->logicalHead = _ltl->logicalHead;
_sector->logicalSector = _hardSectorId;
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalCylinder = _ltl->logicalCylinder;
if (headerSize == NORTHSTAR_HEADER_SIZE_DD)
{

View File

@@ -129,7 +129,7 @@ public:
{
}
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{

View File

@@ -5,6 +5,7 @@
#include "protocol.h"
#include "lib/decoders/decoders.h"
#include "lib/data/sector.h"
#include "lib/data/layout.h"
#include "arch/smaky6/smaky6.h"
#include "lib/core/bytes.h"
#include "lib/core/crc.h"
@@ -129,11 +130,11 @@ public:
uint8_t wantedChecksum = br.read_8();
uint8_t gotChecksum = sumBytes(data) & 0xff;
if (track != _sector->physicalTrack)
if (track != _ltl->logicalCylinder)
return;
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = _sector->physicalSide;
_sector->logicalCylinder = _ltl->physicalCylinder;
_sector->logicalHead = _ltl->logicalHead;
_sector->logicalSector = _sectorId;
_sector->data = data;

View File

@@ -43,8 +43,8 @@ public:
ByteReader br(bytes);
uint8_t track = br.read_8();
_sector->logicalTrack = track >> 1;
_sector->logicalSide = track & 1;
_sector->logicalCylinder = track >> 1;
_sector->logicalHead = track & 1;
br.skip(1); /* seems always to be 1 */
_sector->logicalSector = br.read_8();
uint8_t wantChecksum = br.read_8();

View File

@@ -17,7 +17,7 @@ public:
{
}
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
@@ -83,7 +83,7 @@ private:
Bytes bytes;
ByteWriter bw(bytes);
bw.write_8(
(sectorData->logicalTrack << 1) | sectorData->logicalSide);
(sectorData->logicalCylinder << 1) | sectorData->logicalHead);
bw.write_8(1);
bw.write_8(sectorData->logicalSector);
bw.write_8(~sumBytes(bytes.slice(0, 3)));

View File

@@ -64,8 +64,8 @@ public:
uint16_t gotChecksum =
crc16(CCITT_POLY, bytes.slice(1, TIDS990_SECTOR_RECORD_SIZE - 3));
_sector->logicalSide = br.read_8() >> 3;
_sector->logicalTrack = br.read_8();
_sector->logicalHead = br.read_8() >> 3;
_sector->logicalCylinder = br.read_8();
br.read_8(); /* number of sectors per track */
_sector->logicalSector = br.read_8();
br.read_be16(); /* sector size */

View File

@@ -59,7 +59,7 @@ private:
}
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
@@ -95,8 +95,8 @@ public:
writeBytes(12, 0x55);
bw.write_8(am1Unencoded);
bw.write_8(sectorData->logicalSide << 3);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorData->logicalHead << 3);
bw.write_8(sectorData->logicalCylinder);
bw.write_8(_config.sector_count());
bw.write_8(sectorData->logicalSector);
bw.write_be16(sectorData->data.size());

View File

@@ -80,11 +80,11 @@ public:
_sector->logicalSector = bytes[1];
uint8_t gotChecksum = bytes[2];
_sector->logicalTrack = rawTrack & 0x7f;
_sector->logicalSide = rawTrack >> 7;
_sector->logicalCylinder = rawTrack & 0x7f;
_sector->logicalHead = rawTrack >> 7;
uint8_t wantChecksum = bytes[0] + bytes[1];
if ((_sector->logicalSector > 20) || (_sector->logicalTrack > 85) ||
(_sector->logicalSide > 1))
if ((_sector->logicalSector > 20) || (_sector->logicalCylinder > 85) ||
(_sector->logicalHead > 1))
return;
if (wantChecksum == gotChecksum)

View File

@@ -112,7 +112,7 @@ static void write_sector(std::vector<bool>& bits,
write_one_bits(bits, cursor, trackdata.pre_header_sync_bits());
write_bits(bits, cursor, VICTOR9K_SECTOR_RECORD, 10);
uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide << 7);
uint8_t encodedTrack = sector.logicalCylinder | (sector.logicalHead << 7);
uint8_t encodedSector = sector.logicalSector;
write_bytes(bits,
cursor,
@@ -164,13 +164,12 @@ private:
}
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
Victor9kEncoderProto::TrackdataProto trackdata;
getTrackFormat(
trackdata, trackInfo->logicalTrack, trackInfo->logicalSide);
getTrackFormat(trackdata, ltl.logicalCylinder, ltl.logicalHead);
unsigned bitsPerRevolution = (trackdata.rotational_period_ms() * 1e3) /
trackdata.clock_period_us();

View File

@@ -34,11 +34,11 @@ public:
ByteReader br(bytes);
_sector->logicalSector = br.read_8() & 0x1f;
_sector->logicalSide = 0;
_sector->logicalTrack = br.read_8() & 0x7f;
_sector->logicalHead = 0;
_sector->logicalCylinder = br.read_8() & 0x7f;
if (_sector->logicalSector > 31)
return;
if (_sector->logicalTrack > 80)
if (_sector->logicalCylinder > 80)
return;
_sector->data = br.read(132);

View File

@@ -7,6 +7,12 @@ from glob import glob
import config
import re
# Hack for building on Fedora/WSL; executables get the .exe extension,
# but the build system detects it as Linux.
import build.toolchain
toolchain.Toolchain.EXE = "$(EXT)"
package(name="protobuf_lib", package="protobuf")
package(name="z_lib", package="zlib")
package(name="fmt_lib", package="fmt", fallback="dep/fmt")
@@ -22,7 +28,7 @@ else:
("acorndfs", "", "--200"),
("agat", "", ""),
("amiga", "", ""),
("apple2", "", "--140 40track_drive"),
("apple2", "", "--140 --drivetype=40"),
("atarist", "", "--360"),
("atarist", "", "--370"),
("atarist", "", "--400"),
@@ -32,17 +38,17 @@ else:
("atarist", "", "--800"),
("atarist", "", "--820"),
("bk", "", ""),
("brother", "", "--120 40track_drive"),
("brother", "", "--120 --drivetype=40"),
("brother", "", "--240"),
(
"commodore",
"scripts/commodore1541_test.textpb",
"--171 40track_drive",
"--171 --drivetype=40",
),
(
"commodore",
"scripts/commodore1541_test.textpb",
"--192 40track_drive",
"--192 --drivetype=40",
),
("commodore", "", "--800"),
("commodore", "", "--1620"),
@@ -54,17 +60,17 @@ else:
("ibm", "", "--1232"),
("ibm", "", "--1440"),
("ibm", "", "--1680"),
("ibm", "", "--180 40track_drive"),
("ibm", "", "--160 40track_drive"),
("ibm", "", "--320 40track_drive"),
("ibm", "", "--360 40track_drive"),
("ibm", "", "--180 --drivetype=40"),
("ibm", "", "--160 --drivetype=40"),
("ibm", "", "--320 --drivetype=40"),
("ibm", "", "--360 --drivetype=40"),
("ibm", "", "--720_96"),
("ibm", "", "--720_135"),
("mac", "scripts/mac400_test.textpb", "--400"),
("mac", "scripts/mac800_test.textpb", "--800"),
("n88basic", "", ""),
("rx50", "", ""),
("tartu", "", "--390 40track_drive"),
("tartu", "", "--390 --drivetype=40"),
("tartu", "", "--780"),
("tids990", "", ""),
("victor9k", "", "--612"),
@@ -87,7 +93,7 @@ else:
+ c[1]
+ "' '"
+ c[2]
+ "' $(dir $[outs[0]]) > /dev/null"
+ "' $[dirname(filenameof(outs[0]))] > /dev/null"
],
label="CORPUSTEST",
)
@@ -98,11 +104,18 @@ export(
name="all",
items={
"fluxengine$(EXT)": "src+fluxengine",
"fluxengine-gui$(EXT)": "src/gui",
"fluxengine-gui$(EXT)": "src/gui2",
"brother120tool$(EXT)": "tools+brother120tool",
"brother240tool$(EXT)": "tools+brother240tool",
"upgrade-flux-file$(EXT)": "tools+upgrade-flux-file",
}
| ({"FluxEngine.pkg": "src/gui+fluxengine_pkg"} if config.osx else {}),
| (
{
"FluxEngine.pkg": "src/gui2+fluxengine_pkg",
"FluxEngine.app.zip": "src/gui2+fluxengine_app_zip",
}
if config.osx
else {}
),
deps=["tests", "src/formats+docs", "scripts+mkdocindex"] + corpustests,
)

View File

@@ -26,7 +26,7 @@ def main():
print("link", sf)
os.makedirs(dirname(sf), exist_ok=True)
try:
os.link(abspath(f), sf)
os.symlink(abspath(f), sf)
except PermissionError:
shutil.copy(f, sf)
@@ -38,6 +38,11 @@ def main():
df = dirname(f)
if df:
os.makedirs(df, exist_ok=True)
try:
os.remove(f)
except FileNotFoundError:
pass
os.rename(sf, f)

View File

@@ -15,16 +15,17 @@ HOSTCC ?= gcc
HOSTCXX ?= g++
HOSTAR ?= ar
HOSTCFLAGS ?= -g -Og
HOSTCXXFLAGS ?= $(HOSTCFLAGS)
HOSTLDFLAGS ?= -g
CC ?= $(HOSTCC)
CXX ?= $(HOSTCXX)
AR ?= $(HOSTAR)
CFLAGS ?= $(HOSTCFLAGS)
CXXFLAGS ?= $(CFLAGS)
LDFLAGS ?= $(HOSTLDFLAGS)
export PKG_CONFIG
export HOST_PKG_CONFIG
NINJA ?= ninja
ifdef VERBOSE
hide =
@@ -63,32 +64,36 @@ EXT ?=
CWD=$(shell pwd)
ifeq ($(AB_ENABLE_PROGRESS_INFO),true)
ifeq ($(PROGRESSINFO),)
# The first make invocation here has to have its output discarded or else it
# produces spurious 'Leaving directory' messages... don't know why.
rulecount := $(strip $(shell $(MAKE) --no-print-directory -q $(OBJ)/build.mk PROGRESSINFO=1 > /dev/null \
&& $(MAKE) --no-print-directory -n $(MAKECMDGOALS) PROGRESSINFO=XXXPROGRESSINFOXXX | grep XXXPROGRESSINFOXXX | wc -l))
ruleindex := 1
PROGRESSINFO = "[$(ruleindex)/$(rulecount)]$(eval ruleindex := $(shell expr $(ruleindex) + 1)) "
endif
else
PROGRESSINFO = ""
endif
define newline
PKG_CONFIG_HASHES = $(OBJ)/.pkg-config-hashes/target-$(word 1, $(shell $(PKG_CONFIG) --list-all | md5sum))
HOST_PKG_CONFIG_HASHES = $(OBJ)/.pkg-config-hashes/host-$(word 1, $(shell $(HOST_PKG_CONFIG) --list-all | md5sum))
$(OBJ)/build.mk : $(PKG_CONFIG_HASHES) $(HOST_PKG_CONFIG_HASHES)
$(PKG_CONFIG_HASHES) $(HOST_PKG_CONFIG_HASHES) &:
$(hide) rm -rf $(OBJ)/.pkg-config-hashes
$(hide) mkdir -p $(OBJ)/.pkg-config-hashes
$(hide) touch $(PKG_CONFIG_HASHES) $(HOST_PKG_CONFIG_HASHES)
endef
include $(OBJ)/build.mk
define check_for_command
$(shell command -v $1 >/dev/null || (echo "Required command '$1' missing" >&2 && kill $$PPID))
endef
MAKEFLAGS += -r -j$(shell nproc)
.DELETE_ON_ERROR:
$(call check_for_command,ninja)
$(call check_for_command,cmp)
$(call check_for_command,$(PYTHON))
pkg-config-hash = $(shell ($(PKG_CONFIG) --list-all && $(HOST_PKG_CONFIG) --list-all) | md5sum)
build-files = $(shell find . -name .obj -prune -o \( -name 'build.py' -a -type f \) -print) $(wildcard build/*.py) $(wildcard config.py)
build-file-timestamps = $(shell ls -l $(build-files) | md5sum)
# Wipe the build file (forcing a regeneration) if the make environment is different.
# (Conveniently, this includes the pkg-config hash calculated above.)
ignored-variables = MAKE_RESTARTS .VARIABLES MAKECMDGOALS MAKEFLAGS MFLAGS PAGER _ \
DESKTOP_STARTUP_ID XAUTHORITY ICEAUTHORITY SSH_AUTH_SOCK SESSION_MANAGER \
INVOCATION_ID SYSTEMD_EXEC_PID MANAGER_PID SSH_AGENT_PID JOURNAL_STREAM \
GPG_TTY WINDOWID MANAGERPID MAKE_TERMOUT MAKE_TERMERR OLDPWD
$(shell mkdir -p $(OBJ))
$(file >$(OBJ)/newvars.txt,$(foreach v,$(filter-out $(ignored-variables),$(.VARIABLES)),$(v)=$($(v))$(newline)))
$(shell touch $(OBJ)/vars.txt)
#$(shell diff -u $(OBJ)/vars.txt $(OBJ)/newvars.txt >&2)
$(shell cmp -s $(OBJ)/newvars.txt $(OBJ)/vars.txt || (rm -f $(OBJ)/build.ninja && echo "Environment changed --- regenerating" >&2))
$(shell mv $(OBJ)/newvars.txt $(OBJ)/vars.txt)
.PHONY: update-ab
update-ab:
@@ -102,10 +107,19 @@ clean::
@echo CLEAN
$(hide) rm -rf $(OBJ)
compile_commands.json: $(OBJ)/build.ninja
+$(hide) $(NINJA) -f $(OBJ)/build.ninja -t compdb > $@
export PYTHONHASHSEED = 1
build-files = $(shell find . -name 'build.py') $(wildcard build/*.py) $(wildcard config.py)
$(OBJ)/build.mk: Makefile $(build-files) build/ab.mk
$(OBJ)/build.ninja $(OBJ)/build.targets &:
@echo "AB"
@mkdir -p $(OBJ)
$(hide) $(PYTHON) -X pycache_prefix=$(OBJ)/__pycache__ build/ab.py -o $@ build.py \
|| rm -f $@
$(hide) $(PYTHON) -X pycache_prefix=$(OBJ)/__pycache__ build/ab.py \
-o $(OBJ) build.py \
-v $(OBJ)/vars.txt \
|| (rm -f $@ && false)
include $(OBJ)/build.targets
.PHONY: $(ninja-targets)
.NOTPARALLEL:
$(ninja-targets): $(OBJ)/build.ninja
+$(hide) $(NINJA) -f $(OBJ)/build.ninja $@

2
build/ab.ninja Normal file
View File

@@ -0,0 +1,2 @@
rule rule
command = $command

View File

@@ -1,36 +1,32 @@
from collections import namedtuple
from copy import copy
from importlib.machinery import SourceFileLoader, PathFinder, ModuleSpec
from os.path import *
from pathlib import Path
from typing import Iterable
import argparse
import ast
import builtins
from copy import copy
import functools
import hashlib
import importlib
import importlib.util
from importlib.machinery import (
SourceFileLoader,
PathFinder,
ModuleSpec,
)
import inspect
import os
import re
import string
import sys
import hashlib
import re
import ast
from collections import namedtuple
import types
VERBOSE_MK_FILE = False
VERBOSE_NINJA_FILE = False
verbose = False
quiet = False
cwdStack = [""]
targets = {}
unmaterialisedTargets = {} # dict, not set, to get consistent ordering
materialisingStack = []
defaultGlobals = {}
globalId = 1
wordCache = {}
outputTargets = set()
RE_FORMAT_SPEC = re.compile(
r"(?:(?P<fill>[\s\S])?(?P<align>[<>=^]))?"
@@ -52,6 +48,15 @@ sys.path += ["."]
old_import = builtins.__import__
class Environment(types.SimpleNamespace):
def setdefault(self, name, value):
if not hasattr(self, name):
setattr(self, name, value)
G = Environment()
class PathFinderImpl(PathFinder):
def find_spec(self, fullname, path, target=None):
# The second test here is needed for Python 3.9.
@@ -102,27 +107,88 @@ def error(message):
raise ABException(message)
def _undo_escaped_dollar(s, op):
return s.replace(f"$${op}", f"${op}")
class BracketedFormatter(string.Formatter):
def parse(self, format_string):
while format_string:
left, *right = format_string.split("$[", 1)
if not right:
yield (left, None, None, None)
m = re.search(f"(?:[^$]|^)()\\$\\[()", format_string)
if not m:
yield (
_undo_escaped_dollar(format_string, "["),
None,
None,
None,
)
break
right = right[0]
left = format_string[: m.start(1)]
right = format_string[m.end(2) :]
offset = len(right) + 1
try:
ast.parse(right)
except SyntaxError as e:
if not str(e).startswith("unmatched ']'"):
if not str(e).startswith(f"unmatched ']'"):
raise e
offset = e.offset
expr = right[0 : offset - 1]
format_string = right[offset:]
yield (left if left else None, expr, None, None)
yield (
_undo_escaped_dollar(left, "[") if left else None,
expr,
None,
None,
)
class GlobalFormatter(string.Formatter):
def parse(self, format_string):
while format_string:
m = re.search(f"(?:[^$]|^)()\\$\\(([^)]*)\\)()", format_string)
if not m:
yield (
format_string,
None,
None,
None,
)
break
left = format_string[: m.start(1)]
var = m[2]
format_string = format_string[m.end(3) :]
yield (
left if left else None,
var,
None,
None,
)
def get_field(self, name, a1, a2):
return (
getattr(G, name),
False,
)
def format_field(self, value, format_spec):
if not value:
return ""
return str(value)
globalFormatter = GlobalFormatter()
def substituteGlobalVariables(value):
while True:
oldValue = value
value = globalFormatter.format(value)
if value == oldValue:
return _undo_escaped_dollar(value, "(")
def Rule(func):
@@ -187,12 +253,10 @@ def _isiterable(xs):
class Target:
def __init__(self, cwd, name):
if verbose:
print("rule('%s', cwd='%s'" % (name, cwd))
self.name = name
self.localname = self.name.rsplit("+")[-1]
self.traits = set()
self.dir = join("$(OBJ)", name)
self.dir = join(G.OBJ, name)
self.ins = []
self.outs = []
self.deps = []
@@ -232,7 +296,8 @@ class Target:
[selfi.templateexpand(f) for f in filenamesof(value)]
)
return Formatter().format(s)
s = Formatter().format(s)
return substituteGlobalVariables(s)
def materialise(self, replacing=False):
if self not in unmaterialisedTargets:
@@ -341,10 +406,10 @@ def targetof(value, cwd=None):
elif value.startswith("./"):
value = normpath(join(cwd, value))
# Explicit directories are always raw files.
elif value.endswith("/"):
if value.endswith("/"):
return _filetarget(value, cwd)
# Anything starting with a variable expansion is always a raw file.
elif value.startswith("$"):
# Anything in .obj is a raw file.
elif value.startswith(outputdir) or value.startswith(G.OBJ):
return _filetarget(value, cwd)
# If this is not a rule lookup...
@@ -467,79 +532,75 @@ def emit(*args, into=None):
if into is not None:
into += [s]
else:
outputFp.write(s)
ninjaFp.write(s)
def shell(*args):
s = "".join(args) + "\n"
shellFp.write(s)
def emit_rule(self, ins, outs, cmds=[], label=None):
name = self.name
fins_list = filenamesof(ins)
fins = set(fins_list)
fouts = filenamesof(outs)
nonobjs = [f for f in fouts if not f.startswith("$(OBJ)")]
fins = [self.templateexpand(f) for f in set(filenamesof(ins))]
fouts = [self.templateexpand(f) for f in filenamesof(outs)]
global outputTargets
outputTargets.update(fouts)
outputTargets.add(name)
emit("")
if VERBOSE_MK_FILE:
if VERBOSE_NINJA_FILE:
for k, v in self.args.items():
emit(f"# {k} = {v}")
lines = []
if nonobjs:
emit("clean::", into=lines)
emit("\t$(hide) rm -f", *nonobjs, into=lines)
hashable = cmds + fins_list + fouts
hash = hashlib.sha1(bytes("\n".join(hashable), "utf-8")).hexdigest()
hashfile = join(self.dir, f"hash_{hash}")
global globalId
emit(".PHONY:", name, into=lines)
if outs:
outsn = globalId
globalId = globalId + 1
insn = globalId
globalId = globalId + 1
os.makedirs(self.dir, exist_ok=True)
rule = []
emit(f"OUTS_{outsn}", "=", *fouts, into=lines)
emit(f"INS_{insn}", "=", *fins, into=lines)
emit(
name,
":",
hashfile,
f"$(OUTS_{outsn})",
into=lines,
)
emit(f"$(OUTS_{outsn})", ":", hashfile, into=lines)
emit(hashfile, ":", f"$(INS_{insn})", into=lines)
if G.AB_SANDBOX == "yes":
sandbox = join(self.dir, "sandbox")
emit(f"rm -rf {sandbox}", into=rule)
emit(
f"{G.PYTHON} build/_sandbox.py --link -s", sandbox, *fins, into=rule
)
for c in cmds:
emit(f"(cd {sandbox} &&", c, ")", into=rule)
emit(
f"{G.PYTHON} build/_sandbox.py --export -s",
sandbox,
*fouts,
into=rule,
)
else:
for c in cmds:
emit(c, into=rule)
ruletext = "".join(rule)
if len(ruletext) > 7000:
rulehash = hashlib.sha1(ruletext.encode()).hexdigest()
rulef = join(self.dir, f"rule-{rulehash}.sh")
with open(rulef, "wt") as fp:
fp.write("set -e\n")
fp.write(ruletext)
emit("build", *fouts, ":rule", *fins)
emit(" command=sh", rulef)
else:
emit("build", *fouts, ":rule", *fins)
emit(
" command=",
"&&".join([s.strip() for s in rule]).replace("$", "$$"),
)
if label:
emit("\t$(hide)", "$(ECHO) $(PROGRESSINFO)" + label, into=lines)
emit(" description=", label)
emit("build", name, ":phony", *fouts)
sandbox = join(self.dir, "sandbox")
emit("\t$(hide)", f"rm -rf {sandbox}", into=lines)
emit(
"\t$(hide)",
"$(PYTHON) build/_sandbox.py --link -s",
sandbox,
f"$(INS_{insn})",
into=lines,
)
for c in cmds:
emit(f"\t$(hide) cd {sandbox} && (", c, ")", into=lines)
emit(
"\t$(hide)",
"$(PYTHON) build/_sandbox.py --export -s",
sandbox,
f"$(OUTS_{outsn})",
into=lines,
)
else:
assert len(cmds) == 0, "rules with no outputs cannot have commands"
emit(name, ":", *fins, into=lines)
emit("build", name, ":phony", *fins)
outputFp.write("".join(lines))
if outs:
emit(f"\t$(hide) touch {hashfile}")
emit("")
@@ -586,47 +647,66 @@ def export(self, name=None, items: TargetsMap = {}, deps: Targets = []):
dest = self.targetof(dest)
outs += [dest]
destf = filenameof(dest)
destf = self.templateexpand(filenameof(dest))
outputTargets.update([destf])
srcs = filenamesof([src])
assert (
len(srcs) == 1
), "a dependency of an exported file must have exactly one output file"
srcf = self.templateexpand(srcs[0])
subrule = simplerule(
name=f"{self.localname}/{destf}",
cwd=self.cwd,
ins=[srcs[0]],
outs=[destf],
commands=["$(CP) -H %s %s" % (srcs[0], destf)],
label="",
commands=["$(CP) -H %s %s" % (srcf, destf)],
label="EXPORT",
)
subrule.materialise()
self.ins = []
self.outs = deps + outs
outputTargets.add(name)
emit("")
emit(".PHONY:", name)
emit(name, ":", *filenamesof(outs + deps))
emit(
"build",
name,
":phony",
*[self.templateexpand(f) for f in filenamesof(outs + deps)],
)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", action="store_true")
parser.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("-o", "--output")
parser.add_argument("-v", "--varfile")
parser.add_argument("-o", "--outputdir")
parser.add_argument("-D", "--define", action="append", default=[])
parser.add_argument("files", nargs="+")
args = parser.parse_args()
global verbose
verbose = args.verbose
global quiet
quiet = args.quiet
global outputFp
outputFp = open(args.output, "wt")
vardefs = args.define
if args.varfile:
with open(args.varfile, "rt") as fp:
vardefs = vardefs + list(fp)
for line in vardefs:
if "=" in line:
name, value = line.split("=", 1)
G.setdefault(name.strip(), value.strip())
G.setdefault("AB_SANDBOX", "yes")
global ninjaFp, shellFp, outputdir
outputdir = args.outputdir
G.setdefault("OBJ", outputdir)
ninjaFp = open(outputdir + "/build.ninja", "wt")
ninjaFp.write(f"include build/ab.ninja\n")
for k in ["Rule"]:
defaultGlobals[k] = globals()[k]
@@ -641,7 +721,10 @@ def main():
while unmaterialisedTargets:
t = next(iter(unmaterialisedTargets))
t.materialise()
emit("AB_LOADED = 1\n")
with open(outputdir + "/build.targets", "wt") as fp:
fp.write("ninja-targets =")
fp.write(substituteGlobalVariables(" ".join(outputTargets)))
main()

View File

@@ -7,23 +7,22 @@ from build.ab import (
flatten,
simplerule,
emit,
G,
)
from build.utils import filenamesmatchingof, stripext, collectattrs
from build.utils import stripext, collectattrs
from build.toolchain import Toolchain, HostToolchain
from os.path import *
emit(
"""
ifeq ($(OSX),no)
STARTGROUP ?= -Wl,--start-group
ENDGROUP ?= -Wl,--end-group
endif
"""
)
if G.OSX != "yes":
G.STARTGROUP = "-Wl,--start-group"
G.ENDGROUP = "-Wl,--end-group"
else:
G.STARTGROUP = ""
G.ENDGROUP = ""
Toolchain.CC = ["$(CC) -c -o $[outs[0]] $[ins[0]] $(CFLAGS) $[cflags]"]
Toolchain.CPP = ["$(CC) -E -P -o $[outs] $[cflags] -x c $[ins]"]
Toolchain.CXX = ["$(CXX) -c -o $[outs[0]] $[ins[0]] $(CFLAGS) $[cflags]"]
Toolchain.CXX = ["$(CXX) -c -o $[outs[0]] $[ins[0]] $(CXXFLAGS) $[cflags]"]
Toolchain.AR = ["$(AR) cqs $[outs[0]] $[ins]"]
Toolchain.ARXX = ["$(AR) cqs $[outs[0]] $[ins]"]
Toolchain.CLINK = [
@@ -70,13 +69,9 @@ def _toolchain_find_header_targets(deps, initial=[]):
Toolchain.find_c_header_targets = _toolchain_find_header_targets
HostToolchain.CC = [
"$(HOSTCC) -c -o $[outs[0]] $[ins[0]] $(HOSTCFLAGS) $[cflags]"
]
HostToolchain.CC = ["$(HOSTCC) -c -o $[outs[0]] $[ins[0]] $(HOSTCFLAGS) $[cflags]"]
HostToolchain.CPP = ["$(HOSTCC) -E -P -o $[outs] $[cflags] -x c $[ins]"]
HostToolchain.CXX = [
"$(HOSTCXX) -c -o $[outs[0]] $[ins[0]] $(HOSTCFLAGS) $[cflags]"
]
HostToolchain.CXX = ["$(HOSTCXX) -c -o $[outs[0]] $[ins[0]] $(HOSTCFLAGS) $[cflags]"]
HostToolchain.AR = ["$(HOSTAR) cqs $[outs[0]] $[ins]"]
HostToolchain.ARXX = ["$(HOSTAR) cqs $[outs[0]] $[ins]"]
HostToolchain.CLINK = [
@@ -102,9 +97,7 @@ def _indirect(deps, name):
return r
def cfileimpl(
self, name, srcs, deps, suffix, commands, label, toolchain, cflags
):
def cfileimpl(self, name, srcs, deps, suffix, commands, label, toolchain, cflags):
outleaf = "=" + stripext(basename(filenameof(srcs[0]))) + suffix
hdr_deps = toolchain.find_c_header_targets(deps)
@@ -114,9 +107,7 @@ def cfileimpl(
if ("cheader_deps" not in d.args) and ("clibrary_deps" not in d.args)
]
hdr_files = collectattrs(targets=hdr_deps, name="cheader_files")
cflags = collectattrs(
targets=hdr_deps, name="caller_cflags", initial=cflags
)
cflags = collectattrs(targets=hdr_deps, name="caller_cflags", initial=cflags)
t = simplerule(
replaces=self,
@@ -194,7 +185,7 @@ def findsources(self, srcs, deps, cflags, filerule, toolchain, cwd):
for s in flatten(srcs):
objs += [
filerule(
name=join(self.localname, _removeprefix(f, "$(OBJ)/")),
name=join(self.localname, _removeprefix(f, G.OBJ + "/")),
srcs=[f],
deps=deps,
cflags=sorted(set(cflags)),
@@ -239,9 +230,7 @@ def libraryimpl(
i = 0
for dest, src in hdrs.items():
s = filenamesof([src])
assert (
len(s) == 1
), "the target of a header must return exactly one file"
assert len(s) == 1, "the target of a header must return exactly one file"
cs += [f"$(CP) $[ins[{i}]] $[outs[{i}]]"]
outs += ["=" + dest]
@@ -431,20 +420,16 @@ def programimpl(
label,
filerule,
):
cfiles = findsources(
self, srcs, deps, cflags, filerule, toolchain, self.cwd
)
cfiles = findsources(self, srcs, deps, cflags, filerule, toolchain, self.cwd)
lib_deps = toolchain.find_c_library_targets(deps)
libs = collectattrs(targets=lib_deps, name="clibrary_files")
ldflags = collectattrs(
targets=lib_deps, name="caller_ldflags", initial=ldflags
)
ldflags = collectattrs(targets=lib_deps, name="caller_ldflags", initial=ldflags)
simplerule(
replaces=self,
ins=cfiles + libs,
outs=[f"={self.localname}$(EXT)"],
outs=[f"={self.localname}{toolchain.EXE}"],
deps=deps,
label=label,
commands=commands,
@@ -558,9 +543,7 @@ def hostcxxprogram(
def _cppfileimpl(self, name, srcs, deps, cflags, toolchain):
hdr_deps = _indirect(deps, "cheader_deps")
cflags = collectattrs(
targets=hdr_deps, name="caller_cflags", initial=cflags
)
cflags = collectattrs(targets=hdr_deps, name="caller_cflags", initial=cflags)
simplerule(
replaces=self,

View File

@@ -1,4 +1,4 @@
from build.ab import Rule, Target
from build.ab import Rule, Target, G
import os
import subprocess
@@ -31,8 +31,8 @@ class _PkgConfig:
return self.package_properties[p]
TargetPkgConfig = _PkgConfig(os.getenv("PKG_CONFIG"))
HostPkgConfig = _PkgConfig(os.getenv("HOST_PKG_CONFIG"))
TargetPkgConfig = _PkgConfig(G.PKG_CONFIG)
HostPkgConfig = _PkgConfig(G.HOST_PKG_CONFIG)
def _package(self, name, package, fallback, pkgconfig):
@@ -49,9 +49,7 @@ def _package(self, name, package, fallback, pkgconfig):
self.traits.update({"clibrary", "cxxlibrary"})
return
assert (
fallback
), f"Required package '{package}' not installed when materialising target '$[name]'"
assert fallback, f"Required package '{package}' not installed"
if "cheader_deps" in fallback.args:
self.args["cheader_deps"] = fallback.args["cheader_deps"]

View File

@@ -1,18 +1,16 @@
from build.ab import Rule, Targets, emit, simplerule, filenamesof
from build.ab import Rule, Targets, emit, simplerule, filenamesof, G
from build.utils import filenamesmatchingof, collectattrs
from os.path import join, abspath, dirname, relpath
from build.pkg import has_package
emit(
"""
PROTOC ?= protoc
HOSTPROTOC ?= protoc
"""
)
G.setdefault("PROTOC", "protoc")
G.setdefault("PROTOC_SEPARATOR", ":")
G.setdefault("HOSTPROTOC", "hostprotoc")
assert has_package("protobuf"), "required package 'protobuf' not installed"
def _getprotodeps(deps):
r = set()
for d in deps:
@@ -23,7 +21,7 @@ def _getprotodeps(deps):
@Rule
def proto(self, name, srcs: Targets = [], deps: Targets = []):
protodeps = _getprotodeps(deps)
descriptorlist = ":".join(
descriptorlist = (G.PROTOC_SEPARATOR).join(
[
relpath(f, start=self.dir)
for f in filenamesmatchingof(protodeps, "*.descriptor")
@@ -50,7 +48,7 @@ def proto(self, name, srcs: Targets = [], deps: Targets = []):
f"--descriptor_set_out={self.localname}.descriptor",
]
+ (
[f"--descriptor_set_in={descriptorlist}"]
[f"--descriptor_set_in='{descriptorlist}'"]
if descriptorlist
else []
)
@@ -93,7 +91,7 @@ def protocc(self, name, srcs: Targets = [], deps: Targets = []):
outs += ["=" + cc, "=" + h]
protodeps = _getprotodeps(deps + srcs)
descriptorlist = ":".join(
descriptorlist = G.PROTOC_SEPARATOR.join(
[
relpath(f, start=self.dir)
for f in filenamesmatchingof(protodeps, "*.descriptor")
@@ -114,7 +112,7 @@ def protocc(self, name, srcs: Targets = [], deps: Targets = []):
"$(PROTOC)",
"--proto_path=.",
"--cpp_out=.",
f"--descriptor_set_in={descriptorlist}",
f"--descriptor_set_in='{descriptorlist}'",
]
+ protos
)

View File

@@ -1,5 +1,10 @@
import platform
_is_windows = (platform.system() == "Windows")
class Toolchain:
PREFIX = ""
EXE = ".exe" if _is_windows else ""
class HostToolchain(Toolchain):

View File

@@ -7,10 +7,13 @@ from build.ab import (
cwdStack,
error,
simplerule,
G
)
from os.path import relpath, splitext, join, basename, isfile
from glob import iglob
import fnmatch
import subprocess
import shutil
def filenamesmatchingof(xs, pattern):
@@ -51,6 +54,16 @@ def itemsof(pattern, root=None, cwd=None):
return result
def does_command_exist(cmd):
basecmd = cmd.strip().split()[0]
return shutil.which(basecmd)
def shell(cmd):
r = subprocess.check_output([G.SHELL, "-c", cmd])
return r.decode("utf-8").strip()
@Rule
def objectify(self, name, src: Target, symbol):
simplerule(

View File

@@ -7,9 +7,7 @@ from build.ab import (
@Rule
def zip(
self, name, flags="", items: TargetsMap = {}, extension="zip", label="ZIP"
):
def zip(self, name, flags="", items: TargetsMap = {}, extension="zip", label="ZIP"):
cs = ["$(PYTHON) build/_zip.py -z $[outs]"]
ins = []

View File

@@ -35,7 +35,7 @@ clibrary(
"./config.h",
"./src/adflib.h",
],
cflags=["-Idep/adflib", "-Idep/adflib/src"],
cflags=["-Wno-stringop-overflow"],
hdrs={
"adf_blk.h": "./src/adf_blk.h",
"adf_defs.h": "./src/adf_defs.h",

2
dep/alphanum/UPSTREAM.md Normal file
View File

@@ -0,0 +1,2 @@
Downloaded from:
https://web.archive.org/web/20210918044134/http://davekoelle.com/files/alphanum.hpp

450
dep/alphanum/alphanum.h Normal file
View File

@@ -0,0 +1,450 @@
#ifndef ALPHANUM__HPP
#define ALPHANUM__HPP
/*
Released under the MIT License - https://opensource.org/licenses/MIT
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.
*/
/* $Header: /code/doj/alphanum.hpp,v 1.3 2008/01/28 23:06:47 doj Exp $ */
#include <cassert>
#include <functional>
#include <string>
#include <sstream>
#ifdef ALPHANUM_LOCALE
#include <cctype>
#endif
#ifdef DOJDEBUG
#include <iostream>
#include <typeinfo>
#endif
// TODO: make comparison with hexadecimal numbers. Extend the alphanum_comp() function by traits to choose between decimal and hexadecimal.
namespace doj
{
// anonymous namespace for functions we use internally. But if you
// are coding in C, you can use alphanum_impl() directly, since it
// uses not C++ features.
namespace {
// if you want to honour the locale settings for detecting digit
// characters, you should define ALPHANUM_LOCALE
#ifdef ALPHANUM_LOCALE
/** wrapper function for ::isdigit() */
bool alphanum_isdigit(int c)
{
return isdigit(c);
}
#else
/** this function does not consider the current locale and only
works with ASCII digits.
@return true if c is a digit character
*/
bool alphanum_isdigit(const char c)
{
return c>='0' && c<='9';
}
#endif
/**
compare l and r with strcmp() semantics, but using
the "Alphanum Algorithm". This function is designed to read
through the l and r strings only one time, for
maximum performance. It does not allocate memory for
substrings. It can either use the C-library functions isdigit()
and atoi() to honour your locale settings, when recognizing
digit characters when you "#define ALPHANUM_LOCALE=1" or use
it's own digit character handling which only works with ASCII
digit characters, but provides better performance.
@param l NULL-terminated C-style string
@param r NULL-terminated C-style string
@return negative if l<r, 0 if l equals r, positive if l>r
*/
int alphanum_impl(const char *l, const char *r)
{
enum mode_t { STRING, NUMBER } mode=STRING;
while(*l && *r)
{
if(mode == STRING)
{
char l_char, r_char;
while((l_char=*l) && (r_char=*r))
{
// check if this are digit characters
const bool l_digit=alphanum_isdigit(l_char), r_digit=alphanum_isdigit(r_char);
// if both characters are digits, we continue in NUMBER mode
if(l_digit && r_digit)
{
mode=NUMBER;
break;
}
// if only the left character is a digit, we have a result
if(l_digit) return -1;
// if only the right character is a digit, we have a result
if(r_digit) return +1;
// compute the difference of both characters
const int diff=l_char - r_char;
// if they differ we have a result
if(diff != 0) return diff;
// otherwise process the next characters
++l;
++r;
}
}
else // mode==NUMBER
{
#ifdef ALPHANUM_LOCALE
// get the left number
char *end;
unsigned long l_int=strtoul(l, &end, 0);
l=end;
// get the right number
unsigned long r_int=strtoul(r, &end, 0);
r=end;
#else
// get the left number
unsigned long l_int=0;
while(*l && alphanum_isdigit(*l))
{
// TODO: this can overflow
l_int=l_int*10 + *l-'0';
++l;
}
// get the right number
unsigned long r_int=0;
while(*r && alphanum_isdigit(*r))
{
// TODO: this can overflow
r_int=r_int*10 + *r-'0';
++r;
}
#endif
// if the difference is not equal to zero, we have a comparison result
const long diff=l_int-r_int;
if(diff != 0)
return diff;
// otherwise we process the next substring in STRING mode
mode=STRING;
}
}
if(*r) return -1;
if(*l) return +1;
return 0;
}
}
/**
Compare left and right with the same semantics as strcmp(), but with the
"Alphanum Algorithm" which produces more human-friendly
results. The classes lT and rT must implement "std::ostream
operator<< (std::ostream&, const Ty&)".
@return negative if left<right, 0 if left==right, positive if left>right.
*/
template <typename lT, typename rT>
int alphanum_comp(const lT& left, const rT& right)
{
#ifdef DOJDEBUG
std::clog << "alphanum_comp<" << typeid(left).name() << "," << typeid(right).name() << "> " << left << "," << right << std::endl;
#endif
std::ostringstream l; l << left;
std::ostringstream r; r << right;
return alphanum_impl(l.str().c_str(), r.str().c_str());
}
/**
Compare l and r with the same semantics as strcmp(), but with
the "Alphanum Algorithm" which produces more human-friendly
results.
@return negative if l<r, 0 if l==r, positive if l>r.
*/
template <>
int alphanum_comp<std::string>(const std::string& l, const std::string& r)
{
#ifdef DOJDEBUG
std::clog << "alphanum_comp<std::string,std::string> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l.c_str(), r.c_str());
}
////////////////////////////////////////////////////////////////////////////
// now follow a lot of overloaded alphanum_comp() functions to get a
// direct call to alphanum_impl() upon the various combinations of c
// and c++ strings.
/**
Compare l and r with the same semantics as strcmp(), but with
the "Alphanum Algorithm" which produces more human-friendly
results.
@return negative if l<r, 0 if l==r, positive if l>r.
*/
int alphanum_comp(char* l, char* r)
{
assert(l);
assert(r);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<char*,char*> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l, r);
}
int alphanum_comp(const char* l, const char* r)
{
assert(l);
assert(r);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<const char*,const char*> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l, r);
}
int alphanum_comp(char* l, const char* r)
{
assert(l);
assert(r);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<char*,const char*> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l, r);
}
int alphanum_comp(const char* l, char* r)
{
assert(l);
assert(r);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<const char*,char*> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l, r);
}
int alphanum_comp(const std::string& l, char* r)
{
assert(r);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<std::string,char*> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l.c_str(), r);
}
int alphanum_comp(char* l, const std::string& r)
{
assert(l);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<char*,std::string> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l, r.c_str());
}
int alphanum_comp(const std::string& l, const char* r)
{
assert(r);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<std::string,const char*> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l.c_str(), r);
}
int alphanum_comp(const char* l, const std::string& r)
{
assert(l);
#ifdef DOJDEBUG
std::clog << "alphanum_comp<const char*,std::string> " << l << "," << r << std::endl;
#endif
return alphanum_impl(l, r.c_str());
}
////////////////////////////////////////////////////////////////////////////
/**
Functor class to compare two objects with the "Alphanum
Algorithm". If the objects are no std::string, they must
implement "std::ostream operator<< (std::ostream&, const Ty&)".
*/
template<class Ty>
struct alphanum_less
{
bool operator()(const Ty& left, const Ty& right) const
{
return alphanum_comp(left, right) < 0;
}
};
}
#ifdef TESTMAIN
#include <algorithm>
#include <iostream>
#include <iterator>
#include <map>
#include <set>
#include <vector>
int main()
{
// testcases for the algorithm
assert(doj::alphanum_comp("","") == 0);
assert(doj::alphanum_comp("","a") < 0);
assert(doj::alphanum_comp("a","") > 0);
assert(doj::alphanum_comp("a","a") == 0);
assert(doj::alphanum_comp("","9") < 0);
assert(doj::alphanum_comp("9","") > 0);
assert(doj::alphanum_comp("1","1") == 0);
assert(doj::alphanum_comp("1","2") < 0);
assert(doj::alphanum_comp("3","2") > 0);
assert(doj::alphanum_comp("a1","a1") == 0);
assert(doj::alphanum_comp("a1","a2") < 0);
assert(doj::alphanum_comp("a2","a1") > 0);
assert(doj::alphanum_comp("a1a2","a1a3") < 0);
assert(doj::alphanum_comp("a1a2","a1a0") > 0);
assert(doj::alphanum_comp("134","122") > 0);
assert(doj::alphanum_comp("12a3","12a3") == 0);
assert(doj::alphanum_comp("12a1","12a0") > 0);
assert(doj::alphanum_comp("12a1","12a2") < 0);
assert(doj::alphanum_comp("a","aa") < 0);
assert(doj::alphanum_comp("aaa","aa") > 0);
assert(doj::alphanum_comp("Alpha 2","Alpha 2") == 0);
assert(doj::alphanum_comp("Alpha 2","Alpha 2A") < 0);
assert(doj::alphanum_comp("Alpha 2 B","Alpha 2") > 0);
assert(doj::alphanum_comp(1,1) == 0);
assert(doj::alphanum_comp(1,2) < 0);
assert(doj::alphanum_comp(2,1) > 0);
assert(doj::alphanum_comp(1.2,3.14) < 0);
assert(doj::alphanum_comp(3.14,2.71) > 0);
assert(doj::alphanum_comp(true,true) == 0);
assert(doj::alphanum_comp(true,false) > 0);
assert(doj::alphanum_comp(false,true) < 0);
std::string str("Alpha 2");
assert(doj::alphanum_comp(str,"Alpha 2") == 0);
assert(doj::alphanum_comp(str,"Alpha 2A") < 0);
assert(doj::alphanum_comp("Alpha 2 B",str) > 0);
assert(doj::alphanum_comp(str,strdup("Alpha 2")) == 0);
assert(doj::alphanum_comp(str,strdup("Alpha 2A")) < 0);
assert(doj::alphanum_comp(strdup("Alpha 2 B"),str) > 0);
#if 1
// show usage of the comparison functor with a set
std::set<std::string, doj::alphanum_less<std::string> > s;
s.insert("Xiph Xlater 58");
s.insert("Xiph Xlater 5000");
s.insert("Xiph Xlater 500");
s.insert("Xiph Xlater 50");
s.insert("Xiph Xlater 5");
s.insert("Xiph Xlater 40");
s.insert("Xiph Xlater 300");
s.insert("Xiph Xlater 2000");
s.insert("Xiph Xlater 10000");
s.insert("QRS-62F Intrinsia Machine");
s.insert("QRS-62 Intrinsia Machine");
s.insert("QRS-60F Intrinsia Machine");
s.insert("QRS-60 Intrinsia Machine");
s.insert("Callisto Morphamax 7000 SE2");
s.insert("Callisto Morphamax 7000 SE");
s.insert("Callisto Morphamax 7000");
s.insert("Callisto Morphamax 700");
s.insert("Callisto Morphamax 600");
s.insert("Callisto Morphamax 5000");
s.insert("Callisto Morphamax 500");
s.insert("Callisto Morphamax");
s.insert("Alpha 2A-900");
s.insert("Alpha 2A-8000");
s.insert("Alpha 2A");
s.insert("Alpha 200");
s.insert("Alpha 2");
s.insert("Alpha 100");
s.insert("Allegia 60 Clasteron");
s.insert("Allegia 52 Clasteron");
s.insert("Allegia 51B Clasteron");
s.insert("Allegia 51 Clasteron");
s.insert("Allegia 500 Clasteron");
s.insert("Allegia 50 Clasteron");
s.insert("40X Radonius");
s.insert("30X Radonius");
s.insert("20X Radonius Prime");
s.insert("20X Radonius");
s.insert("200X Radonius");
s.insert("10X Radonius");
s.insert("1000X Radonius Maximus");
// print sorted set to cout
std::copy(s.begin(), s.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
// show usage of comparision functor with a map
typedef std::map<std::string, int, doj::alphanum_less<std::string> > m_t;
m_t m;
m["z1.doc"]=1;
m["z10.doc"]=2;
m["z100.doc"]=3;
m["z101.doc"]=4;
m["z102.doc"]=5;
m["z11.doc"]=6;
m["z12.doc"]=7;
m["z13.doc"]=8;
m["z14.doc"]=9;
m["z15.doc"]=10;
m["z16.doc"]=11;
m["z17.doc"]=12;
m["z18.doc"]=13;
m["z19.doc"]=14;
m["z2.doc"]=15;
m["z20.doc"]=16;
m["z3.doc"]=17;
m["z4.doc"]=18;
m["z5.doc"]=19;
m["z6.doc"]=20;
m["z7.doc"]=21;
m["z8.doc"]=22;
m["z9.doc"]=23;
// print sorted map to cout
for(m_t::iterator i=m.begin(); i!=m.end(); ++i)
std::cout << i->first << '\t' << i->second << std::endl;
// show usage of comparison functor with an STL algorithm on a vector
std::vector<std::string> v;
// vector contents are reversed sorted contents of the old set
std::copy(s.rbegin(), s.rend(), std::back_inserter(v));
// now sort the vector with the algorithm
std::sort(v.begin(), v.end(), doj::alphanum_less<std::string>());
// and print the vector to cout
std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
#endif
return 0;
}
#endif
#endif

8
dep/alphanum/build.py Normal file
View File

@@ -0,0 +1,8 @@
from build.c import clibrary
clibrary(
name="alphanum",
srcs=[],
hdrs={"dep/alphanum/alphanum.h": "./alphanum.h"},
)

1
dep/cli11 Submodule

Submodule dep/cli11 added at 89dc726939

1
dep/imgui Submodule

Submodule dep/imgui added at 4d216d4510

1
dep/imhex Submodule

Submodule dep/imhex added at a76eae2c11

23
dep/lexy/LICENSE Normal file
View File

@@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

175
dep/lexy/README.adoc Normal file
View File

@@ -0,0 +1,175 @@
= lexy
ifdef::env-github[]
image:https://img.shields.io/endpoint?url=https%3A%2F%2Fwww.jonathanmueller.dev%2Fproject%2Flexy%2Findex.json[Project Status,link=https://www.jonathanmueller.dev/project/]
image:https://github.com/foonathan/lexy/workflows/Main%20CI/badge.svg[Build Status]
image:https://img.shields.io/badge/try_it_online-blue[Playground,link=https://lexy.foonathan.net/playground]
endif::[]
lexy is a parser combinator library for {cpp}17 and onwards.
It allows you to write a parser by specifying it in a convenient {cpp} DSL,
which gives you all the flexibility and control of a handwritten parser without any of the manual work.
ifdef::env-github[]
*Documentation*: https://lexy.foonathan.net/[lexy.foonathan.net]
endif::[]
.IPv4 address parser
--
ifndef::env-github[]
[.godbolt-example]
.+++<a href="https://godbolt.org/z/scvajjE17", title="Try it online">{{< svg "icons/play.svg" >}}</a>+++
endif::[]
[source,cpp]
----
namespace dsl = lexy::dsl;
// Parse an IPv4 address into a `std::uint32_t`.
struct ipv4_address
{
// What is being matched.
static constexpr auto rule = []{
// Match a sequence of (decimal) digits and convert it into a std::uint8_t.
auto octet = dsl::integer<std::uint8_t>;
// Match four of them separated by periods.
return dsl::times<4>(octet, dsl::sep(dsl::period)) + dsl::eof;
}();
// How the matched output is being stored.
static constexpr auto value
= lexy::callback<std::uint32_t>([](std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d) {
return (a << 24) | (b << 16) | (c << 8) | d;
});
};
----
--
== Features
Full control::
* *Describe the parser, not some abstract grammar*:
Unlike parser generators that use some table driven magic for parsing, lexy's grammar is just syntax sugar for a hand-written recursive descent parser.
The parsing algorithm does exactly what you've instructed it to do -- no more ambiguities or weird shift/reduce errors!
* *No implicit backtracking or lookahead*:
It will only backtrack when you say it should, and only lookahead when and how far you want it.
Don't worry about rules that have side-effects, they won't be executed unnecessarily thanks to the user-specified lookahead conditions.
https://lexy.foonathan.net/playground?example=peek[Try it online].
* *Escape hatch for manual parsing*:
Sometimes you want to parse something that can't be expressed easily with lexy's facilities.
Don't worry, you can integrate a hand-written parser into the grammar at any point.
https://lexy.foonathan.net/playground/?example=scan[Try it online].
* *Tracing*:
Figure out why the grammar isn't working the way you want it to.
https://lexy.foonathan.net/playground/?example=trace&mode=trace[Try it online].
Easily integrated::
* *A pure {cpp} DSL*:
No need to use an external grammar file; embed the grammar directly in your {cpp} project using operator overloading and functions.
* *Bring your own data structures*:
You can directly store results into your own types and have full control over all heap allocations.
* *Fully `constexpr` parsing*:
You want to parse a string literal at compile-time? You can do so.
* *Minimal standard library dependencies*:
The core parsing library only depends on fundamental headers such as `<type_traits>` or `<cstddef>`; no big includes like `<vector>` or `<algorithm>`.
* *Header-only core library* (by necessity, not by choice -- it's `constexpr` after all).
ifdef::env-github[Designed for text::]
ifndef::env-github[Designed for text (e.g. {{< github-example json >}}, {{< github-example xml >}}, {{< github-example email >}}) ::]
* *Unicode support*: parse UTF-8, UTF-16, or UTF-32, and access the Unicode character database to query char classes or perform case folding.
https://lexy.foonathan.net/playground?example=identifier-unicode[Try it online].
* *Convenience*:
Built-in rules for parsing nested structures, quotes and escape sequences.
https://lexy.foonathan.net/playground?example=parenthesized[Try it online].
* *Automatic whitespace skipping*:
No need to manually handle whitespace or comments.
https://lexy.foonathan.net/playground/?example=whitespace_comment[Try it online].
ifdef::env-github[Designed for programming languages::]
ifndef::env-github[Designed for programming languages (e.g. {{< github-example calculator >}}, {{< github-example shell >}})::]
* *Keyword and identifier parsing*:
Reserve a set of keywords that won't be matched as regular identifiers.
https://lexy.foonathan.net/playground/?example=reserved_identifier[Try it online].
* *Operator parsing*:
Parse unary/binary operators with different precedences and associativity, including chained comparisons `a < b < c`.
https://lexy.foonathan.net/playground/?example=expr[Try it online].
* *Automatic error recovery*:
Log an error, recover, and continue parsing!
https://lexy.foonathan.net/playground/?example=recover[Try it online].
ifdef::env-github[Designed for binary input::]
ifndef::env-github[Designed for binary input (e.g. {{< github-example protobuf >}})::]
* *Bytes*: Rules for parsing `N` bytes or Nbit big/little endian integer.
* *Bits*: Rules for parsing individual bit patterns.
* *Blobs*: Rules for parsing TLV formats.
== FAQ
Why should I use lexy over XYZ?::
lexy is closest to other PEG parsers.
However, they usually do more implicit backtracking, which can hurt performance and you need to be very careful with rules that have side-effects.
This is not the case for lexy, where backtracking is controlled using branch conditions.
lexy also gives you a lot of control over error reporting, supports error recovery, special support for operator precedence parsing, and other advanced features.
http://boost-spirit.com/home/[Boost.Spirit]:::
The main difference: it is not a Boost library.
In addition, Boost.Spirit is quite old and doesn't support e.g. non-common ranges as input.
Boost.Spirit also eagerly creates attributes from the rules, which can lead to nested tuples/variants while lexy uses callbacks which enables zero-copy parsing directly into your own data structure.
However, lexy's grammar is more verbose and designed to parser bigger grammars instead of the small one-off rules that Boost.Spirit is good at.
https://github.com/taocpp/PEGTL[PEGTL]:::
PEGTL is very similar and was a big inspiration.
The biggest difference is that lexy uses an operator based DSL instead of inheriting from templated classes as PEGTL does;
depending on your preference this can be an advantage or disadvantage.
Hand-written Parsers:::
Writing a handwritten parser is more manual work and error prone.
lexy automates that away without having to sacrifice control.
You can use it to quickly prototype a parser and then slowly replace more and more with a handwritten parser over time;
mixing a hand-written parser and a lexy grammar works seamlessly.
How bad are the compilation times?::
They're not as bad as you might expect (in debug mode, that is).
+
The example JSON parser compiles in about 2s on my machine.
If we remove all the lexy specific parts and just benchmark the time it takes for the compiler to process the datastructure (and stdlib includes),
that takes about 700ms.
If we validate JSON only instead of parsing it, so remove the data structures and keep only the lexy specific parts, we're looking at about 840ms.
+
Keep in mind, that you can fully isolate lexy in a single translation unit that only needs to be touched when you change the parser.
You can also split a lexy grammar into multiple translation units using the `dsl::subgrammar` rule.
How bad are the {cpp} error messages if you mess something up?::
They're certainly worse than the error message lexy gives you.
The big problem here is that the first line gives you the error, followed by dozens of template instantiations, which end at your `lexy::parse` call.
Besides providing an external tool to filter those error messages, there is nothing I can do about that.
How fast is it?::
Benchmarks are available in the `benchmarks/` directory.
A sample result of the JSON validator benchmark which compares the example JSON parser with various other implementations is available https://lexy.foonathan.net/benchmark_json/[here].
Why is it called lexy?::
I previously had a tokenizer library called foonathan/lex.
I've tried adding a parser to it, but found that the line between pure tokenization and parsing has become increasingly blurred.
lexy is a re-imagination on of the parser I've added to foonathan/lex, and I've simply kept a similar name.
ifdef::env-github[]
== Documentation
The documentation, including tutorials, reference documentation, and an interactive playground can be found at https://lexy.foonathan.net/[lexy.foonathan.net].
A minimal `CMakeLists.txt` that uses lexy can look like this:
.`CMakeLists.txt`
```cmake
project(lexy-example)
include(FetchContent)
FetchContent_Declare(lexy URL https://lexy.foonathan.net/download/lexy-src.zip)
FetchContent_MakeAvailable(lexy)
add_executable(lexy_example)
target_sources(lexy_example PRIVATE main.cpp)
target_link_libraries(lexy_example PRIVATE foonathan::lexy)
```
endif::[]

2
dep/lexy/UPSTREAM.md Normal file
View File

@@ -0,0 +1,2 @@
This is a heavily truncated copy of https://github.com/foonathan/lexy, commit
20926cf.

11
dep/lexy/build.py Normal file
View File

@@ -0,0 +1,11 @@
from build.c import cxxlibrary
from glob import glob
cxxlibrary(
name="lexy",
srcs=[],
hdrs={
h: f"./include/{h}"
for h in glob("**/*.hpp", root_dir="dep/lexy/include", recursive=True)
},
)

View File

@@ -0,0 +1,68 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_ANY_REF_HPP_INCLUDED
#define LEXY_DETAIL_ANY_REF_HPP_INCLUDED
#include <lexy/_detail/config.hpp>
// Essentially a void*, but we can cast it in a constexpr context.
// The cost is an extra layer of indirection.
namespace lexy::_detail
{
template <typename T>
class any_holder;
// Store a pointer to this instead of a void*.
class any_base
{
public:
any_base(const any_base&) = delete;
any_base& operator=(const any_base&) = delete;
template <typename T>
constexpr T& get() noexcept
{
return static_cast<any_holder<T>*>(this)->get();
}
template <typename T>
constexpr const T& get() const noexcept
{
return static_cast<const any_holder<T>*>(this)->get();
}
private:
constexpr any_base() = default;
~any_base() = default;
template <typename T>
friend class any_holder;
};
using any_ref = any_base*;
using any_cref = const any_base*;
// Need to store the object in here.
template <typename T>
class any_holder : public any_base
{
public:
constexpr explicit any_holder(T&& obj) : _obj(LEXY_MOV(obj)) {}
constexpr T& get() noexcept
{
return _obj;
}
constexpr const T& get() const noexcept
{
return _obj;
}
private:
T _obj;
};
} // namespace lexy::_detail
#endif // LEXY_DETAIL_ANY_REF_HPP_INCLUDED

View File

@@ -0,0 +1,51 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_ASSERT_HPP_INCLUDED
#define LEXY_DETAIL_ASSERT_HPP_INCLUDED
#include <lexy/_detail/config.hpp>
#ifndef LEXY_ENABLE_ASSERT
// By default, enable assertions if NDEBUG is not defined.
# if NDEBUG
# define LEXY_ENABLE_ASSERT 0
# else
# define LEXY_ENABLE_ASSERT 1
# endif
#endif
#if LEXY_ENABLE_ASSERT
// We want assertions: use assert() if that's available, otherwise abort.
// We don't use assert() directly as that's not constexpr.
# if NDEBUG
# include <cstdlib>
# define LEXY_PRECONDITION(Expr) ((Expr) ? void(0) : std::abort())
# define LEXY_ASSERT(Expr, Msg) ((Expr) ? void(0) : std::abort())
# else
# include <cassert>
# define LEXY_PRECONDITION(Expr) ((Expr) ? void(0) : assert(Expr))
# define LEXY_ASSERT(Expr, Msg) ((Expr) ? void(0) : assert((Expr) && (Msg)))
# endif
#else
// We don't want assertions.
# define LEXY_PRECONDITION(Expr) static_cast<void>(sizeof(Expr))
# define LEXY_ASSERT(Expr, Msg) static_cast<void>(sizeof(Expr))
#endif
#endif // LEXY_DETAIL_ASSERT_HPP_INCLUDED

View File

@@ -0,0 +1,160 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED
#define LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED
#include <cstring>
#include <lexy/_detail/assert.hpp>
#include <lexy/_detail/config.hpp>
#include <lexy/_detail/iterator.hpp>
#include <new>
namespace lexy::_detail
{
// Builds a buffer: it has a read are and a write area.
// The characters in the read area are already valid and can be read.
// The characters in the write area are not valid, but can be written too.
template <typename T>
class buffer_builder
{
static_assert(std::is_trivial_v<T>);
static constexpr std::size_t total_size_bytes = 1024;
static constexpr std::size_t stack_buffer_size
= (total_size_bytes - 3 * sizeof(T*)) / sizeof(T);
static constexpr auto growth_factor = 2;
public:
buffer_builder() noexcept : _data(_stack_buffer), _read_size(0), _write_size(stack_buffer_size)
{
static_assert(sizeof(*this) == total_size_bytes, "invalid buffer size calculation");
}
~buffer_builder() noexcept
{
// Free memory if we allocated any.
if (_data != _stack_buffer)
::operator delete(_data);
}
buffer_builder(const buffer_builder&) = delete;
buffer_builder& operator=(const buffer_builder&) = delete;
// The total capacity: read + write.
std::size_t capacity() const noexcept
{
return _read_size + _write_size;
}
// The read area.
const T* read_data() const noexcept
{
return _data;
}
std::size_t read_size() const noexcept
{
return _read_size;
}
// The write area.
T* write_data() noexcept
{
return _data + _read_size;
}
std::size_t write_size() const noexcept
{
return _write_size;
}
// Clears the read area.
void clear() noexcept
{
_write_size += _read_size;
_read_size = 0;
}
// Takes the first n characters of the write area and appends them to the read area.
void commit(std::size_t n) noexcept
{
LEXY_PRECONDITION(n <= _write_size);
_read_size += n;
_write_size -= n;
}
// Increases the write area, invalidates all pointers.
void grow()
{
const auto cur_cap = capacity();
const auto new_cap = growth_factor * cur_cap;
// Allocate new memory.
auto memory = static_cast<T*>(::operator new(new_cap * sizeof(T)));
// Copy the read area into the new memory.
std::memcpy(memory, _data, _read_size);
// Release the old memory, if there was any.
if (_data != _stack_buffer)
::operator delete(_data);
// Update for the new area.
_data = memory;
// _read_size hasn't been changed
_write_size = new_cap - _read_size;
}
//=== iterator ===//
// Stable iterator over the memory.
class stable_iterator : public forward_iterator_base<stable_iterator, const T>
{
public:
constexpr stable_iterator() = default;
explicit constexpr stable_iterator(const _detail::buffer_builder<T>& buffer,
std::size_t idx) noexcept
: _buffer(&buffer), _idx(idx)
{}
constexpr const T& deref() const noexcept
{
LEXY_PRECONDITION(_idx != _buffer->read_size());
return _buffer->read_data()[_idx];
}
constexpr void increment() noexcept
{
LEXY_PRECONDITION(_idx != _buffer->read_size());
++_idx;
}
constexpr bool equal(stable_iterator rhs) const noexcept
{
if (!_buffer || !rhs._buffer)
return !_buffer && !rhs._buffer;
else
{
LEXY_PRECONDITION(_buffer == rhs._buffer);
return _idx == rhs._idx;
}
}
constexpr std::size_t index() const noexcept
{
return _idx;
}
private:
const _detail::buffer_builder<T>* _buffer = nullptr;
std::size_t _idx = 0;
};
private:
T* _data;
std::size_t _read_size;
std::size_t _write_size;
T _stack_buffer[stack_buffer_size];
};
} // namespace lexy::_detail
#endif // LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED

View File

@@ -0,0 +1,368 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_CODE_POINT_HPP_INCLUDED
#define LEXY_DETAIL_CODE_POINT_HPP_INCLUDED
#include <lexy/input/base.hpp>
//=== encoding ===//
namespace lexy::_detail
{
template <typename Encoding>
constexpr std::size_t encode_code_point(char32_t cp, typename Encoding::char_type* buffer,
std::size_t size)
{
if constexpr (std::is_same_v<Encoding, lexy::ascii_encoding>)
{
LEXY_PRECONDITION(size >= 1);
*buffer = char(cp);
return 1;
}
else if constexpr (std::is_same_v<Encoding,
lexy::utf8_encoding> //
|| std::is_same_v<Encoding, lexy::utf8_char_encoding>)
{
using char_type = typename Encoding::char_type;
// Taken from http://www.herongyang.com/Unicode/UTF-8-UTF-8-Encoding-Algorithm.html.
if (cp <= 0x7F)
{
LEXY_PRECONDITION(size >= 1);
buffer[0] = char_type(cp);
return 1;
}
else if (cp <= 0x07'FF)
{
LEXY_PRECONDITION(size >= 2);
auto first = (cp >> 6) & 0x1F;
auto second = (cp >> 0) & 0x3F;
buffer[0] = char_type(0xC0 | first);
buffer[1] = char_type(0x80 | second);
return 2;
}
else if (cp <= 0xFF'FF)
{
LEXY_PRECONDITION(size >= 3);
auto first = (cp >> 12) & 0x0F;
auto second = (cp >> 6) & 0x3F;
auto third = (cp >> 0) & 0x3F;
buffer[0] = char_type(0xE0 | first);
buffer[1] = char_type(0x80 | second);
buffer[2] = char_type(0x80 | third);
return 3;
}
else
{
LEXY_PRECONDITION(size >= 4);
auto first = (cp >> 18) & 0x07;
auto second = (cp >> 12) & 0x3F;
auto third = (cp >> 6) & 0x3F;
auto fourth = (cp >> 0) & 0x3F;
buffer[0] = char_type(0xF0 | first);
buffer[1] = char_type(0x80 | second);
buffer[2] = char_type(0x80 | third);
buffer[3] = char_type(0x80 | fourth);
return 4;
}
}
else if constexpr (std::is_same_v<Encoding, lexy::utf16_encoding>)
{
if (cp <= 0xFF'FF)
{
LEXY_PRECONDITION(size >= 1);
buffer[0] = char16_t(cp);
return 1;
}
else
{
// Algorithm implemented from
// https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF.
LEXY_PRECONDITION(size >= 2);
auto u_prime = cp - 0x1'0000;
auto high_ten_bits = u_prime >> 10;
auto low_ten_bits = u_prime & 0b0000'0011'1111'1111;
buffer[0] = char16_t(0xD800 + high_ten_bits);
buffer[1] = char16_t(0xDC00 + low_ten_bits);
return 2;
}
}
else if constexpr (std::is_same_v<Encoding, lexy::utf32_encoding>)
{
LEXY_PRECONDITION(size >= 1);
*buffer = cp;
return 1;
}
else
{
static_assert(lexy::_detail::error<Encoding>,
"cannot encode a code point in this encoding");
(void)cp;
(void)buffer;
(void)size;
return 0;
}
}
} // namespace lexy::_detail
//=== parsing ===//
namespace lexy::_detail
{
enum class cp_error
{
success,
eof,
leads_with_trailing,
missing_trailing,
surrogate,
overlong_sequence,
out_of_range,
};
template <typename Reader>
struct cp_result
{
char32_t cp;
cp_error error;
typename Reader::marker end;
};
template <typename Reader>
constexpr cp_result<Reader> parse_code_point(Reader reader)
{
if constexpr (std::is_same_v<typename Reader::encoding, lexy::ascii_encoding>)
{
if (reader.peek() == Reader::encoding::eof())
return {{}, cp_error::eof, reader.current()};
auto cur = reader.peek();
reader.bump();
auto cp = static_cast<char32_t>(cur);
if (cp <= 0x7F)
return {cp, cp_error::success, reader.current()};
else
return {cp, cp_error::out_of_range, reader.current()};
}
else if constexpr (std::is_same_v<typename Reader::encoding, lexy::utf8_encoding> //
|| std::is_same_v<typename Reader::encoding, lexy::utf8_char_encoding>)
{
using uchar_t = unsigned char;
constexpr auto payload_lead1 = 0b0111'1111;
constexpr auto payload_lead2 = 0b0001'1111;
constexpr auto payload_lead3 = 0b0000'1111;
constexpr auto payload_lead4 = 0b0000'0111;
constexpr auto payload_cont = 0b0011'1111;
constexpr auto pattern_lead1 = 0b0 << 7;
constexpr auto pattern_lead2 = 0b110 << 5;
constexpr auto pattern_lead3 = 0b1110 << 4;
constexpr auto pattern_lead4 = 0b11110 << 3;
constexpr auto pattern_cont = 0b10 << 6;
auto first = uchar_t(reader.peek());
if ((first & ~payload_lead1) == pattern_lead1)
{
// ASCII character.
reader.bump();
return {first, cp_error::success, reader.current()};
}
else if ((first & ~payload_cont) == pattern_cont)
{
return {{}, cp_error::leads_with_trailing, reader.current()};
}
else if ((first & ~payload_lead2) == pattern_lead2)
{
reader.bump();
auto second = uchar_t(reader.peek());
if ((second & ~payload_cont) != pattern_cont)
return {{}, cp_error::missing_trailing, reader.current()};
reader.bump();
auto result = char32_t(first & payload_lead2);
result <<= 6;
result |= char32_t(second & payload_cont);
// C0 and C1 are overlong ASCII.
if (first == 0xC0 || first == 0xC1)
return {result, cp_error::overlong_sequence, reader.current()};
else
return {result, cp_error::success, reader.current()};
}
else if ((first & ~payload_lead3) == pattern_lead3)
{
reader.bump();
auto second = uchar_t(reader.peek());
if ((second & ~payload_cont) != pattern_cont)
return {{}, cp_error::missing_trailing, reader.current()};
reader.bump();
auto third = uchar_t(reader.peek());
if ((third & ~payload_cont) != pattern_cont)
return {{}, cp_error::missing_trailing, reader.current()};
reader.bump();
auto result = char32_t(first & payload_lead3);
result <<= 6;
result |= char32_t(second & payload_cont);
result <<= 6;
result |= char32_t(third & payload_cont);
auto cp = result;
if (0xD800 <= cp && cp <= 0xDFFF)
return {cp, cp_error::surrogate, reader.current()};
else if (first == 0xE0 && second < 0xA0)
return {cp, cp_error::overlong_sequence, reader.current()};
else
return {cp, cp_error::success, reader.current()};
}
else if ((first & ~payload_lead4) == pattern_lead4)
{
reader.bump();
auto second = uchar_t(reader.peek());
if ((second & ~payload_cont) != pattern_cont)
return {{}, cp_error::missing_trailing, reader.current()};
reader.bump();
auto third = uchar_t(reader.peek());
if ((third & ~payload_cont) != pattern_cont)
return {{}, cp_error::missing_trailing, reader.current()};
reader.bump();
auto fourth = uchar_t(reader.peek());
if ((fourth & ~payload_cont) != pattern_cont)
return {{}, cp_error::missing_trailing, reader.current()};
reader.bump();
auto result = char32_t(first & payload_lead4);
result <<= 6;
result |= char32_t(second & payload_cont);
result <<= 6;
result |= char32_t(third & payload_cont);
result <<= 6;
result |= char32_t(fourth & payload_cont);
auto cp = result;
if (cp > 0x10'FFFF)
return {cp, cp_error::out_of_range, reader.current()};
else if (first == 0xF0 && second < 0x90)
return {cp, cp_error::overlong_sequence, reader.current()};
else
return {cp, cp_error::success, reader.current()};
}
else // FE or FF
{
return {{}, cp_error::eof, reader.current()};
}
}
else if constexpr (std::is_same_v<typename Reader::encoding, lexy::utf16_encoding>)
{
constexpr auto payload1 = 0b0000'0011'1111'1111;
constexpr auto payload2 = payload1;
constexpr auto pattern1 = 0b110110 << 10;
constexpr auto pattern2 = 0b110111 << 10;
if (reader.peek() == Reader::encoding::eof())
return {{}, cp_error::eof, reader.current()};
auto first = char16_t(reader.peek());
if ((first & ~payload1) == pattern1)
{
reader.bump();
if (reader.peek() == Reader::encoding::eof())
return {{}, cp_error::missing_trailing, reader.current()};
auto second = char16_t(reader.peek());
if ((second & ~payload2) != pattern2)
return {{}, cp_error::missing_trailing, reader.current()};
reader.bump();
// We've got a valid code point.
auto result = char32_t(first & payload1);
result <<= 10;
result |= char32_t(second & payload2);
result |= 0x10000;
return {result, cp_error::success, reader.current()};
}
else if ((first & ~payload2) == pattern2)
{
return {{}, cp_error::leads_with_trailing, reader.current()};
}
else
{
// Single code unit code point; always valid.
reader.bump();
return {first, cp_error::success, reader.current()};
}
}
else if constexpr (std::is_same_v<typename Reader::encoding, lexy::utf32_encoding>)
{
if (reader.peek() == Reader::encoding::eof())
return {{}, cp_error::eof, reader.current()};
auto cur = reader.peek();
reader.bump();
auto cp = cur;
if (cp > 0x10'FFFF)
return {cp, cp_error::out_of_range, reader.current()};
else if (0xD800 <= cp && cp <= 0xDFFF)
return {cp, cp_error::surrogate, reader.current()};
else
return {cp, cp_error::success, reader.current()};
}
else
{
static_assert(lexy::_detail::error<typename Reader::encoding>,
"no known code point for this encoding");
return {};
}
}
template <typename Reader>
constexpr void recover_code_point(Reader& reader, cp_result<Reader> result)
{
switch (result.error)
{
case cp_error::success:
// Consume the entire code point.
reader.reset(result.end);
break;
case cp_error::eof:
// We don't need to do anything to "recover" from EOF.
break;
case cp_error::leads_with_trailing:
// Invalid code unit, consume to recover.
LEXY_PRECONDITION(result.end.position() == reader.position());
reader.bump();
break;
case cp_error::missing_trailing:
case cp_error::surrogate:
case cp_error::out_of_range:
case cp_error::overlong_sequence:
// Consume all the invalid code units to recover.
reader.reset(result.end);
break;
}
}
} // namespace lexy::_detail
#endif // LEXY_DETAIL_CODE_POINT_HPP_INCLUDED

View File

@@ -0,0 +1,199 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_CONFIG_HPP_INCLUDED
#define LEXY_DETAIL_CONFIG_HPP_INCLUDED
#include <cstddef>
#include <type_traits>
#if defined(LEXY_USER_CONFIG_HEADER)
# include LEXY_USER_CONFIG_HEADER
#elif defined(__has_include)
# if __has_include(<lexy_user_config.hpp>)
# include <lexy_user_config.hpp>
# elif __has_include("lexy_user_config.hpp")
# include "lexy_user_config.hpp"
# endif
#endif
#ifndef LEXY_HAS_UNICODE_DATABASE
# define LEXY_HAS_UNICODE_DATABASE 0
#endif
#ifndef LEXY_EXPERIMENTAL
# define LEXY_EXPERIMENTAL 0
#endif
//=== utility traits===//
#define LEXY_MOV(...) static_cast<std::remove_reference_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__)
#define LEXY_FWD(...) static_cast<decltype(__VA_ARGS__)>(__VA_ARGS__)
#define LEXY_DECLVAL(...) lexy::_detail::declval<__VA_ARGS__>()
#define LEXY_DECAY_DECLTYPE(...) std::decay_t<decltype(__VA_ARGS__)>
/// Creates a new type from the instantiation of a template.
/// This is used to shorten type names.
#define LEXY_INSTANTIATION_NEWTYPE(Name, Templ, ...) \
struct Name : Templ<__VA_ARGS__> \
{ \
using Templ<__VA_ARGS__>::Templ; \
}
namespace lexy::_detail
{
template <typename... T>
constexpr bool error = false;
template <typename T>
std::add_rvalue_reference_t<T> declval();
template <typename T>
constexpr void swap(T& lhs, T& rhs) noexcept
{
T tmp = LEXY_MOV(lhs);
lhs = LEXY_MOV(rhs);
rhs = LEXY_MOV(tmp);
}
template <typename T, typename U>
constexpr bool is_decayed_same = std::is_same_v<std::decay_t<T>, std::decay_t<U>>;
template <typename T, typename Fallback>
using type_or = std::conditional_t<std::is_void_v<T>, Fallback, T>;
} // namespace lexy::_detail
//=== NTTP ===//
#ifndef LEXY_HAS_NTTP
// See https://github.com/foonathan/lexy/issues/15.
# if __cpp_nontype_template_parameter_class >= 201806 || __cpp_nontype_template_args >= 201911
# define LEXY_HAS_NTTP 1
# else
# define LEXY_HAS_NTTP 0
# endif
#endif
#if LEXY_HAS_NTTP
# define LEXY_NTTP_PARAM auto
#else
# define LEXY_NTTP_PARAM const auto&
#endif
//=== consteval ===//
#ifndef LEXY_HAS_CONSTEVAL
# if defined(_MSC_VER) && !defined(__clang__)
// Currently can't handle returning strings from consteval, check back later.
# define LEXY_HAS_CONSTEVAL 0
# elif __cpp_consteval
# define LEXY_HAS_CONSTEVAL 1
# else
# define LEXY_HAS_CONSTEVAL 0
# endif
#endif
#if LEXY_HAS_CONSTEVAL
# define LEXY_CONSTEVAL consteval
#else
# define LEXY_CONSTEVAL constexpr
#endif
//=== constexpr ===//
#ifndef LEXY_HAS_CONSTEXPR_DTOR
# if __cpp_constexpr_dynamic_alloc
# define LEXY_HAS_CONSTEXPR_DTOR 1
# else
# define LEXY_HAS_CONSTEXPR_DTOR 0
# endif
#endif
#if LEXY_HAS_CONSTEXPR_DTOR
# define LEXY_CONSTEXPR_DTOR constexpr
#else
# define LEXY_CONSTEXPR_DTOR
#endif
//=== char8_t ===//
#ifndef LEXY_HAS_CHAR8_T
# if __cpp_char8_t
# define LEXY_HAS_CHAR8_T 1
# else
# define LEXY_HAS_CHAR8_T 0
# endif
#endif
#if LEXY_HAS_CHAR8_T
# define LEXY_CHAR_OF_u8 char8_t
# define LEXY_CHAR8_T char8_t
# define LEXY_CHAR8_STR(Str) u8##Str
#else
namespace lexy
{
using _char8_t = unsigned char;
} // namespace lexy
# define LEXY_CHAR_OF_u8 char
# define LEXY_CHAR8_T ::lexy::_char8_t
# define LEXY_CHAR8_STR(Str) \
LEXY_NTTP_STRING(::lexy::_detail::type_string, u8##Str)::c_str<LEXY_CHAR8_T>
#endif
//=== endianness ===//
#ifndef LEXY_IS_LITTLE_ENDIAN
# if defined(__BYTE_ORDER__)
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define LEXY_IS_LITTLE_ENDIAN 1
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define LEXY_IS_LITTLE_ENDIAN 0
# else
# error "unsupported byte order"
# endif
# elif defined(_MSC_VER)
# define LEXY_IS_LITTLE_ENDIAN 1
# else
# error "unknown endianness"
# endif
#endif
//=== force inline ===//
#ifndef LEXY_FORCE_INLINE
# if defined(__has_cpp_attribute)
# if __has_cpp_attribute(gnu::always_inline)
# define LEXY_FORCE_INLINE [[gnu::always_inline]]
# endif
# endif
#
# ifndef LEXY_FORCE_INLINE
# define LEXY_FORCE_INLINE inline
# endif
#endif
//=== empty_member ===//
#ifndef LEXY_EMPTY_MEMBER
# if defined(__has_cpp_attribute)
# if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 11
// GCC <= 11 has buggy support, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101040
# define LEXY_HAS_EMPTY_MEMBER 0
# elif __has_cpp_attribute(no_unique_address)
# define LEXY_HAS_EMPTY_MEMBER 1
# endif
# endif
# ifndef LEXY_HAS_EMPTY_MEMBER
# define LEXY_HAS_EMPTY_MEMBER 0
# endif
# if LEXY_HAS_EMPTY_MEMBER
# define LEXY_EMPTY_MEMBER [[no_unique_address]]
# else
# define LEXY_EMPTY_MEMBER
# endif
#endif
#endif // LEXY_DETAIL_CONFIG_HPP_INCLUDED

View File

@@ -0,0 +1,35 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_DETECT_HPP_INCLUDED
#define LEXY_DETAIL_DETECT_HPP_INCLUDED
#include <lexy/_detail/config.hpp>
namespace lexy::_detail
{
template <typename... Args>
using void_t = void;
template <template <typename...> typename Op, typename Void, typename... Args>
struct _detector : std::false_type
{
template <typename Fallback>
using type_or = Fallback;
};
template <template <typename...> typename Op, typename... Args>
struct _detector<Op, void_t<Op<Args...>>, Args...> : std::true_type
{
template <typename Fallback>
using type_or = Op<Args...>;
};
template <template <typename...> typename Op, typename... Args>
constexpr bool is_detected = _detector<Op, void, Args...>::value;
template <typename Fallback, template <typename...> typename Op, typename... Args>
using detected_or = typename _detector<Op, void, Args...>::template type_or<Fallback>;
} // namespace lexy::_detail
#endif // LEXY_DETAIL_DETECT_HPP_INCLUDED

View File

@@ -0,0 +1,64 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_INTEGER_SEQUENCE_HPP_INCLUDED
#define LEXY_DETAIL_INTEGER_SEQUENCE_HPP_INCLUDED
#include <lexy/_detail/config.hpp>
namespace lexy::_detail
{
template <typename T, T... Indices>
struct integer_sequence
{
using type = integer_sequence<T, Indices...>;
};
template <std::size_t... Indices>
using index_sequence = integer_sequence<std::size_t, Indices...>;
#if defined(__clang__)
template <std::size_t Size>
using make_index_sequence = __make_integer_seq<integer_sequence, std::size_t, Size>;
#elif defined(__GNUC__) && __GNUC__ >= 8
template <std::size_t Size>
using make_index_sequence = index_sequence<__integer_pack(Size)...>;
#elif defined(_MSC_VER)
template <std::size_t Size>
using make_index_sequence = __make_integer_seq<integer_sequence, std::size_t, Size>;
#else
// Adapted from https://stackoverflow.com/a/32223343.
template <class Sequence1, class Sequence2>
struct concat_seq;
template <std::size_t... I1, std::size_t... I2>
struct concat_seq<index_sequence<I1...>, index_sequence<I2...>>
{
using type = index_sequence<I1..., (sizeof...(I1) + I2)...>;
};
template <size_t N>
struct _make_index_sequence : concat_seq<typename _make_index_sequence<N / 2>::type,
typename _make_index_sequence<N - N / 2>::type>
{};
template <>
struct _make_index_sequence<0>
{
using type = index_sequence<>;
};
template <>
struct _make_index_sequence<1>
{
using type = index_sequence<0>;
};
template <std::size_t Size>
using make_index_sequence = typename _make_index_sequence<Size>::type;
#endif
template <typename... T>
using index_sequence_for = make_index_sequence<sizeof...(T)>;
} // namespace lexy::_detail
#endif // LEXY_DETAIL_INTEGER_SEQUENCE_HPP_INCLUDED

View File

@@ -0,0 +1,70 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_INVOKE_HPP_INCLUDED
#define LEXY_DETAIL_INVOKE_HPP_INCLUDED
#include <lexy/_detail/config.hpp>
namespace lexy::_detail
{
template <typename MemberPtr, bool = std::is_member_object_pointer_v<MemberPtr>>
struct _mem_invoker;
template <typename R, typename ClassT>
struct _mem_invoker<R ClassT::*, true>
{
static constexpr decltype(auto) invoke(R ClassT::*f, ClassT& object)
{
return object.*f;
}
static constexpr decltype(auto) invoke(R ClassT::*f, const ClassT& object)
{
return object.*f;
}
template <typename Ptr>
static constexpr auto invoke(R ClassT::*f, Ptr&& ptr) -> decltype((*LEXY_FWD(ptr)).*f)
{
return (*LEXY_FWD(ptr)).*f;
}
};
template <typename F, typename ClassT>
struct _mem_invoker<F ClassT::*, false>
{
template <typename ObjectT, typename... Args>
static constexpr auto _invoke(int, F ClassT::*f, ObjectT&& object, Args&&... args)
-> decltype((LEXY_FWD(object).*f)(LEXY_FWD(args)...))
{
return (LEXY_FWD(object).*f)(LEXY_FWD(args)...);
}
template <typename PtrT, typename... Args>
static constexpr auto _invoke(short, F ClassT::*f, PtrT&& ptr, Args&&... args)
-> decltype(((*LEXY_FWD(ptr)).*f)(LEXY_FWD(args)...))
{
return ((*LEXY_FWD(ptr)).*f)(LEXY_FWD(args)...);
}
template <typename... Args>
static constexpr auto invoke(F ClassT::*f,
Args&&... args) -> decltype(_invoke(0, f, LEXY_FWD(args)...))
{
return _invoke(0, f, LEXY_FWD(args)...);
}
};
template <typename ClassT, typename F, typename... Args>
constexpr auto invoke(F ClassT::*f, Args&&... args)
-> decltype(_mem_invoker<F ClassT::*>::invoke(f, LEXY_FWD(args)...))
{
return _mem_invoker<F ClassT::*>::invoke(f, LEXY_FWD(args)...);
}
template <typename F, typename... Args>
constexpr auto invoke(F&& f, Args&&... args) -> decltype(LEXY_FWD(f)(LEXY_FWD(args)...))
{
return LEXY_FWD(f)(LEXY_FWD(args)...);
}
} // namespace lexy::_detail
#endif // LEXY_DETAIL_INVOKE_HPP_INCLUDED

View File

@@ -0,0 +1,244 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_ITERATOR_HPP_INCLUDED
#define LEXY_DETAIL_ITERATOR_HPP_INCLUDED
#include <lexy/_detail/assert.hpp>
#include <lexy/_detail/config.hpp>
#include <lexy/_detail/detect.hpp>
#include <lexy/_detail/std.hpp>
//=== iterator algorithms ===//
namespace lexy::_detail
{
// Can't use std::is_base_of_v<std::random_access_iterator_tag, ...> without including <iterator>.
template <typename Iterator>
using _detect_random_access = decltype(LEXY_DECLVAL(Iterator) - LEXY_DECLVAL(Iterator));
template <typename Iterator>
constexpr auto is_random_access_iterator = is_detected<_detect_random_access, Iterator>;
template <typename Iterator, typename Sentinel>
constexpr std::size_t range_size(Iterator begin, Sentinel end)
{
if constexpr (std::is_same_v<Iterator, Sentinel> && is_random_access_iterator<Iterator>)
{
return static_cast<std::size_t>(end - begin);
}
else
{
std::size_t result = 0;
for (auto cur = begin; cur != end; ++cur)
++result;
return result;
}
}
template <typename Iterator>
constexpr Iterator next(Iterator iter)
{
return ++iter;
}
template <typename Iterator>
constexpr Iterator next(Iterator iter, std::size_t n)
{
if constexpr (is_random_access_iterator<Iterator>)
{
return iter + n;
}
else
{
for (auto i = 0u; i != n; ++i)
++iter;
return iter;
}
}
template <typename Iterator, typename Sentinel>
constexpr Iterator next_clamped(Iterator iter, std::size_t n, Sentinel end)
{
if constexpr (is_random_access_iterator<Iterator> && std::is_same_v<Iterator, Sentinel>)
{
auto remaining = std::size_t(end - iter);
if (remaining < n)
return end;
else
return iter + n;
}
else
{
for (auto i = 0u; i != n; ++i)
{
if (iter == end)
break;
++iter;
}
return iter;
}
}
// Used for assertions.
template <typename Iterator, typename Sentinel>
constexpr bool precedes([[maybe_unused]] Iterator first, [[maybe_unused]] Sentinel after)
{
if constexpr (is_random_access_iterator<Iterator> && std::is_same_v<Iterator, Sentinel>)
return first <= after;
else
return true;
}
// Requires: begin <= end_a && begin <= end_b.
// Returns min(end_a, end_b).
template <typename Iterator>
constexpr Iterator min_range_end(Iterator begin, Iterator end_a, Iterator end_b)
{
if constexpr (is_random_access_iterator<Iterator>)
{
LEXY_PRECONDITION(begin <= end_a && begin <= end_b);
if (end_a <= end_b)
return end_a;
else
return end_b;
}
else
{
auto cur = begin;
while (cur != end_a && cur != end_b)
++cur;
return cur;
}
}
// Requires: begin <= end_a && begin <= end_b.
// Returns max(end_a, end_b).
template <typename Iterator>
constexpr Iterator max_range_end(Iterator begin, Iterator end_a, Iterator end_b)
{
if constexpr (is_random_access_iterator<Iterator>)
{
LEXY_PRECONDITION(begin <= end_a && begin <= end_b);
if (end_a <= end_b)
return end_b;
else
return end_a;
}
else
{
auto cur = begin;
while (true)
{
if (cur == end_a)
return end_b;
else if (cur == end_b)
return end_a;
++cur;
}
return begin; // unreachable
}
}
} // namespace lexy::_detail
//=== facade classes ===//
namespace lexy::_detail
{
template <typename T>
struct _proxy_pointer
{
T value;
constexpr T* operator->() noexcept
{
return &value;
}
};
template <typename Derived, typename T, typename Reference = T&, typename Pointer = const T*>
struct forward_iterator_base
{
using value_type = std::remove_cv_t<T>;
using reference = Reference;
using pointer = lexy::_detail::type_or<Pointer, _proxy_pointer<value_type>>;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
constexpr reference operator*() const noexcept
{
return static_cast<const Derived&>(*this).deref();
}
constexpr pointer operator->() const noexcept
{
if constexpr (std::is_void_v<Pointer>)
return pointer{**this};
else
return &**this;
}
constexpr Derived& operator++() noexcept
{
auto& derived = static_cast<Derived&>(*this);
derived.increment();
return derived;
}
constexpr Derived operator++(int) noexcept
{
auto& derived = static_cast<Derived&>(*this);
auto copy = derived;
derived.increment();
return copy;
}
friend constexpr bool operator==(const Derived& lhs, const Derived& rhs)
{
return lhs.equal(rhs);
}
friend constexpr bool operator!=(const Derived& lhs, const Derived& rhs)
{
return !lhs.equal(rhs);
}
};
template <typename Derived, typename T, typename Reference = T&, typename Pointer = const T*>
struct bidirectional_iterator_base : forward_iterator_base<Derived, T, Reference, Pointer>
{
using iterator_category = std::bidirectional_iterator_tag;
constexpr Derived& operator--() noexcept
{
auto& derived = static_cast<Derived&>(*this);
derived.decrement();
return derived;
}
constexpr Derived operator--(int) noexcept
{
auto& derived = static_cast<Derived&>(*this);
auto copy = derived;
derived.decrement();
return copy;
}
};
template <typename Derived, typename Iterator>
struct sentinel_base
{
friend constexpr bool operator==(const Iterator& lhs, Derived) noexcept
{
return lhs.is_end();
}
friend constexpr bool operator!=(const Iterator& lhs, Derived) noexcept
{
return !(lhs == Derived{});
}
friend constexpr bool operator==(Derived, const Iterator& rhs) noexcept
{
return rhs == Derived{};
}
friend constexpr bool operator!=(Derived, const Iterator& rhs) noexcept
{
return !(rhs == Derived{});
}
};
} // namespace lexy::_detail
#endif // LEXY_DETAIL_ITERATOR_HPP_INCLUDED

View File

@@ -0,0 +1,246 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_LAZY_INIT_HPP_INCLUDED
#define LEXY_DETAIL_LAZY_INIT_HPP_INCLUDED
#include <lexy/_detail/assert.hpp>
#include <lexy/_detail/config.hpp>
#include <lexy/_detail/std.hpp>
namespace lexy::_detail
{
template <typename T>
struct _lazy_init_storage_trivial
{
bool _init;
union
{
char _empty;
T _value;
};
constexpr _lazy_init_storage_trivial() noexcept : _init(false), _empty() {}
template <typename... Args>
constexpr _lazy_init_storage_trivial(int, Args&&... args)
: _init(true), _value(LEXY_FWD(args)...)
{}
template <typename... Args>
constexpr void _construct(Args&&... args)
{
*this = _lazy_init_storage_trivial(0, LEXY_FWD(args)...);
}
};
template <typename T>
struct _lazy_init_storage_non_trivial
{
bool _init;
union
{
char _empty;
T _value;
};
constexpr _lazy_init_storage_non_trivial() noexcept : _init(false), _empty() {}
template <typename... Args>
LEXY_CONSTEXPR_DTOR void _construct(Args&&... args)
{
_detail::construct_at(&_value, LEXY_FWD(args)...);
_init = true;
}
// Cannot add noexcept due to https://github.com/llvm/llvm-project/issues/59854.
LEXY_CONSTEXPR_DTOR ~_lazy_init_storage_non_trivial() /* noexcept */
{
if (_init)
_value.~T();
}
LEXY_CONSTEXPR_DTOR _lazy_init_storage_non_trivial(
_lazy_init_storage_non_trivial&& other) noexcept
: _init(other._init), _empty()
{
if (_init)
_detail::construct_at(&_value, LEXY_MOV(other._value));
}
LEXY_CONSTEXPR_DTOR _lazy_init_storage_non_trivial& operator=(
_lazy_init_storage_non_trivial&& other) noexcept
{
if (_init && other._init)
_value = LEXY_MOV(other._value);
else if (_init && !other._init)
{
_value.~T();
_init = false;
}
else if (!_init && other._init)
{
_detail::construct_at(&_value, LEXY_MOV(other._value));
_init = true;
}
else
{
// Both not initialized, nothing to do.
}
return *this;
}
};
template <typename T>
constexpr auto _lazy_init_trivial = [] {
// https://www.foonathan.net/2021/03/trivially-copyable/
return std::is_trivially_destructible_v<T> //
&& std::is_trivially_copy_constructible_v<T> //
&& std::is_trivially_copy_assignable_v<T> //
&& std::is_trivially_move_constructible_v<T> //
&& std::is_trivially_move_assignable_v<T>;
}();
template <typename T>
using _lazy_init_storage = std::conditional_t<_lazy_init_trivial<T>, _lazy_init_storage_trivial<T>,
_lazy_init_storage_non_trivial<T>>;
template <typename T>
class lazy_init : _lazy_init_storage<T>
{
public:
using value_type = T;
constexpr lazy_init() noexcept = default;
template <typename... Args>
constexpr T& emplace(Args&&... args)
{
if (*this)
this->_value = T(LEXY_FWD(args)...);
else
this->_construct(LEXY_FWD(args)...);
return this->_value;
}
template <typename Fn, typename... Args>
constexpr T& emplace_result(Fn&& fn, Args&&... args)
{
return emplace(LEXY_FWD(fn)(LEXY_FWD(args)...));
}
constexpr explicit operator bool() const noexcept
{
return this->_init;
}
constexpr T& operator*() & noexcept
{
LEXY_PRECONDITION(*this);
return this->_value;
}
constexpr const T& operator*() const& noexcept
{
LEXY_PRECONDITION(*this);
return this->_value;
}
constexpr T&& operator*() && noexcept
{
LEXY_PRECONDITION(*this);
return LEXY_MOV(this->_value);
}
constexpr const T&& operator*() const&& noexcept
{
LEXY_PRECONDITION(*this);
return LEXY_MOV(this->_value);
}
constexpr T* operator->() noexcept
{
LEXY_PRECONDITION(*this);
return &this->_value;
}
constexpr const T* operator->() const noexcept
{
LEXY_PRECONDITION(*this);
return &this->_value;
}
private:
template <typename... Args>
constexpr explicit lazy_init(int, Args&&... args) noexcept
: _lazy_init_storage<T>(0, LEXY_FWD(args)...)
{}
};
template <typename T>
class lazy_init<T&>
{
public:
using value_type = T&;
constexpr lazy_init() noexcept : _ptr(nullptr) {}
constexpr T& emplace(T& ref)
{
_ptr = &ref;
return ref;
}
template <typename Fn, typename... Args>
constexpr T& emplace_result(Fn&& fn, Args&&... args)
{
return emplace(LEXY_FWD(fn)(LEXY_FWD(args)...));
}
constexpr explicit operator bool() const noexcept
{
return _ptr != nullptr;
}
constexpr T& operator*() const noexcept
{
LEXY_PRECONDITION(*this);
return *_ptr;
}
constexpr T* operator->() const noexcept
{
LEXY_PRECONDITION(*this);
return _ptr;
}
private:
T* _ptr;
};
template <>
class lazy_init<void>
{
public:
using value_type = void;
constexpr lazy_init() noexcept : _init(false) {}
constexpr void emplace()
{
_init = true;
}
template <typename Fn, typename... Args>
constexpr void emplace_result(Fn&& fn, Args&&... args)
{
LEXY_FWD(fn)(LEXY_FWD(args)...);
_init = true;
}
constexpr explicit operator bool() const noexcept
{
return _init;
}
private:
bool _init;
};
} // namespace lexy::_detail
#endif // LEXY_DETAIL_LAZY_INIT_HPP_INCLUDED

View File

@@ -0,0 +1,152 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_MEMORY_RESOURCE_HPP_INCLUDED
#define LEXY_DETAIL_MEMORY_RESOURCE_HPP_INCLUDED
#include <cstring>
#include <lexy/_detail/assert.hpp>
#include <lexy/_detail/config.hpp>
#include <new>
#if 0 // NOLINT
// Subset of the interface of std::pmr::memory_resource.
class MemoryResource
{
public:
void* allocate(std::size_t bytes, std::size_t alignment);
void deallocate(void* ptr, std::size_t bytes, std::size_t alignment);
friend bool operator==(const MemoryResource& lhs, const MemoryResource& rhs);
};
#endif
namespace lexy::_detail
{
class default_memory_resource
{
public:
static void* allocate(std::size_t bytes, std::size_t alignment)
{
if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
return ::operator new(bytes, std::align_val_t{alignment});
else
return ::operator new(bytes);
}
static void deallocate(void* ptr, std::size_t bytes, std::size_t alignment) noexcept
{
#if LEXY_ENABLE_ASSERT
// In debug mode, we fill freed memory with 0xFF to detect dangling lexemes.
// For default, ASCII, bytes, this is just a noticable value.
// For UTF-8, this is the EOF integer value as its an invalid code unit.
// For UTF-16, this is the code point 0xFFFF, which is the replacement character.
// For UTF-32, this is an out of range code point.
std::memset(ptr, 0xFF, bytes);
#endif
#ifdef __cpp_sized_deallocation
if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
::operator delete(ptr, bytes, std::align_val_t{alignment});
else
::operator delete(ptr, bytes);
#else
(void)bytes;
if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
::operator delete(ptr, std::align_val_t{alignment});
else
::operator delete(ptr);
#endif
}
friend constexpr bool operator==(default_memory_resource, default_memory_resource) noexcept
{
return true;
}
};
} // namespace lexy::_detail
namespace lexy::_detail
{
template <typename MemoryResource>
class _memory_resource_ptr_empty
{
public:
constexpr explicit _memory_resource_ptr_empty(MemoryResource*) noexcept {}
constexpr explicit _memory_resource_ptr_empty(void*) noexcept {}
constexpr auto operator*() const noexcept
{
return MemoryResource{};
}
constexpr auto operator->() const noexcept
{
struct proxy
{
MemoryResource _resource;
constexpr MemoryResource* operator->() noexcept
{
return &_resource;
}
};
return proxy{};
}
constexpr MemoryResource* get() const noexcept
{
return nullptr;
}
};
template <typename MemoryResource>
class _memory_resource_ptr
{
public:
constexpr explicit _memory_resource_ptr(MemoryResource* resource) noexcept : _resource(resource)
{
LEXY_PRECONDITION(resource);
}
constexpr MemoryResource& operator*() const noexcept
{
return *_resource;
}
constexpr MemoryResource* operator->() const noexcept
{
return _resource;
}
constexpr MemoryResource* get() const noexcept
{
return _resource;
}
private:
MemoryResource* _resource;
};
// clang-format off
template <typename MemoryResource>
using memory_resource_ptr
= std::conditional_t<std::is_void_v<MemoryResource>,
_memory_resource_ptr_empty<default_memory_resource>,
std::conditional_t<std::is_empty_v<MemoryResource>,
_memory_resource_ptr_empty<MemoryResource>,
_memory_resource_ptr<MemoryResource>>>;
// clang-format on
template <typename MemoryResource, typename = std::enable_if_t<std::is_void_v<MemoryResource>
|| std::is_empty_v<MemoryResource>>>
constexpr MemoryResource* get_memory_resource()
{
return nullptr;
}
} // namespace lexy::_detail
#endif // LEXY_DETAIL_MEMORY_RESOURCE_HPP_INCLUDED

View File

@@ -0,0 +1,147 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_NTTP_STRING_HPP_INCLUDED
#define LEXY_DETAIL_NTTP_STRING_HPP_INCLUDED
#include <lexy/_detail/assert.hpp>
#include <lexy/_detail/config.hpp>
#include <lexy/encoding.hpp>
namespace lexy::_detail
{
// Note: we can't use type_string<auto...>, it doesn't work on older GCC.
template <typename CharT, CharT... Cs>
struct type_string
{
using char_type = CharT;
template <template <typename C, C...> typename T>
using rename = T<CharT, Cs...>;
static constexpr auto size = sizeof...(Cs);
template <typename T = char_type>
static constexpr T c_str[sizeof...(Cs) + 1] = {transcode_char<T>(Cs)..., T()};
};
} // namespace lexy::_detail
#if LEXY_HAS_NTTP // string NTTP implementation
# include <lexy/_detail/integer_sequence.hpp>
namespace lexy::_detail
{
template <std::size_t N, typename CharT>
struct string_literal
{
CharT data[N];
using char_type = CharT;
LEXY_CONSTEVAL string_literal(const CharT* str) : data{}
{
for (auto i = 0u; i != N; ++i)
data[i] = str[i];
}
LEXY_CONSTEVAL string_literal(CharT c) : data{}
{
data[0] = c;
}
static LEXY_CONSTEVAL auto size()
{
return N;
}
};
template <std::size_t N, typename CharT>
string_literal(const CharT (&)[N]) -> string_literal<N - 1, CharT>;
template <typename CharT>
string_literal(CharT) -> string_literal<1, CharT>;
template <template <typename C, C... Cs> typename T, string_literal Str, std::size_t... Idx>
auto _to_type_string(index_sequence<Idx...>)
{
return T<typename decltype(Str)::char_type, Str.data[Idx]...>{};
}
template <template <typename C, C... Cs> typename T, string_literal Str>
using to_type_string
= decltype(_to_type_string<T, Str>(make_index_sequence<decltype(Str)::size()>{}));
} // namespace lexy::_detail
# define LEXY_NTTP_STRING(T, Str) \
::lexy::_detail::to_type_string<T, ::lexy::_detail::string_literal(Str)>
#elif defined(__GNUC__) // literal implementation
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wpedantic"
# ifdef __clang__
# pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template"
# endif
template <typename CharT, CharT... Cs>
constexpr ::lexy::_detail::type_string<CharT, Cs...> operator""_lexy_string_udl()
{
return {};
}
# define LEXY_NTTP_STRING(T, Str) decltype(Str##_lexy_string_udl)::rename<T>
# pragma GCC diagnostic pop
#else // string<Cs...> macro implementation
namespace lexy::_detail
{
template <typename A, typename B>
struct cat_;
template <typename CharT, CharT... C1, CharT... C2>
struct cat_<type_string<CharT, C1...>, type_string<CharT, C2...>>
{
using type = type_string<CharT, C1..., C2...>;
};
template <typename A, typename B>
using cat = typename cat_<A, B>::type;
template <template <typename CharT, CharT...> typename T, typename TypeString, std::size_t Size,
std::size_t MaxSize>
struct macro_type_string
{
static_assert(Size <= MaxSize, "string out of range");
using type = typename TypeString::template rename<T>;
};
} // namespace lexy::_detail
# define LEXY_NTTP_STRING_LENGTH(Str) (sizeof(Str) / sizeof(Str[0]) - 1)
// extract Ith character if not out of bounds
# define LEXY_NTTP_STRING1(Str, I) \
::std::conditional_t< \
(I < LEXY_NTTP_STRING_LENGTH(Str)), \
::lexy::_detail::type_string<::LEXY_DECAY_DECLTYPE(Str[0]), \
(I >= LEXY_NTTP_STRING_LENGTH(Str) ? Str[0] : Str[I])>, \
::lexy::_detail::type_string<::LEXY_DECAY_DECLTYPE(Str[0])>>
// recursively split the string in two
# define LEXY_NTTP_STRING2(Str, I) \
::lexy::_detail::cat<LEXY_NTTP_STRING1(Str, I), LEXY_NTTP_STRING1(Str, I + 1)>
# define LEXY_NTTP_STRING4(Str, I) \
::lexy::_detail::cat<LEXY_NTTP_STRING2(Str, I), LEXY_NTTP_STRING2(Str, I + 2)>
# define LEXY_NTTP_STRING8(Str, I) \
::lexy::_detail::cat<LEXY_NTTP_STRING4(Str, I), LEXY_NTTP_STRING4(Str, I + 4)>
# define LEXY_NTTP_STRING16(Str, I) \
::lexy::_detail::cat<LEXY_NTTP_STRING8(Str, I), LEXY_NTTP_STRING8(Str, I + 8)>
# define LEXY_NTTP_STRING32(Str, I) \
::lexy::_detail::cat<LEXY_NTTP_STRING16(Str, I), LEXY_NTTP_STRING16(Str, I + 16)>
// instantiate with overflow check
# define LEXY_NTTP_STRING(T, Str) \
::lexy::_detail::macro_type_string<T, LEXY_NTTP_STRING32(Str, 0), \
LEXY_NTTP_STRING_LENGTH(Str), 32>::type
#endif
#endif // LEXY_DETAIL_NTTP_STRING_HPP_INCLUDED

View File

@@ -0,0 +1,72 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_STATELESS_LAMBDA_HPP_INCLUDED
#define LEXY_DETAIL_STATELESS_LAMBDA_HPP_INCLUDED
#include <lexy/_detail/config.hpp>
namespace lexy::_detail
{
template <typename Lambda>
struct stateless_lambda
{
static_assert(std::is_class_v<Lambda>);
static_assert(std::is_empty_v<Lambda>);
static constexpr Lambda get()
{
if constexpr (std::is_default_constructible_v<Lambda>)
{
// We're using C++20, lambdas are default constructible.
return Lambda();
}
else
{
// We're not having C++20; use a sequence of weird workarounds to legally construct a
// Lambda object without invoking any constructors.
// This works and is well-defined, but sadly not constexpr.
// Taken from: https://www.youtube.com/watch?v=yTb6xz_FSkY
// We're defining two standard layout types that have a char as a common initial
// sequence (as the Lambda is empty, it doesn't add anymore members to B).
struct A
{
char member;
};
struct B : Lambda
{
char member;
};
static_assert(std::is_standard_layout_v<A> && std::is_standard_layout_v<B>);
// We put the two types in a union and initialize the a member, which we can do.
union storage_t
{
A a;
B b;
} storage{};
// We can now take the address of member via b, as it is in the common initial sequence.
auto char_ptr = &storage.b.member;
// char_ptr is a pointer to the first member of B, so we can reinterpret_cast it to a
// pointer to B.
auto b_ptr = reinterpret_cast<B*>(char_ptr);
// Now we're having a pointer to a B object, which can we can cast to the base class
// Lambda.
auto lambda_ptr = static_cast<Lambda*>(b_ptr);
// Dereference the pointer to get the lambda object.
return *lambda_ptr;
}
}
template <typename... Args>
constexpr decltype(auto) operator()(Args&&... args) const
{
return get()(LEXY_FWD(args)...);
}
};
} // namespace lexy::_detail
#endif // LEXY_DETAIL_STATELESS_LAMBDA_HPP_INCLUDED

View File

@@ -0,0 +1,98 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_STD_HPP_INCLUDED
#define LEXY_DETAIL_STD_HPP_INCLUDED
#include <lexy/_detail/config.hpp>
//=== iterator tags ===//
#if defined(__GLIBCXX__)
namespace std
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
struct forward_iterator_tag;
struct bidirectional_iterator_tag;
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#elif defined(_LIBCPP_VERSION)
_LIBCPP_BEGIN_NAMESPACE_STD
struct forward_iterator_tag;
struct bidirectional_iterator_tag;
_LIBCPP_END_NAMESPACE_STD
#else
// Forward declaring things in std is not allowed, but I'm willing to take the risk.
namespace std
{
struct forward_iterator_tag;
struct bidirectional_iterator_tag;
} // namespace std
#endif
//=== (constexpr) construct_at ===//
#if !LEXY_HAS_CONSTEXPR_DTOR
namespace lexy::_detail
{
// We don't have constexpr dtor's, so this is just a regular function.
template <typename T, typename... Args>
T* construct_at(T* ptr, Args&&... args)
{
return ::new ((void*)ptr) T(LEXY_FWD(args)...);
}
} // namespace lexy::_detail
#elif defined(_MSC_VER)
namespace lexy::_detail
{
// MSVC can make it constexpr if marked with an attribute given by a macro.
template <typename T, typename... Args>
constexpr T* construct_at(T* ptr, Args&&... args)
{
# if defined(_MSVC_CONSTEXPR)
_MSVC_CONSTEXPR
# endif
return ::new ((void*)ptr) T(LEXY_FWD(args)...);
}
} // namespace lexy::_detail
#else
namespace lexy::_detail
{
struct _construct_at_tag
{};
} // namespace lexy::_detail
namespace std
{
// GCC only allows constexpr placement new inside a function called `std::construct_at`.
// So we write our own.
template <typename T, typename... Args>
constexpr T* construct_at(lexy::_detail::_construct_at_tag, T* ptr, Args&&... args)
{
return ::new ((void*)ptr) T(LEXY_FWD(args)...);
}
} // namespace std
namespace lexy::_detail
{
template <typename T, typename... Args>
constexpr T* construct_at(T* ptr, Args&&... args)
{
return std::construct_at(lexy::_detail::_construct_at_tag{}, ptr, LEXY_FWD(args)...);
}
} // namespace lexy::_detail
#endif
#endif // LEXY_DETAIL_STD_HPP_INCLUDED

View File

@@ -0,0 +1,212 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_STRING_VIEW_HPP_INCLUDED
#define LEXY_DETAIL_STRING_VIEW_HPP_INCLUDED
#include <lexy/_detail/assert.hpp>
#include <lexy/_detail/config.hpp>
#include <lexy/_detail/integer_sequence.hpp>
namespace lexy::_detail
{
struct null_terminated
{};
template <typename CharT>
class basic_string_view
{
static constexpr CharT empty_string[] = {CharT()};
public:
using char_type = CharT;
//=== constructor ===//
constexpr basic_string_view() noexcept : _ptr(empty_string), _size(0u), _null_terminated(true)
{}
constexpr basic_string_view(const char_type* str) noexcept
: _ptr(str), _size(0u), _null_terminated(true)
{
while (*str++)
++_size;
}
constexpr basic_string_view(const char_type* ptr, std::size_t size) noexcept
: _ptr(ptr), _size(size), _null_terminated(false)
{}
constexpr basic_string_view(null_terminated, const char_type* ptr, std::size_t size) noexcept
: _ptr(ptr), _size(size), _null_terminated(true)
{
LEXY_PRECONDITION(_ptr[_size] == CharT());
}
constexpr basic_string_view(const char_type* begin, const char_type* end) noexcept
: _ptr(begin), _size(std::size_t(end - begin)), _null_terminated(false)
{
LEXY_PRECONDITION(begin <= end);
}
//=== access ===//
using iterator = const char_type*;
constexpr iterator begin() const noexcept
{
return _ptr;
}
constexpr iterator end() const noexcept
{
return _ptr + _size;
}
constexpr bool empty() const noexcept
{
return _size == 0u;
}
constexpr std::size_t size() const noexcept
{
return _size;
}
constexpr std::size_t length() const noexcept
{
return _size;
}
constexpr char_type operator[](std::size_t i) const noexcept
{
LEXY_PRECONDITION(i <= _size);
return _ptr[i];
}
constexpr char_type front() const noexcept
{
LEXY_PRECONDITION(!empty());
return *_ptr;
}
constexpr char_type back() const noexcept
{
LEXY_PRECONDITION(!empty());
return _ptr[_size - 1];
}
constexpr const char_type* data() const noexcept
{
return _ptr;
}
constexpr bool is_null_terminated() const noexcept
{
return _null_terminated;
}
constexpr const char_type* c_str() const noexcept
{
LEXY_PRECONDITION(is_null_terminated());
return _ptr;
}
//=== operations ===//
static constexpr std::size_t npos = std::size_t(-1);
constexpr void remove_prefix(std::size_t n) noexcept
{
LEXY_PRECONDITION(n <= _size);
_ptr += n;
_size -= n;
}
constexpr void remove_suffix(std::size_t n) noexcept
{
LEXY_PRECONDITION(n <= _size);
_size -= n;
_null_terminated = false;
}
constexpr basic_string_view substr(std::size_t pos, std::size_t length = npos) const noexcept
{
LEXY_PRECONDITION(pos < _size);
if (length >= _size - pos)
{
auto result = basic_string_view(_ptr + pos, end());
result._null_terminated = _null_terminated;
return result;
}
else
{
// Note that we're loosing null-terminated-ness.
return basic_string_view(_ptr + pos, length);
}
}
constexpr bool starts_with(basic_string_view prefix) const noexcept
{
return substr(0, prefix.size()) == prefix;
}
constexpr bool try_remove_prefix(basic_string_view prefix) noexcept
{
if (!starts_with(prefix))
return false;
remove_prefix(prefix.length());
return true;
}
constexpr std::size_t find(basic_string_view str, std::size_t pos = 0) const noexcept
{
for (auto i = pos; i < length(); ++i)
{
if (substr(i, str.length()) == str)
return i;
}
return npos;
}
constexpr std::size_t find(CharT c, std::size_t pos = 0) const noexcept
{
return find(basic_string_view(&c, 1), pos);
}
//=== comparison ===//
friend constexpr bool operator==(basic_string_view<CharT> lhs,
basic_string_view<CharT> rhs) noexcept
{
if (lhs.size() != rhs.size())
return false;
for (auto a = lhs.begin(), b = rhs.begin(); a != lhs.end(); ++a, ++b)
if (*a != *b)
return false;
return true;
}
friend constexpr bool operator!=(basic_string_view<CharT> lhs,
basic_string_view<CharT> rhs) noexcept
{
return !(lhs == rhs);
}
private:
const CharT* _ptr;
std::size_t _size;
bool _null_terminated;
};
using string_view = basic_string_view<char>;
} // namespace lexy::_detail
namespace lexy::_detail
{
template <auto FnPtr, typename Indices = make_index_sequence<FnPtr().size()>>
struct _string_view_holder;
template <auto FnPtr, std::size_t... Indices>
struct _string_view_holder<FnPtr, index_sequence<Indices...>>
{
static constexpr auto view = FnPtr();
static constexpr typename decltype(view)::char_type value[] = {view[Indices]..., {}};
};
template <auto FnPtr>
inline constexpr const auto* make_cstr = _string_view_holder<FnPtr>::value;
} // namespace lexy::_detail
#endif // LEXY_DETAIL_STRING_VIEW_HPP_INCLUDED

View File

@@ -0,0 +1,251 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_SWAR_HPP_INCLUDED
#define LEXY_DETAIL_SWAR_HPP_INCLUDED
#include <climits>
#include <cstdint>
#include <cstring>
#include <lexy/_detail/config.hpp>
#include <lexy/input/base.hpp>
#if defined(_MSC_VER)
# include <intrin.h>
#endif
namespace lexy::_detail
{
// Contains the chars in little endian order; rightmost bits are first char.
using swar_int = std::uintmax_t;
// The number of chars that can fit into one SWAR.
template <typename CharT>
constexpr auto swar_length = [] {
static_assert(sizeof(CharT) < sizeof(swar_int) && sizeof(swar_int) % sizeof(CharT) == 0);
return sizeof(swar_int) / sizeof(CharT);
}();
template <typename CharT>
constexpr auto char_bit_size = sizeof(CharT) * CHAR_BIT;
template <typename CharT>
constexpr auto make_uchar(CharT c)
{
if constexpr (std::is_same_v<CharT, LEXY_CHAR8_T>)
// Not all libstdc++ support char8_t and std::make_unsigned_t.
return c;
else
return std::make_unsigned_t<CharT>(c);
}
template <typename CharT>
using uchar_t = decltype(make_uchar(CharT()));
// Returns a swar_int filled with the specific char.
template <typename CharT>
constexpr swar_int swar_fill(CharT _c)
{
auto c = make_uchar(_c);
auto result = swar_int(0);
for (auto i = 0u; i != swar_length<CharT>; ++i)
{
result <<= char_bit_size<CharT>;
result |= c;
}
return result;
}
// Returns a swar_int filled with the complement of the specific char.
template <typename CharT>
constexpr swar_int swar_fill_compl(CharT _c)
{
auto c = uchar_t<CharT>(~uchar_t<CharT>(_c));
auto result = swar_int(0);
for (auto i = 0u; i != swar_length<CharT>; ++i)
{
result <<= char_bit_size<CharT>;
result |= c;
}
return result;
}
constexpr void _swar_pack(swar_int&, int) {}
template <typename H, typename... T>
constexpr void _swar_pack(swar_int& result, int index, H h, T... t)
{
if (std::size_t(index) == char_bit_size<swar_int>)
return;
if (index >= 0)
result |= swar_int(make_uchar(h)) << index;
_swar_pack(result, index + int(char_bit_size<H>), t...);
}
template <typename CharT>
struct swar_pack_result
{
swar_int value;
swar_int mask;
std::size_t count;
constexpr CharT operator[](std::size_t idx) const
{
constexpr auto mask = (swar_int(1) << char_bit_size<CharT>)-1;
return (value >> idx * char_bit_size<CharT>)&mask;
}
};
// Returns a swar_int containing the specified characters.
// If more are provided than fit, will only take the first couple ones.
template <int SkipFirstNChars = 0, typename... CharT>
constexpr auto swar_pack(CharT... cs)
{
using char_type = std::common_type_t<CharT...>;
swar_pack_result<char_type> result{0, 0, 0};
_swar_pack(result.value, -SkipFirstNChars * int(char_bit_size<char_type>), cs...);
auto count = int(sizeof...(CharT)) - SkipFirstNChars;
if (count <= 0)
{
result.mask = 0;
result.count = 0;
}
else if (count >= int(swar_length<char_type>))
{
result.mask = swar_int(-1);
result.count = swar_length<char_type>;
}
else
{
result.mask = swar_int(swar_int(1) << count * int(char_bit_size<char_type>)) - 1;
result.count = std::size_t(count);
}
return result;
}
// Returns the index of the char that is different between lhs and rhs.
template <typename CharT>
constexpr std::size_t swar_find_difference(swar_int lhs, swar_int rhs)
{
if (lhs == rhs)
return swar_length<CharT>;
auto mask = lhs ^ rhs;
#if defined(__GNUC__)
auto bit_idx = __builtin_ctzll(mask);
#elif defined(_MSC_VER) && defined(_WIN64)
unsigned long bit_idx;
_BitScanForward64(&bit_idx, mask);
#elif defined(_MSC_VER)
unsigned long bit_idx = 0;
if (!_BitScanForward(&bit_idx, static_cast<std::uint32_t>(mask))
&& _BitScanForward(&bit_idx, mask >> 32))
bit_idx += 32;
#else
# error "unsupported compiler; please file an issue"
#endif
return std::size_t(bit_idx) / char_bit_size<CharT>;
}
// Returns true if v has a char less than N.
template <typename CharT, CharT N>
constexpr bool swar_has_char_less(swar_int v)
{
// https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
constexpr auto offset = swar_fill(CharT(N));
auto zero_or_msb = v - offset;
constexpr auto msb_mask = swar_fill(CharT(1 << (char_bit_size<CharT> - 1)));
auto not_msb = ~v & msb_mask;
return zero_or_msb & not_msb;
}
// Returns true if v has a zero char.
template <typename CharT>
constexpr bool swar_has_zero(swar_int v)
{
return swar_has_char_less<CharT, 1>(v);
}
// Returns true if v contains the specified char.
template <typename CharT, CharT C>
constexpr bool swar_has_char(swar_int v)
{
if constexpr (C == 0)
{
return swar_has_zero<CharT>(v);
}
else
{
constexpr auto mask = swar_fill(C);
return swar_has_zero<CharT>(v ^ mask);
}
}
} // namespace lexy::_detail
namespace lexy::_detail
{
struct _swar_base
{};
template <typename Reader>
constexpr auto is_swar_reader = std::is_base_of_v<_swar_base, Reader>;
template <typename Derived>
class swar_reader_base : _swar_base
{
public:
swar_int peek_swar() const
{
auto ptr = static_cast<const Derived&>(*this).position();
swar_int result;
#if LEXY_IS_LITTLE_ENDIAN
std::memcpy(&result, ptr, sizeof(swar_int));
#else
using char_type = typename Derived::encoding::char_type;
auto dst = reinterpret_cast<char*>(&result);
auto length = sizeof(swar_int) / sizeof(char_type);
for (auto i = 0u; i != length; ++i)
{
std::memcpy(dst + i, ptr + length - i - 1, sizeof(char_type));
}
#endif
return result;
}
void bump_swar()
{
auto ptr = static_cast<Derived&>(*this).position();
ptr += swar_length<typename Derived::encoding::char_type>;
static_cast<Derived&>(*this).reset({ptr});
}
void bump_swar(std::size_t char_count)
{
auto ptr = static_cast<Derived&>(*this).position();
ptr += char_count;
static_cast<Derived&>(*this).reset({ptr});
}
};
constexpr std::size_t round_size_for_swar(std::size_t size_in_bytes)
{
// We round up to the next multiple.
if (auto remainder = size_in_bytes % sizeof(swar_int); remainder > 0)
size_in_bytes += sizeof(swar_int) - remainder;
// Then add one extra space of padding on top.
size_in_bytes += sizeof(swar_int);
return size_in_bytes;
}
} // namespace lexy::_detail
#endif // LEXY_DETAIL_SWAR_HPP_INCLUDED

View File

@@ -0,0 +1,119 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_TUPLE_HPP_INCLUDED
#define LEXY_DETAIL_TUPLE_HPP_INCLUDED
#include <lexy/_detail/config.hpp>
#include <lexy/_detail/integer_sequence.hpp>
namespace lexy::_detail
{
template <std::size_t Idx, typename T>
struct _tuple_holder
{
#if !defined(__GNUC__) || defined(__clang__)
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105795
LEXY_EMPTY_MEMBER
#endif
T value;
};
template <std::size_t Idx, typename... T>
struct _nth_type;
template <std::size_t Idx, typename H, typename... T>
struct _nth_type<Idx, H, T...>
{
using type = typename _nth_type<Idx - 1, T...>::type;
};
template <typename H, typename... T>
struct _nth_type<0, H, T...>
{
using type = H;
};
template <typename T>
struct _tuple_get_type
{
using type = T&;
};
template <typename T>
struct _tuple_get_type<T&&>
{
using type = T&&;
};
template <typename Indices, typename... T>
class _tuple;
template <std::size_t... Idx, typename... T>
class _tuple<index_sequence<Idx...>, T...> : public _tuple_holder<Idx, T>...
{
public:
constexpr _tuple() = default;
template <typename... Args>
constexpr _tuple(Args&&... args) : _tuple_holder<Idx, T>{LEXY_FWD(args)}...
{}
};
template <typename... T>
struct tuple : _tuple<index_sequence_for<T...>, T...>
{
constexpr tuple() = default;
template <typename... Args>
constexpr explicit tuple(Args&&... args)
: _tuple<index_sequence_for<T...>, T...>(LEXY_FWD(args)...)
{}
template <std::size_t N>
using element_type = typename _nth_type<N, T...>::type;
template <std::size_t N>
constexpr decltype(auto) get() noexcept
{
// NOLINTNEXTLINE: this is fine.
auto&& holder = static_cast<_tuple_holder<N, element_type<N>>&>(*this);
// NOLINTNEXTLINE
return static_cast<typename _tuple_get_type<element_type<N>>::type>(holder.value);
}
template <std::size_t N>
constexpr decltype(auto) get() const noexcept
{
// NOLINTNEXTLINE: this is fine.
auto&& holder = static_cast<const _tuple_holder<N, element_type<N>>&>(*this);
// NOLINTNEXTLINE
return static_cast<typename _tuple_get_type<const element_type<N>>::type>(holder.value);
}
static constexpr auto index_sequence()
{
return index_sequence_for<T...>{};
}
};
template <>
struct tuple<>
{
constexpr tuple() = default;
static constexpr auto index_sequence()
{
return index_sequence_for<>{};
}
};
template <typename... Args>
constexpr auto make_tuple(Args&&... args)
{
return tuple<std::decay_t<Args>...>(LEXY_FWD(args)...);
}
template <typename... Args>
constexpr auto forward_as_tuple(Args&&... args)
{
return tuple<Args&&...>(LEXY_FWD(args)...);
}
} // namespace lexy::_detail
#endif // LEXY_DETAIL_TUPLE_HPP_INCLUDED

View File

@@ -0,0 +1,130 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DETAIL_TYPE_NAME_HPP_INCLUDED
#define LEXY_DETAIL_TYPE_NAME_HPP_INCLUDED
#include <lexy/_detail/config.hpp>
#include <lexy/_detail/detect.hpp>
#include <lexy/_detail/integer_sequence.hpp>
#include <lexy/_detail/string_view.hpp>
namespace lexy::_detail
{
template <typename T>
using _detect_name_f = std::enable_if_t<std::is_convertible_v<decltype(T::name()), string_view>>;
template <typename T>
using _detect_name_v = decltype(T::name);
template <typename T>
constexpr auto _full_type_name()
{
#if defined(__clang__)
# define LEXY_HAS_AUTOMATIC_TYPE_NAME 1
# define LEXY_HAS_CONSTEXPR_AUTOMATIC_TYPE_NAME 1
constexpr auto prefix = string_view("auto lexy::_detail::_full_type_name() [T = ");
constexpr auto suffix = string_view("]");
auto function = string_view(__PRETTY_FUNCTION__);
function.remove_prefix(prefix.length());
function.remove_suffix(suffix.length());
function.try_remove_prefix("(anonymous namespace)::");
return function;
#elif defined(__GNUC__)
# define LEXY_HAS_AUTOMATIC_TYPE_NAME 1
# if __GNUC__ > 8
# define LEXY_HAS_CONSTEXPR_AUTOMATIC_TYPE_NAME 1
# else
# define LEXY_HAS_CONSTEXPR_AUTOMATIC_TYPE_NAME 0
# endif
constexpr auto prefix
= string_view("constexpr auto lexy::_detail::_full_type_name() [with T = ");
constexpr auto suffix = string_view("]");
auto function = string_view(__PRETTY_FUNCTION__);
function.remove_prefix(prefix.length());
function.remove_suffix(suffix.length());
function.try_remove_prefix("{anonymous}::");
return function;
#elif defined(_MSC_VER)
# define LEXY_HAS_AUTOMATIC_TYPE_NAME 1
# define LEXY_HAS_CONSTEXPR_AUTOMATIC_TYPE_NAME 1
constexpr auto prefix = string_view("auto __cdecl lexy::_detail::_full_type_name<");
constexpr auto suffix = string_view(">(void)");
auto function = string_view(__FUNCSIG__);
function.remove_prefix(prefix.length());
function.remove_suffix(suffix.length());
function.try_remove_prefix("struct ") || function.try_remove_prefix("class ");
function.try_remove_prefix("`anonymous-namespace'::");
return function;
#else
# define LEXY_HAS_AUTOMATIC_TYPE_NAME 0
# define LEXY_HAS_CONSTEXPR_AUTOMATIC_TYPE_NAME 0
return string_view("unknown-type");
#endif
}
template <typename T, int NsCount>
constexpr string_view _type_name()
{
auto name = _full_type_name<T>();
if (name.find('<') != string_view::npos && NsCount != 0)
return name;
for (auto namespace_count = NsCount; namespace_count > 0; --namespace_count)
{
auto pos = name.find("::");
if (pos == string_view::npos)
break;
name.remove_prefix(pos + 2);
}
return name;
}
template <typename T, int NsCount = 1>
constexpr const char* type_name()
{
if constexpr (_detail::is_detected<_detect_name_f, T>)
return T::name();
else if constexpr (_detail::is_detected<_detect_name_v, T>)
return T::name;
else if constexpr (LEXY_HAS_CONSTEXPR_AUTOMATIC_TYPE_NAME)
return make_cstr<_type_name<T, NsCount>>;
else
return "unknown-type";
}
template <typename T, int NsCount>
inline constexpr const char* _type_id_holder = type_name<T, NsCount>();
// Returns a unique address for each type.
// For implementation reasons, it also doubles as the pointer to the name.
template <typename T, int NsCount = 1>
constexpr const char* const* type_id()
{
if constexpr (_detail::is_detected<_detect_name_v, T> //
&& !_detail::is_detected<_detect_name_f, T>)
{
// We can use the address of the static constexpr directly.
return &T::name;
}
else
{
// We instantiate a variable template with a function unique by type.
// As the variable is inline, there should be a single address only.
return &_type_id_holder<T, NsCount>;
}
}
} // namespace lexy::_detail
#endif // LEXY_DETAIL_TYPE_NAME_HPP_INCLUDED

View File

@@ -0,0 +1,83 @@
// AUTOGENERATED FILE --- DO NOT EDIT!
// Generated by `support/generate-unicode-db.py`.
#define LEXY_UNICODE_DATABASE_VERSION "15.0.0"
namespace lexy::_unicode_db
{
constexpr const char* block_starts = "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\021\025\026\027\030\031\032\033\034\035\036\037 !\"#$%&'(!)*+,-./0'\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\0211\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\0212\021\021\0213\021456789\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021:;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<\021=>?@ABCDEFGH\021IJKLMNOPQRSTUVWXYZ[\\]^_`a\021\021\021bcdeeeeeeeeef\021\021\021\021geeeeeeeeeeeeeee\021\021heeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\021\021ijeekl\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021m\021\021\021\021noeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep\021qreeeeeeeeeseeeeeeeeeeeeeeeeeetuvwxyz{|''}eeee~\177\200\201e\202ee\203\204\205ee\206\207\210e\211\212\213\214''\215\216\217'\220\221eeee\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\222\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\223\224\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\225\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\226eeeeeeeeeeee\021\021\227eeeee\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\230\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\231eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\232\233eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\234"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\234";
constexpr const char* blocks = "\000\000\000\000\000\000\000\000\000\001\001\001\001\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\003\003\003\004\003\003\003\005\006\003\007\003\010\003\003\011\011\011\011\011\011\011\011\011\011\003\003\007\007\007\003\003\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\005\003\006\013\014\013\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\005\007\006\007\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\003\004\004\004\004\016\003\013\016\017\020\007\021\016\013\016\007\022\022\013\023\003\024\013\022\017\025\022\022\022\003\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\007\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\027\015\026\015\026\015\026\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\030\026\015\026\015\026\015\031\015\032\026\015\026\015\033\026\015\034\034\026\015\015\035\036\037\026\015\034 \015!\"\026\015\015\015!#\015$\026\015\026\015\026\015%\026\015%\015\015\026\015%\026\015&&\026\015\026\015'\026\015\015(\026\015\015\015(((()*\015)*\015)*\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015)*\015\026\015+,\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015-\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\015\015\015\015.\026\015/0\015\015\026\015123\026\015\026\015\026\015\026\015\026\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015(\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015444444444555555544\013\013\013\013555555555555\013\013\013\013\013\013\013\013\013\013\013\013\013\01344444\013\013\013\013\013\013\0135\0135\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\0136666666666666666666666666666666666666666666666666666666666666666666667666666666666666666666666666666666666666666\026\015\026\0155\013\026\015889\015\015\015\003:8888\013\013;\024<<<8=8>>\015\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\0128\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015?\015\015\015\015\015\015\015\015\015\015\015\015@AB\027\027\027CD\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015EF\015\015GH\007\026\015I\026\015\015---"
"JJJJJJJJJJJJJJJJ\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\01666666KK\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015L\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\0158MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM885\003\003\003\003\003\003\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\003\01088\016\016\00486666666666666666666666666666666NNNNNNNNNNNNNN\010N\003NN\003NN\003N88888888(((((((((((((((((((((((((((8888((((\003\00388888888888\021\021\021\021\021\021\007\007\007\003\003\004\003\003\016\016NNNNNNNNNNN\003\021\003\003\003((((((((((((((((((((((((((((((((5((((((((((NNNNNNNNNNNNN6NNNNNNN\011\011\011\011\011\011\011\011\011\011\003\003\003\003((N(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003(NNNNNNN\021\01666NNNN55NN\016666N((\011\011\011\011\011\011\011\011\011\011(((\016\016(\003\003\003\003\003\003\003\003\003\003\003\003\003\0038\021(N((((((((((((((((((((((((((((((NNNNNNNNNNNNNNNN6666666666688(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((NNNNNNNNNNN(88888888888888\011\011\011\011\011\011\011\011\011\011(((((((((((((((((((((((((((((((((66666666655\016\003\003\0035886\004\004"
"((((((((((((((((((((((NN665NNNNNNNNN5NNN5NNNN688\003\003\003\003\003\003\003\003\003\003\003\003\003\003\0038(((((((((((((((((((((((((66688\0038(((((((((((88888((((((((((((((((((((((((\013((((((8\021\02188888866666666(((((((((((((((((((((((((((((((((((((((((56666666666NNNNNNNNNNNN66\021NNNNNNN666666NNNNNNNNNNNNNNNNNNNO((((((((((((((((((((((((((((((((((((((((((((((((((((((NO6(OOONNNNNNNNOOOO6OO(6666NNN((((((((((NN\003\003\011\011\011\011\011\011\011\011\011\011\0035(((((((((((((((NOO8((((((((88((88((((((((((((((((((((((8(((((((8(888((((886(OOONNNN88OO88OO6(88888888O8888((8(((NN88\011\011\011\011\011\011\011\011\011\011((\004\004\022\022\022\022\022\022\016\004(\003688NNO8((((((8888((88((((((((((((((((((((((8(((((((8((8((8((8868OOONN8888NN88NN6888N8888888((((8(8888888\011\011\011\011\011\011\011\011\011\011NN(((N\0038888888888NNO8(((((((((8(((8((((((((((((((((((((((8(((((((8((8(((((886(OOONNNNN8NNO8OO688(888888888888888((NN88\011\011\011\011\011\011\011\011\011\011\003\0048888888(NNN6668NOO8((((((((88((88((((((((((((((((((((((8(((((((8((8(((((886(ONONNNN88OO88OO688888886NO8888((8(((NN88\011\011\011\011\011\011\011\011\011\011\016(\022\022\022\022\022\0228888888888N(8((((((888(((8((((888((8(8((888((888(((888((((((((((((8888OONOO888OOO8OOO688(888888O88888888888888\011\011\011\011\011\011\011\011\011\011\022\022\022\016\016\016\016\016\016\004\01688888"
"NOOON((((((((8(((8(((((((((((((((((((((((8((((((((((((((((886(NNNOOOO8NNN8NNN68888888NN8(((88(88((NN88\011\011\011\011\011\011\011\011\011\0118888888\003\022\022\022\022\022\022\022\016(NOO\003((((((((8(((8(((((((((((((((((((((((8((((((((((8(((((886(ONOOOOO8NOO8OON68888888OO888888((8((NN88\011\011\011\011\011\011\011\011\011\0118((O888888888888NNOO(((((((((8(((8(((((((((((((((((((((((((((((((((((((((((66(OOONNNN8OOO8OOO6(\0168888(((O\022\022\022\022\022\022\022(((NN88\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022\016((((((8NOO8((((((((((((((((((888((((((((((((((((((((((((8(((((((((8(88(((((((88868888OOONNN8N8OOOOOOOO888888\011\011\011\011\011\011\011\011\011\01188OO\003888888888888((((((((((((((((((((((((((((((((((((((((((((((((N(PNNNNNNN8888\004((((((5666666N6\003\011\011\011\011\011\011\011\011\011\011\003\0038888888888888888888888888888888888888((8(8(((((8((((((((((((((((((((((((8(8((((((((((N(PNNNNNN6NN(88(((((85866666N68\011\011\011\011\011\011\011\011\011\01188((((88888888888888888888888888888888(\016\016\016\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\016\003\016\016\01666\016\016\016\016\016\016\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022\022\0166\0166\0166\005\006\005\006QQ((((((((8((((((((((((((((((((((((((((((((((((8888NNNNNNNNNNNNNNONNNN6\00366(((((NNNNNNNNNNN8NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN8\016\016\016\016\016\016\016\0166\016\016\016\016\016\0168\016\016\003\003\003\003\003\016\016\016\016\003\0038888888888888888888888888888888888888"
"(((((((((((((((((((((((((((((((((((((((((((OONNNNONNNNN6O66OONN(\011\011\011\011\011\011\011\011\011\011\003\003\003\003\003\003((((((OONN((((NNN(OOO((OOOOOOO(((NNNN(((((((((((((NOONNOOOOOON(O\011\011\011\011\011\011\011\011\011\011OOON\016\016RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR8R88888R88\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0034\015\015\015(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8((((88(((((((8(8((((88(((((((((((((((((((((((((((((((((((((((((8((((88(((((((((((((((((((((((((((((((((8((((88(((((((8(8((((88(((((((((((((((8(((((((((((((((((((((((((((((((((((((((((((((((((((((((((8((((88(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88666\003\003\003\003\003\003\003\003\003SSSSSSSSS\022\022\022\022\022\022\022\022\022\022\022888((((((((((((((((\016\016\016\016\016\016\016\016\016\016888888\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\02788TTTTTT88"
"\010((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\016\003(((((((((((((((((\002((((((((((((((((((((((((((\005\006888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003\003\003UUU((((((((8888888((((((((((((((((((NN6Q888888888(((((((((((((((((((NNQ\003\003888888888((((((((((((((((((NN888888888888(((((((((((((8(((8NN888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((66ONNNNNNNOOOOOOOONOO66666666666\003\003\0035\003\003\003\004(688\011\011\011\011\011\011\011\011\011\011888888\022\022\022\022\022\022\022\022\022\022888888\003\003\003\003\003\003\010\003\003\003\003666\0216\011\011\011\011\011\011\011\011\011\011888888(((((((((((((((((((((((((((((((((((5(((((((((((((((((((((((((((((((((((((((((((((((((((((8888888(((((VV((((((((((((((((((((((((((((((((((N(88888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888888"
"(((((((((((((((((((((((((((((((8NNNOOOONNOOO8888OONOOOOOO6668888\016888\003\003\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((((88(((((88888888888((((((((((((((((((((((((((((((((((((((((((((8888((((((((((((((((((((((((((888888\011\011\011\011\011\011\011\011\011\011S888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016(((((((((((((((((((((((NNOON88\003\003(((((((((((((((((((((((((((((((((((((((((((((((((((((ONONNNNNNN86ONOONNNNNNNNOOOOOONN66666666886\011\011\011\011\011\011\011\011\011\011888888\011\011\011\011\011\011\011\011\011\011888888\003\003\003\003\003\003\0035\003\003\003\003\003\0038866666666666666KNN66666666666NNN8888888888888888888888888888888888888888888888888NNNNO(((((((((((((((((((((((((((((((((((((((((((((((6ONNNNNONOOOOONOQ((((((((888\011\011\011\011\011\011\011\011\011\011\003\003\003\003\003\003\003\016\016\016\016\016\016\016\016\016\016666666666\016\016\016\016\016\016\016\016\016\003\0038NNO((((((((((((((((((((((((((((((ONNNNOONNQ6NN((\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((((((((((((((((((6ONNOOONONNNQQ88888888\003\003\003\003((((((((((((((((((((((((((((((((((((OOOOOOOONNNNNNNNOON6888\003\003\003\003\003\011\011\011\011\011\011\011\011\011\011888(((\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((((555555\003\003WXYZZ[\\]^8888888___________________________________________88___\003\003\003\003\003\003\003\00388888888666\0036666666666666Q6666666((((6((((((6((Q66(88888"
"\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015444444444444444444444444444444444444444444444444444444444444444\015\015\015\015\015\015\015\015\015\015\015\015\0154\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0154444444444444444444444444444444444444666666666666666666666666666666666666666NNNNNNNNNNNNNN66666666666\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\015\015\015`\015\015a\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\015\015\015\015\015\015bbbbbbbb\015\015\015\015\015\01588bbbbbb88\015\015\015\015\015\015\015\015bbbbbbbb\015\015\015\015\015\015\015\015bbbbbbbb\015\015\015\015\015\01588bbbbbb88\015\015\015\015\015\015\015\0158b8b8b8b\015\015\015\015\015\015\015\015bbbbbbbb\015\015\015\015\015\015\015\015\015\015\015\015\015\01588\015\015\015\015\015\015\015\015cccccccc\015\015\015\015\015\015\015\015cccccccc\015\015\015\015\015\015\015\015cccccccc\015\015\015\015\0158\015\015bbdde\013f\013\013\013\015\015\0158\015\015gggge\013\013\013\015\015\015\01588\015\015bbhh8\013\013\013\015\015\015\015\015\015\015\015bbiiI\013\013\01388\015\015\0158\015\015jjkke\013\0138\002\002\002\002\002\002\002\002\002\002\002\021ll\021\021\010\010\010\010\010\010\003\003\020\025\005\020\020\025\005\020\003\003\003\003\003\003\003\003mn\021\021\021\021\021\002\003\003\003\003\003\003\003\003\003\020\025\003\003\003\003\014\014\003\003\003\007\005\006\003\003\003\003\003\003\003\003\003\003\003\007\003\014\003\003\003\003\003\003\003\003\003\003\002\021\021\021\021\0218\021\021\021\021\021\021\021\021\021\021\022488\022\022\022\022\022\022\007\007\007\005\0064\022\022\022\022\022\022\022\022\022\022\007\007\007\005\00684444444444444888\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\0048888888888888886666666666666KKKK6KKK666666666666888888888888888"
"\016\016\027\016\016\016\016\027\016\016\015\027\027\027\015\015\027\027\027\015\016\027\016\016o\027\027\027\027\027\016\016\016\016\016\016\027\016p\016\027\016qr\027\027s\015\027\027t\027\015((((\015\016\016\015\015\027\027\007\007\007\007\007\027\015\015\015\015\016\007\016\016\015\016\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022uuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvUUU\026\015UUUU\022\016\0168888\007\007\007\007\007\016\016\016\016\016\007\007\016\016\016\016\007\016\016\007\016\016\007\016\016\016\016\016\016\016\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\016\016\007\016\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\016\016\016\016\016\016\016\016\005\006\005\006\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\016\016\016\016\016\016\016\005\006\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016wwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxx\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022"
"\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\016\016\016\016\016\016\016\016\016\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\007\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\005\006\005\006\005\006\005\006\005\006\005\006\005\006\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\005\006\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\005\006\005\006\005\006\005\006\005\006\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016"
"\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\005\006\005\006\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\005\006\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\016\016\007\007\007\007\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\026\015yz{\015\015\026\015\026\015\026\015|}~\177\015\026\015\015\026\015\015\015\015\015\01544\200\200\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\016\016\016\016\016\016\026\015\026\015666\026\01588888\003\003\003\003\022\003\003\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0158\01588888\01588((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888885\003888888888888886(((((((((((((((((((((((888888888(((((((8(((((((8(((((((8(((((((8(((((((8(((((((8(((((((8(((((((8NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN"
"\003\003\020\025\020\025\003\003\003\020\025\003\020\025\003\003\003\003\003\003\003\003\003\010\003\003\010\003\020\025\003\003\020\025\005\006\005\006\005\006\005\006\003\003\003\003\003\201\003\003\003\003\003\003\003\003\003\003\010\010\003\003\003\003\010\003\005\003\003\003\003\003\003\003\003\003\003\003\003\003\016\016\003\003\003\005\006\005\006\005\006\005\006\0108888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\0168888\002\003\003\003\0165(U\005\006\005\006\005\006\005\006\005\006\016\016\005\006\005\006\005\006\005\006\010\005\006\006\016UUUUUUUUU6666QQ\01055555\016\016UUU5(\003\016\0168((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8866\013\01355(\010((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003555(88888(((((((((((((((((((((((((((((((((((((((((((8((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8\016\016\022\022\022\022\016\016\016\016\016\016\016\016\016\016((((((((((((((((((((((((((((((((\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888(((((((((((((((("
"\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\022\022\022\022\022\022\022\022\016\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016(((((((((((((((((((((5(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888((((((((((((((((((((((((((((((((((((((((555555\003\003"
"((((((((((((5\003\003\003((((((((((((((((\011\011\011\011\011\011\011\011\011\011((88888888888888888888\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015(6KKK\003NNNNNNNN66\0035\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\01544NN((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((UUUUUUUUUU66\003\003\003\003\003\00388888888\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013555555555\013\013\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\0154\015\015\015\015\015\015\015\015\026\015\026\015\202\026\015\026\015\026\015\026\015\026\0155\013\013\026\015\203\015(\026\015\026\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\204\205\206\207\204\015\210\211\212\213\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\214\215\216\026\015\026\01588888\026\0158\0158\015\026\015\026\015888888888888888888888888444\026\015(44\015(((((((N(((6((((N(((((((((((((((((((((((OONNO\016\016\016\0166888\022\022\022\022\022\022\016\016\004\016888888((((((((((((((((((((((((((((((((((((((((((((((((((((\003\003\003\00388888888OO((((((((((((((((((((((((((((((((((((((((((((((((((OOOOOOOOOOOOOOOO6N88888888\003\003\011\011\011\011\011\011\011\011\011\011888888666666666666666666((((((\003\003\003(\003((N\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((NNNNN666\003\003(((((((((((((((((((((((NNNNNNNNNNNOQ88888888888\003(((((((((((((((((((((((((((((888NNNO(((((((((((((((((((((((((((((((((((((((((((((((6OONNNNOONNOOQ\003\003\003\003\003\003\003\003\003\003\003\003\00385\011\011\011\011\011\011\011\011\011\0118888\003\003(((((N5(((((((((\011\011\011\011\011\011\011\011\011\011(((((8"
"(((((((((((((((((((((((((((((((((((((((((NNNNNNOONNOONN888888888(((N((((((((NO88\011\011\011\011\011\011\011\011\011\01188\003\003\003\003((((((((((((((((5((((((\016\016\016(ONO((((((((((((((((((((((((((((((((((((((((((((((((((N(NNN((NN(((((N6(6(888888888888888888888888((5\003\003(((((((((((ONNOO\003\003(55O68888888888((((((88((((((88((((((888888888(((((((8(((((((8\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0134444\015\015\015\015\015\015\015\015\0154\013\0138888\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217(((((((((((((((((((((((((((((((((((OONOONOO\003Q688\011\011\011\011\011\011\011\011\011\011888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888(((((((((((((((((((((((8888(((((((((((((((((((((((((((((((((((((((((((((((((8888\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220"
"\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888888888\015\015\015\015\015\015\015888888888888\015\015\015\015\01588888(N((((((((((\007(((((((((((((8(((((8(8((8((8((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\0138888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\222\222\222\222\222\222(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((("
"((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\006\005\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888\01688888888888888888888888888888888((((((((((\222\222\004\016\016\0166666666666666666\003\003\003\003\003\003\003\005\006\0038888886666666666666666\003\010\010\014\014\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\003\003\005\006\003\003\003\003\014\014\014\003\003\0038\003\003\003\003\010\005\006\005\006\005\006\003\003\003\007\010\007\007\0078\003\004\003\0038888\222(\222(\2228\222(\222(\222(\222(\222((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88\0218\003\003\003\004\003\003\003\005\006\003\007\003\010\003\003\011\011\011\011\011\011\011\011\011\011\003\003\007\007\007\003\003\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\005\003\006\013\014\013\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\005\007\006\007\005\006\003\005\006\003\003((((((((((5(((((((((((((((((((((((((((((((((((((((((((((\223\223(((((((((((((((((((((((((((((((888((((((88((((((88((((((88(((888\004\004\007\013\016\004\0048\016\007\007\007\007\016\0168888888888\021\021\021\016\01688((((((((((((8((((((((((((((((((((((((((8(((((((((((((((((((8((8(((((((((((((((88((((((((((((((8888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888"
"\003\003\0038888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888\016\016\016\016\016\016\016\016\016UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\022\022\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016888\01688888888888888888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01668888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((888(((((((((((((((((((((((((((((((((((((((((((((((((8888888888888886\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\0228888((((((((((((((((((((((((((((((((\022\022\022\022888888888((((((((((((((((((((U((((((((U88888((((((((((((((((((((((((((((((((((((((NNNNN88888((((((((((((((((((((((((((((((8\003((((((((((((((((((((((((((((((((((((8888((((((((\003UUUUU888888888888888888888888888888888888888888\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88\011\011\011\011\011\011\011\011\011\011888888\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\2248888\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0158888"
"((((((((((((((((((((((((((((((((((((((((88888888((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888\003\225\225\225\225\225\225\225\225\225\225\2258\225\225\225\225\225\225\225\225\225\225\225\225\225\225\2258\225\225\225\225\225\225\2258\225\2258\015\015\015\015\015\015\015\015\015\015\0158\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0158\015\015\015\015\015\015\0158\015\0158888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888((((((((((((((((((((((8888888888((((((((88888888888888888888888845544484444444444444444444444444444444444444444448444444444888888888888888888888888888888888888888888888888888888888888888888888((((((88(8((((((((((((((((((((((((((((((((((((((((((((8((888(88(((((((((((((((((((((((8\003\022\022\022\022\022\022\022\022(((((((((((((((((((((((\016\016\022\022\022\022\022\022\022(((((((((((((((((((((((((((((((88888888\022\022\022\022\022\022\022\022\022888888888888888888888888888888888888888888888888(((((((((((((((((((8((88888\022\022\022\022\022((((((((((((((((((((((\022\022\022\022\022\022888\003((((((((((((((((((((((((((88888\0038888888888888888888888888888888888888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888\022\022((\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\02288\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022"
"(NNN8NN88888NNNN((((8(((8(((((((((((((((((((((((((((((8866688886\022\022\022\022\022\022\022\022\0228888888\003\003\003\003\003\003\003\003\0038888888(((((((((((((((((((((((((((((\022\022\003(((((((((((((((((((((((((((((\022\022\02288888888888888888888888888888888((((((((\016((((((((((((((((((((((((((((668888\022\022\022\022\022\003\003\003\003\003\003\003888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((888\003\003\003\003\003\003\003((((((((((((((((((((((88\022\022\022\022\022\022\022\022(((((((((((((((((((88888\022\022\022\022\022\022\022\022((((((((((((((((((8888888\003\003\003\003888888888888\022\022\022\022\022\022\02288888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888888888888888888888888888888888888888888888888888===================================================8888888888888\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0158888888\022\022\022\022\022\022((((((((((((((((((((((((((((((((((((NNNN88888888\011\011\011\011\011\011\011\011\011\011888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888"
"888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\0228((((((((((((((((((((((((((((((((((((((((((8NN\01088((888888888888888888888888888888888888888888888888888888888888888888888888888666(((((((((((((((((((((((((((((\022\022\022\022\022\022\022\022\022\022(88888888((((((((((((((((((((((66666666666\022\022\022\022\003\003\003\003\0038888888888888888888888((((((((((((((((((6666\003\003\003\00388888888888888888888888888888888888888(((((((((((((((((((((\022\022\022\022\022\022\02288888888888888888888(((((((((((((((((((((((888888888ONO(((((((((((((((((((((((((((((((((((((((((((((((((((((NNNNNNNNNNNNNN6\003\003\003\003\003\003\0038888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\011\011\011\011\011\011\011\011\011\0116((NN(8888888886NNO(((((((((((((((((((((((((((((((((((((((((((((OOONNNNOO66\003\003\021\003\003\003\003N8888888888\02188(((((((((((((((((((((((((8888888\011\011\011\011\011\011\011\011\011\011888888NNN((((((((((((((((((((((((((((((((((((NNNNNONNNNNN668\011\011\011\011\011\011\011\011\011\011\003\003\003\003(OO(88888888(((((((((((((((((((((((((((((((((((6\003\003(888888888NNO((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNNOQ((((\003\003\003\0036666\003ON\011\011\011\011\011\011\011\011\011\011(\003(\003\003\0038\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\02288888888888"
"((((((((((((((((((8(((((((((((((((((((((((((OOONNNOONQ6N\003\003\003\003\003\003N((N88888888888888888888888888888888888888888888888888888888888888(((((((8(8((((8(((((((((((((((8((((((((((\003888888(((((((((((((((((((((((((((((((((((((((((((((((NOOONNNNNN6688888\011\011\011\011\011\011\011\011\011\011888888NNOO8((((((((88((88((((((((((((((((((((((8(((((((8((8(((((866(OONOOOO88OO88OOQ88(888888O88888(((((OO886666666888666668888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNOO6NNO6((((\003\003\003\003\003\011\011\011\011\011\011\011\011\011\011\003\0038\0036(((888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNONOOOONNO66((\003(88888888\011\011\011\011\011\011\011\011\011\0118888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((OOONNNN88OOOONNO66\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003((((NN8888888888888888888888888888888888"
"((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNOONO6N\003\003\003(88888888888\011\011\011\011\011\011\011\011\011\011888888\003\003\003\003\003\003\003\003\003\003\003\003\0038888888888888888888(((((((((((((((((((((((((((((((((((((((((((NONOONNNNNNQ6(\003888888\011\011\011\011\011\011\011\011\011\011888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((88NNNOONNNNONNNN68888\011\011\011\011\011\011\011\011\011\011\022\022\003\003\003\016(((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNNO66\0038888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022888888888888((((((((88(88((((((((8((8((((((((((((((((((((((((OOOOOO8OO88NNQ6(O(O6\003\003\003888888888\011\011\011\011\011\011\011\011\011\0118888888888888888888888888888888888888888888888888888888888888888888888((((((((88(((((((((((((((((((((((((((((((((((((((OOONNNN88NNOOOO6(\003(O888888888888888888888888888"
"(NNNNNNNNNN((((((((((((((((((((((((((((((((((((((((66NNNNO(NNNN\003\003\003\003\003\003\003\003688888888(NNNNNNOONNN((((((((((((((((((((((((((((((((((((((((((((((NNNNNNNNNNNNNO66\003\003\003(\003\003\003\003\0038888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888\003\003\003\003\003\003\003\003\003\003888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((8(((((((((((((((((((((((((((((((((((((ONNNNNNN8NNNNNNO6(\003\003\003\003\0038888888888\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888\003\003((((((((((((((((((((((((((((((88NNNNNNNNNNNNNNNNNNNNNN8ONNNNNNNONNONN8888888888888888888888888888888888888888888888888888888888888888888888888(((((((8((8((((((((((((((((((((((((((((((((((((((NNNNNN888N8NN8NNN6N66(N88888888\011\011\011\011\011\011\011\011\011\011888888((((((8((8((((((((((((((((((((((((((((((((OOOOO8NN8OONO6(8888888\011\011\011\011\011\011\011\011\011\01188888888888888888888888888888888888888888888888888888888888888888888888888888888888888"
"88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((NNOO\003\0038888888NN(O(((((((((((((8((((((((((((((((((((((((((((((((((OONNNNN888OONQ6\003\003\003\003\003\003\003\003\003\003\003\003\003\011\011\011\011\011\011\011\011\011\01188888888888888888888888888888888888888888888888888888888888888888888888888888888888888(888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\004\004\004\004\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888\003((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU8\003\003\003\003\00388888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((("
"((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003\0038888888888888((((((((((((((((((((((((((((((((((((((((((((((((\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\0216((((((66666666666666688888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888"
"(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888(((((((((((((((((((((((((((((((8\011\011\011\011\011\011\011\011\011\0118888\003\003(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8\011\011\011\011\011\011\011\011\011\011888888((((((((((((((((((((((((((((((8866666\0038888888888((((((((((((((((((((((((((((((((((((((((((((((((6666666\003\003\003\003\003\016\016\016\0165555\003\0168888888888\011\011\011\011\011\011\011\011\011\0118\022\022\022\022\022\022\0228(((((((((((((((((((((88888(((((((((((((((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\003\003\003\00388888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888"
"(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888N(OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO8888888NNNN5555555555555888888888888888888888888888888888888888888888888888888888888888855\0035688888888888OO88888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888(((((((((8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888"
"8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888885555855555558558(((((((((((((((((((((((((((((((((((888888888888888(88888888888888888888888888888(((88(88888888888888((((88888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888(((((((((((((888(((((((((8888888((((((((((88\0166N\003\021\021\021\02188888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888"
"66666666666666666666666666666666666666666666668866666666666666666666666888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888888888888888888888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016QQ666\016\016\016QQQQQQ\021\021\021\021\021\021\021\02166666666\016\0166666666\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0166666\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016666\01688888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888888888888"
"\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\0158\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0278\027\02788\02788\027\02788\027\027\027\0278\027\027\027\027\027\027\027\027\015\015\015\0158\0158\015\015\015\015\015\015\0158\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\0278\027\027\027\02788\027\027\027\027\027\027\027\0278\027\027\027\027\027\027\0278\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\0278\027\027\027\0278\027\027\027\027\0278\027888\027\027\027\027\027\027\0278\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\01588\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015"
"\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\01588\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\0116666666666666666666666666666666666666666666666666666666\016\016\016\01666666666666666666666666666666666666666666666666666\016\016\016\016\016\016\016\0166\016\016\016\016\016\016\016\016\016\016\016\016\016\0166\016\016\003\003\003\003\00388888888888888866666866666666666666688888888888888888888888888888888888888888888888888888888888888888888888888888888\015\015\015\015\015\015\015\015\015\015(\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015888888\015\015\015\015\015\015888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888NNNNNNN8NNNNNNNNNNNNNNNNN88NNNNNNN8NN8NNNNN8888844444444444444444444444444444444444444444444444444444444444444888888888888888888888888888888888N8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888"
"(((((((((((((((((((((((((((((((((((((((((((((8886666666555555588\011\011\011\011\011\011\011\011\011\0118888(\01688888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888((((((((((((((((((((((((((((((688888888888888888((((((((((((((((((((((((((((((((((((((((((((6666\011\011\011\011\011\011\011\011\011\01188888\0048888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((56666\011\011\011\011\011\011\011\011\011\01188888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((8((((8((8(((((((((((((((8"
"(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88\022\022\022\022\022\022\022\022\022666666688888888888888888888888888888888888888888\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015666N66658888\011\011\011\011\011\011\011\011\011\0118888\003\003888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\022\022\022\004\022\022\022\0228888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\022\022\022\022\022\022\022\022\022\022\022\022\022\022\02288888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888"
"((((8(((((((((((((((((((((((((((8((8(88(8((((((((((8((((8(8(888888(8888(8(8(8(((8((8(88(8(8(8(8(8((8(88((((8(((((((8((((8((((8(8((((((((((8(((((((((((((((((88888(((8(((((8(((((((((((((((((8888888888888888888888888888888888888888888888888888\007\00788888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\016\016\016\016\016\016\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\016\016\016\016\016\016\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888888888888888888888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\0168888888\016\01688888888888888\016\016\016\016\016\0168888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888"
"\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\013\013\013\013\013\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888\016\016\016\016\016\016\016\016\016\016\016\016\016888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888\016\016\016\016\016\016\016\016\016\016\016\0168888\016888888888888888\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888\016\016\016\016\016\016\016\016\016\016888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016888888888888888888888888888888888888888888888888888888888888888888888888888888"
"\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016888\016\016\016\016\016\016\016\016\0168888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\01688888888\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\0168888888\016\016\016\016\016\016\016\016\0168888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888888888888888888888888888\011\011\011\011\011\011\011\011\011\011888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((("
"((((((((((((((((((((((((((((((88((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888888888888888888888888888((((((((((((((((((((((((((((((8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888"
"(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888888888888888888888888888888888888888888\021888888888888888888888888888888\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888886666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666668888888888888888"
"\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\22188";
constexpr std::size_t property_index(char32_t code_point)
{
LEXY_PRECONDITION(code_point <= 0x10FFFF);
auto high = (code_point >> 8) & 0xFFFF;
auto low = code_point & 0xFF;
auto block_start = static_cast<unsigned char>(block_starts[high]);
auto block_index = block_start * 256u + low;
return static_cast<unsigned char>(blocks[block_index]);
}
constexpr lexy::code_point::general_category_t category[] = {lexy::code_point::Cc,lexy::code_point::Cc,lexy::code_point::Zs,lexy::code_point::Po,lexy::code_point::Sc,lexy::code_point::Ps,lexy::code_point::Pe,lexy::code_point::Sm,lexy::code_point::Pd,lexy::code_point::Nd,lexy::code_point::Lu,lexy::code_point::Sk,lexy::code_point::Pc,lexy::code_point::Ll,lexy::code_point::So,lexy::code_point::Lo,lexy::code_point::Pi,lexy::code_point::Cf,lexy::code_point::No,lexy::code_point::Ll,lexy::code_point::Po,lexy::code_point::Pf,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lo,lexy::code_point::Lu,lexy::code_point::Lt,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lm,lexy::code_point::Lm,lexy::code_point::Mn,lexy::code_point::Mn,lexy::code_point::Cn,lexy::code_point::Lm,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Me,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Mn,lexy::code_point::Mc,lexy::code_point::Lo,lexy::code_point::Mc,lexy::code_point::Lu,lexy::code_point::No,lexy::code_point::Ll,lexy::code_point::Nl,lexy::code_point::Mn,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lt,lexy::code_point::Lu,lexy::code_point::Lt,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Cf,lexy::code_point::Zl,lexy::code_point::Zp,lexy::code_point::Sm,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::So,lexy::code_point::Lu,lexy::code_point::Nl,lexy::code_point::Nl,lexy::code_point::So,lexy::code_point::So,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lm,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Ll,lexy::code_point::Cs,lexy::code_point::Co,lexy::code_point::Lo,lexy::code_point::Lm,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::So,};
enum binary_properties_t
{
whitespace,
join_control,
alphabetic,
uppercase,
lowercase,
xid_start,
xid_continue,
_property_count,
};
static_assert(static_cast<int>(_property_count) <= 8);
constexpr std::uint_least8_t binary_properties[] = {0,1,1,0,0,0,0,0,0,64,108,0,64,116,0,116,0,0,0,116,64,0,108,108,108,116,108,108,108,108,108,108,108,108,108,108,108,108,108,108,100,108,100,108,108,108,108,108,108,108,108,108,116,100,64,84,0,20,108,108,108,108,108,116,108,116,116,116,116,116,116,108,116,108,108,0,108,108,68,68,68,64,108,64,116,100,100,116,116,116,116,116,116,116,116,108,116,108,108,100,108,100,116,108,108,108,108,108,2,1,1,96,108,108,108,96,108,108,116,12,20,108,108,108,108,108,108,108,108,4,108,108,108,108,108,108,108,108,108,108,108,108,108,116,0,0,4,68,108,108,108,12,};
constexpr std::int_least32_t case_folding_offset[] = {0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,775,0,0,1,0,-121,-268,210,206,205,79,202,203,207,211,209,213,214,218,217,219,0,2,1,-97,-56,-130,10795,-163,10792,-195,69,71,0,0,0,116,0,0,116,38,37,64,63,1,8,-30,-25,-15,-22,-54,-48,-60,-64,-7,80,0,15,48,0,0,0,0,7264,0,-8,0,0,-6222,-6221,-6212,-6210,-6211,-6204,-6180,35267,-3008,-58,-7615,-8,-8,-74,-9,-7173,-86,-100,-112,-128,-126,0,0,0,0,-7517,-8383,-8262,0,28,16,0,26,0,-10743,-3814,-10727,-10780,-10749,-10783,-10782,-10815,0,-35332,-42280,-42308,-42319,-42315,-42305,-42258,-42282,-42261,928,-48,-42307,-35384,-38864,0,0,0,0,40,39,34,0,};
} // namespace lexy::_unicode_db

View File

@@ -0,0 +1,275 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_ACTION_BASE_HPP_INCLUDED
#define LEXY_ACTION_BASE_HPP_INCLUDED
#include <lexy/_detail/config.hpp>
#include <lexy/_detail/lazy_init.hpp>
#include <lexy/_detail/type_name.hpp>
#include <lexy/callback/noop.hpp>
#include <lexy/dsl/base.hpp>
#include <lexy/grammar.hpp>
//=== parse_context ===//
namespace lexy
{
namespace _detail
{
struct parse_context_var_base
{
const void* id;
parse_context_var_base* next;
constexpr parse_context_var_base(const void* id) : id(id), next(nullptr) {}
template <typename Context>
constexpr void link(Context& context)
{
auto cb = context.control_block;
next = cb->vars;
cb->vars = this;
}
template <typename Context>
constexpr void unlink(Context& context)
{
auto cb = context.control_block;
cb->vars = next;
}
};
template <typename Id, typename T>
struct parse_context_var : parse_context_var_base
{
static constexpr auto type_id = lexy::_detail::type_id<Id>();
T value;
explicit constexpr parse_context_var(T&& value)
: parse_context_var_base(static_cast<const void*>(&type_id) /* NOLINT */),
value(LEXY_MOV(value))
{}
template <typename ControlBlock>
static constexpr T& get(const ControlBlock* cb)
{
for (auto cur = cb->vars; cur; cur = cur->next)
if (cur->id == static_cast<const void*>(&type_id) /* NOLINT */)
return static_cast<parse_context_var*>(cur)->value;
LEXY_ASSERT(false, "context variable hasn't been created");
return static_cast<parse_context_var*>(cb->vars)->value;
}
};
template <typename Handler, typename State = void>
struct parse_context_control_block
{
using handler_type = Handler;
using state_type = State;
LEXY_EMPTY_MEMBER Handler parse_handler;
State* parse_state;
parse_context_var_base* vars;
int cur_depth, max_depth;
bool enable_whitespace_skipping;
constexpr parse_context_control_block(Handler&& handler, State* state,
std::size_t max_depth)
: parse_handler(LEXY_MOV(handler)), parse_state(state), //
vars(nullptr), //
cur_depth(0), max_depth(static_cast<int>(max_depth)), enable_whitespace_skipping(true)
{}
template <typename OtherHandler>
constexpr parse_context_control_block(Handler&& handler,
parse_context_control_block<OtherHandler, State>* cb)
: parse_handler(LEXY_MOV(handler)), parse_state(cb->parse_state), //
vars(cb->vars), cur_depth(cb->cur_depth), max_depth(cb->max_depth),
enable_whitespace_skipping(cb->enable_whitespace_skipping)
{}
template <typename OtherHandler>
constexpr void copy_vars_from(parse_context_control_block<OtherHandler, State>* cb)
{
vars = cb->vars;
cur_depth = cb->cur_depth;
max_depth = cb->max_depth;
enable_whitespace_skipping = cb->enable_whitespace_skipping;
}
};
} // namespace _detail
// If a production doesn't define whitespace, we don't need to pass it and can shorten the template
// name.
template <typename Production>
using _whitespace_production_of
= std::conditional_t<_production_defines_whitespace<Production>, Production, void>;
template <typename Handler, typename State, typename Production>
using _production_value_type =
typename Handler::template value_callback<Production, State>::return_type;
template <typename Handler, typename State, typename Production,
typename WhitespaceProduction = _whitespace_production_of<Production>>
struct _pc
{
using handler_type = Handler;
using state_type = State;
using production = Production;
using whitespace_production = WhitespaceProduction;
using value_type = _production_value_type<Handler, State, Production>;
typename Handler::event_handler handler;
_detail::parse_context_control_block<Handler, State>* control_block;
_detail::lazy_init<value_type> value;
constexpr explicit _pc(_detail::parse_context_control_block<Handler, State>* cb)
: handler(Production{}), control_block(cb)
{}
template <typename ChildProduction>
constexpr auto sub_context(ChildProduction)
{
// Update the whitespace production if necessary.
// If the current production is a token or defines whitespace,
// we change it to the current production (or void), otherwise keep it.
using new_whitespace_production
= std::conditional_t<is_token_production<ChildProduction> //
|| _production_defines_whitespace<ChildProduction>,
_whitespace_production_of<ChildProduction>, WhitespaceProduction>;
return _pc<Handler, State, ChildProduction, new_whitespace_production>(control_block);
}
constexpr auto value_callback()
{
using callback = typename Handler::template value_callback<Production, State>;
return callback(control_block->parse_state);
}
template <typename Event, typename... Args>
constexpr auto on(Event ev, Args&&... args)
{
return handler.on(control_block->parse_handler, ev, LEXY_FWD(args)...);
}
};
} // namespace lexy
//=== do_action ===//
namespace lexy::_detail
{
struct final_parser
{
template <typename Context, typename Reader, typename... Args>
LEXY_PARSER_FUNC static bool parse(Context& context, Reader&, Args&&... args)
{
context.value.emplace_result(context.value_callback(), LEXY_FWD(args)...);
return true;
}
};
template <typename NextParser>
struct context_finish_parser
{
template <typename Context, typename Reader, typename SubContext, typename... Args>
LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, SubContext& sub_context,
Args&&... args)
{
// Might need to skip whitespace, according to the original context.
using continuation
= std::conditional_t<lexy::is_token_production<typename SubContext::production>,
lexy::whitespace_parser<Context, NextParser>, NextParser>;
// Pass the produced value to the next parser.
if constexpr (std::is_void_v<typename SubContext::value_type>)
return continuation::parse(context, reader, LEXY_FWD(args)...);
else
return continuation::parse(context, reader, LEXY_FWD(args)...,
LEXY_MOV(*sub_context.value));
}
};
} // namespace lexy::_detail
namespace lexy
{
constexpr void* no_parse_state = nullptr;
template <typename Handler, typename State, typename Production, typename Reader>
constexpr auto _do_action(_pc<Handler, State, Production>& context, Reader& reader)
{
context.on(parse_events::grammar_start{}, reader.position());
context.on(parse_events::production_start{}, reader.position());
// We parse whitespace, theen the rule, then finish.
using parser = lexy::whitespace_parser<
LEXY_DECAY_DECLTYPE(context),
lexy::parser_for<lexy::production_rule<Production>, _detail::final_parser>>;
auto rule_result = parser::parse(context, reader);
if (rule_result)
{
context.on(parse_events::production_finish{}, reader.position());
context.on(parse_events::grammar_finish{}, reader);
}
else
{
context.on(parse_events::production_cancel{}, reader.position());
context.on(parse_events::grammar_cancel{}, reader);
}
return rule_result;
}
template <typename Production, template <typename> typename Result, typename Handler,
typename State, typename Reader>
constexpr auto do_action(Handler&& handler, State* state, Reader& reader)
{
static_assert(!std::is_reference_v<Handler>, "need to move handler in");
_detail::parse_context_control_block control_block(LEXY_MOV(handler), state,
max_recursion_depth<Production>());
_pc<Handler, State, Production> context(&control_block);
auto rule_result = _do_action(context, reader);
using value_type = typename decltype(context)::value_type;
if constexpr (std::is_void_v<value_type>)
return LEXY_MOV(control_block.parse_handler).template get_result<Result<void>>(rule_result);
else if (context.value)
return LEXY_MOV(control_block.parse_handler)
.template get_result<Result<value_type>>(rule_result, LEXY_MOV(*context.value));
else
return LEXY_MOV(control_block.parse_handler)
.template get_result<Result<value_type>>(rule_result);
}
} // namespace lexy
//=== value callback ===//
namespace lexy::_detail
{
struct void_value_callback
{
constexpr void_value_callback() = default;
template <typename State>
constexpr explicit void_value_callback(State*)
{}
using return_type = void;
constexpr auto sink() const
{
return lexy::noop.sink();
}
template <typename... Args>
constexpr void operator()(Args&&...) const
{}
};
} // namespace lexy::_detail
#endif // LEXY_ACTION_BASE_HPP_INCLUDED

View File

@@ -0,0 +1,90 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_ACTION_MATCH_HPP_INCLUDED
#define LEXY_ACTION_MATCH_HPP_INCLUDED
#include <lexy/action/base.hpp>
namespace lexy
{
class _mh
{
public:
constexpr _mh() : _failed(false) {}
class event_handler
{
public:
constexpr event_handler(production_info) {}
template <typename Error>
constexpr void on(_mh& handler, parse_events::error, Error&&)
{
handler._failed = true;
}
template <typename Event, typename... Args>
constexpr int on(_mh&, Event, const Args&...)
{
return 0; // operation_chain_start needs to return something
}
};
template <typename Production, typename State>
using value_callback = _detail::void_value_callback;
template <typename>
constexpr bool get_result(bool rule_parse_result) &&
{
return rule_parse_result && !_failed;
}
private:
bool _failed;
};
template <typename State, typename Input>
struct match_action
{
State* _state = nullptr;
using handler = _mh;
using state = State;
using input = Input;
template <typename>
using result_type = bool;
constexpr match_action() = default;
template <typename U = State>
constexpr explicit match_action(U& state) : _state(&state)
{}
template <typename Production>
constexpr auto operator()(Production, const Input& input) const
{
auto reader = input.reader();
return lexy::do_action<Production, result_type>(handler(), _state, reader);
}
};
template <typename Production, typename Input>
constexpr bool match(const Input& input)
{
return match_action<void, Input>()(Production{}, input);
}
template <typename Production, typename Input, typename State>
constexpr bool match(const Input& input, State& state)
{
return match_action<State, Input>(state)(Production{}, input);
}
template <typename Production, typename Input, typename State>
constexpr bool match(const Input& input, const State& state)
{
return match_action<const State, Input>(state)(Production{}, input);
}
} // namespace lexy
#endif // LEXY_ACTION_MATCH_HPP_INCLUDED

View File

@@ -0,0 +1,191 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_ACTION_PARSE_HPP_INCLUDED
#define LEXY_ACTION_PARSE_HPP_INCLUDED
#include <lexy/_detail/invoke.hpp>
#include <lexy/action/base.hpp>
#include <lexy/action/validate.hpp>
#include <lexy/callback/base.hpp>
#include <lexy/callback/bind.hpp>
namespace lexy
{
template <typename T, typename ErrorCallback>
class parse_result
{
using _impl_t = lexy::validate_result<ErrorCallback>;
public:
using value_type = T;
using error_callback = ErrorCallback;
using error_type = typename _impl_t::error_type;
//=== status ===//
constexpr explicit operator bool() const noexcept
{
return _impl.is_success();
}
constexpr bool is_success() const noexcept
{
return _impl.is_success();
}
constexpr bool is_error() const noexcept
{
return _impl.is_error();
}
constexpr bool is_recovered_error() const noexcept
{
return _impl.is_recovered_error();
}
constexpr bool is_fatal_error() const noexcept
{
return _impl.is_fatal_error();
}
//=== value ===//
constexpr bool has_value() const noexcept
{
return static_cast<bool>(_value);
}
constexpr decltype(auto) value() const& noexcept
{
return *_value;
}
constexpr decltype(auto) value() && noexcept
{
return LEXY_MOV(*_value);
}
//=== error ===//
constexpr std::size_t error_count() const noexcept
{
return _impl.error_count();
}
constexpr const auto& errors() const& noexcept
{
return _impl.errors();
}
constexpr auto&& errors() && noexcept
{
return LEXY_MOV(_impl).errors();
}
private:
constexpr explicit parse_result(_impl_t&& impl) noexcept : _impl(LEXY_MOV(impl)), _value() {}
template <typename U>
constexpr explicit parse_result(_impl_t&& impl, U&& v) noexcept : _impl(LEXY_MOV(impl))
{
LEXY_PRECONDITION(impl.is_success() || impl.is_recovered_error());
_value.emplace(LEXY_FWD(v));
}
// In principle we could do a space optimization, as we can reconstruct the impl's status from
// the state of _value and error. Feel free to implement it.
_impl_t _impl;
lexy::_detail::lazy_init<T> _value;
template <typename Reader>
friend class _ph;
};
} // namespace lexy
namespace lexy
{
template <typename Reader>
class _ph
{
using iterator = typename Reader::iterator;
public:
template <typename Input, typename Sink>
constexpr explicit _ph(const _detail::any_holder<const Input*>& input,
_detail::any_holder<Sink>& sink)
: _validate(input, sink)
{}
using event_handler = typename _vh<Reader>::event_handler;
constexpr operator _vh<Reader>&()
{
return _validate;
}
template <typename Production, typename State>
using value_callback = production_value_callback<Production, State>;
template <typename Result, typename T>
constexpr auto get_result(bool rule_parse_result, T&& result) &&
{
using validate_result = lexy::validate_result<typename Result::error_callback>;
return Result(LEXY_MOV(_validate).template get_result<validate_result>(rule_parse_result),
LEXY_MOV(result));
}
template <typename Result>
constexpr auto get_result(bool rule_parse_result) &&
{
using validate_result = lexy::validate_result<typename Result::error_callback>;
return Result(LEXY_MOV(_validate).template get_result<validate_result>(rule_parse_result));
}
private:
_vh<Reader> _validate;
};
template <typename State, typename Input, typename ErrorCallback>
struct parse_action
{
const ErrorCallback* _callback;
State* _state = nullptr;
using handler = _ph<lexy::input_reader<Input>>;
using state = State;
using input = Input;
template <typename T>
using result_type = parse_result<T, ErrorCallback>;
constexpr explicit parse_action(const ErrorCallback& callback) : _callback(&callback) {}
template <typename U = State>
constexpr explicit parse_action(U& state, const ErrorCallback& callback)
: _callback(&callback), _state(&state)
{}
template <typename Production>
constexpr auto operator()(Production, const Input& input) const
{
_detail::any_holder input_holder(&input);
_detail::any_holder sink(_get_error_sink(*_callback));
auto reader = input.reader();
return lexy::do_action<Production, result_type>(handler(input_holder, sink), _state,
reader);
}
};
/// Parses the production into a value, invoking the callback on error.
template <typename Production, typename Input, typename ErrorCallback>
constexpr auto parse(const Input& input, const ErrorCallback& callback)
{
return parse_action<void, Input, ErrorCallback>(callback)(Production{}, input);
}
/// Parses the production into a value, invoking the callback on error.
/// All callbacks gain access to the specified parse state.
template <typename Production, typename Input, typename State, typename ErrorCallback>
constexpr auto parse(const Input& input, State& state, const ErrorCallback& callback)
{
return parse_action<State, Input, ErrorCallback>(state, callback)(Production{}, input);
}
template <typename Production, typename Input, typename State, typename ErrorCallback>
constexpr auto parse(const Input& input, const State& state, const ErrorCallback& callback)
{
return parse_action<const State, Input, ErrorCallback>(state, callback)(Production{}, input);
}
} // namespace lexy
#endif // LEXY_ACTION_PARSE_HPP_INCLUDED

View File

@@ -0,0 +1,217 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_ACTION_PARSE_AS_TREE_HPP_INCLUDED
#define LEXY_ACTION_PARSE_AS_TREE_HPP_INCLUDED
#include <lexy/action/base.hpp>
#include <lexy/action/validate.hpp>
#include <lexy/dsl/any.hpp>
#include <lexy/parse_tree.hpp>
namespace lexy
{
template <typename Tree, typename Reader>
class _pth
{
public:
template <typename Input, typename Sink>
explicit _pth(Tree& tree, const _detail::any_holder<const Input*>& input,
_detail::any_holder<Sink>& sink)
: _tree(&tree), _depth(0), _validate(input, sink)
{}
class event_handler
{
using iterator = typename Reader::iterator;
public:
event_handler(production_info info) : _validate(info) {}
void on(_pth& handler, parse_events::grammar_start, iterator)
{
LEXY_PRECONDITION(handler._depth == 0);
handler._builder.emplace(LEXY_MOV(*handler._tree), _validate.get_info());
}
void on(_pth& handler, parse_events::grammar_finish, Reader& reader)
{
LEXY_PRECONDITION(handler._depth == 0);
auto begin = reader.position();
lexy::try_match_token(dsl::any, reader);
auto end = reader.position();
*handler._tree = LEXY_MOV(*handler._builder).finish({begin, end});
}
void on(_pth& handler, parse_events::grammar_cancel, Reader&)
{
LEXY_PRECONDITION(handler._depth == 0);
handler._tree->clear();
}
void on(_pth& handler, parse_events::production_start ev, iterator pos)
{
if (handler._depth++ > 0)
_marker = handler._builder->start_production(_validate.get_info());
_validate.on(handler._validate, ev, pos);
}
void on(_pth& handler, parse_events::production_finish ev, iterator pos)
{
if (--handler._depth > 0)
{
if (handler._builder->current_child_count() == 0)
handler._builder->token(lexy::position_token_kind, _validate.production_begin(),
_validate.production_begin());
handler._builder->finish_production(LEXY_MOV(_marker));
}
_validate.on(handler._validate, ev, pos);
}
void on(_pth& handler, parse_events::production_cancel ev, iterator pos)
{
if (--handler._depth > 0)
{
// Cancelling the production removes all nodes from the tree.
// To ensure that the parse tree remains lossless, we add everything consumed by it
// as an error token.
handler._builder->cancel_production(LEXY_MOV(_marker));
handler._builder->token(lexy::error_token_kind, _validate.production_begin(), pos);
}
_validate.on(handler._validate, ev, pos);
}
auto on(_pth& handler, lexy::parse_events::operation_chain_start, iterator)
{
// As we don't know the production yet (or whether it is actually an operation),
// we create a container node to decide later.
return handler._builder->start_container();
}
template <typename Operation>
void on(_pth& handler, lexy::parse_events::operation_chain_op, Operation op, iterator)
{
// We set the production of the current container.
// This will do a "left rotation" on the parse tree, making a new container the parent.
handler._builder->set_container_production(op);
}
template <typename Marker>
void on(_pth& handler, lexy::parse_events::operation_chain_finish, Marker&& marker,
iterator)
{
handler._builder->finish_container(LEXY_MOV(marker));
}
template <typename TokenKind>
void on(_pth& handler, parse_events::token, TokenKind kind, iterator begin, iterator end)
{
handler._builder->token(kind, begin, end);
}
template <typename Error>
void on(_pth& handler, parse_events::error ev, Error&& error)
{
_validate.on(handler._validate, ev, LEXY_FWD(error));
}
template <typename Event, typename... Args>
auto on(_pth& handler, Event ev, Args&&... args)
{
return _validate.on(handler._validate, ev, LEXY_FWD(args)...);
}
private:
typename Tree::builder::marker _marker;
typename _vh<Reader>::event_handler _validate;
};
template <typename Production, typename State>
using value_callback = _detail::void_value_callback;
template <typename T>
constexpr auto get_result(bool rule_parse_result) &&
{
LEXY_PRECONDITION(_depth == 0);
return LEXY_MOV(_validate).template get_result<T>(rule_parse_result);
}
private:
lexy::_detail::lazy_init<typename Tree::builder> _builder;
Tree* _tree;
int _depth;
_vh<Reader> _validate;
};
template <typename State, typename Input, typename ErrorCallback, typename TokenKind = void,
typename MemoryResource = void>
struct parse_as_tree_action
{
using tree_type = lexy::parse_tree_for<Input, TokenKind, MemoryResource>;
tree_type* _tree;
const ErrorCallback* _callback;
State* _state = nullptr;
using handler = _pth<tree_type, lexy::input_reader<Input>>;
using state = State;
using input = Input;
template <typename>
using result_type = validate_result<ErrorCallback>;
constexpr explicit parse_as_tree_action(tree_type& tree, const ErrorCallback& callback)
: _tree(&tree), _callback(&callback)
{}
template <typename U = State>
constexpr explicit parse_as_tree_action(U& state, tree_type& tree,
const ErrorCallback& callback)
: _tree(&tree), _callback(&callback), _state(&state)
{}
template <typename Production>
constexpr auto operator()(Production, const Input& input) const
{
_detail::any_holder input_holder(&input);
_detail::any_holder sink(_get_error_sink(*_callback));
auto reader = input.reader();
return lexy::do_action<Production, result_type>(handler(*_tree, input_holder, sink), _state,
reader);
}
};
template <typename Production, typename TokenKind, typename MemoryResource, typename Input,
typename ErrorCallback>
auto parse_as_tree(parse_tree<lexy::input_reader<Input>, TokenKind, MemoryResource>& tree,
const Input& input,
const ErrorCallback& callback) -> validate_result<ErrorCallback>
{
return parse_as_tree_action<void, Input, ErrorCallback, TokenKind,
MemoryResource>(tree, callback)(Production{}, input);
}
template <typename Production, typename TokenKind, typename MemoryResource, typename Input,
typename State, typename ErrorCallback>
auto parse_as_tree(parse_tree<lexy::input_reader<Input>, TokenKind, MemoryResource>& tree,
const Input& input, State& state,
const ErrorCallback& callback) -> validate_result<ErrorCallback>
{
return parse_as_tree_action<State, Input, ErrorCallback, TokenKind,
MemoryResource>(state, tree, callback)(Production{}, input);
}
template <typename Production, typename TokenKind, typename MemoryResource, typename Input,
typename State, typename ErrorCallback>
auto parse_as_tree(parse_tree<lexy::input_reader<Input>, TokenKind, MemoryResource>& tree,
const Input& input, const State& state,
const ErrorCallback& callback) -> validate_result<ErrorCallback>
{
return parse_as_tree_action<const State, Input, ErrorCallback, TokenKind,
MemoryResource>(state, tree, callback)(Production{}, input);
}
} // namespace lexy
#endif // LEXY_ACTION_PARSE_AS_TREE_HPP_INCLUDED

View File

@@ -0,0 +1,98 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_ACTION_SCAN_HPP_INCLUDED
#define LEXY_ACTION_SCAN_HPP_INCLUDED
#include <lexy/action/base.hpp>
#include <lexy/action/validate.hpp>
#include <lexy/dsl/scan.hpp>
namespace lexy
{
template <typename ControlProduction>
struct _scp;
template <>
struct _scp<void>
{
static LEXY_CONSTEVAL auto name()
{
return "scanner control production";
}
static constexpr auto rule = dsl::scan;
static constexpr auto value = lexy::noop;
};
template <typename ControlProduction>
struct _scp : ControlProduction, _scp<void>
{};
template <typename ControlProduction, typename Input, typename State, typename ErrorCallback>
class scanner : public _detail::scanner<scanner<ControlProduction, Input, State, ErrorCallback>,
lexy::input_reader<Input>>
{
using _impl = _detail::scanner<scanner<ControlProduction, Input, State, ErrorCallback>,
lexy::input_reader<Input>>;
using _handler = lexy::_vh<lexy::input_reader<Input>>;
using _production = _scp<ControlProduction>;
public:
constexpr explicit scanner(const Input& input, State* state, const ErrorCallback& callback)
: _impl(input.reader()), _input(&input), _sink(_get_error_sink(callback)),
_cb(_handler(_input, _sink), state, max_recursion_depth<_production>()), _context(&_cb)
{
_context.on(parse_events::production_start{}, this->position());
}
constexpr const auto& parse_state() const
{
return *_context.control_block->parse_state;
}
constexpr auto finish() && -> lexy::validate_result<ErrorCallback>
{
auto parse_result = static_cast<bool>(*this);
if (parse_result)
_context.on(parse_events::production_finish{}, this->position());
else
_context.on(parse_events::production_cancel{}, this->position());
return LEXY_MOV(_cb.parse_handler)
.template get_result<validate_result<ErrorCallback>>(parse_result);
}
private:
auto& context() noexcept
{
return _context;
}
_detail::any_holder<const Input*> _input;
_detail::any_holder<_error_sink_t<ErrorCallback>> _sink;
_detail::parse_context_control_block<_handler, State> _cb;
_pc<_handler, State, _production> _context;
friend _impl;
};
template <typename ControlProduction = void, typename Input, typename ErrorCallback>
constexpr auto scan(const Input& input, const ErrorCallback& callback)
{
return scanner<ControlProduction, Input, void, ErrorCallback>(input, no_parse_state, callback);
}
template <typename ControlProduction = void, typename Input, typename State, typename ErrorCallback>
constexpr auto scan(const Input& input, State& state, const ErrorCallback& callback)
{
return scanner<ControlProduction, Input, State, ErrorCallback>(input, &state, callback);
}
template <typename ControlProduction = void, typename Input, typename State, typename ErrorCallback>
constexpr auto scan(const Input& input, const State& state, const ErrorCallback& callback)
{
return scanner<ControlProduction, Input, const State, ErrorCallback>(input, &state, callback);
}
} // namespace lexy
#endif // LEXY_ACTION_SCAN_HPP_INCLUDED

View File

@@ -0,0 +1,506 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_ACTION_TRACE_HPP_INCLUDED
#define LEXY_ACTION_TRACE_HPP_INCLUDED
#include <lexy/_detail/nttp_string.hpp>
#include <lexy/action/base.hpp>
#include <lexy/input_location.hpp>
#include <lexy/token.hpp>
#include <lexy/visualize.hpp>
//=== debug event ===//
namespace lexy::parse_events
{
/// Debug event was triggered.
/// Arguments: pos, str
struct debug
{};
} // namespace lexy::parse_events
namespace lexyd
{
template <typename CharT, CharT... C>
struct _debug : rule_base
{
template <typename NextParser>
struct p
{
template <typename Context, typename Reader, typename... Args>
LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args)
{
constexpr auto str = lexy::_detail::type_string<CharT, C...>::template c_str<>;
context.on(_ev::debug{}, reader.position(), str);
return NextParser::parse(context, reader, LEXY_FWD(args)...);
}
};
};
#if LEXY_HAS_NTTP
template <lexy::_detail::string_literal Str>
constexpr auto debug = lexy::_detail::to_type_string<_debug, Str>{};
#endif
#define LEXY_DEBUG(Str) \
LEXY_NTTP_STRING(::lexyd::_debug, Str) {}
} // namespace lexyd
//=== trace ====//
namespace lexy::_detail
{
template <typename OutputIt, typename TokenKind>
class trace_writer
{
public:
explicit trace_writer(OutputIt out, visualization_options opts)
: _out(out), _opts(opts), _cur_depth(0)
{}
template <typename Location>
void write_production_start(const Location& loc, const char* name)
{
if (_cur_depth <= _opts.max_tree_depth)
{
write_prefix(loc, prefix::event);
_out = _detail::write_color<_detail::color::bold>(_out, _opts);
_out = _detail::write_str(_out, name);
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
if (_cur_depth == _opts.max_tree_depth)
{
// Print an ellipsis instead of children.
_out = _detail::write_str(_out, ": ");
_out = _detail::write_ellipsis(_out, _opts);
}
else
{
// Prepare for children.
_out = _detail::write_str(_out, ":");
}
}
++_cur_depth;
}
template <typename Location, typename Reader>
void write_token(const Location& loc, lexy::token_kind<TokenKind> kind,
lexy::lexeme<Reader> lexeme)
{
if (_cur_depth > _opts.max_tree_depth || (kind.ignore_if_empty() && lexeme.empty()))
return;
write_prefix(loc, prefix::event);
_out = _detail::write_color<_detail::color::bold>(_out, _opts);
_out = _detail::write_str(_out, kind.name());
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
if (!lexeme.empty())
{
_out = _detail::write_str(_out, ": ");
_out = visualize_to(_out, lexeme, _opts | visualize_space);
}
}
template <typename Location, typename Reader>
void write_backtrack(const Location& loc, lexy::lexeme<Reader> lexeme)
{
if (_cur_depth > _opts.max_tree_depth || lexeme.empty())
return;
write_prefix(loc, prefix::event);
_out = _detail::write_color<_detail::color::yellow, _detail::color::bold>(_out, _opts);
_out = _detail::write_str(_out, "backtracked");
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
_out = _detail::write_str(_out, ": ");
_out = _detail::write_color<_detail::color::yellow>(_out, _opts);
_out = visualize_to(_out, lexeme, _opts.reset(visualize_use_color) | visualize_space);
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
}
template <typename Location, typename Reader, typename Tag>
void write_error(const Location& loc, const lexy::error<Reader, Tag>& error)
{
if (_cur_depth > _opts.max_tree_depth)
return;
write_prefix(loc, prefix::event);
_out = _detail::write_color<_detail::color::red, _detail::color::bold>(_out, _opts);
_out = _detail::write_str(_out, "error");
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
_out = _detail::write_color<_detail::color::red>(_out, _opts);
_out = _detail::write_str(_out, ": ");
if constexpr (std::is_same_v<Tag, lexy::expected_literal>)
{
auto string = _detail::make_literal_lexeme<typename Reader::encoding>(error.string(),
error.length());
_out = _detail::write_str(_out, "expected '");
_out = visualize_to(_out, string, _opts);
_out = _detail::write_str(_out, "'");
}
else if constexpr (std::is_same_v<Tag, lexy::expected_keyword>)
{
auto string = _detail::make_literal_lexeme<typename Reader::encoding>(error.string(),
error.length());
_out = _detail::write_str(_out, "expected keyword '");
_out = visualize_to(_out, string, _opts);
_out = _detail::write_str(_out, "'");
}
else if constexpr (std::is_same_v<Tag, lexy::expected_char_class>)
{
_out = _detail::write_str(_out, "expected ");
_out = _detail::write_str(_out, error.name());
}
else
{
_out = _detail::write_str(_out, error.message());
}
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
}
template <typename Location>
void write_recovery_start(const Location& loc)
{
if (_cur_depth <= _opts.max_tree_depth)
{
write_prefix(loc, prefix::event);
_out = _detail::write_color<_detail::color::yellow, _detail::color::bold>(_out, _opts);
_out = _detail::write_str(_out, "error recovery");
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
_out = _detail::write_color<_detail::color::yellow>(_out, _opts);
_out = _detail::write_str(_out, ":");
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
if (_cur_depth == _opts.max_tree_depth)
{
// Print an ellipsis instead of children.
_out = _detail::write_str(_out, " ");
_out = _detail::write_ellipsis(_out, _opts);
}
}
++_cur_depth;
}
template <typename Location>
void write_operation(const Location& loc, const char* name)
{
if (_cur_depth > _opts.max_tree_depth)
return;
write_prefix(loc, prefix::event);
_out = _detail::write_color<_detail::color::bold>(_out, _opts);
_out = _detail::write_str(_out, "operation");
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
_out = _detail::write_str(_out, ": ");
_out = _detail::write_str(_out, name);
}
template <typename Location>
void write_debug(const Location& loc, const char* str)
{
if (_cur_depth > _opts.max_tree_depth)
return;
write_prefix(loc, prefix::event);
_out = _detail::write_color<_detail::color::blue, _detail::color::bold>(_out, _opts);
_out = _detail::write_str(_out, "debug");
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
_out = _detail::write_color<_detail::color::blue>(_out, _opts);
_out = _detail::write_str(_out, ": ");
_out = _detail::write_str(_out, str);
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
}
template <typename Location>
void write_finish(const Location& loc)
{
if (_cur_depth <= _opts.max_tree_depth)
write_prefix(loc, prefix::finish);
--_cur_depth;
}
template <typename Location>
void write_cancel(const Location& loc)
{
if (_cur_depth <= _opts.max_tree_depth)
write_prefix(loc, prefix::cancel);
--_cur_depth;
}
OutputIt finish() &&
{
*_out++ = '\n';
return _out;
}
private:
enum class prefix
{
event,
cancel,
finish,
};
template <typename Location>
void write_prefix(const Location& loc, prefix p)
{
const auto use_unicode = _opts.is_set(visualize_use_unicode);
if (_cur_depth > 0)
*_out++ = '\n';
_out = _detail::write_color<_detail::color::faint>(_out, _opts);
_out = _detail::write_format(_out, "%2u:%3u", loc.line_nr(), loc.column_nr());
_out = _detail::write_str(_out, ": ");
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
if (_cur_depth > 0)
{
for (auto i = 0u; i != _cur_depth - 1; ++i)
_out = _detail::write_str(_out, use_unicode ? u8"" : u8" ");
switch (p)
{
case prefix::event:
_out = _detail::write_str(_out, use_unicode ? u8"├──" : u8"- ");
break;
case prefix::cancel:
_out = _detail::write_str(_out, use_unicode ? u8"" : u8"-");
_out = _detail::write_color<_detail::color::yellow>(_out, _opts);
_out = _detail::write_str(_out, use_unicode ? u8"" : u8"x");
_out = _detail::write_color<_detail::color::reset>(_out, _opts);
break;
case prefix::finish:
_out = _detail::write_str(_out, use_unicode ? u8"" : u8"- finish");
break;
}
}
}
OutputIt _out;
visualization_options _opts;
std::size_t _cur_depth;
};
} // namespace lexy::_detail
namespace lexy
{
template <typename OutputIt, typename Input, typename TokenKind = void>
class _th
{
public:
explicit _th(OutputIt out, const Input& input, visualization_options opts = {}) noexcept
: _writer(out, opts), _input(&input), _anchor(input)
{
LEXY_PRECONDITION(opts.max_tree_depth <= visualization_options::max_tree_depth_limit);
}
class event_handler
{
using iterator = typename lexy::input_reader<Input>::iterator;
public:
constexpr event_handler(production_info info) : _info(info) {}
void on(_th&, parse_events::grammar_start, iterator) {}
void on(_th&, parse_events::grammar_finish, lexy::input_reader<Input>&) {}
void on(_th&, parse_events::grammar_cancel, lexy::input_reader<Input>&) {}
void on(_th& handler, parse_events::production_start, iterator pos)
{
auto loc = handler.get_location(pos);
handler._writer.write_production_start(loc, _info.name);
// All events for the production are after the initial event.
_previous_anchor.emplace(handler._anchor);
handler._anchor = loc.anchor();
}
void on(_th& handler, parse_events::production_finish, iterator pos)
{
auto loc = handler.get_location(pos);
handler._writer.write_finish(loc);
}
void on(_th& handler, parse_events::production_cancel, iterator pos)
{
auto loc = handler.get_location(pos);
handler._writer.write_cancel(loc);
// We've backtracked, so we need to restore the anchor.
handler._anchor = *_previous_anchor;
}
int on(_th& handler, parse_events::operation_chain_start, iterator pos)
{
auto loc = handler.get_location(pos);
handler._writer.write_production_start(loc, "operation chain");
return 0; // need to return something
}
template <typename Operation>
void on(_th& handler, parse_events::operation_chain_op, Operation, iterator pos)
{
auto loc = handler.get_location(pos);
handler._writer.write_operation(loc, lexy::production_name<Operation>());
}
void on(_th& handler, parse_events::operation_chain_finish, int, iterator pos)
{
auto loc = handler.get_location(pos);
handler._writer.write_finish(loc);
}
template <typename TK>
void on(_th& handler, parse_events::token, TK kind, iterator begin, iterator end)
{
auto loc = handler.get_location(begin);
handler._writer.write_token(loc, token_kind<TokenKind>(kind),
lexeme_for<Input>(begin, end));
}
void on(_th& handler, parse_events::backtracked, iterator begin, iterator end)
{
auto loc = handler.get_location(begin);
handler._writer.write_backtrack(loc, lexeme_for<Input>(begin, end));
}
template <typename Error>
void on(_th& handler, parse_events::error, const Error& error)
{
auto loc = handler.get_location(error.position());
handler._writer.write_error(loc, error);
}
void on(_th& handler, parse_events::recovery_start, iterator pos)
{
auto loc = handler.get_location(pos);
handler._writer.write_recovery_start(loc);
}
void on(_th& handler, parse_events::recovery_finish, iterator pos)
{
auto loc = handler.get_location(pos);
handler._writer.write_finish(loc);
}
void on(_th& handler, parse_events::recovery_cancel, iterator pos)
{
auto loc = handler.get_location(pos);
handler._writer.write_cancel(loc);
}
void on(_th& handler, parse_events::debug, iterator pos, const char* str)
{
auto loc = handler.get_location(pos);
handler._writer.write_debug(loc, str);
}
private:
production_info _info;
// The beginning of the previous production.
// If the current production gets canceled, it needs to be restored.
_detail::lazy_init<input_location_anchor<Input>> _previous_anchor;
};
template <typename Production, typename State>
using value_callback = _detail::void_value_callback;
template <typename>
constexpr OutputIt get_result(bool) &&
{
return LEXY_MOV(_writer).finish();
}
private:
input_location<Input> get_location(typename lexy::input_reader<Input>::iterator pos)
{
return get_input_location(*_input, pos, _anchor);
}
_detail::trace_writer<OutputIt, TokenKind> _writer;
const Input* _input;
input_location_anchor<Input> _anchor;
};
template <typename State, typename Input, typename OutputIt, typename TokenKind = void>
struct trace_action
{
OutputIt _out;
visualization_options _opts;
State* _state = nullptr;
using handler = _th<OutputIt, Input>;
using state = State;
using input = Input;
template <typename>
using result_type = OutputIt;
constexpr explicit trace_action(OutputIt out, visualization_options opts = {})
: _out(out), _opts(opts)
{}
template <typename U = State>
constexpr explicit trace_action(U& state, OutputIt out, visualization_options opts = {})
: _out(out), _opts(opts), _state(&state)
{}
template <typename Production>
constexpr auto operator()(Production, const Input& input) const
{
auto reader = input.reader();
return lexy::do_action<Production, result_type>(handler(_out, input, _opts), _state,
reader);
}
};
template <typename Production, typename TokenKind = void, typename OutputIt, typename Input>
OutputIt trace_to(OutputIt out, const Input& input, visualization_options opts = {})
{
return trace_action<void, Input, OutputIt, TokenKind>(out, opts)(Production{}, input);
}
template <typename Production, typename TokenKind = void, typename OutputIt, typename Input,
typename State>
OutputIt trace_to(OutputIt out, const Input& input, State& state, visualization_options opts = {})
{
return trace_action<State, Input, OutputIt, TokenKind>(state, out, opts)(Production{}, input);
}
template <typename Production, typename TokenKind = void, typename OutputIt, typename Input,
typename State>
OutputIt trace_to(OutputIt out, const Input& input, const State& state,
visualization_options opts = {})
{
return trace_action<const State, Input, OutputIt, TokenKind>(state, out, opts)(Production{},
input);
}
template <typename Production, typename TokenKind = void, typename Input>
void trace(std::FILE* file, const Input& input, visualization_options opts = {})
{
trace_to<Production, TokenKind>(cfile_output_iterator{file}, input, opts);
}
template <typename Production, typename TokenKind = void, typename Input, typename State>
void trace(std::FILE* file, const Input& input, State& state, visualization_options opts = {})
{
trace_to<Production, TokenKind>(cfile_output_iterator{file}, input, state, opts);
}
template <typename Production, typename TokenKind = void, typename Input, typename State>
void trace(std::FILE* file, const Input& input, const State& state, visualization_options opts = {})
{
trace_to<Production, TokenKind>(cfile_output_iterator{file}, input, state, opts);
}
} // namespace lexy
#endif // LEXY_ACTION_TRACE_HPP_INCLUDED

View File

@@ -0,0 +1,320 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_ACTION_VALIDATE_HPP_INCLUDED
#define LEXY_ACTION_VALIDATE_HPP_INCLUDED
#include <lexy/_detail/any_ref.hpp>
#include <lexy/_detail/lazy_init.hpp>
#include <lexy/action/base.hpp>
#include <lexy/callback/base.hpp>
#include <lexy/callback/container.hpp>
#include <lexy/callback/noop.hpp>
#include <lexy/error.hpp>
namespace lexy
{
// Convert the callback into an appropriate sink.
template <typename Callback>
constexpr auto _get_error_sink(const Callback& callback)
{
if constexpr (std::is_same_v<Callback, lexy::_noop>)
{
// We collect noop instead, which counts the errors.
return lexy::collect(callback).sink();
}
else if constexpr (lexy::is_sink<Callback>)
{
// It already is a sink.
return callback.sink();
}
else
{
static_assert(
std::is_void_v<typename Callback::return_type>,
"need to use `lexy::collect()` to create an error callback that can handle multiple errors");
// We need to collect the errors.
return lexy::collect(callback).sink();
}
}
template <typename Callback>
using _error_sink_t = decltype(_get_error_sink(LEXY_DECLVAL(Callback)));
template <typename ErrorCallback>
class validate_result
{
using _sink_t = _error_sink_t<ErrorCallback>;
public:
using error_callback = ErrorCallback;
using error_type = typename _sink_t::return_type;
static_assert(!std::is_void_v<error_type>, "ErrorCallback must not be a void returning sink");
constexpr explicit operator bool() const noexcept
{
return is_success();
}
constexpr bool is_success() const noexcept
{
return _status == _status_success;
}
constexpr bool is_error() const noexcept
{
return !is_success();
}
constexpr bool is_recovered_error() const noexcept
{
return _status == _status_recovered;
}
constexpr bool is_fatal_error() const noexcept
{
return _status == _status_fatal;
}
constexpr std::size_t error_count() const noexcept
{
if constexpr (std::is_same_v<error_type, std::size_t>)
// void-returning callback yields the count only.
return _error;
else
// We assume it's some sort of container otherwise.
return _error.size();
}
constexpr const auto& errors() const& noexcept
{
return _error;
}
constexpr auto&& errors() && noexcept
{
return LEXY_MOV(_error);
}
private:
constexpr explicit validate_result(bool did_recover, error_type&& error)
: _error(LEXY_MOV(error)), _status()
{
if (error_count() == 0u)
_status = _status_success;
else if (did_recover)
_status = _status_recovered;
else
_status = _status_fatal;
}
error_type _error;
enum
{
_status_success,
_status_recovered,
_status_fatal,
} _status;
template <typename Reader>
friend class _vh;
};
} // namespace lexy
namespace lexy
{
template <typename Reader>
struct _validate_callbacks
{
_detail::any_ref sink;
_detail::any_cref input;
void (*generic)(_detail::any_ref sink, production_info info, _detail::any_cref input,
typename Reader::iterator begin, const error<Reader, void>& error);
void (*literal)(_detail::any_ref sink, production_info info, _detail::any_cref input,
typename Reader::iterator begin, const error<Reader, expected_literal>& error);
void (*keyword)(_detail::any_ref sink, production_info info, _detail::any_cref input,
typename Reader::iterator begin, const error<Reader, expected_keyword>& error);
void (*char_class)(_detail::any_ref sink, production_info info, _detail::any_cref input,
typename Reader::iterator begin,
const error<Reader, expected_char_class>& error);
template <typename Input, typename Sink>
constexpr _validate_callbacks(const _detail::any_holder<const Input*>& input,
_detail::any_holder<Sink>& sink)
: sink(&sink), input(&input),
generic([](_detail::any_ref sink, production_info info, _detail::any_cref input,
typename Reader::iterator begin, const error<Reader, void>& error) {
lexy::error_context err_ctx(info, *input->template get<const Input*>(), begin);
sink->template get<Sink>()(err_ctx, LEXY_FWD(error));
}),
literal([](_detail::any_ref sink, production_info info, _detail::any_cref input,
typename Reader::iterator begin, const error<Reader, expected_literal>& error) {
lexy::error_context err_ctx(info, *input->template get<const Input*>(), begin);
sink->template get<Sink>()(err_ctx, LEXY_FWD(error));
}),
keyword([](_detail::any_ref sink, production_info info, _detail::any_cref input,
typename Reader::iterator begin, const error<Reader, expected_keyword>& error) {
lexy::error_context err_ctx(info, *input->template get<const Input*>(), begin);
sink->template get<Sink>()(err_ctx, LEXY_FWD(error));
}),
char_class([](_detail::any_ref sink, production_info info, _detail::any_cref input,
typename Reader::iterator begin,
const error<Reader, expected_char_class>& error) {
lexy::error_context err_ctx(info, *input->template get<const Input*>(), begin);
sink->template get<Sink>()(err_ctx, LEXY_FWD(error));
})
{}
};
template <typename Reader>
class _vh
{
public:
template <typename Input, typename Sink>
constexpr explicit _vh(const _detail::any_holder<const Input*>& input,
_detail::any_holder<Sink>& sink)
: _cb(input, sink)
{}
class event_handler
{
using iterator = typename Reader::iterator;
public:
constexpr event_handler(production_info info) : _begin(), _info(info) {}
constexpr void on(_vh& handler, parse_events::production_start, iterator pos)
{
_begin = pos;
_prev = handler._top;
handler._top = this;
}
constexpr void on(_vh& handler, parse_events::production_finish, iterator)
{
handler._top = _prev;
}
constexpr void on(_vh& handler, parse_events::production_cancel, iterator)
{
handler._top = _prev;
}
template <typename R, typename Tag>
constexpr void on(_vh& handler, parse_events::error, const error<R, Tag>& error)
{
handler._cb.generic(handler._cb.sink, get_info(), handler._cb.input, _begin, error);
}
template <typename R>
constexpr void on(_vh& handler, parse_events::error, const error<R, void>& error)
{
handler._cb.generic(handler._cb.sink, get_info(), handler._cb.input, _begin, error);
}
template <typename R>
constexpr void on(_vh& handler, parse_events::error,
const error<R, expected_literal>& error)
{
handler._cb.literal(handler._cb.sink, get_info(), handler._cb.input, _begin, error);
}
template <typename R>
constexpr void on(_vh& handler, parse_events::error,
const error<R, expected_keyword>& error)
{
handler._cb.keyword(handler._cb.sink, get_info(), handler._cb.input, _begin, error);
}
template <typename R>
constexpr void on(_vh& handler, parse_events::error,
const error<R, expected_char_class>& error)
{
handler._cb.char_class(handler._cb.sink, get_info(), handler._cb.input, _begin, error);
}
template <typename Event, typename... Args>
constexpr auto on(_vh&, Event, const Args&...)
{
return 0; // operation_chain_start must return something
}
constexpr iterator production_begin() const
{
return _begin;
}
constexpr production_info get_info() const
{
auto cur = this;
while (cur->_info.is_transparent && cur->_prev != nullptr)
cur = cur->_prev;
return cur->_info;
}
private:
iterator _begin;
production_info _info;
event_handler* _prev = nullptr;
};
template <typename Production, typename State>
using value_callback = _detail::void_value_callback;
template <typename Result>
constexpr auto get_result(bool rule_parse_result) &&
{
using sink_t = _error_sink_t<typename Result::error_callback>;
return Result(rule_parse_result, LEXY_MOV(_cb.sink->template get<sink_t>()).finish());
}
private:
_validate_callbacks<Reader> _cb;
event_handler* _top = nullptr;
};
template <typename State, typename Input, typename ErrorCallback>
struct validate_action
{
const ErrorCallback* _callback;
State* _state = nullptr;
using handler = _vh<lexy::input_reader<Input>>;
using state = State;
using input = Input;
template <typename>
using result_type = validate_result<ErrorCallback>;
constexpr explicit validate_action(const ErrorCallback& callback) : _callback(&callback) {}
template <typename U = State>
constexpr explicit validate_action(U& state, const ErrorCallback& callback)
: _callback(&callback), _state(&state)
{}
template <typename Production>
constexpr auto operator()(Production, const Input& input) const
{
_detail::any_holder input_holder(&input);
_detail::any_holder sink(_get_error_sink(*_callback));
auto reader = input.reader();
return lexy::do_action<Production, result_type>(handler(input_holder, sink), _state,
reader);
}
};
template <typename Production, typename Input, typename ErrorCallback>
constexpr auto validate(const Input& input,
const ErrorCallback& callback) -> validate_result<ErrorCallback>
{
return validate_action<void, Input, ErrorCallback>(callback)(Production{}, input);
}
template <typename Production, typename Input, typename State, typename ErrorCallback>
constexpr auto validate(const Input& input, State& state,
const ErrorCallback& callback) -> validate_result<ErrorCallback>
{
return validate_action<State, Input, ErrorCallback>(state, callback)(Production{}, input);
}
template <typename Production, typename Input, typename State, typename ErrorCallback>
constexpr auto validate(const Input& input, const State& state,
const ErrorCallback& callback) -> validate_result<ErrorCallback>
{
return validate_action<const State, Input, ErrorCallback>(state, callback)(Production{}, input);
}
} // namespace lexy
#endif // LEXY_ACTION_VALIDATE_HPP_INCLUDED

View File

@@ -0,0 +1,23 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_HPP_INCLUDED
#define LEXY_CALLBACK_HPP_INCLUDED
#include <lexy/callback/adapter.hpp>
#include <lexy/callback/aggregate.hpp>
#include <lexy/callback/base.hpp>
#include <lexy/callback/bind.hpp>
#include <lexy/callback/bit_cast.hpp>
#include <lexy/callback/composition.hpp>
#include <lexy/callback/constant.hpp>
#include <lexy/callback/container.hpp>
#include <lexy/callback/fold.hpp>
#include <lexy/callback/forward.hpp>
#include <lexy/callback/integer.hpp>
#include <lexy/callback/noop.hpp>
#include <lexy/callback/object.hpp>
#include <lexy/callback/string.hpp>
#endif // LEXY_CALLBACK_HPP_INCLUDED

View File

@@ -0,0 +1,168 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_ADAPTER_HPP_INCLUDED
#define LEXY_CALLBACK_ADAPTER_HPP_INCLUDED
#include <lexy/callback/base.hpp>
namespace lexy
{
template <typename ReturnType, typename... Fns>
struct _callback : _overloaded<Fns...>
{
using return_type = ReturnType;
constexpr explicit _callback(Fns... fns) : _overloaded<Fns...>(LEXY_MOV(fns)...) {}
};
template <typename ReturnType, typename... Fns>
struct _callback_with_state : _overloaded<Fns...>
{
using return_type = ReturnType;
template <typename State>
struct _with_state
{
const _callback_with_state& _cb;
State& _state;
template <typename... Args>
constexpr return_type operator()(Args&&... args) const&&
{
return _cb(_state, LEXY_FWD(args)...);
}
};
constexpr explicit _callback_with_state(Fns... fns) : _overloaded<Fns...>(LEXY_MOV(fns)...) {}
template <typename State>
constexpr auto operator[](State& state) const
{
return _with_state<State>{*this, state};
}
};
/// Creates a callback.
template <typename... Fns>
constexpr auto callback(Fns&&... fns)
{
if constexpr ((lexy::is_callback<std::decay_t<Fns>> && ...))
return _callback<std::common_type_t<typename std::decay_t<Fns>::return_type...>,
std::decay_t<Fns>...>(LEXY_FWD(fns)...);
else
return _callback<void, std::decay_t<Fns>...>(LEXY_FWD(fns)...);
}
template <typename ReturnType, typename... Fns>
constexpr auto callback(Fns&&... fns)
{
return _callback<ReturnType, std::decay_t<Fns>...>(LEXY_FWD(fns)...);
}
/// Creates a callback that also receives the parse state.
template <typename... Fns>
constexpr auto callback_with_state(Fns&&... fns)
{
if constexpr ((lexy::is_callback<std::decay_t<Fns>> && ...))
return _callback_with_state<std::common_type_t<typename std::decay_t<Fns>::return_type...>,
std::decay_t<Fns>...>(LEXY_FWD(fns)...);
else
return _callback_with_state<void, std::decay_t<Fns>...>(LEXY_FWD(fns)...);
}
template <typename ReturnType, typename... Fns>
constexpr auto callback_with_state(Fns&&... fns)
{
return _callback_with_state<ReturnType, std::decay_t<Fns>...>(LEXY_FWD(fns)...);
}
template <typename Sink>
struct _cb_from_sink
{
Sink _sink;
using _cb = lexy::sink_callback<Sink>;
using return_type = typename _cb::return_type;
template <typename... Args>
constexpr auto operator()(Args&&... args) const -> decltype((LEXY_DECLVAL(_cb&)(LEXY_FWD(args)),
..., LEXY_DECLVAL(_cb&&).finish()))
{
auto cb = _sink.sink();
(cb(LEXY_FWD(args)), ...);
return LEXY_MOV(cb).finish();
}
};
/// Creates a callback that forwards all arguments to the sink.
template <typename Sink, typename = lexy::sink_callback<Sink>>
constexpr auto callback(Sink&& sink)
{
return _cb_from_sink<std::decay_t<Sink>>{LEXY_FWD(sink)};
}
} // namespace lexy
namespace lexy
{
template <typename MemFn>
struct _mem_fn_traits // MemFn is member data
{
using return_type = MemFn;
};
#define LEXY_MAKE_MEM_FN_TRAITS(...) \
template <typename ReturnType, typename... Args> \
struct _mem_fn_traits<ReturnType(Args...) __VA_ARGS__> \
{ \
using return_type = ReturnType; \
}; \
template <typename ReturnType, typename... Args> \
struct _mem_fn_traits<ReturnType(Args..., ...) __VA_ARGS__> \
{ \
using return_type = ReturnType; \
};
#define LEXY_MAKE_MEM_FN_TRAITS_CV(...) \
LEXY_MAKE_MEM_FN_TRAITS(__VA_ARGS__) \
LEXY_MAKE_MEM_FN_TRAITS(const __VA_ARGS__) \
LEXY_MAKE_MEM_FN_TRAITS(volatile __VA_ARGS__) \
LEXY_MAKE_MEM_FN_TRAITS(const volatile __VA_ARGS__)
#define LEXY_MAKE_MEM_FN_TRAITS_CV_REF(...) \
LEXY_MAKE_MEM_FN_TRAITS_CV(__VA_ARGS__) \
LEXY_MAKE_MEM_FN_TRAITS_CV(&__VA_ARGS__) \
LEXY_MAKE_MEM_FN_TRAITS_CV(&&__VA_ARGS__)
LEXY_MAKE_MEM_FN_TRAITS_CV_REF()
LEXY_MAKE_MEM_FN_TRAITS_CV_REF(noexcept)
#undef LEXY_MAKE_MEM_FN_TRAITS_CV_REF
#undef LEXY_MAKE_MEM_FN_TRAITS_CV
#undef LEXY_MAKE_MEM_FN_TRAITS
template <typename Fn>
struct _mem_fn;
template <typename MemFn, typename T>
struct _mem_fn<MemFn T::*>
{
MemFn T::*_fn;
using return_type = typename _mem_fn_traits<MemFn>::return_type;
template <typename... Args>
constexpr auto operator()(Args&&... args) const
-> decltype(_detail::_mem_invoker<MemFn T::*>::invoke(_fn, LEXY_FWD(args)...))
{
return _detail::_mem_invoker<MemFn T::*>::invoke(_fn, LEXY_FWD(args)...);
}
};
/// Creates a callback from a member function.
template <typename MemFn, typename T>
constexpr auto mem_fn(MemFn T::*fn)
{
return _mem_fn<MemFn T::*>{fn};
}
} // namespace lexy
#endif // LEXY_CALLBACK_ADAPTER_HPP_INCLUDED

View File

@@ -0,0 +1,72 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_AGGREGATE_HPP_INCLUDED
#define LEXY_CALLBACK_AGGREGATE_HPP_INCLUDED
#include <lexy/callback/base.hpp>
#include <lexy/dsl/member.hpp>
namespace lexy
{
struct nullopt;
template <typename T>
struct _as_aggregate
{
using return_type = T;
static_assert(std::is_aggregate_v<return_type>);
constexpr T operator()(lexy::nullopt&&) const
{
return {};
}
constexpr T operator()(T&& result) const
{
return LEXY_MOV(result);
}
template <typename Fn, typename Value, typename... Tail>
constexpr T operator()(lexy::member<Fn>, Value&& value, Tail&&... tail) const
{
T result{};
Fn{}(result, LEXY_FWD(value));
return (*this)(LEXY_MOV(result), LEXY_FWD(tail)...);
}
template <typename Fn, typename Value, typename... Tail>
constexpr T operator()(T&& result, lexy::member<Fn>, Value&& value, Tail&&... tail) const
{
Fn{}(result, LEXY_FWD(value));
return (*this)(LEXY_MOV(result), LEXY_FWD(tail)...);
}
struct _sink
{
T _result{};
using return_type = T;
template <typename Fn, typename Value>
constexpr void operator()(lexy::member<Fn>, Value&& value)
{
Fn()(_result, LEXY_FWD(value));
}
constexpr auto&& finish() &&
{
return LEXY_MOV(_result);
}
};
constexpr auto sink() const
{
return _sink{};
}
};
/// A callback with sink that creates an aggregate.
template <typename T>
constexpr auto as_aggregate = _as_aggregate<T>{};
} // namespace lexy
#endif // LEXY_CALLBACK_AGGREGATE_HPP_INCLUDED

View File

@@ -0,0 +1,91 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_BASE_HPP_INCLUDED
#define LEXY_CALLBACK_BASE_HPP_INCLUDED
#include <lexy/_detail/config.hpp>
#include <lexy/_detail/detect.hpp>
#include <lexy/_detail/invoke.hpp>
namespace lexy
{
template <typename T>
using _detect_callback = typename T::return_type;
template <typename T>
constexpr bool is_callback = _detail::is_detected<_detect_callback, T>;
template <typename T, typename... Args>
using _detect_callback_for = decltype(LEXY_DECLVAL(const T)(LEXY_DECLVAL(Args)...));
template <typename T, typename... Args>
constexpr bool is_callback_for
= _detail::is_detected<_detect_callback_for, std::decay_t<T>, Args...>;
template <typename T, typename State>
using _detect_callback_state = decltype(LEXY_DECLVAL(const T)[LEXY_DECLVAL(State&)]);
template <typename T, typename State>
constexpr bool is_callback_state
= _detail::is_detected<_detect_callback_state, T, std::decay_t<State>>;
template <typename T, typename State, typename... Args>
using _detect_callback_with_state_for
= decltype(LEXY_DECLVAL(const T)[LEXY_DECLVAL(State&)](LEXY_DECLVAL(Args)...));
template <typename T, typename State, typename... Args>
constexpr bool is_callback_with_state_for
= _detail::is_detected<_detect_callback_with_state_for, std::decay_t<T>, State, Args...>;
/// Returns the type of the `.sink()` function.
template <typename Sink, typename... Args>
using sink_callback = decltype(LEXY_DECLVAL(Sink).sink(LEXY_DECLVAL(Args)...));
template <typename T, typename... Args>
using _detect_sink_callback_for = decltype(LEXY_DECLVAL(T&)(LEXY_DECLVAL(Args)...));
template <typename T, typename... Args>
constexpr bool is_sink_callback_for
= _detail::is_detected<_detect_sink_callback_for, std::decay_t<T>, Args...>;
template <typename T, typename... Args>
using _detect_sink = decltype(LEXY_DECLVAL(const T).sink(LEXY_DECLVAL(Args)...).finish());
template <typename T, typename... Args>
constexpr bool is_sink = _detail::is_detected<_detect_sink, T, Args...>;
} // namespace lexy
namespace lexy
{
template <typename Fn>
struct _fn_holder
{
Fn fn;
constexpr explicit _fn_holder(Fn fn) : fn(fn) {}
template <typename... Args>
constexpr auto operator()(Args&&... args) const -> decltype(_detail::invoke(fn,
LEXY_FWD(args)...))
{
return _detail::invoke(fn, LEXY_FWD(args)...);
}
};
template <typename Fn>
using _fn_as_base = std::conditional_t<std::is_class_v<Fn>, Fn, _fn_holder<Fn>>;
template <typename... Fns>
struct _overloaded : _fn_as_base<Fns>...
{
constexpr explicit _overloaded(Fns... fns) : _fn_as_base<Fns>(LEXY_MOV(fns))... {}
using _fn_as_base<Fns>::operator()...;
};
template <typename... Op>
constexpr auto _make_overloaded(Op&&... op)
{
if constexpr (sizeof...(Op) == 1)
return (LEXY_FWD(op), ...);
else
return _overloaded(LEXY_FWD(op)...);
}
} // namespace lexy
#endif // LEXY_CALLBACK_BASE_HPP_INCLUDED

View File

@@ -0,0 +1,385 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_BIND_HPP_INCLUDED
#define LEXY_CALLBACK_BIND_HPP_INCLUDED
#include <lexy/_detail/tuple.hpp>
#include <lexy/callback/base.hpp>
//=== placeholder details ===//
namespace lexy::_detail
{
struct placeholder_base
{};
template <typename T>
constexpr auto is_placeholder = std::is_base_of_v<placeholder_base, T>;
template <typename BoundArg, typename State, typename... Args>
constexpr decltype(auto) expand_bound_arg(const BoundArg& bound, State& state,
const _detail::tuple<Args...>& actual_args)
{
if constexpr (is_placeholder<std::decay_t<BoundArg>>)
return bound(state, actual_args);
else
return bound;
}
struct all_values_placeholder
{};
struct no_bind_state
{};
template <std::size_t Idx, typename Fn, typename... BoundArgs, typename State,
typename... ActualArgs, std::size_t... ActualIdx, typename... ProducedArgs>
constexpr decltype(auto) _invoke_bound(Fn&& fn, const _detail::tuple<BoundArgs...>& bound_args,
State& state,
const _detail::tuple<ActualArgs...>& actual_args,
_detail::index_sequence<ActualIdx...>,
ProducedArgs&&... produced_args)
{
if constexpr (Idx == sizeof...(BoundArgs))
{
(void)bound_args;
(void)state;
(void)actual_args;
return LEXY_FWD(fn)(LEXY_FWD(produced_args)...);
}
else
{
using bound_arg_t
= std::decay_t<typename _detail::tuple<BoundArgs...>::template element_type<Idx>>;
if constexpr (std::is_same_v<bound_arg_t, all_values_placeholder>)
{
return _invoke_bound<Idx + 1>(LEXY_FWD(fn), bound_args, state, actual_args,
actual_args.index_sequence(), LEXY_FWD(produced_args)...,
// Expand to all actual arguments.
LEXY_FWD(actual_args.template get<ActualIdx>())...);
}
else
{
return _invoke_bound<Idx + 1>(LEXY_FWD(fn), bound_args, state, actual_args,
actual_args.index_sequence(), LEXY_FWD(produced_args)...,
// Expand the currently bound argument
expand_bound_arg(bound_args.template get<Idx>(), state,
actual_args));
}
}
}
template <typename Fn, typename... BoundArgs, std::size_t... Idx, typename State,
typename... Args>
constexpr decltype(auto) invoke_bound(Fn&& fn, //
const _detail::tuple<BoundArgs...>& bound_args,
_detail::index_sequence<Idx...>, //
State& state, Args&&... args)
{
auto actual_args = _detail::forward_as_tuple(LEXY_FWD(args)...);
if constexpr ((_detail::is_decayed_same<BoundArgs, _detail::all_values_placeholder> || ...))
{
// If we're having the placeholder we need to recursively expand every argument.
return _invoke_bound<0>(LEXY_FWD(fn), bound_args, state, actual_args,
actual_args.index_sequence());
}
else
{
// If we're not having the all_values_placeholder, every placeholder expands to a single
// argument. We can thus expand it easily by mapping each value of bound args to the actual
// argument.
return LEXY_FWD(fn)(
expand_bound_arg(bound_args.template get<Idx>(), state, actual_args)...);
}
}
} // namespace lexy::_detail
//=== placeholders ===//
namespace lexy
{
/// Placeholder for bind that expands to all values produced by the rule.
constexpr auto values = _detail::all_values_placeholder{};
struct nullopt;
struct _default
{
template <typename T, typename = std::enable_if_t<std::is_default_constructible_v<T>>>
constexpr operator T() const noexcept
{
return T();
}
};
template <std::size_t N, typename T, typename Fn>
struct _nth_value : _detail::placeholder_base // fallback + map
{
LEXY_EMPTY_MEMBER T _fallback;
LEXY_EMPTY_MEMBER Fn _fn;
template <typename State, typename... Args>
LEXY_FORCE_INLINE constexpr decltype(auto) operator()(State&,
const _detail::tuple<Args...>& args) const
{
if constexpr (N > sizeof...(Args))
return _fallback; // Argument is missing.
else
{
using arg_t = typename _detail::tuple<Args...>::template element_type<N - 1>;
if constexpr (_detail::is_decayed_same<arg_t, nullopt>)
return _fallback; // Argument is nullopt.
else
return _detail::invoke(_fn, args.template get<N - 1>());
}
}
};
template <std::size_t N, typename T>
struct _nth_value<N, T, void> : _detail::placeholder_base // fallback only
{
LEXY_EMPTY_MEMBER T _fallback;
template <typename State, typename... Args>
LEXY_FORCE_INLINE constexpr decltype(auto) operator()(State&,
const _detail::tuple<Args...>& args) const
{
if constexpr (N > sizeof...(Args))
return _fallback; // Argument is missing.
else
{
using arg_t = typename _detail::tuple<Args...>::template element_type<N - 1>;
if constexpr (_detail::is_decayed_same<arg_t, nullopt>)
return _fallback; // Argument is nullopt.
else
return args.template get<N - 1>();
}
}
template <typename Fn>
constexpr auto map(Fn&& fn) const
{
return _nth_value<N, T, std::decay_t<Fn>>{{}, _fallback, LEXY_FWD(fn)};
}
};
template <std::size_t N, typename Fn>
struct _nth_value<N, void, Fn> : _detail::placeholder_base // map only
{
LEXY_EMPTY_MEMBER Fn _fn;
template <typename State, typename... Args>
LEXY_FORCE_INLINE constexpr decltype(auto) operator()(State&,
const _detail::tuple<Args...>& args) const
{
static_assert(N <= sizeof...(Args), "not enough arguments for nth_value<N>");
return _detail::invoke(_fn, args.template get<N - 1>());
}
template <typename Arg>
constexpr auto operator||(Arg&& fallback) const
{
return _nth_value<N, std::decay_t<Arg>, Fn>{{}, LEXY_FWD(fallback), _fn};
}
template <typename Arg>
constexpr auto or_(Arg&& fallback) const
{
return *this || LEXY_FWD(fallback);
}
constexpr auto or_default() const
{
return *this || _default{};
}
};
template <std::size_t N>
struct _nth_value<N, void, void> : _detail::placeholder_base
{
// I'm sorry, but this is for consistency with std::bind.
static_assert(N > 0, "values are 1-indexed");
template <typename State, typename... Args>
LEXY_FORCE_INLINE constexpr decltype(auto) operator()(State&,
const _detail::tuple<Args...>& args) const
{
static_assert(N <= sizeof...(Args), "not enough arguments for nth_value<N>");
return args.template get<N - 1>();
}
template <typename Arg>
constexpr auto operator||(Arg&& fallback) const
{
return _nth_value<N, std::decay_t<Arg>, void>{{}, LEXY_FWD(fallback)};
}
template <typename Arg>
constexpr auto or_(Arg&& fallback) const
{
return *this || LEXY_FWD(fallback);
}
constexpr auto or_default() const
{
return *this || _default{};
}
template <typename Fn>
constexpr auto map(Fn&& fn) const
{
return _nth_value<N, void, std::decay_t<Fn>>{{}, LEXY_FWD(fn)};
}
};
/// Placeholder for bind that expands to the nth value produced by the rule.
template <std::size_t N>
constexpr auto nth_value = _nth_value<N, void, void>{};
inline namespace placeholders
{
constexpr auto _1 = nth_value<1>;
constexpr auto _2 = nth_value<2>;
constexpr auto _3 = nth_value<3>;
constexpr auto _4 = nth_value<4>;
constexpr auto _5 = nth_value<5>;
constexpr auto _6 = nth_value<6>;
constexpr auto _7 = nth_value<7>;
constexpr auto _8 = nth_value<8>;
} // namespace placeholders
template <typename Fn>
struct _parse_state : _detail::placeholder_base
{
LEXY_EMPTY_MEMBER Fn _fn;
template <typename State, typename... Args>
constexpr decltype(auto) operator()(State& state, const _detail::tuple<Args...>&) const
{
static_assert(!std::is_same_v<State, _detail::no_bind_state>,
"lexy::parse_state requires that a state is passed to lexy::parse()");
return _detail::invoke(_fn, state);
}
};
template <>
struct _parse_state<void> : _detail::placeholder_base
{
template <typename State, typename... Args>
constexpr decltype(auto) operator()(State& state, const _detail::tuple<Args...>&) const
{
static_assert(!std::is_same_v<State, _detail::no_bind_state>,
"lexy::parse_state requires that a state is passed to lexy::parse()");
return state;
}
template <typename Fn>
constexpr auto map(Fn&& fn) const
{
return _parse_state<std::decay_t<Fn>>{{}, LEXY_FWD(fn)};
}
};
constexpr auto parse_state = _parse_state<void>{};
} // namespace lexy
//=== bind ===//
namespace lexy
{
template <typename Callback, typename... BoundArgs>
struct _bound_cb
{
LEXY_EMPTY_MEMBER Callback _callback;
LEXY_EMPTY_MEMBER _detail::tuple<BoundArgs...> _bound_args;
using return_type = typename Callback::return_type;
template <typename State>
struct _with_state
{
const _bound_cb& _bound;
State& _state;
template <typename... Args>
constexpr return_type operator()(Args&&... args) const&&
{
return _detail::invoke_bound(_bound._callback, _bound._bound_args,
_bound._bound_args.index_sequence(), _state,
LEXY_FWD(args)...);
}
};
template <typename State>
constexpr auto operator[](State& state) const
{
return _with_state<State>{*this, state};
}
template <typename... Args>
constexpr return_type operator()(Args&&... args) const
{
auto state = _detail::no_bind_state{};
return _detail::invoke_bound(_callback, _bound_args, _bound_args.index_sequence(), state,
LEXY_FWD(args)...);
}
};
/// Binds the `operator()` of the callback with pre-defined/remapped values.
template <typename Callback, typename... BoundArgs>
constexpr auto bind(Callback&& callback, BoundArgs&&... args)
{
using bound = _bound_cb<std::decay_t<Callback>, std::decay_t<BoundArgs>...>;
return bound{LEXY_FWD(callback), _detail::make_tuple(LEXY_FWD(args)...)};
}
} // namespace lexy
namespace lexy
{
template <typename Sink>
struct _sink_wrapper
{
const Sink& _sink;
template <typename... Args>
constexpr auto operator()(Args&&... args)
{
return _sink.sink(LEXY_FWD(args)...);
}
};
template <typename Sink, typename... BoundArgs>
struct _bound_sink
{
LEXY_EMPTY_MEMBER Sink _sink;
LEXY_EMPTY_MEMBER _detail::tuple<BoundArgs...> _bound;
template <typename... Args>
constexpr auto operator()(Args&&... args) const -> decltype(_sink(LEXY_FWD(args)...))
{
return _sink(LEXY_FWD(args)...);
}
template <bool Dummy = true,
typename = std::enable_if_t<(!_detail::is_placeholder<BoundArgs> && ... && Dummy)>>
constexpr auto sink() const
{
auto state = _detail::no_bind_state{};
return _detail::invoke_bound(_sink_wrapper<Sink>{_sink}, _bound, _bound.index_sequence(),
state);
}
template <typename State>
constexpr auto sink(State& state) const
{
return _detail::invoke_bound(_sink_wrapper<Sink>{_sink}, _bound, _bound.index_sequence(),
state);
}
};
/// Binds the `.sink()` function of a sink.
/// The result has a `.sink()` function that accepts the state (i.e. the parse state), but no
/// additional values.
template <typename Sink, typename... BoundArgs>
constexpr auto bind_sink(Sink&& sink, BoundArgs&&... args)
{
static_assert(
(!_detail::is_decayed_same<BoundArgs, _detail::all_values_placeholder> && ...),
"lexy::values as a placeholder for bind_sink() doesn't make sense - there won't be any values");
using bound = _bound_sink<std::decay_t<Sink>, std::decay_t<BoundArgs>...>;
return bound{LEXY_FWD(sink), _detail::make_tuple(LEXY_FWD(args)...)};
}
} // namespace lexy
#endif // LEXY_CALLBACK_BIND_HPP_INCLUDED

View File

@@ -0,0 +1,80 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_BIT_CAST_HPP_INCLUDED
#define LEXY_CALLBACK_BIT_CAST_HPP_INCLUDED
#include <lexy/callback/base.hpp>
#ifndef LEXY_HAS_BITCAST
# if defined(__has_builtin)
# if __has_builtin(__builtin_bit_cast)
# define LEXY_HAS_BITCAST 2
# endif
# endif
#endif
#ifndef LEXY_HAS_BITCAST
# if defined(__has_include)
# if __has_include(<bit>) && __cplusplus >= 202002L
# include <bit>
# ifdef __cpp_lib_bit_cast
# define LEXY_HAS_BITCAST 1
# endif
# endif
# endif
#endif
#ifndef LEXY_HAS_BITCAST
# define LEXY_HAS_BITCAST 0
#endif
#if LEXY_HAS_BITCAST == 2
# define LEXY_BITCAST_CONSTEXPR constexpr
#elif LEXY_HAS_BITCAST == 1
# define LEXY_BITCAST_CONSTEXPR constexpr
#else
# include <cstring>
# define LEXY_BITCAST_CONSTEXPR
#endif
namespace lexy
{
template <typename T>
struct _bit_cast
{
static_assert(std::is_trivially_copyable_v<T>);
using return_type = T;
template <typename Arg, typename = std::enable_if_t<sizeof(T) == sizeof(Arg)
&& std::is_trivially_copyable_v<Arg>>>
LEXY_BITCAST_CONSTEXPR T operator()(const Arg& arg) const
{
#if LEXY_HAS_BITCAST == 2
return __builtin_bit_cast(T, arg);
#elif LEXY_HAS_BITCAST == 1
return std::bit_cast<T>(arg);
#else
static_assert(std::is_default_constructible_v<T>, "sorry, get a better standard library");
T to;
std::memcpy(&to, &arg, sizeof(T));
return to;
#endif
}
};
/// std::bit_cast as a callback.
template <typename T>
constexpr auto bit_cast = _bit_cast<T>{};
} // namespace lexy
#endif // LEXY_CALLBACK_BIT_CAST_HPP_INCLUDED

View File

@@ -0,0 +1,120 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_COMPOSITION_HPP_INCLUDED
#define LEXY_CALLBACK_COMPOSITION_HPP_INCLUDED
#include <lexy/callback/base.hpp>
namespace lexy
{
template <typename Cb, typename State, typename = void>
struct _compose_state
{
const Cb& _cb;
State& _state;
using return_type = typename Cb::return_type;
template <typename... Args>
constexpr auto operator()(Args&&... args) const -> decltype(_cb(LEXY_FWD(args)...))
{
return _cb(LEXY_FWD(args)...);
}
};
template <typename Cb, typename State>
struct _compose_state<Cb, State, std::enable_if_t<lexy::is_callback_state<Cb, State>>>
{
const Cb& _cb;
State& _state;
using return_type = typename Cb::return_type;
template <typename... Args>
constexpr auto operator()(Args&&... args) const -> decltype(_cb[_state](LEXY_FWD(args)...))
{
return _cb[_state](LEXY_FWD(args)...);
}
};
template <typename First, typename Second>
struct _compose_cb
{
LEXY_EMPTY_MEMBER First _first;
LEXY_EMPTY_MEMBER Second _second;
constexpr explicit _compose_cb(First&& first, Second&& second)
: _first(LEXY_MOV(first)), _second(LEXY_MOV(second))
{}
using return_type = typename Second::return_type;
template <typename State,
typename = std::enable_if_t<lexy::is_callback_state<First, State> //
|| lexy::is_callback_state<Second, State>>>
constexpr auto operator[](State& state) const
{
auto first = _compose_state<First, State>{_first, state};
auto second = _compose_state<Second, State>{_second, state};
return lexy::_compose_cb(LEXY_MOV(first), LEXY_MOV(second));
}
template <typename... Args>
constexpr auto operator()(Args&&... args) const
-> LEXY_DECAY_DECLTYPE(_first(LEXY_FWD(args)...), LEXY_DECLVAL(return_type))
{
return _second(_first(LEXY_FWD(args)...));
}
};
template <typename Sink, typename Callback>
struct _compose_s
{
LEXY_EMPTY_MEMBER Sink _sink;
LEXY_EMPTY_MEMBER Callback _callback;
using return_type = typename Callback::return_type;
template <typename... Args>
constexpr auto sink(Args&&... args) const -> decltype(_sink.sink(LEXY_FWD(args)...))
{
return _sink.sink(LEXY_FWD(args)...);
}
template <typename State, typename = std::enable_if_t<lexy::is_callback_state<Callback, State>>>
constexpr auto operator[](State& state) const
{
return _compose_state<Callback, State>{_callback, state};
}
template <typename... Args>
constexpr auto operator()(Args&&... args) const -> decltype(_callback(LEXY_FWD(args)...))
{
return _callback(LEXY_FWD(args)...);
}
};
/// Composes two callbacks.
template <typename First, typename Second, typename = _detect_callback<First>,
typename = _detect_callback<Second>>
constexpr auto operator|(First first, Second second)
{
return _compose_cb(LEXY_MOV(first), LEXY_MOV(second));
}
template <typename S, typename Cb, typename Second>
constexpr auto operator|(_compose_s<S, Cb> composed, Second second)
{
auto cb = LEXY_MOV(composed._callback) | LEXY_MOV(second);
return _compose_s<S, decltype(cb)>{LEXY_MOV(composed._sink), LEXY_MOV(cb)};
}
/// Composes a sink with a callback.
template <typename Sink, typename Callback, typename = _detect_callback<Callback>>
constexpr auto operator>>(Sink sink, Callback cb)
{
return _compose_s<Sink, Callback>{LEXY_MOV(sink), LEXY_MOV(cb)};
}
} // namespace lexy
#endif // LEXY_CALLBACK_COMPOSITION_HPP_INCLUDED

View File

@@ -0,0 +1,33 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_CONSTANT_HPP_INCLUDED
#define LEXY_CALLBACK_CONSTANT_HPP_INCLUDED
#include <lexy/callback/base.hpp>
namespace lexy
{
template <typename T>
struct _constant
{
T _value;
using return_type = T;
constexpr const T& operator()() const
{
return _value;
}
};
/// Creates a callback that produces the given value without accepting arguments.
template <typename Arg>
LEXY_CONSTEVAL auto constant(Arg&& value)
{
return _constant<std::decay_t<Arg>>{LEXY_FWD(value)};
}
} // namespace lexy
#endif // LEXY_CALLBACK_CONSTANT_HPP_INCLUDED

View File

@@ -0,0 +1,511 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_CONTAINER_HPP_INCLUDED
#define LEXY_CALLBACK_CONTAINER_HPP_INCLUDED
#include <lexy/callback/base.hpp>
namespace lexy
{
struct nullopt;
template <typename Container>
using _detect_reserve = decltype(LEXY_DECLVAL(Container&).reserve(std::size_t()));
template <typename Container>
constexpr auto _has_reserve = _detail::is_detected<_detect_reserve, Container>;
template <typename Container>
using _detect_append = decltype(LEXY_DECLVAL(Container&).append(LEXY_DECLVAL(Container&&)));
template <typename Container>
constexpr auto _has_append = _detail::is_detected<_detect_append, Container>;
} // namespace lexy
//=== as_list ===//
namespace lexy
{
template <typename Container>
struct _list_sink
{
Container _result;
using return_type = Container;
template <typename C = Container, typename U>
constexpr auto operator()(U&& obj) -> decltype(LEXY_DECLVAL(C&).push_back(LEXY_FWD(obj)))
{
return _result.push_back(LEXY_FWD(obj));
}
template <typename C = Container, typename... Args>
constexpr auto operator()(Args&&... args)
-> decltype(LEXY_DECLVAL(C&).emplace_back(LEXY_FWD(args)...))
{
return _result.emplace_back(LEXY_FWD(args)...);
}
constexpr Container&& finish() &&
{
return LEXY_MOV(_result);
}
};
template <typename Container, typename AllocFn>
struct _list_alloc
{
AllocFn _alloc;
using return_type = Container;
template <typename State>
struct _with_state
{
State& _state;
const AllocFn& _alloc;
constexpr Container operator()(Container&& container) const
{
return LEXY_MOV(container);
}
constexpr Container operator()(nullopt&&) const
{
return Container(_detail::invoke(_alloc, _state));
}
template <typename... Args>
constexpr auto operator()(Args&&... args) const
-> LEXY_DECAY_DECLTYPE((LEXY_DECLVAL(Container&).push_back(LEXY_FWD(args)), ...),
LEXY_DECLVAL(Container))
{
Container result(_detail::invoke(_alloc, _state));
if constexpr (_has_reserve<Container>)
result.reserve(sizeof...(args));
(result.emplace_back(LEXY_FWD(args)), ...);
return result;
}
};
template <typename State>
constexpr auto operator[](State& state) const
{
return _with_state<State>{state, _alloc};
}
template <typename State>
constexpr auto sink(State& state) const
{
return _list_sink<Container>{Container(_detail::invoke(_alloc, state))};
}
};
template <typename Container>
struct _list
{
using return_type = Container;
constexpr Container operator()(Container&& container) const
{
return LEXY_MOV(container);
}
constexpr Container operator()(nullopt&&) const
{
return Container();
}
template <typename... Args>
constexpr auto operator()(Args&&... args) const
-> LEXY_DECAY_DECLTYPE((LEXY_DECLVAL(Container&).push_back(LEXY_FWD(args)), ...),
LEXY_DECLVAL(Container))
{
Container result;
if constexpr (_has_reserve<Container>)
result.reserve(sizeof...(args));
(result.emplace_back(LEXY_FWD(args)), ...);
return result;
}
template <typename C = Container, typename... Args>
constexpr auto operator()(const typename C::allocator_type& allocator, Args&&... args) const
-> decltype((LEXY_DECLVAL(C&).push_back(LEXY_FWD(args)), ...), C(allocator))
{
Container result(allocator);
if constexpr (_has_reserve<Container>)
result.reserve(sizeof...(args));
(result.emplace_back(LEXY_FWD(args)), ...);
return result;
}
constexpr auto sink() const
{
return _list_sink<Container>{Container()};
}
template <typename C = Container>
constexpr auto sink(const typename C::allocator_type& allocator) const
{
return _list_sink<Container>{Container(allocator)};
}
template <typename AllocFn>
constexpr auto allocator(AllocFn alloc_fn) const
{
return _list_alloc<Container, AllocFn>{alloc_fn};
}
constexpr auto allocator() const
{
return allocator([](const auto& alloc) { return alloc; });
}
};
/// A callback with sink that creates a list of things (e.g. a `std::vector`, `std::list`, etc.).
/// It repeatedly calls `push_back()` and `emplace_back()`.
template <typename Container>
constexpr auto as_list = _list<Container>{};
} // namespace lexy
//=== as_collection ===//
namespace lexy
{
template <typename Container>
struct _collection_sink
{
Container _result;
using return_type = Container;
template <typename C = Container, typename U>
constexpr auto operator()(U&& obj) -> decltype(LEXY_DECLVAL(C&).insert(LEXY_FWD(obj)))
{
return _result.insert(LEXY_FWD(obj));
}
template <typename C = Container, typename... Args>
constexpr auto operator()(Args&&... args)
-> decltype(LEXY_DECLVAL(C&).emplace(LEXY_FWD(args)...))
{
return _result.emplace(LEXY_FWD(args)...);
}
constexpr Container&& finish() &&
{
return LEXY_MOV(_result);
}
};
template <typename Container, typename AllocFn>
struct _collection_alloc
{
AllocFn _alloc;
using return_type = Container;
template <typename State>
struct _with_state
{
State& _state;
const AllocFn& _alloc;
constexpr Container operator()(Container&& container) const
{
return LEXY_MOV(container);
}
constexpr Container operator()(nullopt&&) const
{
return Container(_detail::invoke(_alloc, _state));
}
template <typename... Args>
constexpr auto operator()(Args&&... args) const
-> LEXY_DECAY_DECLTYPE((LEXY_DECLVAL(Container&).insert(LEXY_FWD(args)), ...),
LEXY_DECLVAL(Container))
{
Container result(_detail::invoke(_alloc, _state));
if constexpr (_has_reserve<Container>)
result.reserve(sizeof...(args));
(result.emplace(LEXY_FWD(args)), ...);
return result;
}
};
template <typename State>
constexpr auto operator[](State& state) const
{
return _with_state<State>{state, _alloc};
}
template <typename State>
constexpr auto sink(State& state) const
{
return _collection_sink<Container>{Container(_detail::invoke(_alloc, state))};
}
};
template <typename Container>
struct _collection
{
using return_type = Container;
constexpr Container operator()(Container&& container) const
{
return LEXY_MOV(container);
}
constexpr Container operator()(nullopt&&) const
{
return Container();
}
template <typename... Args>
constexpr auto operator()(Args&&... args) const
-> LEXY_DECAY_DECLTYPE((LEXY_DECLVAL(Container&).insert(LEXY_FWD(args)), ...),
LEXY_DECLVAL(Container))
{
Container result;
if constexpr (_has_reserve<Container>)
result.reserve(sizeof...(args));
(result.emplace(LEXY_FWD(args)), ...);
return result;
}
template <typename C = Container, typename... Args>
constexpr auto operator()(const typename C::allocator_type& allocator, Args&&... args) const
-> decltype((LEXY_DECLVAL(C&).insert(LEXY_FWD(args)), ...), C(allocator))
{
Container result(allocator);
if constexpr (_has_reserve<Container>)
result.reserve(sizeof...(args));
(result.emplace(LEXY_FWD(args)), ...);
return result;
}
constexpr auto sink() const
{
return _collection_sink<Container>{Container()};
}
template <typename C = Container>
constexpr auto sink(const typename C::allocator_type& allocator) const
{
return _collection_sink<Container>{Container(allocator)};
}
template <typename AllocFn>
constexpr auto allocator(AllocFn alloc_fn) const
{
return _collection_alloc<Container, AllocFn>{alloc_fn};
}
constexpr auto allocator() const
{
return allocator([](const auto& alloc) { return alloc; });
}
};
/// A callback with sink that creates an unordered collection of things (e.g. a `std::set`,
/// `std::unordered_map`, etc.). It repeatedly calls `insert()` and `emplace()`.
template <typename T>
constexpr auto as_collection = _collection<T>{};
} // namespace lexy
//=== concat ===//
namespace lexy
{
template <typename Container>
struct _concat
{
using return_type = Container;
constexpr Container operator()(nullopt&&) const
{
return Container();
}
template <typename... Tail>
constexpr Container _call(Container&& head, Tail&&... tail) const
{
if constexpr (sizeof...(Tail) == 0)
return LEXY_MOV(head);
else
{
if constexpr (_has_reserve<Container>)
{
auto total_size = (head.size() + ... + tail.size());
head.reserve(total_size);
}
auto append = [&head](Container&& container) {
if constexpr (_has_append<Container>)
{
head.append(LEXY_MOV(container));
}
else
{
for (auto& elem : container)
head.push_back(LEXY_MOV(elem));
}
};
(append(LEXY_MOV(tail)), ...);
return LEXY_MOV(head);
}
}
template <typename... Args>
constexpr auto operator()(Args&&... args) const -> decltype(_call(Container(LEXY_FWD(args))...))
{
return _call(Container(LEXY_FWD(args))...);
}
struct _sink
{
Container _result;
using return_type = Container;
constexpr void operator()(Container&& container)
{
if (_result.empty())
{
// We assign until we have items.
// That way we get the existing allocator.
_result = LEXY_MOV(container);
}
else if constexpr (_has_append<Container>)
{
_result.append(LEXY_MOV(container));
}
else
{
if constexpr (_has_reserve<Container>)
{
auto capacity = _result.capacity();
auto total_size = _result.size() + container.size();
if (total_size > capacity)
{
// If we need more space we reserve at least twice as much.
auto exp_capacity = 2 * capacity;
if (total_size > exp_capacity)
_result.reserve(total_size);
else
_result.reserve(exp_capacity);
}
}
for (auto& elem : container)
_result.push_back(LEXY_MOV(elem));
}
}
constexpr Container&& finish() &&
{
return LEXY_MOV(_result);
}
};
constexpr auto sink() const
{
return _sink{};
}
};
template <typename Container>
constexpr auto concat = _concat<Container>{};
} // namespace lexy
//=== collect ===//
namespace lexy
{
template <typename Container, typename Callback>
class _collect_sink
{
public:
constexpr explicit _collect_sink(Callback callback) : _callback(LEXY_MOV(callback)) {}
template <typename C = Container>
constexpr explicit _collect_sink(Callback callback, const typename C::allocator_type& allocator)
: _result(allocator), _callback(LEXY_MOV(callback))
{}
using return_type = Container;
template <typename... Args>
constexpr auto operator()(Args&&... args)
-> decltype(void(LEXY_DECLVAL(Callback)(LEXY_FWD(args)...)))
{
_result.push_back(_callback(LEXY_FWD(args)...));
}
constexpr auto finish() &&
{
return LEXY_MOV(_result);
}
private:
Container _result;
LEXY_EMPTY_MEMBER Callback _callback;
};
template <typename Callback>
class _collect_sink<void, Callback>
{
public:
constexpr explicit _collect_sink(Callback callback) : _count(0), _callback(LEXY_MOV(callback))
{}
using return_type = std::size_t;
template <typename... Args>
constexpr auto operator()(Args&&... args)
-> decltype(void(LEXY_DECLVAL(Callback)(LEXY_FWD(args)...)))
{
_callback(LEXY_FWD(args)...);
++_count;
}
constexpr auto finish() &&
{
return _count;
}
private:
std::size_t _count;
LEXY_EMPTY_MEMBER Callback _callback;
};
template <typename Container, typename Callback>
class _collect
{
public:
constexpr explicit _collect(Callback callback) : _callback(LEXY_MOV(callback)) {}
constexpr auto sink() const
{
return _collect_sink<Container, Callback>(_callback);
}
template <typename C = Container>
constexpr auto sink(const typename C::allocator_type& allocator) const
{
return _collect_sink<Container, Callback>(_callback, allocator);
}
private:
LEXY_EMPTY_MEMBER Callback _callback;
};
/// Returns a sink that invokes the void-returning callback multiple times, resulting in the number
/// of times it was invoked.
template <typename Callback>
constexpr auto collect(Callback&& callback)
{
using callback_t = std::decay_t<Callback>;
static_assert(std::is_void_v<typename callback_t::return_type>,
"need to specify a container to collect into for non-void callbacks");
return _collect<void, callback_t>(LEXY_FWD(callback));
}
/// Returns a sink that invokes the callback multiple times, storing each result in the container.
template <typename Container, typename Callback>
constexpr auto collect(Callback&& callback)
{
using callback_t = std::decay_t<Callback>;
static_assert(!std::is_void_v<typename callback_t::return_type>,
"cannot collect a void callback into a container");
return _collect<Container, callback_t>(LEXY_FWD(callback));
}
} // namespace lexy
#endif // LEXY_CALLBACK_CONTAINER_HPP_INCLUDED

View File

@@ -0,0 +1,92 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_FOLD_HPP_INCLUDED
#define LEXY_CALLBACK_FOLD_HPP_INCLUDED
#include <lexy/callback/base.hpp>
namespace lexy
{
template <bool Inplace>
struct _fold_sfinae;
template <>
struct _fold_sfinae<true>
{
template <typename Op, typename T, typename... Args>
using type = decltype(void(
_detail::invoke(LEXY_DECLVAL(Op), LEXY_DECLVAL(T&), LEXY_DECLVAL(Args)...)));
};
template <>
struct _fold_sfinae<false>
{
template <typename Op, typename T, typename... Args>
using type = decltype(void(
_detail::invoke(LEXY_DECLVAL(Op), LEXY_DECLVAL(T&&), LEXY_DECLVAL(Args)...)));
};
template <typename T, typename Arg, bool Inplace, typename Op>
struct _fold
{
Arg _init;
LEXY_EMPTY_MEMBER Op _op;
struct _sink_callback
{
T _result;
Op _op;
using return_type = T;
template <typename... Args>
constexpr auto operator()(Args&&... args) ->
typename _fold_sfinae<Inplace>::template type<Op, T, Args&&...>
{
if constexpr (Inplace)
_detail::invoke(_op, _result, LEXY_FWD(args)...);
else
_result = _detail::invoke(_op, LEXY_MOV(_result), LEXY_FWD(args)...);
}
constexpr T finish() &&
{
return LEXY_MOV(_result);
}
};
constexpr auto sink() const
{
if constexpr (std::is_constructible_v<T, Arg>)
return _sink_callback{T(_init), _op};
else
return _sink_callback{_init(), _op};
}
};
/// Sink that folds all the arguments with the binary operation op.
template <typename T, typename Arg = T, typename... Op>
constexpr auto fold(Arg&& init, Op&&... op)
{
auto fn = _make_overloaded(LEXY_FWD(op)...);
return _fold<T, std::decay_t<Arg>, false, decltype(fn)>{LEXY_FWD(init), LEXY_MOV(fn)};
}
/// Sink that folds all the arguments with the binary operation op that modifies the
/// result in-place.
template <typename T, typename Arg = T, typename... Op>
constexpr auto fold_inplace(Arg&& init, Op&&... op)
{
auto fn = _make_overloaded(LEXY_FWD(op)...);
return _fold<T, std::decay_t<Arg>, true, decltype(fn)>{LEXY_FWD(init), LEXY_MOV(fn)};
}
} // namespace lexy
namespace lexy
{
/// Sink that counts all arguments.
constexpr auto count
= fold_inplace<std::size_t>(0u, [](std::size_t& result, auto&&...) { ++result; });
} // namespace lexy
#endif // LEXY_CALLBACK_FOLD_HPP_INCLUDED

View File

@@ -0,0 +1,51 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_FORWARD_HPP_INCLUDED
#define LEXY_CALLBACK_FORWARD_HPP_INCLUDED
#include <lexy/callback/base.hpp>
namespace lexy
{
struct nullopt;
template <typename T>
struct _fwd
{
using return_type = T;
constexpr T operator()(T&& t) const
{
return LEXY_MOV(t);
}
constexpr T operator()(const T& t) const
{
return t;
}
};
template <>
struct _fwd<void>
{
using return_type = void;
template <typename... Args>
constexpr auto sink(const Args&...) const
{
// We don't need a separate type, forward itself can have the required functions.
return *this;
}
constexpr void operator()() const {}
constexpr void operator()(const lexy::nullopt&) const {}
constexpr void finish() && {}
};
/// A callback that just forwards an existing object.
template <typename T>
constexpr auto forward = _fwd<T>{};
} // namespace lexy
#endif // LEXY_CALLBACK_FORWARD_HPP_INCLUDED

View File

@@ -0,0 +1,44 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_INTEGER_HPP_INCLUDED
#define LEXY_CALLBACK_INTEGER_HPP_INCLUDED
#include <lexy/callback/base.hpp>
#include <lexy/dsl/sign.hpp>
namespace lexy
{
template <typename T>
struct _int
{
using return_type = T;
// You don't actually produce an integer value.
constexpr T operator()(lexy::plus_sign) const = delete;
constexpr T operator()(lexy::minus_sign) const = delete;
template <typename Integer>
constexpr T operator()(const Integer& value) const
{
return T(value);
}
template <typename Integer>
constexpr T operator()(lexy::plus_sign, const Integer& value) const
{
return T(value);
}
template <typename Integer>
constexpr T operator()(lexy::minus_sign, const Integer& value) const
{
return T(-value);
}
};
// A callback that takes an optional sign and an integer and produces the signed integer.
template <typename T>
constexpr auto as_integer = _int<T>{};
} // namespace lexy
#endif // LEXY_CALLBACK_INTEGER_HPP_INCLUDED

View File

@@ -0,0 +1,34 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_NOOP_HPP_INCLUDED
#define LEXY_CALLBACK_NOOP_HPP_INCLUDED
#include <lexy/callback/base.hpp>
namespace lexy
{
struct _noop
{
using return_type = void;
template <typename... Args>
constexpr auto sink(const Args&...) const
{
// We don't need a separate type, noop itself can have the required functions.
return *this;
}
template <typename... Args>
constexpr void operator()(const Args&...) const
{}
constexpr void finish() && {}
};
/// A callback with sink that does nothing.
inline constexpr auto noop = _noop{};
} // namespace lexy
#endif // LEXY_CALLBACK_NOOP_HPP_INCLUDED

View File

@@ -0,0 +1,98 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_OBJECT_HPP_INCLUDED
#define LEXY_CALLBACK_OBJECT_HPP_INCLUDED
#include <lexy/callback/base.hpp>
namespace lexy::_detail
{
template <typename T, typename... Args>
using _detect_brace_construct = decltype(T{LEXY_DECLVAL(Args)...});
template <typename T, typename... Args>
constexpr auto is_brace_constructible = _detail::is_detected<_detect_brace_construct, T, Args...>;
template <typename T, typename... Args>
constexpr auto is_constructible
= std::is_constructible_v<T, Args...> || is_brace_constructible<T, Args...>;
} // namespace lexy::_detail
namespace lexy
{
template <typename T>
struct _construct
{
using return_type = T;
constexpr T operator()(T&& t) const
{
return LEXY_MOV(t);
}
constexpr T operator()(const T& t) const
{
return t;
}
template <typename... Args>
constexpr auto operator()(Args&&... args) const
-> std::enable_if_t<_detail::is_constructible<T, Args&&...>, T>
{
if constexpr (std::is_constructible_v<T, Args&&...>)
return T(LEXY_FWD(args)...);
else
return T{LEXY_FWD(args)...};
}
};
template <>
struct _construct<void>
{
using return_type = void;
constexpr void operator()() const {}
};
/// A callback that constructs an object of type T by forwarding the arguments.
template <typename T>
constexpr auto construct = _construct<T>{};
template <typename T, typename PtrT>
struct _new
{
using return_type = PtrT;
constexpr PtrT operator()(T&& t) const
{
auto ptr = new T(LEXY_MOV(t));
return PtrT(ptr);
}
constexpr PtrT operator()(const T& t) const
{
auto ptr = new T(t);
return PtrT(ptr);
}
template <typename... Args>
constexpr auto operator()(Args&&... args) const
-> std::enable_if_t<_detail::is_constructible<T, Args&&...>, PtrT>
{
if constexpr (std::is_constructible_v<T, Args&&...>)
{
auto ptr = new T(LEXY_FWD(args)...);
return PtrT(ptr);
}
else
{
auto ptr = new T{LEXY_FWD(args)...};
return PtrT(ptr);
}
}
};
/// A callback that constructs an object of type T on the heap by forwarding the arguments.
template <typename T, typename PtrT = T*>
constexpr auto new_ = _new<T, PtrT>{};
} // namespace lexy
#endif // LEXY_CALLBACK_OBJECT_HPP_INCLUDED

View File

@@ -0,0 +1,210 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_STRING_HPP_INCLUDED
#define LEXY_CALLBACK_STRING_HPP_INCLUDED
#include <lexy/_detail/code_point.hpp>
#include <lexy/callback/base.hpp>
#include <lexy/code_point.hpp>
#include <lexy/encoding.hpp>
#include <lexy/input/base.hpp>
#include <lexy/lexeme.hpp>
namespace lexy
{
struct nullopt;
template <typename String>
using _string_char_type = LEXY_DECAY_DECLTYPE(LEXY_DECLVAL(String)[0]);
template <typename String, typename Encoding, typename CaseFoldingDSL = void>
struct _as_string
{
using return_type = String;
using _char_type = _string_char_type<String>;
static_assert(lexy::_detail::is_compatible_char_type<Encoding, _char_type>,
"invalid character type/encoding combination");
static constexpr String&& _case_folding(String&& str)
{
if constexpr (std::is_void_v<CaseFoldingDSL>)
{
return LEXY_MOV(str);
}
else if constexpr (CaseFoldingDSL::template is_inplace<Encoding>)
{
// We can change the string in place.
auto original_reader = lexy::_range_reader<Encoding>(str.begin(), str.end());
auto reader = typename CaseFoldingDSL::template case_folding<decltype(original_reader)>{
original_reader};
for (auto ptr = str.data(); true; ++ptr)
{
auto cur = reader.peek();
if (cur == Encoding::eof())
break;
reader.bump();
// Once we've bumped it, we're not looking at it again.
*ptr = static_cast<_char_type>(cur);
}
return LEXY_MOV(str);
}
else
{
// We store the existing string somewhere else and clear it.
// Then we can read the case folded string and append each code unit.
auto original = LEXY_MOV(str);
str = String();
str.reserve(original.size());
auto original_reader = lexy::_range_reader<Encoding>(original.begin(), original.end());
auto reader = typename CaseFoldingDSL::template case_folding<decltype(original_reader)>{
original_reader};
while (true)
{
auto cur = reader.peek();
if (cur == Encoding::eof())
break;
str.push_back(static_cast<_char_type>(cur));
reader.bump();
}
return LEXY_MOV(str);
}
}
template <typename NewCaseFoldingDSL>
constexpr auto case_folding(NewCaseFoldingDSL) const
{
return _as_string<String, Encoding, NewCaseFoldingDSL>{};
}
constexpr String operator()(nullopt&&) const
{
return String();
}
constexpr String&& operator()(String&& str) const
{
return _case_folding(LEXY_MOV(str));
}
template <typename Iterator>
constexpr auto operator()(Iterator begin, Iterator end) const -> decltype(String(begin, end))
{
return _case_folding(String(begin, end));
}
template <typename Str = String, typename Iterator>
constexpr auto operator()(const typename Str::allocator_type& allocator, Iterator begin,
Iterator end) const -> decltype(String(begin, end, allocator))
{
return _case_folding(String(begin, end, allocator));
}
template <typename Reader>
constexpr String operator()(lexeme<Reader> lex) const
{
static_assert(lexy::char_type_compatible_with_reader<Reader, _char_type>,
"cannot convert lexeme to this string type");
using iterator = typename lexeme<Reader>::iterator;
if constexpr (std::is_convertible_v<iterator, const _char_type*>)
return _case_folding(String(lex.data(), lex.size()));
else
return _case_folding(String(lex.begin(), lex.end()));
}
template <typename Str = String, typename Reader>
constexpr String operator()(const typename Str::allocator_type& allocator,
lexeme<Reader> lex) const
{
static_assert(lexy::char_type_compatible_with_reader<Reader, _char_type>,
"cannot convert lexeme to this string type");
using iterator = typename lexeme<Reader>::iterator;
if constexpr (std::is_convertible_v<iterator, const _char_type*>)
return _case_folding(String(lex.data(), lex.size(), allocator));
else
return _case_folding(String(lex.begin(), lex.end(), allocator));
}
constexpr String operator()(code_point cp) const
{
typename Encoding::char_type buffer[4] = {};
auto size = _detail::encode_code_point<Encoding>(cp.value(), buffer, 4);
return _case_folding(String(buffer, buffer + size));
}
template <typename Str = String>
constexpr String operator()(const typename Str::allocator_type& allocator, code_point cp) const
{
typename Encoding::char_type buffer[4] = {};
auto size = _detail::encode_code_point<Encoding>(cp.value(), buffer, 4);
return _case_folding(String(buffer, buffer + size, allocator));
}
struct _sink
{
String _result;
using return_type = String;
template <typename CharT, typename = decltype(LEXY_DECLVAL(String).push_back(CharT()))>
constexpr void operator()(CharT c)
{
_result.push_back(c);
}
constexpr void operator()(String&& str)
{
_result.append(LEXY_MOV(str));
}
template <typename Str = String, typename Iterator>
constexpr auto operator()(Iterator begin, Iterator end)
-> decltype(void(LEXY_DECLVAL(Str).append(begin, end)))
{
_result.append(begin, end);
}
template <typename Reader>
constexpr void operator()(lexeme<Reader> lex)
{
static_assert(lexy::char_type_compatible_with_reader<Reader, _char_type>,
"cannot convert lexeme to this string type");
_result.append(lex.begin(), lex.end());
}
constexpr void operator()(code_point cp)
{
typename Encoding::char_type buffer[4] = {};
auto size = _detail::encode_code_point<Encoding>(cp.value(), buffer, 4);
_result.append(buffer, buffer + size);
}
constexpr String&& finish() &&
{
return _case_folding(LEXY_MOV(_result));
}
};
constexpr auto sink() const
{
return _sink{String()};
}
template <typename S = String>
constexpr auto sink(const typename S::allocator_type& allocator) const
{
return _sink{String(allocator)};
}
};
/// A callback with sink that creates a string (e.g. `std::string`).
/// As a callback, it converts a lexeme into the string.
/// As a sink, it repeatedly calls `.push_back()` for individual characters,
/// or `.append()` for lexemes or other strings.
template <typename String, typename Encoding = deduce_encoding<_string_char_type<String>>>
constexpr auto as_string = _as_string<String, Encoding>{};
} // namespace lexy
#endif // LEXY_CALLBACK_STRING_HPP_INCLUDED

View File

@@ -0,0 +1,306 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CODE_POINT_HPP_INCLUDED
#define LEXY_CODE_POINT_HPP_INCLUDED
#include <cstdint>
#include <lexy/_detail/assert.hpp>
#include <lexy/_detail/config.hpp>
#if LEXY_HAS_UNICODE_DATABASE
# define LEXY_UNICODE_CONSTEXPR constexpr
#else
# define LEXY_UNICODE_CONSTEXPR
#endif
namespace lexy
{
/// A unicode code point.
class code_point
{
public:
constexpr code_point() noexcept : _value(0xFFFF'FFFF) {}
constexpr explicit code_point(char32_t value) noexcept : _value(value) {}
constexpr auto value() const noexcept
{
return _value;
}
//=== classification ===//
constexpr bool is_ascii() const noexcept
{
return _value <= 0x7F;
}
constexpr bool is_bmp() const noexcept
{
return _value <= 0xFFFF;
}
constexpr bool is_valid() const noexcept
{
return _value <= 0x10'FFFF;
}
constexpr bool is_control() const noexcept
{
return _value <= 0x1F || (0x7F <= _value && _value <= 0x9F);
}
constexpr bool is_surrogate() const noexcept
{
return 0xD800 <= _value && _value <= 0xDFFF;
}
constexpr bool is_private_use() const noexcept
{
return (0xE000 <= _value && _value <= 0xF8FF)
|| (0x0F'0000 <= _value && _value <= 0x0F'FFFD)
|| (0x10'0000 <= _value && _value <= 0x10'FFFD);
}
constexpr bool is_noncharacter() const noexcept
{
// Contiguous range of 32 non-characters.
if (0xFDD0 <= _value && _value <= 0xFDEF)
return true;
// Last two code points of every plane.
auto in_plane = _value & 0xFFFF;
return in_plane == 0xFFFE || in_plane == 0xFFFF;
}
constexpr bool is_scalar() const noexcept
{
return is_valid() && !is_surrogate();
}
//=== general category ===//
enum general_category_t
{
// NOLINTNEXTLINE: can't use parentheses here
#define LEXY_UNICODE_CATEGORY(Short, Long) Short, Long = Short
LEXY_UNICODE_CATEGORY(Lu, uppercase_letter),
LEXY_UNICODE_CATEGORY(Ll, lowercase_letter),
LEXY_UNICODE_CATEGORY(Lt, titlecase_letter),
LEXY_UNICODE_CATEGORY(Lm, modifier_letter),
LEXY_UNICODE_CATEGORY(Lo, other_letter),
LEXY_UNICODE_CATEGORY(Mn, nonspacing_mark),
LEXY_UNICODE_CATEGORY(Mc, spacing_mark),
LEXY_UNICODE_CATEGORY(Me, enclosing_mark),
LEXY_UNICODE_CATEGORY(Nd, decimal_number),
LEXY_UNICODE_CATEGORY(Nl, letter_number),
LEXY_UNICODE_CATEGORY(No, other_number),
LEXY_UNICODE_CATEGORY(Pc, connector_punctuation),
LEXY_UNICODE_CATEGORY(Pd, dash_punctuation),
LEXY_UNICODE_CATEGORY(Ps, open_punctuation),
LEXY_UNICODE_CATEGORY(Pe, closing_punctuation),
LEXY_UNICODE_CATEGORY(Pi, initial_puncutation),
LEXY_UNICODE_CATEGORY(Pf, final_puncutation),
LEXY_UNICODE_CATEGORY(Po, other_punctuation),
LEXY_UNICODE_CATEGORY(Sm, math_symbol),
LEXY_UNICODE_CATEGORY(Sc, currency_symbol),
LEXY_UNICODE_CATEGORY(Sk, modifier_symbol),
LEXY_UNICODE_CATEGORY(So, other_symbol),
LEXY_UNICODE_CATEGORY(Zs, space_separator),
LEXY_UNICODE_CATEGORY(Zl, line_separator),
LEXY_UNICODE_CATEGORY(Zp, paragraph_separator),
LEXY_UNICODE_CATEGORY(Cc, control),
LEXY_UNICODE_CATEGORY(Cf, format),
LEXY_UNICODE_CATEGORY(Cs, surrogate),
LEXY_UNICODE_CATEGORY(Co, private_use),
LEXY_UNICODE_CATEGORY(Cn, unassigned),
#undef LEXY_UNICODE_CATEGORY
};
template <general_category_t... Cats>
struct _gc_group
{
const char* name;
friend constexpr bool operator==(_gc_group, general_category_t cat)
{
return ((cat == Cats) || ...);
}
friend constexpr bool operator==(general_category_t cat, _gc_group)
{
return ((cat == Cats) || ...);
}
friend constexpr bool operator!=(_gc_group, general_category_t cat)
{
return !(_gc_group{} == cat);
}
friend constexpr bool operator!=(general_category_t cat, _gc_group)
{
return !(_gc_group{} == cat);
}
};
#define LEXY_UNICODE_CATEGORY_GROUP(Name, Short, Long, ...) \
static constexpr _gc_group<__VA_ARGS__> Short{"code-point." Name}; \
static constexpr _gc_group<__VA_ARGS__> Long = Short
LEXY_UNICODE_CATEGORY_GROUP("cased-letter", LC, cased_letter, Lu, Ll, Lt);
LEXY_UNICODE_CATEGORY_GROUP("letter", L, letter, Lu, Ll, Lt, Lm, Lo);
LEXY_UNICODE_CATEGORY_GROUP("mark", M, mark, Mn, Mc, Me);
LEXY_UNICODE_CATEGORY_GROUP("number", N, number, Nd, Nl, No);
LEXY_UNICODE_CATEGORY_GROUP("punctuation", P, punctuation, Pc, Pd, Ps, Pe, Pi, Pf, Po);
LEXY_UNICODE_CATEGORY_GROUP("symbol", S, symbol, Sm, Sc, Sk, So);
LEXY_UNICODE_CATEGORY_GROUP("separator", Z, separator, Zs, Zl, Zp);
LEXY_UNICODE_CATEGORY_GROUP("other", C, other, Cc, Cf, Cs, Co, Cn);
#undef LEXY_UNICODE_CATEGORY_GROUP
LEXY_UNICODE_CONSTEXPR general_category_t general_category() const noexcept;
//=== comparision ===//
friend constexpr bool operator==(code_point lhs, code_point rhs) noexcept
{
return lhs._value == rhs._value;
}
friend constexpr bool operator!=(code_point lhs, code_point rhs) noexcept
{
return lhs._value != rhs._value;
}
private:
char32_t _value;
};
LEXY_UNICODE_CONSTEXPR code_point simple_case_fold(code_point cp) noexcept;
} // namespace lexy
namespace lexy::_detail
{
constexpr const char* general_category_name(lexy::code_point::general_category_t category)
{
switch (category)
{
case lexy::code_point::Lu:
return "code-point.uppercase-letter";
case lexy::code_point::Ll:
return "code-point.lowercase-letter";
case lexy::code_point::Lt:
return "code-point.titlecase-letter";
case lexy::code_point::Lm:
return "code-point.modifier-letter";
case lexy::code_point::Lo:
return "code-point.other-letter";
case lexy::code_point::Mn:
return "code-point.nonspacing-mark";
case lexy::code_point::Mc:
return "code-point.combining-mark";
case lexy::code_point::Me:
return "code-point.enclosing-mark";
case lexy::code_point::Nd:
return "code-point.decimal-number";
case lexy::code_point::Nl:
return "code-point.letter-number";
case lexy::code_point::No:
return "code-point.other-number";
case lexy::code_point::Pc:
return "code-point.connector-punctuation";
case lexy::code_point::Pd:
return "code-point.dash-punctuation";
case lexy::code_point::Ps:
return "code-point.open-punctuation";
case lexy::code_point::Pe:
return "code-point.close-punctuation";
case lexy::code_point::Pi:
return "code-point.initial-quote-punctuation";
case lexy::code_point::Pf:
return "code-point.final-quote-punctuation";
case lexy::code_point::Po:
return "code-point.other-punctuation";
case lexy::code_point::Sm:
return "code-point.math-symbol";
case lexy::code_point::Sc:
return "code-point.currency-symbol";
case lexy::code_point::Sk:
return "code-point.modifier-symbol";
case lexy::code_point::So:
return "code-point.other-symbol";
case lexy::code_point::Zs:
return "code-point.space-separator";
case lexy::code_point::Zl:
return "code-point.line-separator";
case lexy::code_point::Zp:
return "code-point.paragraph-separator";
case lexy::code_point::Cc:
return "code-point.control";
case lexy::code_point::Cf:
return "code-point.format";
case lexy::code_point::Cs:
return "code-point.surrogate";
case lexy::code_point::Co:
return "code-point.private-use";
case lexy::code_point::Cn:
return "code-point.not-assigned";
}
return nullptr; // unreachable
}
} // namespace lexy::_detail
#if LEXY_HAS_UNICODE_DATABASE
# include <lexy/_detail/unicode_database.hpp>
constexpr lexy::code_point::general_category_t lexy::code_point::general_category() const noexcept
{
if (!is_valid())
return general_category_t::unassigned;
auto idx = _unicode_db::property_index(_value);
return _unicode_db::category[idx];
}
constexpr lexy::code_point lexy::simple_case_fold(code_point cp) noexcept
{
if (!cp.is_valid())
return cp;
auto idx = _unicode_db::property_index(cp.value());
auto offset = _unicode_db::case_folding_offset[idx];
return code_point(char32_t(std::int_least32_t(cp.value()) + offset));
}
namespace lexy::_detail
{
template <lexy::_unicode_db::binary_properties_t... Props>
LEXY_FORCE_INLINE constexpr bool code_point_has_properties(char32_t cp)
{
constexpr auto mask = ((1 << Props) | ...);
auto idx = _unicode_db::property_index(cp);
auto props = _unicode_db::binary_properties[idx];
return (props & mask) != 0;
}
} // namespace lexy::_detail
# define LEXY_UNICODE_PROPERTY(Name) ::lexy::_unicode_db::Name
#else
namespace lexy::_detail
{
template <int... Props>
bool code_point_has_properties(char32_t cp); // not implemented
} // namespace lexy::_detail
# define LEXY_UNICODE_PROPERTY(Name) 0
#endif
#endif // LEXY_CODE_POINT_HPP_INCLUDED

View File

@@ -0,0 +1,68 @@
// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_DSL_HPP_INCLUDED
#define LEXY_DSL_HPP_INCLUDED
#include <lexy/dsl/any.hpp>
#include <lexy/dsl/ascii.hpp>
#include <lexy/dsl/bits.hpp>
#include <lexy/dsl/bom.hpp>
#include <lexy/dsl/brackets.hpp>
#include <lexy/dsl/branch.hpp>
#include <lexy/dsl/byte.hpp>
#include <lexy/dsl/capture.hpp>
#include <lexy/dsl/case_folding.hpp>
#include <lexy/dsl/char_class.hpp>
#include <lexy/dsl/choice.hpp>
#include <lexy/dsl/code_point.hpp>
#include <lexy/dsl/combination.hpp>
#include <lexy/dsl/context_counter.hpp>
#include <lexy/dsl/context_flag.hpp>
#include <lexy/dsl/context_identifier.hpp>
#include <lexy/dsl/delimited.hpp>
#include <lexy/dsl/digit.hpp>
#include <lexy/dsl/effect.hpp>
#include <lexy/dsl/eof.hpp>
#include <lexy/dsl/error.hpp>
#include <lexy/dsl/expression.hpp>
#include <lexy/dsl/flags.hpp>
#include <lexy/dsl/follow.hpp>
#include <lexy/dsl/identifier.hpp>
#include <lexy/dsl/if.hpp>
#include <lexy/dsl/integer.hpp>
#include <lexy/dsl/list.hpp>
#include <lexy/dsl/literal.hpp>
#include <lexy/dsl/lookahead.hpp>
#include <lexy/dsl/loop.hpp>
#include <lexy/dsl/member.hpp>
#include <lexy/dsl/newline.hpp>
#include <lexy/dsl/operator.hpp>
#include <lexy/dsl/option.hpp>
#include <lexy/dsl/parse_as.hpp>
#include <lexy/dsl/peek.hpp>
#include <lexy/dsl/position.hpp>
#include <lexy/dsl/production.hpp>
#include <lexy/dsl/punctuator.hpp>
#include <lexy/dsl/recover.hpp>
#include <lexy/dsl/repeat.hpp>
#include <lexy/dsl/return.hpp>
#include <lexy/dsl/scan.hpp>
#include <lexy/dsl/separator.hpp>
#include <lexy/dsl/sequence.hpp>
#include <lexy/dsl/sign.hpp>
#include <lexy/dsl/subgrammar.hpp>
#include <lexy/dsl/symbol.hpp>
#include <lexy/dsl/terminator.hpp>
#include <lexy/dsl/times.hpp>
#include <lexy/dsl/token.hpp>
#include <lexy/dsl/unicode.hpp>
#include <lexy/dsl/until.hpp>
#include <lexy/dsl/whitespace.hpp>
#if LEXY_EXPERIMENTAL
# include <lexy/dsl/parse_tree_node.hpp>
#endif
#endif // LEXY_DSL_HPP_INCLUDED

Some files were not shown because too many files have changed in this diff Show More